summaryrefslogtreecommitdiffstats
path: root/stand
diff options
context:
space:
mode:
authorkevans <kevans@FreeBSD.org>2018-02-12 01:08:44 +0000
committerkevans <kevans@FreeBSD.org>2018-02-12 01:08:44 +0000
commit7d97ee5b28b409c00bfaf12daf5ab497a6038b9d (patch)
tree245306b754606bcf49c0ff17b131b58609b6c7a6 /stand
parent43b278e1b66cf4de337a17034087ea785031bd6f (diff)
downloadFreeBSD-src-7d97ee5b28b409c00bfaf12daf5ab497a6038b9d.zip
FreeBSD-src-7d97ee5b28b409c00bfaf12daf5ab497a6038b9d.tar.gz
MFC r325834,r325997,326502: Move sys/boot to stand/
This is effectively a direct commit to stable/11, due to differences between stable/11 and head. Changes to DTS in sys/boot/fdt/dts were often accompanied by kernel changes. Many of these were also risc-v updates that likely had many more dependencies to MFC. Because of this, sys/boot/fdt/dts remains as-is while everything else in sys/boot relocates to stand/. r325834: Move sys/boot to stand. Fix all references to new location r325997: Remove empty directories. r326502: Document the sys/boot -> stand move in hier.7 and the top-level README.
Diffstat (limited to 'stand')
-rw-r--r--stand/Makefile20
-rw-r--r--stand/Makefile.amd6418
-rw-r--r--stand/Makefile.arm10
-rw-r--r--stand/Makefile.arm6410
-rw-r--r--stand/Makefile.i38610
-rw-r--r--stand/Makefile.inc37
-rw-r--r--stand/Makefile.mips7
-rw-r--r--stand/Makefile.pc983
-rw-r--r--stand/Makefile.powerpc8
-rw-r--r--stand/Makefile.sparc646
-rw-r--r--stand/arm/Makefile5
-rw-r--r--stand/arm/Makefile.inc3
-rw-r--r--stand/arm/loader/loader.conf13
-rw-r--r--stand/arm/uboot/Makefile70
-rw-r--r--stand/arm/uboot/conf.c97
-rw-r--r--stand/arm/uboot/help.uboot27
-rw-r--r--stand/arm/uboot/ldscript.arm134
-rw-r--r--stand/arm/uboot/start.S145
-rw-r--r--stand/arm/uboot/version9
-rw-r--r--stand/arm64/Makefile3
-rw-r--r--stand/arm64/libarm64/cache.c95
-rw-r--r--stand/arm64/libarm64/cache.h38
-rw-r--r--stand/common/Makefile.depend11
-rw-r--r--stand/common/bcache.c503
-rw-r--r--stand/common/boot.c410
-rw-r--r--stand/common/bootstrap.h334
-rw-r--r--stand/common/commands.c511
-rw-r--r--stand/common/console.c302
-rw-r--r--stand/common/dev_net.c435
-rw-r--r--stand/common/dev_net.h36
-rw-r--r--stand/common/devopen.c67
-rw-r--r--stand/common/disk.c432
-rw-r--r--stand/common/disk.h117
-rw-r--r--stand/common/help.common407
-rw-r--r--stand/common/install.c355
-rw-r--r--stand/common/interp.c371
-rw-r--r--stand/common/interp_backslash.c167
-rw-r--r--stand/common/interp_forth.c332
-rw-r--r--stand/common/interp_parse.c222
-rw-r--r--stand/common/isapnp.c313
-rw-r--r--stand/common/isapnp.h313
-rw-r--r--stand/common/load_elf.c1038
-rw-r--r--stand/common/load_elf32.c7
-rw-r--r--stand/common/load_elf32_obj.c7
-rw-r--r--stand/common/load_elf64.c6
-rw-r--r--stand/common/load_elf64_obj.c6
-rw-r--r--stand/common/load_elf_obj.c537
-rw-r--r--stand/common/ls.c212
-rw-r--r--stand/common/md.c157
-rw-r--r--stand/common/merge_help.awk104
-rw-r--r--stand/common/misc.c219
-rw-r--r--stand/common/module.c1095
-rwxr-xr-xstand/common/newvers.sh60
-rw-r--r--stand/common/part.c898
-rw-r--r--stand/common/part.h83
-rw-r--r--stand/common/paths.h39
-rw-r--r--stand/common/pnp.c236
-rw-r--r--stand/common/rbx.h61
-rw-r--r--stand/common/reloc_elf.c231
-rw-r--r--stand/common/reloc_elf32.c6
-rw-r--r--stand/common/reloc_elf64.c6
-rw-r--r--stand/common/self_reloc.c124
-rw-r--r--stand/defs.mk171
-rw-r--r--stand/efi/Makefile23
-rw-r--r--stand/efi/Makefile.inc32
-rw-r--r--stand/efi/boot1/Makefile129
-rw-r--r--stand/efi/boot1/Makefile.depend14
-rw-r--r--stand/efi/boot1/Makefile.fat4
-rw-r--r--stand/efi/boot1/boot1.c583
-rw-r--r--stand/efi/boot1/boot_module.h109
-rw-r--r--stand/efi/boot1/fat-amd64.tmpl.xzbin0 -> 1712 bytes
-rw-r--r--stand/efi/boot1/fat-arm.tmpl.xzbin0 -> 1708 bytes
-rw-r--r--stand/efi/boot1/fat-arm64.tmpl.xzbin0 -> 1720 bytes
-rw-r--r--stand/efi/boot1/fat-i386.tmpl.xzbin0 -> 1720 bytes
-rwxr-xr-xstand/efi/boot1/generate-fat.sh79
-rw-r--r--stand/efi/boot1/ufs_module.c185
-rw-r--r--stand/efi/boot1/zfs_module.c248
-rw-r--r--stand/efi/fdt/Makefile30
-rw-r--r--stand/efi/fdt/Makefile.depend13
-rw-r--r--stand/efi/fdt/efi_fdt.c64
-rw-r--r--stand/efi/include/README36
-rw-r--r--stand/efi/include/amd64/efibind.h271
-rw-r--r--stand/efi/include/amd64/pe.h591
-rw-r--r--stand/efi/include/arm/efibind.h165
-rw-r--r--stand/efi/include/arm64/efibind.h217
-rw-r--r--stand/efi/include/efi.h63
-rw-r--r--stand/efi/include/efi_driver_utils.h38
-rw-r--r--stand/efi/include/efi_drivers.h45
-rw-r--r--stand/efi/include/efi_nii.h86
-rw-r--r--stand/efi/include/efiapi.h902
-rw-r--r--stand/efi/include/efichar.h36
-rw-r--r--stand/efi/include/eficon.h309
-rw-r--r--stand/efi/include/eficonsctl.h134
-rw-r--r--stand/efi/include/efidebug.h118
-rw-r--r--stand/efi/include/efidef.h206
-rw-r--r--stand/efi/include/efidevp.h454
-rw-r--r--stand/efi/include/efierr.h68
-rw-r--r--stand/efi/include/efifpswa.h40
-rw-r--r--stand/efi/include/efifs.h123
-rw-r--r--stand/efi/include/efigop.h121
-rw-r--r--stand/efi/include/efilib.h109
-rw-r--r--stand/efi/include/efinet.h348
-rw-r--r--stand/efi/include/efipart.h69
-rw-r--r--stand/efi/include/efipciio.h557
-rw-r--r--stand/efi/include/efiprot.h636
-rw-r--r--stand/efi/include/efipxebc.h472
-rw-r--r--stand/efi/include/efiser.h139
-rw-r--r--stand/efi/include/efistdarg.h39
-rw-r--r--stand/efi/include/efiuga.h168
-rw-r--r--stand/efi/include/efizfs.h54
-rw-r--r--stand/efi/include/i386/efibind.h267
-rw-r--r--stand/efi/include/i386/pe.h630
-rw-r--r--stand/efi/libefi/Makefile57
-rw-r--r--stand/efi/libefi/Makefile.depend13
-rw-r--r--stand/efi/libefi/delay.c47
-rw-r--r--stand/efi/libefi/devicename.c219
-rw-r--r--stand/efi/libefi/devpath.c197
-rw-r--r--stand/efi/libefi/efi_console.c518
-rw-r--r--stand/efi/libefi/efi_driver_utils.c91
-rw-r--r--stand/efi/libefi/efichar.c201
-rw-r--r--stand/efi/libefi/efinet.c390
-rw-r--r--stand/efi/libefi/efipart.c984
-rw-r--r--stand/efi/libefi/efizfs.c122
-rw-r--r--stand/efi/libefi/env.c534
-rw-r--r--stand/efi/libefi/errno.c157
-rw-r--r--stand/efi/libefi/handles.c118
-rw-r--r--stand/efi/libefi/libefi.c52
-rw-r--r--stand/efi/libefi/time.c283
-rw-r--r--stand/efi/libefi/time_event.c82
-rw-r--r--stand/efi/libefi/wchar.c73
-rw-r--r--stand/efi/loader/Makefile137
-rw-r--r--stand/efi/loader/Makefile.depend16
-rw-r--r--stand/efi/loader/arch/amd64/Makefile.inc15
-rw-r--r--stand/efi/loader/arch/amd64/amd64_tramp.S64
-rw-r--r--stand/efi/loader/arch/amd64/elf64_freebsd.c208
-rw-r--r--stand/efi/loader/arch/amd64/exc.S165
-rw-r--r--stand/efi/loader/arch/amd64/ldscript.amd6472
-rw-r--r--stand/efi/loader/arch/amd64/start.S76
-rw-r--r--stand/efi/loader/arch/amd64/trap.c408
-rw-r--r--stand/efi/loader/arch/arm/Makefile.inc6
-rw-r--r--stand/efi/loader/arch/arm/exec.c103
-rw-r--r--stand/efi/loader/arch/arm/ldscript.arm67
-rw-r--r--stand/efi/loader/arch/arm/start.S189
-rw-r--r--stand/efi/loader/arch/arm64/Makefile.inc12
-rw-r--r--stand/efi/loader/arch/arm64/exec.c144
-rw-r--r--stand/efi/loader/arch/arm64/ldscript.arm6485
-rw-r--r--stand/efi/loader/arch/arm64/start.S165
-rw-r--r--stand/efi/loader/arch/i386/Makefile.inc14
-rw-r--r--stand/efi/loader/arch/i386/bootinfo.c275
-rw-r--r--stand/efi/loader/arch/i386/efimd.c142
-rw-r--r--stand/efi/loader/arch/i386/elf32_freebsd.c100
-rw-r--r--stand/efi/loader/arch/i386/exec.c49
-rw-r--r--stand/efi/loader/arch/i386/i386_copy.c59
-rw-r--r--stand/efi/loader/arch/i386/ldscript.i38677
-rw-r--r--stand/efi/loader/arch/i386/start.S68
-rw-r--r--stand/efi/loader/autoload.c37
-rw-r--r--stand/efi/loader/bootinfo.c471
-rw-r--r--stand/efi/loader/conf.c83
-rw-r--r--stand/efi/loader/copy.c287
-rw-r--r--stand/efi/loader/efi_main.c188
-rw-r--r--stand/efi/loader/framebuffer.c568
-rw-r--r--stand/efi/loader/framebuffer.h36
-rw-r--r--stand/efi/loader/loader_efi.h47
-rw-r--r--stand/efi/loader/main.c936
-rw-r--r--stand/efi/loader/version7
-rw-r--r--stand/fdt.mk9
-rw-r--r--stand/fdt/Makefile28
-rw-r--r--stand/fdt/Makefile.depend14
-rw-r--r--stand/fdt/fdt_loader_cmd.c1796
-rw-r--r--stand/fdt/fdt_platform.h56
-rw-r--r--stand/fdt/help.fdt93
-rw-r--r--stand/ficl.mk23
-rw-r--r--stand/ficl/Makefile32
-rw-r--r--stand/ficl/Makefile.depend15
-rw-r--r--stand/ficl/aarch64/sysdep.c99
-rw-r--r--stand/ficl/aarch64/sysdep.h411
-rw-r--r--stand/ficl/amd64/sysdep.c99
-rw-r--r--stand/ficl/amd64/sysdep.h434
-rw-r--r--stand/ficl/arm/sysdep.c99
-rw-r--r--stand/ficl/arm/sysdep.h432
-rw-r--r--stand/ficl/dict.c864
-rw-r--r--stand/ficl/ficl.c696
-rw-r--r--stand/ficl/ficl.h1164
-rw-r--r--stand/ficl/fileaccess.c425
-rw-r--r--stand/ficl/float.c1067
-rw-r--r--stand/ficl/i386/sysdep.c149
-rw-r--r--stand/ficl/i386/sysdep.h432
-rw-r--r--stand/ficl/loader.c841
-rw-r--r--stand/ficl/math64.c561
-rw-r--r--stand/ficl/math64.h88
-rw-r--r--stand/ficl/mips/sysdep.c99
-rw-r--r--stand/ficl/mips/sysdep.h432
-rw-r--r--stand/ficl/mips64/sysdep.c99
-rw-r--r--stand/ficl/mips64/sysdep.h432
-rw-r--r--stand/ficl/powerpc/sysdep.c99
-rw-r--r--stand/ficl/powerpc/sysdep.h432
-rw-r--r--stand/ficl/prefix.c199
-rw-r--r--stand/ficl/riscv/sysdep.c99
-rw-r--r--stand/ficl/riscv/sysdep.h411
-rw-r--r--stand/ficl/search.c393
-rw-r--r--stand/ficl/softwords/classes.fr173
-rw-r--r--stand/ficl/softwords/ficlclass.fr86
-rw-r--r--stand/ficl/softwords/ficllocal.fr49
-rw-r--r--stand/ficl/softwords/fileaccess.fr25
-rw-r--r--stand/ficl/softwords/forml.fr75
-rw-r--r--stand/ficl/softwords/freebsd.fr36
-rw-r--r--stand/ficl/softwords/ifbrack.fr50
-rw-r--r--stand/ficl/softwords/jhlocal.fr105
-rw-r--r--stand/ficl/softwords/marker.fr27
-rw-r--r--stand/ficl/softwords/oo.fr694
-rw-r--r--stand/ficl/softwords/prefix.fr59
-rw-r--r--stand/ficl/softwords/softcore.awk183
-rw-r--r--stand/ficl/softwords/softcore.fr206
-rw-r--r--stand/ficl/softwords/string.fr148
-rw-r--r--stand/ficl/sparc64/sysdep.c99
-rw-r--r--stand/ficl/sparc64/sysdep.h412
-rw-r--r--stand/ficl/stack.c372
-rw-r--r--stand/ficl/testmain.c345
-rw-r--r--stand/ficl/tools.c918
-rw-r--r--stand/ficl/unix.c23
-rw-r--r--stand/ficl/vm.c805
-rw-r--r--stand/ficl/words.c5208
-rw-r--r--stand/ficl32/Makefile5
-rw-r--r--stand/ficl32/Makefile.depend15
-rw-r--r--stand/forth/Makefile49
-rw-r--r--stand/forth/Makefile.depend11
-rw-r--r--stand/forth/beastie.4th110
-rw-r--r--stand/forth/beastie.4th.8173
-rw-r--r--stand/forth/brand-fbsd.4th46
-rw-r--r--stand/forth/brand.4th74
-rw-r--r--stand/forth/brand.4th.8125
-rw-r--r--stand/forth/check-password.4th179
-rw-r--r--stand/forth/check-password.4th.8167
-rw-r--r--stand/forth/color.4th49
-rw-r--r--stand/forth/color.4th.8116
-rw-r--r--stand/forth/delay.4th119
-rw-r--r--stand/forth/delay.4th.8126
-rw-r--r--stand/forth/efi.4th30
-rw-r--r--stand/forth/frames.4th165
-rw-r--r--stand/forth/loader.4th266
-rw-r--r--stand/forth/loader.4th.8233
-rw-r--r--stand/forth/loader.conf575
-rw-r--r--stand/forth/loader.conf.5330
-rw-r--r--stand/forth/loader.rc23
-rw-r--r--stand/forth/logo-beastie.4th61
-rw-r--r--stand/forth/logo-beastiebw.4th59
-rw-r--r--stand/forth/logo-fbsdbw.4th53
-rw-r--r--stand/forth/logo-orb.4th55
-rw-r--r--stand/forth/logo-orbbw.4th54
-rw-r--r--stand/forth/menu-commands.4th418
-rw-r--r--stand/forth/menu.4th1319
-rw-r--r--stand/forth/menu.4th.8352
-rw-r--r--stand/forth/menu.rc202
-rw-r--r--stand/forth/menusets.4th624
-rw-r--r--stand/forth/menusets.4th.8372
-rw-r--r--stand/forth/pcibios.4th47
-rw-r--r--stand/forth/pnp.4th205
-rw-r--r--stand/forth/screen.4th74
-rw-r--r--stand/forth/shortcuts.4th50
-rw-r--r--stand/forth/support.4th1606
-rw-r--r--stand/forth/version.4th96
-rw-r--r--stand/forth/version.4th.8128
-rw-r--r--stand/geli/Makefile56
-rw-r--r--stand/geli/Makefile.depend16
-rw-r--r--stand/geli/geliboot.c437
-rw-r--r--stand/geli/geliboot.h69
-rw-r--r--stand/geli/geliboot_crypto.c140
-rw-r--r--stand/geli/geliboot_internal.h69
-rw-r--r--stand/geli/pwgets.c79
-rw-r--r--stand/i386/Makefile25
-rw-r--r--stand/i386/Makefile.inc36
-rw-r--r--stand/i386/boot.ldscript11
-rw-r--r--stand/i386/boot0/Makefile80
-rw-r--r--stand/i386/boot0/Makefile.depend11
-rw-r--r--stand/i386/boot0/boot0.S682
-rw-r--r--stand/i386/boot0sio/Makefile8
-rw-r--r--stand/i386/boot0sio/Makefile.depend11
-rw-r--r--stand/i386/boot2/Makefile99
-rw-r--r--stand/i386/boot2/Makefile.depend14
-rw-r--r--stand/i386/boot2/boot1.S373
-rw-r--r--stand/i386/boot2/boot2.c646
-rw-r--r--stand/i386/boot2/lib.h24
-rw-r--r--stand/i386/boot2/sio.S84
-rw-r--r--stand/i386/btx/Makefile5
-rw-r--r--stand/i386/btx/Makefile.inc3
-rw-r--r--stand/i386/btx/btx/Makefile35
-rw-r--r--stand/i386/btx/btx/Makefile.depend11
-rw-r--r--stand/i386/btx/btx/btx.S1082
-rw-r--r--stand/i386/btx/btxldr/Makefile23
-rw-r--r--stand/i386/btx/btxldr/Makefile.depend11
-rw-r--r--stand/i386/btx/btxldr/btxldr.S409
-rw-r--r--stand/i386/btx/lib/Makefile12
-rw-r--r--stand/i386/btx/lib/Makefile.depend11
-rw-r--r--stand/i386/btx/lib/btxcsu.S49
-rw-r--r--stand/i386/btx/lib/btxsys.s40
-rw-r--r--stand/i386/btx/lib/btxv86.h75
-rw-r--r--stand/i386/btx/lib/btxv86.s85
-rw-r--r--stand/i386/cdboot/Makefile20
-rw-r--r--stand/i386/cdboot/Makefile.depend11
-rw-r--r--stand/i386/cdboot/cdboot.S594
-rw-r--r--stand/i386/common/bootargs.h93
-rw-r--r--stand/i386/common/cons.c177
-rw-r--r--stand/i386/common/cons.h35
-rw-r--r--stand/i386/common/drv.c102
-rw-r--r--stand/i386/common/drv.h48
-rw-r--r--stand/i386/common/edd.h110
-rw-r--r--stand/i386/gptboot/Makefile75
-rw-r--r--stand/i386/gptboot/Makefile.depend19
-rw-r--r--stand/i386/gptboot/gptboot.8245
-rw-r--r--stand/i386/gptboot/gptboot.c648
-rw-r--r--stand/i386/gptboot/gptldr.S142
-rw-r--r--stand/i386/gptzfsboot/Makefile87
-rw-r--r--stand/i386/gptzfsboot/Makefile.depend19
-rw-r--r--stand/i386/gptzfsboot/gptzfsboot.8193
-rw-r--r--stand/i386/kgzldr/Makefile21
-rw-r--r--stand/i386/kgzldr/Makefile.depend12
-rw-r--r--stand/i386/kgzldr/boot.c129
-rw-r--r--stand/i386/kgzldr/crt.s83
-rw-r--r--stand/i386/kgzldr/kgzldr.h41
-rw-r--r--stand/i386/kgzldr/lib.c88
-rw-r--r--stand/i386/kgzldr/sio.s44
-rw-r--r--stand/i386/kgzldr/start.s45
-rw-r--r--stand/i386/libfirewire/Makefile20
-rw-r--r--stand/i386/libfirewire/Makefile.depend13
-rw-r--r--stand/i386/libfirewire/dconsole.c127
-rw-r--r--stand/i386/libfirewire/firewire.c484
-rw-r--r--stand/i386/libfirewire/fwohci.c479
-rw-r--r--stand/i386/libfirewire/fwohci.h162
-rw-r--r--stand/i386/libfirewire/fwohcireg.h369
-rw-r--r--stand/i386/libi386/Makefile61
-rw-r--r--stand/i386/libi386/Makefile.depend14
-rw-r--r--stand/i386/libi386/amd64_tramp.S113
-rw-r--r--stand/i386/libi386/biosacpi.c144
-rw-r--r--stand/i386/libi386/bioscd.c452
-rw-r--r--stand/i386/libi386/biosdisk.c1013
-rw-r--r--stand/i386/libi386/biosmem.c258
-rw-r--r--stand/i386/libi386/biospci.c588
-rw-r--r--stand/i386/libi386/biospnp.c292
-rw-r--r--stand/i386/libi386/biossmap.c159
-rw-r--r--stand/i386/libi386/bootinfo.c170
-rw-r--r--stand/i386/libi386/bootinfo32.c291
-rw-r--r--stand/i386/libi386/bootinfo64.c280
-rw-r--r--stand/i386/libi386/comconsole.c385
-rw-r--r--stand/i386/libi386/devicename.c206
-rw-r--r--stand/i386/libi386/elf32_freebsd.c84
-rw-r--r--stand/i386/libi386/elf64_freebsd.c126
-rw-r--r--stand/i386/libi386/i386_copy.c75
-rw-r--r--stand/i386/libi386/i386_module.c44
-rw-r--r--stand/i386/libi386/libi386.h156
-rw-r--r--stand/i386/libi386/multiboot.c467
-rw-r--r--stand/i386/libi386/multiboot.h225
-rw-r--r--stand/i386/libi386/multiboot_tramp.S51
-rw-r--r--stand/i386/libi386/nullconsole.c88
-rw-r--r--stand/i386/libi386/pread.c80
-rw-r--r--stand/i386/libi386/pxe.c540
-rw-r--r--stand/i386/libi386/pxe.h513
-rw-r--r--stand/i386/libi386/pxetramp.s38
-rw-r--r--stand/i386/libi386/relocater_tramp.S358
-rw-r--r--stand/i386/libi386/smbios.c454
-rw-r--r--stand/i386/libi386/smbios.h34
-rw-r--r--stand/i386/libi386/spinconsole.c112
-rw-r--r--stand/i386/libi386/time.c118
-rw-r--r--stand/i386/libi386/vidconsole.c632
-rw-r--r--stand/i386/loader/Makefile89
-rw-r--r--stand/i386/loader/Makefile.depend21
-rw-r--r--stand/i386/loader/chain.c134
-rw-r--r--stand/i386/loader/conf.c159
-rw-r--r--stand/i386/loader/help.i38654
-rw-r--r--stand/i386/loader/loader.rc18
-rw-r--r--stand/i386/loader/main.c477
-rw-r--r--stand/i386/loader/version14
-rw-r--r--stand/i386/mbr/Makefile17
-rw-r--r--stand/i386/mbr/Makefile.depend11
-rw-r--r--stand/i386/mbr/mbr.s157
-rw-r--r--stand/i386/pmbr/Makefile14
-rw-r--r--stand/i386/pmbr/Makefile.depend11
-rw-r--r--stand/i386/pmbr/pmbr.s252
-rw-r--r--stand/i386/pxeldr/Makefile47
-rw-r--r--stand/i386/pxeldr/Makefile.depend15
-rw-r--r--stand/i386/pxeldr/pxeboot.8158
-rw-r--r--stand/i386/pxeldr/pxeldr.S301
-rw-r--r--stand/i386/zfsboot/Makefile93
-rw-r--r--stand/i386/zfsboot/Makefile.depend16
-rw-r--r--stand/i386/zfsboot/zfsboot.8133
-rw-r--r--stand/i386/zfsboot/zfsboot.c1151
-rw-r--r--stand/i386/zfsboot/zfsldr.S283
-rw-r--r--stand/i386/zfsloader/Makefile7
-rw-r--r--stand/i386/zfsloader/Makefile.depend22
-rw-r--r--stand/kshim/bsd_busspace.c216
-rw-r--r--stand/kshim/bsd_global.h68
-rw-r--r--stand/kshim/bsd_kernel.c1459
-rw-r--r--stand/kshim/bsd_kernel.h647
-rw-r--r--stand/kshim/kshim.mk79
-rw-r--r--stand/kshim/sysinit.h57
-rw-r--r--stand/libsa/Makefile160
-rw-r--r--stand/libsa/Makefile.depend15
-rw-r--r--stand/libsa/__main.c43
-rw-r--r--stand/libsa/amd64/_setjmp.S93
-rw-r--r--stand/libsa/arp.c305
-rw-r--r--stand/libsa/assert.c44
-rw-r--r--stand/libsa/bcd.c38
-rw-r--r--stand/libsa/bootp.c791
-rw-r--r--stand/libsa/bootp.h151
-rw-r--r--stand/libsa/bootparam.c435
-rw-r--r--stand/libsa/bootparam.h5
-rw-r--r--stand/libsa/bzipfs.c388
-rw-r--r--stand/libsa/cd9660.c600
-rw-r--r--stand/libsa/close.c98
-rw-r--r--stand/libsa/closeall.c76
-rw-r--r--stand/libsa/crc32.c108
-rw-r--r--stand/libsa/crc32.h13
-rw-r--r--stand/libsa/dev.c61
-rw-r--r--stand/libsa/dosfs.c868
-rw-r--r--stand/libsa/dosfs.h123
-rw-r--r--stand/libsa/environment.c223
-rw-r--r--stand/libsa/ether.c147
-rw-r--r--stand/libsa/ext2fs.c908
-rw-r--r--stand/libsa/fstat.c61
-rw-r--r--stand/libsa/getopt.c108
-rw-r--r--stand/libsa/gets.c112
-rw-r--r--stand/libsa/globals.c38
-rw-r--r--stand/libsa/gpt.c379
-rw-r--r--stand/libsa/gpt.h41
-rw-r--r--stand/libsa/gzipfs.c337
-rw-r--r--stand/libsa/i386/_setjmp.S77
-rw-r--r--stand/libsa/in_cksum.c94
-rw-r--r--stand/libsa/inet_ntoa.c64
-rw-r--r--stand/libsa/ioctl.c88
-rw-r--r--stand/libsa/iodesc.h52
-rw-r--r--stand/libsa/ip.c423
-rw-r--r--stand/libsa/libstand.3676
-rw-r--r--stand/libsa/lseek.c141
-rw-r--r--stand/libsa/mips/_setjmp.S108
-rw-r--r--stand/libsa/nandfs.c1061
-rw-r--r--stand/libsa/net.c283
-rw-r--r--stand/libsa/net.h133
-rw-r--r--stand/libsa/netif.c316
-rw-r--r--stand/libsa/netif.h65
-rw-r--r--stand/libsa/nfs.c860
-rw-r--r--stand/libsa/nfsv2.h121
-rw-r--r--stand/libsa/nullfs.c105
-rw-r--r--stand/libsa/open.c159
-rw-r--r--stand/libsa/pager.c161
-rw-r--r--stand/libsa/panic.c59
-rw-r--r--stand/libsa/pkgfs.c791
-rw-r--r--stand/libsa/powerpc/_setjmp.S115
-rw-r--r--stand/libsa/powerpc/syncicache.c103
-rw-r--r--stand/libsa/printf.c518
-rw-r--r--stand/libsa/qdivrem.c348
-rw-r--r--stand/libsa/quad.h114
-rw-r--r--stand/libsa/random.c70
-rw-r--r--stand/libsa/rarp.c218
-rw-r--r--stand/libsa/read.c127
-rw-r--r--stand/libsa/readdir.c51
-rw-r--r--stand/libsa/rpc.c433
-rw-r--r--stand/libsa/rpc.h66
-rw-r--r--stand/libsa/rpcv2.h87
-rw-r--r--stand/libsa/saioctl.h50
-rw-r--r--stand/libsa/sbrk.c64
-rw-r--r--stand/libsa/sparc64/_setjmp.S94
-rw-r--r--stand/libsa/splitfs.c313
-rw-r--r--stand/libsa/stand.h426
-rw-r--r--stand/libsa/stat.c52
-rw-r--r--stand/libsa/strcasecmp.c73
-rw-r--r--stand/libsa/strdup.c55
-rw-r--r--stand/libsa/strerror.c87
-rw-r--r--stand/libsa/strtol.c132
-rw-r--r--stand/libsa/strtoul.c121
-rw-r--r--stand/libsa/tftp.c785
-rw-r--r--stand/libsa/tftp.h36
-rw-r--r--stand/libsa/twiddle.c69
-rw-r--r--stand/libsa/udp.c180
-rw-r--r--stand/libsa/ufs.c861
-rw-r--r--stand/libsa/ufsread.c326
-rw-r--r--stand/libsa/util.c182
-rw-r--r--stand/libsa/util.h53
-rw-r--r--stand/libsa/uuid_from_string.c132
-rw-r--r--stand/libsa/uuid_to_string.c111
-rw-r--r--stand/libsa/write.c95
-rw-r--r--stand/libsa/zalloc.c316
-rw-r--r--stand/libsa/zalloc_defs.h78
-rw-r--r--stand/libsa/zalloc_malloc.c200
-rw-r--r--stand/libsa/zalloc_mem.h53
-rw-r--r--stand/libsa/zalloc_protos.h35
-rw-r--r--stand/libsa32/Makefile11
-rw-r--r--stand/libsa32/Makefile.depend15
-rw-r--r--stand/loader.mk102
-rw-r--r--stand/man/Makefile10
-rw-r--r--stand/man/loader.81101
-rw-r--r--stand/man/zfsloader.8106
-rw-r--r--stand/mips/Makefile14
-rw-r--r--stand/mips/Makefile.inc3
-rw-r--r--stand/mips/beri/Makefile5
-rw-r--r--stand/mips/beri/Makefile.inc6
-rw-r--r--stand/mips/beri/boot2/Makefile86
-rw-r--r--stand/mips/beri/boot2/boot2.c661
-rw-r--r--stand/mips/beri/boot2/flashboot.ldscript65
-rw-r--r--stand/mips/beri/boot2/jtagboot.ldscript64
-rw-r--r--stand/mips/beri/boot2/relocate.S103
-rw-r--r--stand/mips/beri/boot2/start.S82
-rw-r--r--stand/mips/beri/common/altera_jtag_uart.c182
-rw-r--r--stand/mips/beri/common/beri.h42
-rw-r--r--stand/mips/beri/common/cfi.c75
-rw-r--r--stand/mips/beri/common/cfi.h40
-rw-r--r--stand/mips/beri/common/common.ldscript76
-rw-r--r--stand/mips/beri/common/cons.h40
-rw-r--r--stand/mips/beri/common/mips.h156
-rw-r--r--stand/mips/beri/common/sdcard.c334
-rw-r--r--stand/mips/beri/common/sdcard.h41
-rw-r--r--stand/mips/beri/loader/Makefile111
-rw-r--r--stand/mips/beri/loader/arch.c97
-rw-r--r--stand/mips/beri/loader/beri_console.c90
-rw-r--r--stand/mips/beri/loader/beri_disk_cfi.c141
-rw-r--r--stand/mips/beri/loader/beri_disk_sdcard.c147
-rw-r--r--stand/mips/beri/loader/devicename.c205
-rw-r--r--stand/mips/beri/loader/exec.c125
-rw-r--r--stand/mips/beri/loader/help.mips1
-rw-r--r--stand/mips/beri/loader/loader.h63
-rw-r--r--stand/mips/beri/loader/loader.ldscript84
-rw-r--r--stand/mips/beri/loader/main.c244
-rw-r--r--stand/mips/beri/loader/metadata.c355
-rw-r--r--stand/mips/beri/loader/start.S49
-rw-r--r--stand/mips/beri/loader/version6
-rw-r--r--stand/mips/uboot/Makefile57
-rw-r--r--stand/mips/uboot/conf.c118
-rw-r--r--stand/mips/uboot/help.uboot27
-rw-r--r--stand/mips/uboot/ldscript.mips134
-rw-r--r--stand/mips/uboot/loader.conf13
-rw-r--r--stand/mips/uboot/start.S71
-rw-r--r--stand/mips/uboot/version9
-rw-r--r--stand/ofw/Makefile5
-rw-r--r--stand/ofw/Makefile.inc3
-rw-r--r--stand/ofw/common/Makefile.inc3
-rw-r--r--stand/ofw/common/main.c185
-rw-r--r--stand/ofw/libofw/Makefile28
-rw-r--r--stand/ofw/libofw/devicename.c147
-rw-r--r--stand/ofw/libofw/elf_freebsd.c104
-rw-r--r--stand/ofw/libofw/libofw.h90
-rw-r--r--stand/ofw/libofw/ofw_console.c120
-rw-r--r--stand/ofw/libofw/ofw_copy.c173
-rw-r--r--stand/ofw/libofw/ofw_disk.c184
-rw-r--r--stand/ofw/libofw/ofw_memory.c146
-rw-r--r--stand/ofw/libofw/ofw_module.c49
-rw-r--r--stand/ofw/libofw/ofw_net.c275
-rw-r--r--stand/ofw/libofw/ofw_reboot.c37
-rw-r--r--stand/ofw/libofw/ofw_time.c61
-rw-r--r--stand/ofw/libofw/openfirm.c832
-rw-r--r--stand/ofw/libofw/openfirm.h124
-rw-r--r--stand/ofw/libofw/ppc64_elf_freebsd.c110
-rw-r--r--stand/pc98/Makefile5
-rw-r--r--stand/pc98/Makefile.inc29
-rw-r--r--stand/pc98/boot0.5/Makefile26
-rw-r--r--stand/pc98/boot0.5/boot.s174
-rw-r--r--stand/pc98/boot0.5/boot0.5.s293
-rw-r--r--stand/pc98/boot0.5/disk.s296
-rw-r--r--stand/pc98/boot0.5/ldscript12
-rw-r--r--stand/pc98/boot0.5/putssjis.s137
-rw-r--r--stand/pc98/boot0.5/selector.s450
-rw-r--r--stand/pc98/boot0.5/start.s73
-rw-r--r--stand/pc98/boot0.5/support.s94
-rw-r--r--stand/pc98/boot0.5/syscons.s253
-rw-r--r--stand/pc98/boot0/Makefile19
-rw-r--r--stand/pc98/boot0/boot0.s108
-rw-r--r--stand/pc98/boot2/Makefile116
-rw-r--r--stand/pc98/boot2/boot1.S395
-rw-r--r--stand/pc98/boot2/boot2.c803
-rw-r--r--stand/pc98/btx/Makefile5
-rw-r--r--stand/pc98/btx/Makefile.inc3
-rw-r--r--stand/pc98/btx/btx/Makefile33
-rw-r--r--stand/pc98/btx/btx/btx.S1104
-rw-r--r--stand/pc98/btx/btxldr/Makefile21
-rw-r--r--stand/pc98/btx/btxldr/btxldr.S430
-rw-r--r--stand/pc98/btx/lib/Makefile10
-rw-r--r--stand/pc98/btx/lib/btxcsu.S49
-rw-r--r--stand/pc98/btx/lib/btxsys.s40
-rw-r--r--stand/pc98/btx/lib/btxv86.h67
-rw-r--r--stand/pc98/btx/lib/btxv86.s85
-rw-r--r--stand/pc98/cdboot/Makefile18
-rw-r--r--stand/pc98/cdboot/cdboot.S805
-rw-r--r--stand/pc98/kgzldr/Makefile20
-rw-r--r--stand/pc98/kgzldr/crt.s89
-rw-r--r--stand/pc98/libpc98/Makefile51
-rw-r--r--stand/pc98/libpc98/bioscd.c420
-rw-r--r--stand/pc98/libpc98/biosdisk.c1120
-rw-r--r--stand/pc98/libpc98/biosmem.c64
-rw-r--r--stand/pc98/libpc98/biossmap.c38
-rw-r--r--stand/pc98/libpc98/comconsole.c367
-rw-r--r--stand/pc98/libpc98/libpc98.h29
-rw-r--r--stand/pc98/libpc98/pc98_sys.c78
-rw-r--r--stand/pc98/libpc98/time.c98
-rw-r--r--stand/pc98/libpc98/vidconsole.c596
-rw-r--r--stand/pc98/loader/Makefile99
-rw-r--r--stand/pc98/loader/conf.c116
-rw-r--r--stand/pc98/loader/help.pc9838
-rw-r--r--stand/pc98/loader/main.c322
-rw-r--r--stand/pc98/pc98boot/Makefile25
-rw-r--r--stand/powerpc/Makefile13
-rw-r--r--stand/powerpc/Makefile.inc3
-rw-r--r--stand/powerpc/boot1.chrp/Makefile42
-rw-r--r--stand/powerpc/boot1.chrp/Makefile.hfs4
-rw-r--r--stand/powerpc/boot1.chrp/boot1.c777
-rw-r--r--stand/powerpc/boot1.chrp/bootinfo.txt14
-rwxr-xr-xstand/powerpc/boot1.chrp/generate-hfs.sh64
-rw-r--r--stand/powerpc/boot1.chrp/hfs.tmpl.bz2.uu18
-rw-r--r--stand/powerpc/kboot/Makefile50
-rw-r--r--stand/powerpc/kboot/conf.c117
-rw-r--r--stand/powerpc/kboot/host_syscall.S75
-rw-r--r--stand/powerpc/kboot/host_syscall.h51
-rw-r--r--stand/powerpc/kboot/hostcons.c97
-rw-r--r--stand/powerpc/kboot/hostdisk.c125
-rw-r--r--stand/powerpc/kboot/kbootfdt.c184
-rw-r--r--stand/powerpc/kboot/kerneltramp.S55
-rw-r--r--stand/powerpc/kboot/ldscript.powerpc111
-rw-r--r--stand/powerpc/kboot/main.c319
-rw-r--r--stand/powerpc/kboot/metadata.c343
-rw-r--r--stand/powerpc/kboot/ppc64_elf_freebsd.c124
-rw-r--r--stand/powerpc/kboot/version6
-rw-r--r--stand/powerpc/ofw/Makefile55
-rw-r--r--stand/powerpc/ofw/conf.c122
-rw-r--r--stand/powerpc/ofw/ldscript.powerpc138
-rw-r--r--stand/powerpc/ofw/metadata.c349
-rw-r--r--stand/powerpc/ofw/ofwfdt.c219
-rw-r--r--stand/powerpc/ofw/start.c74
-rw-r--r--stand/powerpc/ofw/version6
-rw-r--r--stand/powerpc/ps3/Makefile48
-rw-r--r--stand/powerpc/ps3/conf.c123
-rw-r--r--stand/powerpc/ps3/devicename.c238
-rw-r--r--stand/powerpc/ps3/ldscript.powerpc111
-rw-r--r--stand/powerpc/ps3/lv1call.S346
-rw-r--r--stand/powerpc/ps3/lv1call.h80
-rw-r--r--stand/powerpc/ps3/main.c248
-rw-r--r--stand/powerpc/ps3/metadata.c333
-rw-r--r--stand/powerpc/ps3/ppc64_elf_freebsd.c101
-rw-r--r--stand/powerpc/ps3/ps3.h35
-rw-r--r--stand/powerpc/ps3/ps3bus.h41
-rw-r--r--stand/powerpc/ps3/ps3cdrom.c156
-rw-r--r--stand/powerpc/ps3/ps3cons.c173
-rw-r--r--stand/powerpc/ps3/ps3devdesc.h53
-rw-r--r--stand/powerpc/ps3/ps3disk.c315
-rw-r--r--stand/powerpc/ps3/ps3mmu.c120
-rw-r--r--stand/powerpc/ps3/ps3net.c278
-rw-r--r--stand/powerpc/ps3/ps3repo.c249
-rw-r--r--stand/powerpc/ps3/ps3repo.h51
-rw-r--r--stand/powerpc/ps3/ps3stor.c176
-rw-r--r--stand/powerpc/ps3/ps3stor.h59
-rw-r--r--stand/powerpc/ps3/start.S169
-rw-r--r--stand/powerpc/ps3/version8
-rw-r--r--stand/powerpc/uboot/Makefile37
-rw-r--r--stand/powerpc/uboot/conf.c112
-rw-r--r--stand/powerpc/uboot/ldscript.powerpc138
-rw-r--r--stand/powerpc/uboot/start.S94
-rw-r--r--stand/powerpc/uboot/version11
-rw-r--r--stand/sparc64/Makefile10
-rw-r--r--stand/sparc64/Makefile.inc6
-rw-r--r--stand/sparc64/boot1/Makefile32
-rw-r--r--stand/sparc64/boot1/_start.s8
-rw-r--r--stand/sparc64/boot1/boot1.c751
-rw-r--r--stand/sparc64/loader/Makefile50
-rw-r--r--stand/sparc64/loader/help.sparc640
-rw-r--r--stand/sparc64/loader/locore.S42
-rw-r--r--stand/sparc64/loader/main.c1000
-rw-r--r--stand/sparc64/loader/metadata.c345
-rw-r--r--stand/sparc64/loader/version6
-rw-r--r--stand/sparc64/zfsboot/Makefile9
-rw-r--r--stand/sparc64/zfsloader/Makefile7
-rw-r--r--stand/uboot.mk18
-rw-r--r--stand/uboot/Makefile11
-rw-r--r--stand/uboot/Makefile.inc3
-rw-r--r--stand/uboot/common/main.c680
-rw-r--r--stand/uboot/common/metadata.c366
-rw-r--r--stand/uboot/fdt/Makefile25
-rw-r--r--stand/uboot/fdt/uboot_fdt.c191
-rw-r--r--stand/uboot/lib/Makefile31
-rw-r--r--stand/uboot/lib/api_public.h160
-rw-r--r--stand/uboot/lib/console.c89
-rw-r--r--stand/uboot/lib/copy.c166
-rw-r--r--stand/uboot/lib/devicename.c201
-rw-r--r--stand/uboot/lib/disk.c320
-rw-r--r--stand/uboot/lib/elf_freebsd.c100
-rw-r--r--stand/uboot/lib/glue.c564
-rw-r--r--stand/uboot/lib/glue.h107
-rw-r--r--stand/uboot/lib/libuboot.h84
-rw-r--r--stand/uboot/lib/module.c57
-rw-r--r--stand/uboot/lib/net.c364
-rw-r--r--stand/uboot/lib/reboot.c38
-rw-r--r--stand/uboot/lib/time.c65
-rw-r--r--stand/usb/Makefile58
-rw-r--r--stand/usb/Makefile.test61
-rw-r--r--stand/usb/bsd_usbloader_test.c101
-rw-r--r--stand/usb/storage/umass_common.c90
-rw-r--r--stand/usb/storage/umass_common.h41
-rw-r--r--stand/usb/storage/umass_loader.c239
-rw-r--r--stand/usb/tools/Makefile10
-rw-r--r--stand/usb/tools/sysinit.c331
-rw-r--r--stand/usb/usb_busdma_loader.c619
-rw-r--r--stand/usb/usbcore.mk175
-rw-r--r--stand/userboot/Makefile8
-rw-r--r--stand/userboot/Makefile.inc3
-rw-r--r--stand/userboot/test/Makefile14
-rw-r--r--stand/userboot/test/Makefile.depend18
-rw-r--r--stand/userboot/test/test.c475
-rw-r--r--stand/userboot/userboot.h213
-rw-r--r--stand/userboot/userboot/Makefile59
-rw-r--r--stand/userboot/userboot/Makefile.depend16
-rw-r--r--stand/userboot/userboot/autoload.c35
-rw-r--r--stand/userboot/userboot/biossmap.c74
-rw-r--r--stand/userboot/userboot/bootinfo.c170
-rw-r--r--stand/userboot/userboot/bootinfo32.c262
-rw-r--r--stand/userboot/userboot/bootinfo64.c257
-rw-r--r--stand/userboot/userboot/conf.c107
-rw-r--r--stand/userboot/userboot/copy.c73
-rw-r--r--stand/userboot/userboot/devicename.c224
-rw-r--r--stand/userboot/userboot/elf32_freebsd.c114
-rw-r--r--stand/userboot/userboot/elf64_freebsd.c172
-rw-r--r--stand/userboot/userboot/host.c202
-rw-r--r--stand/userboot/userboot/libuserboot.h68
-rw-r--r--stand/userboot/userboot/main.c304
-rw-r--r--stand/userboot/userboot/userboot_cons.c130
-rw-r--r--stand/userboot/userboot/userboot_disk.c242
-rw-r--r--stand/userboot/userboot/version4
-rw-r--r--stand/zfs/Makefile22
-rw-r--r--stand/zfs/Makefile.depend13
-rw-r--r--stand/zfs/devicename_stubs.c47
-rw-r--r--stand/zfs/libzfs.h93
-rw-r--r--stand/zfs/zfs.c956
-rw-r--r--stand/zfs/zfsimpl.c2536
-rw-r--r--stand/zfs32/Makefile5
727 files changed, 145691 insertions, 0 deletions
diff --git a/stand/Makefile b/stand/Makefile
new file mode 100644
index 0000000..5234189
--- /dev/null
+++ b/stand/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+SUBDIR+= libsa
+.if ${MK_FORTH} != "no"
+# Build the add-in FORTH interpreter.
+SUBDIR+= ficl
+SUBDIR+= forth
+.endif
+
+SUBDIR+= man
+
+.include <bsd.arch.inc.mk>
+
+.if exists(${.CURDIR}/${MACHINE}/.)
+SUBDIR+= ${MACHINE}
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/stand/Makefile.amd64 b/stand/Makefile.amd64
new file mode 100644
index 0000000..abecaa6
--- /dev/null
+++ b/stand/Makefile.amd64
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+SUBDIR+= libsa32
+.if ${MK_ZFS} != "no"
+SUBDIR+= zfs zfs32
+.endif
+.if ${MK_FORTH} != "no"
+SUBDIR+= ficl32
+.endif
+
+SUBDIR+= efi
+SUBDIR+= userboot
+
+.if ${LOADER_GELI_SUPPORT:Uyes} == "yes"
+SUBDIR+= geli
+.endif
+
+SUBDIR+= i386
diff --git a/stand/Makefile.arm b/stand/Makefile.arm
new file mode 100644
index 0000000..387b77b
--- /dev/null
+++ b/stand/Makefile.arm
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.if ${MK_FDT} != "no"
+SUBDIR+= fdt
+.endif
+.if ${MK_ZFS} != "no"
+SUBDIR+= zfs
+.endif
+
+SUBDIR+= efi uboot
diff --git a/stand/Makefile.arm64 b/stand/Makefile.arm64
new file mode 100644
index 0000000..bf0fd39
--- /dev/null
+++ b/stand/Makefile.arm64
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.if ${MK_FDT} != "no"
+SUBDIR+= fdt
+.endif
+.if ${MK_ZFS} != "no"
+SUBDIR+= zfs
+.endif
+
+SUBDIR+= efi
diff --git a/stand/Makefile.i386 b/stand/Makefile.i386
new file mode 100644
index 0000000..fb53744
--- /dev/null
+++ b/stand/Makefile.i386
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.if ${LOADER_GELI_SUPPORT:Uyes} == "yes"
+SUBDIR+= geli
+.endif
+.if ${MK_ZFS} != "no"
+SUBDIR+= zfs
+.endif
+
+SUBDIR+= efi
diff --git a/stand/Makefile.inc b/stand/Makefile.inc
new file mode 100644
index 0000000..cf833d9
--- /dev/null
+++ b/stand/Makefile.inc
@@ -0,0 +1,37 @@
+# $FreeBSD$
+
+.include "defs.mk"
+
+.if !defined(__BOOT_MAKEFILE_INC__)
+__BOOT_MAKEFILE_INC__=${MFILE}
+
+CFLAGS+=-I${SASRC}
+
+SSP_CFLAGS=
+
+.if ${MACHINE_CPUARCH} == "arm"
+# Do not generate movt/movw, because the relocation fixup for them does not
+# translate to the -Bsymbolic -pie format required by self_reloc() in loader(8).
+# Also, the fpu is not available in a standalone environment.
+.if ${COMPILER_VERSION} < 30800
+CFLAGS.clang+= -mllvm -arm-use-movt=0
+.else
+CFLAGS.clang+= -mno-movt
+.endif
+CFLAGS.clang+= -mfpu=none
+.endif
+
+# The boot loader build uses dd status=none, where possible, for reproducible
+# build output (since performance varies from run to run). Trouble is that
+# option was recently (10.3) added to FreeBSD and is non-standard. Only use it
+# when this test succeeds rather than require dd to be a bootstrap tool.
+DD_NOSTATUS!=(dd status=none count=0 2> /dev/null && echo status=none) || true
+DD=dd ${DD_NOSTATUS}
+
+.if ${MK_LOADER_FORCE_LE} != "no"
+.if ${MACHINE_ARCH} == "powerpc64"
+CFLAGS+= -mlittle-endian
+.endif
+.endif
+
+.endif
diff --git a/stand/Makefile.mips b/stand/Makefile.mips
new file mode 100644
index 0000000..46fc574
--- /dev/null
+++ b/stand/Makefile.mips
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.if ${MK_FDT} != "no"
+SUBDIR+= fdt
+.endif
+
+SUBDIR+= uboot
diff --git a/stand/Makefile.pc98 b/stand/Makefile.pc98
new file mode 100644
index 0000000..8468399
--- /dev/null
+++ b/stand/Makefile.pc98
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+SUBDIR+= libstand32
diff --git a/stand/Makefile.powerpc b/stand/Makefile.powerpc
new file mode 100644
index 0000000..b7660f4
--- /dev/null
+++ b/stand/Makefile.powerpc
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.if ${MK_FDT} != "no"
+SUBDIR+= fdt
+.endif
+
+SUBDIR+= ofw
+SUBDIR+= uboot
diff --git a/stand/Makefile.sparc64 b/stand/Makefile.sparc64
new file mode 100644
index 0000000..40b42e9
--- /dev/null
+++ b/stand/Makefile.sparc64
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SUBDIR+= ofw
+.if ${MK_ZFS} != "no"
+SUBDIR+= zfs
+.endif
diff --git a/stand/arm/Makefile b/stand/arm/Makefile
new file mode 100644
index 0000000..1d12d98
--- /dev/null
+++ b/stand/arm/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= uboot
+
+.include <bsd.subdir.mk>
diff --git a/stand/arm/Makefile.inc b/stand/arm/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/stand/arm/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/stand/arm/loader/loader.conf b/stand/arm/loader/loader.conf
new file mode 100644
index 0000000..dd2a23a
--- /dev/null
+++ b/stand/arm/loader/loader.conf
@@ -0,0 +1,13 @@
+# This is defaults/loader.conf for ARM, containing defaults for loader(8).
+# Do not modify the contents of this file, instead put your customizations
+# into /boot/loader.conf or /boot/loader.conf.local
+# $FreeBSD$
+
+autoboot_delay=10
+bootfile="kernel" # Kernel name (possibly absolute path)
+kernel="kernel" # /boot sub-directory containing kernel and modules
+loader_conf_files="/boot/loader.conf /boot/loader.conf.local"
+module_path="/boot/kernel;/boot/modules;/boot/dtb;/boot/overlays"
+nextboot_conf="/boot/nextboot.conf"
+nextboot_enable="NO"
+verbose_loading="NO"
diff --git a/stand/arm/uboot/Makefile b/stand/arm/uboot/Makefile
new file mode 100644
index 0000000..5bf7d53
--- /dev/null
+++ b/stand/arm/uboot/Makefile
@@ -0,0 +1,70 @@
+# $FreeBSD$
+
+LOADER_UFS_SUPPORT?= yes
+LOADER_CD9660_SUPPORT?= no
+LOADER_MSDOS_SUPPORT?= no
+LOADER_EXT2FS_SUPPORT?= no
+LOADER_NET_SUPPORT?= yes
+LOADER_NFS_SUPPORT?= yes
+LOADER_TFTP_SUPPORT?= no
+LOADER_GZIP_SUPPORT?= no
+LOADER_BZIP2_SUPPORT?= no
+
+.include <bsd.init.mk>
+
+FILES+= ubldr ubldr.bin
+
+NEWVERSWHAT= "U-Boot loader" ${MACHINE_ARCH}
+INSTALLFLAGS= -b
+WARNS?= 1
+# Address at which ubldr will be loaded.
+# This varies for different boards and SOCs.
+UBLDR_LOADADDR?= 0x1000000
+
+# Architecture-specific loader code
+SRCS= start.S conf.c self_reloc.c vers.c
+
+.if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} > 40201
+CWARNFLAGS.self_reloc.c+= -Wno-error=maybe-uninitialized
+.endif
+
+HELP_FILES+= help.uboot ${BOOTSRC}/fdt/help.fdt
+
+# Always add MI sources
+.include "${BOOTSRC}/loader.mk"
+
+CFLAGS+= -ffreestanding -msoft-float
+
+LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.${MACHINE_CPUARCH}
+LDFLAGS+= -Wl,-znotext
+
+# Pull in common loader code
+.include "${BOOTSRC}/uboot.mk"
+
+CFLAGS+= -fPIC
+
+DPADD= ${LIBFICL} ${LIBUBOOT} ${LIBFDT} ${LIBUBOOT_FDT} ${LIBSA}
+LDADD= ${LIBFICL} ${LIBUBOOT} ${LIBFDT} ${LIBUBOOT_FDT} ${LIBSA}
+
+OBJS+= ${SRCS:N*.h:R:S/$/.o/g}
+
+ldscript.abs:
+ echo "UBLDR_LOADADDR = ${UBLDR_LOADADDR};" >${.TARGET}
+
+ldscript.pie:
+ echo "UBLDR_LOADADDR = 0;" >${.TARGET}
+
+ubldr: ${OBJS} ldscript.abs ${.CURDIR}/ldscript.${MACHINE_CPUARCH} ${DPADD}
+ ${CC} ${CFLAGS} -T ldscript.abs ${LDFLAGS} \
+ -o ${.TARGET} ${OBJS} ${LDADD}
+
+ubldr.pie: ${OBJS} ldscript.pie ${.CURDIR}/ldscript.${MACHINE_CPUARCH} ${DPADD}
+ ${CC} ${CFLAGS} -T ldscript.pie ${LDFLAGS} -pie -Wl,-Bsymbolic \
+ -o ${.TARGET} ${OBJS} ${LDADD}
+
+ubldr.bin: ubldr.pie
+ ${OBJCOPY} -S -O binary ubldr.pie ${.TARGET}
+
+CLEANFILES+= ldscript.abs ldscript.pie ubldr ubldr.pie ubldr.bin
+
+.include <bsd.prog.mk>
diff --git a/stand/arm/uboot/conf.c b/stand/arm/uboot/conf.c
new file mode 100644
index 0000000..777a7b2
--- /dev/null
+++ b/stand/arm/uboot/conf.c
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 2008 Semihalf, Rafal Jaworowski
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "bootstrap.h"
+#include "libuboot.h"
+
+#if defined(LOADER_NET_SUPPORT)
+#include "dev_net.h"
+#endif
+
+/* Make sure we have an explicit reference to exit so libsa's panic pulls in the MD exit */
+void (*exitfn)(int) = exit;
+
+struct devsw *devsw[] = {
+#if defined(LOADER_DISK_SUPPORT) || defined(LOADER_CD9660_SUPPORT)
+ &uboot_storage,
+#endif
+#if defined(LOADER_NET_SUPPORT)
+ &netdev,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#if defined(LOADER_UFS_SUPPORT)
+ &ufs_fsops,
+#endif
+#if defined(LOADER_CD9660_SUPPORT)
+ &cd9660_fsops,
+#endif
+#if defined(LOADER_EXT2FS_SUPPORT)
+ &ext2fs_fsops,
+#endif
+#if defined(LOADER_NANDFS_SUPPORT)
+ &nandfs_fsops,
+#endif
+#if defined(LOADER_NFS_SUPPORT)
+ &nfs_fsops,
+#endif
+#if defined(LOADER_TFTP_SUPPORT)
+ &tftp_fsops,
+#endif
+#if defined(LOADER_GZIP_SUPPORT)
+ &gzipfs_fsops,
+#endif
+#if defined(LOADER_BZIP2_SUPPORT)
+ &bzipfs_fsops,
+#endif
+ NULL
+};
+
+struct netif_driver *netif_drivers[] = {
+#if defined(LOADER_NET_SUPPORT)
+ &uboot_net,
+#endif
+ NULL,
+};
+
+struct file_format *file_formats[] = {
+ &uboot_elf,
+ NULL
+};
+
+extern struct console uboot_console;
+
+struct console *consoles[] = {
+ &uboot_console,
+ NULL
+};
diff --git a/stand/arm/uboot/help.uboot b/stand/arm/uboot/help.uboot
new file mode 100644
index 0000000..c1574af
--- /dev/null
+++ b/stand/arm/uboot/help.uboot
@@ -0,0 +1,27 @@
+$FreeBSD$
+
+###############################################################################
+# Tubenv DShow or import U-Boot environment variables
+
+ ubenv <import | show> [varname ...]
+
+ Display U-Boot environment variables, or import them into the
+ loader environment (which makes them available in the kernel).
+
+###############################################################################
+# Tubenv Simport DImport U-Boot env vars
+
+ ubenv import [varname ...]
+
+ If no variable names are specified, all U-Boot environment
+ variables are imported. Each variable is prefixed with "uboot."
+ to avoid any possible conflicts with loader or kernel variables.
+
+###############################################################################
+# Tubenv Sshow DShow U-Boot env vars
+
+ ubenv show [varname ...]
+
+ If no variable names are specified, all U-Boot environment
+ variables are shown.
+
diff --git a/stand/arm/uboot/ldscript.arm b/stand/arm/uboot/ldscript.arm
new file mode 100644
index 0000000..8eb604c
--- /dev/null
+++ b/stand/arm/uboot/ldscript.arm
@@ -0,0 +1,134 @@
+/* $FreeBSD$ */
+
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = UBLDR_LOADADDR + SIZEOF_HEADERS;
+ . = ALIGN(8);
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ PROVIDE (etext = .);
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rela.got : { *(.rela.got) }
+ .rela.got1 : { *(.rela.got1) }
+ .rela.got2 : { *(.rela.got2) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rela.init : { *(.rela.init) }
+ .rela.fini : { *(.rela.fini) }
+ .rela.bss : { *(.rela.bss) }
+ .rela.plt : { *(.rela.plt) }
+ .rela.sdata : { *(.rela.sdata) }
+ .rela.sbss : { *(.rela.sbss) }
+ .rela.sdata2 : { *(.rela.sdata2) }
+ .rela.sbss2 : { *(.rela.sbss2) }
+ .init : { *(.init) } =0
+ .fini : { *(.fini) } =0
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 : { *(.sdata2) }
+ .sbss2 : { *(.sbss2) }
+ /* Adjust the address for the data segment to the doubleword boundary. */
+ . = ALIGN(8);
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .got1 : { *(.got1) }
+ .dynamic : { *(.dynamic) }
+ /* Put .ctors and .dtors next to the .got2 section, so that the pointers
+ get relocated with -mrelocatable. Also put in the .fixup pointers.
+ The current compiler no longer needs this, but keep it around for 2.7.2 */
+ PROVIDE (_GOT2_START_ = .);
+ .got2 : { *(.got2) }
+ PROVIDE (__CTOR_LIST__ = .);
+ .ctors : { *(.ctors) }
+ PROVIDE (__CTOR_END__ = .);
+ PROVIDE (__DTOR_LIST__ = .);
+ .dtors : { *(.dtors) }
+ PROVIDE (__DTOR_END__ = .);
+ PROVIDE (_FIXUP_START_ = .);
+ .fixup : { *(.fixup) }
+ PROVIDE (_FIXUP_END_ = .);
+ PROVIDE (_GOT2_END_ = .);
+ PROVIDE (_GOT_START_ = .);
+ .got : { *(.got) }
+ .got.plt : { *(.got.plt) }
+ PROVIDE (_GOT_END_ = .);
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ *(.sbss)
+ *(.scommon)
+ *(.dynsbss)
+ PROVIDE (__sbss_end = .);
+ }
+ .plt : { *(.plt) }
+ .bss :
+ {
+ PROVIDE (__bss_start = .);
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ /* 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) }
+ .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) }
+ /* These must appear regardless of . */
+}
diff --git a/stand/arm/uboot/start.S b/stand/arm/uboot/start.S
new file mode 100644
index 0000000..ede6a62
--- /dev/null
+++ b/stand/arm/uboot/start.S
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 2008 Semihalf, Rafal Czubak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+#include <machine/armreg.h>
+
+ .text
+ .extern _C_LABEL(self_reloc), _C_LABEL(main)
+ .weak _DYNAMIC
+
+/*
+ * Entry point to the loader that U-Boot passes control to.
+ */
+ .globl _start
+_start:
+
+#ifdef _ARM_ARCH_6
+ mrc p15, 0, ip, c1, c0, 0
+ orr ip, ip, #(CPU_CONTROL_UNAL_ENABLE)
+ orr ip, ip, #(CPU_CONTROL_AFLT_ENABLE)
+ mcr p15, 0, ip, c1, c0, 0
+#endif
+
+ /* Save the arguments and return register before calling self_reloc */
+ push {r0, r1, r9, lr}
+
+ /*
+ * Do self-relocation when the weak external symbol _DYNAMIC is non-NULL.
+ * When linked as a dynamic relocatable file, the linker automatically
+ * defines _DYNAMIC with a value that is the offset of the dynamic
+ * relocation info section.
+ * Note that we're still on u-boot's stack here, but the self_reloc
+ * code uses only a couple dozen bytes of stack space.
+ */
+ adr ip, .here_off /* .here_off is a symbol whose value */
+ ldr r0, [ip] /* is its own offset in the text seg. */
+ sub r0, ip, r0 /* Get its pc-relative address and */
+ ldr r1, .dynamic_off /* subtract its value and we get */
+ teq r1, #0 /* r0 = physaddr we were loaded at. */
+ addne r1, r1, r0 /* r1 = dynamic section physaddr. */
+ blne _C_LABEL(self_reloc) /* Do reloc if _DYNAMIC is non-NULL. */
+
+ /* Restore saved arguments */
+ pop {r0, r1, r9, lr}
+
+ /* Hint where to look for the API signature */
+ ldr ip, =uboot_address
+ str sp, [ip]
+
+ /* Save U-Boot's r8 and r9 for syscall trampoline */
+ ldr ip, =saved_regs
+ str r8, [ip, #0] /* old gd pointer (use to hold lr) */
+ str r9, [ip, #4] /* new gd pointer */
+
+ /*
+ * Start loader. Save return address first (r8 is available from
+ * trampoline save).
+ */
+ mov r8, lr
+ bl main
+ mov lr, r8
+
+ /* Restore U-Boot environment */
+ ldr ip, =saved_regs
+ ldr r8, [ip, #0]
+ ldr r9, [ip, #4]
+ mov pc, lr
+
+ /*
+ * Data for self-relocation, in the text segment for pc-rel access.
+ */
+.here_off:
+ .word .
+.dynamic_off:
+ .word _DYNAMIC
+
+/*
+ * syscall()
+ */
+ENTRY(syscall)
+ /* Save caller's lr, r8 and r9 */
+ ldr ip, =saved_regs
+ str r8, [ip, #8]
+ str r9, [ip, #12]
+ str lr, [ip, #16]
+ /* Restore U-Boot's r8 and r9 */
+ ldr r8, [ip, #0]
+ ldr r9, [ip, #4]
+ /* Call into U-Boot */
+ ldr lr, =return_from_syscall
+ ldr ip, =syscall_ptr
+ ldr pc, [ip]
+return_from_syscall:
+ /* Restore loader's r8, r9 and lr */
+ ldr ip, =saved_regs
+ ldr lr, [ip, #16]
+ ldr r9, [ip, #12]
+ ldr r8, [ip, #8]
+ /* Return to caller */
+ mov pc, lr
+
+/*
+ * Data section
+ */
+ .data
+ .align 4
+ .globl syscall_ptr
+syscall_ptr:
+ .long 0
+
+ .globl uboot_address
+uboot_address:
+ .long 0
+
+saved_regs:
+ .long 0 /* U-Boot's r8 */
+ .long 0 /* U-Boot's r9 */
+ .long 0 /* Loader's r8 */
+ .long 0 /* Loader's r9 */
+ .long 0 /* Loader's lr */
diff --git a/stand/arm/uboot/version b/stand/arm/uboot/version
new file mode 100644
index 0000000..486c412
--- /dev/null
+++ b/stand/arm/uboot/version
@@ -0,0 +1,9 @@
+$FreeBSD$
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+1.2: Extended with NAND FS support.
+1.1: Flattened Device Tree blob support.
+1.0: Added storage support. Booting from HDD, USB, etc. is now possible.
+0.5: Initial U-Boot/arm version (netbooting only).
diff --git a/stand/arm64/Makefile b/stand/arm64/Makefile
new file mode 100644
index 0000000..3ecb582
--- /dev/null
+++ b/stand/arm64/Makefile
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include <bsd.subdir.mk>
diff --git a/stand/arm64/libarm64/cache.c b/stand/arm64/libarm64/cache.c
new file mode 100644
index 0000000..25766ef
--- /dev/null
+++ b/stand/arm64/libarm64/cache.c
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under
+ * the sponsorship of the FreeBSD Foundation.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <machine/armreg.h>
+
+#include <stand.h>
+#include <efi.h>
+
+#include "cache.h"
+
+static unsigned int
+get_dcache_line_size(void)
+{
+ uint64_t ctr;
+ unsigned int dcl_size;
+
+ /* Accessible from all security levels */
+ ctr = READ_SPECIALREG(ctr_el0);
+
+ /*
+ * Relevant field [19:16] is LOG2
+ * of the number of words in DCache line
+ */
+ dcl_size = CTR_DLINE_SIZE(ctr);
+
+ /* Size of word shifted by cache line size */
+ return (sizeof(int) << dcl_size);
+}
+
+void
+cpu_flush_dcache(const void *ptr, size_t len)
+{
+
+ uint64_t cl_size;
+ vm_offset_t addr, end;
+
+ cl_size = get_dcache_line_size();
+
+ /* Calculate end address to clean */
+ end = (vm_offset_t)ptr + (vm_offset_t)len;
+ /* Align start address to cache line */
+ addr = (vm_offset_t)ptr;
+ addr = rounddown2(addr, cl_size);
+
+ for (; addr < end; addr += cl_size)
+ __asm __volatile("dc civac, %0" : : "r" (addr) : "memory");
+ /* Full system DSB */
+ __asm __volatile("dsb sy" : : : "memory");
+}
+
+void
+cpu_inval_icache(const void *ptr, size_t len)
+{
+
+ /* NULL ptr or 0 len means all */
+ if (ptr == NULL || len == 0) {
+ __asm __volatile(
+ "ic ialluis \n"
+ "dsb ish \n"
+ : : : "memory");
+ return;
+ }
+
+ /* TODO: Other cache ranges if necessary */
+}
diff --git a/stand/arm64/libarm64/cache.h b/stand/arm64/libarm64/cache.h
new file mode 100644
index 0000000..89b094b
--- /dev/null
+++ b/stand/arm64/libarm64/cache.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under
+ * the sponsorship of the FreeBSD Foundation.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CACHE_H_
+#define _CACHE_H_
+
+/* cache.c */
+void cpu_flush_dcache(const void *, size_t);
+void cpu_inval_icache(const void *, size_t);
+
+#endif /* _CACHE_H_ */
diff --git a/stand/common/Makefile.depend b/stand/common/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/common/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/common/bcache.c b/stand/common/bcache.c
new file mode 100644
index 0000000..198dd5f
--- /dev/null
+++ b/stand/common/bcache.c
@@ -0,0 +1,503 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright 2015 Toomas Soome <tsoome@me.com>
+ * 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 <sys/cdefs.h>
+#include <sys/param.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Simple hashed block cache
+ */
+
+#include <sys/stdint.h>
+
+#include <stand.h>
+#include <string.h>
+#include <strings.h>
+
+#include "bootstrap.h"
+
+/* #define BCACHE_DEBUG */
+
+#ifdef BCACHE_DEBUG
+# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
+#else
+# define DEBUG(fmt, args...)
+#endif
+
+struct bcachectl
+{
+ daddr_t bc_blkno;
+ int bc_count;
+};
+
+/*
+ * bcache per device node. cache is allocated on device first open and freed
+ * on last close, to save memory. The issue there is the size; biosdisk
+ * supports up to 31 (0x1f) devices. Classic setup would use single disk
+ * to boot from, but this has changed with zfs.
+ */
+struct bcache {
+ struct bcachectl *bcache_ctl;
+ caddr_t bcache_data;
+ size_t bcache_nblks;
+ size_t ra;
+};
+
+static u_int bcache_total_nblks; /* set by bcache_init */
+static u_int bcache_blksize; /* set by bcache_init */
+static u_int bcache_numdev; /* set by bcache_add_dev */
+/* statistics */
+static u_int bcache_units; /* number of devices with cache */
+static u_int bcache_unit_nblks; /* nblocks per unit */
+static u_int bcache_hits;
+static u_int bcache_misses;
+static u_int bcache_ops;
+static u_int bcache_bypasses;
+static u_int bcache_bcount;
+static u_int bcache_rablks;
+
+#define BHASH(bc, blkno) ((blkno) & ((bc)->bcache_nblks - 1))
+#define BCACHE_LOOKUP(bc, blkno) \
+ ((bc)->bcache_ctl[BHASH((bc), (blkno))].bc_blkno != (blkno))
+#define BCACHE_READAHEAD 256
+#define BCACHE_MINREADAHEAD 32
+#define BCACHE_MARKER 0xdeadbeef
+
+static void bcache_invalidate(struct bcache *bc, daddr_t blkno);
+static void bcache_insert(struct bcache *bc, daddr_t blkno);
+static void bcache_free_instance(struct bcache *bc);
+
+/*
+ * Initialise the cache for (nblks) of (bsize).
+ */
+void
+bcache_init(size_t nblks, size_t bsize)
+{
+ /* set up control data */
+ bcache_total_nblks = nblks;
+ bcache_blksize = bsize;
+}
+
+/*
+ * add number of devices to bcache. we have to divide cache space
+ * between the devices, so bcache_add_dev() can be used to set up the
+ * number. The issue is, we need to get the number before actual allocations.
+ * bcache_add_dev() is supposed to be called from device init() call, so the
+ * assumption is, devsw dv_init is called for plain devices first, and
+ * for zfs, last.
+ */
+void
+bcache_add_dev(int devices)
+{
+ bcache_numdev += devices;
+}
+
+void *
+bcache_allocate(void)
+{
+ u_int i;
+ struct bcache *bc = malloc(sizeof (struct bcache));
+ int disks = bcache_numdev;
+ uint32_t *marker;
+
+ if (disks == 0)
+ disks = 1; /* safe guard */
+
+ if (bc == NULL) {
+ errno = ENOMEM;
+ return (bc);
+ }
+
+ /*
+ * the bcache block count must be power of 2 for hash function
+ */
+ i = fls(disks) - 1; /* highbit - 1 */
+ if (disks > (1 << i)) /* next power of 2 */
+ i++;
+
+ bc->bcache_nblks = bcache_total_nblks >> i;
+ bcache_unit_nblks = bc->bcache_nblks;
+ bc->bcache_data = malloc(bc->bcache_nblks * bcache_blksize +
+ sizeof(uint32_t));
+ if (bc->bcache_data == NULL) {
+ /* dont error out yet. fall back to 32 blocks and try again */
+ bc->bcache_nblks = 32;
+ bc->bcache_data = malloc(bc->bcache_nblks * bcache_blksize +
+ sizeof(uint32_t));
+ }
+
+ bc->bcache_ctl = malloc(bc->bcache_nblks * sizeof(struct bcachectl));
+
+ if ((bc->bcache_data == NULL) || (bc->bcache_ctl == NULL)) {
+ bcache_free_instance(bc);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ /* Insert cache end marker. */
+ marker = (uint32_t *)(bc->bcache_data + bc->bcache_nblks * bcache_blksize);
+ *marker = BCACHE_MARKER;
+
+ /* Flush the cache */
+ for (i = 0; i < bc->bcache_nblks; i++) {
+ bc->bcache_ctl[i].bc_count = -1;
+ bc->bcache_ctl[i].bc_blkno = -1;
+ }
+ bcache_units++;
+ bc->ra = BCACHE_READAHEAD; /* optimistic read ahead */
+ return (bc);
+}
+
+void
+bcache_free(void *cache)
+{
+ struct bcache *bc = cache;
+
+ if (bc == NULL)
+ return;
+
+ bcache_free_instance(bc);
+ bcache_units--;
+}
+
+/*
+ * Handle a write request; write directly to the disk, and populate the
+ * cache with the new values.
+ */
+static int
+write_strategy(void *devdata, int rw, daddr_t blk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct bcache_devdata *dd = (struct bcache_devdata *)devdata;
+ struct bcache *bc = dd->dv_cache;
+ daddr_t i, nblk;
+
+ nblk = size / bcache_blksize;
+
+ /* Invalidate the blocks being written */
+ for (i = 0; i < nblk; i++) {
+ bcache_invalidate(bc, blk + i);
+ }
+
+ /* Write the blocks */
+ return (dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize));
+}
+
+/*
+ * Handle a read request; fill in parts of the request that can
+ * be satisfied by the cache, use the supplied strategy routine to do
+ * device I/O and then use the I/O results to populate the cache.
+ */
+static int
+read_strategy(void *devdata, int rw, daddr_t blk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct bcache_devdata *dd = (struct bcache_devdata *)devdata;
+ struct bcache *bc = dd->dv_cache;
+ size_t i, nblk, p_size, r_size, complete, ra;
+ int result;
+ daddr_t p_blk;
+ caddr_t p_buf;
+ uint32_t *marker;
+
+ if (bc == NULL) {
+ errno = ENODEV;
+ return (-1);
+ }
+
+ marker = (uint32_t *)(bc->bcache_data + bc->bcache_nblks * bcache_blksize);
+
+ if (rsize != NULL)
+ *rsize = 0;
+
+ nblk = size / bcache_blksize;
+ if (nblk == 0 && size != 0)
+ nblk++;
+ result = 0;
+ complete = 1;
+
+ /* Satisfy any cache hits up front, break on first miss */
+ for (i = 0; i < nblk; i++) {
+ if (BCACHE_LOOKUP(bc, (daddr_t)(blk + i))) {
+ bcache_misses += (nblk - i);
+ complete = 0;
+ if (nblk - i > BCACHE_MINREADAHEAD && bc->ra > BCACHE_MINREADAHEAD)
+ bc->ra >>= 1; /* reduce read ahead */
+ break;
+ } else {
+ bcache_hits++;
+ }
+ }
+
+ if (complete) { /* whole set was in cache, return it */
+ if (bc->ra < BCACHE_READAHEAD)
+ bc->ra <<= 1; /* increase read ahead */
+ bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)), buf, size);
+ goto done;
+ }
+
+ /*
+ * Fill in any misses. From check we have i pointing to first missing
+ * block, read in all remaining blocks + readahead.
+ * We have space at least for nblk - i before bcache wraps.
+ */
+ p_blk = blk + i;
+ p_buf = bc->bcache_data + (bcache_blksize * BHASH(bc, p_blk));
+ r_size = bc->bcache_nblks - BHASH(bc, p_blk); /* remaining blocks */
+
+ p_size = MIN(r_size, nblk - i); /* read at least those blocks */
+
+ /*
+ * The read ahead size setup.
+ * While the read ahead can save us IO, it also can complicate things:
+ * 1. We do not want to read ahead by wrapping around the
+ * bcache end - this would complicate the cache management.
+ * 2. We are using bc->ra as dynamic hint for read ahead size,
+ * detected cache hits will increase the read-ahead block count, and
+ * misses will decrease, see the code above.
+ * 3. The bcache is sized by 512B blocks, however, the underlying device
+ * may have a larger sector size, and we should perform the IO by
+ * taking into account these larger sector sizes. We could solve this by
+ * passing the sector size to bcache_allocate(), or by using ioctl(), but
+ * in this version we are using the constant, 16 blocks, and are rounding
+ * read ahead block count down to multiple of 16.
+ * Using the constant has two reasons, we are not entirely sure if the
+ * BIOS disk interface is providing the correct value for sector size.
+ * And secondly, this way we get the most conservative setup for the ra.
+ *
+ * The selection of multiple of 16 blocks (8KB) is quite arbitrary, however,
+ * we want to cover CDs (2K) and 4K disks.
+ * bcache_allocate() will always fall back to a minimum of 32 blocks.
+ * Our choice of 16 read ahead blocks will always fit inside the bcache.
+ */
+
+ if ((rw & F_NORA) == F_NORA)
+ ra = 0;
+ else
+ ra = bc->bcache_nblks - BHASH(bc, p_blk + p_size);
+
+ if (ra != 0 && ra != bc->bcache_nblks) { /* do we have RA space? */
+ ra = MIN(bc->ra, ra - 1);
+ ra = rounddown(ra, 16); /* multiple of 16 blocks */
+ p_size += ra;
+ }
+
+ /* invalidate bcache */
+ for (i = 0; i < p_size; i++) {
+ bcache_invalidate(bc, p_blk + i);
+ }
+
+ r_size = 0;
+ /*
+ * with read-ahead, it may happen we are attempting to read past
+ * disk end, as bcache has no information about disk size.
+ * in such case we should get partial read if some blocks can be
+ * read or error, if no blocks can be read.
+ * in either case we should return the data in bcache and only
+ * return error if there is no data.
+ */
+ rw &= F_MASK;
+ result = dd->dv_strategy(dd->dv_devdata, rw, p_blk,
+ p_size * bcache_blksize, p_buf, &r_size);
+
+ r_size /= bcache_blksize;
+ for (i = 0; i < r_size; i++)
+ bcache_insert(bc, p_blk + i);
+
+ /* update ra statistics */
+ if (r_size != 0) {
+ if (r_size < p_size)
+ bcache_rablks += (p_size - r_size);
+ else
+ bcache_rablks += ra;
+ }
+
+ /* check how much data can we copy */
+ for (i = 0; i < nblk; i++) {
+ if (BCACHE_LOOKUP(bc, (daddr_t)(blk + i)))
+ break;
+ }
+
+ if (size > i * bcache_blksize)
+ size = i * bcache_blksize;
+
+ if (size != 0) {
+ bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)), buf, size);
+ result = 0;
+ }
+
+ if (*marker != BCACHE_MARKER) {
+ printf("BUG: bcache corruption detected: nblks: %zu p_blk: %lu, "
+ "p_size: %zu, ra: %zu\n", bc->bcache_nblks,
+ (long unsigned)BHASH(bc, p_blk), p_size, ra);
+ }
+
+ done:
+ if ((result == 0) && (rsize != NULL))
+ *rsize = size;
+ return(result);
+}
+
+/*
+ * Requests larger than 1/2 cache size will be bypassed and go
+ * directly to the disk. XXX tune this.
+ */
+int
+bcache_strategy(void *devdata, int rw, daddr_t blk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct bcache_devdata *dd = (struct bcache_devdata *)devdata;
+ struct bcache *bc = dd->dv_cache;
+ u_int bcache_nblks = 0;
+ int nblk, cblk, ret;
+ size_t csize, isize, total;
+
+ bcache_ops++;
+
+ if (bc != NULL)
+ bcache_nblks = bc->bcache_nblks;
+
+ /* bypass large requests, or when the cache is inactive */
+ if (bc == NULL ||
+ ((size * 2 / bcache_blksize) > bcache_nblks)) {
+ DEBUG("bypass %zu from %qu", size / bcache_blksize, blk);
+ bcache_bypasses++;
+ rw &= F_MASK;
+ return (dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize));
+ }
+
+ switch (rw & F_MASK) {
+ case F_READ:
+ nblk = size / bcache_blksize;
+ if (size != 0 && nblk == 0)
+ nblk++; /* read at least one block */
+
+ ret = 0;
+ total = 0;
+ while(size) {
+ cblk = bcache_nblks - BHASH(bc, blk); /* # of blocks left */
+ cblk = MIN(cblk, nblk);
+
+ if (size <= bcache_blksize)
+ csize = size;
+ else
+ csize = cblk * bcache_blksize;
+
+ ret = read_strategy(devdata, rw, blk, csize, buf+total, &isize);
+
+ /*
+ * we may have error from read ahead, if we have read some data
+ * return partial read.
+ */
+ if (ret != 0 || isize == 0) {
+ if (total != 0)
+ ret = 0;
+ break;
+ }
+ blk += isize / bcache_blksize;
+ total += isize;
+ size -= isize;
+ nblk = size / bcache_blksize;
+ }
+
+ if (rsize)
+ *rsize = total;
+
+ return (ret);
+ case F_WRITE:
+ return write_strategy(devdata, F_WRITE, blk, size, buf, rsize);
+ }
+ return -1;
+}
+
+/*
+ * Free allocated bcache instance
+ */
+static void
+bcache_free_instance(struct bcache *bc)
+{
+ if (bc != NULL) {
+ if (bc->bcache_ctl)
+ free(bc->bcache_ctl);
+ if (bc->bcache_data)
+ free(bc->bcache_data);
+ free(bc);
+ }
+}
+
+/*
+ * Insert a block into the cache.
+ */
+static void
+bcache_insert(struct bcache *bc, daddr_t blkno)
+{
+ u_int cand;
+
+ cand = BHASH(bc, blkno);
+
+ DEBUG("insert blk %llu -> %u # %d", blkno, cand, bcache_bcount);
+ bc->bcache_ctl[cand].bc_blkno = blkno;
+ bc->bcache_ctl[cand].bc_count = bcache_bcount++;
+}
+
+/*
+ * Invalidate a block from the cache.
+ */
+static void
+bcache_invalidate(struct bcache *bc, daddr_t blkno)
+{
+ u_int i;
+
+ i = BHASH(bc, blkno);
+ if (bc->bcache_ctl[i].bc_blkno == blkno) {
+ bc->bcache_ctl[i].bc_count = -1;
+ bc->bcache_ctl[i].bc_blkno = -1;
+ DEBUG("invalidate blk %llu", blkno);
+ }
+}
+
+#ifndef BOOT2
+COMMAND_SET(bcachestat, "bcachestat", "get disk block cache stats", command_bcache);
+
+static int
+command_bcache(int argc, char *argv[])
+{
+ if (argc != 1) {
+ command_errmsg = "wrong number of arguments";
+ return(CMD_ERROR);
+ }
+
+ printf("\ncache blocks: %d\n", bcache_total_nblks);
+ printf("cache blocksz: %d\n", bcache_blksize);
+ printf("cache readahead: %d\n", bcache_rablks);
+ printf("unit cache blocks: %d\n", bcache_unit_nblks);
+ printf("cached units: %d\n", bcache_units);
+ printf("%d ops %d bypasses %d hits %d misses\n", bcache_ops,
+ bcache_bypasses, bcache_hits, bcache_misses);
+ return(CMD_OK);
+}
+#endif
diff --git a/stand/common/boot.c b/stand/common/boot.c
new file mode 100644
index 0000000..5ee9521
--- /dev/null
+++ b/stand/common/boot.c
@@ -0,0 +1,410 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Loading modules, booting the system
+ */
+
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+
+static char *getbootfile(int try);
+static int loadakernel(int try, int argc, char* argv[]);
+
+/* List of kernel names to try (may be overwritten by boot.config) XXX should move from here? */
+static const char *default_bootfiles = "kernel";
+
+static int autoboot_tried;
+
+/*
+ * The user wants us to boot.
+ */
+COMMAND_SET(boot, "boot", "boot a file or loaded kernel", command_boot);
+
+static int
+command_boot(int argc, char *argv[])
+{
+ struct preloaded_file *fp;
+
+ /*
+ * See if the user has specified an explicit kernel to boot.
+ */
+ if ((argc > 1) && (argv[1][0] != '-')) {
+
+ /* XXX maybe we should discard everything and start again? */
+ if (file_findfile(NULL, NULL) != NULL) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "can't boot '%s', kernel module already loaded", argv[1]);
+ return(CMD_ERROR);
+ }
+
+ /* find/load the kernel module */
+ if (mod_loadkld(argv[1], argc - 2, argv + 2) != 0)
+ return(CMD_ERROR);
+ /* we have consumed all arguments */
+ argc = 1;
+ }
+
+ /*
+ * See if there is a kernel module already loaded
+ */
+ if (file_findfile(NULL, NULL) == NULL)
+ if (loadakernel(0, argc - 1, argv + 1))
+ /* we have consumed all arguments */
+ argc = 1;
+
+ /*
+ * Loaded anything yet?
+ */
+ if ((fp = file_findfile(NULL, NULL)) == NULL) {
+ command_errmsg = "no bootable kernel";
+ return(CMD_ERROR);
+ }
+
+ /*
+ * If we were given arguments, discard any previous.
+ * XXX should we merge arguments? Hard to DWIM.
+ */
+ if (argc > 1) {
+ if (fp->f_args != NULL)
+ free(fp->f_args);
+ fp->f_args = unargv(argc - 1, argv + 1);
+ }
+
+ /* Hook for platform-specific autoloading of modules */
+ if (archsw.arch_autoload() != 0)
+ return(CMD_ERROR);
+
+ /* Call the exec handler from the loader matching the kernel */
+ file_formats[fp->f_loader]->l_exec(fp);
+ return(CMD_ERROR);
+}
+
+
+/*
+ * Autoboot after a delay
+ */
+
+COMMAND_SET(autoboot, "autoboot", "boot automatically after a delay", command_autoboot);
+
+static int
+command_autoboot(int argc, char *argv[])
+{
+ int howlong;
+ char *cp, *prompt;
+
+ prompt = NULL;
+ howlong = -1;
+ switch(argc) {
+ case 3:
+ prompt = argv[2];
+ /* FALLTHROUGH */
+ case 2:
+ howlong = strtol(argv[1], &cp, 0);
+ if (*cp != 0) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "bad delay '%s'", argv[1]);
+ return(CMD_ERROR);
+ }
+ /* FALLTHROUGH */
+ case 1:
+ return(autoboot(howlong, prompt));
+ }
+
+ command_errmsg = "too many arguments";
+ return(CMD_ERROR);
+}
+
+/*
+ * Called before we go interactive. If we think we can autoboot, and
+ * we haven't tried already, try now.
+ */
+void
+autoboot_maybe()
+{
+ char *cp;
+
+ cp = getenv("autoboot_delay");
+ if ((autoboot_tried == 0) && ((cp == NULL) || strcasecmp(cp, "NO")))
+ autoboot(-1, NULL); /* try to boot automatically */
+}
+
+int
+autoboot(int timeout, char *prompt)
+{
+ time_t when, otime, ntime;
+ int c, yes;
+ char *argv[2], *cp, *ep;
+ char *kernelname;
+#ifdef BOOT_PROMPT_123
+ const char *seq = "123", *p = seq;
+#endif
+
+ autoboot_tried = 1;
+
+ if (timeout == -1) {
+ timeout = 10;
+ /* try to get a delay from the environment */
+ if ((cp = getenv("autoboot_delay"))) {
+ timeout = strtol(cp, &ep, 0);
+ if (cp == ep)
+ timeout = 10; /* Unparseable? Set default! */
+ }
+ }
+
+ kernelname = getenv("kernelname");
+ if (kernelname == NULL) {
+ argv[0] = NULL;
+ loadakernel(0, 0, argv);
+ kernelname = getenv("kernelname");
+ if (kernelname == NULL) {
+ command_errmsg = "no valid kernel found";
+ return(CMD_ERROR);
+ }
+ }
+
+ if (timeout >= 0) {
+ otime = time(NULL);
+ when = otime + timeout; /* when to boot */
+
+ yes = 0;
+
+#ifdef BOOT_PROMPT_123
+ printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or "
+ "1 2 3 sequence for command prompt." : prompt);
+#else
+ printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or any other key for command prompt." : prompt);
+#endif
+
+ for (;;) {
+ if (ischar()) {
+ c = getchar();
+#ifdef BOOT_PROMPT_123
+ if ((c == '\r') || (c == '\n')) {
+ yes = 1;
+ break;
+ } else if (c != *p++)
+ p = seq;
+ if (*p == 0)
+ break;
+#else
+ if ((c == '\r') || (c == '\n'))
+ yes = 1;
+ break;
+#endif
+ }
+ ntime = time(NULL);
+ if (ntime >= when) {
+ yes = 1;
+ break;
+ }
+
+ if (ntime != otime) {
+ printf("\rBooting [%s] in %d second%s... ",
+ kernelname, (int)(when - ntime),
+ (when-ntime)==1?"":"s");
+ otime = ntime;
+ }
+ }
+ } else {
+ yes = 1;
+ }
+
+ if (yes)
+ printf("\rBooting [%s]... ", kernelname);
+ putchar('\n');
+ if (yes) {
+ argv[0] = "boot";
+ argv[1] = NULL;
+ return(command_boot(1, argv));
+ }
+ return(CMD_OK);
+}
+
+/*
+ * Scrounge for the name of the (try)'th file we will try to boot.
+ */
+static char *
+getbootfile(int try)
+{
+ static char *name = NULL;
+ const char *spec, *ep;
+ size_t len;
+
+ /* we use dynamic storage */
+ if (name != NULL) {
+ free(name);
+ name = NULL;
+ }
+
+ /*
+ * Try $bootfile, then try our builtin default
+ */
+ if ((spec = getenv("bootfile")) == NULL)
+ spec = default_bootfiles;
+
+ while ((try > 0) && (spec != NULL)) {
+ spec = strchr(spec, ';');
+ if (spec)
+ spec++; /* skip over the leading ';' */
+ try--;
+ }
+ if (spec != NULL) {
+ if ((ep = strchr(spec, ';')) != NULL) {
+ len = ep - spec;
+ } else {
+ len = strlen(spec);
+ }
+ name = malloc(len + 1);
+ strncpy(name, spec, len);
+ name[len] = 0;
+ }
+ if (name && name[0] == 0) {
+ free(name);
+ name = NULL;
+ }
+ return(name);
+}
+
+/*
+ * Try to find the /etc/fstab file on the filesystem (rootdev),
+ * which should be be the root filesystem, and parse it to find
+ * out what the kernel ought to think the root filesystem is.
+ *
+ * If we're successful, set vfs.root.mountfrom to <vfstype>:<path>
+ * so that the kernel can tell both which VFS and which node to use
+ * to mount the device. If this variable's already set, don't
+ * overwrite it.
+ */
+int
+getrootmount(char *rootdev)
+{
+ char lbuf[128], *cp, *ep, *dev, *fstyp, *options;
+ int fd, error;
+
+ if (getenv("vfs.root.mountfrom") != NULL)
+ return(0);
+
+ error = 1;
+ sprintf(lbuf, "%s/etc/fstab", rootdev);
+ if ((fd = open(lbuf, O_RDONLY)) < 0)
+ goto notfound;
+
+ /* loop reading lines from /etc/fstab What was that about sscanf again? */
+ while (fgetstr(lbuf, sizeof(lbuf), fd) >= 0) {
+ if ((lbuf[0] == 0) || (lbuf[0] == '#'))
+ continue;
+
+ /* skip device name */
+ for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++)
+ ;
+ if (*cp == 0) /* misformatted */
+ continue;
+ /* delimit and save */
+ *cp++ = 0;
+ dev = strdup(lbuf);
+
+ /* skip whitespace up to mountpoint */
+ while ((*cp != 0) && isspace(*cp))
+ cp++;
+ /* must have /<space> to be root */
+ if ((*cp == 0) || (*cp != '/') || !isspace(*(cp + 1)))
+ continue;
+ /* skip whitespace up to fstype */
+ cp += 2;
+ while ((*cp != 0) && isspace(*cp))
+ cp++;
+ if (*cp == 0) /* misformatted */
+ continue;
+ /* skip text to end of fstype and delimit */
+ ep = cp;
+ while ((*cp != 0) && !isspace(*cp))
+ cp++;
+ *cp = 0;
+ fstyp = strdup(ep);
+
+ /* skip whitespace up to mount options */
+ cp += 1;
+ while ((*cp != 0) && isspace(*cp))
+ cp++;
+ if (*cp == 0) /* misformatted */
+ continue;
+ /* skip text to end of mount options and delimit */
+ ep = cp;
+ while ((*cp != 0) && !isspace(*cp))
+ cp++;
+ *cp = 0;
+ options = strdup(ep);
+ /* Build the <fstype>:<device> and save it in vfs.root.mountfrom */
+ sprintf(lbuf, "%s:%s", fstyp, dev);
+ free(dev);
+ free(fstyp);
+ setenv("vfs.root.mountfrom", lbuf, 0);
+
+ /* Don't override vfs.root.mountfrom.options if it is already set */
+ if (getenv("vfs.root.mountfrom.options") == NULL) {
+ /* save mount options */
+ setenv("vfs.root.mountfrom.options", options, 0);
+ }
+ free(options);
+ error = 0;
+ break;
+ }
+ close(fd);
+
+notfound:
+ if (error) {
+ const char *currdev;
+
+ currdev = getenv("currdev");
+ if (currdev != NULL && strncmp("zfs:", currdev, 4) == 0) {
+ cp = strdup(currdev);
+ cp[strlen(cp) - 1] = '\0';
+ setenv("vfs.root.mountfrom", cp, 0);
+ error = 0;
+ }
+ }
+
+ return(error);
+}
+
+static int
+loadakernel(int try, int argc, char* argv[])
+{
+ char *cp;
+
+ for (try = 0; (cp = getbootfile(try)) != NULL; try++)
+ if (mod_loadkld(cp, argc - 1, argv + 1) != 0)
+ printf("can't load '%s'\n", cp);
+ else
+ return 1;
+ return 0;
+}
diff --git a/stand/common/bootstrap.h b/stand/common/bootstrap.h
new file mode 100644
index 0000000..2234e05
--- /dev/null
+++ b/stand/common/bootstrap.h
@@ -0,0 +1,334 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOTSTRAP_H_
+#define _BOOTSTRAP_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/linker_set.h>
+
+/* Commands and return values; nonzero return sets command_errmsg != NULL */
+typedef int (bootblk_cmd_t)(int argc, char *argv[]);
+#define COMMAND_ERRBUFSZ (256)
+extern char *command_errmsg;
+extern char command_errbuf[COMMAND_ERRBUFSZ];
+#define CMD_OK 0
+#define CMD_WARN 1
+#define CMD_ERROR 2
+#define CMD_CRIT 3
+#define CMD_FATAL 4
+
+/* interp.c */
+void interact(const char *rc);
+int include(const char *filename);
+
+/* interp_backslash.c */
+char *backslash(char *str);
+
+/* interp_parse.c */
+int parse(int *argc, char ***argv, char *str);
+
+/* interp_forth.c */
+void bf_init(const char *rc);
+int bf_run(char *line);
+
+/* boot.c */
+int autoboot(int timeout, char *prompt);
+void autoboot_maybe(void);
+int getrootmount(char *rootdev);
+
+/* misc.c */
+char *unargv(int argc, char *argv[]);
+void hexdump(caddr_t region, size_t len);
+size_t strlenout(vm_offset_t str);
+char *strdupout(vm_offset_t str);
+void kern_bzero(vm_offset_t dest, size_t len);
+int kern_pread(int fd, vm_offset_t dest, size_t len, off_t off);
+void *alloc_pread(int fd, off_t off, size_t len);
+
+/* bcache.c */
+void bcache_init(size_t nblks, size_t bsize);
+void bcache_add_dev(int);
+void *bcache_allocate(void);
+void bcache_free(void *);
+int bcache_strategy(void *devdata, int rw, daddr_t blk, size_t size,
+ char *buf, size_t *rsize);
+
+/*
+ * Disk block cache
+ */
+struct bcache_devdata
+{
+ int (*dv_strategy)(void *devdata, int rw, daddr_t blk,
+ size_t size, char *buf, size_t *rsize);
+ void *dv_devdata;
+ void *dv_cache;
+};
+
+/*
+ * Modular console support.
+ */
+struct console
+{
+ const char *c_name;
+ const char *c_desc;
+ int c_flags;
+#define C_PRESENTIN (1<<0) /* console can provide input */
+#define C_PRESENTOUT (1<<1) /* console can provide output */
+#define C_ACTIVEIN (1<<2) /* user wants input from console */
+#define C_ACTIVEOUT (1<<3) /* user wants output to console */
+#define C_WIDEOUT (1<<4) /* c_out routine groks wide chars */
+ void (* c_probe)(struct console *cp); /* set c_flags to match hardware */
+ int (* c_init)(int arg); /* reinit XXX may need more args */
+ void (* c_out)(int c); /* emit c */
+ int (* c_in)(void); /* wait for and return input */
+ int (* c_ready)(void); /* return nonzer if input waiting */
+};
+extern struct console *consoles[];
+void cons_probe(void);
+
+/*
+ * Plug-and-play enumerator/configurator interface.
+ */
+struct pnphandler
+{
+ const char *pp_name; /* handler/bus name */
+ void (* pp_enumerate)(void); /* enumerate PnP devices, add to chain */
+};
+
+struct pnpident
+{
+ char *id_ident; /* ASCII identifier, actual format varies with bus/handler */
+ STAILQ_ENTRY(pnpident) id_link;
+};
+
+struct pnpinfo
+{
+ char *pi_desc; /* ASCII description, optional */
+ int pi_revision; /* optional revision (or -1) if not supported */
+ char *pi_module; /* module/args nominated to handle device */
+ int pi_argc; /* module arguments */
+ char **pi_argv;
+ struct pnphandler *pi_handler; /* handler which detected this device */
+ STAILQ_HEAD(,pnpident) pi_ident; /* list of identifiers */
+ STAILQ_ENTRY(pnpinfo) pi_link;
+};
+
+STAILQ_HEAD(pnpinfo_stql, pnpinfo);
+
+extern struct pnphandler *pnphandlers[]; /* provided by MD code */
+
+void pnp_addident(struct pnpinfo *pi, char *ident);
+struct pnpinfo *pnp_allocinfo(void);
+void pnp_freeinfo(struct pnpinfo *pi);
+void pnp_addinfo(struct pnpinfo *pi);
+char *pnp_eisaformat(u_int8_t *data);
+
+/*
+ * < 0 - No ISA in system
+ * == 0 - Maybe ISA, search for read data port
+ * > 0 - ISA in system, value is read data port address
+ */
+extern int isapnp_readport;
+
+/*
+ * Preloaded file metadata header.
+ *
+ * Metadata are allocated on our heap, and copied into kernel space
+ * before executing the kernel.
+ */
+struct file_metadata
+{
+ size_t md_size;
+ u_int16_t md_type;
+ struct file_metadata *md_next;
+ char md_data[1]; /* data are immediately appended */
+};
+
+struct preloaded_file;
+struct mod_depend;
+
+struct kernel_module
+{
+ char *m_name; /* module name */
+ int m_version; /* module version */
+/* char *m_args;*/ /* arguments for the module */
+ struct preloaded_file *m_fp;
+ struct kernel_module *m_next;
+};
+
+/*
+ * Preloaded file information. Depending on type, file can contain
+ * additional units called 'modules'.
+ *
+ * At least one file (the kernel) must be loaded in order to boot.
+ * The kernel is always loaded first.
+ *
+ * String fields (m_name, m_type) should be dynamically allocated.
+ */
+struct preloaded_file
+{
+ char *f_name; /* file name */
+ char *f_type; /* verbose file type, eg 'ELF kernel', 'pnptable', etc. */
+ char *f_args; /* arguments for the file */
+ struct file_metadata *f_metadata; /* metadata that will be placed in the module directory */
+ int f_loader; /* index of the loader that read the file */
+ vm_offset_t f_addr; /* load address */
+ size_t f_size; /* file size */
+ struct kernel_module *f_modules; /* list of modules if any */
+ struct preloaded_file *f_next; /* next file */
+};
+
+struct file_format
+{
+ /* Load function must return EFTYPE if it can't handle the module supplied */
+ int (* l_load)(char *filename, u_int64_t dest, struct preloaded_file **result);
+ /* Only a loader that will load a kernel (first module) should have an exec handler */
+ int (* l_exec)(struct preloaded_file *mp);
+};
+
+extern struct file_format *file_formats[]; /* supplied by consumer */
+extern struct preloaded_file *preloaded_files;
+
+int mod_load(char *name, struct mod_depend *verinfo, int argc, char *argv[]);
+int mod_loadkld(const char *name, int argc, char *argv[]);
+void unload(void);
+
+struct preloaded_file *file_alloc(void);
+struct preloaded_file *file_findfile(const char *name, const char *type);
+struct file_metadata *file_findmetadata(struct preloaded_file *fp, int type);
+struct preloaded_file *file_loadraw(const char *name, char *type, int insert);
+void file_discard(struct preloaded_file *fp);
+void file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p);
+int file_addmodule(struct preloaded_file *fp, char *modname, int version,
+ struct kernel_module **newmp);
+void file_removemetadata(struct preloaded_file *fp);
+
+/* MI module loaders */
+#ifdef __elfN
+/* Relocation types. */
+#define ELF_RELOC_REL 1
+#define ELF_RELOC_RELA 2
+
+/* Relocation offset for some architectures */
+extern u_int64_t __elfN(relocation_offset);
+
+struct elf_file;
+typedef Elf_Addr (symaddr_fn)(struct elf_file *ef, Elf_Size symidx);
+
+int __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result);
+int __elfN(obj_loadfile)(char *filename, u_int64_t dest,
+ struct preloaded_file **result);
+int __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr,
+ const void *reldata, int reltype, Elf_Addr relbase,
+ Elf_Addr dataaddr, void *data, size_t len);
+int __elfN(loadfile_raw)(char *filename, u_int64_t dest,
+ struct preloaded_file **result, int multiboot);
+int __elfN(load_modmetadata)(struct preloaded_file *fp, u_int64_t dest);
+#endif
+
+/*
+ * Support for commands
+ */
+struct bootblk_command
+{
+ const char *c_name;
+ const char *c_desc;
+ bootblk_cmd_t *c_fn;
+};
+
+#define COMMAND_SET(tag, key, desc, func) \
+ static bootblk_cmd_t func; \
+ static struct bootblk_command _cmd_ ## tag = { key, desc, func }; \
+ DATA_SET(Xcommand_set, _cmd_ ## tag)
+
+SET_DECLARE(Xcommand_set, struct bootblk_command);
+
+/*
+ * The intention of the architecture switch is to provide a convenient
+ * encapsulation of the interface between the bootstrap MI and MD code.
+ * MD code may selectively populate the switch at runtime based on the
+ * actual configuration of the target system.
+ */
+struct arch_switch
+{
+ /* Automatically load modules as required by detected hardware */
+ int (*arch_autoload)(void);
+ /* Locate the device for (name), return pointer to tail in (*path) */
+ int (*arch_getdev)(void **dev, const char *name, const char **path);
+ /* Copy from local address space to module address space, similar to bcopy() */
+ ssize_t (*arch_copyin)(const void *src, vm_offset_t dest,
+ const size_t len);
+ /* Copy to local address space from module address space, similar to bcopy() */
+ ssize_t (*arch_copyout)(const vm_offset_t src, void *dest,
+ const size_t len);
+ /* Read from file to module address space, same semantics as read() */
+ ssize_t (*arch_readin)(const int fd, vm_offset_t dest,
+ const size_t len);
+ /* Perform ISA byte port I/O (only for systems with ISA) */
+ int (*arch_isainb)(int port);
+ void (*arch_isaoutb)(int port, int value);
+
+ /*
+ * Interface to adjust the load address according to the "object"
+ * being loaded.
+ */
+ uint64_t (*arch_loadaddr)(u_int type, void *data, uint64_t addr);
+#define LOAD_ELF 1 /* data points to the ELF header. */
+#define LOAD_RAW 2 /* data points to the file name. */
+
+ /*
+ * Interface to inform MD code about a loaded (ELF) segment. This
+ * can be used to flush caches and/or set up translations.
+ */
+#ifdef __elfN
+ void (*arch_loadseg)(Elf_Ehdr *eh, Elf_Phdr *ph, uint64_t delta);
+#else
+ void (*arch_loadseg)(void *eh, void *ph, uint64_t delta);
+#endif
+
+ /* Probe ZFS pool(s), if needed. */
+ void (*arch_zfs_probe)(void);
+};
+extern struct arch_switch archsw;
+
+/* This must be provided by the MD code, but should it be in the archsw? */
+void delay(int delay);
+
+void dev_cleanup(void);
+
+time_t time(time_t *tloc);
+
+#ifndef CTASSERT /* Allow lint to override */
+#define CTASSERT(x) _CTASSERT(x, __LINE__)
+#define _CTASSERT(x, y) __CTASSERT(x, y)
+#define __CTASSERT(x, y) typedef char __assert ## y[(x) ? 1 : -1]
+#endif
+
+#endif /* !_BOOTSTRAP_H_ */
diff --git a/stand/common/commands.c b/stand/common/commands.c
new file mode 100644
index 0000000..def7ff2
--- /dev/null
+++ b/stand/common/commands.c
@@ -0,0 +1,511 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+
+char *command_errmsg;
+/* XXX should have procedural interface for setting, size limit? */
+char command_errbuf[COMMAND_ERRBUFSZ];
+
+static int page_file(char *filename);
+
+/*
+ * Help is read from a formatted text file.
+ *
+ * Entries in the file are formatted as
+
+# Ttopic [Ssubtopic] Ddescription
+help
+text
+here
+#
+
+ *
+ * Note that for code simplicity's sake, the above format must be followed
+ * exactly.
+ *
+ * Subtopic entries must immediately follow the topic (this is used to
+ * produce the listing of subtopics).
+ *
+ * If no argument(s) are supplied by the user, the help for 'help' is displayed.
+ */
+COMMAND_SET(help, "help", "detailed help", command_help);
+
+static int
+help_getnext(int fd, char **topic, char **subtopic, char **desc)
+{
+ char line[81], *cp, *ep;
+
+ for (;;) {
+ if (fgetstr(line, 80, fd) < 0)
+ return(0);
+
+ if ((strlen(line) < 3) || (line[0] != '#') || (line[1] != ' '))
+ continue;
+
+ *topic = *subtopic = *desc = NULL;
+ cp = line + 2;
+ while((cp != NULL) && (*cp != 0)) {
+ ep = strchr(cp, ' ');
+ if ((*cp == 'T') && (*topic == NULL)) {
+ if (ep != NULL)
+ *ep++ = 0;
+ *topic = strdup(cp + 1);
+ } else if ((*cp == 'S') && (*subtopic == NULL)) {
+ if (ep != NULL)
+ *ep++ = 0;
+ *subtopic = strdup(cp + 1);
+ } else if (*cp == 'D') {
+ *desc = strdup(cp + 1);
+ ep = NULL;
+ }
+ cp = ep;
+ }
+ if (*topic == NULL) {
+ if (*subtopic != NULL)
+ free(*subtopic);
+ if (*desc != NULL)
+ free(*desc);
+ continue;
+ }
+ return(1);
+ }
+}
+
+static int
+help_emitsummary(char *topic, char *subtopic, char *desc)
+{
+ int i;
+
+ pager_output(" ");
+ pager_output(topic);
+ i = strlen(topic);
+ if (subtopic != NULL) {
+ pager_output(" ");
+ pager_output(subtopic);
+ i += strlen(subtopic) + 1;
+ }
+ if (desc != NULL) {
+ do {
+ pager_output(" ");
+ } while (i++ < 30);
+ pager_output(desc);
+ }
+ return (pager_output("\n"));
+}
+
+
+static int
+command_help(int argc, char *argv[])
+{
+ char buf[81]; /* XXX buffer size? */
+ int hfd, matched, doindex;
+ char *topic, *subtopic, *t, *s, *d;
+
+ /* page the help text from our load path */
+ snprintf(buf, sizeof(buf), "%s/boot/loader.help", getenv("loaddev"));
+ if ((hfd = open(buf, O_RDONLY)) < 0) {
+ printf("Verbose help not available, use '?' to list commands\n");
+ return(CMD_OK);
+ }
+
+ /* pick up request from arguments */
+ topic = subtopic = NULL;
+ switch(argc) {
+ case 3:
+ subtopic = strdup(argv[2]);
+ case 2:
+ topic = strdup(argv[1]);
+ break;
+ case 1:
+ topic = strdup("help");
+ break;
+ default:
+ command_errmsg = "usage is 'help <topic> [<subtopic>]";
+ close(hfd);
+ return(CMD_ERROR);
+ }
+
+ /* magic "index" keyword */
+ doindex = !strcmp(topic, "index");
+ matched = doindex;
+
+ /* Scan the helpfile looking for help matching the request */
+ pager_open();
+ while(help_getnext(hfd, &t, &s, &d)) {
+
+ if (doindex) { /* dink around formatting */
+ if (help_emitsummary(t, s, d))
+ break;
+
+ } else if (strcmp(topic, t)) {
+ /* topic mismatch */
+ if(matched) /* nothing more on this topic, stop scanning */
+ break;
+
+ } else {
+ /* topic matched */
+ matched = 1;
+ if (((subtopic == NULL) && (s == NULL)) ||
+ ((subtopic != NULL) && (s != NULL) && !strcmp(subtopic, s))) {
+ /* exact match, print text */
+ while((fgetstr(buf, 80, hfd) >= 0) && (buf[0] != '#')) {
+ if (pager_output(buf))
+ break;
+ if (pager_output("\n"))
+ break;
+ }
+ } else if ((subtopic == NULL) && (s != NULL)) {
+ /* topic match, list subtopics */
+ if (help_emitsummary(t, s, d))
+ break;
+ }
+ }
+ free(t);
+ free(s);
+ free(d);
+ }
+ pager_close();
+ close(hfd);
+ if (!matched) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "no help available for '%s'", topic);
+ free(topic);
+ if (subtopic)
+ free(subtopic);
+ return(CMD_ERROR);
+ }
+ free(topic);
+ if (subtopic)
+ free(subtopic);
+ return(CMD_OK);
+}
+
+
+COMMAND_SET(commandlist, "?", "list commands", command_commandlist);
+
+/*
+ * Please note: although we use the pager for the list of commands,
+ * this routine is called from the ? FORTH function which then
+ * unconditionally prints some commands. This will lead to anomalous
+ * behavior. There's no 'pager_output' binding to FORTH to allow
+ * things to work right, so I'm documenting the bug rather than
+ * fixing it.
+ */
+static int
+command_commandlist(int argc, char *argv[])
+{
+ struct bootblk_command **cmdp;
+ int res;
+ char name[20];
+
+ res = 0;
+ pager_open();
+ res = pager_output("Available commands:\n");
+ SET_FOREACH(cmdp, Xcommand_set) {
+ if (res)
+ break;
+ if (((*cmdp)->c_name != NULL) && ((*cmdp)->c_desc != NULL)) {
+ sprintf(name, " %-15s ", (*cmdp)->c_name);
+ pager_output(name);
+ pager_output((*cmdp)->c_desc);
+ res = pager_output("\n");
+ }
+ }
+ pager_close();
+ return(CMD_OK);
+}
+
+/*
+ * XXX set/show should become set/echo if we have variable
+ * substitution happening.
+ */
+
+COMMAND_SET(show, "show", "show variable(s)", command_show);
+
+static int
+command_show(int argc, char *argv[])
+{
+ struct env_var *ev;
+ char *cp;
+
+ if (argc < 2) {
+ /*
+ * With no arguments, print everything.
+ */
+ pager_open();
+ for (ev = environ; ev != NULL; ev = ev->ev_next) {
+ pager_output(ev->ev_name);
+ cp = getenv(ev->ev_name);
+ if (cp != NULL) {
+ pager_output("=");
+ pager_output(cp);
+ }
+ if (pager_output("\n"))
+ break;
+ }
+ pager_close();
+ } else {
+ if ((cp = getenv(argv[1])) != NULL) {
+ printf("%s\n", cp);
+ } else {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "variable '%s' not found", argv[1]);
+ return(CMD_ERROR);
+ }
+ }
+ return(CMD_OK);
+}
+
+COMMAND_SET(set, "set", "set a variable", command_set);
+
+static int
+command_set(int argc, char *argv[])
+{
+ int err;
+
+ if (argc != 2) {
+ command_errmsg = "wrong number of arguments";
+ return(CMD_ERROR);
+ } else {
+ if ((err = putenv(argv[1])) != 0) {
+ command_errmsg = strerror(err);
+ return(CMD_ERROR);
+ }
+ }
+ return(CMD_OK);
+}
+
+COMMAND_SET(unset, "unset", "unset a variable", command_unset);
+
+static int
+command_unset(int argc, char *argv[])
+{
+ int err;
+
+ if (argc != 2) {
+ command_errmsg = "wrong number of arguments";
+ return(CMD_ERROR);
+ } else {
+ if ((err = unsetenv(argv[1])) != 0) {
+ command_errmsg = strerror(err);
+ return(CMD_ERROR);
+ }
+ }
+ return(CMD_OK);
+}
+
+COMMAND_SET(echo, "echo", "echo arguments", command_echo);
+
+static int
+command_echo(int argc, char *argv[])
+{
+ char *s;
+ int nl, ch;
+
+ nl = 0;
+ optind = 1;
+ optreset = 1;
+ while ((ch = getopt(argc, argv, "n")) != -1) {
+ switch(ch) {
+ case 'n':
+ nl = 1;
+ break;
+ case '?':
+ default:
+ /* getopt has already reported an error */
+ return(CMD_OK);
+ }
+ }
+ argv += (optind);
+ argc -= (optind);
+
+ s = unargv(argc, argv);
+ if (s != NULL) {
+ printf("%s", s);
+ free(s);
+ }
+ if (!nl)
+ printf("\n");
+ return(CMD_OK);
+}
+
+/*
+ * A passable emulation of the sh(1) command of the same name.
+ */
+
+COMMAND_SET(read, "read", "read input from the terminal", command_read);
+
+static int
+command_read(int argc, char *argv[])
+{
+ char *prompt;
+ int timeout;
+ time_t when;
+ char *cp;
+ char *name;
+ char buf[256]; /* XXX size? */
+ int c;
+
+ timeout = -1;
+ prompt = NULL;
+ optind = 1;
+ optreset = 1;
+ while ((c = getopt(argc, argv, "p:t:")) != -1) {
+ switch(c) {
+
+ case 'p':
+ prompt = optarg;
+ break;
+ case 't':
+ timeout = strtol(optarg, &cp, 0);
+ if (cp == optarg) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "bad timeout '%s'", optarg);
+ return(CMD_ERROR);
+ }
+ break;
+ default:
+ return(CMD_OK);
+ }
+ }
+
+ argv += (optind);
+ argc -= (optind);
+ name = (argc > 0) ? argv[0]: NULL;
+
+ if (prompt != NULL)
+ printf("%s", prompt);
+ if (timeout >= 0) {
+ when = time(NULL) + timeout;
+ while (!ischar())
+ if (time(NULL) >= when)
+ return(CMD_OK); /* is timeout an error? */
+ }
+
+ ngets(buf, sizeof(buf));
+
+ if (name != NULL)
+ setenv(name, buf, 1);
+ return(CMD_OK);
+}
+
+/*
+ * File pager
+ */
+COMMAND_SET(more, "more", "show contents of a file", command_more);
+
+static int
+command_more(int argc, char *argv[])
+{
+ int i;
+ int res;
+ char line[80];
+
+ res=0;
+ pager_open();
+ for (i = 1; (i < argc) && (res == 0); i++) {
+ sprintf(line, "*** FILE %s BEGIN ***\n", argv[i]);
+ if (pager_output(line))
+ break;
+ res = page_file(argv[i]);
+ if (!res) {
+ sprintf(line, "*** FILE %s END ***\n", argv[i]);
+ res = pager_output(line);
+ }
+ }
+ pager_close();
+
+ if (res == 0)
+ return CMD_OK;
+ else
+ return CMD_ERROR;
+}
+
+static int
+page_file(char *filename)
+{
+ int result;
+
+ result = pager_file(filename);
+
+ if (result == -1) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "error showing %s", filename);
+ }
+
+ return result;
+}
+
+/*
+ * List all disk-like devices
+ */
+COMMAND_SET(lsdev, "lsdev", "list all devices", command_lsdev);
+
+static int
+command_lsdev(int argc, char *argv[])
+{
+ int verbose, ch, i;
+ char line[80];
+
+ verbose = 0;
+ optind = 1;
+ optreset = 1;
+ while ((ch = getopt(argc, argv, "v")) != -1) {
+ switch(ch) {
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ /* getopt has already reported an error */
+ return(CMD_OK);
+ }
+ }
+ argv += (optind);
+ argc -= (optind);
+
+ pager_open();
+ for (i = 0; devsw[i] != NULL; i++) {
+ if (devsw[i]->dv_print != NULL){
+ if (devsw[i]->dv_print(verbose))
+ break;
+ } else {
+ sprintf(line, "%s: (unknown)\n", devsw[i]->dv_name);
+ if (pager_output(line))
+ break;
+ }
+ }
+ pager_close();
+ return(CMD_OK);
+}
+
diff --git a/stand/common/console.c b/stand/common/console.c
new file mode 100644
index 0000000..f4ffc56
--- /dev/null
+++ b/stand/common/console.c
@@ -0,0 +1,302 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+/*
+ * Core console support
+ */
+
+static int cons_set(struct env_var *ev, int flags, const void *value);
+static int cons_find(const char *name);
+static int cons_check(const char *string);
+static int cons_change(const char *string);
+static int twiddle_set(struct env_var *ev, int flags, const void *value);
+
+/*
+ * Detect possible console(s) to use. If preferred console(s) have been
+ * specified, mark them as active. Else, mark the first probed console
+ * as active. Also create the console variable.
+ */
+void
+cons_probe(void)
+{
+ int cons;
+ int active;
+ char *prefconsole;
+
+ /* We want a callback to install the new value when this var changes. */
+ env_setenv("twiddle_divisor", EV_VOLATILE, "1", twiddle_set, env_nounset);
+
+ /* Do all console probes */
+ for (cons = 0; consoles[cons] != NULL; cons++) {
+ consoles[cons]->c_flags = 0;
+ consoles[cons]->c_probe(consoles[cons]);
+ }
+ /* Now find the first working one */
+ active = -1;
+ for (cons = 0; consoles[cons] != NULL && active == -1; cons++) {
+ consoles[cons]->c_flags = 0;
+ consoles[cons]->c_probe(consoles[cons]);
+ if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT))
+ active = cons;
+ }
+ /* Force a console even if all probes failed */
+ if (active == -1)
+ active = 0;
+
+ /* Check to see if a console preference has already been registered */
+ prefconsole = getenv("console");
+ if (prefconsole != NULL)
+ prefconsole = strdup(prefconsole);
+ if (prefconsole != NULL) {
+ unsetenv("console"); /* we want to replace this */
+ cons_change(prefconsole);
+ } else {
+ consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
+ consoles[active]->c_init(0);
+ prefconsole = strdup(consoles[active]->c_name);
+ }
+
+ printf("Consoles: ");
+ for (cons = 0; consoles[cons] != NULL; cons++)
+ if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT))
+ printf("%s ", consoles[cons]->c_desc);
+ printf("\n");
+
+ if (prefconsole != NULL) {
+ env_setenv("console", EV_VOLATILE, prefconsole, cons_set,
+ env_nounset);
+ free(prefconsole);
+ }
+}
+
+int
+getchar(void)
+{
+ int cons;
+ int rv;
+
+ /* Loop forever polling all active consoles */
+ for(;;)
+ for (cons = 0; consoles[cons] != NULL; cons++)
+ if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) ==
+ (C_PRESENTIN | C_ACTIVEIN) &&
+ ((rv = consoles[cons]->c_in()) != -1))
+ return(rv);
+}
+
+int
+ischar(void)
+{
+ int cons;
+
+ for (cons = 0; consoles[cons] != NULL; cons++)
+ if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) ==
+ (C_PRESENTIN | C_ACTIVEIN) &&
+ (consoles[cons]->c_ready() != 0))
+ return(1);
+ return(0);
+}
+
+void
+putchar(int c)
+{
+ int cons;
+
+ /* Expand newlines */
+ if (c == '\n')
+ putchar('\r');
+
+ for (cons = 0; consoles[cons] != NULL; cons++)
+ if ((consoles[cons]->c_flags & (C_PRESENTOUT | C_ACTIVEOUT)) ==
+ (C_PRESENTOUT | C_ACTIVEOUT))
+ consoles[cons]->c_out(c);
+}
+
+/*
+ * Find the console with the specified name.
+ */
+static int
+cons_find(const char *name)
+{
+ int cons;
+
+ for (cons = 0; consoles[cons] != NULL; cons++)
+ if (!strcmp(consoles[cons]->c_name, name))
+ return (cons);
+ return (-1);
+}
+
+/*
+ * Select one or more consoles.
+ */
+static int
+cons_set(struct env_var *ev, int flags, const void *value)
+{
+ int ret;
+
+ if ((value == NULL) || (cons_check(value) == 0)) {
+ /*
+ * Return CMD_OK instead of CMD_ERROR to prevent forth syntax error,
+ * which would prevent it processing any further loader.conf entries.
+ */
+ return (CMD_OK);
+ }
+
+ ret = cons_change(value);
+ if (ret != CMD_OK)
+ return (ret);
+
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return (CMD_OK);
+}
+
+/*
+ * Check that at least one the consoles listed in *string is valid
+ */
+static int
+cons_check(const char *string)
+{
+ int cons, found, failed;
+ char *curpos, *dup, *next;
+
+ dup = next = strdup(string);
+ found = failed = 0;
+ while (next != NULL) {
+ curpos = strsep(&next, " ,");
+ if (*curpos != '\0') {
+ cons = cons_find(curpos);
+ if (cons == -1) {
+ printf("console %s is invalid!\n", curpos);
+ failed++;
+ } else {
+ found++;
+ }
+ }
+ }
+
+ free(dup);
+
+ if (found == 0)
+ printf("no valid consoles!\n");
+
+ if (found == 0 || failed != 0) {
+ printf("Available consoles:\n");
+ for (cons = 0; consoles[cons] != NULL; cons++)
+ printf(" %s\n", consoles[cons]->c_name);
+ }
+
+ return (found);
+}
+
+/*
+ * Activate all the valid consoles listed in *string and disable all others.
+ */
+static int
+cons_change(const char *string)
+{
+ int cons, active;
+ char *curpos, *dup, *next;
+
+ /* Disable all consoles */
+ for (cons = 0; consoles[cons] != NULL; cons++) {
+ consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT);
+ }
+
+ /* Enable selected consoles */
+ dup = next = strdup(string);
+ active = 0;
+ while (next != NULL) {
+ curpos = strsep(&next, " ,");
+ if (*curpos == '\0')
+ continue;
+ cons = cons_find(curpos);
+ if (cons >= 0) {
+ consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
+ consoles[cons]->c_init(0);
+ if ((consoles[cons]->c_flags & (C_PRESENTIN | C_PRESENTOUT)) ==
+ (C_PRESENTIN | C_PRESENTOUT)) {
+ active++;
+ continue;
+ }
+
+ if (active != 0) {
+ /* If no consoles have initialised we wouldn't see this. */
+ printf("console %s failed to initialize\n", consoles[cons]->c_name);
+ }
+ }
+ }
+
+ free(dup);
+
+ if (active == 0) {
+ /* All requested consoles failed to initialise, try to recover. */
+ for (cons = 0; consoles[cons] != NULL; cons++) {
+ consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
+ consoles[cons]->c_init(0);
+ if ((consoles[cons]->c_flags &
+ (C_PRESENTIN | C_PRESENTOUT)) ==
+ (C_PRESENTIN | C_PRESENTOUT))
+ active++;
+ }
+
+ if (active == 0)
+ return (CMD_ERROR); /* Recovery failed. */
+ }
+
+ return (CMD_OK);
+}
+
+/*
+ * Change the twiddle divisor.
+ *
+ * The user can set the twiddle_divisor variable to directly control how fast
+ * the progress twiddle spins, useful for folks with slow serial consoles. The
+ * code to monitor changes to the variable and propagate them to the twiddle
+ * routines has to live somewhere. Twiddling is console-related so it's here.
+ */
+static int
+twiddle_set(struct env_var *ev, int flags, const void *value)
+{
+ u_long tdiv;
+ char * eptr;
+
+ tdiv = strtoul(value, &eptr, 0);
+ if (*(const char *)value == 0 || *eptr != 0) {
+ printf("invalid twiddle_divisor '%s'\n", (const char *)value);
+ return (CMD_ERROR);
+ }
+ twiddle_divisor((u_int)tdiv);
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+
+ return(CMD_OK);
+}
diff --git a/stand/common/dev_net.c b/stand/common/dev_net.c
new file mode 100644
index 0000000..c5b1e0a
--- /dev/null
+++ b/stand/common/dev_net.c
@@ -0,0 +1,435 @@
+/* $NetBSD: dev_net.c,v 1.23 2008/04/28 20:24:06 martin Exp $ */
+
+/*-
+ * Copyright (c) 1997 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Gordon W. Ross.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * This module implements a "raw device" interface suitable for
+ * use by the stand-alone I/O library NFS code. This interface
+ * does not support any "block" access, and exists only for the
+ * purpose of initializing the network interface, getting boot
+ * parameters, and performing the NFS mount.
+ *
+ * At open time, this does:
+ *
+ * find interface - netif_open()
+ * RARP for IP address - rarp_getipaddress()
+ * RPC/bootparams - callrpc(d, RPC_BOOTPARAMS, ...)
+ * RPC/mountd - nfs_mount(sock, ip, path)
+ *
+ * the root file handle from mountd is saved in a global
+ * for use by the NFS open code (NFS/lookup).
+ */
+
+#include <machine/stdarg.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <stand.h>
+#include <stddef.h>
+#include <string.h>
+#include <net.h>
+#include <netif.h>
+#include <bootp.h>
+#include <bootparam.h>
+
+#include "dev_net.h"
+#include "bootstrap.h"
+
+#ifdef NETIF_DEBUG
+int debug = 0;
+#endif
+
+static char *netdev_name;
+static int netdev_sock = -1;
+static int netdev_opens;
+
+static int net_init(void);
+static int net_open(struct open_file *, ...);
+static int net_close(struct open_file *);
+static void net_cleanup(void);
+static int net_strategy(void *, int, daddr_t, size_t, char *, size_t *);
+static int net_print(int);
+
+static int net_getparams(int sock);
+
+struct devsw netdev = {
+ "net",
+ DEVT_NET,
+ net_init,
+ net_strategy,
+ net_open,
+ net_close,
+ noioctl,
+ net_print,
+ net_cleanup
+};
+
+static struct uri_scheme {
+ const char *scheme;
+ int proto;
+} uri_schemes[] = {
+ { "tftp:/", NET_TFTP },
+ { "nfs:/", NET_NFS },
+};
+
+static int
+net_init(void)
+{
+
+ return (0);
+}
+
+/*
+ * Called by devopen after it sets f->f_dev to our devsw entry.
+ * This opens the low-level device and sets f->f_devdata.
+ * This is declared with variable arguments...
+ */
+static int
+net_open(struct open_file *f, ...)
+{
+ struct iodesc *d;
+ va_list args;
+ char *devname; /* Device part of file name (or NULL). */
+ int error = 0;
+
+ va_start(args, f);
+ devname = va_arg(args, char*);
+ va_end(args);
+
+ /* Before opening another interface, close the previous one first. */
+ if (netdev_sock >= 0 && strcmp(devname, netdev_name) != 0)
+ net_cleanup();
+
+ /* On first open, do netif open, mount, etc. */
+ if (netdev_opens == 0) {
+ /* Find network interface. */
+ if (netdev_sock < 0) {
+ netdev_sock = netif_open(devname);
+ if (netdev_sock < 0) {
+ printf("net_open: netif_open() failed\n");
+ return (ENXIO);
+ }
+ netdev_name = strdup(devname);
+#ifdef NETIF_DEBUG
+ if (debug)
+ printf("net_open: netif_open() succeeded\n");
+#endif
+ }
+ /*
+ * If network params were not set by netif_open(), try to get
+ * them via bootp, rarp, etc.
+ */
+ if (rootip.s_addr == 0) {
+ /* Get root IP address, and path, etc. */
+ error = net_getparams(netdev_sock);
+ if (error) {
+ /* getparams makes its own noise */
+ free(netdev_name);
+ netif_close(netdev_sock);
+ netdev_sock = -1;
+ return (error);
+ }
+ }
+ /*
+ * Set the variables required by the kernel's nfs_diskless
+ * mechanism. This is the minimum set of variables required to
+ * mount a root filesystem without needing to obtain additional
+ * info from bootp or other sources.
+ */
+ d = socktodesc(netdev_sock);
+ setenv("boot.netif.hwaddr", ether_sprintf(d->myea), 1);
+ setenv("boot.netif.ip", inet_ntoa(myip), 1);
+ setenv("boot.netif.netmask", intoa(netmask), 1);
+ setenv("boot.netif.gateway", inet_ntoa(gateip), 1);
+ setenv("boot.netif.server", inet_ntoa(rootip), 1);
+ if (netproto == NET_TFTP) {
+ setenv("boot.tftproot.server", inet_ntoa(rootip), 1);
+ setenv("boot.tftproot.path", rootpath, 1);
+ } else if (netproto == NET_NFS) {
+ setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
+ setenv("boot.nfsroot.path", rootpath, 1);
+ }
+ if (intf_mtu != 0) {
+ char mtu[16];
+ snprintf(mtu, sizeof(mtu), "%u", intf_mtu);
+ setenv("boot.netif.mtu", mtu, 1);
+ }
+
+ }
+ netdev_opens++;
+ f->f_devdata = &netdev_sock;
+ return (error);
+}
+
+static int
+net_close(struct open_file *f)
+{
+
+#ifdef NETIF_DEBUG
+ if (debug)
+ printf("net_close: opens=%d\n", netdev_opens);
+#endif
+
+ f->f_devdata = NULL;
+
+ return (0);
+}
+
+static void
+net_cleanup(void)
+{
+
+ if (netdev_sock >= 0) {
+#ifdef NETIF_DEBUG
+ if (debug)
+ printf("net_cleanup: calling netif_close()\n");
+#endif
+ rootip.s_addr = 0;
+ free(netdev_name);
+ netif_close(netdev_sock);
+ netdev_sock = -1;
+ }
+}
+
+static int
+net_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf,
+ size_t *rsize)
+{
+
+ return (EIO);
+}
+
+#define SUPPORT_BOOTP
+
+/*
+ * Get info for NFS boot: our IP address, our hostname,
+ * server IP address, and our root path on the server.
+ * There are two ways to do this: The old, Sun way,
+ * and the more modern, BOOTP way. (RFC951, RFC1048)
+ *
+ * The default is to use the Sun bootparams RPC
+ * (because that is what the kernel will do).
+ * MD code can make try_bootp initialied data,
+ * which will override this common definition.
+ */
+#ifdef SUPPORT_BOOTP
+int try_bootp = 1;
+#endif
+
+extern n_long ip_convertaddr(char *p);
+
+static int
+net_getparams(int sock)
+{
+ char buf[MAXHOSTNAMELEN];
+ n_long rootaddr, smask;
+
+#ifdef SUPPORT_BOOTP
+ /*
+ * Try to get boot info using BOOTP. If we succeed, then
+ * the server IP address, gateway, and root path will all
+ * be initialized. If any remain uninitialized, we will
+ * use RARP and RPC/bootparam (the Sun way) to get them.
+ */
+ if (try_bootp)
+ bootp(sock);
+ if (myip.s_addr != 0)
+ goto exit;
+#ifdef NETIF_DEBUG
+ if (debug)
+ printf("net_open: BOOTP failed, trying RARP/RPC...\n");
+#endif
+#endif
+
+ /*
+ * Use RARP to get our IP address. This also sets our
+ * netmask to the "natural" default for our address.
+ */
+ if (rarp_getipaddress(sock)) {
+ printf("net_open: RARP failed\n");
+ return (EIO);
+ }
+ printf("net_open: client addr: %s\n", inet_ntoa(myip));
+
+ /* Get our hostname, server IP address, gateway. */
+ if (bp_whoami(sock)) {
+ printf("net_open: bootparam/whoami RPC failed\n");
+ return (EIO);
+ }
+#ifdef NETIF_DEBUG
+ if (debug)
+ printf("net_open: client name: %s\n", hostname);
+#endif
+
+ /*
+ * Ignore the gateway from whoami (unreliable).
+ * Use the "gateway" parameter instead.
+ */
+ smask = 0;
+ gateip.s_addr = 0;
+ if (bp_getfile(sock, "gateway", &gateip, buf) == 0) {
+ /* Got it! Parse the netmask. */
+ smask = ip_convertaddr(buf);
+ }
+ if (smask) {
+ netmask = smask;
+#ifdef NETIF_DEBUG
+ if (debug)
+ printf("net_open: subnet mask: %s\n", intoa(netmask));
+#endif
+ }
+#ifdef NETIF_DEBUG
+ if (gateip.s_addr && debug)
+ printf("net_open: net gateway: %s\n", inet_ntoa(gateip));
+#endif
+
+ /* Get the root server and pathname. */
+ if (bp_getfile(sock, "root", &rootip, rootpath)) {
+ printf("net_open: bootparam/getfile RPC failed\n");
+ return (EIO);
+ }
+exit:
+ if ((rootaddr = net_parse_rootpath()) != INADDR_NONE)
+ rootip.s_addr = rootaddr;
+
+#ifdef NETIF_DEBUG
+ if (debug) {
+ printf("net_open: server addr: %s\n", inet_ntoa(rootip));
+ printf("net_open: server path: %s\n", rootpath);
+ }
+#endif
+
+ return (0);
+}
+
+static int
+net_print(int verbose)
+{
+ struct netif_driver *drv;
+ int i, d, cnt;
+ int ret = 0;
+
+ if (netif_drivers[0] == NULL)
+ return (ret);
+
+ printf("%s devices:", netdev.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ cnt = 0;
+ for (d = 0; netif_drivers[d]; d++) {
+ drv = netif_drivers[d];
+ for (i = 0; i < drv->netif_nifs; i++) {
+ printf("\t%s%d:", netdev.dv_name, cnt++);
+ if (verbose) {
+ printf(" (%s%d)", drv->netif_bname,
+ drv->netif_ifs[i].dif_unit);
+ }
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Parses the rootpath if present
+ *
+ * The rootpath format can be in the form
+ * <scheme>://ip/path
+ * <scheme>:/path
+ *
+ * For compatibility with previous behaviour it also accepts as an NFS scheme
+ * ip:/path
+ * /path
+ *
+ * If an ip is set it returns it in network byte order.
+ * The default scheme defined in the global netproto, if not set it defaults to
+ * NFS.
+ * It leaves just the pathname in the global rootpath.
+ */
+uint32_t
+net_parse_rootpath()
+{
+ n_long addr = htonl(INADDR_NONE);
+ size_t i;
+ char ip[FNAME_SIZE];
+ char *ptr, *val;
+
+ netproto = NET_NONE;
+
+ for (i = 0; i < nitems(uri_schemes); i++) {
+ if (strncmp(rootpath, uri_schemes[i].scheme,
+ strlen(uri_schemes[i].scheme)) != 0)
+ continue;
+
+ netproto = uri_schemes[i].proto;
+ break;
+ }
+ ptr = rootpath;
+ /* Fallback for compatibility mode */
+ if (netproto == NET_NONE) {
+ netproto = NET_NFS;
+ (void)strsep(&ptr, ":");
+ if (ptr != NULL) {
+ addr = inet_addr(rootpath);
+ bcopy(ptr, rootpath, strlen(ptr) + 1);
+ }
+ } else {
+ ptr += strlen(uri_schemes[i].scheme);
+ if (*ptr == '/') {
+ /* we are in the form <scheme>://, we do expect an ip */
+ ptr++;
+ /*
+ * XXX when http will be there we will need to check for
+ * a port, but right now we do not need it yet
+ */
+ val = strchr(ptr, '/');
+ if (val != NULL) {
+ snprintf(ip, sizeof(ip), "%.*s",
+ (int)((uintptr_t)val - (uintptr_t)ptr),
+ ptr);
+ addr = inet_addr(ip);
+ bcopy(val, rootpath, strlen(val) + 1);
+ }
+ } else {
+ ptr--;
+ bcopy(ptr, rootpath, strlen(ptr) + 1);
+ }
+ }
+
+ return (addr);
+}
diff --git a/stand/common/dev_net.h b/stand/common/dev_net.h
new file mode 100644
index 0000000..995b672
--- /dev/null
+++ b/stand/common/dev_net.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1998 Doug Rabson <dfr@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOT_DEV_NET_H_
+#define _BOOT_DEV_NET_H_
+
+extern struct devsw netdev;
+
+uint32_t net_parse_rootpath(void);
+
+#endif
diff --git a/stand/common/devopen.c b/stand/common/devopen.c
new file mode 100644
index 0000000..de6165c
--- /dev/null
+++ b/stand/common/devopen.c
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+
+int
+devopen(struct open_file *f, const char *fname, const char **file)
+{
+ struct devdesc *dev;
+ int result;
+
+ result = archsw.arch_getdev((void **)&dev, fname, file);
+ if (result)
+ return (result);
+
+ /* point to device-specific data so that device open can use it */
+ f->f_devdata = dev;
+ result = dev->d_dev->dv_open(f, dev);
+ if (result != 0) {
+ f->f_devdata = NULL;
+ free(dev);
+ return (result);
+ }
+
+ /* reference the devsw entry from the open_file structure */
+ f->f_dev = dev->d_dev;
+ return (0);
+}
+
+int
+devclose(struct open_file *f)
+{
+
+ if (f->f_devdata != NULL) {
+ free(f->f_devdata);
+ }
+ return (0);
+}
diff --git a/stand/common/disk.c b/stand/common/disk.c
new file mode 100644
index 0000000..4cb57d4
--- /dev/null
+++ b/stand/common/disk.c
@@ -0,0 +1,432 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/disk.h>
+#include <sys/queue.h>
+#include <stand.h>
+#include <stdarg.h>
+#include <bootstrap.h>
+#include <part.h>
+
+#include "disk.h"
+
+#ifdef DISK_DEBUG
+# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
+#else
+# define DEBUG(fmt, args...)
+#endif
+
+struct open_disk {
+ struct ptable *table;
+ uint64_t mediasize;
+ uint64_t entrysize;
+ u_int sectorsize;
+};
+
+struct print_args {
+ struct disk_devdesc *dev;
+ const char *prefix;
+ int verbose;
+};
+
+/* Convert size to a human-readable number. */
+static char *
+display_size(uint64_t size, u_int sectorsize)
+{
+ static char buf[80];
+ char unit;
+
+ size = size * sectorsize / 1024;
+ unit = 'K';
+ if (size >= 10485760000LL) {
+ size /= 1073741824;
+ unit = 'T';
+ } else if (size >= 10240000) {
+ size /= 1048576;
+ unit = 'G';
+ } else if (size >= 10000) {
+ size /= 1024;
+ unit = 'M';
+ }
+ sprintf(buf, "%ld%cB", (long)size, unit);
+ return (buf);
+}
+
+int
+ptblread(void *d, void *buf, size_t blocks, uint64_t offset)
+{
+ struct disk_devdesc *dev;
+ struct open_disk *od;
+
+ dev = (struct disk_devdesc *)d;
+ od = (struct open_disk *)dev->d_opendata;
+
+ /*
+ * The strategy function assumes the offset is in units of 512 byte
+ * sectors. For larger sector sizes, we need to adjust the offset to
+ * match the actual sector size.
+ */
+ offset *= (od->sectorsize / 512);
+ /*
+ * As the GPT backup partition is located at the end of the disk,
+ * to avoid reading past disk end, flag bcache not to use RA.
+ */
+ return (dev->d_dev->dv_strategy(dev, F_READ | F_NORA, offset,
+ blocks * od->sectorsize, (char *)buf, NULL));
+}
+
+#define PWIDTH 35
+static int
+ptable_print(void *arg, const char *pname, const struct ptable_entry *part)
+{
+ struct disk_devdesc dev;
+ struct print_args *pa, bsd;
+ struct open_disk *od;
+ struct ptable *table;
+ char line[80];
+ int res;
+
+ pa = (struct print_args *)arg;
+ od = (struct open_disk *)pa->dev->d_opendata;
+ sprintf(line, " %s%s: %s", pa->prefix, pname,
+ parttype2str(part->type));
+ if (pa->verbose)
+ sprintf(line, "%-*s%s", PWIDTH, line,
+ display_size(part->end - part->start + 1,
+ od->sectorsize));
+ strcat(line, "\n");
+ if (pager_output(line))
+ return 1;
+ res = 0;
+ if (part->type == PART_FREEBSD) {
+ /* Open slice with BSD label */
+ dev.d_dev = pa->dev->d_dev;
+ dev.d_unit = pa->dev->d_unit;
+ dev.d_slice = part->index;
+ dev.d_partition = -1;
+ if (disk_open(&dev, part->end - part->start + 1,
+ od->sectorsize) == 0) {
+ table = ptable_open(&dev, part->end - part->start + 1,
+ od->sectorsize, ptblread);
+ if (table != NULL) {
+ sprintf(line, " %s%s", pa->prefix, pname);
+ bsd.dev = pa->dev;
+ bsd.prefix = line;
+ bsd.verbose = pa->verbose;
+ res = ptable_iterate(table, &bsd, ptable_print);
+ ptable_close(table);
+ }
+ disk_close(&dev);
+ }
+ }
+
+ return (res);
+}
+#undef PWIDTH
+
+int
+disk_print(struct disk_devdesc *dev, char *prefix, int verbose)
+{
+ struct open_disk *od;
+ struct print_args pa;
+
+ /* Disk should be opened */
+ od = (struct open_disk *)dev->d_opendata;
+ pa.dev = dev;
+ pa.prefix = prefix;
+ pa.verbose = verbose;
+ return (ptable_iterate(od->table, &pa, ptable_print));
+}
+
+int
+disk_read(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
+{
+ struct open_disk *od;
+ int ret;
+
+ od = (struct open_disk *)dev->d_opendata;
+ ret = dev->d_dev->dv_strategy(dev, F_READ, dev->d_offset + offset,
+ blocks * od->sectorsize, buf, NULL);
+
+ return (ret);
+}
+
+int
+disk_write(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
+{
+ struct open_disk *od;
+ int ret;
+
+ od = (struct open_disk *)dev->d_opendata;
+ ret = dev->d_dev->dv_strategy(dev, F_WRITE, dev->d_offset + offset,
+ blocks * od->sectorsize, buf, NULL);
+
+ return (ret);
+}
+
+int
+disk_ioctl(struct disk_devdesc *dev, u_long cmd, void *data)
+{
+ struct open_disk *od = dev->d_opendata;
+
+ if (od == NULL)
+ return (ENOTTY);
+
+ switch (cmd) {
+ case DIOCGSECTORSIZE:
+ *(u_int *)data = od->sectorsize;
+ break;
+ case DIOCGMEDIASIZE:
+ if (dev->d_offset == 0)
+ *(uint64_t *)data = od->mediasize;
+ else
+ *(uint64_t *)data = od->entrysize * od->sectorsize;
+ break;
+ default:
+ return (ENOTTY);
+ }
+
+ return (0);
+}
+
+int
+disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize)
+{
+ struct open_disk *od;
+ struct ptable *table;
+ struct ptable_entry part;
+ int rc, slice, partition;
+
+ rc = 0;
+ /*
+ * While we are reading disk metadata, make sure we do it relative
+ * to the start of the disk
+ */
+ dev->d_offset = 0;
+ table = NULL;
+ slice = dev->d_slice;
+ partition = dev->d_partition;
+ od = (struct open_disk *)malloc(sizeof(struct open_disk));
+ if (od == NULL) {
+ DEBUG("no memory");
+ return (ENOMEM);
+ }
+ dev->d_opendata = od;
+ od->entrysize = 0;
+ od->mediasize = mediasize;
+ od->sectorsize = sectorsize;
+ DEBUG("%s unit %d, slice %d, partition %d => %p",
+ disk_fmtdev(dev), dev->d_unit, dev->d_slice, dev->d_partition, od);
+
+ /* Determine disk layout. */
+ od->table = ptable_open(dev, mediasize / sectorsize, sectorsize,
+ ptblread);
+ if (od->table == NULL) {
+ DEBUG("Can't read partition table");
+ rc = ENXIO;
+ goto out;
+ }
+
+ if (ptable_getsize(od->table, &mediasize) != 0) {
+ rc = ENXIO;
+ goto out;
+ }
+ if (mediasize > od->mediasize) {
+ od->mediasize = mediasize;
+ }
+
+ if (ptable_gettype(od->table) == PTABLE_BSD &&
+ partition >= 0) {
+ /* It doesn't matter what value has d_slice */
+ rc = ptable_getpart(od->table, &part, partition);
+ if (rc == 0) {
+ dev->d_offset = part.start;
+ od->entrysize = part.end - part.start + 1;
+ }
+ } else if (slice >= 0) {
+ /* Try to get information about partition */
+ if (slice == 0)
+ rc = ptable_getbestpart(od->table, &part);
+ else
+ rc = ptable_getpart(od->table, &part, slice);
+ if (rc != 0) /* Partition doesn't exist */
+ goto out;
+ dev->d_offset = part.start;
+ od->entrysize = part.end - part.start + 1;
+ slice = part.index;
+ if (ptable_gettype(od->table) == PTABLE_GPT) {
+ partition = 255;
+ goto out; /* Nothing more to do */
+ } else if (partition == 255) {
+ /*
+ * When we try to open GPT partition, but partition
+ * table isn't GPT, reset d_partition value to -1
+ * and try to autodetect appropriate value.
+ */
+ partition = -1;
+ }
+ /*
+ * If d_partition < 0 and we are looking at a BSD slice,
+ * then try to read BSD label, otherwise return the
+ * whole MBR slice.
+ */
+ if (partition == -1 &&
+ part.type != PART_FREEBSD)
+ goto out;
+ /* Try to read BSD label */
+ table = ptable_open(dev, part.end - part.start + 1,
+ od->sectorsize, ptblread);
+ if (table == NULL) {
+ DEBUG("Can't read BSD label");
+ rc = ENXIO;
+ goto out;
+ }
+ /*
+ * If slice contains BSD label and d_partition < 0, then
+ * assume the 'a' partition. Otherwise just return the
+ * whole MBR slice, because it can contain ZFS.
+ */
+ if (partition < 0) {
+ if (ptable_gettype(table) != PTABLE_BSD)
+ goto out;
+ partition = 0;
+ }
+ rc = ptable_getpart(table, &part, partition);
+ if (rc != 0)
+ goto out;
+ dev->d_offset += part.start;
+ od->entrysize = part.end - part.start + 1;
+ }
+out:
+ if (table != NULL)
+ ptable_close(table);
+
+ if (rc != 0) {
+ if (od->table != NULL)
+ ptable_close(od->table);
+ free(od);
+ DEBUG("%s could not open", disk_fmtdev(dev));
+ } else {
+ /* Save the slice and partition number to the dev */
+ dev->d_slice = slice;
+ dev->d_partition = partition;
+ DEBUG("%s offset %lld => %p", disk_fmtdev(dev),
+ (long long)dev->d_offset, od);
+ }
+ return (rc);
+}
+
+int
+disk_close(struct disk_devdesc *dev)
+{
+ struct open_disk *od;
+
+ od = (struct open_disk *)dev->d_opendata;
+ DEBUG("%s closed => %p", disk_fmtdev(dev), od);
+ ptable_close(od->table);
+ free(od);
+ return (0);
+}
+
+char*
+disk_fmtdev(struct disk_devdesc *dev)
+{
+ static char buf[128];
+ char *cp;
+
+ cp = buf + sprintf(buf, "%s%d", dev->d_dev->dv_name, dev->d_unit);
+ if (dev->d_slice >= 0) {
+#ifdef LOADER_GPT_SUPPORT
+ if (dev->d_partition == 255) {
+ sprintf(cp, "p%d:", dev->d_slice);
+ return (buf);
+ } else
+#endif
+#ifdef LOADER_MBR_SUPPORT
+ cp += sprintf(cp, "s%d", dev->d_slice);
+#endif
+ }
+ if (dev->d_partition >= 0)
+ cp += sprintf(cp, "%c", dev->d_partition + 'a');
+ strcat(cp, ":");
+ return (buf);
+}
+
+int
+disk_parsedev(struct disk_devdesc *dev, const char *devspec, const char **path)
+{
+ int unit, slice, partition;
+ const char *np;
+ char *cp;
+
+ np = devspec;
+ unit = slice = partition = -1;
+ if (*np != '\0' && *np != ':') {
+ unit = strtol(np, &cp, 10);
+ if (cp == np)
+ return (EUNIT);
+#ifdef LOADER_GPT_SUPPORT
+ if (*cp == 'p') {
+ np = cp + 1;
+ slice = strtol(np, &cp, 10);
+ if (np == cp)
+ return (ESLICE);
+ /* we don't support nested partitions on GPT */
+ if (*cp != '\0' && *cp != ':')
+ return (EINVAL);
+ partition = 255;
+ } else
+#endif
+#ifdef LOADER_MBR_SUPPORT
+ if (*cp == 's') {
+ np = cp + 1;
+ slice = strtol(np, &cp, 10);
+ if (np == cp)
+ return (ESLICE);
+ }
+#endif
+ if (*cp != '\0' && *cp != ':') {
+ partition = *cp - 'a';
+ if (partition < 0)
+ return (EPART);
+ cp++;
+ }
+ } else
+ return (EINVAL);
+
+ if (*cp != '\0' && *cp != ':')
+ return (EINVAL);
+ dev->d_unit = unit;
+ dev->d_slice = slice;
+ dev->d_partition = partition;
+ if (path != NULL)
+ *path = (*cp == '\0') ? cp: cp + 1;
+ return (0);
+}
diff --git a/stand/common/disk.h b/stand/common/disk.h
new file mode 100644
index 0000000..51e1498
--- /dev/null
+++ b/stand/common/disk.h
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 2011 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Device descriptor for partitioned disks. To use, set the
+ * d_slice and d_partition variables as follows:
+ *
+ * Whole disk access:
+ *
+ * d_slice = -1
+ * d_partition = -1
+ *
+ * Whole MBR slice:
+ *
+ * d_slice = MBR slice number (typically 1..4)
+ * d_partition = -1
+ *
+ * BSD disklabel partition within an MBR slice:
+ *
+ * d_slice = MBR slice number (typically 1..4)
+ * d_partition = disklabel partition (typically 0..19)
+ *
+ * BSD disklabel partition on the true dedicated disk:
+ *
+ * d_slice = -1
+ * d_partition = disklabel partition (typically 0..19)
+ *
+ * GPT partition:
+ *
+ * d_slice = GPT partition number (typically 1..N)
+ * d_partition = 255
+ *
+ * For both MBR and GPT, to automatically find the 'best' slice or partition,
+ * set d_slice to zero. This uses the partition type to decide which partition
+ * to use according to the following list of preferences:
+ *
+ * FreeBSD (active)
+ * FreeBSD (inactive)
+ * Linux (active)
+ * Linux (inactive)
+ * DOS/Windows (active)
+ * DOS/Windows (inactive)
+ *
+ * Active MBR slices (marked as bootable) are preferred over inactive. GPT
+ * doesn't have the concept of active/inactive partitions. In both MBR and GPT,
+ * if there are multiple slices/partitions of a given type, the first one
+ * is chosen.
+ *
+ * The low-level disk device will typically call disk_open() from its open
+ * method to interpret the disk partition tables according to the rules above.
+ * This will initialize d_offset to the block offset of the start of the
+ * selected partition - this offset should be added to the offset passed to
+ * the device's strategy method.
+ */
+
+#ifndef _DISK_H
+#define _DISK_H
+
+struct disk_devdesc
+{
+ struct devsw *d_dev;
+ int d_type;
+ int d_unit;
+ void *d_opendata;
+ int d_slice;
+ int d_partition;
+ uint64_t d_offset;
+};
+
+enum disk_ioctl {
+ IOCTL_GET_BLOCKS,
+ IOCTL_GET_BLOCK_SIZE
+};
+
+/*
+ * Parse disk metadata and initialise dev->d_offset.
+ */
+extern int disk_open(struct disk_devdesc *, uint64_t, u_int);
+extern int disk_close(struct disk_devdesc *);
+extern int disk_ioctl(struct disk_devdesc *, u_long, void *);
+extern int disk_read(struct disk_devdesc *, void *, uint64_t, u_int);
+extern int disk_write(struct disk_devdesc *, void *, uint64_t, u_int);
+extern int ptblread(void *, void *, size_t, uint64_t);
+
+/*
+ * Print information about slices on a disk.
+ */
+extern int disk_print(struct disk_devdesc *, char *, int);
+extern char* disk_fmtdev(struct disk_devdesc *);
+extern int disk_parsedev(struct disk_devdesc *, const char *, const char **);
+
+#endif /* _DISK_H */
diff --git a/stand/common/help.common b/stand/common/help.common
new file mode 100644
index 0000000..3618501
--- /dev/null
+++ b/stand/common/help.common
@@ -0,0 +1,407 @@
+################################################################################
+# Thelp DDisplay command help
+
+ help [topic [subtopic]]
+ help index
+
+ The help command displays help on commands and their usage.
+
+ In command help, a term enclosed with <...> indicates a value as
+ described by the term. A term enclosed with [...] is optional,
+ and may not be required by all forms of the command.
+
+ Some commands may not be available. Use the '?' command to list
+ most available commands.
+
+################################################################################
+# T? DList available commands
+
+ ?
+
+ Lists all available commands.
+
+################################################################################
+# Tautoboot DBoot after a delay
+
+ autoboot [<delay> [<prompt>]]
+
+ Displays <prompt> or a default prompt, and counts down <delay> seconds
+ before attempting to boot. If <delay> is not specified, the default
+ value is 10.
+
+################################################################################
+# Tboot DBoot immediately
+
+ boot [<kernelname>] [-<arg> ...]
+
+ Boot the system. If arguments are specified, they are added to the
+ arguments for the kernel. If <kernelname> is specified, and a kernel
+ has not already been loaded, it will be booted instead of the default
+ kernel.
+
+################################################################################
+# Tbcachestat DGet disk block cache stats
+
+ bcachestat
+
+ Displays statistics about disk cache usage. For debugging only.
+
+################################################################################
+# Techo DEcho arguments
+
+ echo [-n] [<message>]
+
+ Emits <message>, with no trailing newline if -n is specified. This is
+ most useful in conjunction with scripts and the '@' line prefix.
+
+ Variables are substituted by prefixing them with $, eg.
+
+ echo Current device is $currdev
+
+ will print the current device.
+
+################################################################################
+# Tload DLoad a kernel or module
+
+ load [-t <type>] <filename>
+
+ Loads the module contained in <filename> into memory. If no other
+ modules are loaded, <filename> must be a kernel or the command will
+ fail.
+
+ If -t is specified, the module is loaded as raw data of <type>, for
+ later use by the kernel or other modules. <type> may be any string.
+
+################################################################################
+# Tls DList files
+
+ ls [-l] [<path>]
+
+ Displays a listing of files in the directory <path>, or the root
+ directory of the current device if <path> is not specified.
+
+ The -l argument displays file sizes as well; the process of obtaining
+ file sizes on some media may be very slow.
+
+################################################################################
+# Tlsdev DList devices
+
+ lsdev [-v]
+
+ List all of the devices from which it may be possible to load modules.
+ If -v is specified, print more details.
+
+################################################################################
+# Tlsmod DList modules
+
+ lsmod [-v]
+
+ List loaded modules. If [-v] is specified, print more details.
+
+################################################################################
+# Tmore DPage files
+
+ more <filename> [<filename> ...]
+
+ Show contents of text files. When displaying the contents of more,
+ than one file, if the user elects to quit displaying a file, the
+ remaining files will not be shown.
+
+################################################################################
+# Tpnpscan DScan for PnP devices
+
+ pnpscan [-v]
+
+ Scan for Plug-and-Play devices. This command is normally automatically
+ run as part of the boot process, in order to dynamically load modules
+ required for system operation.
+
+ If the -v argument is specified, details on the devices found will
+ be printed.
+
+################################################################################
+# Tset DSet a variable
+
+ set <variable name>
+ set <variable name>=<value>
+
+ The set command is used to set variables.
+
+################################################################################
+# Tset Sautoboot_delay DSet the default autoboot delay
+
+ set autoboot_delay=<value>
+
+ Sets the default delay for the autoboot command to <value> seconds.
+ Set value to -1 if you don't want to allow user to interrupt autoboot
+ process and escape to the loader prompt.
+
+################################################################################
+# Tset Sbootfile DSet the default boot file set
+
+ set bootfile=<filename>[;<filename>...]
+
+ Sets the default set of kernel boot filename(s). It may be overridden
+ by setting the bootfile variable to a semicolon-separated list of
+ filenames, each of which will be searched for in the module_path
+ directories. The default bootfile set is "kernel".
+
+################################################################################
+# Tset Sboot_askname DPrompt for root device
+
+ set boot_askname
+
+ Instructs the kernel to prompt the user for the name of the root device
+ when the kernel is booted.
+
+################################################################################
+# Tset Sboot_cdrom DMount root file system from CD-ROM
+
+ set boot_cdrom
+
+ Instructs the kernel to try to mount the root file system from CD-ROM.
+
+################################################################################
+# Tset Sboot_ddb DDrop to the kernel debugger (DDB)
+
+ set boot_ddb
+
+ Instructs the kernel to start in the DDB debugger, rather than
+ proceeding to initialize when booted.
+
+################################################################################
+# Tset Sboot_dfltroot DUse default root file system
+
+ set boot_dfltroot
+
+ Instructs the kernel to mount the statically compiled-in root
+ file system.
+
+################################################################################
+# Tset Sboot_gdb DSelect gdb-remote mode for the kernel debugger
+
+ set boot_gdb
+
+ Selects gdb-remote mode for the kernel debugger by default.
+
+################################################################################
+# Tset Sboot_multicons DUse multiple consoles
+
+ set boot_multicons
+
+ Enables multiple console support in the kernel early on boot.
+ In a running system, console configuration can be manipulated
+ by the conscontrol(8) utility.
+
+################################################################################
+# Tset Sboot_mute DMute the console
+
+ set boot_mute
+
+ All console output is suppressed when console is muted.
+ In a running system, the state of console muting can be
+ manipulated by the conscontrol(8) utility.
+
+################################################################################
+# Tset Sboot_pause DPause after each line during device probing
+
+ set boot_pause
+
+ During the device probe, pause after each line is printed.
+
+################################################################################
+# Tset Sboot_serial DUse serial console
+
+ set boot_serial
+
+ Force the use of a serial console even when an internal console
+ is present.
+
+################################################################################
+# Tset Sboot_single DStart system in single-user mode
+
+ set boot_single
+
+ Prevents the kernel from initiating a multi-user startup; instead,
+ a single-user mode will be entered when the kernel has finished
+ device probes.
+
+################################################################################
+# Tset Sboot_verbose DVerbose boot messages
+
+ set boot_verbose
+
+ Setting this variable causes extra debugging information to be printed
+ by the kernel during the boot phase.
+
+################################################################################
+# Tset Sconsole DSet the current console
+
+ set console[=<value>]
+
+ Sets the current console. If <value> is omitted, a list of valid
+ consoles will be displayed.
+
+################################################################################
+# Tset Scurrdev DSet the current device
+
+ set currdev=<device>
+
+ Selects the default device. See lsdev for available devices.
+
+################################################################################
+# Tset Sinit_path DSet the list of init candidates
+
+ set init_path=<path>[:<path>...]
+
+ Sets the list of binaries which the kernel will try to run as initial
+ process.
+
+
+################################################################################
+# Tset Smodule_path DSet the module search path
+
+ set module_path=<path>[;<path>...]
+
+ Sets the list of directories which will be searched in for modules
+ named in a load command or implicitly required by a dependency. The
+ default module_path is "/boot/modules" with the kernel directory
+ prepended.
+
+################################################################################
+# Tset Sprompt DSet the command prompt
+
+ set prompt=<value>
+
+ The command prompt is displayed when the loader is waiting for input.
+ Variable substitution is performed on the prompt. The default
+ prompt can be set with:
+
+ set prompt=\${interpret}
+
+################################################################################
+# Tset Srootdev DSet the root filesystem
+
+ set rootdev=<path>
+
+ By default the value of $currdev is used to set the root filesystem
+ when the kernel is booted. This can be overridden by setting
+ $rootdev explicitly.
+
+################################################################################
+# Tset Stunables DSet kernel tunable values
+
+ Various kernel tunable parameters can be overridden by specifying new
+ values in the environment.
+
+ set kern.ipc.nmbclusters=<value>
+
+ Set the number of mbuf clusters to be allocated. The value
+ cannot be set below the default determined when the kernel
+ was compiled.
+
+ set kern.ipc.nsfbufs=<value> NSFBUFS
+
+ Set the number of sendfile buffers to be allocated. This
+ overrides the value determined when the kernel was compiled.
+
+ set vm.kmem_size=<value> VM_KMEM_SIZE
+
+ Sets the size of kernel memory (bytes). This overrides
+ the value determined when the kernel was compiled.
+
+ set machdep.disable_mtrrs=1
+
+ Disable the use of i686 MTRRs (i386 only)
+
+ set net.inet.tcp.tcbhashsize=<value> TCBHASHSIZE
+
+ Overrides the compile-time set value of TCBHASHSIZE or
+ the preset default of 512. Must be a power of 2.
+
+ hw.syscons.sc_no_suspend_vtswitch=<value>
+
+ Disable VT switching on suspend.
+
+ value is 0 (default) or non-zero to enable.
+
+ set hw.physmem=<value> MAXMEM (i386 only)
+
+ Limits the amount of physical memory space available to
+ the system to <value> bytes. <value> may have a k, M or G
+ suffix to indicate kilobytes, megabytes and gigabytes
+ respectively. Note that the current i386 architecture
+ limits this value to 4GB.
+
+ On systems where memory cannot be accurately probed,
+ this option provides a hint as to the actual size of
+ system memory (which will be tested before use).
+
+ set hw.{acpi,pci}.host_start_mem=<value>
+
+ Sets the lowest address that the pci code will assign
+ when it doesn't have other information about the address
+ to assign (like from a pci bridge). This is only useful
+ in older systems without a pci bridge. Also, it only
+ impacts devices that the BIOS doesn't assign to, typically
+ CardBus bridges. The default <value> is 0x80000000, but
+ some systems need values like 0xf0000000, 0xfc000000 or
+ 0xfe000000 may be suitable for older systems (the older
+ the system, the higher the number typically should be).
+
+ set hw.pci.enable_io_modes=<value>
+
+ Enable PCI resources which are left off by some BIOSes
+ or are not enabled correctly by the device driver.
+
+ value is 1 (default), but this may cause problems with
+ some peripherals. Set to 0 to disable.
+
+################################################################################
+# Tshow DShow the values of variables
+
+ show [<variable>]
+
+ Displays the value of <variable>, or all variables if not specified.
+ Multiple paths can be separated with a semicolon.
+
+################################################################################
+# Tinclude DRead commands from a script file
+
+ include <filename> [<filename> ...]
+
+ The entire contents of <filename> are read into memory before executing
+ commands, so it is safe to source a file from removable media.
+
+################################################################################
+# Tread DRead input from the terminal
+
+ read [-t <value>] [-p <prompt>] [<variable name>]
+
+ The read command reads a line of input from the terminal. If the
+ -t argument is specified, it will return nothing if no input has been
+ received after <value> seconds. (Any keypress will cancel the
+ timeout).
+
+ If -p is specified, <prompt> is printed before reading input. No
+ newline is emitted after the prompt.
+
+ If a variable name is supplied, the variable is set to the value read,
+ less any terminating newline.
+
+################################################################################
+# Tunload DRemove all modules from memory
+
+ unload
+
+ This command removes any kernel and all loaded modules from memory.
+
+################################################################################
+# Tunset DUnset a variable
+
+ unset <variable name>
+
+ If allowed, the named variable's value is discarded and the variable
+ is removed.
+
+################################################################################
diff --git a/stand/common/install.c b/stand/common/install.c
new file mode 100644
index 0000000..8c19066
--- /dev/null
+++ b/stand/common/install.c
@@ -0,0 +1,355 @@
+/*-
+ * Copyright (c) 2008-2014, Juniper Networks, Inc.
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <stand.h>
+#include <net.h>
+#include <string.h>
+
+#include "bootstrap.h"
+
+extern struct in_addr servip;
+
+extern int pkgfs_init(const char *, struct fs_ops *);
+extern void pkgfs_cleanup(void);
+
+COMMAND_SET(install, "install", "install software package", command_install);
+
+static char *inst_kernel;
+static char **inst_modules;
+static char *inst_rootfs;
+static char *inst_loader_rc;
+
+static int
+setpath(char **what, char *val)
+{
+ char *path;
+ size_t len;
+ int rel;
+
+ len = strlen(val) + 1;
+ rel = (val[0] != '/') ? 1 : 0;
+ path = malloc(len + rel);
+ if (path == NULL)
+ return (ENOMEM);
+ path[0] = '/';
+ strcpy(path + rel, val);
+
+ *what = path;
+ return (0);
+}
+
+static int
+setmultipath(char ***what, char *val)
+{
+ char *s, *v;
+ int count, error, idx;
+
+ count = 0;
+ v = val;
+ do {
+ count++;
+ s = strchr(v, ',');
+ v = (s == NULL) ? NULL : s + 1;
+ } while (v != NULL);
+
+ *what = calloc(count + 1, sizeof(char *));
+ if (*what == NULL)
+ return (ENOMEM);
+
+ for (idx = 0; idx < count; idx++) {
+ s = strchr(val, ',');
+ if (s != NULL)
+ *s++ = '\0';
+ error = setpath(*what + idx, val);
+ if (error)
+ return (error);
+ val = s;
+ }
+
+ return (0);
+}
+
+static int
+read_metatags(int fd)
+{
+ char buf[1024];
+ char *p, *tag, *val;
+ ssize_t fsize;
+ int error;
+
+ fsize = read(fd, buf, sizeof(buf));
+ if (fsize == -1)
+ return (errno);
+
+ /*
+ * Assume that if we read a whole buffer worth of data, we
+ * haven't read the entire file. In other words, the buffer
+ * size must always be larger than the file size. That way
+ * we can append a '\0' and use standard string operations.
+ * Return an error if this is not possible.
+ */
+ if (fsize == sizeof(buf))
+ return (ENOMEM);
+
+ buf[fsize] = '\0';
+ error = 0;
+ tag = buf;
+ while (!error && *tag != '\0') {
+ val = strchr(tag, '=');
+ if (val == NULL) {
+ error = EINVAL;
+ break;
+ }
+ *val++ = '\0';
+ p = strchr(val, '\n');
+ if (p == NULL) {
+ error = EINVAL;
+ break;
+ }
+ *p++ = '\0';
+
+ if (strcmp(tag, "KERNEL") == 0)
+ error = setpath(&inst_kernel, val);
+ else if (strcmp(tag, "MODULES") == 0)
+ error = setmultipath(&inst_modules, val);
+ else if (strcmp(tag, "ROOTFS") == 0)
+ error = setpath(&inst_rootfs, val);
+ else if (strcmp(tag, "LOADER_RC") == 0)
+ error = setpath(&inst_loader_rc, val);
+
+ tag = p;
+ }
+
+ return (error);
+}
+
+static void
+cleanup(void)
+{
+ u_int i;
+
+ if (inst_kernel != NULL) {
+ free(inst_kernel);
+ inst_kernel = NULL;
+ }
+ if (inst_modules != NULL) {
+ i = 0;
+ while (inst_modules[i] != NULL)
+ free(inst_modules[i++]);
+ free(inst_modules);
+ inst_modules = NULL;
+ }
+ if (inst_rootfs != NULL) {
+ free(inst_rootfs);
+ inst_rootfs = NULL;
+ }
+ if (inst_loader_rc != NULL) {
+ free(inst_loader_rc);
+ inst_loader_rc = NULL;
+ }
+ pkgfs_cleanup();
+}
+
+/*
+ * usage: install URL
+ * where: URL = (tftp|file)://[host]/<package>
+ */
+static int
+install(char *pkgname)
+{
+ static char buf[256];
+ struct fs_ops *proto;
+ struct preloaded_file *fp;
+ char *s, *currdev;
+ const char *devname;
+ int error, fd, i, local;
+
+ s = strstr(pkgname, "://");
+ if (s == NULL)
+ goto invalid_url;
+
+ i = s - pkgname;
+ if (i == 4 && !strncasecmp(pkgname, "tftp", i)) {
+ devname = "net0";
+ proto = &tftp_fsops;
+ local = 0;
+ } else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
+ currdev = getenv("currdev");
+ if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) {
+ devname = "pxe0";
+ proto = NULL;
+ } else {
+ devname = "disk1";
+ proto = &dosfs_fsops;
+ }
+ local = 1;
+ } else
+ goto invalid_url;
+
+ s += 3;
+ if (*s == '\0')
+ goto invalid_url;
+
+ if (*s != '/' ) {
+ if (local)
+ goto invalid_url;
+
+ pkgname = strchr(s, '/');
+ if (pkgname == NULL)
+ goto invalid_url;
+
+ *pkgname = '\0';
+ servip.s_addr = inet_addr(s);
+ if (servip.s_addr == htonl(INADDR_NONE))
+ goto invalid_url;
+
+ setenv("serverip", inet_ntoa(servip), 1);
+
+ *pkgname = '/';
+ } else
+ pkgname = s;
+
+ if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) {
+ command_errmsg = "package name too long";
+ return (CMD_ERROR);
+ }
+ sprintf(buf, "%s:%s", devname, pkgname);
+ setenv("install_package", buf, 1);
+
+ error = pkgfs_init(buf, proto);
+ if (error) {
+ command_errmsg = "cannot open package";
+ goto fail;
+ }
+
+ /*
+ * Point of no return: unload anything that may have been
+ * loaded and prune the environment from harmful variables.
+ */
+ unload();
+ unsetenv("vfs.root.mountfrom");
+
+ /*
+ * read the metatags file.
+ */
+ fd = open("/metatags", O_RDONLY);
+ if (fd != -1) {
+ error = read_metatags(fd);
+ close(fd);
+ if (error) {
+ command_errmsg = "cannot load metatags";
+ goto fail;
+ }
+ }
+
+ s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
+ error = mod_loadkld(s, 0, NULL);
+ if (error) {
+ command_errmsg = "cannot load kernel from package";
+ goto fail;
+ }
+
+ /* If there is a loader.rc in the package, execute it */
+ s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc;
+ fd = open(s, O_RDONLY);
+ if (fd != -1) {
+ close(fd);
+ error = include(s);
+ if (error == CMD_ERROR)
+ goto fail;
+ }
+
+ i = 0;
+ while (inst_modules != NULL && inst_modules[i] != NULL) {
+ error = mod_loadkld(inst_modules[i], 0, NULL);
+ if (error) {
+ command_errmsg = "cannot load module(s) from package";
+ goto fail;
+ }
+ i++;
+ }
+
+ s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
+ if (file_loadraw(s, "mfs_root", 1) == NULL) {
+ error = errno;
+ command_errmsg = "cannot load root file system";
+ goto fail;
+ }
+
+ cleanup();
+
+ fp = file_findfile(NULL, NULL);
+ if (fp != NULL)
+ file_formats[fp->f_loader]->l_exec(fp);
+ error = CMD_ERROR;
+ command_errmsg = "unable to start installation";
+
+ fail:
+ sprintf(buf, "%s (error %d)", command_errmsg, error);
+ cleanup();
+ unload();
+ exclusive_file_system = NULL;
+ command_errmsg = buf; /* buf is static. */
+ return (CMD_ERROR);
+
+ invalid_url:
+ command_errmsg = "invalid URL";
+ return (CMD_ERROR);
+}
+
+static int
+command_install(int argc, char *argv[])
+{
+ int argidx;
+
+ unsetenv("install_format");
+
+ argidx = 1;
+ while (1) {
+ if (argc == argidx) {
+ command_errmsg =
+ "usage: install [--format] <URL>";
+ return (CMD_ERROR);
+ }
+ if (!strcmp(argv[argidx], "--format")) {
+ setenv("install_format", "yes", 1);
+ argidx++;
+ continue;
+ }
+ break;
+ }
+
+ return (install(argv[argidx]));
+}
diff --git a/stand/common/interp.c b/stand/common/interp.c
new file mode 100644
index 0000000..f4117b9
--- /dev/null
+++ b/stand/common/interp.c
@@ -0,0 +1,371 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Simple commandline interpreter, toplevel and misc.
+ *
+ * XXX may be obsoleted by BootFORTH or some other, better, interpreter.
+ */
+
+#include <stand.h>
+#include <string.h>
+#include "bootstrap.h"
+
+#ifdef BOOT_FORTH
+#include "ficl.h"
+#define RETURN(x) stackPushINT(bf_vm->pStack,!x); return(x)
+
+extern FICL_VM *bf_vm;
+#else
+#define RETURN(x) return(x)
+#endif
+
+#define MAXARGS 20 /* maximum number of arguments allowed */
+
+static void prompt(void);
+
+#ifndef BOOT_FORTH
+static int perform(int argc, char *argv[]);
+
+/*
+ * Perform the command
+ */
+int
+perform(int argc, char *argv[])
+{
+ int result;
+ struct bootblk_command **cmdp;
+ bootblk_cmd_t *cmd;
+
+ if (argc < 1)
+ return(CMD_OK);
+
+ /* set return defaults; a successful command will override these */
+ command_errmsg = command_errbuf;
+ strcpy(command_errbuf, "no error message");
+ cmd = NULL;
+ result = CMD_ERROR;
+
+ /* search the command set for the command */
+ SET_FOREACH(cmdp, Xcommand_set) {
+ if (((*cmdp)->c_name != NULL) && !strcmp(argv[0], (*cmdp)->c_name))
+ cmd = (*cmdp)->c_fn;
+ }
+ if (cmd != NULL) {
+ result = (cmd)(argc, argv);
+ } else {
+ command_errmsg = "unknown command";
+ }
+ RETURN(result);
+}
+#endif /* ! BOOT_FORTH */
+
+/*
+ * Interactive mode
+ */
+void
+interact(const char *rc)
+{
+ static char input[256]; /* big enough? */
+#ifndef BOOT_FORTH
+ int argc;
+ char **argv;
+#endif
+
+#ifdef BOOT_FORTH
+ bf_init((rc) ? "" : NULL);
+#endif
+
+ if (rc == NULL) {
+ /* Read our default configuration. */
+ include("/boot/loader.rc");
+ } else if (*rc != '\0')
+ include(rc);
+
+ printf("\n");
+
+ /*
+ * Before interacting, we might want to autoboot.
+ */
+ autoboot_maybe();
+
+ /*
+ * Not autobooting, go manual
+ */
+ printf("\nType '?' for a list of commands, 'help' for more detailed help.\n");
+ if (getenv("prompt") == NULL)
+ setenv("prompt", "${interpret}", 1);
+ if (getenv("interpret") == NULL)
+ setenv("interpret", "OK", 1);
+
+
+ for (;;) {
+ input[0] = '\0';
+ prompt();
+ ngets(input, sizeof(input));
+#ifdef BOOT_FORTH
+ bf_vm->sourceID.i = 0;
+ bf_run(input);
+#else
+ if (!parse(&argc, &argv, input)) {
+ if (perform(argc, argv))
+ printf("%s: %s\n", argv[0], command_errmsg);
+ free(argv);
+ } else {
+ printf("parse error\n");
+ }
+#endif
+ }
+}
+
+/*
+ * Read commands from a file, then execute them.
+ *
+ * We store the commands in memory and close the source file so that the media
+ * holding it can safely go away while we are executing.
+ *
+ * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so
+ * that the script won't stop if they fail).
+ */
+COMMAND_SET(include, "include", "read commands from a file", command_include);
+
+static int
+command_include(int argc, char *argv[])
+{
+ int i;
+ int res;
+ char **argvbuf;
+
+ /*
+ * Since argv is static, we need to save it here.
+ */
+ argvbuf = (char**) calloc((u_int)argc, sizeof(char*));
+ for (i = 0; i < argc; i++)
+ argvbuf[i] = strdup(argv[i]);
+
+ res=CMD_OK;
+ for (i = 1; (i < argc) && (res == CMD_OK); i++)
+ res = include(argvbuf[i]);
+
+ for (i = 0; i < argc; i++)
+ free(argvbuf[i]);
+ free(argvbuf);
+
+ return(res);
+}
+
+/*
+ * Header prepended to each line. The text immediately follows the header.
+ * We try to make this short in order to save memory -- the loader has
+ * limited memory available, and some of the forth files are very long.
+ */
+struct includeline
+{
+ struct includeline *next;
+#ifndef BOOT_FORTH
+ int flags;
+ int line;
+#define SL_QUIET (1<<0)
+#define SL_IGNOREERR (1<<1)
+#endif
+ char text[0];
+};
+
+int
+include(const char *filename)
+{
+ struct includeline *script, *se, *sp;
+ char input[256]; /* big enough? */
+#ifdef BOOT_FORTH
+ int res;
+ char *cp;
+ int prevsrcid, fd, line;
+#else
+ int argc,res;
+ char **argv, *cp;
+ int fd, flags, line;
+#endif
+
+ if (((fd = open(filename, O_RDONLY)) == -1)) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "can't open '%s': %s", filename, strerror(errno));
+ return(CMD_ERROR);
+ }
+
+ /*
+ * Read the script into memory.
+ */
+ script = se = NULL;
+ line = 0;
+
+ while (fgetstr(input, sizeof(input), fd) >= 0) {
+ line++;
+#ifdef BOOT_FORTH
+ cp = input;
+#else
+ flags = 0;
+ /* Discard comments */
+ if (strncmp(input+strspn(input, " "), "\\ ", 2) == 0)
+ continue;
+ cp = input;
+ /* Echo? */
+ if (input[0] == '@') {
+ cp++;
+ flags |= SL_QUIET;
+ }
+ /* Error OK? */
+ if (input[0] == '-') {
+ cp++;
+ flags |= SL_IGNOREERR;
+ }
+#endif
+ /* Allocate script line structure and copy line, flags */
+ if (*cp == '\0')
+ continue; /* ignore empty line, save memory */
+ sp = malloc(sizeof(struct includeline) + strlen(cp) + 1);
+ /* On malloc failure (it happens!), free as much as possible and exit */
+ if (sp == NULL) {
+ while (script != NULL) {
+ se = script;
+ script = script->next;
+ free(se);
+ }
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "file '%s' line %d: memory allocation failure - aborting",
+ filename, line);
+ return (CMD_ERROR);
+ }
+ strcpy(sp->text, cp);
+#ifndef BOOT_FORTH
+ sp->flags = flags;
+ sp->line = line;
+#endif
+ sp->next = NULL;
+
+ if (script == NULL) {
+ script = sp;
+ } else {
+ se->next = sp;
+ }
+ se = sp;
+ }
+ close(fd);
+
+ /*
+ * Execute the script
+ */
+#ifndef BOOT_FORTH
+ argv = NULL;
+#else
+ prevsrcid = bf_vm->sourceID.i;
+ bf_vm->sourceID.i = fd;
+#endif
+ res = CMD_OK;
+ for (sp = script; sp != NULL; sp = sp->next) {
+
+#ifdef BOOT_FORTH
+ res = bf_run(sp->text);
+ if (res != VM_OUTOFTEXT) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "Error while including %s, in the line:\n%s",
+ filename, sp->text);
+ res = CMD_ERROR;
+ break;
+ } else
+ res = CMD_OK;
+#else
+ /* print if not being quiet */
+ if (!(sp->flags & SL_QUIET)) {
+ prompt();
+ printf("%s\n", sp->text);
+ }
+
+ /* Parse the command */
+ if (!parse(&argc, &argv, sp->text)) {
+ if ((argc > 0) && (perform(argc, argv) != 0)) {
+ /* normal command */
+ printf("%s: %s\n", argv[0], command_errmsg);
+ if (!(sp->flags & SL_IGNOREERR)) {
+ res=CMD_ERROR;
+ break;
+ }
+ }
+ free(argv);
+ argv = NULL;
+ } else {
+ printf("%s line %d: parse error\n", filename, sp->line);
+ res=CMD_ERROR;
+ break;
+ }
+#endif
+ }
+#ifndef BOOT_FORTH
+ if (argv != NULL)
+ free(argv);
+#else
+ bf_vm->sourceID.i = prevsrcid;
+#endif
+ while(script != NULL) {
+ se = script;
+ script = script->next;
+ free(se);
+ }
+ return(res);
+}
+
+/*
+ * Emit the current prompt; use the same syntax as the parser
+ * for embedding environment variables.
+ */
+static void
+prompt(void)
+{
+ char *pr, *p, *cp, *ev;
+
+ if ((cp = getenv("prompt")) == NULL)
+ cp = ">";
+ pr = p = strdup(cp);
+
+ while (*p != 0) {
+ if ((*p == '$') && (*(p+1) == '{')) {
+ for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++)
+ ;
+ *cp = 0;
+ ev = getenv(p + 2);
+
+ if (ev != NULL)
+ printf("%s", ev);
+ p = cp + 1;
+ continue;
+ }
+ putchar(*p++);
+ }
+ putchar(' ');
+ free(pr);
+}
diff --git a/stand/common/interp_backslash.c b/stand/common/interp_backslash.c
new file mode 100644
index 0000000..09b8f57
--- /dev/null
+++ b/stand/common/interp_backslash.c
@@ -0,0 +1,167 @@
+/*-
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 29 August 1998
+ *
+ * Routine for doing backslash elimination.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include "bootstrap.h"
+
+#define DIGIT(x) (isdigit(x) ? (x) - '0' : islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A')
+
+/*
+ * backslash: Return malloc'd copy of str with all standard "backslash
+ * processing" done on it. Original can be free'd if desired.
+ */
+char *
+backslash(char *str)
+{
+ /*
+ * Remove backslashes from the strings. Turn \040 etc. into a single
+ * character (we allow eight bit values). Currently NUL is not
+ * allowed.
+ *
+ * Turn "\n" and "\t" into '\n' and '\t' characters. Etc.
+ *
+ */
+ char *new_str;
+ int seenbs = 0;
+ int i = 0;
+
+ if ((new_str = strdup(str)) == NULL)
+ return NULL;
+
+ while (*str) {
+ if (seenbs) {
+ seenbs = 0;
+ switch (*str) {
+ case '\\':
+ new_str[i++] = '\\';
+ str++;
+ break;
+
+ /* preserve backslashed quotes, dollar signs */
+ case '\'':
+ case '"':
+ case '$':
+ new_str[i++] = '\\';
+ new_str[i++] = *str++;
+ break;
+
+ case 'b':
+ new_str[i++] = '\b';
+ str++;
+ break;
+
+ case 'f':
+ new_str[i++] = '\f';
+ str++;
+ break;
+
+ case 'r':
+ new_str[i++] = '\r';
+ str++;
+ break;
+
+ case 'n':
+ new_str[i++] = '\n';
+ str++;
+ break;
+
+ case 's':
+ new_str[i++] = ' ';
+ str++;
+ break;
+
+ case 't':
+ new_str[i++] = '\t';
+ str++;
+ break;
+
+ case 'v':
+ new_str[i++] = '\13';
+ str++;
+ break;
+
+ case 'z':
+ str++;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': {
+ char val;
+
+ /* Three digit octal constant? */
+ if (*str >= '0' && *str <= '3' &&
+ *(str + 1) >= '0' && *(str + 1) <= '7' &&
+ *(str + 2) >= '0' && *(str + 2) <= '7') {
+
+ val = (DIGIT(*str) << 6) + (DIGIT(*(str + 1)) << 3) +
+ DIGIT(*(str + 2));
+
+ /* Allow null value if user really wants to shoot
+ at feet, but beware! */
+ new_str[i++] = val;
+ str += 3;
+ break;
+ }
+
+ /* One or two digit hex constant?
+ * If two are there they will both be taken.
+ * Use \z to split them up if this is not wanted.
+ */
+ if (*str == '0' &&
+ (*(str + 1) == 'x' || *(str + 1) == 'X') &&
+ isxdigit(*(str + 2))) {
+ val = DIGIT(*(str + 2));
+ if (isxdigit(*(str + 3))) {
+ val = (val << 4) + DIGIT(*(str + 3));
+ str += 4;
+ }
+ else
+ str += 3;
+ /* Yep, allow null value here too */
+ new_str[i++] = val;
+ break;
+ }
+ }
+ break;
+
+ default:
+ new_str[i++] = *str++;
+ break;
+ }
+ }
+ else {
+ if (*str == '\\') {
+ seenbs = 1;
+ str++;
+ }
+ else
+ new_str[i++] = *str++;
+ }
+ }
+
+ if (seenbs) {
+ /*
+ * The final character was a '\'. Put it in as a single backslash.
+ */
+ new_str[i++] = '\\';
+ }
+ new_str[i] = '\0';
+ return new_str;
+}
diff --git a/stand/common/interp_forth.c b/stand/common/interp_forth.c
new file mode 100644
index 0000000..a3b7776
--- /dev/null
+++ b/stand/common/interp_forth.c
@@ -0,0 +1,332 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h> /* to pick up __FreeBSD_version */
+#include <string.h>
+#include <stand.h>
+#include "bootstrap.h"
+#include "ficl.h"
+
+extern unsigned bootprog_rev;
+
+/* #define BFORTH_DEBUG */
+
+#ifdef BFORTH_DEBUG
+#define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
+#else
+#define DEBUG(fmt, args...)
+#endif
+
+/*
+ * Eventually, all builtin commands throw codes must be defined
+ * elsewhere, possibly bootstrap.h. For now, just this code, used
+ * just in this file, it is getting defined.
+ */
+#define BF_PARSE 100
+
+/*
+ * FreeBSD loader default dictionary cells
+ */
+#ifndef BF_DICTSIZE
+#define BF_DICTSIZE 10000
+#endif
+
+/*
+ * BootForth Interface to Ficl Forth interpreter.
+ */
+
+FICL_SYSTEM *bf_sys;
+FICL_VM *bf_vm;
+
+/*
+ * Shim for taking commands from BF and passing them out to 'standard'
+ * argv/argc command functions.
+ */
+static void
+bf_command(FICL_VM *vm)
+{
+ char *name, *line, *tail, *cp;
+ size_t len;
+ struct bootblk_command **cmdp;
+ bootblk_cmd_t *cmd;
+ int nstrings, i;
+ int argc, result;
+ char **argv;
+
+ /* Get the name of the current word */
+ name = vm->runningWord->name;
+
+ /* Find our command structure */
+ cmd = NULL;
+ SET_FOREACH(cmdp, Xcommand_set) {
+ if (((*cmdp)->c_name != NULL) && !strcmp(name, (*cmdp)->c_name))
+ cmd = (*cmdp)->c_fn;
+ }
+ if (cmd == NULL)
+ panic("callout for unknown command '%s'", name);
+
+ /* Check whether we have been compiled or are being interpreted */
+ if (stackPopINT(vm->pStack)) {
+ /*
+ * Get parameters from stack, in the format:
+ * an un ... a2 u2 a1 u1 n --
+ * Where n is the number of strings, a/u are pairs of
+ * address/size for strings, and they will be concatenated
+ * in LIFO order.
+ */
+ nstrings = stackPopINT(vm->pStack);
+ for (i = 0, len = 0; i < nstrings; i++)
+ len += stackFetch(vm->pStack, i * 2).i + 1;
+ line = malloc(strlen(name) + len + 1);
+ strcpy(line, name);
+
+ if (nstrings)
+ for (i = 0; i < nstrings; i++) {
+ len = stackPopINT(vm->pStack);
+ cp = stackPopPtr(vm->pStack);
+ strcat(line, " ");
+ strncat(line, cp, len);
+ }
+ } else {
+ /* Get remainder of invocation */
+ tail = vmGetInBuf(vm);
+ for (cp = tail, len = 0; cp != vm->tib.end && *cp != 0 && *cp != '\n'; cp++, len++)
+ ;
+
+ line = malloc(strlen(name) + len + 2);
+ strcpy(line, name);
+ if (len > 0) {
+ strcat(line, " ");
+ strncat(line, tail, len);
+ vmUpdateTib(vm, tail + len);
+ }
+ }
+ DEBUG("cmd '%s'", line);
+
+ command_errmsg = command_errbuf;
+ command_errbuf[0] = 0;
+ if (!parse(&argc, &argv, line)) {
+ result = (cmd)(argc, argv);
+ free(argv);
+ } else {
+ result=BF_PARSE;
+ }
+
+ switch (result) {
+ case CMD_CRIT:
+ printf("%s\n", command_errmsg);
+ break;
+ case CMD_FATAL:
+ panic("%s\n", command_errmsg);
+ }
+
+ free(line);
+ /*
+ * If there was error during nested ficlExec(), we may no longer have
+ * valid environment to return. Throw all exceptions from here.
+ */
+ if (result != CMD_OK)
+ vmThrow(vm, result);
+
+ /* This is going to be thrown!!! */
+ stackPushINT(vm->pStack,result);
+}
+
+/*
+ * Replace a word definition (a builtin command) with another
+ * one that:
+ *
+ * - Throw error results instead of returning them on the stack
+ * - Pass a flag indicating whether the word was compiled or is
+ * being interpreted.
+ *
+ * There is one major problem with builtins that cannot be overcome
+ * in anyway, except by outlawing it. We want builtins to behave
+ * differently depending on whether they have been compiled or they
+ * are being interpreted. Notice that this is *not* the interpreter's
+ * current state. For example:
+ *
+ * : example ls ; immediate
+ * : problem example ; \ "ls" gets executed while compiling
+ * example \ "ls" gets executed while interpreting
+ *
+ * Notice that, though the current state is different in the two
+ * invocations of "example", in both cases "ls" has been
+ * *compiled in*, which is what we really want.
+ *
+ * The problem arises when you tick the builtin. For example:
+ *
+ * : example-1 ['] ls postpone literal ; immediate
+ * : example-2 example-1 execute ; immediate
+ * : problem example-2 ;
+ * example-2
+ *
+ * We have no way, when we get EXECUTEd, of knowing what our behavior
+ * should be. Thus, our only alternative is to "outlaw" this. See RFI
+ * 0007, and ANS Forth Standard's appendix D, item 6.7 for a related
+ * problem, concerning compile semantics.
+ *
+ * The problem is compounded by the fact that "' builtin CATCH" is valid
+ * and desirable. The only solution is to create an intermediary word.
+ * For example:
+ *
+ * : my-ls ls ;
+ * : example ['] my-ls catch ;
+ *
+ * So, with the below implementation, here is a summary of the behavior
+ * of builtins:
+ *
+ * ls -l \ "interpret" behavior, ie,
+ * \ takes parameters from TIB
+ * : ex-1 s" -l" 1 ls ; \ "compile" behavior, ie,
+ * \ takes parameters from the stack
+ * : ex-2 ['] ls catch ; immediate \ undefined behavior
+ * : ex-3 ['] ls catch ; \ undefined behavior
+ * ex-2 ex-3 \ "interpret" behavior,
+ * \ catch works
+ * : ex-4 ex-2 ; \ "compile" behavior,
+ * \ catch does not work
+ * : ex-5 ex-3 ; immediate \ same as ex-2
+ * : ex-6 ex-3 ; \ same as ex-3
+ * : ex-7 ['] ex-1 catch ; \ "compile" behavior,
+ * \ catch works
+ * : ex-8 postpone ls ; immediate \ same as ex-2
+ * : ex-9 postpone ls ; \ same as ex-3
+ *
+ * As the definition below is particularly tricky, and it's side effects
+ * must be well understood by those playing with it, I'll be heavy on
+ * the comments.
+ *
+ * (if you edit this definition, pay attention to trailing spaces after
+ * each word -- I warned you! :-) )
+ */
+#define BUILTIN_CONSTRUCTOR \
+": builtin: " \
+ ">in @ " /* save the tib index pointer */ \
+ "' " /* get next word's xt */ \
+ "swap >in ! " /* point again to next word */ \
+ "create " /* create a new definition of the next word */ \
+ ", " /* save previous definition's xt */ \
+ "immediate " /* make the new definition an immediate word */ \
+ \
+ "does> " /* Now, the *new* definition will: */ \
+ "state @ if " /* if in compiling state: */ \
+ "1 postpone literal " /* pass 1 flag to indicate compile */ \
+ "@ compile, " /* compile in previous definition */ \
+ "postpone throw " /* throw stack-returned result */ \
+ "else " /* if in interpreting state: */ \
+ "0 swap " /* pass 0 flag to indicate interpret */ \
+ "@ execute " /* call previous definition */ \
+ "throw " /* throw stack-returned result */ \
+ "then ; "
+
+/*
+ * Initialise the Forth interpreter, create all our commands as words.
+ */
+void
+bf_init(const char *rc)
+{
+ struct bootblk_command **cmdp;
+ char create_buf[41]; /* 31 characters-long builtins */
+ int fd;
+
+ bf_sys = ficlInitSystem(BF_DICTSIZE);
+ bf_vm = ficlNewVM(bf_sys);
+
+ /* Put all private definitions in a "builtins" vocabulary */
+ ficlExec(bf_vm, "vocabulary builtins also builtins definitions");
+
+ /* Builtin constructor word */
+ ficlExec(bf_vm, BUILTIN_CONSTRUCTOR);
+
+ /* make all commands appear as Forth words */
+ SET_FOREACH(cmdp, Xcommand_set) {
+ ficlBuild(bf_sys, (char *)(*cmdp)->c_name, bf_command, FW_DEFAULT);
+ ficlExec(bf_vm, "forth definitions builtins");
+ sprintf(create_buf, "builtin: %s", (*cmdp)->c_name);
+ ficlExec(bf_vm, create_buf);
+ ficlExec(bf_vm, "builtins definitions");
+ }
+ ficlExec(bf_vm, "only forth definitions");
+
+ /* Export some version numbers so that code can detect the loader/host version */
+ ficlSetEnv(bf_sys, "FreeBSD_version", __FreeBSD_version);
+ ficlSetEnv(bf_sys, "loader_version", bootprog_rev);
+
+ /* try to load and run init file if present */
+ if (rc == NULL)
+ rc = "/boot/boot.4th";
+ if (*rc != '\0') {
+ fd = open(rc, O_RDONLY);
+ if (fd != -1) {
+ (void)ficlExecFD(bf_vm, fd);
+ close(fd);
+ }
+ }
+}
+
+/*
+ * Feed a line of user input to the Forth interpreter
+ */
+int
+bf_run(char *line)
+{
+ int result;
+
+ result = ficlExec(bf_vm, line);
+
+ DEBUG("ficlExec '%s' = %d", line, result);
+ switch (result) {
+ case VM_OUTOFTEXT:
+ case VM_ABORTQ:
+ case VM_QUIT:
+ case VM_ERREXIT:
+ break;
+ case VM_USEREXIT:
+ printf("No where to leave to!\n");
+ break;
+ case VM_ABORT:
+ printf("Aborted!\n");
+ break;
+ case BF_PARSE:
+ printf("Parse error!\n");
+ break;
+ default:
+ if (command_errmsg != NULL) {
+ printf("%s\n", command_errmsg);
+ command_errmsg = NULL;
+ }
+ }
+
+ if (result == VM_USEREXIT)
+ panic("interpreter exit");
+ setenv("interpret", bf_vm->state ? "" : "OK", 1);
+
+ return (result);
+}
diff --git a/stand/common/interp_parse.c b/stand/common/interp_parse.c
new file mode 100644
index 0000000..8d8a2e2
--- /dev/null
+++ b/stand/common/interp_parse.c
@@ -0,0 +1,222 @@
+/*-
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 29 August 1998
+ *
+ * The meat of the simple parser.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include "bootstrap.h"
+
+static void clean(void);
+static int insert(int *argcp, char *buf);
+static char *variable_lookup(char *name);
+
+#define PARSE_BUFSIZE 1024 /* maximum size of one element */
+#define MAXARGS 20 /* maximum number of elements */
+static char *args[MAXARGS];
+
+/*
+ * parse: accept a string of input and "parse" it for backslash
+ * substitutions and environment variable expansions (${var}),
+ * returning an argc/argv style vector of whitespace separated
+ * arguments. Returns 0 on success, 1 on failure (ok, ok, so I
+ * wimped-out on the error codes! :).
+ *
+ * Note that the argv array returned must be freed by the caller, but
+ * we own the space allocated for arguments and will free that on next
+ * invocation. This allows argv consumers to modify the array if
+ * required.
+ *
+ * NB: environment variables that expand to more than one whitespace
+ * separated token will be returned as a single argv[] element, not
+ * split in turn. Expanded text is also immune to further backslash
+ * elimination or expansion since this is a one-pass, non-recursive
+ * parser. You didn't specify more than this so if you want more, ask
+ * me. - jkh
+ */
+
+#define PARSE_FAIL(expr) \
+if (expr) { \
+ printf("fail at line %d\n", __LINE__); \
+ clean(); \
+ free(copy); \
+ free(buf); \
+ return 1; \
+}
+
+/* Accept the usual delimiters for a variable, returning counterpart */
+static char
+isdelim(int ch)
+{
+ if (ch == '{')
+ return '}';
+ else if (ch == '(')
+ return ')';
+ return '\0';
+}
+
+static int
+isquote(int ch)
+{
+ return (ch == '\'');
+}
+
+static int
+isdquote(int ch)
+{
+ return (ch == '"');
+}
+
+int
+parse(int *argc, char ***argv, char *str)
+{
+ int ac;
+ char *val, *p, *q, *copy = NULL;
+ size_t i = 0;
+ char token, tmp, quote, dquote, *buf;
+ enum { STR, VAR, WHITE } state;
+
+ ac = *argc = 0;
+ dquote = quote = 0;
+ if (!str || (p = copy = backslash(str)) == NULL)
+ return 1;
+
+ /* Initialize vector and state */
+ clean();
+ state = STR;
+ buf = (char *)malloc(PARSE_BUFSIZE);
+ token = 0;
+
+ /* And awaaaaaaaaay we go! */
+ while (*p) {
+ switch (state) {
+ case STR:
+ if ((*p == '\\') && p[1]) {
+ p++;
+ PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
+ buf[i++] = *p++;
+ } else if (isquote(*p)) {
+ quote = quote ? 0 : *p;
+ if (dquote) { /* keep quote */
+ PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
+ buf[i++] = *p++;
+ } else
+ ++p;
+ } else if (isdquote(*p)) {
+ dquote = dquote ? 0 : *p;
+ if (quote) { /* keep dquote */
+ PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
+ buf[i++] = *p++;
+ } else
+ ++p;
+ } else if (isspace(*p) && !quote && !dquote) {
+ state = WHITE;
+ if (i) {
+ buf[i] = '\0';
+ PARSE_FAIL(insert(&ac, buf));
+ i = 0;
+ }
+ ++p;
+ } else if (*p == '$' && !quote) {
+ token = isdelim(*(p + 1));
+ if (token)
+ p += 2;
+ else
+ ++p;
+ state = VAR;
+ } else {
+ PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
+ buf[i++] = *p++;
+ }
+ break;
+
+ case WHITE:
+ if (isspace(*p))
+ ++p;
+ else
+ state = STR;
+ break;
+
+ case VAR:
+ if (token) {
+ PARSE_FAIL((q = strchr(p, token)) == NULL);
+ } else {
+ q = p;
+ while (*q && !isspace(*q))
+ ++q;
+ }
+ tmp = *q;
+ *q = '\0';
+ if ((val = variable_lookup(p)) != NULL) {
+ size_t len = strlen(val);
+
+ strncpy(buf + i, val, PARSE_BUFSIZE - (i + 1));
+ i += min(len, PARSE_BUFSIZE - 1);
+ }
+ *q = tmp; /* restore value */
+ p = q + (token ? 1 : 0);
+ state = STR;
+ break;
+ }
+ }
+ /* missing terminating ' or " */
+ PARSE_FAIL(quote || dquote);
+ /* If at end of token, add it */
+ if (i && state == STR) {
+ buf[i] = '\0';
+ PARSE_FAIL(insert(&ac, buf));
+ }
+ args[ac] = NULL;
+ *argc = ac;
+ *argv = (char **)malloc((sizeof(char *) * ac + 1));
+ bcopy(args, *argv, sizeof(char *) * ac + 1);
+ free(buf);
+ free(copy);
+ return 0;
+}
+
+#define MAXARGS 20
+
+/* Clean vector space */
+static void
+clean(void)
+{
+ int i;
+
+ for (i = 0; i < MAXARGS; i++) {
+ if (args[i] != NULL) {
+ free(args[i]);
+ args[i] = NULL;
+ }
+ }
+}
+
+static int
+insert(int *argcp, char *buf)
+{
+ if (*argcp >= MAXARGS)
+ return 1;
+ args[(*argcp)++] = strdup(buf);
+ return 0;
+}
+
+static char *
+variable_lookup(char *name)
+{
+ /* XXX search "special variable" space first? */
+ return (char *)getenv(name);
+}
diff --git a/stand/common/isapnp.c b/stand/common/isapnp.c
new file mode 100644
index 0000000..5867600
--- /dev/null
+++ b/stand/common/isapnp.c
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 1998, Michael Smith
+ * Copyright (c) 1996, Sujal M. Patel
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Machine-independant ISA PnP enumerator implementing a subset of the
+ * ISA PnP specification.
+ */
+#include <stand.h>
+#include <string.h>
+#include <bootstrap.h>
+#include <isapnp.h>
+
+#define inb(x) (archsw.arch_isainb((x)))
+#define outb(x,y) (archsw.arch_isaoutb((x),(y)))
+
+static void isapnp_write(int d, int r);
+static void isapnp_send_Initiation_LFSR(void);
+static int isapnp_get_serial(u_int8_t *p);
+static int isapnp_isolation_protocol(void);
+static void isapnp_enumerate(void);
+
+/* PnP read data port */
+int isapnp_readport = 0;
+
+#define _PNP_ID_LEN 9
+
+struct pnphandler isapnphandler =
+{
+ "ISA bus",
+ isapnp_enumerate
+};
+
+static void
+isapnp_write(int d, int r)
+{
+ outb (_PNP_ADDRESS, d);
+ outb (_PNP_WRITE_DATA, r);
+}
+
+/*
+ * Send Initiation LFSR as described in "Plug and Play ISA Specification",
+ * Intel May 94.
+ */
+static void
+isapnp_send_Initiation_LFSR(void)
+{
+ int cur, i;
+
+ /* Reset the LSFR */
+ outb(_PNP_ADDRESS, 0);
+ outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */
+
+ cur = 0x6a;
+ outb(_PNP_ADDRESS, cur);
+
+ for (i = 1; i < 32; i++) {
+ cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
+ outb(_PNP_ADDRESS, cur);
+ }
+}
+
+/*
+ * Get the device's serial number. Returns 1 if the serial is valid.
+ */
+static int
+isapnp_get_serial(u_int8_t *data)
+{
+ int i, bit, valid = 0, sum = 0x6a;
+
+ bzero(data, _PNP_ID_LEN);
+ outb(_PNP_ADDRESS, SERIAL_ISOLATION);
+ for (i = 0; i < 72; i++) {
+ bit = inb(isapnp_readport) == 0x55;
+ delay(250); /* Delay 250 usec */
+
+ /* Can't Short Circuit the next evaluation, so 'and' is last */
+ bit = (inb(isapnp_readport) == 0xaa) && bit;
+ delay(250); /* Delay 250 usec */
+
+ valid = valid || bit;
+
+ if (i < 64)
+ sum = (sum >> 1) |
+ (((sum ^ (sum >> 1) ^ bit) << 7) & 0xff);
+
+ data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0);
+ }
+
+ valid = valid && (data[8] == sum);
+
+ return valid;
+}
+
+/*
+ * Fills the buffer with resource info from the device.
+ * Returns nonzero if the device fails to report
+ */
+static int
+isapnp_get_resource_info(u_int8_t *buffer, int len)
+{
+ int i, j;
+ u_char temp;
+
+ for (i = 0; i < len; i++) {
+ outb(_PNP_ADDRESS, STATUS);
+ for (j = 0; j < 100; j++) {
+ if ((inb(isapnp_readport)) & 0x1)
+ break;
+ delay(1);
+ }
+ if (j == 100) {
+ printf("PnP device failed to report resource data\n");
+ return(1);
+ }
+ outb(_PNP_ADDRESS, RESOURCE_DATA);
+ temp = inb(isapnp_readport);
+ if (buffer != NULL)
+ buffer[i] = temp;
+ }
+ return(0);
+}
+
+/*
+ * Scan Resource Data for useful information.
+ *
+ * We scan the resource data for compatible device IDs and
+ * identifier strings; we only take the first identifier string
+ * and assume it's for the card as a whole.
+ *
+ * Returns 0 if the scan completed OK, nonzero on error.
+ */
+static int
+isapnp_scan_resdata(struct pnpinfo *pi)
+{
+ u_char tag, resinfo[8];
+ u_int limit;
+ size_t large_len;
+ u_char *str;
+
+ limit = 1000;
+ while ((limit-- > 0) && !isapnp_get_resource_info(&tag, 1)) {
+ if (PNP_RES_TYPE(tag) == 0) {
+ /* Small resource */
+ switch (PNP_SRES_NUM(tag)) {
+
+ case COMP_DEVICE_ID:
+ /* Got a compatible device id resource */
+ if (isapnp_get_resource_info(resinfo, PNP_SRES_LEN(tag)))
+ return(1);
+ pnp_addident(pi, pnp_eisaformat(resinfo));
+
+ case END_TAG:
+ return(0);
+ break;
+
+ default:
+ /* Skip this resource */
+ if (isapnp_get_resource_info(NULL, PNP_SRES_LEN(tag)))
+ return(1);
+ break;
+ }
+ } else {
+ /* Large resource */
+ if (isapnp_get_resource_info(resinfo, 2))
+ return(1);
+
+ large_len = resinfo[1];
+ large_len = (large_len << 8) + resinfo[0];
+
+ switch(PNP_LRES_NUM(tag)) {
+
+ case ID_STRING_ANSI:
+ str = malloc(large_len + 1);
+ if (isapnp_get_resource_info(str, (ssize_t)large_len)) {
+ free(str);
+ return(1);
+ }
+ str[large_len] = 0;
+ if (pi->pi_desc == NULL) {
+ pi->pi_desc = (char *)str;
+ } else {
+ free(str);
+ }
+ break;
+
+ default:
+ /* Large resource, skip it */
+ if (isapnp_get_resource_info(NULL, (ssize_t)large_len))
+ return(1);
+ }
+ }
+ }
+ return(1);
+}
+
+/*
+ * Run the isolation protocol. Upon exiting, all cards are aware that
+ * they should use isapnp_readport as the READ_DATA port.
+ */
+static int
+isapnp_isolation_protocol(void)
+{
+ int csn;
+ struct pnpinfo *pi;
+ u_int8_t cardid[_PNP_ID_LEN];
+ int ndevs;
+
+ isapnp_send_Initiation_LFSR();
+ ndevs = 0;
+
+ isapnp_write(CONFIG_CONTROL, 0x04); /* Reset CSN for All Cards */
+
+ for (csn = 1; ; csn++) {
+ /* Wake up cards without a CSN (ie. all of them) */
+ isapnp_write(WAKE, 0);
+ isapnp_write(SET_RD_DATA, (isapnp_readport >> 2));
+ outb(_PNP_ADDRESS, SERIAL_ISOLATION);
+ delay(1000); /* Delay 1 msec */
+
+ if (isapnp_get_serial(cardid)) {
+ isapnp_write(SET_CSN, csn);
+ pi = pnp_allocinfo();
+ ndevs++;
+ pnp_addident(pi, pnp_eisaformat(cardid));
+ /* scan the card obtaining all the identifiers it holds */
+ if (isapnp_scan_resdata(pi)) {
+ pnp_freeinfo(pi); /* error getting data, ignore */
+ } else {
+ pnp_addinfo(pi);
+ }
+ } else {
+ break;
+ }
+ }
+ /* Move all cards to wait-for-key state */
+ while (--csn > 0) {
+ isapnp_send_Initiation_LFSR();
+ isapnp_write(WAKE, csn);
+ isapnp_write(CONFIG_CONTROL, 0x02);
+ delay(1000); /* XXX is it really necessary ? */
+ csn--;
+ }
+ return(ndevs);
+}
+
+/*
+ * Locate ISA-PnP devices and populate the supplied list.
+ */
+static void
+isapnp_enumerate(void)
+{
+ int pnp_rd_port;
+
+ /* Check for I/O port access */
+ if ((archsw.arch_isainb == NULL) || (archsw.arch_isaoutb == NULL))
+ return;
+
+ /*
+ * Validate a possibly-suggested read port value. If the autoscan failed
+ * last time, this will return us to autoscan mode again.
+ */
+ if ((isapnp_readport > 0) &&
+ (((isapnp_readport < 0x203) ||
+ (isapnp_readport > 0x3ff) ||
+ (isapnp_readport & 0x3) != 0x3)))
+ /* invalid, go look for ourselves */
+ isapnp_readport = 0;
+
+ if (isapnp_readport < 0) {
+ /* someone is telling us there is no ISA in the system */
+ return;
+
+ } else if (isapnp_readport > 0) {
+ /* Someone has told us where the port is/should be, or we found one last time */
+ isapnp_isolation_protocol();
+
+ } else {
+ /* No clues, look for it ourselves */
+ for (pnp_rd_port = 0x80; pnp_rd_port < 0xff; pnp_rd_port += 0x10) {
+ /* Look for something, quit when we find it */
+ isapnp_readport = (pnp_rd_port << 2) | 0x3;
+ if (isapnp_isolation_protocol() > 0)
+ break;
+ }
+ }
+}
diff --git a/stand/common/isapnp.h b/stand/common/isapnp.h
new file mode 100644
index 0000000..0f9956c
--- /dev/null
+++ b/stand/common/isapnp.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 1996, Sujal M. Patel
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Sujal M. Patel
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _I386_ISA_PNP_H_
+#define _I386_ISA_PNP_H_
+
+/* Maximum Number of PnP Devices. 8 should be plenty */
+#define MAX_PNP_CARDS 8
+/*
+ * the following is the maximum number of PnP Logical devices that
+ * userconfig can handle.
+ */
+#define MAX_PNP_LDN 20
+
+/* Static ports to access PnP state machine */
+#ifndef _KERNEL
+#ifdef PC98
+/* pnp.h is included from pnpinfo.c. */
+#define _PNP_ADDRESS 0x259
+#define _PNP_WRITE_DATA 0xa59
+#else
+#define _PNP_ADDRESS 0x279
+#define _PNP_WRITE_DATA 0xa79
+#endif
+#endif
+
+/* PnP Registers. Write to ADDRESS and then use WRITE/READ_DATA */
+#define SET_RD_DATA 0x00
+ /***
+ Writing to this location modifies the address of the port used for
+ reading from the Plug and Play ISA cards. Bits[7:0] become I/O
+ read port address bits[9:2]. Reads from this register are ignored.
+ ***/
+
+#define SERIAL_ISOLATION 0x01
+ /***
+ A read to this register causes a Plug and Play cards in the Isolation
+ state to compare one bit of the boards ID.
+ This register is read only.
+ ***/
+
+#define CONFIG_CONTROL 0x02
+ /***
+ Bit[2] Reset CSN to 0
+ Bit[1] Return to the Wait for Key state
+ Bit[0] Reset all logical devices and restore configuration
+ registers to their power-up values.
+
+ A write to bit[0] of this register performs a reset function on
+ all logical devices. This resets the contents of configuration
+ registers to their default state. All card's logical devices
+ enter their default state and the CSN is preserved.
+
+ A write to bit[1] of this register causes all cards to enter the
+ Wait for Key state but all CSNs are preserved and logical devices
+ are not affected.
+
+ A write to bit[2] of this register causes all cards to reset their
+ CSN to zero .
+
+ This register is write-only. The values are not sticky, that is,
+ hardware will automatically clear them and there is no need for
+ software to clear the bits.
+ ***/
+
+#define WAKE 0x03
+ /***
+ A write to this port will cause all cards that have a CSN that
+ matches the write data[7:0] to go from the Sleep state to the either
+ the Isolation state if the write data for this command is zero or
+ the Config state if the write data is not zero. Additionally, the
+ pointer to the byte-serial device is reset. This register is
+ writeonly.
+ ***/
+
+#define RESOURCE_DATA 0x04
+ /***
+ A read from this address reads the next byte of resource information.
+ The Status register must be polled until bit[0] is set before this
+ register may be read. This register is read only.
+ ***/
+
+#define STATUS 0x05
+ /***
+ Bit[0] when set indicates it is okay to read the next data byte
+ from the Resource Data register. This register is readonly.
+ ***/
+
+#define SET_CSN 0x06
+ /***
+ A write to this port sets a card's CSN. The CSN is a value uniquely
+ assigned to each ISA card after the serial identification process
+ so that each card may be individually selected during a Wake[CSN]
+ command. This register is read/write.
+ ***/
+
+#define SET_LDN 0x07
+ /***
+ Selects the current logical device. All reads and writes of memory,
+ I/O, interrupt and DMA configuration information access the registers
+ of the logical device written here. In addition, the I/O Range
+ Check and Activate commands operate only on the selected logical
+ device. This register is read/write. If a card has only 1 logical
+ device, this location should be a read-only value of 0x00.
+ ***/
+
+/*** addresses 0x08 - 0x1F Card Level Reserved for future use ***/
+/*** addresses 0x20 - 0x2F Card Level, Vendor Defined ***/
+
+#define ACTIVATE 0x30
+ /***
+ For each logical device there is one activate register that controls
+ whether or not the logical device is active on the ISA bus. Bit[0],
+ if set, activates the logical device. Bits[7:1] are reserved and
+ must return 0 on reads. This is a read/write register. Before a
+ logical device is activated, I/O range check must be disabled.
+ ***/
+
+#define IO_RANGE_CHECK 0x31
+ /***
+ This register is used to perform a conflict check on the I/O port
+ range programmed for use by a logical device.
+
+ Bit[7:2] Reserved and must return 0 on reads
+ Bit[1] Enable I/O Range check, if set then I/O Range Check
+ is enabled. I/O range check is only valid when the logical
+ device is inactive.
+
+ Bit[0], if set, forces the logical device to respond to I/O reads
+ of the logical device's assigned I/O range with a 0x55 when I/O
+ range check is in operation. If clear, the logical device drives
+ 0xAA. This register is read/write.
+ ***/
+
+/*** addr 0x32 - 0x37 Logical Device Control Reserved for future use ***/
+/*** addr 0x38 - 0x3F Logical Device Control Vendor Define ***/
+
+#define MEM_CONFIG 0x40
+ /***
+ Four memory resource registers per range, four ranges.
+ Fill with 0 if no ranges are enabled.
+
+ Offset 0: RW Memory base address bits[23:16]
+ Offset 1: RW Memory base address bits[15:8]
+ Offset 2: Memory control
+ Bit[1] specifies 8/16-bit control. This bit is set to indicate
+ 16-bit memory, and cleared to indicate 8-bit memory.
+ Bit[0], if cleared, indicates the next field can be used as a range
+ length for decode (implies range length and base alignment of memory
+ descriptor are equal).
+ Bit[0], if set, indicates the next field is the upper limit for
+ the address. - - Bit[0] is read-only.
+ Offset 3: RW upper limit or range len, bits[23:16]
+ Offset 4: RW upper limit or range len, bits[15:8]
+ Offset 5-Offset 7: filler, unused.
+ ***/
+
+#define IO_CONFIG_BASE 0x60
+ /***
+ Eight ranges, two bytes per range.
+ Offset 0: I/O port base address bits[15:8]
+ Offset 1: I/O port base address bits[7:0]
+ ***/
+
+#define IRQ_CONFIG 0x70
+ /***
+ Two entries, two bytes per entry.
+ Offset 0: RW interrupt level (1..15, 0=unused).
+ Offset 1: Bit[1]: level(1:hi, 0:low),
+ Bit[0]: type (1:level, 0:edge)
+ byte 1 can be readonly if 1 type of int is used.
+ ***/
+
+#define DRQ_CONFIG 0x74
+ /***
+ Two entries, one byte per entry. Bits[2:0] select
+ which DMA channel is in use for DMA 0. Zero selects DMA channel
+ 0, seven selects DMA channel 7. DMA channel 4, the cascade channel
+ is used to indicate no DMA channel is active.
+ ***/
+
+/*** 32-bit memory accesses are at 0x76 ***/
+
+/* Macros to parse Resource IDs */
+#define PNP_RES_TYPE(a) (a >> 7)
+#define PNP_SRES_NUM(a) (a >> 3)
+#define PNP_SRES_LEN(a) (a & 0x07)
+#define PNP_LRES_NUM(a) (a & 0x7f)
+
+/* Small Resource Item names */
+#define PNP_VERSION 0x1
+#define LOG_DEVICE_ID 0x2
+#define COMP_DEVICE_ID 0x3
+#define IRQ_FORMAT 0x4
+#define DMA_FORMAT 0x5
+#define START_DEPEND_FUNC 0x6
+#define END_DEPEND_FUNC 0x7
+#define IO_PORT_DESC 0x8
+#define FIXED_IO_PORT_DESC 0x9
+#define SM_RES_RESERVED 0xa-0xd
+#define SM_VENDOR_DEFINED 0xe
+#define END_TAG 0xf
+
+/* Large Resource Item names */
+#define MEMORY_RANGE_DESC 0x1
+#define ID_STRING_ANSI 0x2
+#define ID_STRING_UNICODE 0x3
+#define LG_VENDOR_DEFINED 0x4
+#define _32BIT_MEM_RANGE_DESC 0x5
+#define _32BIT_FIXED_LOC_DESC 0x6
+#define LG_RES_RESERVED 0x7-0x7f
+
+/*
+ * pnp_cinfo contains Configuration Information. They are used
+ * to communicate to the device driver the actual configuration
+ * of the device, and also by the userconfig menu to let the
+ * operating system override any configuration set by the bios.
+ *
+ */
+struct pnp_cinfo {
+ u_int vendor_id; /* board id */
+ u_int serial; /* Board's Serial Number */
+ u_long flags; /* OS-reserved flags */
+ u_char csn; /* assigned Card Select Number */
+ u_char ldn; /* Logical Device Number */
+ u_char enable; /* pnp enable */
+ u_char override; /* override bios parms (in userconfig) */
+ u_char irq[2]; /* IRQ Number */
+ u_char irq_type[2]; /* IRQ Type */
+ u_char drq[2];
+ u_short port[8]; /* The Base Address of the Port */
+ struct {
+ u_long base; /* Memory Base Address */
+ int control; /* Memory Control Register */
+ u_long range; /* Memory Range *OR* Upper Limit */
+ } mem[4];
+};
+
+#ifdef _KERNEL
+
+struct pnp_device {
+ char *pd_name;
+ char * (*pd_probe ) (u_long csn, u_long vendor_id);
+ void (*pd_attach ) (u_long csn, u_long vend_id, char * name,
+ struct isa_device *dev);
+ u_long *pd_count;
+ u_int *imask ;
+};
+
+struct _pnp_id {
+ u_long vendor_id;
+ u_long serial;
+ u_char checksum;
+} ;
+
+struct pnp_dlist_node {
+ struct pnp_device *pnp;
+ struct isa_device dev;
+ struct pnp_dlist_node *next;
+};
+
+typedef struct _pnp_id pnp_id;
+extern struct pnp_dlist_node *pnp_device_list;
+extern pnp_id pnp_devices[MAX_PNP_CARDS];
+extern struct pnp_cinfo pnp_ldn_overrides[MAX_PNP_LDN];
+extern int pnp_overrides_valid;
+
+/*
+ * these two functions are for use in drivers
+ */
+int read_pnp_parms(struct pnp_cinfo *d, int ldn);
+int write_pnp_parms(struct pnp_cinfo *d, int ldn);
+int enable_pnp_card(void);
+
+/*
+ * used by autoconfigure to actually probe and attach drivers
+ */
+void pnp_configure(void);
+
+#endif /* _KERNEL */
+
+#endif /* !_I386_ISA_PNP_H_ */
diff --git a/stand/common/load_elf.c b/stand/common/load_elf.c
new file mode 100644
index 0000000..679842b
--- /dev/null
+++ b/stand/common/load_elf.c
@@ -0,0 +1,1038 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 1998 Peter Wemm <peter@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/stdint.h>
+#include <string.h>
+#include <machine/elf.h>
+#include <stand.h>
+#define FREEBSD_ELF
+#include <link.h>
+
+#include "bootstrap.h"
+
+#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l)
+
+#if defined(__i386__) && __ELF_WORD_SIZE == 64
+#undef ELF_TARG_CLASS
+#undef ELF_TARG_MACH
+#define ELF_TARG_CLASS ELFCLASS64
+#define ELF_TARG_MACH EM_X86_64
+#endif
+
+typedef struct elf_file {
+ Elf_Phdr *ph;
+ Elf_Ehdr *ehdr;
+ Elf_Sym *symtab;
+ Elf_Hashelt *hashtab;
+ Elf_Hashelt nbuckets;
+ Elf_Hashelt nchains;
+ Elf_Hashelt *buckets;
+ Elf_Hashelt *chains;
+ Elf_Rel *rel;
+ size_t relsz;
+ Elf_Rela *rela;
+ size_t relasz;
+ char *strtab;
+ size_t strsz;
+ int fd;
+ caddr_t firstpage;
+ size_t firstlen;
+ int kernel;
+ u_int64_t off;
+} *elf_file_t;
+
+static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, u_int64_t loadaddr);
+static int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym);
+static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef,
+ Elf_Addr p, void *val, size_t len);
+static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef,
+ Elf_Addr p_start, Elf_Addr p_end);
+static symaddr_fn __elfN(symaddr);
+static char *fake_modname(const char *name);
+
+const char *__elfN(kerneltype) = "elf kernel";
+const char *__elfN(moduletype) = "elf module";
+
+u_int64_t __elfN(relocation_offset) = 0;
+
+static int
+__elfN(load_elf_header)(char *filename, elf_file_t ef)
+{
+ ssize_t bytes_read;
+ Elf_Ehdr *ehdr;
+ int err;
+
+ /*
+ * Open the image, read and validate the ELF header
+ */
+ if (filename == NULL) /* can't handle nameless */
+ return (EFTYPE);
+ if ((ef->fd = open(filename, O_RDONLY)) == -1)
+ return (errno);
+ ef->firstpage = malloc(PAGE_SIZE);
+ if (ef->firstpage == NULL) {
+ close(ef->fd);
+ return (ENOMEM);
+ }
+ bytes_read = read(ef->fd, ef->firstpage, PAGE_SIZE);
+ ef->firstlen = (size_t)bytes_read;
+ if (bytes_read < 0 || ef->firstlen <= sizeof(Elf_Ehdr)) {
+ err = EFTYPE; /* could be EIO, but may be small file */
+ goto error;
+ }
+ ehdr = ef->ehdr = (Elf_Ehdr *)ef->firstpage;
+
+ /* Is it ELF? */
+ if (!IS_ELF(*ehdr)) {
+ err = EFTYPE;
+ goto error;
+ }
+ if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */
+ ehdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
+ ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */
+ ehdr->e_version != EV_CURRENT ||
+ ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */
+ err = EFTYPE;
+ goto error;
+ }
+
+ return (0);
+
+error:
+ if (ef->firstpage != NULL) {
+ free(ef->firstpage);
+ ef->firstpage = NULL;
+ }
+ if (ef->fd != -1) {
+ close(ef->fd);
+ ef->fd = -1;
+ }
+ return (err);
+}
+
+/*
+ * Attempt to load the file (file) as an ELF module. It will be stored at
+ * (dest), and a pointer to a module structure describing the loaded object
+ * will be saved in (result).
+ */
+int
+__elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result)
+{
+ return (__elfN(loadfile_raw)(filename, dest, result, 0));
+}
+
+int
+__elfN(loadfile_raw)(char *filename, u_int64_t dest,
+ struct preloaded_file **result, int multiboot)
+{
+ struct preloaded_file *fp, *kfp;
+ struct elf_file ef;
+ Elf_Ehdr *ehdr;
+ int err;
+
+ fp = NULL;
+ bzero(&ef, sizeof(struct elf_file));
+ ef.fd = -1;
+
+ err = __elfN(load_elf_header)(filename, &ef);
+ if (err != 0)
+ return (err);
+
+ ehdr = ef.ehdr;
+
+ /*
+ * Check to see what sort of module we are.
+ */
+ kfp = file_findfile(NULL, __elfN(kerneltype));
+#ifdef __powerpc__
+ /*
+ * Kernels can be ET_DYN, so just assume the first loaded object is the
+ * kernel. This assumption will be checked later.
+ */
+ if (kfp == NULL)
+ ef.kernel = 1;
+#endif
+ if (ef.kernel || ehdr->e_type == ET_EXEC) {
+ /* Looks like a kernel */
+ if (kfp != NULL) {
+ printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: kernel already loaded\n");
+ err = EPERM;
+ goto oerr;
+ }
+ /*
+ * Calculate destination address based on kernel entrypoint.
+ *
+ * For ARM, the destination address is independent of any values in the
+ * elf header (an ARM kernel can be loaded at any 2MB boundary), so we
+ * leave dest set to the value calculated by archsw.arch_loadaddr() and
+ * passed in to this function.
+ */
+#ifndef __arm__
+ if (ehdr->e_type == ET_EXEC)
+ dest = (ehdr->e_entry & ~PAGE_MASK);
+#endif
+ if ((ehdr->e_entry & ~PAGE_MASK) == 0) {
+ printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: not a kernel (maybe static binary?)\n");
+ err = EPERM;
+ goto oerr;
+ }
+ ef.kernel = 1;
+
+ } else if (ehdr->e_type == ET_DYN) {
+ /* Looks like a kld module */
+ if (multiboot != 0) {
+ printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module as multiboot\n");
+ err = EPERM;
+ goto oerr;
+ }
+ if (kfp == NULL) {
+ printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module before kernel\n");
+ err = EPERM;
+ goto oerr;
+ }
+ if (strcmp(__elfN(kerneltype), kfp->f_type)) {
+ printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module with kernel type '%s'\n", kfp->f_type);
+ err = EPERM;
+ goto oerr;
+ }
+ /* Looks OK, got ahead */
+ ef.kernel = 0;
+
+ } else {
+ err = EFTYPE;
+ goto oerr;
+ }
+
+ if (archsw.arch_loadaddr != NULL)
+ dest = archsw.arch_loadaddr(LOAD_ELF, ehdr, dest);
+ else
+ dest = roundup(dest, PAGE_SIZE);
+
+ /*
+ * Ok, we think we should handle this.
+ */
+ fp = file_alloc();
+ if (fp == NULL) {
+ printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: cannot allocate module info\n");
+ err = EPERM;
+ goto out;
+ }
+ if (ef.kernel == 1 && multiboot == 0)
+ setenv("kernelname", filename, 1);
+ fp->f_name = strdup(filename);
+ if (multiboot == 0)
+ fp->f_type = strdup(ef.kernel ?
+ __elfN(kerneltype) : __elfN(moduletype));
+ else
+ fp->f_type = strdup("elf multiboot kernel");
+
+#ifdef ELF_VERBOSE
+ if (ef.kernel)
+ printf("%s entry at 0x%jx\n", filename, (uintmax_t)ehdr->e_entry);
+#else
+ printf("%s ", filename);
+#endif
+
+ fp->f_size = __elfN(loadimage)(fp, &ef, dest);
+ if (fp->f_size == 0 || fp->f_addr == 0)
+ goto ioerr;
+
+ /* save exec header as metadata */
+ file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr);
+
+ /* Load OK, return module pointer */
+ *result = (struct preloaded_file *)fp;
+ err = 0;
+ goto out;
+
+ ioerr:
+ err = EIO;
+ oerr:
+ file_discard(fp);
+ out:
+ if (ef.firstpage)
+ free(ef.firstpage);
+ if (ef.fd != -1)
+ close(ef.fd);
+ return(err);
+}
+
+/*
+ * With the file (fd) open on the image, and (ehdr) containing
+ * the Elf header, load the image at (off)
+ */
+static int
+__elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off)
+{
+ int i;
+ u_int j;
+ Elf_Ehdr *ehdr;
+ Elf_Phdr *phdr, *php;
+ Elf_Shdr *shdr;
+ char *shstr;
+ int ret;
+ vm_offset_t firstaddr;
+ vm_offset_t lastaddr;
+ size_t chunk;
+ ssize_t result;
+ Elf_Addr ssym, esym;
+ Elf_Dyn *dp;
+ Elf_Addr adp;
+ Elf_Addr ctors;
+ int ndp;
+ int symstrindex;
+ int symtabindex;
+ Elf_Size size;
+ u_int fpcopy;
+ Elf_Sym sym;
+ Elf_Addr p_start, p_end;
+
+ dp = NULL;
+ shdr = NULL;
+ ret = 0;
+ firstaddr = lastaddr = 0;
+ ehdr = ef->ehdr;
+ if (ehdr->e_type == ET_EXEC) {
+#if defined(__i386__) || defined(__amd64__)
+#if __ELF_WORD_SIZE == 64
+ off = - (off & 0xffffffffff000000ull);/* x86_64 relocates after locore */
+#else
+ off = - (off & 0xff000000u); /* i386 relocates after locore */
+#endif
+#elif defined(__powerpc__)
+ /*
+ * On the purely virtual memory machines like e500, the kernel is
+ * linked against its final VA range, which is most often not
+ * available at the loader stage, but only after kernel initializes
+ * and completes its VM settings. In such cases we cannot use p_vaddr
+ * field directly to load ELF segments, but put them at some
+ * 'load-time' locations.
+ */
+ if (off & 0xf0000000u) {
+ off = -(off & 0xf0000000u);
+ /*
+ * XXX the physical load address should not be hardcoded. Note
+ * that the Book-E kernel assumes that it's loaded at a 16MB
+ * boundary for now...
+ */
+ off += 0x01000000;
+ ehdr->e_entry += off;
+#ifdef ELF_VERBOSE
+ printf("Converted entry 0x%08x\n", ehdr->e_entry);
+#endif
+ } else
+ off = 0;
+#elif defined(__arm__) && !defined(EFI)
+ /*
+ * The elf headers in arm kernels specify virtual addresses in all
+ * header fields, even the ones that should be physical addresses.
+ * We assume the entry point is in the first page, and masking the page
+ * offset will leave us with the virtual address the kernel was linked
+ * at. We subtract that from the load offset, making 'off' into the
+ * value which, when added to a virtual address in an elf header,
+ * translates it to a physical address. We do the va->pa conversion on
+ * the entry point address in the header now, so that later we can
+ * launch the kernel by just jumping to that address.
+ *
+ * When booting from UEFI the copyin and copyout functions handle
+ * adjusting the location relative to the first virtual address.
+ * Because of this there is no need to adjust the offset or entry
+ * point address as these will both be handled by the efi code.
+ */
+ off -= ehdr->e_entry & ~PAGE_MASK;
+ ehdr->e_entry += off;
+#ifdef ELF_VERBOSE
+ printf("ehdr->e_entry 0x%08x, va<->pa off %llx\n", ehdr->e_entry, off);
+#endif
+#else
+ off = 0; /* other archs use direct mapped kernels */
+#endif
+ }
+ ef->off = off;
+
+ if (ef->kernel)
+ __elfN(relocation_offset) = off;
+
+ if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) {
+ printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: program header not within first page\n");
+ goto out;
+ }
+ phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff);
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ /* We want to load PT_LOAD segments only.. */
+ if (phdr[i].p_type != PT_LOAD)
+ continue;
+
+#ifdef ELF_VERBOSE
+ printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
+ (long)phdr[i].p_filesz, (long)phdr[i].p_offset,
+ (long)(phdr[i].p_vaddr + off),
+ (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
+#else
+ if ((phdr[i].p_flags & PF_W) == 0) {
+ printf("text=0x%lx ", (long)phdr[i].p_filesz);
+ } else {
+ printf("data=0x%lx", (long)phdr[i].p_filesz);
+ if (phdr[i].p_filesz < phdr[i].p_memsz)
+ printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz));
+ printf(" ");
+ }
+#endif
+ fpcopy = 0;
+ if (ef->firstlen > phdr[i].p_offset) {
+ fpcopy = ef->firstlen - phdr[i].p_offset;
+ archsw.arch_copyin(ef->firstpage + phdr[i].p_offset,
+ phdr[i].p_vaddr + off, fpcopy);
+ }
+ if (phdr[i].p_filesz > fpcopy) {
+ if (kern_pread(ef->fd, phdr[i].p_vaddr + off + fpcopy,
+ phdr[i].p_filesz - fpcopy, phdr[i].p_offset + fpcopy) != 0) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "_loadimage: read failed\n");
+ goto out;
+ }
+ }
+ /* clear space from oversized segments; eg: bss */
+ if (phdr[i].p_filesz < phdr[i].p_memsz) {
+#ifdef ELF_VERBOSE
+ printf(" (bss: 0x%lx-0x%lx)",
+ (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz),
+ (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
+#endif
+
+ kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz,
+ phdr[i].p_memsz - phdr[i].p_filesz);
+ }
+#ifdef ELF_VERBOSE
+ printf("\n");
+#endif
+
+ if (archsw.arch_loadseg != NULL)
+ archsw.arch_loadseg(ehdr, phdr + i, off);
+
+ if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off))
+ firstaddr = phdr[i].p_vaddr + off;
+ if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz))
+ lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz;
+ }
+ lastaddr = roundup(lastaddr, sizeof(long));
+
+ /*
+ * Get the section headers. We need this for finding the .ctors
+ * section as well as for loading any symbols. Both may be hard
+ * to do if reading from a .gz file as it involves seeking. I
+ * think the rule is going to have to be that you must strip a
+ * file to remove symbols before gzipping it.
+ */
+ chunk = ehdr->e_shnum * ehdr->e_shentsize;
+ if (chunk == 0 || ehdr->e_shoff == 0)
+ goto nosyms;
+ shdr = alloc_pread(ef->fd, ehdr->e_shoff, chunk);
+ if (shdr == NULL) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "_loadimage: failed to read section headers");
+ goto nosyms;
+ }
+ file_addmetadata(fp, MODINFOMD_SHDR, chunk, shdr);
+
+ /*
+ * Read the section string table and look for the .ctors section.
+ * We need to tell the kernel where it is so that it can call the
+ * ctors.
+ */
+ chunk = shdr[ehdr->e_shstrndx].sh_size;
+ if (chunk) {
+ shstr = alloc_pread(ef->fd, shdr[ehdr->e_shstrndx].sh_offset, chunk);
+ if (shstr) {
+ for (i = 0; i < ehdr->e_shnum; i++) {
+ if (strcmp(shstr + shdr[i].sh_name, ".ctors") != 0)
+ continue;
+ ctors = shdr[i].sh_addr;
+ file_addmetadata(fp, MODINFOMD_CTORS_ADDR, sizeof(ctors),
+ &ctors);
+ size = shdr[i].sh_size;
+ file_addmetadata(fp, MODINFOMD_CTORS_SIZE, sizeof(size),
+ &size);
+ break;
+ }
+ free(shstr);
+ }
+ }
+
+ /*
+ * Now load any symbols.
+ */
+ symtabindex = -1;
+ symstrindex = -1;
+ for (i = 0; i < ehdr->e_shnum; i++) {
+ if (shdr[i].sh_type != SHT_SYMTAB)
+ continue;
+ for (j = 0; j < ehdr->e_phnum; j++) {
+ if (phdr[j].p_type != PT_LOAD)
+ continue;
+ if (shdr[i].sh_offset >= phdr[j].p_offset &&
+ (shdr[i].sh_offset + shdr[i].sh_size <=
+ phdr[j].p_offset + phdr[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)
+ continue; /* alread loaded in a PT_LOAD above */
+ /* Save it for loading below */
+ symtabindex = i;
+ symstrindex = shdr[i].sh_link;
+ }
+ if (symtabindex < 0 || symstrindex < 0)
+ goto nosyms;
+
+ /* Ok, committed to a load. */
+#ifndef ELF_VERBOSE
+ printf("syms=[");
+#endif
+ ssym = lastaddr;
+ for (i = symtabindex; i >= 0; i = symstrindex) {
+#ifdef ELF_VERBOSE
+ char *secname;
+
+ switch(shdr[i].sh_type) {
+ case SHT_SYMTAB: /* Symbol table */
+ secname = "symtab";
+ break;
+ case SHT_STRTAB: /* String table */
+ secname = "strtab";
+ break;
+ default:
+ secname = "WHOA!!";
+ break;
+ }
+#endif
+
+ size = shdr[i].sh_size;
+ archsw.arch_copyin(&size, lastaddr, sizeof(size));
+ lastaddr += sizeof(size);
+
+#ifdef ELF_VERBOSE
+ printf("\n%s: 0x%jx@0x%jx -> 0x%jx-0x%jx", secname,
+ (uintmax_t)shdr[i].sh_size, (uintmax_t)shdr[i].sh_offset,
+ (uintmax_t)lastaddr, (uintmax_t)(lastaddr + shdr[i].sh_size));
+#else
+ if (i == symstrindex)
+ printf("+");
+ printf("0x%lx+0x%lx", (long)sizeof(size), (long)size);
+#endif
+
+ if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not seek for symbols - skipped!");
+ lastaddr = ssym;
+ ssym = 0;
+ goto nosyms;
+ }
+ result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size);
+ if (result < 0 || (size_t)result != shdr[i].sh_size) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped! (%ju != %ju)", (uintmax_t)result,
+ (uintmax_t)shdr[i].sh_size);
+ lastaddr = ssym;
+ ssym = 0;
+ goto nosyms;
+ }
+ /* Reset offsets relative to ssym */
+ lastaddr += shdr[i].sh_size;
+ lastaddr = roundup(lastaddr, sizeof(size));
+ if (i == symtabindex)
+ symtabindex = -1;
+ else if (i == symstrindex)
+ symstrindex = -1;
+ }
+ esym = lastaddr;
+#ifndef ELF_VERBOSE
+ printf("]");
+#endif
+
+ file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym);
+ file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym);
+
+nosyms:
+ printf("\n");
+
+ ret = lastaddr - firstaddr;
+ fp->f_addr = firstaddr;
+
+ php = NULL;
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ if (phdr[i].p_type == PT_DYNAMIC) {
+ php = phdr + i;
+ adp = php->p_vaddr;
+ file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(adp), &adp);
+ break;
+ }
+ }
+
+ if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */
+ goto out;
+
+ ndp = php->p_filesz / sizeof(Elf_Dyn);
+ if (ndp == 0)
+ goto out;
+ dp = malloc(php->p_filesz);
+ if (dp == NULL)
+ goto out;
+ archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz);
+
+ ef->strsz = 0;
+ for (i = 0; i < ndp; i++) {
+ if (dp[i].d_tag == 0)
+ break;
+ switch (dp[i].d_tag) {
+ case DT_HASH:
+ ef->hashtab = (Elf_Hashelt*)(uintptr_t)(dp[i].d_un.d_ptr + off);
+ break;
+ case DT_STRTAB:
+ ef->strtab = (char *)(uintptr_t)(dp[i].d_un.d_ptr + off);
+ break;
+ case DT_STRSZ:
+ ef->strsz = dp[i].d_un.d_val;
+ break;
+ case DT_SYMTAB:
+ ef->symtab = (Elf_Sym*)(uintptr_t)(dp[i].d_un.d_ptr + off);
+ break;
+ case DT_REL:
+ ef->rel = (Elf_Rel *)(uintptr_t)(dp[i].d_un.d_ptr + off);
+ break;
+ case DT_RELSZ:
+ ef->relsz = dp[i].d_un.d_val;
+ break;
+ case DT_RELA:
+ ef->rela = (Elf_Rela *)(uintptr_t)(dp[i].d_un.d_ptr + off);
+ break;
+ case DT_RELASZ:
+ ef->relasz = dp[i].d_un.d_val;
+ break;
+ default:
+ break;
+ }
+ }
+ if (ef->hashtab == NULL || ef->symtab == NULL ||
+ ef->strtab == NULL || ef->strsz == 0)
+ goto out;
+ COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets));
+ COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains));
+ ef->buckets = ef->hashtab + 2;
+ ef->chains = ef->buckets + ef->nbuckets;
+
+ if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0)
+ return 0;
+ p_start = sym.st_value + ef->off;
+ if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0)
+ return ENOENT;
+ p_end = sym.st_value + ef->off;
+
+ if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 0)
+ goto out;
+
+ if (ef->kernel) /* kernel must not depend on anything */
+ goto out;
+
+out:
+ if (dp)
+ free(dp);
+ if (shdr)
+ free(shdr);
+ return ret;
+}
+
+static char invalid_name[] = "bad";
+
+char *
+fake_modname(const char *name)
+{
+ const char *sp, *ep;
+ char *fp;
+ size_t len;
+
+ sp = strrchr(name, '/');
+ if (sp)
+ sp++;
+ else
+ sp = name;
+ ep = strrchr(name, '.');
+ if (ep) {
+ if (ep == name) {
+ sp = invalid_name;
+ ep = invalid_name + sizeof(invalid_name) - 1;
+ }
+ } else
+ ep = name + strlen(name);
+ len = ep - sp;
+ fp = malloc(len + 1);
+ if (fp == NULL)
+ return NULL;
+ memcpy(fp, sp, len);
+ fp[len] = '\0';
+ return fp;
+}
+
+#if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64
+struct mod_metadata64 {
+ int md_version; /* structure version MDTV_* */
+ int md_type; /* type of entry MDT_* */
+ u_int64_t md_data; /* specific data */
+ u_int64_t md_cval; /* common string label */
+};
+#endif
+#if defined(__amd64__) && __ELF_WORD_SIZE == 32
+struct mod_metadata32 {
+ int md_version; /* structure version MDTV_* */
+ int md_type; /* type of entry MDT_* */
+ u_int32_t md_data; /* specific data */
+ u_int32_t md_cval; /* common string label */
+};
+#endif
+
+int
+__elfN(load_modmetadata)(struct preloaded_file *fp, u_int64_t dest)
+{
+ struct elf_file ef;
+ int err, i, j;
+ Elf_Shdr *sh_meta, *shdr = NULL;
+ Elf_Shdr *sh_data[2];
+ char *shstrtab = NULL;
+ size_t size;
+ Elf_Addr p_start, p_end;
+
+ bzero(&ef, sizeof(struct elf_file));
+ ef.fd = -1;
+
+ err = __elfN(load_elf_header)(fp->f_name, &ef);
+ if (err != 0)
+ goto out;
+
+ if (ef.kernel == 1 || ef.ehdr->e_type == ET_EXEC) {
+ ef.kernel = 1;
+ } else if (ef.ehdr->e_type != ET_DYN) {
+ err = EFTYPE;
+ goto out;
+ }
+
+ size = ef.ehdr->e_shnum * ef.ehdr->e_shentsize;
+ shdr = alloc_pread(ef.fd, ef.ehdr->e_shoff, size);
+ if (shdr == NULL) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ /* Load shstrtab. */
+ shstrtab = alloc_pread(ef.fd, shdr[ef.ehdr->e_shstrndx].sh_offset,
+ shdr[ef.ehdr->e_shstrndx].sh_size);
+ if (shstrtab == NULL) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "load_modmetadata: unable to load shstrtab\n");
+ err = EFTYPE;
+ goto out;
+ }
+
+ /* Find set_modmetadata_set and data sections. */
+ sh_data[0] = sh_data[1] = sh_meta = NULL;
+ for (i = 0, j = 0; i < ef.ehdr->e_shnum; i++) {
+ if (strcmp(&shstrtab[shdr[i].sh_name],
+ "set_modmetadata_set") == 0) {
+ sh_meta = &shdr[i];
+ }
+ if ((strcmp(&shstrtab[shdr[i].sh_name], ".data") == 0) ||
+ (strcmp(&shstrtab[shdr[i].sh_name], ".rodata") == 0)) {
+ sh_data[j++] = &shdr[i];
+ }
+ }
+ if (sh_meta == NULL || sh_data[0] == NULL || sh_data[1] == NULL) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "load_modmetadata: unable to find set_modmetadata_set or data sections\n");
+ err = EFTYPE;
+ goto out;
+ }
+
+ /* Load set_modmetadata_set into memory */
+ err = kern_pread(ef.fd, dest, sh_meta->sh_size, sh_meta->sh_offset);
+ if (err != 0) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "load_modmetadata: unable to load set_modmetadata_set: %d\n", err);
+ goto out;
+ }
+ p_start = dest;
+ p_end = dest + sh_meta->sh_size;
+ dest += sh_meta->sh_size;
+
+ /* Load data sections into memory. */
+ err = kern_pread(ef.fd, dest, sh_data[0]->sh_size,
+ sh_data[0]->sh_offset);
+ if (err != 0) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "load_modmetadata: unable to load data: %d\n", err);
+ goto out;
+ }
+
+ /*
+ * We have to increment the dest, so that the offset is the same into
+ * both the .rodata and .data sections.
+ */
+ ef.off = -(sh_data[0]->sh_addr - dest);
+ dest += (sh_data[1]->sh_addr - sh_data[0]->sh_addr);
+
+ err = kern_pread(ef.fd, dest, sh_data[1]->sh_size,
+ sh_data[1]->sh_offset);
+ if (err != 0) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "load_modmetadata: unable to load data: %d\n", err);
+ goto out;
+ }
+
+ err = __elfN(parse_modmetadata)(fp, &ef, p_start, p_end);
+ if (err != 0) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "load_modmetadata: unable to parse metadata: %d\n", err);
+ goto out;
+ }
+
+out:
+ if (shstrtab != NULL)
+ free(shstrtab);
+ if (shdr != NULL)
+ free(shdr);
+ if (ef.firstpage != NULL)
+ free(ef.firstpage);
+ if (ef.fd != -1)
+ close(ef.fd);
+ return (err);
+}
+
+int
+__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef,
+ Elf_Addr p_start, Elf_Addr p_end)
+{
+ struct mod_metadata md;
+#if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64
+ struct mod_metadata64 md64;
+#elif defined(__amd64__) && __ELF_WORD_SIZE == 32
+ struct mod_metadata32 md32;
+#endif
+ struct mod_depend *mdepend;
+ struct mod_version mver;
+ char *s;
+ int error, modcnt, minfolen;
+ Elf_Addr v, p;
+
+ modcnt = 0;
+ p = p_start;
+ while (p < p_end) {
+ COPYOUT(p, &v, sizeof(v));
+ error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v));
+ if (error == EOPNOTSUPP)
+ v += ef->off;
+ else if (error != 0)
+ return (error);
+#if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64
+ COPYOUT(v, &md64, sizeof(md64));
+ error = __elfN(reloc_ptr)(fp, ef, v, &md64, sizeof(md64));
+ if (error == EOPNOTSUPP) {
+ md64.md_cval += ef->off;
+ md64.md_data += ef->off;
+ } else if (error != 0)
+ return (error);
+ md.md_version = md64.md_version;
+ md.md_type = md64.md_type;
+ md.md_cval = (const char *)(uintptr_t)md64.md_cval;
+ md.md_data = (void *)(uintptr_t)md64.md_data;
+#elif defined(__amd64__) && __ELF_WORD_SIZE == 32
+ COPYOUT(v, &md32, sizeof(md32));
+ error = __elfN(reloc_ptr)(fp, ef, v, &md32, sizeof(md32));
+ if (error == EOPNOTSUPP) {
+ md32.md_cval += ef->off;
+ md32.md_data += ef->off;
+ } else if (error != 0)
+ return (error);
+ md.md_version = md32.md_version;
+ md.md_type = md32.md_type;
+ md.md_cval = (const char *)(uintptr_t)md32.md_cval;
+ md.md_data = (void *)(uintptr_t)md32.md_data;
+#else
+ COPYOUT(v, &md, sizeof(md));
+ error = __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md));
+ if (error == EOPNOTSUPP) {
+ md.md_cval += ef->off;
+ md.md_data = (void *)((uintptr_t)md.md_data + (uintptr_t)ef->off);
+ } else if (error != 0)
+ return (error);
+#endif
+ p += sizeof(Elf_Addr);
+ switch(md.md_type) {
+ case MDT_DEPEND:
+ if (ef->kernel) /* kernel must not depend on anything */
+ break;
+ s = strdupout((vm_offset_t)md.md_cval);
+ minfolen = sizeof(*mdepend) + strlen(s) + 1;
+ mdepend = malloc(minfolen);
+ if (mdepend == NULL)
+ return ENOMEM;
+ COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend));
+ strcpy((char*)(mdepend + 1), s);
+ free(s);
+ file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend);
+ free(mdepend);
+ break;
+ case MDT_VERSION:
+ s = strdupout((vm_offset_t)md.md_cval);
+ COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver));
+ file_addmodule(fp, s, mver.mv_version, NULL);
+ free(s);
+ modcnt++;
+ break;
+ }
+ }
+ if (modcnt == 0) {
+ s = fake_modname(fp->f_name);
+ file_addmodule(fp, s, 1, NULL);
+ free(s);
+ }
+ return 0;
+}
+
+static unsigned long
+elf_hash(const char *name)
+{
+ const unsigned char *p = (const unsigned char *) name;
+ unsigned long h = 0;
+ unsigned long g;
+
+ while (*p != '\0') {
+ h = (h << 4) + *p++;
+ if ((g = h & 0xf0000000) != 0)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+static const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE) "_lookup_symbol: corrupt symbol table\n";
+int
+__elfN(lookup_symbol)(struct preloaded_file *fp, elf_file_t ef, const char* name,
+ Elf_Sym *symp)
+{
+ Elf_Hashelt symnum;
+ Elf_Sym sym;
+ char *strp;
+ unsigned long hash;
+
+ hash = elf_hash(name);
+ COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum));
+
+ while (symnum != STN_UNDEF) {
+ if (symnum >= ef->nchains) {
+ printf(__elfN(bad_symtable));
+ return ENOENT;
+ }
+
+ COPYOUT(ef->symtab + symnum, &sym, sizeof(sym));
+ if (sym.st_name == 0) {
+ printf(__elfN(bad_symtable));
+ return ENOENT;
+ }
+
+ strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name));
+ if (strcmp(name, strp) == 0) {
+ free(strp);
+ if (sym.st_shndx != SHN_UNDEF ||
+ (sym.st_value != 0 &&
+ ELF_ST_TYPE(sym.st_info) == STT_FUNC)) {
+ *symp = sym;
+ return 0;
+ }
+ return ENOENT;
+ }
+ free(strp);
+ COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum));
+ }
+ return ENOENT;
+}
+
+/*
+ * Apply any intra-module relocations to the value. p is the load address
+ * of the value and val/len is the value to be modified. This does NOT modify
+ * the image in-place, because this is done by kern_linker later on.
+ *
+ * Returns EOPNOTSUPP if no relocation method is supplied.
+ */
+static int
+__elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef,
+ Elf_Addr p, void *val, size_t len)
+{
+ size_t n;
+ Elf_Rela a;
+ Elf_Rel r;
+ int error;
+
+ /*
+ * The kernel is already relocated, but we still want to apply
+ * offset adjustments.
+ */
+ if (ef->kernel)
+ return (EOPNOTSUPP);
+
+ for (n = 0; n < ef->relsz / sizeof(r); n++) {
+ COPYOUT(ef->rel + n, &r, sizeof(r));
+
+ error = __elfN(reloc)(ef, __elfN(symaddr), &r, ELF_RELOC_REL,
+ ef->off, p, val, len);
+ if (error != 0)
+ return (error);
+ }
+ for (n = 0; n < ef->relasz / sizeof(a); n++) {
+ COPYOUT(ef->rela + n, &a, sizeof(a));
+
+ error = __elfN(reloc)(ef, __elfN(symaddr), &a, ELF_RELOC_RELA,
+ ef->off, p, val, len);
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+static Elf_Addr
+__elfN(symaddr)(struct elf_file *ef, Elf_Size symidx)
+{
+
+ /* Symbol lookup by index not required here. */
+ return (0);
+}
diff --git a/stand/common/load_elf32.c b/stand/common/load_elf32.c
new file mode 100644
index 0000000..0c9f460
--- /dev/null
+++ b/stand/common/load_elf32.c
@@ -0,0 +1,7 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 32
+#define _MACHINE_ELF_WANT_32BIT
+
+#include "load_elf.c"
diff --git a/stand/common/load_elf32_obj.c b/stand/common/load_elf32_obj.c
new file mode 100644
index 0000000..94b0896
--- /dev/null
+++ b/stand/common/load_elf32_obj.c
@@ -0,0 +1,7 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 32
+#define _MACHINE_ELF_WANT_32BIT
+
+#include "load_elf_obj.c"
diff --git a/stand/common/load_elf64.c b/stand/common/load_elf64.c
new file mode 100644
index 0000000..c29e8e3
--- /dev/null
+++ b/stand/common/load_elf64.c
@@ -0,0 +1,6 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+
+#include "load_elf.c"
diff --git a/stand/common/load_elf64_obj.c b/stand/common/load_elf64_obj.c
new file mode 100644
index 0000000..3c9371b
--- /dev/null
+++ b/stand/common/load_elf64_obj.c
@@ -0,0 +1,6 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+
+#include "load_elf_obj.c"
diff --git a/stand/common/load_elf_obj.c b/stand/common/load_elf_obj.c
new file mode 100644
index 0000000..a32b9fd
--- /dev/null
+++ b/stand/common/load_elf_obj.c
@@ -0,0 +1,537 @@
+/*-
+ * Copyright (c) 2004 Ian Dowse <iedowse@freebsd.org>
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 1998 Peter Wemm <peter@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <inttypes.h>
+#include <string.h>
+#include <machine/elf.h>
+#include <stand.h>
+#define FREEBSD_ELF
+#include <link.h>
+
+#include "bootstrap.h"
+
+#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l)
+
+#if defined(__i386__) && __ELF_WORD_SIZE == 64
+#undef ELF_TARG_CLASS
+#undef ELF_TARG_MACH
+#define ELF_TARG_CLASS ELFCLASS64
+#define ELF_TARG_MACH EM_X86_64
+#endif
+
+typedef struct elf_file {
+ Elf_Ehdr hdr;
+ Elf_Shdr *e_shdr;
+
+ int symtabindex; /* Index of symbol table */
+ int shstrindex; /* Index of section name string table */
+
+ int fd;
+ vm_offset_t off;
+} *elf_file_t;
+
+static int __elfN(obj_loadimage)(struct preloaded_file *mp, elf_file_t ef,
+ u_int64_t loadaddr);
+static int __elfN(obj_lookup_set)(struct preloaded_file *mp, elf_file_t ef,
+ const char *name, Elf_Addr *startp, Elf_Addr *stopp, int *countp);
+static int __elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef,
+ Elf_Addr p, void *val, size_t len);
+static int __elfN(obj_parse_modmetadata)(struct preloaded_file *mp,
+ elf_file_t ef);
+static Elf_Addr __elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx);
+
+const char *__elfN(obj_kerneltype) = "elf kernel";
+const char *__elfN(obj_moduletype) = "elf obj module";
+
+/*
+ * Attempt to load the file (file) as an ELF module. It will be stored at
+ * (dest), and a pointer to a module structure describing the loaded object
+ * will be saved in (result).
+ */
+int
+__elfN(obj_loadfile)(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ struct preloaded_file *fp, *kfp;
+ struct elf_file ef;
+ Elf_Ehdr *hdr;
+ int err;
+ ssize_t bytes_read;
+
+ fp = NULL;
+ bzero(&ef, sizeof(struct elf_file));
+
+ /*
+ * Open the image, read and validate the ELF header
+ */
+ if (filename == NULL) /* can't handle nameless */
+ return(EFTYPE);
+ if ((ef.fd = open(filename, O_RDONLY)) == -1)
+ return(errno);
+
+ hdr = &ef.hdr;
+ bytes_read = read(ef.fd, hdr, sizeof(*hdr));
+ if (bytes_read != sizeof(*hdr)) {
+ err = EFTYPE; /* could be EIO, but may be small file */
+ goto oerr;
+ }
+
+ /* Is it ELF? */
+ if (!IS_ELF(*hdr)) {
+ err = EFTYPE;
+ goto oerr;
+ }
+ if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */
+ hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
+ hdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */
+ hdr->e_version != EV_CURRENT ||
+ hdr->e_machine != ELF_TARG_MACH || /* Machine ? */
+ hdr->e_type != ET_REL) {
+ err = EFTYPE;
+ goto oerr;
+ }
+
+ if (hdr->e_shnum * hdr->e_shentsize == 0 || hdr->e_shoff == 0 ||
+ hdr->e_shentsize != sizeof(Elf_Shdr)) {
+ err = EFTYPE;
+ goto oerr;
+ }
+
+ kfp = file_findfile(NULL, __elfN(obj_kerneltype));
+ if (kfp == NULL) {
+ printf("elf" __XSTRING(__ELF_WORD_SIZE)
+ "_obj_loadfile: can't load module before kernel\n");
+ err = EPERM;
+ goto oerr;
+ }
+
+ if (archsw.arch_loadaddr != NULL)
+ dest = archsw.arch_loadaddr(LOAD_ELF, hdr, dest);
+ else
+ dest = roundup(dest, PAGE_SIZE);
+
+ /*
+ * Ok, we think we should handle this.
+ */
+ fp = file_alloc();
+ if (fp == NULL) {
+ printf("elf" __XSTRING(__ELF_WORD_SIZE)
+ "_obj_loadfile: cannot allocate module info\n");
+ err = EPERM;
+ goto out;
+ }
+ fp->f_name = strdup(filename);
+ fp->f_type = strdup(__elfN(obj_moduletype));
+
+ printf("%s ", filename);
+
+ fp->f_size = __elfN(obj_loadimage)(fp, &ef, dest);
+ if (fp->f_size == 0 || fp->f_addr == 0)
+ goto ioerr;
+
+ /* save exec header as metadata */
+ file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*hdr), hdr);
+
+ /* Load OK, return module pointer */
+ *result = (struct preloaded_file *)fp;
+ err = 0;
+ goto out;
+
+ioerr:
+ err = EIO;
+oerr:
+ file_discard(fp);
+out:
+ close(ef.fd);
+ if (ef.e_shdr != NULL)
+ free(ef.e_shdr);
+
+ return(err);
+}
+
+/*
+ * With the file (fd) open on the image, and (ehdr) containing
+ * the Elf header, load the image at (off)
+ */
+static int
+__elfN(obj_loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off)
+{
+ Elf_Ehdr *hdr;
+ Elf_Shdr *shdr, *cshdr, *lshdr;
+ vm_offset_t firstaddr, lastaddr;
+ int i, nsym, res, ret, shdrbytes, symstrindex;
+
+ ret = 0;
+ firstaddr = lastaddr = (vm_offset_t)off;
+ hdr = &ef->hdr;
+ ef->off = (vm_offset_t)off;
+
+ /* Read in the section headers. */
+ shdrbytes = hdr->e_shnum * hdr->e_shentsize;
+ shdr = alloc_pread(ef->fd, (off_t)hdr->e_shoff, shdrbytes);
+ if (shdr == NULL) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "_obj_loadimage: read section headers failed\n");
+ goto out;
+ }
+ ef->e_shdr = shdr;
+
+ /*
+ * Decide where to load everything, but don't read it yet.
+ * We store the load address as a non-zero sh_addr value.
+ * Start with the code/data and bss.
+ */
+ for (i = 0; i < hdr->e_shnum; i++)
+ shdr[i].sh_addr = 0;
+ for (i = 0; i < hdr->e_shnum; i++) {
+ if (shdr[i].sh_size == 0)
+ continue;
+ switch (shdr[i].sh_type) {
+ case SHT_PROGBITS:
+ case SHT_NOBITS:
+#if defined(__i386__) || defined(__amd64__)
+ case SHT_X86_64_UNWIND:
+#endif
+ lastaddr = roundup(lastaddr, shdr[i].sh_addralign);
+ shdr[i].sh_addr = (Elf_Addr)lastaddr;
+ lastaddr += shdr[i].sh_size;
+ break;
+ }
+ }
+
+ /* Symbols. */
+ nsym = 0;
+ for (i = 0; i < hdr->e_shnum; i++) {
+ switch (shdr[i].sh_type) {
+ case SHT_SYMTAB:
+ nsym++;
+ ef->symtabindex = i;
+ shdr[i].sh_addr = (Elf_Addr)lastaddr;
+ lastaddr += shdr[i].sh_size;
+ break;
+ }
+ }
+ if (nsym != 1) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "_obj_loadimage: file has no valid symbol table\n");
+ goto out;
+ }
+ lastaddr = roundup(lastaddr, shdr[ef->symtabindex].sh_addralign);
+ shdr[ef->symtabindex].sh_addr = (Elf_Addr)lastaddr;
+ lastaddr += shdr[ef->symtabindex].sh_size;
+
+ symstrindex = shdr[ef->symtabindex].sh_link;
+ if (symstrindex < 0 || symstrindex >= hdr->e_shnum ||
+ shdr[symstrindex].sh_type != SHT_STRTAB) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "_obj_loadimage: file has invalid symbol strings\n");
+ goto out;
+ }
+ lastaddr = roundup(lastaddr, shdr[symstrindex].sh_addralign);
+ shdr[symstrindex].sh_addr = (Elf_Addr)lastaddr;
+ lastaddr += shdr[symstrindex].sh_size;
+
+ /* Section names. */
+ if (hdr->e_shstrndx == 0 || hdr->e_shstrndx >= hdr->e_shnum ||
+ shdr[hdr->e_shstrndx].sh_type != SHT_STRTAB) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "_obj_loadimage: file has no section names\n");
+ goto out;
+ }
+ ef->shstrindex = hdr->e_shstrndx;
+ lastaddr = roundup(lastaddr, shdr[ef->shstrindex].sh_addralign);
+ shdr[ef->shstrindex].sh_addr = (Elf_Addr)lastaddr;
+ lastaddr += shdr[ef->shstrindex].sh_size;
+
+ /* Relocation tables. */
+ for (i = 0; i < hdr->e_shnum; i++) {
+ switch (shdr[i].sh_type) {
+ case SHT_REL:
+ case SHT_RELA:
+ lastaddr = roundup(lastaddr, shdr[i].sh_addralign);
+ shdr[i].sh_addr = (Elf_Addr)lastaddr;
+ lastaddr += shdr[i].sh_size;
+ break;
+ }
+ }
+
+ /* Clear the whole area, including bss regions. */
+ kern_bzero(firstaddr, lastaddr - firstaddr);
+
+ /* Figure section with the lowest file offset we haven't loaded yet. */
+ for (cshdr = NULL; /* none */; /* none */)
+ {
+ /*
+ * Find next section to load. The complexity of this loop is
+ * O(n^2), but with the number of sections being typically
+ * small, we do not care.
+ */
+ lshdr = cshdr;
+
+ for (i = 0; i < hdr->e_shnum; i++) {
+ if (shdr[i].sh_addr == 0 ||
+ shdr[i].sh_type == SHT_NOBITS)
+ continue;
+ /* Skip sections that were loaded already. */
+ if (lshdr != NULL &&
+ lshdr->sh_offset >= shdr[i].sh_offset)
+ continue;
+ /* Find section with smallest offset. */
+ if (cshdr == lshdr ||
+ cshdr->sh_offset > shdr[i].sh_offset)
+ cshdr = &shdr[i];
+ }
+
+ if (cshdr == lshdr)
+ break;
+
+ if (kern_pread(ef->fd, (vm_offset_t)cshdr->sh_addr,
+ cshdr->sh_size, (off_t)cshdr->sh_offset) != 0) {
+ printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
+ "_obj_loadimage: read failed\n");
+ goto out;
+ }
+ }
+
+ file_addmetadata(fp, MODINFOMD_SHDR, shdrbytes, shdr);
+
+ res = __elfN(obj_parse_modmetadata)(fp, ef);
+ if (res != 0)
+ goto out;
+
+ ret = lastaddr - firstaddr;
+ fp->f_addr = firstaddr;
+
+ printf("size 0x%lx at 0x%lx", (u_long)ret, (u_long)firstaddr);
+
+out:
+ printf("\n");
+ return ret;
+}
+
+#if defined(__i386__) && __ELF_WORD_SIZE == 64
+struct mod_metadata64 {
+ int md_version; /* structure version MDTV_* */
+ int md_type; /* type of entry MDT_* */
+ u_int64_t md_data; /* specific data */
+ u_int64_t md_cval; /* common string label */
+};
+#endif
+
+int
+__elfN(obj_parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef)
+{
+ struct mod_metadata md;
+#if defined(__i386__) && __ELF_WORD_SIZE == 64
+ struct mod_metadata64 md64;
+#endif
+ struct mod_depend *mdepend;
+ struct mod_version mver;
+ char *s;
+ int error, modcnt, minfolen;
+ Elf_Addr v, p, p_stop;
+
+ if (__elfN(obj_lookup_set)(fp, ef, "modmetadata_set", &p, &p_stop,
+ &modcnt) != 0)
+ return 0;
+
+ modcnt = 0;
+ while (p < p_stop) {
+ COPYOUT(p, &v, sizeof(v));
+ error = __elfN(obj_reloc_ptr)(fp, ef, p, &v, sizeof(v));
+ if (error != 0)
+ return (error);
+#if defined(__i386__) && __ELF_WORD_SIZE == 64
+ COPYOUT(v, &md64, sizeof(md64));
+ error = __elfN(obj_reloc_ptr)(fp, ef, v, &md64, sizeof(md64));
+ if (error != 0)
+ return (error);
+ md.md_version = md64.md_version;
+ md.md_type = md64.md_type;
+ md.md_cval = (const char *)(uintptr_t)md64.md_cval;
+ md.md_data = (void *)(uintptr_t)md64.md_data;
+#else
+ COPYOUT(v, &md, sizeof(md));
+ error = __elfN(obj_reloc_ptr)(fp, ef, v, &md, sizeof(md));
+ if (error != 0)
+ return (error);
+#endif
+ p += sizeof(Elf_Addr);
+ switch(md.md_type) {
+ case MDT_DEPEND:
+ s = strdupout((vm_offset_t)md.md_cval);
+ minfolen = sizeof(*mdepend) + strlen(s) + 1;
+ mdepend = malloc(minfolen);
+ if (mdepend == NULL)
+ return ENOMEM;
+ COPYOUT((vm_offset_t)md.md_data, mdepend,
+ sizeof(*mdepend));
+ strcpy((char*)(mdepend + 1), s);
+ free(s);
+ file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen,
+ mdepend);
+ free(mdepend);
+ break;
+ case MDT_VERSION:
+ s = strdupout((vm_offset_t)md.md_cval);
+ COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver));
+ file_addmodule(fp, s, mver.mv_version, NULL);
+ free(s);
+ modcnt++;
+ break;
+ case MDT_MODULE:
+ case MDT_PNP_INFO:
+ break;
+ default:
+ printf("unknown type %d\n", md.md_type);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int
+__elfN(obj_lookup_set)(struct preloaded_file *fp, elf_file_t ef,
+ const char* name, Elf_Addr *startp, Elf_Addr *stopp, int *countp)
+{
+ Elf_Ehdr *hdr;
+ Elf_Shdr *shdr;
+ char *p;
+ vm_offset_t shstrtab;
+ int i;
+
+ hdr = &ef->hdr;
+ shdr = ef->e_shdr;
+ shstrtab = shdr[ef->shstrindex].sh_addr;
+
+ for (i = 0; i < hdr->e_shnum; i++) {
+ if (shdr[i].sh_type != SHT_PROGBITS)
+ continue;
+ if (shdr[i].sh_name == 0)
+ continue;
+ p = strdupout(shstrtab + shdr[i].sh_name);
+ if (strncmp(p, "set_", 4) == 0 && strcmp(p + 4, name) == 0) {
+ *startp = shdr[i].sh_addr;
+ *stopp = shdr[i].sh_addr + shdr[i].sh_size;
+ *countp = (*stopp - *startp) / sizeof(Elf_Addr);
+ free(p);
+ return (0);
+ }
+ free(p);
+ }
+
+ return (ESRCH);
+}
+
+/*
+ * Apply any intra-module relocations to the value. p is the load address
+ * of the value and val/len is the value to be modified. This does NOT modify
+ * the image in-place, because this is done by kern_linker later on.
+ */
+static int
+__elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p,
+ void *val, size_t len)
+{
+ Elf_Ehdr *hdr;
+ Elf_Shdr *shdr;
+ Elf_Addr off = p;
+ Elf_Addr base;
+ Elf_Rela a, *abase;
+ Elf_Rel r, *rbase;
+ int error, i, j, nrel, nrela;
+
+ hdr = &ef->hdr;
+ shdr = ef->e_shdr;
+
+ for (i = 0; i < hdr->e_shnum; i++) {
+ if (shdr[i].sh_type != SHT_RELA && shdr[i].sh_type != SHT_REL)
+ continue;
+ base = shdr[shdr[i].sh_info].sh_addr;
+ if (base == 0 || shdr[i].sh_addr == 0)
+ continue;
+ if (off < base || off + len > base +
+ shdr[shdr[i].sh_info].sh_size)
+ continue;
+
+ switch (shdr[i].sh_type) {
+ case SHT_RELA:
+ abase = (Elf_Rela *)(intptr_t)shdr[i].sh_addr;
+
+ nrela = shdr[i].sh_size / sizeof(Elf_Rela);
+ for (j = 0; j < nrela; j++) {
+ COPYOUT(abase + j, &a, sizeof(a));
+
+ error = __elfN(reloc)(ef, __elfN(obj_symaddr),
+ &a, ELF_RELOC_RELA, base, off, val, len);
+ if (error != 0)
+ return (error);
+ }
+ break;
+ case SHT_REL:
+ rbase = (Elf_Rel *)(intptr_t)shdr[i].sh_addr;
+
+ nrel = shdr[i].sh_size / sizeof(Elf_Rel);
+ for (j = 0; j < nrel; j++) {
+ COPYOUT(rbase + j, &r, sizeof(r));
+
+ error = __elfN(reloc)(ef, __elfN(obj_symaddr),
+ &r, ELF_RELOC_REL, base, off, val, len);
+ if (error != 0)
+ return (error);
+ }
+ break;
+ }
+ }
+ return (0);
+}
+
+/* Look up the address of a specified symbol. */
+static Elf_Addr
+__elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx)
+{
+ Elf_Sym sym;
+ Elf_Addr base;
+
+ if (symidx >= ef->e_shdr[ef->symtabindex].sh_size / sizeof(Elf_Sym))
+ return (0);
+ COPYOUT(ef->e_shdr[ef->symtabindex].sh_addr + symidx * sizeof(Elf_Sym),
+ &sym, sizeof(sym));
+ if (sym.st_shndx == SHN_UNDEF || sym.st_shndx >= ef->hdr.e_shnum)
+ return (0);
+ base = ef->e_shdr[sym.st_shndx].sh_addr;
+ if (base == 0)
+ return (0);
+ return (base + sym.st_value);
+}
diff --git a/stand/common/ls.c b/stand/common/ls.c
new file mode 100644
index 0000000..cd6b7c4
--- /dev/null
+++ b/stand/common/ls.c
@@ -0,0 +1,212 @@
+/*
+ * $NetBSD: ls.c,v 1.3 1997/06/13 13:48:47 drochner Exp $
+ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1996
+ * Matthias Drochner. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+
+static char typestr[] = "?fc?d?b? ?l?s?w";
+
+static int ls_getdir(char **pathp);
+
+COMMAND_SET(ls, "ls", "list files", command_ls);
+
+static int
+command_ls(int argc, char *argv[])
+{
+ int fd;
+ struct stat sb;
+ struct dirent *d;
+ char *buf, *path;
+ char lbuf[128]; /* one line */
+ int result, ch;
+ int verbose;
+
+ result = CMD_OK;
+ fd = -1;
+ verbose = 0;
+ optind = 1;
+ optreset = 1;
+ while ((ch = getopt(argc, argv, "l")) != -1) {
+ switch (ch) {
+ case 'l':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ /* getopt has already reported an error */
+ return (CMD_OK);
+ }
+ }
+ argv += (optind - 1);
+ argc -= (optind - 1);
+
+ if (argc < 2) {
+ path = "";
+ } else {
+ path = argv[1];
+ }
+
+ if (stat(path, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
+ if (verbose) {
+ printf(" %c %8d %s\n",
+ typestr[sb.st_mode >> 12],
+ (int)sb.st_size, path);
+ } else {
+ printf(" %c %s\n",
+ typestr[sb.st_mode >> 12], path);
+ }
+ return (CMD_OK);
+ }
+
+ fd = ls_getdir(&path);
+ if (fd == -1) {
+ result = CMD_ERROR;
+ goto out;
+ }
+ pager_open();
+ pager_output(path);
+ pager_output("\n");
+
+ while ((d = readdirfd(fd)) != NULL) {
+ if (strcmp(d->d_name, ".") && strcmp(d->d_name, "..")) {
+ if (d->d_type == 0 || verbose) {
+ /* stat the file, if possible */
+ sb.st_size = 0;
+ sb.st_mode = 0;
+ buf = malloc(strlen(path) + strlen(d->d_name) + 2);
+ if (buf != NULL) {
+ sprintf(buf, "%s/%s", path, d->d_name);
+ /* ignore return, could be symlink, etc. */
+ if (stat(buf, &sb)) {
+ sb.st_size = 0;
+ sb.st_mode = 0;
+ }
+ free(buf);
+ }
+ }
+ if (verbose) {
+ snprintf(lbuf, sizeof(lbuf), " %c %8d %s\n",
+ typestr[d->d_type? d->d_type:sb.st_mode >> 12],
+ (int)sb.st_size, d->d_name);
+ } else {
+ snprintf(lbuf, sizeof(lbuf), " %c %s\n",
+ typestr[d->d_type? d->d_type:sb.st_mode >> 12], d->d_name);
+ }
+ if (pager_output(lbuf))
+ goto out;
+ }
+ }
+ out:
+ pager_close();
+ if (fd != -1)
+ close(fd);
+ free(path); /* ls_getdir() did allocate path */
+ return (result);
+}
+
+/*
+ * Given (path) containing a vaguely reasonable path specification, return an fd
+ * on the directory, and an allocated copy of the path to the directory.
+ */
+static int
+ls_getdir(char **pathp)
+{
+ struct stat sb;
+ int fd;
+ const char *cp;
+ char *path;
+
+ fd = -1;
+
+ /* one extra byte for a possible trailing slash required */
+ path = malloc(strlen(*pathp) + 2);
+ if (path == NULL) {
+ snprintf(command_errbuf, sizeof (command_errbuf),
+ "out of memory");
+ goto out;
+ }
+ strcpy(path, *pathp);
+
+ /* Make sure the path is respectable to begin with */
+ if (archsw.arch_getdev(NULL, path, &cp)) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "bad path '%s'", path);
+ goto out;
+ }
+
+ /* If there's no path on the device, assume '/' */
+ if (*cp == 0)
+ strcat(path, "/");
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "open '%s' failed: %s", path, strerror(errno));
+ goto out;
+ }
+ if (fstat(fd, &sb) < 0) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "stat failed: %s", strerror(errno));
+ goto out;
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "%s: %s", path, strerror(ENOTDIR));
+ goto out;
+ }
+
+ *pathp = path;
+ return (fd);
+
+ out:
+ free(path);
+ *pathp = NULL;
+ if (fd != -1)
+ close(fd);
+ return (-1);
+}
diff --git a/stand/common/md.c b/stand/common/md.c
new file mode 100644
index 0000000..5585218
--- /dev/null
+++ b/stand/common/md.c
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 2009 Marcel Moolenaar
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/queue.h>
+#include <machine/stdarg.h>
+
+#include "bootstrap.h"
+
+#define MD_BLOCK_SIZE 512
+
+#ifndef MD_IMAGE_SIZE
+#error Must be compiled with MD_IMAGE_SIZE defined
+#endif
+#if (MD_IMAGE_SIZE == 0 || MD_IMAGE_SIZE % MD_BLOCK_SIZE)
+#error Image size must be a multiple of 512.
+#endif
+
+/*
+ * Preloaded image gets put here.
+ * Applications that patch the object with the image can determine
+ * the size looking at the start and end markers (strings),
+ * so we want them contiguous.
+ */
+static struct {
+ u_char start[MD_IMAGE_SIZE];
+ u_char end[128];
+} md_image = {
+ .start = "MFS Filesystem goes here",
+ .end = "MFS Filesystem had better STOP here",
+};
+
+/* devsw I/F */
+static int md_init(void);
+static int md_strategy(void *, int, daddr_t, size_t, char *, size_t *);
+static int md_open(struct open_file *, ...);
+static int md_close(struct open_file *);
+static int md_print(int);
+
+struct devsw md_dev = {
+ "md",
+ DEVT_DISK,
+ md_init,
+ md_strategy,
+ md_open,
+ md_close,
+ noioctl,
+ md_print,
+ NULL
+};
+
+static int
+md_init(void)
+{
+
+ return (0);
+}
+
+static int
+md_strategy(void *devdata, int rw, daddr_t blk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct devdesc *dev = (struct devdesc *)devdata;
+ size_t ofs;
+
+ if (dev->d_unit != 0)
+ return (ENXIO);
+
+ if (blk < 0 || blk >= (MD_IMAGE_SIZE / MD_BLOCK_SIZE))
+ return (EIO);
+
+ if (size % MD_BLOCK_SIZE)
+ return (EIO);
+
+ ofs = blk * MD_BLOCK_SIZE;
+ if ((ofs + size) > MD_IMAGE_SIZE)
+ size = MD_IMAGE_SIZE - ofs;
+
+ if (rsize != NULL)
+ *rsize = size;
+
+ switch (rw & F_MASK) {
+ case F_READ:
+ bcopy(md_image.start + ofs, buf, size);
+ return (0);
+ case F_WRITE:
+ bcopy(buf, md_image.start + ofs, size);
+ return (0);
+ }
+
+ return (ENODEV);
+}
+
+static int
+md_open(struct open_file *f, ...)
+{
+ va_list ap;
+ struct devdesc *dev;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct devdesc *);
+ va_end(ap);
+
+ if (dev->d_unit != 0)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+md_close(struct open_file *f)
+{
+ struct devdesc *dev;
+
+ dev = (struct devdesc *)(f->f_devdata);
+ return ((dev->d_unit != 0) ? ENXIO : 0);
+}
+
+static int
+md_print(int verbose)
+{
+
+ printf("%s devices:", md_dev.dv_name);
+ if (pager_output("\n") != 0)
+ return (1);
+
+ printf("MD (%u bytes)", MD_IMAGE_SIZE);
+ return (pager_output("\n"));
+}
diff --git a/stand/common/merge_help.awk b/stand/common/merge_help.awk
new file mode 100644
index 0000000..1070f73
--- /dev/null
+++ b/stand/common/merge_help.awk
@@ -0,0 +1,104 @@
+#!/usr/bin/awk -f
+#
+# $FreeBSD$
+#
+# Merge two boot loader help files for FreeBSD 3.0
+# Joe Abley <jabley@patho.gen.nz>
+
+BEGIN \
+{
+ state = 0;
+ first = -1;
+ ind = 0;
+}
+
+# beginning of first command
+/^###/ && (state == 0) \
+{
+ state = 1;
+ next;
+}
+
+# entry header
+/^# T[[:graph:]]+ (S[[:graph:]]+ )*D[[:graph:]][[:print:]]*$/ && (state == 1) \
+{
+ match($0, " T[[:graph:]]+");
+ T = substr($0, RSTART + 2, RLENGTH - 2);
+ match($0, " S[[:graph:]]+");
+ SSTART = RSTART
+ S = (RLENGTH == -1) ? "" : substr($0, RSTART + 2, RLENGTH - 2);
+ match($0, " D[[:graph:]][[:print:]]*$");
+ D = substr($0, RSTART + 2);
+ if (SSTART > RSTART)
+ S = "";
+
+ # find a suitable place to store this one...
+ ind++;
+ if (ind == 1)
+ {
+ first = ind;
+ help[ind, "T"] = T;
+ help[ind, "S"] = S;
+ help[ind, "link"] = -1;
+ } else {
+ i = first; j = -1;
+ while (help[i, "T"] help[i, "S"] < T S)
+ {
+ j = i;
+ i = help[i, "link"];
+ if (i == -1) break;
+ }
+
+ if (i == -1)
+ {
+ help[j, "link"] = ind;
+ help[ind, "link"] = -1;
+ } else {
+ help[ind, "link"] = i;
+ if (j == -1)
+ first = ind;
+ else
+ help[j, "link"] = ind;
+ }
+ }
+ help[ind, "T"] = T;
+ help[ind, "S"] = S;
+ help[ind, "D"] = D;
+
+ # set our state
+ state = 2;
+ help[ind, "text"] = 0;
+ next;
+}
+
+# end of last command, beginning of next one
+/^###/ && (state == 2) \
+{
+ state = 1;
+}
+
+(state == 2) \
+{
+ sub("[[:blank:]]+$", "");
+ if (help[ind, "text"] == 0 && $0 ~ /^[[:blank:]]*$/) next;
+ help[ind, "text", help[ind, "text"]] = $0;
+ help[ind, "text"]++;
+ next;
+}
+
+# show them what we have (it's already sorted in help[])
+END \
+{
+ node = first;
+ while (node != -1)
+ {
+ printf "################################################################################\n";
+ printf "# T%s ", help[node, "T"];
+ if (help[node, "S"] != "") printf "S%s ", help[node, "S"];
+ printf "D%s\n\n", help[node, "D"];
+ for (i = 0; i < help[node, "text"]; i++)
+ printf "%s\n", help[node, "text", i];
+ node = help[node, "link"];
+ }
+ printf "################################################################################\n";
+}
diff --git a/stand/common/misc.c b/stand/common/misc.c
new file mode 100644
index 0000000..9b938af
--- /dev/null
+++ b/stand/common/misc.c
@@ -0,0 +1,219 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <string.h>
+#include <stand.h>
+#include <bootstrap.h>
+
+/*
+ * Concatenate the (argc) elements of (argv) into a single string, and return
+ * a copy of same.
+ */
+char *
+unargv(int argc, char *argv[])
+{
+ size_t hlong;
+ int i;
+ char *cp;
+
+ for (i = 0, hlong = 0; i < argc; i++)
+ hlong += strlen(argv[i]) + 2;
+
+ if(hlong == 0)
+ return(NULL);
+
+ cp = malloc(hlong);
+ cp[0] = 0;
+ for (i = 0; i < argc; i++) {
+ strcat(cp, argv[i]);
+ if (i < (argc - 1))
+ strcat(cp, " ");
+ }
+
+ return(cp);
+}
+
+/*
+ * Get the length of a string in kernel space
+ */
+size_t
+strlenout(vm_offset_t src)
+{
+ char c;
+ size_t len;
+
+ for (len = 0; ; len++) {
+ archsw.arch_copyout(src++, &c, 1);
+ if (c == 0)
+ break;
+ }
+ return(len);
+}
+
+/*
+ * Make a duplicate copy of a string in kernel space
+ */
+char *
+strdupout(vm_offset_t str)
+{
+ char *result, *cp;
+
+ result = malloc(strlenout(str) + 1);
+ for (cp = result; ;cp++) {
+ archsw.arch_copyout(str++, cp, 1);
+ if (*cp == 0)
+ break;
+ }
+ return(result);
+}
+
+/* Zero a region in kernel space. */
+void
+kern_bzero(vm_offset_t dest, size_t len)
+{
+ char buf[256];
+ size_t chunk, resid;
+
+ bzero(buf, sizeof(buf));
+ resid = len;
+ while (resid > 0) {
+ chunk = min(sizeof(buf), resid);
+ archsw.arch_copyin(buf, dest, chunk);
+ resid -= chunk;
+ dest += chunk;
+ }
+}
+
+/*
+ * Read the specified part of a file to kernel space. Unlike regular
+ * pread, the file pointer is advanced to the end of the read data,
+ * and it just returns 0 if successful.
+ */
+int
+kern_pread(int fd, vm_offset_t dest, size_t len, off_t off)
+{
+
+ if (lseek(fd, off, SEEK_SET) == -1) {
+#ifdef DEBUG
+ printf("\nlseek failed\n");
+#endif
+ return (-1);
+ }
+ if ((size_t)archsw.arch_readin(fd, dest, len) != len) {
+#ifdef DEBUG
+ printf("\nreadin failed\n");
+#endif
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Read the specified part of a file to a malloced buffer. The file
+ * pointer is advanced to the end of the read data.
+ */
+void *
+alloc_pread(int fd, off_t off, size_t len)
+{
+ void *buf;
+
+ buf = malloc(len);
+ if (buf == NULL) {
+#ifdef DEBUG
+ printf("\nmalloc(%d) failed\n", (int)len);
+#endif
+ return (NULL);
+ }
+ if (lseek(fd, off, SEEK_SET) == -1) {
+#ifdef DEBUG
+ printf("\nlseek failed\n");
+#endif
+ free(buf);
+ return (NULL);
+ }
+ if ((size_t)read(fd, buf, len) != len) {
+#ifdef DEBUG
+ printf("\nread failed\n");
+#endif
+ free(buf);
+ return (NULL);
+ }
+ return (buf);
+}
+
+/*
+ * Display a region in traditional hexdump format.
+ */
+void
+hexdump(caddr_t region, size_t len)
+{
+ caddr_t line;
+ int x, c;
+ char lbuf[80];
+#define emit(fmt, args...) {sprintf(lbuf, fmt , ## args); pager_output(lbuf);}
+
+ pager_open();
+ for (line = region; line < (region + len); line += 16) {
+ emit("%08lx ", (long) line);
+
+ for (x = 0; x < 16; x++) {
+ if ((line + x) < (region + len)) {
+ emit("%02x ", *(u_int8_t *)(line + x));
+ } else {
+ emit("-- ");
+ }
+ if (x == 7)
+ emit(" ");
+ }
+ emit(" |");
+ for (x = 0; x < 16; x++) {
+ if ((line + x) < (region + len)) {
+ c = *(u_int8_t *)(line + x);
+ if ((c < ' ') || (c > '~')) /* !isprint(c) */
+ c = '.';
+ emit("%c", c);
+ } else {
+ emit(" ");
+ }
+ }
+ emit("|\n");
+ }
+ pager_close();
+}
+
+void
+dev_cleanup(void)
+{
+ int i;
+
+ /* Call cleanup routines */
+ for (i = 0; devsw[i] != NULL; ++i)
+ if (devsw[i]->dv_cleanup != NULL)
+ (devsw[i]->dv_cleanup)();
+}
diff --git a/stand/common/module.c b/stand/common/module.c
new file mode 100644
index 0000000..d651ad1
--- /dev/null
+++ b/stand/common/module.c
@@ -0,0 +1,1095 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * file/module function dispatcher, support, etc.
+ */
+
+#include <stand.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/queue.h>
+#include <sys/stdint.h>
+
+#include "bootstrap.h"
+
+#define MDIR_REMOVED 0x0001
+#define MDIR_NOHINTS 0x0002
+
+struct moduledir {
+ char *d_path; /* path of modules directory */
+ u_char *d_hints; /* content of linker.hints file */
+ int d_hintsz; /* size of hints data */
+ int d_flags;
+ STAILQ_ENTRY(moduledir) d_link;
+};
+
+static int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result);
+static int file_load_dependencies(struct preloaded_file *base_mod);
+static char * file_search(const char *name, char **extlist);
+static struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo);
+static int file_havepath(const char *name);
+static char *mod_searchmodule(char *name, struct mod_depend *verinfo);
+static void file_insert_tail(struct preloaded_file *mp);
+struct file_metadata* metadata_next(struct file_metadata *base_mp, int type);
+static void moduledir_readhints(struct moduledir *mdp);
+static void moduledir_rebuild(void);
+
+/* load address should be tweaked by first module loaded (kernel) */
+static vm_offset_t loadaddr = 0;
+
+#if defined(LOADER_FDT_SUPPORT)
+static const char *default_searchpath =
+ "/boot/kernel;/boot/modules;/boot/dtb";
+#else
+static const char *default_searchpath ="/boot/kernel;/boot/modules";
+#endif
+
+static STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list);
+
+struct preloaded_file *preloaded_files = NULL;
+
+static char *kld_ext_list[] = {
+ ".ko",
+ "",
+ ".debug",
+ NULL
+};
+
+
+/*
+ * load an object, either a disk file or code module.
+ *
+ * To load a file, the syntax is:
+ *
+ * load -t <type> <path>
+ *
+ * code modules are loaded as:
+ *
+ * load <path> <options>
+ */
+
+COMMAND_SET(load, "load", "load a kernel or module", command_load);
+
+static int
+command_load(int argc, char *argv[])
+{
+ struct preloaded_file *fp;
+ char *typestr;
+ int dofile, dokld, ch, error;
+
+ dokld = dofile = 0;
+ optind = 1;
+ optreset = 1;
+ typestr = NULL;
+ if (argc == 1) {
+ command_errmsg = "no filename specified";
+ return (CMD_CRIT);
+ }
+ while ((ch = getopt(argc, argv, "kt:")) != -1) {
+ switch(ch) {
+ case 'k':
+ dokld = 1;
+ break;
+ case 't':
+ typestr = optarg;
+ dofile = 1;
+ break;
+ case '?':
+ default:
+ /* getopt has already reported an error */
+ return (CMD_OK);
+ }
+ }
+ argv += (optind - 1);
+ argc -= (optind - 1);
+
+ /*
+ * Request to load a raw file?
+ */
+ if (dofile) {
+ if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) {
+ command_errmsg = "invalid load type";
+ return (CMD_CRIT);
+ }
+
+ fp = file_findfile(argv[1], typestr);
+ if (fp) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "warning: file '%s' already loaded", argv[1]);
+ return (CMD_WARN);
+ }
+
+ if (file_loadraw(argv[1], typestr, 1) != NULL)
+ return (CMD_OK);
+
+ /* Failing to load mfs_root is never going to end well! */
+ if (strcmp("mfs_root", typestr) == 0)
+ return (CMD_FATAL);
+
+ return (CMD_ERROR);
+ }
+ /*
+ * Do we have explicit KLD load ?
+ */
+ if (dokld || file_havepath(argv[1])) {
+ error = mod_loadkld(argv[1], argc - 2, argv + 2);
+ if (error == EEXIST) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "warning: KLD '%s' already loaded", argv[1]);
+ return (CMD_WARN);
+ }
+
+ return (error == 0 ? CMD_OK : CMD_CRIT);
+ }
+ /*
+ * Looks like a request for a module.
+ */
+ error = mod_load(argv[1], NULL, argc - 2, argv + 2);
+ if (error == EEXIST) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "warning: module '%s' already loaded", argv[1]);
+ return (CMD_WARN);
+ }
+
+ return (error == 0 ? CMD_OK : CMD_CRIT);
+}
+
+#ifdef LOADER_GELI_SUPPORT
+COMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli);
+
+static int
+command_load_geli(int argc, char *argv[])
+{
+ char typestr[80];
+ char *cp;
+ int ch, num;
+
+ if (argc < 3) {
+ command_errmsg = "usage is [-n key#] <prov> <file>";
+ return(CMD_ERROR);
+ }
+
+ num = 0;
+ optind = 1;
+ optreset = 1;
+ while ((ch = getopt(argc, argv, "n:")) != -1) {
+ switch(ch) {
+ case 'n':
+ num = strtol(optarg, &cp, 0);
+ if (cp == optarg) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "bad key index '%s'", optarg);
+ return(CMD_ERROR);
+ }
+ break;
+ case '?':
+ default:
+ /* getopt has already reported an error */
+ return(CMD_OK);
+ }
+ }
+ argv += (optind - 1);
+ argc -= (optind - 1);
+ sprintf(typestr, "%s:geli_keyfile%d", argv[1], num);
+ return (file_loadraw(argv[2], typestr, 1) ? CMD_OK : CMD_ERROR);
+}
+#endif
+
+void
+unload(void)
+{
+ struct preloaded_file *fp;
+
+ while (preloaded_files != NULL) {
+ fp = preloaded_files;
+ preloaded_files = preloaded_files->f_next;
+ file_discard(fp);
+ }
+ loadaddr = 0;
+ unsetenv("kernelname");
+}
+
+COMMAND_SET(unload, "unload", "unload all modules", command_unload);
+
+static int
+command_unload(int argc, char *argv[])
+{
+ unload();
+ return(CMD_OK);
+}
+
+COMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
+
+static int
+command_lsmod(int argc, char *argv[])
+{
+ struct preloaded_file *fp;
+ struct kernel_module *mp;
+ struct file_metadata *md;
+ char lbuf[80];
+ int ch, verbose, ret = 0;
+
+ verbose = 0;
+ optind = 1;
+ optreset = 1;
+ while ((ch = getopt(argc, argv, "v")) != -1) {
+ switch(ch) {
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ /* getopt has already reported an error */
+ return(CMD_OK);
+ }
+ }
+
+ pager_open();
+ for (fp = preloaded_files; fp; fp = fp->f_next) {
+ snprintf(lbuf, sizeof(lbuf), " %p: ", (void *) fp->f_addr);
+ pager_output(lbuf);
+ pager_output(fp->f_name);
+ snprintf(lbuf, sizeof(lbuf), " (%s, 0x%lx)\n", fp->f_type,
+ (long)fp->f_size);
+ if (pager_output(lbuf))
+ break;
+ if (fp->f_args != NULL) {
+ pager_output(" args: ");
+ pager_output(fp->f_args);
+ if (pager_output("\n"))
+ break;
+ }
+ if (fp->f_modules) {
+ pager_output(" modules: ");
+ for (mp = fp->f_modules; mp; mp = mp->m_next) {
+ snprintf(lbuf, sizeof(lbuf), "%s.%d ", mp->m_name,
+ mp->m_version);
+ pager_output(lbuf);
+ }
+ if (pager_output("\n"))
+ break;
+ }
+ if (verbose) {
+ /* XXX could add some formatting smarts here to display some better */
+ for (md = fp->f_metadata; md != NULL; md = md->md_next) {
+ snprintf(lbuf, sizeof(lbuf), " 0x%04x, 0x%lx\n",
+ md->md_type, (long) md->md_size);
+ if (pager_output(lbuf))
+ break;
+ }
+ }
+ if (ret)
+ break;
+ }
+ pager_close();
+ return(CMD_OK);
+}
+
+/*
+ * File level interface, functions file_*
+ */
+int
+file_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
+{
+ static int last_file_format = 0;
+ struct preloaded_file *fp;
+ int error;
+ int i;
+
+ if (archsw.arch_loadaddr != NULL)
+ dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest);
+
+ error = EFTYPE;
+ for (i = last_file_format, fp = NULL;
+ file_formats[i] && fp == NULL; i++) {
+ error = (file_formats[i]->l_load)(filename, dest, &fp);
+ if (error == 0) {
+ fp->f_loader = last_file_format = i; /* remember the loader */
+ *result = fp;
+ break;
+ } else if (last_file_format == i && i != 0) {
+ /* Restart from the beginning */
+ i = -1;
+ last_file_format = 0;
+ fp = NULL;
+ continue;
+ }
+ if (error == EFTYPE)
+ continue; /* Unknown to this handler? */
+ if (error) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "can't load file '%s': %s", filename, strerror(error));
+ break;
+ }
+ }
+ return (error);
+}
+
+static int
+file_load_dependencies(struct preloaded_file *base_file)
+{
+ struct file_metadata *md;
+ struct preloaded_file *fp;
+ struct mod_depend *verinfo;
+ struct kernel_module *mp;
+ char *dmodname;
+ int error;
+
+ md = file_findmetadata(base_file, MODINFOMD_DEPLIST);
+ if (md == NULL)
+ return (0);
+ error = 0;
+ do {
+ verinfo = (struct mod_depend*)md->md_data;
+ dmodname = (char *)(verinfo + 1);
+ if (file_findmodule(NULL, dmodname, verinfo) == NULL) {
+ printf("loading required module '%s'\n", dmodname);
+ error = mod_load(dmodname, verinfo, 0, NULL);
+ if (error)
+ break;
+ /*
+ * If module loaded via kld name which isn't listed
+ * in the linker.hints file, we should check if it have
+ * required version.
+ */
+ mp = file_findmodule(NULL, dmodname, verinfo);
+ if (mp == NULL) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "module '%s' exists but with wrong version", dmodname);
+ error = ENOENT;
+ break;
+ }
+ }
+ md = metadata_next(md, MODINFOMD_DEPLIST);
+ } while (md);
+ if (!error)
+ return (0);
+ /* Load failed; discard everything */
+ while (base_file != NULL) {
+ fp = base_file;
+ base_file = base_file->f_next;
+ file_discard(fp);
+ }
+ return (error);
+}
+
+/*
+ * We've been asked to load (fname) as (type), so just suck it in,
+ * no arguments or anything.
+ */
+struct preloaded_file *
+file_loadraw(const char *fname, char *type, int insert)
+{
+ struct preloaded_file *fp;
+ char *name;
+ int fd, got;
+ vm_offset_t laddr;
+
+ /* We can't load first */
+ if ((file_findfile(NULL, NULL)) == NULL) {
+ command_errmsg = "can't load file before kernel";
+ return(NULL);
+ }
+
+ /* locate the file on the load path */
+ name = file_search(fname, NULL);
+ if (name == NULL) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "can't find '%s'", fname);
+ return(NULL);
+ }
+
+ if ((fd = open(name, O_RDONLY)) < 0) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "can't open '%s': %s", name, strerror(errno));
+ free(name);
+ return(NULL);
+ }
+
+ if (archsw.arch_loadaddr != NULL)
+ loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr);
+
+ printf("%s ", name);
+
+ laddr = loadaddr;
+ for (;;) {
+ /* read in 4k chunks; size is not really important */
+ got = archsw.arch_readin(fd, laddr, 4096);
+ if (got == 0) /* end of file */
+ break;
+ if (got < 0) { /* error */
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "error reading '%s': %s", name, strerror(errno));
+ free(name);
+ close(fd);
+ return(NULL);
+ }
+ laddr += got;
+ }
+
+ printf("size=%#jx\n", (uintmax_t)(laddr - loadaddr));
+
+ /* Looks OK so far; create & populate control structure */
+ fp = file_alloc();
+ fp->f_name = strdup(name);
+ fp->f_type = strdup(type);
+ fp->f_args = NULL;
+ fp->f_metadata = NULL;
+ fp->f_loader = -1;
+ fp->f_addr = loadaddr;
+ fp->f_size = laddr - loadaddr;
+
+ /* recognise space consumption */
+ loadaddr = laddr;
+
+ /* Add to the list of loaded files */
+ if (insert != 0)
+ file_insert_tail(fp);
+ close(fd);
+ return(fp);
+}
+
+/*
+ * Load the module (name), pass it (argc),(argv), add container file
+ * to the list of loaded files.
+ * If module is already loaded just assign new argc/argv.
+ */
+int
+mod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[])
+{
+ struct kernel_module *mp;
+ int err;
+ char *filename;
+
+ if (file_havepath(modname)) {
+ printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname);
+ return (mod_loadkld(modname, argc, argv));
+ }
+ /* see if module is already loaded */
+ mp = file_findmodule(NULL, modname, verinfo);
+ if (mp) {
+#ifdef moduleargs
+ if (mp->m_args)
+ free(mp->m_args);
+ mp->m_args = unargv(argc, argv);
+#endif
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "warning: module '%s' already loaded", mp->m_name);
+ return (0);
+ }
+ /* locate file with the module on the search path */
+ filename = mod_searchmodule(modname, verinfo);
+ if (filename == NULL) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "can't find '%s'", modname);
+ return (ENOENT);
+ }
+ err = mod_loadkld(filename, argc, argv);
+ return (err);
+}
+
+/*
+ * Load specified KLD. If path is omitted, then try to locate it via
+ * search path.
+ */
+int
+mod_loadkld(const char *kldname, int argc, char *argv[])
+{
+ struct preloaded_file *fp, *last_file;
+ int err;
+ char *filename;
+
+ /*
+ * Get fully qualified KLD name
+ */
+ filename = file_search(kldname, kld_ext_list);
+ if (filename == NULL) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "can't find '%s'", kldname);
+ return (ENOENT);
+ }
+ /*
+ * Check if KLD already loaded
+ */
+ fp = file_findfile(filename, NULL);
+ if (fp) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "warning: KLD '%s' already loaded", filename);
+ free(filename);
+ return (0);
+ }
+ for (last_file = preloaded_files;
+ last_file != NULL && last_file->f_next != NULL;
+ last_file = last_file->f_next)
+ ;
+
+ do {
+ err = file_load(filename, loadaddr, &fp);
+ if (err)
+ break;
+ fp->f_args = unargv(argc, argv);
+ loadaddr = fp->f_addr + fp->f_size;
+ file_insert_tail(fp); /* Add to the list of loaded files */
+ if (file_load_dependencies(fp) != 0) {
+ err = ENOENT;
+ last_file->f_next = NULL;
+ loadaddr = last_file->f_addr + last_file->f_size;
+ fp = NULL;
+ break;
+ }
+ } while(0);
+ if (err == EFTYPE) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "don't know how to load module '%s'", filename);
+ }
+ if (err && fp)
+ file_discard(fp);
+ free(filename);
+ return (err);
+}
+
+/*
+ * Find a file matching (name) and (type).
+ * NULL may be passed as a wildcard to either.
+ */
+struct preloaded_file *
+file_findfile(const char *name, const char *type)
+{
+ struct preloaded_file *fp;
+
+ for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
+ if (((name == NULL) || !strcmp(name, fp->f_name)) &&
+ ((type == NULL) || !strcmp(type, fp->f_type)))
+ break;
+ }
+ return (fp);
+}
+
+/*
+ * Find a module matching (name) inside of given file.
+ * NULL may be passed as a wildcard.
+ */
+struct kernel_module *
+file_findmodule(struct preloaded_file *fp, char *modname,
+ struct mod_depend *verinfo)
+{
+ struct kernel_module *mp, *best;
+ int bestver, mver;
+
+ if (fp == NULL) {
+ for (fp = preloaded_files; fp; fp = fp->f_next) {
+ mp = file_findmodule(fp, modname, verinfo);
+ if (mp)
+ return (mp);
+ }
+ return (NULL);
+ }
+ best = NULL;
+ bestver = 0;
+ for (mp = fp->f_modules; mp; mp = mp->m_next) {
+ if (strcmp(modname, mp->m_name) == 0) {
+ if (verinfo == NULL)
+ return (mp);
+ mver = mp->m_version;
+ if (mver == verinfo->md_ver_preferred)
+ return (mp);
+ if (mver >= verinfo->md_ver_minimum &&
+ mver <= verinfo->md_ver_maximum &&
+ mver > bestver) {
+ best = mp;
+ bestver = mver;
+ }
+ }
+ }
+ return (best);
+}
+/*
+ * Make a copy of (size) bytes of data from (p), and associate them as
+ * metadata of (type) to the module (mp).
+ */
+void
+file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
+{
+ struct file_metadata *md;
+
+ md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size);
+ md->md_size = size;
+ md->md_type = type;
+ bcopy(p, md->md_data, size);
+ md->md_next = fp->f_metadata;
+ fp->f_metadata = md;
+}
+
+/*
+ * Find a metadata object of (type) associated with the file (fp)
+ */
+struct file_metadata *
+file_findmetadata(struct preloaded_file *fp, int type)
+{
+ struct file_metadata *md;
+
+ for (md = fp->f_metadata; md != NULL; md = md->md_next)
+ if (md->md_type == type)
+ break;
+ return(md);
+}
+
+/*
+ * Remove all metadata from the file.
+ */
+void
+file_removemetadata(struct preloaded_file *fp)
+{
+ struct file_metadata *md, *next;
+
+ for (md = fp->f_metadata; md != NULL; md = next)
+ {
+ next = md->md_next;
+ free(md);
+ }
+ fp->f_metadata = NULL;
+}
+
+struct file_metadata *
+metadata_next(struct file_metadata *md, int type)
+{
+ if (md == NULL)
+ return (NULL);
+ while((md = md->md_next) != NULL)
+ if (md->md_type == type)
+ break;
+ return (md);
+}
+
+static char *emptyextlist[] = { "", NULL };
+
+/*
+ * Check if the given file is in place and return full path to it.
+ */
+static char *
+file_lookup(const char *path, const char *name, int namelen, char **extlist)
+{
+ struct stat st;
+ char *result, *cp, **cpp;
+ int pathlen, extlen, len;
+
+ pathlen = strlen(path);
+ extlen = 0;
+ if (extlist == NULL)
+ extlist = emptyextlist;
+ for (cpp = extlist; *cpp; cpp++) {
+ len = strlen(*cpp);
+ if (len > extlen)
+ extlen = len;
+ }
+ result = malloc(pathlen + namelen + extlen + 2);
+ if (result == NULL)
+ return (NULL);
+ bcopy(path, result, pathlen);
+ if (pathlen > 0 && result[pathlen - 1] != '/')
+ result[pathlen++] = '/';
+ cp = result + pathlen;
+ bcopy(name, cp, namelen);
+ cp += namelen;
+ for (cpp = extlist; *cpp; cpp++) {
+ strcpy(cp, *cpp);
+ if (stat(result, &st) == 0 && S_ISREG(st.st_mode))
+ return result;
+ }
+ free(result);
+ return NULL;
+}
+
+/*
+ * Check if file name have any qualifiers
+ */
+static int
+file_havepath(const char *name)
+{
+ const char *cp;
+
+ archsw.arch_getdev(NULL, name, &cp);
+ return (cp != name || strchr(name, '/') != NULL);
+}
+
+/*
+ * Attempt to find the file (name) on the module searchpath.
+ * If (name) is qualified in any way, we simply check it and
+ * return it or NULL. If it is not qualified, then we attempt
+ * to construct a path using entries in the environment variable
+ * module_path.
+ *
+ * The path we return a pointer to need never be freed, as we manage
+ * it internally.
+ */
+static char *
+file_search(const char *name, char **extlist)
+{
+ struct moduledir *mdp;
+ struct stat sb;
+ char *result;
+ int namelen;
+
+ /* Don't look for nothing */
+ if (name == NULL)
+ return(NULL);
+
+ if (*name == 0)
+ return(strdup(name));
+
+ if (file_havepath(name)) {
+ /* Qualified, so just see if it exists */
+ if (stat(name, &sb) == 0)
+ return(strdup(name));
+ return(NULL);
+ }
+ moduledir_rebuild();
+ result = NULL;
+ namelen = strlen(name);
+ STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
+ result = file_lookup(mdp->d_path, name, namelen, extlist);
+ if (result)
+ break;
+ }
+ return(result);
+}
+
+#define INT_ALIGN(base, ptr) ptr = \
+ (base) + roundup2((ptr) - (base), sizeof(int))
+
+static char *
+mod_search_hints(struct moduledir *mdp, const char *modname,
+ struct mod_depend *verinfo)
+{
+ u_char *cp, *recptr, *bufend, *best;
+ char *result;
+ int *intp, bestver, blen, clen, found, ival, modnamelen, reclen;
+
+ moduledir_readhints(mdp);
+ modnamelen = strlen(modname);
+ found = 0;
+ result = NULL;
+ bestver = 0;
+ if (mdp->d_hints == NULL)
+ goto bad;
+ recptr = mdp->d_hints;
+ bufend = recptr + mdp->d_hintsz;
+ clen = blen = 0;
+ best = cp = NULL;
+ while (recptr < bufend && !found) {
+ intp = (int*)recptr;
+ reclen = *intp++;
+ ival = *intp++;
+ cp = (u_char*)intp;
+ switch (ival) {
+ case MDT_VERSION:
+ clen = *cp++;
+ if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
+ break;
+ cp += clen;
+ INT_ALIGN(mdp->d_hints, cp);
+ ival = *(int*)cp;
+ cp += sizeof(int);
+ clen = *cp++;
+ if (verinfo == NULL || ival == verinfo->md_ver_preferred) {
+ found = 1;
+ break;
+ }
+ if (ival >= verinfo->md_ver_minimum &&
+ ival <= verinfo->md_ver_maximum &&
+ ival > bestver) {
+ bestver = ival;
+ best = cp;
+ blen = clen;
+ }
+ break;
+ default:
+ break;
+ }
+ recptr += reclen + sizeof(int);
+ }
+ /*
+ * Finally check if KLD is in the place
+ */
+ if (found)
+ result = file_lookup(mdp->d_path, (const char *)cp, clen, NULL);
+ else if (best)
+ result = file_lookup(mdp->d_path, (const char *)best, blen, NULL);
+bad:
+ /*
+ * If nothing found or hints is absent - fallback to the old way
+ * by using "kldname[.ko]" as module name.
+ */
+ if (!found && !bestver && result == NULL)
+ result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list);
+ return result;
+}
+
+/*
+ * Attempt to locate the file containing the module (name)
+ */
+static char *
+mod_searchmodule(char *name, struct mod_depend *verinfo)
+{
+ struct moduledir *mdp;
+ char *result;
+
+ moduledir_rebuild();
+ /*
+ * Now we ready to lookup module in the given directories
+ */
+ result = NULL;
+ STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
+ result = mod_search_hints(mdp, name, verinfo);
+ if (result)
+ break;
+ }
+
+ return(result);
+}
+
+int
+file_addmodule(struct preloaded_file *fp, char *modname, int version,
+ struct kernel_module **newmp)
+{
+ struct kernel_module *mp;
+ struct mod_depend mdepend;
+
+ bzero(&mdepend, sizeof(mdepend));
+ mdepend.md_ver_preferred = version;
+ mp = file_findmodule(fp, modname, &mdepend);
+ if (mp)
+ return (EEXIST);
+ mp = malloc(sizeof(struct kernel_module));
+ if (mp == NULL)
+ return (ENOMEM);
+ bzero(mp, sizeof(struct kernel_module));
+ mp->m_name = strdup(modname);
+ mp->m_version = version;
+ mp->m_fp = fp;
+ mp->m_next = fp->f_modules;
+ fp->f_modules = mp;
+ if (newmp)
+ *newmp = mp;
+ return (0);
+}
+
+/*
+ * Throw a file away
+ */
+void
+file_discard(struct preloaded_file *fp)
+{
+ struct file_metadata *md, *md1;
+ struct kernel_module *mp, *mp1;
+ if (fp == NULL)
+ return;
+ md = fp->f_metadata;
+ while (md) {
+ md1 = md;
+ md = md->md_next;
+ free(md1);
+ }
+ mp = fp->f_modules;
+ while (mp) {
+ if (mp->m_name)
+ free(mp->m_name);
+ mp1 = mp;
+ mp = mp->m_next;
+ free(mp1);
+ }
+ if (fp->f_name != NULL)
+ free(fp->f_name);
+ if (fp->f_type != NULL)
+ free(fp->f_type);
+ if (fp->f_args != NULL)
+ free(fp->f_args);
+ free(fp);
+}
+
+/*
+ * Allocate a new file; must be used instead of malloc()
+ * to ensure safe initialisation.
+ */
+struct preloaded_file *
+file_alloc(void)
+{
+ struct preloaded_file *fp;
+
+ if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) {
+ bzero(fp, sizeof(struct preloaded_file));
+ }
+ return (fp);
+}
+
+/*
+ * Add a module to the chain
+ */
+static void
+file_insert_tail(struct preloaded_file *fp)
+{
+ struct preloaded_file *cm;
+
+ /* Append to list of loaded file */
+ fp->f_next = NULL;
+ if (preloaded_files == NULL) {
+ preloaded_files = fp;
+ } else {
+ for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next)
+ ;
+ cm->f_next = fp;
+ }
+}
+
+static char *
+moduledir_fullpath(struct moduledir *mdp, const char *fname)
+{
+ char *cp;
+
+ cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2);
+ if (cp == NULL)
+ return NULL;
+ strcpy(cp, mdp->d_path);
+ strcat(cp, "/");
+ strcat(cp, fname);
+ return (cp);
+}
+
+/*
+ * Read linker.hints file into memory performing some sanity checks.
+ */
+static void
+moduledir_readhints(struct moduledir *mdp)
+{
+ struct stat st;
+ char *path;
+ int fd, size, version;
+
+ if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS))
+ return;
+ path = moduledir_fullpath(mdp, "linker.hints");
+ if (stat(path, &st) != 0 ||
+ st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) ||
+ st.st_size > LINKER_HINTS_MAX || (fd = open(path, O_RDONLY)) < 0) {
+ free(path);
+ mdp->d_flags |= MDIR_NOHINTS;
+ return;
+ }
+ free(path);
+ size = read(fd, &version, sizeof(version));
+ if (size != sizeof(version) || version != LINKER_HINTS_VERSION)
+ goto bad;
+ size = st.st_size - size;
+ mdp->d_hints = malloc(size);
+ if (mdp->d_hints == NULL)
+ goto bad;
+ if (read(fd, mdp->d_hints, size) != size)
+ goto bad;
+ mdp->d_hintsz = size;
+ close(fd);
+ return;
+bad:
+ close(fd);
+ if (mdp->d_hints) {
+ free(mdp->d_hints);
+ mdp->d_hints = NULL;
+ }
+ mdp->d_flags |= MDIR_NOHINTS;
+ return;
+}
+
+/*
+ * Extract directories from the ';' separated list, remove duplicates.
+ */
+static void
+moduledir_rebuild(void)
+{
+ struct moduledir *mdp, *mtmp;
+ const char *path, *cp, *ep;
+ size_t cplen;
+
+ path = getenv("module_path");
+ if (path == NULL)
+ path = default_searchpath;
+ /*
+ * Rebuild list of module directories if it changed
+ */
+ STAILQ_FOREACH(mdp, &moduledir_list, d_link)
+ mdp->d_flags |= MDIR_REMOVED;
+
+ for (ep = path; *ep != 0; ep++) {
+ cp = ep;
+ for (; *ep != 0 && *ep != ';'; ep++)
+ ;
+ /*
+ * Ignore trailing slashes
+ */
+ for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--)
+ ;
+ STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
+ if (strlen(mdp->d_path) != cplen || bcmp(cp, mdp->d_path, cplen) != 0)
+ continue;
+ mdp->d_flags &= ~MDIR_REMOVED;
+ break;
+ }
+ if (mdp == NULL) {
+ mdp = malloc(sizeof(*mdp) + cplen + 1);
+ if (mdp == NULL)
+ return;
+ mdp->d_path = (char*)(mdp + 1);
+ bcopy(cp, mdp->d_path, cplen);
+ mdp->d_path[cplen] = 0;
+ mdp->d_hints = NULL;
+ mdp->d_flags = 0;
+ STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
+ }
+ if (*ep == 0)
+ break;
+ }
+ /*
+ * Delete unused directories if any
+ */
+ mdp = STAILQ_FIRST(&moduledir_list);
+ while (mdp) {
+ if ((mdp->d_flags & MDIR_REMOVED) == 0) {
+ mdp = STAILQ_NEXT(mdp, d_link);
+ } else {
+ if (mdp->d_hints)
+ free(mdp->d_hints);
+ mtmp = mdp;
+ mdp = STAILQ_NEXT(mdp, d_link);
+ STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link);
+ free(mtmp);
+ }
+ }
+ return;
+}
diff --git a/stand/common/newvers.sh b/stand/common/newvers.sh
new file mode 100755
index 0000000..75efece
--- /dev/null
+++ b/stand/common/newvers.sh
@@ -0,0 +1,60 @@
+#!/bin/sh -
+#
+# $FreeBSD$
+# $NetBSD: newvers.sh,v 1.1 1997/07/26 01:50:38 thorpej Exp $
+#
+# Copyright (c) 1984, 1986, 1990, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)newvers.sh 8.1 (Berkeley) 4/20/94
+
+tempfile=$(mktemp tmp.XXXXXX) || exit
+trap "rm -f $tempfile" EXIT INT TERM
+
+include_metadata=true
+while getopts r opt; do
+ case "$opt" in
+ r)
+ include_metadata=
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+LC_ALL=C; export LC_ALL
+u=${USER-root} h=${HOSTNAME-`hostname`} t=`date`
+#r=`head -n 6 $1 | tail -n 1 | awk -F: ' { print $1 } '`
+r=`awk -F: ' /^[0-9]\.[0-9]+:/ { print $1; exit }' $1`
+
+bootprog_info="FreeBSD/${3} ${2}, Revision ${r}\\n"
+if [ -n "${include_metadata}" ]; then
+ bootprog_info="$bootprog_info(${t} ${u}@${h})\\n"
+fi
+
+echo "char bootprog_info[] = \"$bootprog_info\";" > $tempfile
+echo "unsigned bootprog_rev = ${r%%.*}${r##*.};" >> $tempfile
+mv $tempfile vers.c
diff --git a/stand/common/part.c b/stand/common/part.c
new file mode 100644
index 0000000..cdb1e00
--- /dev/null
+++ b/stand/common/part.c
@@ -0,0 +1,898 @@
+/*-
+ * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
+ * 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 AUTHORS 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 AUTHORS 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/diskmbr.h>
+#include <sys/disklabel.h>
+#include <sys/endian.h>
+#include <sys/gpt.h>
+#include <sys/stddef.h>
+#include <sys/queue.h>
+#include <sys/vtoc.h>
+
+#include <crc32.h>
+#include <part.h>
+#include <uuid.h>
+
+#ifdef PART_DEBUG
+#define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
+#else
+#define DEBUG(fmt, args...)
+#endif
+
+#ifdef LOADER_GPT_SUPPORT
+#define MAXTBLSZ 64
+static const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED;
+static const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
+static const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
+static const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI;
+static const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
+static const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
+static const uuid_t gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS;
+static const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
+static const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
+static const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
+#endif
+
+struct pentry {
+ struct ptable_entry part;
+ uint64_t flags;
+ union {
+ uint8_t bsd;
+ uint8_t mbr;
+ uuid_t gpt;
+ uint16_t vtoc8;
+ } type;
+ STAILQ_ENTRY(pentry) entry;
+};
+
+struct ptable {
+ enum ptable_type type;
+ uint16_t sectorsize;
+ uint64_t sectors;
+
+ STAILQ_HEAD(, pentry) entries;
+};
+
+static struct parttypes {
+ enum partition_type type;
+ const char *desc;
+} ptypes[] = {
+ { PART_UNKNOWN, "Unknown" },
+ { PART_EFI, "EFI" },
+ { PART_FREEBSD, "FreeBSD" },
+ { PART_FREEBSD_BOOT, "FreeBSD boot" },
+ { PART_FREEBSD_NANDFS, "FreeBSD nandfs" },
+ { PART_FREEBSD_UFS, "FreeBSD UFS" },
+ { PART_FREEBSD_ZFS, "FreeBSD ZFS" },
+ { PART_FREEBSD_SWAP, "FreeBSD swap" },
+ { PART_FREEBSD_VINUM, "FreeBSD vinum" },
+ { PART_LINUX, "Linux" },
+ { PART_LINUX_SWAP, "Linux swap" },
+ { PART_DOS, "DOS/Windows" },
+};
+
+const char *
+parttype2str(enum partition_type type)
+{
+ size_t i;
+
+ for (i = 0; i < nitems(ptypes); i++)
+ if (ptypes[i].type == type)
+ return (ptypes[i].desc);
+ return (ptypes[0].desc);
+}
+
+#ifdef LOADER_GPT_SUPPORT
+static void
+uuid_letoh(uuid_t *uuid)
+{
+
+ uuid->time_low = le32toh(uuid->time_low);
+ uuid->time_mid = le16toh(uuid->time_mid);
+ uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version);
+}
+
+static enum partition_type
+gpt_parttype(uuid_t type)
+{
+
+ if (uuid_equal(&type, &gpt_uuid_efi, NULL))
+ return (PART_EFI);
+ else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL))
+ return (PART_DOS);
+ else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL))
+ return (PART_FREEBSD_BOOT);
+ else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL))
+ return (PART_FREEBSD_UFS);
+ else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL))
+ return (PART_FREEBSD_ZFS);
+ else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL))
+ return (PART_FREEBSD_SWAP);
+ else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL))
+ return (PART_FREEBSD_VINUM);
+ else if (uuid_equal(&type, &gpt_uuid_freebsd_nandfs, NULL))
+ return (PART_FREEBSD_NANDFS);
+ else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL))
+ return (PART_FREEBSD);
+ return (PART_UNKNOWN);
+}
+
+static struct gpt_hdr *
+gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last,
+ uint16_t sectorsize)
+{
+ uint32_t sz, crc;
+
+ if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) {
+ DEBUG("no GPT signature");
+ return (NULL);
+ }
+ sz = le32toh(hdr->hdr_size);
+ if (sz < 92 || sz > sectorsize) {
+ DEBUG("invalid GPT header size: %d", sz);
+ return (NULL);
+ }
+ crc = le32toh(hdr->hdr_crc_self);
+ hdr->hdr_crc_self = 0;
+ if (crc32(hdr, sz) != crc) {
+ DEBUG("GPT header's CRC doesn't match");
+ return (NULL);
+ }
+ hdr->hdr_crc_self = crc;
+ hdr->hdr_revision = le32toh(hdr->hdr_revision);
+ if (hdr->hdr_revision < GPT_HDR_REVISION) {
+ DEBUG("unsupported GPT revision %d", hdr->hdr_revision);
+ return (NULL);
+ }
+ hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self);
+ if (hdr->hdr_lba_self != lba_self) {
+ DEBUG("self LBA doesn't match");
+ return (NULL);
+ }
+ hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt);
+ if (hdr->hdr_lba_alt == hdr->hdr_lba_self) {
+ DEBUG("invalid alternate LBA");
+ return (NULL);
+ }
+ hdr->hdr_entries = le32toh(hdr->hdr_entries);
+ hdr->hdr_entsz = le32toh(hdr->hdr_entsz);
+ if (hdr->hdr_entries == 0 ||
+ hdr->hdr_entsz < sizeof(struct gpt_ent) ||
+ sectorsize % hdr->hdr_entsz != 0) {
+ DEBUG("invalid entry size or number of entries");
+ return (NULL);
+ }
+ hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start);
+ hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end);
+ hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table);
+ hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table);
+ uuid_letoh(&hdr->hdr_uuid);
+ return (hdr);
+}
+
+static int
+gpt_checktbl(const struct gpt_hdr *hdr, uint8_t *tbl, size_t size,
+ uint64_t lba_last)
+{
+ struct gpt_ent *ent;
+ uint32_t i, cnt;
+
+ cnt = size / hdr->hdr_entsz;
+ if (hdr->hdr_entries <= cnt) {
+ cnt = hdr->hdr_entries;
+ /* Check CRC only when buffer size is enough for table. */
+ if (hdr->hdr_crc_table !=
+ crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) {
+ DEBUG("GPT table's CRC doesn't match");
+ return (-1);
+ }
+ }
+ for (i = 0; i < cnt; i++) {
+ ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
+ uuid_letoh(&ent->ent_type);
+ if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
+ continue;
+ ent->ent_lba_start = le64toh(ent->ent_lba_start);
+ ent->ent_lba_end = le64toh(ent->ent_lba_end);
+ }
+ return (0);
+}
+
+static struct ptable *
+ptable_gptread(struct ptable *table, void *dev, diskread_t dread)
+{
+ struct pentry *entry;
+ struct gpt_hdr *phdr, hdr;
+ struct gpt_ent *ent;
+ uint8_t *buf, *tbl;
+ uint64_t offset;
+ int pri, sec;
+ size_t size, i;
+
+ buf = malloc(table->sectorsize);
+ if (buf == NULL)
+ return (NULL);
+ tbl = malloc(table->sectorsize * MAXTBLSZ);
+ if (tbl == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ /* Read the primary GPT header. */
+ if (dread(dev, buf, 1, 1) != 0) {
+ ptable_close(table);
+ table = NULL;
+ goto out;
+ }
+ pri = sec = 0;
+ /* Check the primary GPT header. */
+ phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1,
+ table->sectorsize);
+ if (phdr != NULL) {
+ /* Read the primary GPT table. */
+ size = MIN(MAXTBLSZ,
+ howmany(phdr->hdr_entries * phdr->hdr_entsz,
+ table->sectorsize));
+ if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
+ gpt_checktbl(phdr, tbl, size * table->sectorsize,
+ table->sectors - 1) == 0) {
+ memcpy(&hdr, phdr, sizeof(hdr));
+ pri = 1;
+ }
+ }
+ offset = pri ? hdr.hdr_lba_alt: table->sectors - 1;
+ /* Read the backup GPT header. */
+ if (dread(dev, buf, 1, offset) != 0)
+ phdr = NULL;
+ else
+ phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset,
+ table->sectors - 1, table->sectorsize);
+ if (phdr != NULL) {
+ /*
+ * Compare primary and backup headers.
+ * If they are equal, then we do not need to read backup
+ * table. If they are different, then prefer backup header
+ * and try to read backup table.
+ */
+ if (pri == 0 ||
+ uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 ||
+ hdr.hdr_revision != phdr->hdr_revision ||
+ hdr.hdr_size != phdr->hdr_size ||
+ hdr.hdr_lba_start != phdr->hdr_lba_start ||
+ hdr.hdr_lba_end != phdr->hdr_lba_end ||
+ hdr.hdr_entries != phdr->hdr_entries ||
+ hdr.hdr_entsz != phdr->hdr_entsz ||
+ hdr.hdr_crc_table != phdr->hdr_crc_table) {
+ /* Read the backup GPT table. */
+ size = MIN(MAXTBLSZ,
+ howmany(phdr->hdr_entries * phdr->hdr_entsz,
+ table->sectorsize));
+ if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
+ gpt_checktbl(phdr, tbl, size * table->sectorsize,
+ table->sectors - 1) == 0) {
+ memcpy(&hdr, phdr, sizeof(hdr));
+ sec = 1;
+ }
+ }
+ }
+ if (pri == 0 && sec == 0) {
+ /* Both primary and backup tables are invalid. */
+ table->type = PTABLE_NONE;
+ goto out;
+ }
+ DEBUG("GPT detected");
+ size = MIN(hdr.hdr_entries * hdr.hdr_entsz,
+ MAXTBLSZ * table->sectorsize);
+
+ /*
+ * If the disk's sector count is smaller than the sector count recorded
+ * in the disk's GPT table header, set the table->sectors to the value
+ * recorded in GPT tables. This is done to work around buggy firmware
+ * that returns truncated disk sizes.
+ *
+ * Note, this is still not a foolproof way to get disk's size. For
+ * example, an image file can be truncated when copied to smaller media.
+ */
+ if (hdr.hdr_lba_alt + 1 > table->sectors)
+ table->sectors = hdr.hdr_lba_alt + 1;
+
+ for (i = 0; i < size / hdr.hdr_entsz; i++) {
+ ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz);
+ if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
+ continue;
+
+ /* Simple sanity checks. */
+ if (ent->ent_lba_start < hdr.hdr_lba_start ||
+ ent->ent_lba_end > hdr.hdr_lba_end ||
+ ent->ent_lba_start > ent->ent_lba_end)
+ continue;
+
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL)
+ break;
+ entry->part.start = ent->ent_lba_start;
+ entry->part.end = ent->ent_lba_end;
+ entry->part.index = i + 1;
+ entry->part.type = gpt_parttype(ent->ent_type);
+ entry->flags = le64toh(ent->ent_attr);
+ memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t));
+ STAILQ_INSERT_TAIL(&table->entries, entry, entry);
+ DEBUG("new GPT partition added");
+ }
+out:
+ free(buf);
+ free(tbl);
+ return (table);
+}
+#endif /* LOADER_GPT_SUPPORT */
+
+#ifdef LOADER_MBR_SUPPORT
+/* We do not need to support too many EBR partitions in the loader */
+#define MAXEBRENTRIES 8
+static enum partition_type
+mbr_parttype(uint8_t type)
+{
+
+ switch (type) {
+ case DOSPTYP_386BSD:
+ return (PART_FREEBSD);
+ case DOSPTYP_LINSWP:
+ return (PART_LINUX_SWAP);
+ case DOSPTYP_LINUX:
+ return (PART_LINUX);
+ case 0x01:
+ case 0x04:
+ case 0x06:
+ case 0x07:
+ case 0x0b:
+ case 0x0c:
+ case 0x0e:
+ return (PART_DOS);
+ }
+ return (PART_UNKNOWN);
+}
+
+static struct ptable *
+ptable_ebrread(struct ptable *table, void *dev, diskread_t dread)
+{
+ struct dos_partition *dp;
+ struct pentry *e1, *entry;
+ uint32_t start, end, offset;
+ u_char *buf;
+ int i, index;
+
+ STAILQ_FOREACH(e1, &table->entries, entry) {
+ if (e1->type.mbr == DOSPTYP_EXT ||
+ e1->type.mbr == DOSPTYP_EXTLBA)
+ break;
+ }
+ if (e1 == NULL)
+ return (table);
+ index = 5;
+ offset = e1->part.start;
+ buf = malloc(table->sectorsize);
+ if (buf == NULL)
+ return (table);
+ DEBUG("EBR detected");
+ for (i = 0; i < MAXEBRENTRIES; i++) {
+#if 0 /* Some BIOSes return an incorrect number of sectors */
+ if (offset >= table->sectors)
+ break;
+#endif
+ if (dread(dev, buf, 1, offset) != 0)
+ break;
+ dp = (struct dos_partition *)(buf + DOSPARTOFF);
+ if (dp[0].dp_typ == 0)
+ break;
+ start = le32toh(dp[0].dp_start);
+ if (dp[0].dp_typ == DOSPTYP_EXT &&
+ dp[1].dp_typ == 0) {
+ offset = e1->part.start + start;
+ continue;
+ }
+ end = le32toh(dp[0].dp_size);
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL)
+ break;
+ entry->part.start = offset + start;
+ entry->part.end = entry->part.start + end - 1;
+ entry->part.index = index++;
+ entry->part.type = mbr_parttype(dp[0].dp_typ);
+ entry->flags = dp[0].dp_flag;
+ entry->type.mbr = dp[0].dp_typ;
+ STAILQ_INSERT_TAIL(&table->entries, entry, entry);
+ DEBUG("new EBR partition added");
+ if (dp[1].dp_typ == 0)
+ break;
+ offset = e1->part.start + le32toh(dp[1].dp_start);
+ }
+ free(buf);
+ return (table);
+}
+#endif /* LOADER_MBR_SUPPORT */
+
+static enum partition_type
+bsd_parttype(uint8_t type)
+{
+
+ switch (type) {
+ case FS_NANDFS:
+ return (PART_FREEBSD_NANDFS);
+ case FS_SWAP:
+ return (PART_FREEBSD_SWAP);
+ case FS_BSDFFS:
+ return (PART_FREEBSD_UFS);
+ case FS_VINUM:
+ return (PART_FREEBSD_VINUM);
+ case FS_ZFS:
+ return (PART_FREEBSD_ZFS);
+ }
+ return (PART_UNKNOWN);
+}
+
+static struct ptable *
+ptable_bsdread(struct ptable *table, void *dev, diskread_t dread)
+{
+ struct disklabel *dl;
+ struct partition *part;
+ struct pentry *entry;
+ uint8_t *buf;
+ uint32_t raw_offset;
+ int i;
+
+ if (table->sectorsize < sizeof(struct disklabel)) {
+ DEBUG("Too small sectorsize");
+ return (table);
+ }
+ buf = malloc(table->sectorsize);
+ if (buf == NULL)
+ return (table);
+ if (dread(dev, buf, 1, 1) != 0) {
+ DEBUG("read failed");
+ ptable_close(table);
+ table = NULL;
+ goto out;
+ }
+ dl = (struct disklabel *)buf;
+ if (le32toh(dl->d_magic) != DISKMAGIC &&
+ le32toh(dl->d_magic2) != DISKMAGIC)
+ goto out;
+ if (le32toh(dl->d_secsize) != table->sectorsize) {
+ DEBUG("unsupported sector size");
+ goto out;
+ }
+ dl->d_npartitions = le16toh(dl->d_npartitions);
+ if (dl->d_npartitions > 20 || dl->d_npartitions < 8) {
+ DEBUG("invalid number of partitions");
+ goto out;
+ }
+ DEBUG("BSD detected");
+ part = &dl->d_partitions[0];
+ raw_offset = le32toh(part[RAW_PART].p_offset);
+ for (i = 0; i < dl->d_npartitions; i++, part++) {
+ if (i == RAW_PART)
+ continue;
+ if (part->p_size == 0)
+ continue;
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL)
+ break;
+ entry->part.start = le32toh(part->p_offset) - raw_offset;
+ entry->part.end = entry->part.start +
+ le32toh(part->p_size) - 1;
+ entry->part.type = bsd_parttype(part->p_fstype);
+ entry->part.index = i; /* starts from zero */
+ entry->type.bsd = part->p_fstype;
+ STAILQ_INSERT_TAIL(&table->entries, entry, entry);
+ DEBUG("new BSD partition added");
+ }
+ table->type = PTABLE_BSD;
+out:
+ free(buf);
+ return (table);
+}
+
+#ifdef LOADER_VTOC8_SUPPORT
+static enum partition_type
+vtoc8_parttype(uint16_t type)
+{
+
+ switch (type) {
+ case VTOC_TAG_FREEBSD_NANDFS:
+ return (PART_FREEBSD_NANDFS);
+ case VTOC_TAG_FREEBSD_SWAP:
+ return (PART_FREEBSD_SWAP);
+ case VTOC_TAG_FREEBSD_UFS:
+ return (PART_FREEBSD_UFS);
+ case VTOC_TAG_FREEBSD_VINUM:
+ return (PART_FREEBSD_VINUM);
+ case VTOC_TAG_FREEBSD_ZFS:
+ return (PART_FREEBSD_ZFS);
+ }
+ return (PART_UNKNOWN);
+}
+
+static struct ptable *
+ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread)
+{
+ struct pentry *entry;
+ struct vtoc8 *dl;
+ uint8_t *buf;
+ uint16_t sum, heads, sectors;
+ int i;
+
+ if (table->sectorsize != sizeof(struct vtoc8))
+ return (table);
+ buf = malloc(table->sectorsize);
+ if (buf == NULL)
+ return (table);
+ if (dread(dev, buf, 1, 0) != 0) {
+ DEBUG("read failed");
+ ptable_close(table);
+ table = NULL;
+ goto out;
+ }
+ dl = (struct vtoc8 *)buf;
+ /* Check the sum */
+ for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum))
+ sum ^= be16dec(buf + i);
+ if (sum != 0) {
+ DEBUG("incorrect checksum");
+ goto out;
+ }
+ if (be16toh(dl->nparts) != VTOC8_NPARTS) {
+ DEBUG("invalid number of entries");
+ goto out;
+ }
+ sectors = be16toh(dl->nsecs);
+ heads = be16toh(dl->nheads);
+ if (sectors * heads == 0) {
+ DEBUG("invalid geometry");
+ goto out;
+ }
+ DEBUG("VTOC8 detected");
+ for (i = 0; i < VTOC8_NPARTS; i++) {
+ dl->part[i].tag = be16toh(dl->part[i].tag);
+ if (i == VTOC_RAW_PART ||
+ dl->part[i].tag == VTOC_TAG_UNASSIGNED)
+ continue;
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL)
+ break;
+ entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors;
+ entry->part.end = be32toh(dl->map[i].nblks) +
+ entry->part.start - 1;
+ entry->part.type = vtoc8_parttype(dl->part[i].tag);
+ entry->part.index = i; /* starts from zero */
+ entry->type.vtoc8 = dl->part[i].tag;
+ STAILQ_INSERT_TAIL(&table->entries, entry, entry);
+ DEBUG("new VTOC8 partition added");
+ }
+ table->type = PTABLE_VTOC8;
+out:
+ free(buf);
+ return (table);
+
+}
+#endif /* LOADER_VTOC8_SUPPORT */
+
+struct ptable *
+ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize,
+ diskread_t *dread)
+{
+ struct dos_partition *dp;
+ struct ptable *table;
+ uint8_t *buf;
+ int i, count;
+#ifdef LOADER_MBR_SUPPORT
+ struct pentry *entry;
+ uint32_t start, end;
+ int has_ext;
+#endif
+ table = NULL;
+ buf = malloc(sectorsize);
+ if (buf == NULL)
+ return (NULL);
+ /* First, read the MBR. */
+ if (dread(dev, buf, 1, DOSBBSECTOR) != 0) {
+ DEBUG("read failed");
+ goto out;
+ }
+
+ table = malloc(sizeof(*table));
+ if (table == NULL)
+ goto out;
+ table->sectors = sectors;
+ table->sectorsize = sectorsize;
+ table->type = PTABLE_NONE;
+ STAILQ_INIT(&table->entries);
+
+#ifdef LOADER_VTOC8_SUPPORT
+ if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) {
+ if (ptable_vtoc8read(table, dev, dread) == NULL) {
+ /* Read error. */
+ table = NULL;
+ goto out;
+ } else if (table->type == PTABLE_VTOC8)
+ goto out;
+ }
+#endif
+ /* Check the BSD label. */
+ if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */
+ table = NULL;
+ goto out;
+ } else if (table->type == PTABLE_BSD)
+ goto out;
+
+#if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
+ /* Check the MBR magic. */
+ if (buf[DOSMAGICOFFSET] != 0x55 ||
+ buf[DOSMAGICOFFSET + 1] != 0xaa) {
+ DEBUG("magic sequence not found");
+#if defined(LOADER_GPT_SUPPORT)
+ /* There is no PMBR, check that we have backup GPT */
+ table->type = PTABLE_GPT;
+ table = ptable_gptread(table, dev, dread);
+#endif
+ goto out;
+ }
+ /* Check that we have PMBR. Also do some validation. */
+ dp = (struct dos_partition *)(buf + DOSPARTOFF);
+ for (i = 0, count = 0; i < NDOSPART; i++) {
+ if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) {
+ DEBUG("invalid partition flag %x", dp[i].dp_flag);
+ goto out;
+ }
+#ifdef LOADER_GPT_SUPPORT
+ if (dp[i].dp_typ == DOSPTYP_PMBR) {
+ table->type = PTABLE_GPT;
+ DEBUG("PMBR detected");
+ }
+#endif
+ if (dp[i].dp_typ != 0)
+ count++;
+ }
+ /* Do we have some invalid values? */
+ if (table->type == PTABLE_GPT && count > 1) {
+ if (dp[1].dp_typ != DOSPTYP_HFS) {
+ table->type = PTABLE_NONE;
+ DEBUG("Incorrect PMBR, ignore it");
+ } else {
+ DEBUG("Bootcamp detected");
+ }
+ }
+#ifdef LOADER_GPT_SUPPORT
+ if (table->type == PTABLE_GPT) {
+ table = ptable_gptread(table, dev, dread);
+ goto out;
+ }
+#endif
+#ifdef LOADER_MBR_SUPPORT
+ /* Read MBR. */
+ DEBUG("MBR detected");
+ table->type = PTABLE_MBR;
+ for (i = has_ext = 0; i < NDOSPART; i++) {
+ if (dp[i].dp_typ == 0)
+ continue;
+ start = le32dec(&(dp[i].dp_start));
+ end = le32dec(&(dp[i].dp_size));
+ if (start == 0 || end == 0)
+ continue;
+#if 0 /* Some BIOSes return an incorrect number of sectors */
+ if (start + end - 1 >= sectors)
+ continue; /* XXX: ignore */
+#endif
+ if (dp[i].dp_typ == DOSPTYP_EXT ||
+ dp[i].dp_typ == DOSPTYP_EXTLBA)
+ has_ext = 1;
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL)
+ break;
+ entry->part.start = start;
+ entry->part.end = start + end - 1;
+ entry->part.index = i + 1;
+ entry->part.type = mbr_parttype(dp[i].dp_typ);
+ entry->flags = dp[i].dp_flag;
+ entry->type.mbr = dp[i].dp_typ;
+ STAILQ_INSERT_TAIL(&table->entries, entry, entry);
+ DEBUG("new MBR partition added");
+ }
+ if (has_ext) {
+ table = ptable_ebrread(table, dev, dread);
+ /* FALLTHROUGH */
+ }
+#endif /* LOADER_MBR_SUPPORT */
+#endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
+out:
+ free(buf);
+ return (table);
+}
+
+void
+ptable_close(struct ptable *table)
+{
+ struct pentry *entry;
+
+ while (!STAILQ_EMPTY(&table->entries)) {
+ entry = STAILQ_FIRST(&table->entries);
+ STAILQ_REMOVE_HEAD(&table->entries, entry);
+ free(entry);
+ }
+ free(table);
+}
+
+enum ptable_type
+ptable_gettype(const struct ptable *table)
+{
+
+ return (table->type);
+}
+
+int
+ptable_getsize(const struct ptable *table, uint64_t *sizep)
+{
+ uint64_t tmp = table->sectors * table->sectorsize;
+
+ if (tmp < table->sectors)
+ return (EOVERFLOW);
+
+ if (sizep != NULL)
+ *sizep = tmp;
+ return (0);
+}
+
+int
+ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index)
+{
+ struct pentry *entry;
+
+ if (part == NULL || table == NULL)
+ return (EINVAL);
+
+ STAILQ_FOREACH(entry, &table->entries, entry) {
+ if (entry->part.index != index)
+ continue;
+ memcpy(part, &entry->part, sizeof(*part));
+ return (0);
+ }
+ return (ENOENT);
+}
+
+/*
+ * Search for a slice with the following preferences:
+ *
+ * 1: Active FreeBSD slice
+ * 2: Non-active FreeBSD slice
+ * 3: Active Linux slice
+ * 4: non-active Linux slice
+ * 5: Active FAT/FAT32 slice
+ * 6: non-active FAT/FAT32 slice
+ */
+#define PREF_RAWDISK 0
+#define PREF_FBSD_ACT 1
+#define PREF_FBSD 2
+#define PREF_LINUX_ACT 3
+#define PREF_LINUX 4
+#define PREF_DOS_ACT 5
+#define PREF_DOS 6
+#define PREF_NONE 7
+int
+ptable_getbestpart(const struct ptable *table, struct ptable_entry *part)
+{
+ struct pentry *entry, *best;
+ int pref, preflevel;
+
+ if (part == NULL || table == NULL)
+ return (EINVAL);
+
+ best = NULL;
+ preflevel = pref = PREF_NONE;
+ STAILQ_FOREACH(entry, &table->entries, entry) {
+#ifdef LOADER_MBR_SUPPORT
+ if (table->type == PTABLE_MBR) {
+ switch (entry->type.mbr) {
+ case DOSPTYP_386BSD:
+ pref = entry->flags & 0x80 ? PREF_FBSD_ACT:
+ PREF_FBSD;
+ break;
+ case DOSPTYP_LINUX:
+ pref = entry->flags & 0x80 ? PREF_LINUX_ACT:
+ PREF_LINUX;
+ break;
+ case 0x01: /* DOS/Windows */
+ case 0x04:
+ case 0x06:
+ case 0x0c:
+ case 0x0e:
+ case DOSPTYP_FAT32:
+ pref = entry->flags & 0x80 ? PREF_DOS_ACT:
+ PREF_DOS;
+ break;
+ default:
+ pref = PREF_NONE;
+ }
+ }
+#endif /* LOADER_MBR_SUPPORT */
+#ifdef LOADER_GPT_SUPPORT
+ if (table->type == PTABLE_GPT) {
+ if (entry->part.type == PART_DOS)
+ pref = PREF_DOS;
+ else if (entry->part.type == PART_FREEBSD_UFS ||
+ entry->part.type == PART_FREEBSD_ZFS)
+ pref = PREF_FBSD;
+ else
+ pref = PREF_NONE;
+ }
+#endif /* LOADER_GPT_SUPPORT */
+ if (pref < preflevel) {
+ preflevel = pref;
+ best = entry;
+ }
+ }
+ if (best != NULL) {
+ memcpy(part, &best->part, sizeof(*part));
+ return (0);
+ }
+ return (ENOENT);
+}
+
+int
+ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter)
+{
+ struct pentry *entry;
+ char name[32];
+ int ret = 0;
+
+ name[0] = '\0';
+ STAILQ_FOREACH(entry, &table->entries, entry) {
+#ifdef LOADER_MBR_SUPPORT
+ if (table->type == PTABLE_MBR)
+ sprintf(name, "s%d", entry->part.index);
+ else
+#endif
+#ifdef LOADER_GPT_SUPPORT
+ if (table->type == PTABLE_GPT)
+ sprintf(name, "p%d", entry->part.index);
+ else
+#endif
+#ifdef LOADER_VTOC8_SUPPORT
+ if (table->type == PTABLE_VTOC8)
+ sprintf(name, "%c", (uint8_t) 'a' +
+ entry->part.index);
+ else
+#endif
+ if (table->type == PTABLE_BSD)
+ sprintf(name, "%c", (uint8_t) 'a' +
+ entry->part.index);
+ if ((ret = iter(arg, name, &entry->part)) != 0)
+ return (ret);
+ }
+ return (ret);
+}
diff --git a/stand/common/part.h b/stand/common/part.h
new file mode 100644
index 0000000..19bd670
--- /dev/null
+++ b/stand/common/part.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
+ * 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PART_H_
+#define _PART_H_
+
+struct ptable;
+
+enum ptable_type {
+ PTABLE_NONE,
+ PTABLE_BSD,
+ PTABLE_MBR,
+ PTABLE_GPT,
+ PTABLE_VTOC8
+};
+
+enum partition_type {
+ PART_UNKNOWN,
+ PART_EFI,
+ PART_FREEBSD,
+ PART_FREEBSD_BOOT,
+ PART_FREEBSD_NANDFS,
+ PART_FREEBSD_UFS,
+ PART_FREEBSD_ZFS,
+ PART_FREEBSD_SWAP,
+ PART_FREEBSD_VINUM,
+ PART_LINUX,
+ PART_LINUX_SWAP,
+ PART_DOS,
+};
+
+struct ptable_entry {
+ uint64_t start;
+ uint64_t end;
+ int index;
+ enum partition_type type;
+};
+
+/* The offset and size are in sectors */
+typedef int (diskread_t)(void *arg, void *buf, size_t blocks, uint64_t offset);
+typedef int (ptable_iterate_t)(void *arg, const char *partname,
+ const struct ptable_entry *part);
+
+struct ptable *ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize,
+ diskread_t *dread);
+void ptable_close(struct ptable *table);
+enum ptable_type ptable_gettype(const struct ptable *table);
+int ptable_getsize(const struct ptable *table, uint64_t *sizep);
+
+int ptable_getpart(const struct ptable *table, struct ptable_entry *part,
+ int index);
+int ptable_getbestpart(const struct ptable *table, struct ptable_entry *part);
+
+int ptable_iterate(const struct ptable *table, void *arg,
+ ptable_iterate_t *iter);
+const char *parttype2str(enum partition_type type);
+
+#endif /* !_PART_H_ */
diff --git a/stand/common/paths.h b/stand/common/paths.h
new file mode 100644
index 0000000..9ed45e6
--- /dev/null
+++ b/stand/common/paths.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2016 M. Warner Losh <imp@freebsd.org>
+ * 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PATHS_H_
+#define _PATHS_H_
+
+#define PATH_DOTCONFIG "/boot.config"
+#define PATH_CONFIG "/boot/config"
+#define PATH_LOADER "/boot/loader"
+#define PATH_LOADER_EFI "/boot/loader.efi"
+#define PATH_LOADER_ZFS "/boot/zfsloader"
+#define PATH_KERNEL "/boot/kernel/kernel"
+
+#endif /* _PATHS_H_ */
diff --git a/stand/common/pnp.c b/stand/common/pnp.c
new file mode 100644
index 0000000..6197776
--- /dev/null
+++ b/stand/common/pnp.c
@@ -0,0 +1,236 @@
+/*
+ * mjs copyright
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * "Plug and Play" functionality.
+ *
+ * We use the PnP enumerators to obtain identifiers for installed hardware,
+ * and the contents of a database to determine modules to be loaded to support
+ * such hardware.
+ */
+
+#include <stand.h>
+#include <string.h>
+#include <bootstrap.h>
+#ifdef BOOT_FORTH
+#include "ficl.h"
+#endif
+
+static struct pnpinfo_stql pnp_devices;
+static int pnp_devices_initted = 0;
+
+static void pnp_discard(void);
+
+/*
+ * Perform complete enumeration sweep
+ */
+
+COMMAND_SET(pnpscan, "pnpscan", "scan for PnP devices", pnp_scan);
+
+static int
+pnp_scan(int argc, char *argv[])
+{
+ struct pnpinfo *pi;
+ int hdlr;
+ int verbose;
+ int ch;
+
+ if (pnp_devices_initted == 0) {
+ STAILQ_INIT(&pnp_devices);
+ pnp_devices_initted = 1;
+ }
+
+ verbose = 0;
+ optind = 1;
+ optreset = 1;
+ while ((ch = getopt(argc, argv, "v")) != -1) {
+ switch(ch) {
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ /* getopt has already reported an error */
+ return(CMD_OK);
+ }
+ }
+
+ /* forget anything we think we knew */
+ pnp_discard();
+
+ /* iterate over all of the handlers */
+ for (hdlr = 0; pnphandlers[hdlr] != NULL; hdlr++) {
+ if (verbose)
+ printf("Probing %s...\n", pnphandlers[hdlr]->pp_name);
+ pnphandlers[hdlr]->pp_enumerate();
+ }
+ if (verbose) {
+ pager_open();
+ if (pager_output("PNP scan summary:\n"))
+ goto out;
+ STAILQ_FOREACH(pi, &pnp_devices, pi_link) {
+ pager_output(STAILQ_FIRST(&pi->pi_ident)->id_ident); /* first ident should be canonical */
+ if (pi->pi_desc != NULL) {
+ pager_output(" : ");
+ pager_output(pi->pi_desc);
+ }
+ if (pager_output("\n"))
+ break;
+ }
+out:
+ pager_close();
+ }
+ return(CMD_OK);
+}
+
+/*
+ * Throw away anything we think we know about PnP devices.
+ */
+static void
+pnp_discard(void)
+{
+ struct pnpinfo *pi;
+
+ while (STAILQ_FIRST(&pnp_devices) != NULL) {
+ pi = STAILQ_FIRST(&pnp_devices);
+ STAILQ_REMOVE_HEAD(&pnp_devices, pi_link);
+ pnp_freeinfo(pi);
+ }
+}
+
+/*
+ * Add a unique identifier to (pi)
+ */
+void
+pnp_addident(struct pnpinfo *pi, char *ident)
+{
+ struct pnpident *id;
+
+ STAILQ_FOREACH(id, &pi->pi_ident, id_link)
+ if (!strcmp(id->id_ident, ident))
+ return; /* already have this one */
+
+ id = malloc(sizeof(struct pnpident));
+ id->id_ident = strdup(ident);
+ STAILQ_INSERT_TAIL(&pi->pi_ident, id, id_link);
+}
+
+/*
+ * Allocate a new pnpinfo struct
+ */
+struct pnpinfo *
+pnp_allocinfo(void)
+{
+ struct pnpinfo *pi;
+
+ pi = malloc(sizeof(struct pnpinfo));
+ bzero(pi, sizeof(struct pnpinfo));
+ STAILQ_INIT(&pi->pi_ident);
+ return(pi);
+}
+
+/*
+ * Release storage held by a pnpinfo struct
+ */
+void
+pnp_freeinfo(struct pnpinfo *pi)
+{
+ struct pnpident *id;
+
+ while (!STAILQ_EMPTY(&pi->pi_ident)) {
+ id = STAILQ_FIRST(&pi->pi_ident);
+ STAILQ_REMOVE_HEAD(&pi->pi_ident, id_link);
+ free(id->id_ident);
+ free(id);
+ }
+ if (pi->pi_desc)
+ free(pi->pi_desc);
+ if (pi->pi_module)
+ free(pi->pi_module);
+ if (pi->pi_argv)
+ free(pi->pi_argv);
+ free(pi);
+}
+
+/*
+ * Add a new pnpinfo struct to the list.
+ */
+void
+pnp_addinfo(struct pnpinfo *pi)
+{
+ STAILQ_INSERT_TAIL(&pnp_devices, pi, pi_link);
+}
+
+
+/*
+ * Format an EISA id as a string in standard ISA PnP format, AAAIIRR
+ * where 'AAA' is the EISA vendor ID, II is the product ID and RR the revision ID.
+ */
+char *
+pnp_eisaformat(u_int8_t *data)
+{
+ static char idbuf[8];
+ const char hextoascii[] = "0123456789abcdef";
+
+ idbuf[0] = '@' + ((data[0] & 0x7c) >> 2);
+ idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5));
+ idbuf[2] = '@' + (data[1] & 0x1f);
+ idbuf[3] = hextoascii[(data[2] >> 4)];
+ idbuf[4] = hextoascii[(data[2] & 0xf)];
+ idbuf[5] = hextoascii[(data[3] >> 4)];
+ idbuf[6] = hextoascii[(data[3] & 0xf)];
+ idbuf[7] = 0;
+ return(idbuf);
+}
+
+#ifdef BOOT_FORTH
+void
+ficlPnpdevices(FICL_VM *pVM)
+{
+ static int pnp_devices_initted = 0;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ if(!pnp_devices_initted) {
+ STAILQ_INIT(&pnp_devices);
+ pnp_devices_initted = 1;
+ }
+
+ stackPushPtr(pVM->pStack, &pnp_devices);
+
+ return;
+}
+
+void
+ficlPnphandlers(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ stackPushPtr(pVM->pStack, pnphandlers);
+
+ return;
+}
+
+/*
+ * Glue function to add the appropriate forth words to access pnp BIOS
+ * functionality.
+ */
+static void ficlCompilePnp(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+ dictAppendWord(dp, "pnpdevices",ficlPnpdevices, FW_DEFAULT);
+ dictAppendWord(dp, "pnphandlers",ficlPnphandlers, FW_DEFAULT);
+}
+
+FICL_COMPILE_SET(ficlCompilePnp);
+#endif
diff --git a/stand/common/rbx.h b/stand/common/rbx.h
new file mode 100644
index 0000000..21371a5
--- /dev/null
+++ b/stand/common/rbx.h
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _RBX_H_
+#define _RBX_H_
+
+#define RBX_ASKNAME 0x0 /* -a */
+#define RBX_SINGLE 0x1 /* -s */
+/* 0x2 is reserved for log2(RB_NOSYNC). */
+/* 0x3 is reserved for log2(RB_HALT). */
+/* 0x4 is reserved for log2(RB_INITNAME). */
+#define RBX_DFLTROOT 0x5 /* -r */
+#define RBX_KDB 0x6 /* -d */
+/* 0x7 is reserved for log2(RB_RDONLY). */
+/* 0x8 is reserved for log2(RB_DUMP). */
+/* 0x9 is reserved for log2(RB_MINIROOT). */
+#define RBX_CONFIG 0xa /* -c */
+#define RBX_VERBOSE 0xb /* -v */
+#define RBX_SERIAL 0xc /* -h */
+#define RBX_CDROM 0xd /* -C */
+/* 0xe is reserved for log2(RB_POWEROFF). */
+#define RBX_GDB 0xf /* -g */
+#define RBX_MUTE 0x10 /* -m */
+/* 0x11 is reserved for log2(RB_SELFTEST). */
+/* 0x12 is reserved for boot programs. */
+/* 0x13 is reserved for boot programs. */
+#define RBX_PAUSE 0x14 /* -p */
+#define RBX_QUIET 0x15 /* -q */
+#define RBX_NOINTR 0x1c /* -n */
+/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
+#define RBX_DUAL 0x1d /* -D */
+/* 0x1f is reserved for log2(RB_BOOTINFO). */
+
+/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
+#define RBX_MASK (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \
+ OPT_SET(RBX_DFLTROOT) | OPT_SET(RBX_KDB ) | \
+ OPT_SET(RBX_CONFIG) | OPT_SET(RBX_VERBOSE) | \
+ OPT_SET(RBX_SERIAL) | OPT_SET(RBX_CDROM) | \
+ OPT_SET(RBX_GDB ) | OPT_SET(RBX_MUTE) | \
+ OPT_SET(RBX_PAUSE) | OPT_SET(RBX_DUAL))
+
+#define OPT_SET(opt) (1 << (opt))
+#define OPT_CHECK(opt) ((opts) & OPT_SET(opt))
+
+extern uint32_t opts;
+
+#endif /* !_RBX_H_ */
diff --git a/stand/common/reloc_elf.c b/stand/common/reloc_elf.c
new file mode 100644
index 0000000..aa3b0f7
--- /dev/null
+++ b/stand/common/reloc_elf.c
@@ -0,0 +1,231 @@
+/*-
+ * Copyright (c) 2003 Jake Burkholder.
+ * Copyright 1996-1998 John D. Polstra.
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 1998 Peter Wemm <peter@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <machine/elf.h>
+
+#include <stand.h>
+
+#define FREEBSD_ELF
+#include <link.h>
+
+#include "bootstrap.h"
+
+#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l)
+
+/*
+ * Apply a single intra-module relocation to the data. `relbase' is the
+ * target relocation base for the section (i.e. it corresponds to where
+ * r_offset == 0). `dataaddr' is the relocated address corresponding to
+ * the start of the data, and `len' is the number of bytes.
+ */
+int
+__elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata,
+ int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len)
+{
+#ifdef __sparc__
+ Elf_Size w;
+ const Elf_Rela *a;
+
+ switch (reltype) {
+ case ELF_RELOC_RELA:
+ a = reldata;
+ if (relbase + a->r_offset >= dataaddr &&
+ relbase + a->r_offset < dataaddr + len) {
+ switch (ELF_R_TYPE(a->r_info)) {
+ case R_SPARC_RELATIVE:
+ w = relbase + a->r_addend;
+ bcopy(&w, (u_char *)data + (relbase +
+ a->r_offset - dataaddr), sizeof(w));
+ break;
+ default:
+ printf("\nunhandled relocation type %u\n",
+ (u_int)ELF_R_TYPE(a->r_info));
+ return (EFTYPE);
+ }
+ }
+ break;
+ }
+
+ return (0);
+#elif (defined(__i386__) || defined(__amd64__)) && __ELF_WORD_SIZE == 64
+ Elf64_Addr *where, val;
+ Elf_Addr addend, addr;
+ Elf_Size rtype, symidx;
+ const Elf_Rel *rel;
+ const Elf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_RELOC_REL:
+ rel = (const Elf_Rel *)reldata;
+ where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
+ dataaddr);
+ addend = 0;
+ rtype = ELF_R_TYPE(rel->r_info);
+ symidx = ELF_R_SYM(rel->r_info);
+ addend = 0;
+ break;
+ case ELF_RELOC_RELA:
+ rela = (const Elf_Rela *)reldata;
+ where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
+ dataaddr);
+ addend = rela->r_addend;
+ rtype = ELF_R_TYPE(rela->r_info);
+ symidx = ELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
+ return (0);
+
+ if (reltype == ELF_RELOC_REL)
+ addend = *where;
+
+/* XXX, definitions not available on i386. */
+#define R_X86_64_64 1
+#define R_X86_64_RELATIVE 8
+#define R_X86_64_IRELATIVE 37
+
+ switch (rtype) {
+ case R_X86_64_64: /* S + A */
+ addr = symaddr(ef, symidx);
+ if (addr == 0)
+ return (ESRCH);
+ val = addr + addend;
+ *where = val;
+ break;
+ case R_X86_64_RELATIVE:
+ addr = (Elf_Addr)addend + relbase;
+ val = addr;
+ *where = val;
+ break;
+ case R_X86_64_IRELATIVE:
+ /* leave it to kernel */
+ break;
+ default:
+ printf("\nunhandled relocation type %u\n", (u_int)rtype);
+ return (EFTYPE);
+ }
+
+ return (0);
+#elif defined(__i386__) && __ELF_WORD_SIZE == 32
+ Elf_Addr addend, addr, *where, val;
+ Elf_Size rtype, symidx;
+ const Elf_Rel *rel;
+ const Elf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_RELOC_REL:
+ rel = (const Elf_Rel *)reldata;
+ where = (Elf_Addr *)((char *)data + relbase + rel->r_offset -
+ dataaddr);
+ addend = 0;
+ rtype = ELF_R_TYPE(rel->r_info);
+ symidx = ELF_R_SYM(rel->r_info);
+ addend = 0;
+ break;
+ case ELF_RELOC_RELA:
+ rela = (const Elf_Rela *)reldata;
+ where = (Elf_Addr *)((char *)data + relbase + rela->r_offset -
+ dataaddr);
+ addend = rela->r_addend;
+ rtype = ELF_R_TYPE(rela->r_info);
+ symidx = ELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if ((char *)where < (char *)data || (char *)where >= (char *)data + len)
+ return (0);
+
+ if (reltype == ELF_RELOC_REL)
+ addend = *where;
+
+/* XXX, definitions not available on amd64. */
+#define R_386_32 1 /* Add symbol value. */
+#define R_386_GLOB_DAT 6 /* Set GOT entry to data address. */
+#define R_386_RELATIVE 8 /* Add load address of shared object. */
+#define R_386_IRELATIVE 42
+
+ switch (rtype) {
+ case R_386_RELATIVE:
+ addr = addend + relbase;
+ *where = addr;
+ break;
+ case R_386_32: /* S + A */
+ addr = symaddr(ef, symidx);
+ if (addr == 0)
+ return (ESRCH);
+ val = addr + addend;
+ *where = val;
+ break;
+ case R_386_IRELATIVE:
+ /* leave it to kernel */
+ break;
+ default:
+ printf("\nunhandled relocation type %u\n", (u_int)rtype);
+ return (EFTYPE);
+ }
+
+ return (0);
+#elif defined(__powerpc__)
+ Elf_Size w;
+ const Elf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_RELOC_RELA:
+ rela = reldata;
+ if (relbase + rela->r_offset >= dataaddr &&
+ relbase + rela->r_offset < dataaddr + len) {
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_PPC_RELATIVE:
+ w = relbase + rela->r_addend;
+ bcopy(&w, (u_char *)data + (relbase +
+ rela->r_offset - dataaddr), sizeof(w));
+ break;
+ default:
+ printf("\nunhandled relocation type %u\n",
+ (u_int)ELF_R_TYPE(rela->r_info));
+ return (EFTYPE);
+ }
+ }
+ break;
+ }
+
+ return (0);
+#else
+ return (EOPNOTSUPP);
+#endif
+}
diff --git a/stand/common/reloc_elf32.c b/stand/common/reloc_elf32.c
new file mode 100644
index 0000000..03d9d73
--- /dev/null
+++ b/stand/common/reloc_elf32.c
@@ -0,0 +1,6 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 32
+
+#include "reloc_elf.c"
diff --git a/stand/common/reloc_elf64.c b/stand/common/reloc_elf64.c
new file mode 100644
index 0000000..c8dcf2a
--- /dev/null
+++ b/stand/common/reloc_elf64.c
@@ -0,0 +1,6 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+
+#include "reloc_elf.c"
diff --git a/stand/common/self_reloc.c b/stand/common/self_reloc.c
new file mode 100644
index 0000000..5f6bfcb
--- /dev/null
+++ b/stand/common/self_reloc.c
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 2008-2010 Rui Paulo <rpaulo@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <elf.h>
+#include <bootstrap.h>
+
+#if defined(__aarch64__) || defined(__amd64__)
+#define ElfW_Rel Elf64_Rela
+#define ElfW_Dyn Elf64_Dyn
+#define ELFW_R_TYPE ELF64_R_TYPE
+#define ELF_RELA
+#elif defined(__arm__) || defined(__i386__)
+#define ElfW_Rel Elf32_Rel
+#define ElfW_Dyn Elf32_Dyn
+#define ELFW_R_TYPE ELF32_R_TYPE
+#else
+#error architecture not supported
+#endif
+#if defined(__aarch64__)
+#define RELOC_TYPE_NONE R_AARCH64_NONE
+#define RELOC_TYPE_RELATIVE R_AARCH64_RELATIVE
+#elif defined(__amd64__)
+#define RELOC_TYPE_NONE R_X86_64_NONE
+#define RELOC_TYPE_RELATIVE R_X86_64_RELATIVE
+#elif defined(__arm__)
+#define RELOC_TYPE_NONE R_ARM_NONE
+#define RELOC_TYPE_RELATIVE R_ARM_RELATIVE
+#elif defined(__i386__)
+#define RELOC_TYPE_NONE R_386_NONE
+#define RELOC_TYPE_RELATIVE R_386_RELATIVE
+#endif
+
+void self_reloc(Elf_Addr baseaddr, ElfW_Dyn *dynamic);
+
+/*
+ * A simple elf relocator.
+ */
+void
+self_reloc(Elf_Addr baseaddr, ElfW_Dyn *dynamic)
+{
+ Elf_Word relsz, relent;
+ Elf_Addr *newaddr;
+ ElfW_Rel *rel;
+ ElfW_Dyn *dynp;
+
+ /*
+ * Find the relocation address, its size and the relocation entry.
+ */
+ relsz = 0;
+ relent = 0;
+ for (dynp = dynamic; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_REL:
+ case DT_RELA:
+ rel = (ElfW_Rel *)(dynp->d_un.d_ptr + baseaddr);
+ break;
+ case DT_RELSZ:
+ case DT_RELASZ:
+ relsz = dynp->d_un.d_val;
+ break;
+ case DT_RELENT:
+ case DT_RELAENT:
+ relent = dynp->d_un.d_val;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Perform the actual relocation. We rely on the object having been
+ * linked at 0, so that the difference between the load and link
+ * address is the same as the load address.
+ */
+ for (; relsz > 0; relsz -= relent) {
+ switch (ELFW_R_TYPE(rel->r_info)) {
+ case RELOC_TYPE_NONE:
+ /* No relocation needs be performed. */
+ break;
+
+ case RELOC_TYPE_RELATIVE:
+ newaddr = (Elf_Addr *)(rel->r_offset + baseaddr);
+#ifdef ELF_RELA
+ /* Addend relative to the base address. */
+ *newaddr = baseaddr + rel->r_addend;
+#else
+ /* Address relative to the base address. */
+ *newaddr += baseaddr;
+#endif
+ break;
+ default:
+ /* XXX: do we need other relocations ? */
+ break;
+ }
+ rel = (ElfW_Rel *)(void *)((caddr_t) rel + relent);
+ }
+}
diff --git a/stand/defs.mk b/stand/defs.mk
new file mode 100644
index 0000000..20e9a77
--- /dev/null
+++ b/stand/defs.mk
@@ -0,0 +1,171 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+.if !defined(__BOOT_DEFS_MK__)
+__BOOT_DEFS_MK__=${MFILE}
+
+BOOTSRC= ${SRCTOP}/stand
+EFISRC= ${BOOTSRC}/efi
+EFIINC= ${EFISRC}/include
+EFIINCMD= ${EFIINC}/${MACHINE}
+FDTSRC= ${BOOTSRC}/fdt
+FICLSRC= ${BOOTSRC}/ficl
+LDRSRC= ${BOOTSRC}/common
+SASRC= ${BOOTSRC}/libsa
+SYSDIR= ${SRCTOP}/sys
+UBOOTSRC= ${BOOTSRC}/uboot
+ZFSSRC= ${BOOTSRC}/zfs
+
+BOOTOBJ= ${OBJTOP}/stand
+
+# BINDIR is where we install
+BINDIR?= /boot
+
+# NB: The makefiles depend on these being empty when we don't build forth.
+.if ${MK_FORTH} != "no"
+LIBFICL= ${BOOTOBJ}/ficl/libficl.a
+.if ${MACHINE} == "i386"
+LIBFICL32= ${LIBFICL}
+.else
+LIBFICL32= ${BOOTOBJ}/ficl32/libficl.a
+.endif
+.endif
+LIBSA= ${BOOTOBJ}/libsa/libsa.a
+.if ${MACHINE} == "i386"
+LIBSA32= ${LIBSA}
+.else
+LIBSA32= ${BOOTOBJ}/libsa32/libsa32.a
+.endif
+
+# Standard options:
+
+# Filesystem support
+.if ${LOADER_CD9660_SUPPORT:Uno} == "yes"
+CFLAGS+= -DLOADER_CD9660_SUPPORT
+.endif
+.if ${LOADER_EXT2FS_SUPPORT:Uno} == "yes"
+CFLAGS+= -DLOADER_EXT2FS_SUPPORT
+.endif
+.if ${LOADER_MSDOS_SUPPORT:Uno} == "yes"
+CFLAGS+= -DLOADER_MSDOS_SUPPORT
+.endif
+.if ${LOADER_NANDFS_SUPPORT:U${MK_NAND}} == "yes"
+CFLAGS+= -DLOADER_NANDFS_SUPPORT
+.endif
+.if ${LOADER_UFS_SUPPORT:Uyes} == "yes"
+CFLAGS+= -DLOADER_UFS_SUPPORT
+.endif
+
+# Compression
+.if ${LOADER_GZIP_SUPPORT:Uno} == "yes"
+CFLAGS+= -DLOADER_GZIP_SUPPORT
+.endif
+.if ${LOADER_BZIP2_SUPPORT:Uno} == "yes"
+CFLAGS+= -DLOADER_BZIP2_SUPPORT
+.endif
+
+# Network related things
+.if ${LOADER_NET_SUPPORT:Uno} == "yes"
+CFLAGS+= -DLOADER_NET_SUPPORT
+.endif
+.if ${LOADER_NFS_SUPPORT:Uno} == "yes"
+CFLAGS+= -DLOADER_NFS_SUPPORT
+.endif
+.if ${LOADER_TFTP_SUPPORT:Uno} == "yes"
+CFLAGS+= -DLOADER_TFTP_SUPPORT
+.endif
+
+# Disk and partition support
+.if ${LOADER_DISK_SUPPORT:Uyes} == "yes"
+CFLAGS+= -DLOADER_DISK_SUPPORT
+.if ${LOADER_GPT_SUPPORT:Uyes} == "yes"
+CFLAGS+= -DLOADER_GPT_SUPPORT
+.endif
+.if ${LOADER_MBR_SUPPORT:Uyes} == "yes"
+CFLAGS+= -DLOADER_MBR_SUPPORT
+.endif
+
+# GELI Support, with backward compat hooks
+.if defined(HAVE_GELI)
+.if defined(LOADER_NO_GELI_SUPPORT)
+MK_LOADER_GELI=no
+.warning "Please move from LOADER_NO_GELI_SUPPORT to WITHOUT_LOADER_GELI"
+.endif
+.if defined(LOADER_GELI_SUPPORT)
+MK_LOADER_GELI=yes
+.warning "Please move from LOADER_GELI_SUPPORT to WITH_LOADER_GELI"
+.endif
+.if ${MK_LOADER_GELI} == "yes"
+CFLAGS+= -DLOADER_GELI_SUPPORT
+CFLAGS+= -I${BOOTSRC}/geli
+LIBGELIBOOT= ${BOOTOBJ}/geli/libgeliboot.a
+.endif
+.endif
+.endif
+
+CFLAGS+= -I${SYSDIR}
+
+# All PowerPC builds are 32 bit. We have no 64-bit loaders on powerpc
+# or powerpc64.
+.if ${MACHINE_ARCH} == "powerpc64"
+CFLAGS+= -m32 -mcpu=powerpc
+.endif
+
+# For amd64, there's a bit of mixed bag. Some of the tree (i386, lib*32) is
+# build 32-bit and some 64-bit (lib*, efi). Centralize all the 32-bit magic here
+# and activate it when DO32 is explicitly defined to be 1.
+.if ${MACHINE_ARCH} == "amd64" && ${DO32:U0} == 1
+CFLAGS+= -m32 -mcpu=i386
+# LD_FLAGS is passed directly to ${LD}, not via ${CC}:
+LD_FLAGS+= -m elf_i386_fbsd
+AFLAGS+= --32
+.endif
+
+# Make sure we use the machine link we're about to create
+CFLAGS+=-I.
+
+_ILINKS=machine
+.if ${MACHINE} != ${MACHINE_CPUARCH} && ${MACHINE} != "arm64"
+_ILINKS+=${MACHINE_CPUARCH}
+.endif
+.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64"
+_ILINKS+=x86
+.endif
+CLEANFILES+=${_ILINKS}
+
+all: ${PROG}
+
+beforedepend: ${_ILINKS}
+beforebuild: ${_ILINKS}
+
+# Ensure that the links exist without depending on it when it exists which
+# causes all the modules to be rebuilt when the directory pointed to changes.
+.for _link in ${_ILINKS}
+.if !exists(${.OBJDIR}/${_link})
+${OBJS}: ${_link}
+.endif
+.endfor
+
+.NOPATH: ${_ILINKS}
+
+${_ILINKS}:
+ @case ${.TARGET} in \
+ machine) \
+ if [ ${DO32:U0} -eq 0 ]; then \
+ path=${SYSDIR}/${MACHINE}/include ; \
+ else \
+ path=${SYSDIR}/${MACHINE:C/amd64/i386/}/include ; \
+ fi ;; \
+ *) \
+ path=${SYSDIR}/${.TARGET:T}/include ;; \
+ esac ; \
+ path=`(cd $$path && /bin/pwd)` ; \
+ ${ECHO} ${.TARGET:T} "->" $$path ; \
+ ln -fhs $$path ${.TARGET:T}
+
+# For loader implementations, we generate a loader.help file. This can be suppressed by
+# setting HELP_FILES to nothing.
+HELP_FILES= ${LDRSRC}/help.common
+
+.endif # __BOOT_DEFS_MK__
diff --git a/stand/efi/Makefile b/stand/efi/Makefile
new file mode 100644
index 0000000..e3b22de
--- /dev/null
+++ b/stand/efi/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+# In-tree GCC does not support __attribute__((ms_abi)), but gcc newer
+# than 4.5 supports it.
+.if ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 40500
+
+.if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "arm"
+.if ${MK_FDT} != "no"
+SUBDIR+= fdt
+.endif
+.endif
+
+.if ${MACHINE_CPUARCH} == "aarch64" || \
+ ${MACHINE_CPUARCH} == "amd64" || \
+ ${MACHINE_CPUARCH} == "arm"
+SUBDIR+= libefi loader boot1
+.endif
+
+.endif # ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 40500
+
+.include <bsd.subdir.mk>
diff --git a/stand/efi/Makefile.inc b/stand/efi/Makefile.inc
new file mode 100644
index 0000000..4fd8762
--- /dev/null
+++ b/stand/efi/Makefile.inc
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+.if ${MACHINE_CPUARCH} == "i386"
+CFLAGS+= -march=i386
+CFLAGS+= -mno-aes
+.endif
+
+# Options used when building app-specific efi components
+# See conf/kern.mk for the correct set of these
+CFLAGS+= -ffreestanding -Wformat ${CFLAGS_NO_SIMD}
+LDFLAGS+= -nostdlib
+
+.if ${MACHINE_CPUARCH} != "aarch64"
+CFLAGS+= -msoft-float
+.endif
+
+.if ${MACHINE_CPUARCH} == "amd64"
+CFLAGS+= -fshort-wchar
+CFLAGS+= -mno-red-zone
+CFLAGS+= -mno-aes
+.endif
+
+.if ${MACHINE_CPUARCH} == "aarch64"
+CFLAGS+= -fshort-wchar
+CFLAGS+= -fPIC
+.endif
+
+.if ${MACHINE_CPUARCH} == "arm"
+CFLAGS+= -fPIC
+.endif
+
+.include "../Makefile.inc"
diff --git a/stand/efi/boot1/Makefile b/stand/efi/boot1/Makefile
new file mode 100644
index 0000000..f2e6946
--- /dev/null
+++ b/stand/efi/boot1/Makefile
@@ -0,0 +1,129 @@
+# $FreeBSD$
+
+MAN=
+
+.include <bsd.init.mk>
+
+MK_SSP= no
+MK_FORTH= no
+
+PROG= boot1.sym
+INTERNALPROG=
+WARNS?= 6
+
+# We implement a slightly non-standard %S in that it always takes a
+# CHAR16 that's common in UEFI-land instead of a wchar_t. This only
+# seems to matter on arm64 where wchar_t defaults to an int instead
+# of a short. There's no good cast to use here so just ignore the
+# warnings for now.
+CWARNFLAGS.boot1.c+= -Wno-format
+
+# Disable warnings that are currently incompatible with the zfs boot code
+CWARNFLAGS.zfs_module.c += -Wno-array-bounds
+CWARNFLAGS.zfs_module.c += -Wno-cast-align
+CWARNFLAGS.zfs_module.c += -Wno-cast-qual
+CWARNFLAGS.zfs_module.c += -Wno-missing-prototypes
+CWARNFLAGS.zfs_module.c += -Wno-sign-compare
+CWARNFLAGS.zfs_module.c += -Wno-unused-parameter
+CWARNFLAGS.zfs_module.c += -Wno-unused-function
+
+# architecture-specific loader code
+SRCS= boot1.c self_reloc.c start.S ufs_module.c
+.if ${MK_ZFS} != "no"
+SRCS+= zfs_module.c
+CFLAGS+= -I${ZFSSRC}
+CFLAGS+= -I${SYSDIR}/cddl/boot/zfs
+CFLAGS+= -DEFI_ZFS_BOOT
+LIBZFSBOOT= ${BOOTOBJ}/zfs/libzfsboot.a
+.endif
+
+.if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} > 40201
+CWARNFLAGS.self_reloc.c+= -Wno-error=maybe-uninitialized
+.endif
+
+CFLAGS+= -I${EFIINC}
+CFLAGS+= -I${EFIINCMD}
+CFLAGS+= -I${SYSDIR}/contrib/dev/acpica/include
+CFLAGS+= -DEFI_UFS_BOOT
+.ifdef(EFI_DEBUG)
+CFLAGS+= -DEFI_DEBUG
+.endif
+
+# Always add MI sources and REGULAR efi loader bits
+.PATH: ${EFISRC}/loader/arch/${MACHINE}
+.PATH: ${EFISRC}/loader
+.PATH: ${LDRSRC}
+CFLAGS+= -I${LDRSRC}
+
+FILES= boot1.efi boot1.efifat
+FILESMODE_boot1.efi= ${BINMODE}
+
+LDSCRIPT= ${EFISRC}/loader/arch/${MACHINE}/ldscript.${MACHINE}
+LDFLAGS+= -Wl,-T${LDSCRIPT},-Bsymbolic,-znotext -shared
+
+.if ${MACHINE_CPUARCH} == "aarch64"
+CFLAGS+= -mgeneral-regs-only
+.endif
+.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386"
+CFLAGS+= -fPIC
+LDFLAGS+= -Wl,-znocombreloc
+.endif
+
+LIBEFI= ${BOOTOBJ}/efi/libefi/libefi.a
+
+#
+# Add libstand for the runtime functions used by the compiler - for example
+# __aeabi_* (arm) or __divdi3 (i386).
+# as well as required string and memory functions for all platforms.
+#
+DPADD+= ${LIBEFI} ${LIBZFSBOOT} ${LIBSA}
+LDADD+= ${LIBEFI} ${LIBZFSBOOT} ${LIBSA}
+
+DPADD+= ${LDSCRIPT}
+
+NM?= nm
+OBJCOPY?= objcopy
+
+.if ${MACHINE_CPUARCH} == "amd64"
+EFI_TARGET= efi-app-x86_64
+.elif ${MACHINE_CPUARCH} == "i386"
+EFI_TARGET= efi-app-ia32
+.else
+EFI_TARGET= binary
+.endif
+
+# Arbitrarily set the PE/COFF header timestamps to 1 Jan 2016 00:00:00
+# for build reproducibility.
+SOURCE_DATE_EPOCH?=1451606400
+boot1.efi: ${PROG}
+ if ${NM} ${.ALLSRC} | grep ' U '; then \
+ echo "Undefined symbols in ${.ALLSRC}"; \
+ exit 1; \
+ fi
+ SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \
+ ${OBJCOPY} -j .peheader -j .text -j .sdata -j .data \
+ -j .dynamic -j .dynsym -j .rel.dyn \
+ -j .rela.dyn -j .reloc -j .eh_frame \
+ --output-target=${EFI_TARGET} ${.ALLSRC} ${.TARGET}
+
+boot1.o: ${SASRC}/ufsread.c
+
+# The following inserts our objects into a template FAT file system
+# created by generate-fat.sh
+
+.include "${.CURDIR}/Makefile.fat"
+
+boot1.efifat: boot1.efi
+ @set -- `ls -l ${.ALLSRC}`; \
+ x=$$(($$5-${BOOT1_MAXSIZE})); \
+ if [ $$x -ge 0 ]; then \
+ echo "boot1 $$x bytes too large; regenerate FAT templates?" >&2 ;\
+ exit 1; \
+ fi
+ echo ${.OBJDIR}
+ xz -d -c ${.CURDIR}/fat-${MACHINE}.tmpl.xz > ${.TARGET}
+ ${DD} if=${.ALLSRC} of=${.TARGET} seek=${BOOT1_OFFSET} conv=notrunc
+
+CLEANFILES+= boot1.efi boot1.efifat
+
+.include <bsd.prog.mk>
diff --git a/stand/efi/boot1/Makefile.depend b/stand/efi/boot1/Makefile.depend
new file mode 100644
index 0000000..2984414
--- /dev/null
+++ b/stand/efi/boot1/Makefile.depend
@@ -0,0 +1,14 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libstand \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/efi/boot1/Makefile.fat b/stand/efi/boot1/Makefile.fat
new file mode 100644
index 0000000..1d40fa8
--- /dev/null
+++ b/stand/efi/boot1/Makefile.fat
@@ -0,0 +1,4 @@
+# This file autogenerated by generate-fat.sh - DO NOT EDIT
+# $FreeBSD$
+BOOT1_OFFSET=0x2d
+BOOT1_MAXSIZE=393216
diff --git a/stand/efi/boot1/boot1.c b/stand/efi/boot1/boot1.c
new file mode 100644
index 0000000..b7cb57f
--- /dev/null
+++ b/stand/efi/boot1/boot1.c
@@ -0,0 +1,583 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ * Copyright (c) 2001 Robert Drehmel
+ * All rights reserved.
+ * Copyright (c) 2014 Nathan Whitehorn
+ * All rights reserved.
+ * Copyright (c) 2015 Eric McCorkle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <machine/elf.h>
+#include <machine/stdarg.h>
+#include <stand.h>
+
+#include <efi.h>
+#include <eficonsctl.h>
+typedef CHAR16 efi_char;
+#include <efichar.h>
+
+#include "boot_module.h"
+#include "paths.h"
+
+static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3);
+
+static const boot_module_t *boot_modules[] =
+{
+#ifdef EFI_ZFS_BOOT
+ &zfs_module,
+#endif
+#ifdef EFI_UFS_BOOT
+ &ufs_module
+#endif
+};
+
+#define NUM_BOOT_MODULES nitems(boot_modules)
+/* The initial number of handles used to query EFI for partitions. */
+#define NUM_HANDLES_INIT 24
+
+static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
+static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
+static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
+static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
+static EFI_GUID FreeBSDBootVarGUID = FREEBSD_BOOT_VAR_GUID;
+
+/*
+ * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures
+ * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from
+ * EFI methods.
+ */
+void *
+Malloc(size_t len, const char *file __unused, int line __unused)
+{
+ void *out;
+
+ if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS)
+ return (out);
+
+ return (NULL);
+}
+
+void
+Free(void *buf, const char *file __unused, int line __unused)
+{
+ if (buf != NULL)
+ (void)BS->FreePool(buf);
+}
+
+static EFI_STATUS
+efi_setenv_freebsd_wcs(const char *varname, CHAR16 *valstr)
+{
+ CHAR16 *var = NULL;
+ size_t len;
+ EFI_STATUS rv;
+
+ utf8_to_ucs2(varname, &var, &len);
+ if (var == NULL)
+ return (EFI_OUT_OF_RESOURCES);
+ rv = RS->SetVariable(var, &FreeBSDBootVarGUID,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ (ucs2len(valstr) + 1) * sizeof(efi_char), valstr);
+ free(var);
+ return (rv);
+}
+
+/*
+ * nodes_match returns TRUE if the imgpath isn't NULL and the nodes match,
+ * FALSE otherwise.
+ */
+static BOOLEAN
+nodes_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath)
+{
+ size_t len;
+
+ if (imgpath == NULL || imgpath->Type != devpath->Type ||
+ imgpath->SubType != devpath->SubType)
+ return (FALSE);
+
+ len = DevicePathNodeLength(imgpath);
+ if (len != DevicePathNodeLength(devpath))
+ return (FALSE);
+
+ return (memcmp(imgpath, devpath, (size_t)len) == 0);
+}
+
+/*
+ * device_paths_match returns TRUE if the imgpath isn't NULL and all nodes
+ * in imgpath and devpath match up to their respective occurrences of a
+ * media node, FALSE otherwise.
+ */
+static BOOLEAN
+device_paths_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath)
+{
+
+ if (imgpath == NULL)
+ return (FALSE);
+
+ while (!IsDevicePathEnd(imgpath) && !IsDevicePathEnd(devpath)) {
+ if (IsDevicePathType(imgpath, MEDIA_DEVICE_PATH) &&
+ IsDevicePathType(devpath, MEDIA_DEVICE_PATH))
+ return (TRUE);
+
+ if (!nodes_match(imgpath, devpath))
+ return (FALSE);
+
+ imgpath = NextDevicePathNode(imgpath);
+ devpath = NextDevicePathNode(devpath);
+ }
+
+ return (FALSE);
+}
+
+/*
+ * devpath_last returns the last non-path end node in devpath.
+ */
+static EFI_DEVICE_PATH *
+devpath_last(EFI_DEVICE_PATH *devpath)
+{
+
+ while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
+ devpath = NextDevicePathNode(devpath);
+
+ return (devpath);
+}
+
+/*
+ * load_loader attempts to load the loader image data.
+ *
+ * It tries each module and its respective devices, identified by mod->probe,
+ * in order until a successful load occurs at which point it returns EFI_SUCCESS
+ * and EFI_NOT_FOUND otherwise.
+ *
+ * Only devices which have preferred matching the preferred parameter are tried.
+ */
+static EFI_STATUS
+load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp,
+ size_t *bufsize, BOOLEAN preferred)
+{
+ UINTN i;
+ dev_info_t *dev;
+ const boot_module_t *mod;
+
+ for (i = 0; i < NUM_BOOT_MODULES; i++) {
+ mod = boot_modules[i];
+ for (dev = mod->devices(); dev != NULL; dev = dev->next) {
+ if (dev->preferred != preferred)
+ continue;
+
+ if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) ==
+ EFI_SUCCESS) {
+ *devinfop = dev;
+ *modp = mod;
+ return (EFI_SUCCESS);
+ }
+ }
+ }
+
+ return (EFI_NOT_FOUND);
+}
+
+/*
+ * try_boot only returns if it fails to load the loader. If it succeeds
+ * it simply boots, otherwise it returns the status of last EFI call.
+ */
+static EFI_STATUS
+try_boot(void)
+{
+ size_t bufsize, loadersize, cmdsize;
+ void *buf, *loaderbuf;
+ char *cmd;
+ dev_info_t *dev;
+ const boot_module_t *mod;
+ EFI_HANDLE loaderhandle;
+ EFI_LOADED_IMAGE *loaded_image;
+ EFI_STATUS status;
+
+ status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE);
+ if (status != EFI_SUCCESS) {
+ status = load_loader(&mod, &dev, &loaderbuf, &loadersize,
+ FALSE);
+ if (status != EFI_SUCCESS) {
+ printf("Failed to load '%s'\n", PATH_LOADER_EFI);
+ return (status);
+ }
+ }
+
+ /*
+ * Read in and parse the command line from /boot.config or /boot/config,
+ * if present. We'll pass it the next stage via a simple ASCII
+ * string. loader.efi has a hack for ASCII strings, so we'll use that to
+ * keep the size down here. We only try to read the alternate file if
+ * we get EFI_NOT_FOUND because all other errors mean that the boot_module
+ * had troubles with the filesystem. We could return early, but we'll let
+ * loading the actual kernel sort all that out. Since these files are
+ * optional, we don't report errors in trying to read them.
+ */
+ cmd = NULL;
+ cmdsize = 0;
+ status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize);
+ if (status == EFI_NOT_FOUND)
+ status = mod->load(PATH_CONFIG, dev, &buf, &bufsize);
+ if (status == EFI_SUCCESS) {
+ cmdsize = bufsize + 1;
+ cmd = malloc(cmdsize);
+ if (cmd == NULL)
+ goto errout;
+ memcpy(cmd, buf, bufsize);
+ cmd[bufsize] = '\0';
+ free(buf);
+ buf = NULL;
+ }
+
+ if ((status = BS->LoadImage(TRUE, IH, devpath_last(dev->devpath),
+ loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) {
+ printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
+ mod->name, loadersize, EFI_ERROR_CODE(status));
+ goto errout;
+ }
+
+ if ((status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
+ (VOID**)&loaded_image)) != EFI_SUCCESS) {
+ printf("Failed to query LoadedImage provided by %s (%lu)\n",
+ mod->name, EFI_ERROR_CODE(status));
+ goto errout;
+ }
+
+ if (cmd != NULL)
+ printf(" command args: %s\n", cmd);
+
+ loaded_image->DeviceHandle = dev->devhandle;
+ loaded_image->LoadOptionsSize = cmdsize;
+ loaded_image->LoadOptions = cmd;
+
+ DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI);
+ DSTALL(1000000);
+ DPRINTF(".");
+ DSTALL(1000000);
+ DPRINTF(".");
+ DSTALL(1000000);
+ DPRINTF(".");
+ DSTALL(1000000);
+ DPRINTF(".");
+ DSTALL(1000000);
+ DPRINTF(".\n");
+
+ if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
+ EFI_SUCCESS) {
+ printf("Failed to start image provided by %s (%lu)\n",
+ mod->name, EFI_ERROR_CODE(status));
+ loaded_image->LoadOptionsSize = 0;
+ loaded_image->LoadOptions = NULL;
+ }
+
+errout:
+ if (cmd != NULL)
+ free(cmd);
+ if (buf != NULL)
+ free(buf);
+ if (loaderbuf != NULL)
+ free(loaderbuf);
+
+ return (status);
+}
+
+/*
+ * probe_handle determines if the passed handle represents a logical partition
+ * if it does it uses each module in order to probe it and if successful it
+ * returns EFI_SUCCESS.
+ */
+static EFI_STATUS
+probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN *preferred)
+{
+ dev_info_t *devinfo;
+ EFI_BLOCK_IO *blkio;
+ EFI_DEVICE_PATH *devpath;
+ EFI_STATUS status;
+ UINTN i;
+
+ /* Figure out if we're dealing with an actual partition. */
+ status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath);
+ if (status == EFI_UNSUPPORTED)
+ return (status);
+
+ if (status != EFI_SUCCESS) {
+ DPRINTF("\nFailed to query DevicePath (%lu)\n",
+ EFI_ERROR_CODE(status));
+ return (status);
+ }
+#ifdef EFI_DEBUG
+ {
+ CHAR16 *text = efi_devpath_name(devpath);
+ DPRINTF("probing: %S\n", text);
+ efi_free_devpath_name(text);
+ }
+#endif
+ status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio);
+ if (status == EFI_UNSUPPORTED)
+ return (status);
+
+ if (status != EFI_SUCCESS) {
+ DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
+ EFI_ERROR_CODE(status));
+ return (status);
+ }
+
+ if (!blkio->Media->LogicalPartition)
+ return (EFI_UNSUPPORTED);
+
+ *preferred = device_paths_match(imgpath, devpath);
+
+ /* Run through each module, see if it can load this partition */
+ for (i = 0; i < NUM_BOOT_MODULES; i++) {
+ devinfo = malloc(sizeof(*devinfo));
+ if (devinfo == NULL) {
+ DPRINTF("\nFailed to allocate devinfo\n");
+ continue;
+ }
+ devinfo->dev = blkio;
+ devinfo->devpath = devpath;
+ devinfo->devhandle = h;
+ devinfo->devdata = NULL;
+ devinfo->preferred = *preferred;
+ devinfo->next = NULL;
+
+ status = boot_modules[i]->probe(devinfo);
+ if (status == EFI_SUCCESS)
+ return (EFI_SUCCESS);
+ free(devinfo);
+ }
+
+ return (EFI_UNSUPPORTED);
+}
+
+/*
+ * probe_handle_status calls probe_handle and outputs the returned status
+ * of the call.
+ */
+static void
+probe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath)
+{
+ EFI_STATUS status;
+ BOOLEAN preferred;
+
+ preferred = FALSE;
+ status = probe_handle(h, imgpath, &preferred);
+
+ DPRINTF("probe: ");
+ switch (status) {
+ case EFI_UNSUPPORTED:
+ printf(".");
+ DPRINTF(" not supported\n");
+ break;
+ case EFI_SUCCESS:
+ if (preferred) {
+ printf("%c", '*');
+ DPRINTF(" supported (preferred)\n");
+ } else {
+ printf("%c", '+');
+ DPRINTF(" supported\n");
+ }
+ break;
+ default:
+ printf("x");
+ DPRINTF(" error (%lu)\n", EFI_ERROR_CODE(status));
+ break;
+ }
+ DSTALL(500000);
+}
+
+EFI_STATUS
+efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
+{
+ EFI_HANDLE *handles;
+ EFI_LOADED_IMAGE *img;
+ EFI_DEVICE_PATH *imgpath;
+ EFI_STATUS status;
+ EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
+ SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
+ UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles;
+ CHAR16 *text;
+
+ /* Basic initialization*/
+ ST = Xsystab;
+ IH = Ximage;
+ BS = ST->BootServices;
+ RS = ST->RuntimeServices;
+
+ /* Set up the console, so printf works. */
+ status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
+ (VOID **)&ConsoleControl);
+ if (status == EFI_SUCCESS)
+ (void)ConsoleControl->SetMode(ConsoleControl,
+ EfiConsoleControlScreenText);
+ /*
+ * Reset the console and find the best text mode.
+ */
+ conout = ST->ConOut;
+ conout->Reset(conout, TRUE);
+ max_dim = best_mode = 0;
+ for (i = 0; ; i++) {
+ status = conout->QueryMode(conout, i, &cols, &rows);
+ if (EFI_ERROR(status))
+ break;
+ if (cols * rows > max_dim) {
+ max_dim = cols * rows;
+ best_mode = i;
+ }
+ }
+ if (max_dim > 0)
+ conout->SetMode(conout, best_mode);
+ conout->EnableCursor(conout, TRUE);
+ conout->ClearScreen(conout);
+
+ printf("\n>> FreeBSD EFI boot block\n");
+ printf(" Loader path: %s\n\n", PATH_LOADER_EFI);
+ printf(" Initializing modules:");
+ for (i = 0; i < NUM_BOOT_MODULES; i++) {
+ printf(" %s", boot_modules[i]->name);
+ if (boot_modules[i]->init != NULL)
+ boot_modules[i]->init();
+ }
+ putchar('\n');
+
+ /* Determine the devpath of our image so we can prefer it. */
+ status = BS->HandleProtocol(IH, &LoadedImageGUID, (VOID**)&img);
+ imgpath = NULL;
+ if (status == EFI_SUCCESS) {
+ text = efi_devpath_name(img->FilePath);
+ if (text != NULL) {
+ printf(" Load Path: %S\n", text);
+ efi_setenv_freebsd_wcs("Boot1Path", text);
+ efi_free_devpath_name(text);
+ }
+
+ status = BS->HandleProtocol(img->DeviceHandle, &DevicePathGUID,
+ (void **)&imgpath);
+ if (status != EFI_SUCCESS) {
+ DPRINTF("Failed to get image DevicePath (%lu)\n",
+ EFI_ERROR_CODE(status));
+ } else {
+ text = efi_devpath_name(imgpath);
+ if (text != NULL) {
+ printf(" Load Device: %S\n", text);
+ efi_setenv_freebsd_wcs("Boot1Dev", text);
+ efi_free_devpath_name(text);
+ }
+ }
+ }
+
+ /* Get all the device handles */
+ hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE);
+ handles = malloc(hsize);
+ if (handles == NULL) {
+ printf("Failed to allocate %d handles\n", NUM_HANDLES_INIT);
+ }
+
+ status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
+ &hsize, handles);
+ switch (status) {
+ case EFI_SUCCESS:
+ break;
+ case EFI_BUFFER_TOO_SMALL:
+ free(handles);
+ handles = malloc(hsize);
+ if (handles == NULL)
+ efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n",
+ NUM_HANDLES_INIT);
+ status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
+ NULL, &hsize, handles);
+ if (status != EFI_SUCCESS)
+ efi_panic(status, "Failed to get device handles\n");
+ break;
+ default:
+ efi_panic(status, "Failed to get device handles\n");
+ break;
+ }
+
+ /* Scan all partitions, probing with all modules. */
+ nhandles = hsize / sizeof(*handles);
+ printf(" Probing %zu block devices...", nhandles);
+ DPRINTF("\n");
+
+ for (i = 0; i < nhandles; i++)
+ probe_handle_status(handles[i], imgpath);
+ printf(" done\n");
+
+ /* Status summary. */
+ for (i = 0; i < NUM_BOOT_MODULES; i++) {
+ printf(" ");
+ boot_modules[i]->status();
+ }
+
+ try_boot();
+
+ /* If we get here, we're out of luck... */
+ efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!");
+}
+
+/*
+ * add_device adds a device to the passed devinfo list.
+ */
+void
+add_device(dev_info_t **devinfop, dev_info_t *devinfo)
+{
+ dev_info_t *dev;
+
+ if (*devinfop == NULL) {
+ *devinfop = devinfo;
+ return;
+ }
+
+ for (dev = *devinfop; dev->next != NULL; dev = dev->next)
+ ;
+
+ dev->next = devinfo;
+}
+
+/*
+ * OK. We totally give up. Exit back to EFI with a sensible status so
+ * it can try the next option on the list.
+ */
+static void
+efi_panic(EFI_STATUS s, const char *fmt, ...)
+{
+ va_list ap;
+
+ printf("panic: ");
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+
+ BS->Exit(IH, s, 0, NULL);
+}
+
+void
+putchar(int c)
+{
+ CHAR16 buf[2];
+
+ if (c == '\n') {
+ buf[0] = '\r';
+ buf[1] = 0;
+ ST->ConOut->OutputString(ST->ConOut, buf);
+ }
+ buf[0] = c;
+ buf[1] = 0;
+ ST->ConOut->OutputString(ST->ConOut, buf);
+}
diff --git a/stand/efi/boot1/boot_module.h b/stand/efi/boot1/boot_module.h
new file mode 100644
index 0000000..bfade34
--- /dev/null
+++ b/stand/efi/boot1/boot_module.h
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 2015 Eric McCorkle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOT_MODULE_H_
+#define _BOOT_MODULE_H_
+
+#include <stdbool.h>
+
+#include <efi.h>
+#include <efilib.h>
+#include <eficonsctl.h>
+
+#ifdef EFI_DEBUG
+#define DPRINTF(fmt, args...) printf(fmt, ##args)
+#define DSTALL(d) BS->Stall(d)
+#else
+#define DPRINTF(fmt, ...) {}
+#define DSTALL(d) {}
+#endif
+
+/* EFI device info */
+typedef struct dev_info
+{
+ EFI_BLOCK_IO *dev;
+ EFI_DEVICE_PATH *devpath;
+ EFI_HANDLE *devhandle;
+ void *devdata;
+ BOOLEAN preferred;
+ struct dev_info *next;
+} dev_info_t;
+
+/*
+ * A boot loader module.
+ *
+ * This is a standard interface for filesystem modules in the EFI system.
+ */
+typedef struct boot_module_t
+{
+ const char *name;
+
+ /* init is the optional initialiser for the module. */
+ void (*init)(void);
+
+ /*
+ * probe checks to see if the module can handle dev.
+ *
+ * Return codes:
+ * EFI_SUCCESS = The module can handle the device.
+ * EFI_NOT_FOUND = The module can not handle the device.
+ * Other = The module encountered an error.
+ */
+ EFI_STATUS (*probe)(dev_info_t* dev);
+
+ /*
+ * load should select the best out of a set of devices that probe
+ * indicated were loadable and load the specified file.
+ *
+ * Return codes:
+ * EFI_SUCCESS = The module can handle the device.
+ * EFI_NOT_FOUND = The module can not handle the device.
+ * Other = The module encountered an error.
+ */
+ EFI_STATUS (*load)(const char *filepath, dev_info_t *devinfo,
+ void **buf, size_t *bufsize);
+
+ /* status outputs information about the probed devices. */
+ void (*status)(void);
+
+ /* valid devices as found by probe. */
+ dev_info_t *(*devices)(void);
+} boot_module_t;
+
+/* Standard boot modules. */
+#ifdef EFI_UFS_BOOT
+extern const boot_module_t ufs_module;
+#endif
+#ifdef EFI_ZFS_BOOT
+extern const boot_module_t zfs_module;
+#endif
+
+/* Functions available to modules. */
+extern void add_device(dev_info_t **devinfop, dev_info_t *devinfo);
+extern int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap);
+#endif
diff --git a/stand/efi/boot1/fat-amd64.tmpl.xz b/stand/efi/boot1/fat-amd64.tmpl.xz
new file mode 100644
index 0000000..fb5f94e
--- /dev/null
+++ b/stand/efi/boot1/fat-amd64.tmpl.xz
Binary files differ
diff --git a/stand/efi/boot1/fat-arm.tmpl.xz b/stand/efi/boot1/fat-arm.tmpl.xz
new file mode 100644
index 0000000..bb253fc
--- /dev/null
+++ b/stand/efi/boot1/fat-arm.tmpl.xz
Binary files differ
diff --git a/stand/efi/boot1/fat-arm64.tmpl.xz b/stand/efi/boot1/fat-arm64.tmpl.xz
new file mode 100644
index 0000000..15df643
--- /dev/null
+++ b/stand/efi/boot1/fat-arm64.tmpl.xz
Binary files differ
diff --git a/stand/efi/boot1/fat-i386.tmpl.xz b/stand/efi/boot1/fat-i386.tmpl.xz
new file mode 100644
index 0000000..2cde337
--- /dev/null
+++ b/stand/efi/boot1/fat-i386.tmpl.xz
Binary files differ
diff --git a/stand/efi/boot1/generate-fat.sh b/stand/efi/boot1/generate-fat.sh
new file mode 100755
index 0000000..f6bda6f
--- /dev/null
+++ b/stand/efi/boot1/generate-fat.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+# This script generates the dummy FAT filesystem used for the EFI boot
+# blocks. It uses newfs_msdos to generate a template filesystem with the
+# relevant interesting files. These are then found by grep, and the offsets
+# written to a Makefile snippet.
+#
+# Because it requires root, and because it is overkill, we do not
+# do this as part of the normal build. If makefs(8) grows workable FAT
+# support, this should be revisited.
+
+# $FreeBSD$
+
+FAT_SIZE=1600 #Size in 512-byte blocks of the produced image
+
+BOOT1_OFFSET=2d
+BOOT1_SIZE=384k
+
+if [ $(id -u) != 0 ]; then
+ echo "${0##*/}: must run as root" >&2
+ exit 1
+fi
+
+# Record maximum boot1 size in bytes
+case $BOOT1_SIZE in
+*k)
+ BOOT1_MAXSIZE=$(expr ${BOOT1_SIZE%k} '*' 1024)
+ ;;
+*)
+ BOOT1_MAXSIZE=$BOOT1_SIZE
+ ;;
+esac
+
+echo '# This file autogenerated by generate-fat.sh - DO NOT EDIT' > Makefile.fat
+echo "# \$FreeBSD\$" >> Makefile.fat
+echo "BOOT1_OFFSET=0x$BOOT1_OFFSET" >> Makefile.fat
+echo "BOOT1_MAXSIZE=$BOOT1_MAXSIZE" >> Makefile.fat
+
+while read ARCH FILENAME; do
+ # Generate 800K FAT image
+ OUTPUT_FILE=fat-${ARCH}.tmpl
+
+ dd if=/dev/zero of=$OUTPUT_FILE bs=512 count=$FAT_SIZE
+ DEVICE=`mdconfig -a -f $OUTPUT_FILE`
+ newfs_msdos -F 12 -L EFI $DEVICE
+ mkdir stub
+ mount -t msdosfs /dev/$DEVICE stub
+
+ # Create and bless a directory for the boot loader
+ mkdir -p stub/efi/boot
+
+ # Make a dummy file for boot1
+ echo 'Boot1 START' | dd of=stub/efi/boot/$FILENAME cbs=$BOOT1_SIZE count=1 conv=block
+ # Provide a fallback startup.nsh
+ echo $FILENAME > stub/efi/boot/startup.nsh
+
+ umount stub
+ mdconfig -d -u $DEVICE
+ rmdir stub
+
+ # Locate the offset of the fake file
+ OFFSET=$(hd $OUTPUT_FILE | grep 'Boot1 START' | cut -f 1 -d ' ')
+
+ # Convert to number of blocks
+ OFFSET=$(echo 0x$OFFSET | awk '{printf("%x\n",$1/512);}')
+
+ # Validate the offset
+ if [ $OFFSET != $BOOT1_OFFSET ]; then
+ echo "Incorrect offset $OFFSET != $BOOT1_OFFSET" >&2
+ exit 1
+ fi
+
+ xz -f $OUTPUT_FILE
+done <<EOF
+ amd64 BOOTx64.efi
+ arm64 BOOTaa64.efi
+ arm BOOTarm.efi
+ i386 BOOTia32.efi
+EOF
diff --git a/stand/efi/boot1/ufs_module.c b/stand/efi/boot1/ufs_module.c
new file mode 100644
index 0000000..4a8016f
--- /dev/null
+++ b/stand/efi/boot1/ufs_module.c
@@ -0,0 +1,185 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ * Copyright (c) 2001 Robert Drehmel
+ * All rights reserved.
+ * Copyright (c) 2014 Nathan Whitehorn
+ * All rights reserved.
+ * Copyright (c) 2015 Eric McCorkle
+ * All rights reverved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <efi.h>
+
+#include "boot_module.h"
+
+static dev_info_t *devinfo;
+static dev_info_t *devices;
+
+static int
+dskread(void *buf, u_int64_t lba, int nblk)
+{
+ int size;
+ EFI_STATUS status;
+
+ lba = lba / (devinfo->dev->Media->BlockSize / DEV_BSIZE);
+ size = nblk * DEV_BSIZE;
+
+ status = devinfo->dev->ReadBlocks(devinfo->dev,
+ devinfo->dev->Media->MediaId, lba, size, buf);
+
+ if (status != EFI_SUCCESS) {
+ DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, "
+ "status: %lu\n", devinfo->dev,
+ devinfo->dev->Media->MediaId, (uintmax_t)lba, size,
+ EFI_ERROR_CODE(status));
+ return (-1);
+ }
+
+ return (0);
+}
+
+#include "ufsread.c"
+
+static struct dmadat __dmadat;
+
+static int
+init_dev(dev_info_t* dev)
+{
+
+ devinfo = dev;
+ dmadat = &__dmadat;
+
+ return fsread(0, NULL, 0);
+}
+
+static EFI_STATUS
+probe(dev_info_t* dev)
+{
+
+ if (init_dev(dev) < 0)
+ return (EFI_UNSUPPORTED);
+
+ add_device(&devices, dev);
+
+ return (EFI_SUCCESS);
+}
+
+static EFI_STATUS
+load(const char *filepath, dev_info_t *dev, void **bufp, size_t *bufsize)
+{
+ ufs_ino_t ino;
+ EFI_STATUS status;
+ size_t size;
+ ssize_t read;
+ void *buf;
+
+#ifdef EFI_DEBUG
+ {
+ CHAR16 *text = efi_devpath_name(dev->devpath);
+ DPRINTF("Loading '%s' from %S\n", filepath, text);
+ efi_free_devpath_name(text);
+ }
+#endif
+ if (init_dev(dev) < 0) {
+ DPRINTF("Failed to init device\n");
+ return (EFI_UNSUPPORTED);
+ }
+
+ if ((ino = lookup(filepath)) == 0) {
+ DPRINTF("Failed to lookup '%s' (file not found?)\n", filepath);
+ return (EFI_NOT_FOUND);
+ }
+
+ if (fsread_size(ino, NULL, 0, &size) < 0 || size <= 0) {
+ printf("Failed to read size of '%s' ino: %d\n", filepath, ino);
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ if ((status = BS->AllocatePool(EfiLoaderData, size, &buf)) !=
+ EFI_SUCCESS) {
+ printf("Failed to allocate read buffer %zu for '%s' (%lu)\n",
+ size, filepath, EFI_ERROR_CODE(status));
+ return (status);
+ }
+
+ read = fsread(ino, buf, size);
+ if ((size_t)read != size) {
+ printf("Failed to read '%s' (%zd != %zu)\n", filepath, read,
+ size);
+ (void)BS->FreePool(buf);
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ DPRINTF("Load complete\n");
+
+ *bufp = buf;
+ *bufsize = size;
+
+ return (EFI_SUCCESS);
+}
+
+static void
+status(void)
+{
+ int i;
+ dev_info_t *dev;
+
+ for (dev = devices, i = 0; dev != NULL; dev = dev->next, i++)
+ ;
+
+ printf("%s found ", ufs_module.name);
+ switch (i) {
+ case 0:
+ printf("no partitions\n");
+ break;
+ case 1:
+ printf("%d partition\n", i);
+ break;
+ default:
+ printf("%d partitions\n", i);
+ }
+}
+
+static dev_info_t *
+_devices(void)
+{
+
+ return (devices);
+}
+
+const boot_module_t ufs_module =
+{
+ .name = "UFS",
+ .probe = probe,
+ .load = load,
+ .status = status,
+ .devices = _devices
+};
diff --git a/stand/efi/boot1/zfs_module.c b/stand/efi/boot1/zfs_module.c
new file mode 100644
index 0000000..e1d1a5a
--- /dev/null
+++ b/stand/efi/boot1/zfs_module.c
@@ -0,0 +1,248 @@
+/*-
+ * Copyright (c) 2015 Eric McCorkle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <efi.h>
+
+#include "boot_module.h"
+
+#include "libzfs.h"
+#include "zfsimpl.c"
+
+static dev_info_t *devices;
+
+uint64_t
+ldi_get_size(void *priv)
+{
+ dev_info_t *devinfo = priv;
+
+ return (devinfo->dev->Media->BlockSize *
+ (devinfo->dev->Media->LastBlock + 1));
+}
+
+static int
+vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
+{
+ dev_info_t *devinfo;
+ uint64_t lba;
+ size_t size, remainder, rb_size, blksz;
+ char *bouncebuf = NULL, *rb_buf;
+ EFI_STATUS status;
+
+ devinfo = (dev_info_t *)priv;
+ lba = off / devinfo->dev->Media->BlockSize;
+ remainder = off % devinfo->dev->Media->BlockSize;
+
+ rb_buf = buf;
+ rb_size = bytes;
+
+ /*
+ * If we have remainder from off, we need to add remainder part.
+ * Since buffer must be multiple of the BlockSize, round it all up.
+ */
+ size = roundup2(bytes + remainder, devinfo->dev->Media->BlockSize);
+ blksz = size;
+ if (remainder != 0 || size != bytes) {
+ rb_size = devinfo->dev->Media->BlockSize;
+ bouncebuf = malloc(rb_size);
+ if (bouncebuf == NULL) {
+ printf("vdev_read: out of memory\n");
+ return (-1);
+ }
+ rb_buf = bouncebuf;
+ blksz = rb_size - remainder;
+ }
+
+ while (bytes > 0) {
+ status = devinfo->dev->ReadBlocks(devinfo->dev,
+ devinfo->dev->Media->MediaId, lba, rb_size, rb_buf);
+ if (EFI_ERROR(status))
+ goto error;
+ if (bytes < blksz)
+ blksz = bytes;
+ if (bouncebuf != NULL)
+ memcpy(buf, rb_buf + remainder, blksz);
+ buf = (void *)((uintptr_t)buf + blksz);
+ bytes -= blksz;
+ lba++;
+ remainder = 0;
+ blksz = rb_size;
+ }
+
+ free(bouncebuf);
+ return (0);
+
+error:
+ free(bouncebuf);
+ DPRINTF("vdev_read: failed dev: %p, id: %u, lba: %ju, size: %zu,"
+ " rb_size: %zu, status: %lu\n", devinfo->dev,
+ devinfo->dev->Media->MediaId, (uintmax_t)lba, bytes, rb_size,
+ EFI_ERROR_CODE(status));
+ return (-1);
+}
+
+static EFI_STATUS
+probe(dev_info_t *dev)
+{
+ spa_t *spa;
+ dev_info_t *tdev;
+ EFI_STATUS status;
+
+ /* ZFS consumes the dev on success so we need a copy. */
+ if ((status = BS->AllocatePool(EfiLoaderData, sizeof(*dev),
+ (void**)&tdev)) != EFI_SUCCESS) {
+ DPRINTF("Failed to allocate tdev (%lu)\n",
+ EFI_ERROR_CODE(status));
+ return (status);
+ }
+ memcpy(tdev, dev, sizeof(*dev));
+
+ if (vdev_probe(vdev_read, tdev, &spa) != 0) {
+ (void)BS->FreePool(tdev);
+ return (EFI_UNSUPPORTED);
+ }
+
+ dev->devdata = spa;
+ add_device(&devices, dev);
+
+ return (EFI_SUCCESS);
+}
+
+static EFI_STATUS
+load(const char *filepath, dev_info_t *devinfo, void **bufp, size_t *bufsize)
+{
+ spa_t *spa;
+ struct zfsmount zfsmount;
+ dnode_phys_t dn;
+ struct stat st;
+ int err;
+ void *buf;
+ EFI_STATUS status;
+
+ spa = devinfo->devdata;
+
+#ifdef EFI_DEBUG
+ {
+ CHAR16 *text = efi_devpath_name(devinfo->devpath);
+ DPRINTF("load: '%s' spa: '%s', devpath: %S\n", filepath,
+ spa->spa_name, text);
+ efi_free_devpath_name(text);
+ }
+#endif
+ if ((err = zfs_spa_init(spa)) != 0) {
+ DPRINTF("Failed to load pool '%s' (%d)\n", spa->spa_name, err);
+ return (EFI_NOT_FOUND);
+ }
+
+ if ((err = zfs_mount(spa, 0, &zfsmount)) != 0) {
+ DPRINTF("Failed to mount pool '%s' (%d)\n", spa->spa_name, err);
+ return (EFI_NOT_FOUND);
+ }
+
+ if ((err = zfs_lookup(&zfsmount, filepath, &dn)) != 0) {
+ if (err == ENOENT) {
+ DPRINTF("Failed to find '%s' on pool '%s' (%d)\n",
+ filepath, spa->spa_name, err);
+ return (EFI_NOT_FOUND);
+ }
+ printf("Failed to lookup '%s' on pool '%s' (%d)\n", filepath,
+ spa->spa_name, err);
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ if ((err = zfs_dnode_stat(spa, &dn, &st)) != 0) {
+ printf("Failed to stat '%s' on pool '%s' (%d)\n", filepath,
+ spa->spa_name, err);
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ if ((status = BS->AllocatePool(EfiLoaderData, (UINTN)st.st_size, &buf))
+ != EFI_SUCCESS) {
+ printf("Failed to allocate load buffer %jd for pool '%s' for '%s' "
+ "(%lu)\n", (intmax_t)st.st_size, spa->spa_name, filepath, EFI_ERROR_CODE(status));
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ if ((err = dnode_read(spa, &dn, 0, buf, st.st_size)) != 0) {
+ printf("Failed to read node from %s (%d)\n", spa->spa_name,
+ err);
+ (void)BS->FreePool(buf);
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ *bufsize = st.st_size;
+ *bufp = buf;
+
+ return (EFI_SUCCESS);
+}
+
+static void
+status(void)
+{
+ spa_t *spa;
+
+ spa = STAILQ_FIRST(&zfs_pools);
+ if (spa == NULL) {
+ printf("%s found no pools\n", zfs_module.name);
+ return;
+ }
+
+ printf("%s found the following pools:", zfs_module.name);
+ STAILQ_FOREACH(spa, &zfs_pools, spa_link)
+ printf(" %s", spa->spa_name);
+
+ printf("\n");
+}
+
+static void
+init(void)
+{
+
+ zfs_init();
+}
+
+static dev_info_t *
+_devices(void)
+{
+
+ return (devices);
+}
+
+const boot_module_t zfs_module =
+{
+ .name = "ZFS",
+ .init = init,
+ .probe = probe,
+ .load = load,
+ .status = status,
+ .devices = _devices
+};
diff --git a/stand/efi/fdt/Makefile b/stand/efi/fdt/Makefile
new file mode 100644
index 0000000..7308ce9
--- /dev/null
+++ b/stand/efi/fdt/Makefile
@@ -0,0 +1,30 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+.PATH: ${LDRSRC}
+
+LIB= efi_fdt
+INTERNALLIB=
+WARNS?= 6
+
+SRCS= efi_fdt.c
+
+CFLAGS+= -ffreestanding
+.if ${MACHINE_CPUARCH} == "aarch64"
+CFLAGS+= -mgeneral-regs-only
+.else
+CFLAGS+= -msoft-float
+.endif
+
+# EFI library headers
+CFLAGS+= -I${EFISRC}/include
+CFLAGS+= -I${EFISRC}/include/${MACHINE}
+
+# libfdt headers
+CFLAGS+= -I${FDTSRC}
+
+# Pick up the bootstrap header for some interface items
+CFLAGS+= -I${LDRSRC}
+
+.include <bsd.lib.mk>
diff --git a/stand/efi/fdt/Makefile.depend b/stand/efi/fdt/Makefile.depend
new file mode 100644
index 0000000..18be76b
--- /dev/null
+++ b/stand/efi/fdt/Makefile.depend
@@ -0,0 +1,13 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/efi/fdt/efi_fdt.c b/stand/efi/fdt/efi_fdt.c
new file mode 100644
index 0000000..8703aed
--- /dev/null
+++ b/stand/efi/fdt/efi_fdt.c
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Andrew Turner under
+ * sponsorship from the FreeBSD Foundation.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <stand.h>
+#include <efi.h>
+#include <efilib.h>
+#include <fdt_platform.h>
+
+#include "bootstrap.h"
+
+static EFI_GUID fdtdtb = FDT_TABLE_GUID;
+
+int
+fdt_platform_load_dtb(void)
+{
+ struct fdt_header *hdr;
+
+ hdr = efi_get_table(&fdtdtb);
+ if (hdr == NULL)
+ return (1);
+ if (fdt_load_dtb_addr(hdr) != 0)
+ return (1);
+ printf("Using DTB provided by EFI at %p.\n", hdr);
+
+ fdt_load_dtb_overlays(NULL);
+ return (0);
+}
+
+void
+fdt_platform_fixups(void)
+{
+
+ fdt_apply_overlays();
+}
diff --git a/stand/efi/include/README b/stand/efi/include/README
new file mode 100644
index 0000000..bf821fa
--- /dev/null
+++ b/stand/efi/include/README
@@ -0,0 +1,36 @@
+/* $FreeBSD$ */
+/*-
+
+Files in this directory and subdirectories are subject to the following
+copyright unless superceded or supplemented by additional specific license
+terms found in the file headers of individual files.
+
+Copyright (c) 1998-2000 Intel Corporation
+
+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.
+
+THIS SOFTWARE IS PROVIDED ``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 INTEL 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. THE EFI SPECIFICATION AND ALL
+OTHER INFORMATION ON THIS WEB SITE ARE PROVIDED "AS IS" WITH NO
+WARRANTIES, AND ARE SUBJECT TO CHANGE WITHOUT NOTICE.
+
+*/
diff --git a/stand/efi/include/amd64/efibind.h b/stand/efi/include/amd64/efibind.h
new file mode 100644
index 0000000..8cfce5b
--- /dev/null
+++ b/stand/efi/include/amd64/efibind.h
@@ -0,0 +1,271 @@
+/* $FreeBSD$ */
+/*++
+
+Copyright (c) 1999 - 2003 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efefind.h
+
+Abstract:
+
+ EFI to compile bindings
+
+
+
+
+Revision History
+
+--*/
+
+#pragma pack()
+
+
+#ifdef __FreeBSD__
+#include <sys/stdint.h>
+#else
+//
+// Basic int types of various widths
+//
+
+#if (__STDC_VERSION__ < 199901L )
+
+ // No ANSI C 1999/2000 stdint.h integer width declarations
+
+ #ifdef _MSC_EXTENSIONS
+
+ // Use Microsoft C compiler integer width declarations
+
+ typedef unsigned __int64 uint64_t;
+ typedef __int64 int64_t;
+ typedef unsigned __int32 uint32_t;
+ typedef __int32 int32_t;
+ typedef unsigned short uint16_t;
+ typedef short int16_t;
+ typedef unsigned char uint8_t;
+ typedef char int8_t;
+ #else
+ #ifdef UNIX_LP64
+
+ // Use LP64 programming model from C_FLAGS for integer width declarations
+
+ typedef unsigned long uint64_t;
+ typedef long int64_t;
+ typedef unsigned int uint32_t;
+ typedef int int32_t;
+ typedef unsigned short uint16_t;
+ typedef short int16_t;
+ typedef unsigned char uint8_t;
+ typedef char int8_t;
+ #else
+
+ // Assume P64 programming model from C_FLAGS for integer width declarations
+
+ typedef unsigned long long uint64_t;
+ typedef long long int64_t;
+ typedef unsigned int uint32_t;
+ typedef int int32_t;
+ typedef unsigned short uint16_t;
+ typedef short int16_t;
+ typedef unsigned char uint8_t;
+ typedef char int8_t;
+ #endif
+ #endif
+#endif
+#endif /* __FreeBSD__ */
+
+//
+// Basic EFI types of various widths
+//
+
+#ifndef ACPI_THREAD_ID /* ACPI's definitions are fine */
+#define ACPI_USE_SYSTEM_INTTYPES 1 /* Tell ACPI we've defined types */
+
+typedef uint64_t UINT64;
+typedef int64_t INT64;
+
+#ifndef _BASETSD_H_
+ typedef uint32_t UINT32;
+ typedef int32_t INT32;
+#endif
+
+typedef uint16_t UINT16;
+typedef int16_t INT16;
+typedef uint8_t UINT8;
+typedef int8_t INT8;
+
+#endif
+
+#undef VOID
+#define VOID void
+
+
+typedef int64_t INTN;
+typedef uint64_t UINTN;
+
+#ifdef EFI_NT_EMULATOR
+ #define POST_CODE(_Data)
+#else
+ #ifdef EFI_DEBUG
+#define POST_CODE(_Data) __asm mov eax,(_Data) __asm out 0x80,al
+ #else
+ #define POST_CODE(_Data)
+ #endif
+#endif
+
+#define EFIERR(a) (0x8000000000000000 | a)
+#define EFI_ERROR_MASK 0x8000000000000000
+#define EFIERR_OEM(a) (0xc000000000000000 | a)
+
+
+#define BAD_POINTER 0xFBFBFBFBFBFBFBFB
+#define MAX_ADDRESS 0xFFFFFFFFFFFFFFFF
+
+#define BREAKPOINT() __asm { int 3 }
+
+//
+// Pointers must be aligned to these address to function
+//
+
+#define MIN_ALIGNMENT_SIZE 4
+
+#define ALIGN_VARIABLE(Value ,Adjustment) \
+ (UINTN)Adjustment = 0; \
+ if((UINTN)Value % MIN_ALIGNMENT_SIZE) \
+ (UINTN)Adjustment = MIN_ALIGNMENT_SIZE - ((UINTN)Value % MIN_ALIGNMENT_SIZE); \
+ Value = (UINTN)Value + (UINTN)Adjustment
+
+
+//
+// Define macros to build data structure signatures from characters.
+//
+
+#define EFI_SIGNATURE_16(A,B) ((A) | (B<<8))
+#define EFI_SIGNATURE_32(A,B,C,D) (EFI_SIGNATURE_16(A,B) | (EFI_SIGNATURE_16(C,D) << 16))
+#define EFI_SIGNATURE_64(A,B,C,D,E,F,G,H) (EFI_SIGNATURE_32(A,B,C,D) | ((UINT64)(EFI_SIGNATURE_32(E,F,G,H)) << 32))
+
+//
+// EFIAPI - prototype calling convention for EFI function pointers
+// BOOTSERVICE - prototype for implementation of a boot service interface
+// RUNTIMESERVICE - prototype for implementation of a runtime service interface
+// RUNTIMEFUNCTION - prototype for implementation of a runtime function that is not a service
+// RUNTIME_CODE - pragma macro for declaring runtime code
+//
+
+#ifdef __amd64__
+#define EFIAPI __attribute__((ms_abi))
+#endif
+
+#ifndef EFIAPI // Forces EFI calling conventions reguardless of compiler options
+ #ifdef _MSC_EXTENSIONS
+ #define EFIAPI __cdecl // Force C calling convention for Microsoft C compiler
+ #else
+ #define EFIAPI // Substitute expresion to force C calling convention
+ #endif
+#endif
+
+#define BOOTSERVICE
+//#define RUNTIMESERVICE(proto,a) alloc_text("rtcode",a); proto a
+//#define RUNTIMEFUNCTION(proto,a) alloc_text("rtcode",a); proto a
+#define RUNTIMESERVICE
+#define RUNTIMEFUNCTION
+
+
+#define RUNTIME_CODE(a) alloc_text("rtcode", a)
+#define BEGIN_RUNTIME_DATA() data_seg("rtdata")
+#define END_RUNTIME_DATA() data_seg("")
+
+#define VOLATILE volatile
+
+#define MEMORY_FENCE()
+
+#ifdef EFI_NO_INTERFACE_DECL
+ #define EFI_FORWARD_DECLARATION(x)
+ #define EFI_INTERFACE_DECL(x)
+#else
+ #define EFI_FORWARD_DECLARATION(x) typedef struct _##x x
+ #define EFI_INTERFACE_DECL(x) typedef struct x
+#endif
+
+#ifdef EFI_NT_EMULATOR
+
+//
+// To help ensure proper coding of integrated drivers, they are
+// compiled as DLLs. In NT they require a dll init entry pointer.
+// The macro puts a stub entry point into the DLL so it will load.
+//
+
+#define EFI_DRIVER_ENTRY_POINT(InitFunction) \
+ EFI_STATUS \
+ InitFunction ( \
+ EFI_HANDLE ImageHandle, \
+ EFI_SYSTEM_TABLE *SystemTable \
+ ); \
+ \
+ UINTN \
+ __stdcall \
+ _DllMainCRTStartup ( \
+ UINTN Inst, \
+ UINTN reason_for_call, \
+ VOID *rserved \
+ ) \
+ { \
+ return 1; \
+ } \
+ \
+ int \
+ __declspec( dllexport ) \
+ __cdecl \
+ InitializeDriver ( \
+ void *ImageHandle, \
+ void *SystemTable \
+ ) \
+ { \
+ return InitFunction(ImageHandle, SystemTable); \
+ }
+
+
+ #define LOAD_INTERNAL_DRIVER(_if, type, name, entry) \
+ (_if)->LoadInternal(type, name, NULL)
+
+#else // EFI_NT_EMULATOR
+
+//
+// When building similar to FW, link everything together as
+// one big module.
+//
+
+ #define EFI_DRIVER_ENTRY_POINT(InitFunction)
+
+ #define LOAD_INTERNAL_DRIVER(_if, type, name, entry) \
+ (_if)->LoadInternal(type, name, entry)
+
+#endif // EFI_FW_NT
+
+#ifdef __FreeBSD__
+#define INTERFACE_DECL(x) struct x
+#else
+//
+// Some compilers don't support the forward reference construct:
+// typedef struct XXXXX
+//
+// The following macro provide a workaround for such cases.
+//
+#ifdef NO_INTERFACE_DECL
+#define INTERFACE_DECL(x)
+#else
+#define INTERFACE_DECL(x) typedef struct x
+#endif
+#endif /* __FreeBSD__ */
+
+#ifdef _MSC_EXTENSIONS
+#pragma warning ( disable : 4731 ) // Suppress warnings about modification of EBP
+#endif
+
diff --git a/stand/efi/include/amd64/pe.h b/stand/efi/include/amd64/pe.h
new file mode 100644
index 0000000..f8033c5
--- /dev/null
+++ b/stand/efi/include/amd64/pe.h
@@ -0,0 +1,591 @@
+/* $FreeBSD$ */
+/*
+ PE32+ header file
+ */
+#ifndef _PE_H
+#define _PE_H
+
+#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
+#define IMAGE_OS2_SIGNATURE 0x454E // NE
+#define IMAGE_OS2_SIGNATURE_LE 0x454C // LE
+#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
+#define IMAGE_EDOS_SIGNATURE 0x44454550 // PEED
+
+
+typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
+ UINT16 e_magic; // Magic number
+ UINT16 e_cblp; // Bytes on last page of file
+ UINT16 e_cp; // Pages in file
+ UINT16 e_crlc; // Relocations
+ UINT16 e_cparhdr; // Size of header in paragraphs
+ UINT16 e_minalloc; // Minimum extra paragraphs needed
+ UINT16 e_maxalloc; // Maximum extra paragraphs needed
+ UINT16 e_ss; // Initial (relative) SS value
+ UINT16 e_sp; // Initial SP value
+ UINT16 e_csum; // Checksum
+ UINT16 e_ip; // Initial IP value
+ UINT16 e_cs; // Initial (relative) CS value
+ UINT16 e_lfarlc; // File address of relocation table
+ UINT16 e_ovno; // Overlay number
+ UINT16 e_res[4]; // Reserved words
+ UINT16 e_oemid; // OEM identifier (for e_oeminfo)
+ UINT16 e_oeminfo; // OEM information; e_oemid specific
+ UINT16 e_res2[10]; // Reserved words
+ UINT32 e_lfanew; // File address of new exe header
+ } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
+
+typedef struct _IMAGE_OS2_HEADER { // OS/2 .EXE header
+ UINT16 ne_magic; // Magic number
+ UINT8 ne_ver; // Version number
+ UINT8 ne_rev; // Revision number
+ UINT16 ne_enttab; // Offset of Entry Table
+ UINT16 ne_cbenttab; // Number of bytes in Entry Table
+ UINT32 ne_crc; // Checksum of whole file
+ UINT16 ne_flags; // Flag UINT16
+ UINT16 ne_autodata; // Automatic data segment number
+ UINT16 ne_heap; // Initial heap allocation
+ UINT16 ne_stack; // Initial stack allocation
+ UINT32 ne_csip; // Initial CS:IP setting
+ UINT32 ne_sssp; // Initial SS:SP setting
+ UINT16 ne_cseg; // Count of file segments
+ UINT16 ne_cmod; // Entries in Module Reference Table
+ UINT16 ne_cbnrestab; // Size of non-resident name table
+ UINT16 ne_segtab; // Offset of Segment Table
+ UINT16 ne_rsrctab; // Offset of Resource Table
+ UINT16 ne_restab; // Offset of resident name table
+ UINT16 ne_modtab; // Offset of Module Reference Table
+ UINT16 ne_imptab; // Offset of Imported Names Table
+ UINT32 ne_nrestab; // Offset of Non-resident Names Table
+ UINT16 ne_cmovent; // Count of movable entries
+ UINT16 ne_align; // Segment alignment shift count
+ UINT16 ne_cres; // Count of resource segments
+ UINT8 ne_exetyp; // Target Operating system
+ UINT8 ne_flagsothers; // Other .EXE flags
+ UINT16 ne_pretthunks; // offset to return thunks
+ UINT16 ne_psegrefbytes; // offset to segment ref. bytes
+ UINT16 ne_swaparea; // Minimum code swap area size
+ UINT16 ne_expver; // Expected Windows version number
+ } IMAGE_OS2_HEADER, *PIMAGE_OS2_HEADER;
+
+//
+// File header format.
+//
+
+typedef struct _IMAGE_FILE_HEADER {
+ UINT16 Machine;
+ UINT16 NumberOfSections;
+ UINT32 TimeDateStamp;
+ UINT32 PointerToSymbolTable;
+ UINT32 NumberOfSymbols;
+ UINT16 SizeOfOptionalHeader;
+ UINT16 Characteristics;
+} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
+
+#define IMAGE_SIZEOF_FILE_HEADER 20
+
+#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file.
+#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved externel references).
+#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file.
+#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file.
+#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed.
+#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine.
+#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in .DBG file
+#define IMAGE_FILE_SYSTEM 0x1000 // System File.
+#define IMAGE_FILE_DLL 0x2000 // File is a DLL.
+#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.
+
+#define IMAGE_FILE_MACHINE_UNKNOWN 0
+#define IMAGE_FILE_MACHINE_I386 0x14c // Intel 386.
+#define IMAGE_FILE_MACHINE_R3000 0x162 // MIPS little-endian, 0540 big-endian
+#define IMAGE_FILE_MACHINE_R4000 0x166 // MIPS little-endian
+#define IMAGE_FILE_MACHINE_ALPHA 0x184 // Alpha_AXP
+#define IMAGE_FILE_MACHINE_POWERPC 0x1F0 // IBM PowerPC Little-Endian
+#define IMAGE_FILE_MACHINE_TAHOE 0x7cc // Intel EM machine
+//
+// Directory format.
+//
+
+typedef struct _IMAGE_DATA_DIRECTORY {
+ UINT32 VirtualAddress;
+ UINT32 Size;
+} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
+
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
+
+//
+// Optional header format.
+//
+
+typedef struct _IMAGE_OPTIONAL_HEADER {
+ //
+ // Standard fields.
+ //
+
+ UINT16 Magic;
+ UINT8 MajorLinkerVersion;
+ UINT8 MinorLinkerVersion;
+ UINT32 SizeOfCode;
+ UINT32 SizeOfInitializedData;
+ UINT32 SizeOfUninitializedData;
+ UINT32 AddressOfEntryPoint;
+ UINT32 BaseOfCode;
+ UINT32 BaseOfData;
+
+ //
+ // NT additional fields.
+ //
+
+ UINT32 ImageBase;
+ UINT32 SectionAlignment;
+ UINT32 FileAlignment;
+ UINT16 MajorOperatingSystemVersion;
+ UINT16 MinorOperatingSystemVersion;
+ UINT16 MajorImageVersion;
+ UINT16 MinorImageVersion;
+ UINT16 MajorSubsystemVersion;
+ UINT16 MinorSubsystemVersion;
+ UINT32 Reserved1;
+ UINT32 SizeOfImage;
+ UINT32 SizeOfHeaders;
+ UINT32 CheckSum;
+ UINT16 Subsystem;
+ UINT16 DllCharacteristics;
+ UINT32 SizeOfStackReserve;
+ UINT32 SizeOfStackCommit;
+ UINT32 SizeOfHeapReserve;
+ UINT32 SizeOfHeapCommit;
+ UINT32 LoaderFlags;
+ UINT32 NumberOfRvaAndSizes;
+ IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
+
+typedef struct _IMAGE_ROM_OPTIONAL_HEADER {
+ UINT16 Magic;
+ UINT8 MajorLinkerVersion;
+ UINT8 MinorLinkerVersion;
+ UINT32 SizeOfCode;
+ UINT32 SizeOfInitializedData;
+ UINT32 SizeOfUninitializedData;
+ UINT32 AddressOfEntryPoint;
+ UINT32 BaseOfCode;
+ UINT32 BaseOfData;
+ UINT32 BaseOfBss;
+ UINT32 GprMask;
+ UINT32 CprMask[4];
+ UINT32 GpValue;
+} IMAGE_ROM_OPTIONAL_HEADER, *PIMAGE_ROM_OPTIONAL_HEADER;
+
+#define IMAGE_SIZEOF_ROM_OPTIONAL_HEADER 56
+#define IMAGE_SIZEOF_STD_OPTIONAL_HEADER 28
+#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER 224
+
+#define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x10b
+#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107
+
+typedef struct _IMAGE_NT_HEADERS {
+ UINT32 Signature;
+ IMAGE_FILE_HEADER FileHeader;
+ IMAGE_OPTIONAL_HEADER OptionalHeader;
+} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
+
+typedef struct _IMAGE_ROM_HEADERS {
+ IMAGE_FILE_HEADER FileHeader;
+ IMAGE_ROM_OPTIONAL_HEADER OptionalHeader;
+} IMAGE_ROM_HEADERS, *PIMAGE_ROM_HEADERS;
+
+#define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER) \
+ ((UINT32)ntheader + \
+ FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + \
+ ((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader \
+ ))
+
+
+// Subsystem Values
+
+#define IMAGE_SUBSYSTEM_UNKNOWN 0 // Unknown subsystem.
+#define IMAGE_SUBSYSTEM_NATIVE 1 // Image doesn't require a subsystem.
+#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem.
+#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem.
+#define IMAGE_SUBSYSTEM_OS2_CUI 5 // image runs in the OS/2 character subsystem.
+#define IMAGE_SUBSYSTEM_POSIX_CUI 7 // image run in the Posix character subsystem.
+
+
+// Directory Entries
+
+#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
+#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
+#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
+#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
+#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
+#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
+#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // Description String
+#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // Machine Value (MIPS GP)
+#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
+#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
+
+//
+// Section header format.
+//
+
+#define IMAGE_SIZEOF_SHORT_NAME 8
+
+typedef struct _IMAGE_SECTION_HEADER {
+ UINT8 Name[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;
+} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
+
+#define IMAGE_SIZEOF_SECTION_HEADER 40
+
+#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 // Reserved.
+
+#define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code.
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data.
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized data.
+
+#define IMAGE_SCN_LNK_OTHER 0x00000100 // Reserved.
+#define IMAGE_SCN_LNK_INFO 0x00000200 // Section contains comments or some other type of information.
+#define IMAGE_SCN_LNK_REMOVE 0x00000800 // Section contents will not become part of image.
+#define IMAGE_SCN_LNK_COMDAT 0x00001000 // Section contents comdat.
+
+#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 //
+#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 //
+#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 //
+#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 //
+#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 // Default alignment if no others are specified.
+#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 //
+#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 //
+
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 // Section can be discarded.
+#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // Section is not cachable.
+#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable.
+#define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable.
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable.
+#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
+#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable.
+
+//
+// Symbol format.
+//
+
+
+#define IMAGE_SIZEOF_SYMBOL 18
+
+//
+// Section values.
+//
+// Symbols have a section number of the section in which they are
+// defined. Otherwise, section numbers have the following meanings:
+//
+
+#define IMAGE_SYM_UNDEFINED (UINT16)0 // Symbol is undefined or is common.
+#define IMAGE_SYM_ABSOLUTE (UINT16)-1 // Symbol is an absolute value.
+#define IMAGE_SYM_DEBUG (UINT16)-2 // Symbol is a special debug item.
+
+//
+// Type (fundamental) values.
+//
+
+#define IMAGE_SYM_TYPE_NULL 0 // no type.
+#define IMAGE_SYM_TYPE_VOID 1 //
+#define IMAGE_SYM_TYPE_CHAR 2 // type character.
+#define IMAGE_SYM_TYPE_SHORT 3 // type short integer.
+#define IMAGE_SYM_TYPE_INT 4 //
+#define IMAGE_SYM_TYPE_LONG 5 //
+#define IMAGE_SYM_TYPE_FLOAT 6 //
+#define IMAGE_SYM_TYPE_DOUBLE 7 //
+#define IMAGE_SYM_TYPE_STRUCT 8 //
+#define IMAGE_SYM_TYPE_UNION 9 //
+#define IMAGE_SYM_TYPE_ENUM 10 // enumeration.
+#define IMAGE_SYM_TYPE_MOE 11 // member of enumeration.
+#define IMAGE_SYM_TYPE_BYTE 12 //
+#define IMAGE_SYM_TYPE_WORD 13 //
+#define IMAGE_SYM_TYPE_UINT 14 //
+#define IMAGE_SYM_TYPE_DWORD 15 //
+
+//
+// Type (derived) values.
+//
+
+#define IMAGE_SYM_DTYPE_NULL 0 // no derived type.
+#define IMAGE_SYM_DTYPE_POINTER 1 // pointer.
+#define IMAGE_SYM_DTYPE_FUNCTION 2 // function.
+#define IMAGE_SYM_DTYPE_ARRAY 3 // array.
+
+//
+// Storage classes.
+//
+
+#define IMAGE_SYM_CLASS_END_OF_FUNCTION (BYTE )-1
+#define IMAGE_SYM_CLASS_NULL 0
+#define IMAGE_SYM_CLASS_AUTOMATIC 1
+#define IMAGE_SYM_CLASS_EXTERNAL 2
+#define IMAGE_SYM_CLASS_STATIC 3
+#define IMAGE_SYM_CLASS_REGISTER 4
+#define IMAGE_SYM_CLASS_EXTERNAL_DEF 5
+#define IMAGE_SYM_CLASS_LABEL 6
+#define IMAGE_SYM_CLASS_UNDEFINED_LABEL 7
+#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8
+#define IMAGE_SYM_CLASS_ARGUMENT 9
+#define IMAGE_SYM_CLASS_STRUCT_TAG 10
+#define IMAGE_SYM_CLASS_MEMBER_OF_UNION 11
+#define IMAGE_SYM_CLASS_UNION_TAG 12
+#define IMAGE_SYM_CLASS_TYPE_DEFINITION 13
+#define IMAGE_SYM_CLASS_UNDEFINED_STATIC 14
+#define IMAGE_SYM_CLASS_ENUM_TAG 15
+#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16
+#define IMAGE_SYM_CLASS_REGISTER_PARAM 17
+#define IMAGE_SYM_CLASS_BIT_FIELD 18
+#define IMAGE_SYM_CLASS_BLOCK 100
+#define IMAGE_SYM_CLASS_FUNCTION 101
+#define IMAGE_SYM_CLASS_END_OF_STRUCT 102
+#define IMAGE_SYM_CLASS_FILE 103
+// new
+#define IMAGE_SYM_CLASS_SECTION 104
+#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 105
+
+// type packing constants
+
+#define N_BTMASK 017
+#define N_TMASK 060
+#define N_TMASK1 0300
+#define N_TMASK2 0360
+#define N_BTSHFT 4
+#define N_TSHIFT 2
+
+// MACROS
+
+//
+// Communal selection types.
+//
+
+#define IMAGE_COMDAT_SELECT_NODUPLICATES 1
+#define IMAGE_COMDAT_SELECT_ANY 2
+#define IMAGE_COMDAT_SELECT_SAME_SIZE 3
+#define IMAGE_COMDAT_SELECT_EXACT_MATCH 4
+#define IMAGE_COMDAT_SELECT_ASSOCIATIVE 5
+
+#define IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY 1
+#define IMAGE_WEAK_EXTERN_SEARCH_LIBRARY 2
+#define IMAGE_WEAK_EXTERN_SEARCH_ALIAS 3
+
+
+//
+// Relocation format.
+//
+
+typedef struct _IMAGE_RELOCATION {
+ UINT32 VirtualAddress;
+ UINT32 SymbolTableIndex;
+ UINT16 Type;
+} IMAGE_RELOCATION;
+
+#define IMAGE_SIZEOF_RELOCATION 10
+
+//
+// I386 relocation types.
+//
+
+#define IMAGE_REL_I386_ABSOLUTE 0 // Reference is absolute, no relocation is necessary
+#define IMAGE_REL_I386_DIR16 01 // Direct 16-bit reference to the symbols virtual address
+#define IMAGE_REL_I386_REL16 02 // PC-relative 16-bit reference to the symbols virtual address
+#define IMAGE_REL_I386_DIR32 06 // Direct 32-bit reference to the symbols virtual address
+#define IMAGE_REL_I386_DIR32NB 07 // Direct 32-bit reference to the symbols virtual address, base not included
+#define IMAGE_REL_I386_SEG12 011 // Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address
+#define IMAGE_REL_I386_SECTION 012
+#define IMAGE_REL_I386_SECREL 013
+#define IMAGE_REL_I386_REL32 024 // PC-relative 32-bit reference to the symbols virtual address
+
+//
+// MIPS relocation types.
+//
+
+#define IMAGE_REL_MIPS_ABSOLUTE 0 // Reference is absolute, no relocation is necessary
+#define IMAGE_REL_MIPS_REFHALF 01
+#define IMAGE_REL_MIPS_REFWORD 02
+#define IMAGE_REL_MIPS_JMPADDR 03
+#define IMAGE_REL_MIPS_REFHI 04
+#define IMAGE_REL_MIPS_REFLO 05
+#define IMAGE_REL_MIPS_GPREL 06
+#define IMAGE_REL_MIPS_LITERAL 07
+#define IMAGE_REL_MIPS_SECTION 012
+#define IMAGE_REL_MIPS_SECREL 013
+#define IMAGE_REL_MIPS_REFWORDNB 042
+#define IMAGE_REL_MIPS_PAIR 045
+
+//
+// Alpha Relocation types.
+//
+
+#define IMAGE_REL_ALPHA_ABSOLUTE 0x0
+#define IMAGE_REL_ALPHA_REFLONG 0x1
+#define IMAGE_REL_ALPHA_REFQUAD 0x2
+#define IMAGE_REL_ALPHA_GPREL32 0x3
+#define IMAGE_REL_ALPHA_LITERAL 0x4
+#define IMAGE_REL_ALPHA_LITUSE 0x5
+#define IMAGE_REL_ALPHA_GPDISP 0x6
+#define IMAGE_REL_ALPHA_BRADDR 0x7
+#define IMAGE_REL_ALPHA_HINT 0x8
+#define IMAGE_REL_ALPHA_INLINE_REFLONG 0x9
+#define IMAGE_REL_ALPHA_REFHI 0xA
+#define IMAGE_REL_ALPHA_REFLO 0xB
+#define IMAGE_REL_ALPHA_PAIR 0xC
+#define IMAGE_REL_ALPHA_MATCH 0xD
+#define IMAGE_REL_ALPHA_SECTION 0xE
+#define IMAGE_REL_ALPHA_SECREL 0xF
+#define IMAGE_REL_ALPHA_REFLONGNB 0x10
+
+//
+// IBM PowerPC relocation types.
+//
+
+#define IMAGE_REL_PPC_ABSOLUTE 0x0000 // NOP
+#define IMAGE_REL_PPC_ADDR64 0x0001 // 64-bit address
+#define IMAGE_REL_PPC_ADDR32 0x0002 // 32-bit address
+#define IMAGE_REL_PPC_ADDR24 0x0003 // 26-bit address, shifted left 2 (branch absolute)
+#define IMAGE_REL_PPC_ADDR16 0x0004 // 16-bit address
+#define IMAGE_REL_PPC_ADDR14 0x0005 // 16-bit address, shifted left 2 (load doubleword)
+#define IMAGE_REL_PPC_REL24 0x0006 // 26-bit PC-relative offset, shifted left 2 (branch relative)
+#define IMAGE_REL_PPC_REL14 0x0007 // 16-bit PC-relative offset, shifted left 2 (br cond relative)
+#define IMAGE_REL_PPC_TOCREL16 0x0008 // 16-bit offset from TOC base
+#define IMAGE_REL_PPC_TOCREL14 0x0009 // 16-bit offset from TOC base, shifted left 2 (load doubleword)
+
+#define IMAGE_REL_PPC_ADDR32NB 0x000A // 32-bit addr w/o image base
+#define IMAGE_REL_PPC_SECREL 0x000B // va of containing section (as in an image sectionhdr)
+#define IMAGE_REL_PPC_SECTION 0x000C // sectionheader number
+#define IMAGE_REL_PPC_IFGLUE 0x000D // substitute TOC restore instruction iff symbol is glue code
+#define IMAGE_REL_PPC_IMGLUE 0x000E // symbol is glue code; virtual address is TOC restore instruction
+
+#define IMAGE_REL_PPC_TYPEMASK 0x00FF // mask to isolate above values in IMAGE_RELOCATION.Type
+
+// Flag bits in IMAGE_RELOCATION.TYPE
+
+#define IMAGE_REL_PPC_NEG 0x0100 // subtract reloc value rather than adding it
+#define IMAGE_REL_PPC_BRTAKEN 0x0200 // fix branch prediction bit to predict branch taken
+#define IMAGE_REL_PPC_BRNTAKEN 0x0400 // fix branch prediction bit to predict branch not taken
+#define IMAGE_REL_PPC_TOCDEFN 0x0800 // toc slot defined in file (or, data in toc)
+
+//
+// Based relocation format.
+//
+
+typedef struct _IMAGE_BASE_RELOCATION {
+ UINT32 VirtualAddress;
+ UINT32 SizeOfBlock;
+// UINT16 TypeOffset[1];
+} IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
+
+#define IMAGE_SIZEOF_BASE_RELOCATION 8
+
+//
+// Based relocation types.
+//
+
+#define IMAGE_REL_BASED_ABSOLUTE 0
+#define IMAGE_REL_BASED_HIGH 1
+#define IMAGE_REL_BASED_LOW 2
+#define IMAGE_REL_BASED_HIGHLOW 3
+#define IMAGE_REL_BASED_HIGHADJ 4
+#define IMAGE_REL_BASED_MIPS_JMPADDR 5
+#define IMAGE_REL_BASED_DIR64 10
+
+//
+// Line number format.
+//
+
+typedef struct _IMAGE_LINENUMBER {
+ 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.
+} IMAGE_LINENUMBER;
+
+#define IMAGE_SIZEOF_LINENUMBER 6
+
+//
+// Archive format.
+//
+
+#define IMAGE_ARCHIVE_START_SIZE 8
+#define IMAGE_ARCHIVE_START "!<arch>\n"
+#define IMAGE_ARCHIVE_END "`\n"
+#define IMAGE_ARCHIVE_PAD "\n"
+#define IMAGE_ARCHIVE_LINKER_MEMBER "/ "
+#define IMAGE_ARCHIVE_LONGNAMES_MEMBER "// "
+
+typedef struct _IMAGE_ARCHIVE_MEMBER_HEADER {
+ 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.
+} IMAGE_ARCHIVE_MEMBER_HEADER, *PIMAGE_ARCHIVE_MEMBER_HEADER;
+
+#define IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60
+
+//
+// DLL support.
+//
+
+//
+// Export Format
+//
+
+typedef struct _IMAGE_EXPORT_DIRECTORY {
+ UINT32 Characteristics;
+ UINT32 TimeDateStamp;
+ UINT16 MajorVersion;
+ UINT16 MinorVersion;
+ UINT32 Name;
+ UINT32 Base;
+ UINT32 NumberOfFunctions;
+ UINT32 NumberOfNames;
+ UINT32 *AddressOfFunctions;
+ UINT32 *AddressOfNames;
+ UINT32 *AddressOfNameOrdinals;
+} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
+
+//
+// Import Format
+//
+
+typedef struct _IMAGE_IMPORT_BY_NAME {
+ UINT16 Hint;
+ UINT8 Name[1];
+} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
+
+typedef struct _IMAGE_THUNK_DATA {
+ union {
+ UINT32 Function;
+ UINT32 Ordinal;
+ PIMAGE_IMPORT_BY_NAME AddressOfData;
+ } u1;
+} IMAGE_THUNK_DATA, *PIMAGE_THUNK_DATA;
+
+#define IMAGE_ORDINAL_FLAG 0x80000000
+#define IMAGE_SNAP_BY_ORDINAL(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG) != 0)
+#define IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff)
+
+typedef struct _IMAGE_IMPORT_DESCRIPTOR {
+ UINT32 Characteristics;
+ UINT32 TimeDateStamp;
+ UINT32 ForwarderChain;
+ UINT32 Name;
+ PIMAGE_THUNK_DATA FirstThunk;
+} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;
+
+#endif
diff --git a/stand/efi/include/arm/efibind.h b/stand/efi/include/arm/efibind.h
new file mode 100644
index 0000000..177032a
--- /dev/null
+++ b/stand/efi/include/arm/efibind.h
@@ -0,0 +1,165 @@
+/* $FreeBSD$ */
+/*++
+
+Copyright (c) 2004 - 2012, 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:
+
+ EfiBind.h
+
+Abstract:
+
+ Processor or Compiler specific defines and types for IA-32.
+ We are using the ANSI C 2000 _t type definitions for basic types.
+ This it technically a violation of the coding standard, but they
+ are used to make EfiTypes.h portable. Code other than EfiTypes.h
+ should never use any ANSI C 2000 _t integer types.
+
+--*/
+
+#ifndef _EFI_BIND_H_
+#define _EFI_BIND_H_
+
+
+#define EFI_DRIVER_ENTRY_POINT(InitFunction)
+#define EFI_APPLICATION_ENTRY_POINT EFI_DRIVER_ENTRY_POINT
+
+
+//
+// Make sure we are using the correct packing rules per EFI specification
+//
+#ifndef __GNUC__
+#pragma pack()
+#endif
+
+
+#ifdef __FreeBSD__
+#include <sys/stdint.h>
+#else
+//
+// Assume standard IA-32 alignment.
+// BugBug: Need to check portability of long long
+//
+typedef unsigned long long uint64_t;
+typedef long long int64_t;
+typedef unsigned int uint32_t;
+typedef int int32_t;
+typedef unsigned short uint16_t;
+typedef short int16_t;
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+#endif
+
+typedef uint64_t UINT64;
+typedef int64_t INT64;
+typedef uint32_t UINT32;
+typedef int32_t INT32;
+typedef uint16_t UINT16;
+typedef int16_t INT16;
+typedef uint8_t UINT8;
+typedef int8_t INT8;
+
+#undef VOID
+#define VOID void
+
+//
+// Native integer size in stdint.h
+//
+typedef uint32_t UINTN;
+typedef int32_t INTN;
+
+#define EFIERR(a) (0x80000000 | a)
+#define EFI_ERROR_MASK 0x80000000
+#define EFIERR_OEM(a) (0xc0000000 | a)
+
+//
+// Processor specific defines
+//
+#define EFI_MAX_BIT 0x80000000
+#define MAX_2_BITS 0xC0000000
+
+//
+// Maximum legal IA-32 address
+//
+#define EFI_MAX_ADDRESS 0xFFFFFFFF
+
+//
+// Bad pointer value to use in check builds.
+// if you see this value you are using uninitialized or free'ed data
+//
+#define EFI_BAD_POINTER 0xAFAFAFAF
+#define EFI_BAD_POINTER_AS_BYTE 0xAF
+
+#define EFI_DEADLOOP() { volatile UINTN __iii; __iii = 1; while (__iii); }
+
+//
+// Inject a break point in the code to assist debugging for NT Emulation Environment
+// For real hardware, just put in a halt loop. Don't do a while(1) because the
+// compiler will optimize away the rest of the function following, so that you run out in
+// the weeds if you skip over it with a debugger.
+//
+#define EFI_BREAKPOINT EFI_DEADLOOP()
+
+
+//
+// Memory Fence forces serialization, and is needed to support out of order
+// memory transactions. The Memory Fence is mainly used to make sure IO
+// transactions complete in a deterministic sequence, and to syncronize locks
+// an other MP code. Currently no memory fencing is required.
+//
+#define MEMORY_FENCE()
+
+//
+// Some compilers don't support the forward reference construct:
+// typedef struct XXXXX. The forward reference is required for
+// ANSI compatibility.
+//
+// The following macro provide a workaround for such cases.
+//
+
+
+#ifdef EFI_NO_INTERFACE_DECL
+ #define EFI_FORWARD_DECLARATION(x)
+#else
+ #define EFI_FORWARD_DECLARATION(x) typedef struct _##x x
+#endif
+
+
+//
+// Some C compilers optimize the calling conventions to increase performance.
+// EFIAPI is used to make all public APIs follow the standard C calling
+// convention.
+//
+#define EFIAPI
+
+
+
+//
+// For symbol name in GNU assembly code, an extra "_" is necessary
+//
+#if defined(__GNUC__)
+ ///
+ /// Private worker functions for ASM_PFX()
+ ///
+ #define _CONCATENATE(a, b) __CONCATENATE(a, b)
+ #define __CONCATENATE(a, b) a ## b
+
+ ///
+ /// The __USER_LABEL_PREFIX__ macro predefined by GNUC represents the prefix
+ /// on symbols in assembly language.
+ ///
+ #define ASM_PFX(name) _CONCATENATE (__USER_LABEL_PREFIX__, name)
+
+#endif
+
+#define INTERFACE_DECL(x) struct x
+
+#endif
diff --git a/stand/efi/include/arm64/efibind.h b/stand/efi/include/arm64/efibind.h
new file mode 100644
index 0000000..4751e2e
--- /dev/null
+++ b/stand/efi/include/arm64/efibind.h
@@ -0,0 +1,217 @@
+/* $FreeBSD$ */
+/*++
+
+Copyright (c) 1999 - 2003 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efefind.h
+
+Abstract:
+
+ EFI to compile bindings
+
+
+
+
+Revision History
+
+--*/
+
+#pragma pack()
+
+
+#ifdef __FreeBSD__
+#include <sys/stdint.h>
+#else
+//
+// Basic int types of various widths
+//
+
+#if (__STDC_VERSION__ < 199901L )
+
+ // No ANSI C 1999/2000 stdint.h integer width declarations
+
+ #ifdef _MSC_EXTENSIONS
+
+ // Use Microsoft C compiler integer width declarations
+
+ typedef unsigned __int64 uint64_t;
+ typedef __int64 int64_t;
+ typedef unsigned __int32 uint32_t;
+ typedef __int32 int32_t;
+ typedef unsigned __int16 uint16_t;
+ typedef __int16 int16_t;
+ typedef unsigned __int8 uint8_t;
+ typedef __int8 int8_t;
+ #else
+ #ifdef UNIX_LP64
+
+ // Use LP64 programming model from C_FLAGS for integer width declarations
+
+ typedef unsigned long uint64_t;
+ typedef long int64_t;
+ typedef unsigned int uint32_t;
+ typedef int int32_t;
+ typedef unsigned short uint16_t;
+ typedef short int16_t;
+ typedef unsigned char uint8_t;
+ typedef char int8_t;
+ #else
+
+ // Assume P64 programming model from C_FLAGS for integer width declarations
+
+ typedef unsigned long long uint64_t;
+ typedef long long int64_t;
+ typedef unsigned int uint32_t;
+ typedef int int32_t;
+ typedef unsigned short uint16_t;
+ typedef short int16_t;
+ typedef unsigned char uint8_t;
+ typedef char int8_t;
+ #endif
+ #endif
+#endif
+#endif /* __FreeBSD__ */
+
+//
+// Basic EFI types of various widths
+//
+
+
+typedef uint64_t UINT64;
+typedef int64_t INT64;
+typedef uint32_t UINT32;
+typedef int32_t INT32;
+typedef uint16_t UINT16;
+typedef int16_t INT16;
+typedef uint8_t UINT8;
+typedef int8_t INT8;
+
+
+#undef VOID
+#define VOID void
+
+
+typedef int64_t INTN;
+typedef uint64_t UINTN;
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// BugBug: Code to debug
+//
+#define BIT63 0x8000000000000000
+
+#define PLATFORM_IOBASE_ADDRESS (0xffffc000000 | BIT63)
+#define PORT_TO_MEMD(_Port) (PLATFORM_IOBASE_ADDRESS | ( ( ( (_Port) & 0xfffc) << 10 ) | ( (_Port) & 0x0fff) ) )
+
+//
+// Macro's with casts make this much easier to use and read.
+//
+#define PORT_TO_MEM8D(_Port) (*(UINT8 *)(PORT_TO_MEMD(_Port)))
+#define POST_CODE(_Data) (PORT_TO_MEM8D(0x80) = (_Data))
+//
+// BugBug: End Debug Code!!!
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+#define EFIERR(a) (0x8000000000000000 | a)
+#define EFI_ERROR_MASK 0x8000000000000000
+#define EFIERR_OEM(a) (0xc000000000000000 | a)
+
+#define BAD_POINTER 0xFBFBFBFBFBFBFBFB
+#define MAX_ADDRESS 0xFFFFFFFFFFFFFFFF
+
+#define BREAKPOINT() __break(0)
+
+//
+// Pointers must be aligned to these address to function
+// you will get an alignment fault if this value is less than 8
+//
+#define MIN_ALIGNMENT_SIZE 8
+
+#define ALIGN_VARIABLE(Value , Adjustment) \
+ (UINTN) Adjustment = 0; \
+ if((UINTN)Value % MIN_ALIGNMENT_SIZE) \
+ (UINTN)Adjustment = MIN_ALIGNMENT_SIZE - ((UINTN)Value % MIN_ALIGNMENT_SIZE); \
+ Value = (UINTN)Value + (UINTN)Adjustment
+
+//
+// Define macros to create data structure signatures.
+//
+
+#define EFI_SIGNATURE_16(A,B) ((A) | (B<<8))
+#define EFI_SIGNATURE_32(A,B,C,D) (EFI_SIGNATURE_16(A,B) | (EFI_SIGNATURE_16(C,D) << 16))
+#define EFI_SIGNATURE_64(A,B,C,D,E,F,G,H) (EFI_SIGNATURE_32(A,B,C,D) | ((UINT64)(EFI_SIGNATURE_32(E,F,G,H)) << 32))
+
+//
+// EFIAPI - prototype calling convention for EFI function pointers
+// BOOTSERVICE - prototype for implementation of a boot service interface
+// RUNTIMESERVICE - prototype for implementation of a runtime service interface
+// RUNTIMEFUNCTION - prototype for implementation of a runtime function that is not a service
+// RUNTIME_CODE - pragma macro for declaring runtime code
+//
+
+#ifndef EFIAPI // Forces EFI calling conventions reguardless of compiler options
+ #ifdef _MSC_EXTENSIONS
+ #define EFIAPI __cdecl // Force C calling convention for Microsoft C compiler
+ #else
+ #define EFIAPI // Substitute expresion to force C calling convention
+ #endif
+#endif
+
+#define BOOTSERVICE
+#define RUNTIMESERVICE
+#define RUNTIMEFUNCTION
+
+#define RUNTIME_CODE(a) alloc_text("rtcode", a)
+#define BEGIN_RUNTIME_DATA() data_seg("rtdata")
+#define END_RUNTIME_DATA() data_seg()
+
+#define VOLATILE volatile
+
+//
+// BugBug: Need to find out if this is portable across compilers.
+//
+void __mfa (void);
+#define MEMORY_FENCE() __mfa()
+
+#ifdef EFI_NO_INTERFACE_DECL
+ #define EFI_FORWARD_DECLARATION(x)
+ #define EFI_INTERFACE_DECL(x)
+#else
+ #define EFI_FORWARD_DECLARATION(x) typedef struct _##x x
+ #define EFI_INTERFACE_DECL(x) typedef struct x
+#endif
+
+//
+// When build similar to FW, then link everything together as
+// one big module.
+//
+
+#define EFI_DRIVER_ENTRY_POINT(InitFunction)
+
+#define LOAD_INTERNAL_DRIVER(_if, type, name, entry) \
+ (_if)->LoadInternal(type, name, entry)
+// entry(NULL, ST)
+
+#ifdef __FreeBSD__
+#define INTERFACE_DECL(x) struct x
+#else
+//
+// Some compilers don't support the forward reference construct:
+// typedef struct XXXXX
+//
+// The following macro provide a workaround for such cases.
+//
+#ifdef NO_INTERFACE_DECL
+#define INTERFACE_DECL(x)
+#else
+#define INTERFACE_DECL(x) typedef struct x
+#endif
+#endif
diff --git a/stand/efi/include/efi.h b/stand/efi/include/efi.h
new file mode 100644
index 0000000..a91e881
--- /dev/null
+++ b/stand/efi/include/efi.h
@@ -0,0 +1,63 @@
+/* $FreeBSD$ */
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efi.h
+
+Abstract:
+
+ Public EFI header files
+
+
+
+Revision History
+
+--*/
+
+//
+// Build flags on input
+// EFI32
+// EFI_DEBUG - Enable debugging code
+// EFI_NT_EMULATOR - Building for running under NT
+//
+
+
+#ifndef _EFI_INCLUDE_
+#define _EFI_INCLUDE_
+
+#define EFI_FIRMWARE_VENDOR L"INTEL"
+#define EFI_FIRMWARE_MAJOR_REVISION 14
+#define EFI_FIRMWARE_MINOR_REVISION 62
+#define EFI_FIRMWARE_REVISION ((EFI_FIRMWARE_MAJOR_REVISION <<16) | (EFI_FIRMWARE_MINOR_REVISION))
+
+#include "efibind.h"
+#include "efidef.h"
+#include "efidevp.h"
+#include "efiprot.h"
+#include "eficon.h"
+#include "efiser.h"
+#include "efi_nii.h"
+#include "efipxebc.h"
+#include "efinet.h"
+#include "efiapi.h"
+#include "efifs.h"
+#include "efierr.h"
+#include "efigop.h"
+
+/*
+ * FreeBSD UUID
+ */
+#define FREEBSD_BOOT_VAR_GUID \
+ { 0xCFEE69AD, 0xA0DE, 0x47A9, {0x93, 0xA8, 0xF6, 0x31, 0x06, 0xF8, 0xAE, 0x99} }
+
+#endif
diff --git a/stand/efi/include/efi_driver_utils.h b/stand/efi/include/efi_driver_utils.h
new file mode 100644
index 0000000..520cff1
--- /dev/null
+++ b/stand/efi/include/efi_driver_utils.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2017 Eric McCorkle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _EFI_DRIVER_UTILS_H_
+#define _EFI_DRIVER_UTILS_H_
+
+#include <efi.h>
+#include <efiprot.h>
+
+extern EFI_STATUS install_driver(EFI_DRIVER_BINDING *driver);
+extern EFI_STATUS connect_controllers(EFI_GUID *filter);
+
+#endif
diff --git a/stand/efi/include/efi_drivers.h b/stand/efi/include/efi_drivers.h
new file mode 100644
index 0000000..8df9133
--- /dev/null
+++ b/stand/efi/include/efi_drivers.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 2016 Eric McCorkle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _EFI_DRIVERS_H_
+#define _EFI_DRIVERS_H_
+
+#include <bootstrap.h>
+
+typedef struct efi_driver_t {
+ const char *name;
+ void (*init)(void);
+} efi_driver_t;
+
+extern struct devsw efipart_dev;
+extern int efipart_getdesc(struct devdesc *dev, char **out);
+
+/* EFI drivers. */
+extern const efi_driver_t fs_driver;
+
+#endif
diff --git a/stand/efi/include/efi_nii.h b/stand/efi/include/efi_nii.h
new file mode 100644
index 0000000..561cbd4
--- /dev/null
+++ b/stand/efi/include/efi_nii.h
@@ -0,0 +1,86 @@
+/* $FreeBSD$ */
+#ifndef _EFI_NII_H
+#define _EFI_NII_H
+
+/*++
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module name:
+ efi_nii.h
+
+Abstract:
+
+Revision history:
+ 2000-Feb-18 M(f)J GUID updated.
+ Structure order changed for machine word alignment.
+ Added StringId[4] to structure.
+
+ 2000-Feb-14 M(f)J Genesis.
+--*/
+
+#define EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL \
+ { 0xE18541CD, 0xF755, 0x4f73, {0x92, 0x8D, 0x64, 0x3C, 0x8A, 0x79, 0xB2, 0x29} }
+#define EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_31 \
+ { 0x1ACED566, 0x76ED, 0x4218, {0xBC, 0x81, 0x76, 0x7F, 0x1F, 0x97, 0x7A, 0x89} }
+
+#define EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE_REVISION 0x00010000
+#define EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE_REVISION_31 0x00010001
+
+typedef enum {
+ EfiNetworkInterfaceUndi = 1
+} EFI_NETWORK_INTERFACE_TYPE;
+
+typedef struct {
+
+ UINT64 Revision;
+ // Revision of the network interface identifier protocol interface.
+
+ UINT64 ID;
+ // Address of the first byte of the identifying structure for this
+ // network interface. This is set to zero if there is no structure.
+ //
+ // For PXE/UNDI this is the first byte of the !PXE structure.
+
+ UINT64 ImageAddr;
+ // Address of the UNrelocated driver/ROM image. This is set
+ // to zero if there is no driver/ROM image.
+ //
+ // For 16-bit UNDI, this is the first byte of the option ROM in
+ // upper memory.
+ //
+ // For 32/64-bit S/W UNDI, this is the first byte of the EFI ROM
+ // image.
+ //
+ // For H/W UNDI, this is set to zero.
+
+ UINT32 ImageSize;
+ // Size of the UNrelocated driver/ROM image of this network interface.
+ // This is set to zero if there is no driver/ROM image.
+
+ CHAR8 StringId[4];
+ // 4 char ASCII string to go in class identifier (option 60) in DHCP
+ // and Boot Server discover packets.
+ // For EfiNetworkInterfaceUndi this field is "UNDI".
+ // For EfiNetworkInterfaceSnp this field is "SNPN".
+
+ UINT8 Type;
+ UINT8 MajorVer;
+ UINT8 MinorVer;
+ // Information to be placed into the PXE DHCP and Discover packets.
+ // This is the network interface type and version number that will
+ // be placed into DHCP option 94 (client network interface identifier).
+ BOOLEAN Ipv6Supported;
+ UINT8 IfNum; // interface number to be used with pxeid structure
+} EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE;
+
+extern EFI_GUID NetworkInterfaceIdentifierProtocol;
+extern EFI_GUID NetworkInterfaceIdentifierProtocol_31;
+
+#endif // _EFI_NII_H
diff --git a/stand/efi/include/efiapi.h b/stand/efi/include/efiapi.h
new file mode 100644
index 0000000..8bab217
--- /dev/null
+++ b/stand/efi/include/efiapi.h
@@ -0,0 +1,902 @@
+/* $FreeBSD$ */
+#ifndef _EFI_API_H
+#define _EFI_API_H
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efiapi.h
+
+Abstract:
+
+ Global EFI runtime & boot service interfaces
+
+
+
+
+Revision History
+
+--*/
+
+//
+// EFI Specification Revision
+//
+
+#define EFI_SPECIFICATION_MAJOR_REVISION 1
+#define EFI_SPECIFICATION_MINOR_REVISION 10
+
+//
+// Declare forward referenced data structures
+//
+
+INTERFACE_DECL(_EFI_SYSTEM_TABLE);
+
+//
+// EFI Memory
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ALLOCATE_PAGES) (
+ IN EFI_ALLOCATE_TYPE Type,
+ IN EFI_MEMORY_TYPE MemoryType,
+ IN UINTN NoPages,
+ OUT EFI_PHYSICAL_ADDRESS *Memory
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FREE_PAGES) (
+ IN EFI_PHYSICAL_ADDRESS Memory,
+ IN UINTN NoPages
+ );
+
+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
+ );
+
+#define NextMemoryDescriptor(Ptr,Size) ((EFI_MEMORY_DESCRIPTOR *) (((UINT8 *) Ptr) + Size))
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ALLOCATE_POOL) (
+ IN EFI_MEMORY_TYPE PoolType,
+ IN UINTN Size,
+ OUT VOID **Buffer
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FREE_POOL) (
+ IN VOID *Buffer
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SET_VIRTUAL_ADDRESS_MAP) (
+ IN UINTN MemoryMapSize,
+ IN UINTN DescriptorSize,
+ IN UINT32 DescriptorVersion,
+ IN EFI_MEMORY_DESCRIPTOR *VirtualMap
+ );
+
+
+#define EFI_OPTIONAL_PTR 0x00000001
+#define EFI_INTERNAL_FNC 0x00000002 // Pointer to internal runtime fnc
+#define EFI_INTERNAL_PTR 0x00000004 // Pointer to internal runtime data
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CONVERT_POINTER) (
+ IN UINTN DebugDisposition,
+ IN OUT VOID **Address
+ );
+
+
+//
+// EFI Events
+//
+
+
+
+#define EVT_TIMER 0x80000000
+#define EVT_RUNTIME 0x40000000
+#define EVT_RUNTIME_CONTEXT 0x20000000
+
+#define EVT_NOTIFY_WAIT 0x00000100
+#define EVT_NOTIFY_SIGNAL 0x00000200
+
+#define EVT_SIGNAL_EXIT_BOOT_SERVICES 0x00000201
+#define EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 0x60000202
+
+#define EVT_EFI_SIGNAL_MASK 0x000000FF
+#define EVT_EFI_SIGNAL_MAX 2
+
+typedef
+VOID
+(EFIAPI *EFI_EVENT_NOTIFY) (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+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
+ );
+
+typedef enum {
+ TimerCancel,
+ TimerPeriodic,
+ TimerRelative,
+ TimerTypeMax
+} EFI_TIMER_DELAY;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SET_TIMER) (
+ IN EFI_EVENT Event,
+ IN EFI_TIMER_DELAY Type,
+ IN UINT64 TriggerTime
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIGNAL_EVENT) (
+ IN EFI_EVENT Event
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_WAIT_FOR_EVENT) (
+ IN UINTN NumberOfEvents,
+ IN EFI_EVENT *Event,
+ OUT UINTN *Index
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CLOSE_EVENT) (
+ IN EFI_EVENT Event
+ );
+
+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
+
+typedef
+EFI_TPL
+(EFIAPI *EFI_RAISE_TPL) (
+ IN EFI_TPL NewTpl
+ );
+
+typedef
+VOID
+(EFIAPI *EFI_RESTORE_TPL) (
+ IN EFI_TPL OldTpl
+ );
+
+
+//
+// EFI platform varibles
+//
+
+#define EFI_GLOBAL_VARIABLE \
+ { 0x8BE4DF61, 0x93CA, 0x11d2, {0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C} }
+
+// Variable attributes
+#define EFI_VARIABLE_NON_VOLATILE 0x00000001
+#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
+#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004
+
+// Variable size limitation
+#define EFI_MAXIMUM_VARIABLE_SIZE 1024
+
+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
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_GET_NEXT_VARIABLE_NAME) (
+ IN OUT UINTN *VariableNameSize,
+ IN OUT CHAR16 *VariableName,
+ IN OUT EFI_GUID *VendorGuid
+ );
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SET_VARIABLE) (
+ IN const CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ IN UINTN DataSize,
+ IN VOID *Data
+ );
+
+
+//
+// EFI Time
+//
+
+typedef struct {
+ UINT32 Resolution; // 1e-6 parts per million
+ UINT32 Accuracy; // hertz
+ BOOLEAN SetsToZero; // Set clears sub-second time
+} EFI_TIME_CAPABILITIES;
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_GET_TIME) (
+ OUT EFI_TIME *Time,
+ OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SET_TIME) (
+ IN EFI_TIME *Time
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_GET_WAKEUP_TIME) (
+ OUT BOOLEAN *Enabled,
+ OUT BOOLEAN *Pending,
+ OUT EFI_TIME *Time
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SET_WAKEUP_TIME) (
+ IN BOOLEAN Enable,
+ IN EFI_TIME *Time OPTIONAL
+ );
+
+
+//
+// Image functions
+//
+
+
+// PE32+ Subsystem type for EFI images
+
+#if !defined(IMAGE_SUBSYSTEM_EFI_APPLICATION)
+#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
+#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
+#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12
+#endif
+
+// PE32+ Machine type for EFI images
+
+#if !defined(EFI_IMAGE_MACHINE_IA32)
+#define EFI_IMAGE_MACHINE_IA32 0x014c
+#endif
+
+#if !defined(EFI_IMAGE_MACHINE_EBC)
+#define EFI_IMAGE_MACHINE_EBC 0x0EBC
+#endif
+
+// Image Entry prototype
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IMAGE_ENTRY_POINT) (
+ IN EFI_HANDLE ImageHandle,
+ IN struct _EFI_SYSTEM_TABLE *SystemTable
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IMAGE_LOAD) (
+ IN BOOLEAN BootPolicy,
+ IN EFI_HANDLE ParentImageHandle,
+ IN EFI_DEVICE_PATH *FilePath,
+ IN VOID *SourceBuffer OPTIONAL,
+ IN UINTN SourceSize,
+ OUT EFI_HANDLE *ImageHandle
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IMAGE_START) (
+ IN EFI_HANDLE ImageHandle,
+ OUT UINTN *ExitDataSize,
+ OUT CHAR16 **ExitData OPTIONAL
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_EXIT) (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_STATUS ExitStatus,
+ IN UINTN ExitDataSize,
+ IN CHAR16 *ExitData OPTIONAL
+ ) __dead2;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IMAGE_UNLOAD) (
+ IN EFI_HANDLE ImageHandle
+ );
+
+
+// Image handle
+#define LOADED_IMAGE_PROTOCOL \
+ { 0x5B1B31A1, 0x9562, 0x11d2, {0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B} }
+
+#define EFI_LOADED_IMAGE_INFORMATION_REVISION 0x1000
+typedef struct {
+ UINT32 Revision;
+ EFI_HANDLE ParentHandle;
+ struct _EFI_SYSTEM_TABLE *SystemTable;
+
+ // Source location of image
+ EFI_HANDLE DeviceHandle;
+ EFI_DEVICE_PATH *FilePath;
+ VOID *Reserved;
+
+ // Images load options
+ UINT32 LoadOptionsSize;
+ VOID *LoadOptions;
+
+ // Location of where image was loaded
+ VOID *ImageBase;
+ UINT64 ImageSize;
+ EFI_MEMORY_TYPE ImageCodeType;
+ EFI_MEMORY_TYPE ImageDataType;
+
+ // If the driver image supports a dynamic unload request
+ EFI_IMAGE_UNLOAD Unload;
+
+} EFI_LOADED_IMAGE;
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_EXIT_BOOT_SERVICES) (
+ IN EFI_HANDLE ImageHandle,
+ IN UINTN MapKey
+ );
+
+//
+// Misc
+//
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_STALL) (
+ IN UINTN Microseconds
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SET_WATCHDOG_TIMER) (
+ IN UINTN Timeout,
+ IN UINT64 WatchdogCode,
+ IN UINTN DataSize,
+ IN CHAR16 *WatchdogData OPTIONAL
+ );
+
+
+typedef enum {
+ EfiResetCold,
+ EfiResetWarm,
+ EfiResetShutdown
+} EFI_RESET_TYPE;
+
+typedef
+VOID
+(EFIAPI *EFI_RESET_SYSTEM) (
+ IN EFI_RESET_TYPE ResetType,
+ IN EFI_STATUS ResetStatus,
+ IN UINTN DataSize,
+ IN CHAR16 *ResetData OPTIONAL
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_GET_NEXT_MONOTONIC_COUNT) (
+ OUT UINT64 *Count
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_GET_NEXT_HIGH_MONO_COUNT) (
+ OUT UINT32 *HighCount
+ );
+
+//
+// Protocol handler functions
+//
+
+typedef enum {
+ EFI_NATIVE_INTERFACE
+} EFI_INTERFACE_TYPE;
+
+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
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_REINSTALL_PROTOCOL_INTERFACE) (
+ IN EFI_HANDLE Handle,
+ IN EFI_GUID *Protocol,
+ IN VOID *OldInterface,
+ IN VOID *NewInterface
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_UNINSTALL_PROTOCOL_INTERFACE) (
+ IN EFI_HANDLE Handle,
+ IN EFI_GUID *Protocol,
+ IN VOID *Interface
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_HANDLE_PROTOCOL) (
+ IN EFI_HANDLE Handle,
+ IN EFI_GUID *Protocol,
+ OUT VOID **Interface
+ );
+
+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;
+
+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
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LOCATE_DEVICE_PATH) (
+ IN EFI_GUID *Protocol,
+ IN OUT EFI_DEVICE_PATH **DevicePath,
+ OUT EFI_HANDLE *Device
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_INSTALL_CONFIGURATION_TABLE) (
+ IN EFI_GUID *Guid,
+ IN VOID *Table
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_RESERVED_SERVICE) (
+ VOID
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CONNECT_CONTROLLER) (
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE *DriverImageHandle OPTIONAL,
+ IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL,
+ IN BOOLEAN Recursive
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_DISCONNECT_CONTROLLER)(
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE DriverImageHandle, OPTIONAL
+ IN EFI_HANDLE ChildHandle OPTIONAL
+ );
+
+#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
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_OPEN_PROTOCOL) (
+ IN EFI_HANDLE Handle,
+ IN EFI_GUID *Protocol,
+ OUT VOID **Interface,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle, OPTIONAL
+ IN UINT32 Attributes
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CLOSE_PROTOCOL) (
+ IN EFI_HANDLE Handle,
+ IN EFI_GUID *Protocol,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE DeviceHandle
+ );
+
+typedef struct {
+ EFI_HANDLE AgentHandle;
+ EFI_HANDLE ControllerHandle;
+ UINT32 Attributes;
+ UINT32 OpenCount;
+} EFI_OPEN_PROTOCOL_INFORMATION_ENTRY;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_OPEN_PROTOCOL_INFORMATION) (
+ IN EFI_HANDLE UserHandle,
+ IN EFI_GUID *Protocol,
+ IN EFI_OPEN_PROTOCOL_INFORMATION_ENTRY **EntryBuffer,
+ OUT UINTN *EntryCount
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PROTOCOLS_PER_HANDLE) (
+ IN EFI_HANDLE UserHandle,
+ OUT EFI_GUID ***ProtocolBuffer,
+ OUT UINTN *ProtocolBufferCount
+ );
+
+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 *NumberHandles,
+ OUT EFI_HANDLE **Buffer
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LOCATE_PROTOCOL) (
+ EFI_GUID *Protocol,
+ VOID *Registration, OPTIONAL
+ VOID **Interface
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES) (
+ IN OUT EFI_HANDLE *Handle,
+ ...
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES) (
+ IN EFI_HANDLE Handle,
+ ...
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CALCULATE_CRC32) (
+ IN VOID *Data,
+ IN UINTN DataSize,
+ OUT UINT32 *Crc32
+ );
+
+typedef
+VOID
+(EFIAPI *EFI_COPY_MEM) (
+ IN VOID *Destination,
+ IN VOID *Source,
+ IN UINTN Length
+ );
+
+typedef
+VOID
+(EFIAPI *EFI_SET_MEM) (
+ IN VOID *Buffer,
+ IN UINTN Size,
+ IN UINT8 Value
+ );
+
+//
+// Standard EFI table header
+//
+
+typedef struct _EFI_TABLE_HEARDER {
+ UINT64 Signature;
+ UINT32 Revision;
+ UINT32 HeaderSize;
+ UINT32 CRC32;
+ UINT32 Reserved;
+} EFI_TABLE_HEADER;
+
+
+//
+// EFI Runtime Serivces Table
+//
+
+#define EFI_RUNTIME_SERVICES_SIGNATURE 0x56524553544e5552
+#define EFI_RUNTIME_SERVICES_REVISION ((EFI_SPECIFICATION_MAJOR_REVISION<<16) | (EFI_SPECIFICATION_MINOR_REVISION))
+
+typedef struct {
+ 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 serviers
+ //
+
+ EFI_GET_VARIABLE GetVariable;
+ EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName;
+ EFI_SET_VARIABLE SetVariable;
+
+ //
+ // Misc
+ //
+
+ EFI_GET_NEXT_HIGH_MONO_COUNT GetNextHighMonotonicCount;
+ EFI_RESET_SYSTEM ResetSystem;
+
+} EFI_RUNTIME_SERVICES;
+
+
+//
+// EFI Boot Services Table
+//
+
+#define EFI_BOOT_SERVICES_SIGNATURE 0x56524553544f4f42
+#define EFI_BOOT_SERVICES_REVISION ((EFI_SPECIFICATION_MAJOR_REVISION<<16) | (EFI_SPECIFICATION_MINOR_REVISION))
+
+typedef struct {
+
+ EFI_TABLE_HEADER Hdr;
+
+ //
+ // Task priority functions
+ //
+
+ EFI_RAISE_TPL RaiseTPL;
+ EFI_RESTORE_TPL RestoreTPL;
+
+ //
+ // Memory functions
+ //
+
+ EFI_ALLOCATE_PAGES AllocatePages;
+ EFI_FREE_PAGES FreePages;
+ EFI_GET_MEMORY_MAP GetMemoryMap;
+ EFI_ALLOCATE_POOL AllocatePool;
+ EFI_FREE_POOL FreePool;
+
+ //
+ // Event & timer functions
+ //
+
+ 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 functions
+ //
+
+ 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 functions
+ //
+
+ EFI_IMAGE_LOAD LoadImage;
+ EFI_IMAGE_START StartImage;
+ EFI_EXIT Exit;
+ EFI_IMAGE_UNLOAD UnloadImage;
+ EFI_EXIT_BOOT_SERVICES ExitBootServices;
+
+ //
+ // Misc functions
+ //
+
+ 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 to reduce size of drivers
+ //
+ 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;
+
+ //
+ // CRC32 services
+ //
+ EFI_CALCULATE_CRC32 CalculateCrc32;
+
+ //
+ // Memory Utility Services
+ //
+ EFI_COPY_MEM CopyMem;
+ EFI_SET_MEM SetMem;
+
+} EFI_BOOT_SERVICES;
+
+
+//
+// EFI Configuration Table and GUID definitions
+//
+
+#define MPS_TABLE_GUID \
+ { 0xeb9d2d2f, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
+
+#define ACPI_TABLE_GUID \
+ { 0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
+
+#define ACPI_20_TABLE_GUID \
+ { 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81} }
+
+#define SMBIOS_TABLE_GUID \
+ { 0xeb9d2d31, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
+
+#define SAL_SYSTEM_TABLE_GUID \
+ { 0xeb9d2d32, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
+
+#define FDT_TABLE_GUID \
+ { 0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0} }
+
+#define DXE_SERVICES_TABLE_GUID \
+ { 0x5ad34ba, 0x6f02, 0x4214, {0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9} }
+
+#define HOB_LIST_TABLE_GUID \
+ { 0x7739f24c, 0x93d7, 0x11d4, {0x9a, 0x3a, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
+
+#define MEMORY_TYPE_INFORMATION_TABLE_GUID \
+ { 0x4c19049f, 0x4137, 0x4dd3, {0x9c, 0x10, 0x8b, 0x97, 0xa8, 0x3f, 0xfd, 0xfa} }
+
+#define DEBUG_IMAGE_INFO_TABLE_GUID \
+ { 0x49152e77, 0x1ada, 0x4764, {0xb7, 0xa2, 0x7a, 0xfe, 0xfe, 0xd9, 0x5e, 0x8b} }
+
+typedef struct _EFI_CONFIGURATION_TABLE {
+ EFI_GUID VendorGuid;
+ VOID *VendorTable;
+} EFI_CONFIGURATION_TABLE;
+
+
+//
+// EFI System Table
+//
+
+
+
+
+#define EFI_SYSTEM_TABLE_SIGNATURE 0x5453595320494249
+#define EFI_SYSTEM_TABLE_REVISION ((EFI_SPECIFICATION_MAJOR_REVISION<<16) | (EFI_SPECIFICATION_MINOR_REVISION))
+#define EFI_1_10_SYSTEM_TABLE_REVISION ((1<<16) | 10)
+#define EFI_1_02_SYSTEM_TABLE_REVISION ((1<<16) | 02)
+
+typedef struct _EFI_SYSTEM_TABLE {
+ EFI_TABLE_HEADER Hdr;
+
+ CHAR16 *FirmwareVendor;
+ UINT32 FirmwareRevision;
+
+ EFI_HANDLE ConsoleInHandle;
+ SIMPLE_INPUT_INTERFACE *ConIn;
+
+ EFI_HANDLE ConsoleOutHandle;
+ SIMPLE_TEXT_OUTPUT_INTERFACE *ConOut;
+
+ EFI_HANDLE StandardErrorHandle;
+ SIMPLE_TEXT_OUTPUT_INTERFACE *StdErr;
+
+ EFI_RUNTIME_SERVICES *RuntimeServices;
+ EFI_BOOT_SERVICES *BootServices;
+
+ UINTN NumberOfTableEntries;
+ EFI_CONFIGURATION_TABLE *ConfigurationTable;
+
+} EFI_SYSTEM_TABLE;
+
+#endif
diff --git a/stand/efi/include/efichar.h b/stand/efi/include/efichar.h
new file mode 100644
index 0000000..2fa4ed4
--- /dev/null
+++ b/stand/efi/include/efichar.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2010 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOT_EFI_EFICHAR_H_
+#define _BOOT_EFI_EFICHAR_H_
+
+int ucs2_to_utf8(const efi_char *, char **);
+int utf8_to_ucs2(const char *, efi_char **, size_t *);
+int ucs2len(const efi_char *);
+
+#endif /* _BOOT_EFI_EFICHAR_H_ */
diff --git a/stand/efi/include/eficon.h b/stand/efi/include/eficon.h
new file mode 100644
index 0000000..2f719e7
--- /dev/null
+++ b/stand/efi/include/eficon.h
@@ -0,0 +1,309 @@
+/* $FreeBSD$ */
+#ifndef _EFI_CON_H
+#define _EFI_CON_H
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ eficon.h
+
+Abstract:
+
+ EFI console protocols
+
+
+
+Revision History
+
+--*/
+
+//
+// Text output protocol
+//
+
+#define SIMPLE_TEXT_OUTPUT_PROTOCOL \
+ { 0x387477c2, 0x69c7, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+
+INTERFACE_DECL(_SIMPLE_TEXT_OUTPUT_INTERFACE);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_RESET) (
+ IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ IN BOOLEAN ExtendedVerification
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_OUTPUT_STRING) (
+ IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ IN CHAR16 *String
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_TEST_STRING) (
+ IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ IN CHAR16 *String
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_QUERY_MODE) (
+ IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ IN UINTN ModeNumber,
+ OUT UINTN *Columns,
+ OUT UINTN *Rows
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_SET_MODE) (
+ IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ IN UINTN ModeNumber
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_SET_ATTRIBUTE) (
+ IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ IN UINTN Attribute
+ );
+
+#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)
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_CLEAR_SCREEN) (
+ IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_SET_CURSOR_POSITION) (
+ IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ IN UINTN Column,
+ IN UINTN Row
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_ENABLE_CURSOR) (
+ IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ IN BOOLEAN Enable
+ );
+
+typedef struct {
+ INT32 MaxMode;
+ // current settings
+ INT32 Mode;
+ INT32 Attribute;
+ INT32 CursorColumn;
+ INT32 CursorRow;
+ BOOLEAN CursorVisible;
+} SIMPLE_TEXT_OUTPUT_MODE;
+
+typedef struct _SIMPLE_TEXT_OUTPUT_INTERFACE {
+ EFI_TEXT_RESET Reset;
+
+ EFI_TEXT_OUTPUT_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;
+
+ // Current mode
+ SIMPLE_TEXT_OUTPUT_MODE *Mode;
+} SIMPLE_TEXT_OUTPUT_INTERFACE;
+
+//
+// Define's for required EFI Unicode Box Draw character
+//
+
+#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_UP 0x2191
+#define ARROW_DOWN 0x2193
+
+//
+// Text input protocol
+//
+
+#define SIMPLE_TEXT_INPUT_PROTOCOL \
+ { 0x387477c1, 0x69c7, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+
+INTERFACE_DECL(_SIMPLE_INPUT_INTERFACE);
+
+typedef struct {
+ UINT16 ScanCode;
+ CHAR16 UnicodeChar;
+} EFI_INPUT_KEY;
+
+//
+// Baseline unicode control chars
+//
+
+#define CHAR_NULL 0x0000
+#define CHAR_BACKSPACE 0x0008
+#define CHAR_TAB 0x0009
+#define CHAR_LINEFEED 0x000A
+#define CHAR_CARRIAGE_RETURN 0x000D
+
+//
+// Scan codes for base line keys
+//
+
+#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_ESC 0x0017
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_INPUT_RESET) (
+ IN struct _SIMPLE_INPUT_INTERFACE *This,
+ IN BOOLEAN ExtendedVerification
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_INPUT_READ_KEY) (
+ IN struct _SIMPLE_INPUT_INTERFACE *This,
+ OUT EFI_INPUT_KEY *Key
+ );
+
+typedef struct _SIMPLE_INPUT_INTERFACE {
+ EFI_INPUT_RESET Reset;
+ EFI_INPUT_READ_KEY ReadKeyStroke;
+ EFI_EVENT WaitForKey;
+} SIMPLE_INPUT_INTERFACE;
+
+#endif
diff --git a/stand/efi/include/eficonsctl.h b/stand/efi/include/eficonsctl.h
new file mode 100644
index 0000000..68be3d6
--- /dev/null
+++ b/stand/efi/include/eficonsctl.h
@@ -0,0 +1,134 @@
+/*-
+ * Copyright (c) 2004 - 2010, 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:
+ *
+ * 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 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 HOLDER 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.
+ */
+
+/*
+ * Original Module Name: ConsoleControl.h
+ * Abstract: Abstraction of a Text mode or GOP/UGA screen
+ */
+
+/* $FreeBSD$ */
+
+#ifndef _EFI_CONS_CTL_H
+#define _EFI_CONS_CTL_H
+
+#define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \
+ { 0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21} }
+
+typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL EFI_CONSOLE_CONTROL_PROTOCOL;
+
+
+typedef enum {
+ EfiConsoleControlScreenText,
+ EfiConsoleControlScreenGraphics,
+ EfiConsoleControlScreenMaxValue
+} EFI_CONSOLE_CONTROL_SCREEN_MODE;
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE) (
+ IN EFI_CONSOLE_CONTROL_PROTOCOL *This,
+ OUT EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode,
+ OUT BOOLEAN *GopUgaExists, OPTIONAL
+ OUT BOOLEAN *StdInLocked OPTIONAL
+ )
+/*++
+
+ Routine Description:
+ Return the current video mode information. Also returns info about existence
+ of Graphics Output devices or UGA Draw devices in system, and if the Std In
+ device is locked. All the arguments are optional and only returned if a non
+ NULL pointer is passed in.
+
+ Arguments:
+ This - Protocol instance pointer.
+ Mode - Are we in text of grahics mode.
+ GopUgaExists - TRUE if Console Spliter has found a GOP or UGA device
+ StdInLocked - TRUE if StdIn device is keyboard locked
+
+ Returns:
+ EFI_SUCCESS - Mode information returned.
+
+--*/
+;
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) (
+ IN EFI_CONSOLE_CONTROL_PROTOCOL *This,
+ IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
+ )
+/*++
+
+ Routine Description:
+ Set the current mode to either text or graphics. Graphics is
+ for Quiet Boot.
+
+ Arguments:
+ This - Protocol instance pointer.
+ Mode - Mode to set the
+
+ Returns:
+ EFI_SUCCESS - Mode information returned.
+
+--*/
+;
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN) (
+ IN EFI_CONSOLE_CONTROL_PROTOCOL *This,
+ IN CHAR16 *Password
+ )
+/*++
+
+ Routine Description:
+ Lock Std In devices until Password is typed.
+
+ Arguments:
+ This - Protocol instance pointer.
+ Password - Password needed to unlock screen. NULL means unlock keyboard
+
+ Returns:
+ EFI_SUCCESS - Mode information returned.
+ EFI_DEVICE_ERROR - Std In not locked
+
+--*/
+;
+
+
+
+struct _EFI_CONSOLE_CONTROL_PROTOCOL {
+ EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode;
+ EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
+ EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn;
+};
+
+extern EFI_GUID gEfiConsoleControlProtocolGuid;
+
+#endif
diff --git a/stand/efi/include/efidebug.h b/stand/efi/include/efidebug.h
new file mode 100644
index 0000000..5576d5f
--- /dev/null
+++ b/stand/efi/include/efidebug.h
@@ -0,0 +1,118 @@
+/* $FreeBSD$ */
+#ifndef _EFI_DEBUG_H
+#define _EFI_DEBUG_H
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efidebug.h
+
+Abstract:
+
+ EFI library debug functions
+
+
+
+Revision History
+
+--*/
+
+extern UINTN EFIDebug;
+
+#if EFI_DEBUG
+
+ #define DBGASSERT(a) DbgAssert(__FILE__, __LINE__, #a)
+ #define DEBUG(a) DbgPrint a
+
+#else
+
+ #define DBGASSERT(a)
+ #define DEBUG(a)
+
+#endif
+
+#if EFI_DEBUG_CLEAR_MEMORY
+
+ #define DBGSETMEM(a,l) SetMem(a,l,(CHAR8)BAD_POINTER)
+
+#else
+
+ #define DBGSETMEM(a,l)
+
+#endif
+
+#define D_INIT 0x00000001 // Initialization style messages
+#define D_WARN 0x00000002 // Warnings
+#define D_LOAD 0x00000004 // Load events
+#define D_FS 0x00000008 // EFI File system
+#define D_POOL 0x00000010 // Alloc & Free's
+#define D_PAGE 0x00000020 // Alloc & Free's
+#define D_INFO 0x00000040 // Verbose
+#define D_VARIABLE 0x00000100 // Variable
+#define D_VAR 0x00000100 // Variable
+#define D_BM 0x00000400 // Boot Manager
+#define D_BLKIO 0x00001000 // BlkIo Driver
+#define D_BLKIO_ULTRA 0x00002000 // BlkIo Driver
+#define D_NET 0x00004000 // SNI Driver
+#define D_NET_ULTRA 0x00008000 // SNI Driver
+#define D_UNDI 0x00010000 // UNDI Driver
+#define D_LOADFILE 0x00020000 // UNDI Driver
+#define D_EVENT 0x00080000 // Event messages
+
+#define D_ERROR 0x80000000 // Error
+
+#define D_RESERVED 0x7ff40A80 // Bits not reserved above
+
+//
+// Current Debug level of the system, value of EFIDebug
+//
+//#define EFI_DBUG_MASK (D_ERROR | D_WARN | D_LOAD | D_BLKIO | D_INIT)
+#define EFI_DBUG_MASK (D_ERROR)
+
+//
+//
+//
+
+#if EFI_DEBUG
+
+ #define ASSERT(a) if(!(a)) DBGASSERT(a)
+ #define ASSERT_LOCKED(l) if(!(l)->Lock) DBGASSERT(l not locked)
+ #define ASSERT_STRUCT(p,t) DBGASSERT(t not structure), p
+
+#else
+
+ #define ASSERT(a)
+ #define ASSERT_LOCKED(l)
+ #define ASSERT_STRUCT(p,t)
+
+#endif
+
+//
+// Prototypes
+//
+
+INTN
+DbgAssert (
+ CHAR8 *file,
+ INTN lineno,
+ CHAR8 *string
+ );
+
+INTN
+DbgPrint (
+ INTN mask,
+ CHAR8 *format,
+ ...
+ );
+
+#endif
diff --git a/stand/efi/include/efidef.h b/stand/efi/include/efidef.h
new file mode 100644
index 0000000..9cc5cdc
--- /dev/null
+++ b/stand/efi/include/efidef.h
@@ -0,0 +1,206 @@
+/* $FreeBSD$ */
+#ifndef _EFI_DEF_H
+#define _EFI_DEF_H
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efidef.h
+
+Abstract:
+
+ EFI definitions
+
+
+
+
+Revision History
+
+--*/
+
+typedef UINT16 CHAR16;
+typedef UINT8 CHAR8;
+#ifndef ACPI_THREAD_ID /* ACPI's definitions are fine */
+typedef UINT8 BOOLEAN;
+#endif
+
+#ifndef TRUE
+ #define TRUE ((BOOLEAN) 1)
+ #define FALSE ((BOOLEAN) 0)
+#endif
+
+#ifndef NULL
+ #define NULL ((VOID *) 0)
+#endif
+
+typedef UINTN EFI_STATUS;
+typedef UINT64 EFI_LBA;
+typedef UINTN EFI_TPL;
+typedef VOID *EFI_HANDLE;
+typedef VOID *EFI_EVENT;
+
+
+//
+// Prototype argument decoration for EFI parameters to indicate
+// their direction
+//
+// IN - argument is passed into the function
+// OUT - argument (pointer) is returned from the function
+// OPTIONAL - argument is optional
+//
+
+#ifndef IN
+ #define IN
+ #define OUT
+ #define OPTIONAL
+#endif
+
+
+//
+// A GUID
+//
+
+typedef struct {
+ UINT32 Data1;
+ UINT16 Data2;
+ UINT16 Data3;
+ UINT8 Data4[8];
+} EFI_GUID;
+
+
+//
+// Time
+//
+
+typedef struct {
+ UINT16 Year; // 1998 - 20XX
+ UINT8 Month; // 1 - 12
+ UINT8 Day; // 1 - 31
+ UINT8 Hour; // 0 - 23
+ UINT8 Minute; // 0 - 59
+ UINT8 Second; // 0 - 59
+ UINT8 Pad1;
+ UINT32 Nanosecond; // 0 - 999,999,999
+ INT16 TimeZone; // -1440 to 1440 or 2047
+ UINT8 Daylight;
+ UINT8 Pad2;
+} EFI_TIME;
+
+// 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
+
+
+
+//
+// Networking
+//
+
+typedef struct {
+ UINT8 Addr[4];
+} EFI_IPv4_ADDRESS;
+
+typedef struct {
+ UINT8 Addr[16];
+} EFI_IPv6_ADDRESS;
+
+typedef struct {
+ UINT8 Addr[32];
+} EFI_MAC_ADDRESS;
+
+//
+// Memory
+//
+
+typedef UINT64 EFI_PHYSICAL_ADDRESS;
+typedef UINT64 EFI_VIRTUAL_ADDRESS;
+
+typedef enum {
+ AllocateAnyPages,
+ AllocateMaxAddress,
+ AllocateAddress,
+ MaxAllocateType
+} EFI_ALLOCATE_TYPE;
+
+//Preseve the attr on any range supplied.
+//ConventialMemory must have WB,SR,SW when supplied.
+//When allocating from ConventialMemory always make it WB,SR,SW
+//When returning to ConventialMemory always make it WB,SR,SW
+//When getting the memory map, or on RT for runtime types
+
+
+typedef enum {
+ EfiReservedMemoryType,
+ EfiLoaderCode,
+ EfiLoaderData,
+ EfiBootServicesCode,
+ EfiBootServicesData,
+ EfiRuntimeServicesCode,
+ EfiRuntimeServicesData,
+ EfiConventionalMemory,
+ EfiUnusableMemory,
+ EfiACPIReclaimMemory,
+ EfiACPIMemoryNVS,
+ EfiMemoryMappedIO,
+ EfiMemoryMappedIOPortSpace,
+ EfiPalCode,
+ EfiMaxMemoryType
+} EFI_MEMORY_TYPE;
+
+// possible caching types for the memory range
+#define EFI_MEMORY_UC 0x0000000000000001
+#define EFI_MEMORY_WC 0x0000000000000002
+#define EFI_MEMORY_WT 0x0000000000000004
+#define EFI_MEMORY_WB 0x0000000000000008
+#define EFI_MEMORY_UCE 0x0000000000000010
+
+// physical memory protection on range
+#define EFI_MEMORY_WP 0x0000000000001000
+#define EFI_MEMORY_RP 0x0000000000002000
+#define EFI_MEMORY_XP 0x0000000000004000
+
+// range requires a runtime mapping
+#define EFI_MEMORY_RUNTIME 0x8000000000000000
+
+#define EFI_MEMORY_DESCRIPTOR_VERSION 1
+typedef struct {
+ UINT32 Type; // Field size is 32 bits followed by 32 bit pad
+ UINT32 Pad;
+ EFI_PHYSICAL_ADDRESS PhysicalStart; // Field size is 64 bits
+ EFI_VIRTUAL_ADDRESS VirtualStart; // Field size is 64 bits
+ UINT64 NumberOfPages; // Field size is 64 bits
+ UINT64 Attribute; // Field size is 64 bits
+} EFI_MEMORY_DESCRIPTOR;
+
+//
+// International Language
+//
+
+typedef UINT8 ISO_639_2;
+#define ISO_639_2_ENTRY_SIZE 3
+
+//
+//
+//
+
+#define EFI_PAGE_SIZE 4096
+#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) )
+
+#endif
diff --git a/stand/efi/include/efidevp.h b/stand/efi/include/efidevp.h
new file mode 100644
index 0000000..cda6b41
--- /dev/null
+++ b/stand/efi/include/efidevp.h
@@ -0,0 +1,454 @@
+/* $FreeBSD$ */
+#ifndef _DEVPATH_H
+#define _DEVPATH_H
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ devpath.h
+
+Abstract:
+
+ Defines for parsing the EFI Device Path structures
+
+
+
+Revision History
+
+--*/
+
+//
+// Device Path structures - Section C
+//
+
+typedef struct _EFI_DEVICE_PATH {
+ UINT8 Type;
+ UINT8 SubType;
+ UINT8 Length[2];
+} EFI_DEVICE_PATH;
+
+#define EFI_DP_TYPE_MASK 0x7F
+#define EFI_DP_TYPE_UNPACKED 0x80
+
+#define END_DEVICE_PATH_TYPE 0x7f
+
+#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff
+#define END_INSTANCE_DEVICE_PATH_SUBTYPE 0x01
+#define END_DEVICE_PATH_LENGTH (sizeof(EFI_DEVICE_PATH))
+
+
+#define DP_IS_END_TYPE(a)
+#define DP_IS_END_SUBTYPE(a) ( ((a)->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE )
+
+#define DevicePathType(a) ( ((a)->Type) & EFI_DP_TYPE_MASK )
+#define DevicePathSubType(a) ( (a)->SubType )
+#define DevicePathNodeLength(a) ((size_t)(((a)->Length[0]) | ((a)->Length[1] << 8)))
+#define NextDevicePathNode(a) ( (EFI_DEVICE_PATH *) ( ((UINT8 *) (a)) + DevicePathNodeLength(a)))
+#define IsDevicePathType(a, t) ( DevicePathType(a) == t )
+#define IsDevicePathEndType(a) IsDevicePathType(a, END_DEVICE_PATH_TYPE)
+#define IsDevicePathEndSubType(a) ( (a)->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE )
+#define IsDevicePathEnd(a) ( IsDevicePathEndType(a) && IsDevicePathEndSubType(a) )
+#define IsDevicePathUnpacked(a) ( (a)->Type & EFI_DP_TYPE_UNPACKED )
+
+
+#define SetDevicePathNodeLength(a,l) { \
+ (a)->Length[0] = (UINT8) (l); \
+ (a)->Length[1] = (UINT8) ((l) >> 8); \
+ }
+
+#define SetDevicePathEndNode(a) { \
+ (a)->Type = END_DEVICE_PATH_TYPE; \
+ (a)->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; \
+ (a)->Length[0] = sizeof(EFI_DEVICE_PATH); \
+ (a)->Length[1] = 0; \
+ }
+
+/*
+ *
+ */
+#define HARDWARE_DEVICE_PATH 0x01
+
+#define HW_PCI_DP 0x01
+typedef struct _PCI_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT8 Function;
+ UINT8 Device;
+} PCI_DEVICE_PATH;
+
+#define HW_PCCARD_DP 0x02
+typedef struct _PCCARD_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT8 FunctionNumber;
+} PCCARD_DEVICE_PATH;
+
+#define HW_MEMMAP_DP 0x03
+typedef struct _MEMMAP_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT32 MemoryType;
+ EFI_PHYSICAL_ADDRESS StartingAddress;
+ EFI_PHYSICAL_ADDRESS EndingAddress;
+} MEMMAP_DEVICE_PATH;
+
+#define HW_VENDOR_DP 0x04
+typedef struct _VENDOR_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ EFI_GUID Guid;
+} VENDOR_DEVICE_PATH;
+
+#define UNKNOWN_DEVICE_GUID \
+ { 0xcf31fac5, 0xc24e, 0x11d2, {0x85, 0xf3, 0x0, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b} }
+
+typedef struct _UKNOWN_DEVICE_VENDOR_DP {
+ VENDOR_DEVICE_PATH DevicePath;
+ UINT8 LegacyDriveLetter;
+} UNKNOWN_DEVICE_VENDOR_DEVICE_PATH;
+
+#define HW_CONTROLLER_DP 0x05
+typedef struct _CONTROLLER_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT32 Controller;
+} CONTROLLER_DEVICE_PATH;
+
+/*
+ *
+ */
+#define ACPI_DEVICE_PATH 0x02
+
+#define ACPI_DP 0x01
+typedef struct _ACPI_HID_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT32 HID;
+ UINT32 UID;
+} ACPI_HID_DEVICE_PATH;
+
+#define ACPI_EXTENDED_DP 0x02
+typedef struct _ACPI_EXTENDED_HID_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT32 HID;
+ UINT32 UID;
+ UINT32 CID;
+} 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 MESSAGING_DEVICE_PATH 0x03
+
+#define MSG_ATAPI_DP 0x01
+typedef struct _ATAPI_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT8 PrimarySecondary;
+ UINT8 SlaveMaster;
+ UINT16 Lun;
+} ATAPI_DEVICE_PATH;
+
+#define MSG_SCSI_DP 0x02
+typedef struct _SCSI_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT16 Pun;
+ UINT16 Lun;
+} SCSI_DEVICE_PATH;
+
+#define MSG_FIBRECHANNEL_DP 0x03
+typedef struct _FIBRECHANNEL_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT32 Reserved;
+ UINT64 WWN;
+ UINT64 Lun;
+} FIBRECHANNEL_DEVICE_PATH;
+
+#define MSG_1394_DP 0x04
+typedef struct _F1394_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT32 Reserved;
+ UINT64 Guid;
+} F1394_DEVICE_PATH;
+
+#define MSG_USB_DP 0x05
+typedef struct _USB_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT8 ParentPortNumber;
+ UINT8 InterfaceNumber;
+} USB_DEVICE_PATH;
+
+#define MSG_USB_CLASS_DP 0x0F
+typedef struct _USB_CLASS_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT16 VendorId;
+ UINT16 ProductId;
+ UINT8 DeviceClass;
+ UINT8 DeviceSubClass;
+ UINT8 DeviceProtocol;
+} USB_CLASS_DEVICE_PATH;
+
+#define MSG_I2O_DP 0x06
+typedef struct _I2O_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT32 Tid;
+} I2O_DEVICE_PATH;
+
+#define MSG_MAC_ADDR_DP 0x0b
+typedef struct _MAC_ADDR_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ EFI_MAC_ADDRESS MacAddress;
+ UINT8 IfType;
+} MAC_ADDR_DEVICE_PATH;
+
+#define MSG_IPv4_DP 0x0c
+typedef struct _IPv4_DEVICE_PATH {
+ EFI_DEVICE_PATH 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 _IPv6_DEVICE_PATH {
+ EFI_DEVICE_PATH 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 _INFINIBAND_DEVICE_PATH {
+ EFI_DEVICE_PATH 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 _UART_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT32 Reserved;
+ UINT64 BaudRate;
+ UINT8 DataBits;
+ UINT8 Parity;
+ UINT8 StopBits;
+} UART_DEVICE_PATH;
+
+#define MSG_VENDOR_DP 0x0A
+/* Use VENDOR_DEVICE_PATH struct */
+
+#define DEVICE_PATH_MESSAGING_PC_ANSI \
+ { 0xe0c14753, 0xf9be, 0x11d2, {0x9a, 0x0c, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
+
+#define DEVICE_PATH_MESSAGING_VT_100 \
+ { 0xdfa66065, 0xb419, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
+
+#define DEVICE_PATH_MESSAGING_VT_100_PLUS \
+ { 0x7baec70b, 0x57e0, 0x4c76, {0x8e, 0x87, 0x2f, 0x9e, 0x28, 0x08, 0x83, 0x43} }
+
+#define DEVICE_PATH_MESSAGING_VT_UTF8 \
+ { 0xad15a0d6, 0x8bec, 0x4acf, {0xa0, 0x73, 0xd0, 0x1d, 0xe7, 0x7e, 0x2d, 0x88} }
+
+#define MSG_SATA_DP 0x12
+typedef struct _SATA_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT16 HBAPortNumber;
+ UINT16 PortMultiplierPortNumber;
+ UINT16 Lun;
+} SATA_DEVICE_PATH;
+
+#define MEDIA_DEVICE_PATH 0x04
+
+#define MEDIA_HARDDRIVE_DP 0x01
+typedef struct _HARDDRIVE_DEVICE_PATH {
+ EFI_DEVICE_PATH 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 _CDROM_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ UINT32 BootEntry;
+ UINT64 PartitionStart;
+ UINT64 PartitionSize;
+} CDROM_DEVICE_PATH;
+
+#define MEDIA_VENDOR_DP 0x03
+/* Use VENDOR_DEVICE_PATH struct */
+
+#define MEDIA_FILEPATH_DP 0x04
+typedef struct _FILEPATH_DEVICE_PATH {
+ EFI_DEVICE_PATH 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 _MEDIA_PROTOCOL_DEVICE_PATH {
+ EFI_DEVICE_PATH Header;
+ EFI_GUID Protocol;
+} MEDIA_PROTOCOL_DEVICE_PATH;
+
+
+#define BBS_DEVICE_PATH 0x05
+#define BBS_BBS_DP 0x01
+typedef struct _BBS_BBS_DEVICE_PATH {
+ EFI_DEVICE_PATH 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_DEV 0x80
+#define BBS_TYPE_UNKNOWN 0xFF
+
+typedef union {
+ EFI_DEVICE_PATH DevPath;
+ PCI_DEVICE_PATH Pci;
+ PCCARD_DEVICE_PATH PcCard;
+ MEMMAP_DEVICE_PATH MemMap;
+ VENDOR_DEVICE_PATH Vendor;
+ UNKNOWN_DEVICE_VENDOR_DEVICE_PATH UnknownVendor;
+ CONTROLLER_DEVICE_PATH Controller;
+ ACPI_HID_DEVICE_PATH Acpi;
+
+ ATAPI_DEVICE_PATH Atapi;
+ SCSI_DEVICE_PATH Scsi;
+ FIBRECHANNEL_DEVICE_PATH FibreChannel;
+
+ F1394_DEVICE_PATH F1394;
+ USB_DEVICE_PATH Usb;
+ 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 *DevPath;
+ PCI_DEVICE_PATH *Pci;
+ PCCARD_DEVICE_PATH *PcCard;
+ MEMMAP_DEVICE_PATH *MemMap;
+ VENDOR_DEVICE_PATH *Vendor;
+ UNKNOWN_DEVICE_VENDOR_DEVICE_PATH *UnknownVendor;
+ 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;
+ 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;
+
+ FILEPATH_DEVICE_PATH *FilePath;
+ MEDIA_PROTOCOL_DEVICE_PATH *MediaProtocol;
+
+ CDROM_DEVICE_PATH *CD;
+ BBS_BBS_DEVICE_PATH *Bbs;
+
+} EFI_DEV_PATH_PTR;
+
+#define EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID \
+ { 0xbc62157e, 0x3e33, 0x4fec, { 0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf } }
+
+#define EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID \
+ { 0x8b843e20, 0x8132, 0x4852, { 0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c } }
+
+INTERFACE_DECL(_EFI_DEVICE_PATH_PROTOCOL);
+
+typedef
+CHAR16*
+(EFIAPI *EFI_DEVICE_PATH_TO_TEXT_NODE) (
+ IN struct _EFI_DEVICE_PATH *This,
+ IN BOOLEAN DisplayOnly,
+ IN BOOLEAN AllowShortCuts
+ );
+
+typedef
+CHAR16*
+(EFIAPI *EFI_DEVICE_PATH_TO_TEXT_PATH) (
+ IN struct _EFI_DEVICE_PATH *This,
+ IN BOOLEAN DisplayOnly,
+ IN BOOLEAN AllowShortCuts
+ );
+
+typedef struct _EFI_DEVICE_PATH_TO_TEXT_PROTOCOL {
+ EFI_DEVICE_PATH_TO_TEXT_NODE ConvertDeviceNodeToText;
+ EFI_DEVICE_PATH_TO_TEXT_PATH ConvertDevicePathToText;
+} EFI_DEVICE_PATH_TO_TEXT_PROTOCOL;
+
+#endif
diff --git a/stand/efi/include/efierr.h b/stand/efi/include/efierr.h
new file mode 100644
index 0000000..a8b6557
--- /dev/null
+++ b/stand/efi/include/efierr.h
@@ -0,0 +1,68 @@
+/* $FreeBSD$ */
+#ifndef _EFI_ERR_H
+#define _EFI_ERR_H
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efierr.h
+
+Abstract:
+
+ EFI error codes
+
+
+
+
+Revision History
+
+--*/
+
+
+#define EFIWARN(a) (a)
+#define EFI_ERROR(a) (((INTN) a) < 0)
+#define EFI_ERROR_CODE(a) (unsigned long)(a & ~EFI_ERROR_MASK)
+
+
+#define EFI_SUCCESS 0
+#define EFI_LOAD_ERROR EFIERR(1)
+#define EFI_INVALID_PARAMETER EFIERR(2)
+#define EFI_UNSUPPORTED EFIERR(3)
+#define EFI_BAD_BUFFER_SIZE EFIERR(4)
+#define EFI_BUFFER_TOO_SMALL EFIERR(5)
+#define EFI_NOT_READY EFIERR(6)
+#define EFI_DEVICE_ERROR EFIERR(7)
+#define EFI_WRITE_PROTECTED EFIERR(8)
+#define EFI_OUT_OF_RESOURCES EFIERR(9)
+#define EFI_VOLUME_CORRUPTED EFIERR(10)
+#define EFI_VOLUME_FULL EFIERR(11)
+#define EFI_NO_MEDIA EFIERR(12)
+#define EFI_MEDIA_CHANGED EFIERR(13)
+#define EFI_NOT_FOUND EFIERR(14)
+#define EFI_ACCESS_DENIED EFIERR(15)
+#define EFI_NO_RESPONSE EFIERR(16)
+#define EFI_NO_MAPPING EFIERR(17)
+#define EFI_TIMEOUT EFIERR(18)
+#define EFI_NOT_STARTED EFIERR(19)
+#define EFI_ALREADY_STARTED EFIERR(20)
+#define EFI_ABORTED EFIERR(21)
+#define EFI_ICMP_ERROR EFIERR(22)
+#define EFI_TFTP_ERROR EFIERR(23)
+#define EFI_PROTOCOL_ERROR EFIERR(24)
+
+#define EFI_WARN_UNKNOWN_GLYPH EFIWARN(1)
+#define EFI_WARN_DELETE_FAILURE EFIWARN(2)
+#define EFI_WARN_WRITE_FAILURE EFIWARN(3)
+#define EFI_WARN_BUFFER_TOO_SMALL EFIWARN(4)
+
+#endif
diff --git a/stand/efi/include/efifpswa.h b/stand/efi/include/efifpswa.h
new file mode 100644
index 0000000..21823c5
--- /dev/null
+++ b/stand/efi/include/efifpswa.h
@@ -0,0 +1,40 @@
+/* $FreeBSD$ */
+#ifndef _EFI_FPSWA_H
+#define _EFI_FPSWA_H
+
+/*
+ * EFI FP SWA Driver (Floating Point Software Assist)
+ */
+
+#define EFI_INTEL_FPSWA \
+ { 0xc41b6531, 0x97b9, 0x11d3, {0x9a, 0x29, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
+
+INTERFACE_DECL(_FPSWA_INTERFACE);
+
+typedef struct _FPSWA_RET {
+ UINT64 status;
+ UINT64 err1;
+ UINT64 err2;
+ UINT64 err3;
+} FPSWA_RET;
+
+typedef
+FPSWA_RET
+(EFIAPI *EFI_FPSWA) (
+ IN UINTN TrapType,
+ IN OUT VOID *Bundle,
+ IN OUT UINT64 *pipsr,
+ IN OUT UINT64 *pfsr,
+ IN OUT UINT64 *pisr,
+ IN OUT UINT64 *ppreds,
+ IN OUT UINT64 *pifs,
+ IN OUT VOID *fp_state
+ );
+
+typedef struct _FPSWA_INTERFACE {
+ UINT32 Revision;
+ UINT32 Reserved;
+ EFI_FPSWA Fpswa;
+} FPSWA_INTERFACE;
+
+#endif
diff --git a/stand/efi/include/efifs.h b/stand/efi/include/efifs.h
new file mode 100644
index 0000000..58febb6
--- /dev/null
+++ b/stand/efi/include/efifs.h
@@ -0,0 +1,123 @@
+/* $FreeBSD$ */
+#ifndef _EFI_FS_H
+#define _EFI_FS_H
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efifs.h
+
+Abstract:
+
+ EFI File System structures
+
+
+
+Revision History
+
+--*/
+
+
+//
+// EFI Partition header (normaly starts in LBA 1)
+//
+
+#define EFI_PARTITION_SIGNATURE 0x5053595320494249
+#define EFI_PARTITION_REVISION 0x00010001
+#define MIN_EFI_PARTITION_BLOCK_SIZE 512
+#define EFI_PARTITION_LBA 1
+
+typedef struct _EFI_PARTITION_HEADER {
+ EFI_TABLE_HEADER Hdr;
+ UINT32 DirectoryAllocationNumber;
+ UINT32 BlockSize;
+ EFI_LBA FirstUsableLba;
+ EFI_LBA LastUsableLba;
+ EFI_LBA UnusableSpace;
+ EFI_LBA FreeSpace;
+ EFI_LBA RootFile;
+ EFI_LBA SecutiryFile;
+} EFI_PARTITION_HEADER;
+
+
+//
+// File header
+//
+
+#define EFI_FILE_HEADER_SIGNATURE 0x454c494620494249
+#define EFI_FILE_HEADER_REVISION 0x00010000
+#define EFI_FILE_STRING_SIZE 260
+
+typedef struct _EFI_FILE_HEADER {
+ EFI_TABLE_HEADER Hdr;
+ UINT32 Class;
+ UINT32 LBALOffset;
+ EFI_LBA Parent;
+ UINT64 FileSize;
+ UINT64 FileAttributes;
+ EFI_TIME FileCreateTime;
+ EFI_TIME FileModificationTime;
+ EFI_GUID VendorGuid;
+ CHAR16 FileString[EFI_FILE_STRING_SIZE];
+} EFI_FILE_HEADER;
+
+
+//
+// Return the file's first LBAL which is in the same
+// logical block as the file header
+//
+
+#define EFI_FILE_LBAL(a) ((EFI_LBAL *) (((CHAR8 *) (a)) + (a)->LBALOffset))
+
+#define EFI_FILE_CLASS_FREE_SPACE 1
+#define EFI_FILE_CLASS_EMPTY 2
+#define EFI_FILE_CLASS_NORMAL 3
+
+
+//
+// Logical Block Address List - the fundemental block
+// description structure
+//
+
+#define EFI_LBAL_SIGNATURE 0x4c41424c20494249
+#define EFI_LBAL_REVISION 0x00010000
+
+typedef struct _EFI_LBAL {
+ EFI_TABLE_HEADER Hdr;
+ UINT32 Class;
+ EFI_LBA Parent;
+ EFI_LBA Next;
+ UINT32 ArraySize;
+ UINT32 ArrayCount;
+} EFI_LBAL;
+
+// Array size
+#define EFI_LBAL_ARRAY_SIZE(lbal,offs,blks) \
+ (((blks) - (offs) - (lbal)->Hdr.HeaderSize) / sizeof(EFI_RL))
+
+//
+// Logical Block run-length
+//
+
+typedef struct {
+ EFI_LBA Start;
+ UINT64 Length;
+} EFI_RL;
+
+//
+// Return the run-length structure from an LBAL header
+//
+
+#define EFI_LBAL_RL(a) ((EFI_RL*) (((CHAR8 *) (a)) + (a)->Hdr.HeaderSize))
+
+#endif
diff --git a/stand/efi/include/efigop.h b/stand/efi/include/efigop.h
new file mode 100644
index 0000000..104fa6e
--- /dev/null
+++ b/stand/efi/include/efigop.h
@@ -0,0 +1,121 @@
+/* $FreeBSD$ */
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efigop.h
+
+Abstract:
+ Info about framebuffers
+
+
+
+
+Revision History
+
+--*/
+
+#ifndef _EFIGOP_H
+#define _EFIGOP_H
+
+#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
+ { 0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a} }
+
+INTERFACE_DECL(_EFI_GRAPHICS_OUTPUT);
+
+typedef struct {
+ UINT32 RedMask;
+ UINT32 GreenMask;
+ UINT32 BlueMask;
+ UINT32 ReservedMask;
+} EFI_PIXEL_BITMASK;
+
+typedef enum {
+ PixelRedGreenBlueReserved8BitPerColor,
+ PixelBlueGreenRedReserved8BitPerColor,
+ PixelBitMask,
+ PixelBltOnly,
+ PixelFormatMax,
+} EFI_GRAPHICS_PIXEL_FORMAT;
+
+typedef struct {
+ UINT32 Version;
+ UINT32 HorizontalResolution;
+ UINT32 VerticalResolution;
+ EFI_GRAPHICS_PIXEL_FORMAT PixelFormat;
+ EFI_PIXEL_BITMASK PixelInformation;
+ UINT32 PixelsPerScanLine;
+} EFI_GRAPHICS_OUTPUT_MODE_INFORMATION;
+
+typedef struct {
+ UINT32 MaxMode;
+ UINT32 Mode;
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
+ UINTN SizeOfInfo;
+ EFI_PHYSICAL_ADDRESS FrameBufferBase;
+ UINTN FrameBufferSize;
+} EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE) (
+ IN struct _EFI_GRAPHICS_OUTPUT *This,
+ IN UINT32 ModeNumber,
+ OUT UINTN *SizeOfInfo,
+ OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE) (
+ IN struct _EFI_GRAPHICS_OUTPUT *This,
+ IN UINT32 ModeNumber
+ );
+
+typedef struct {
+ UINT8 Blue;
+ UINT8 Green;
+ UINT8 Red;
+ UINT8 Reserved;
+} EFI_GRAPHICS_OUTPUT_BLT_PIXEL;
+
+typedef enum {
+ EfiBltVideoFill,
+ EfiBltVideoToBltBuffer,
+ EfiBltBufferToVideo,
+ EfiBltVideoToVideo,
+ EfiGraphcisOutputBltOperationMax,
+} EFI_GRAPHICS_OUTPUT_BLT_OPERATION;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT) (
+ IN struct _EFI_GRAPHICS_OUTPUT *This,
+ IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer,
+ IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
+ IN UINTN SourceX,
+ IN UINTN SourceY,
+ IN UINTN DestinationX,
+ IN UINTN DestinationY,
+ IN UINTN Width,
+ IN UINTN Height,
+ IN UINTN Delta
+ );
+
+typedef struct _EFI_GRAPHICS_OUTPUT {
+ EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE QueryMode;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE SetMode;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT Blt;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode;
+} EFI_GRAPHICS_OUTPUT;
+
+#endif /* _EFIGOP_H */
diff --git a/stand/efi/include/efilib.h b/stand/efi/include/efilib.h
new file mode 100644
index 0000000..8721bc5
--- /dev/null
+++ b/stand/efi/include/efilib.h
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 2000 Doug Rabson
+ * Copyright (c) 2006 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LOADER_EFILIB_H
+#define _LOADER_EFILIB_H
+
+#include <stand.h>
+#include <stdbool.h>
+#include <sys/queue.h>
+
+extern EFI_HANDLE IH;
+extern EFI_SYSTEM_TABLE *ST;
+extern EFI_BOOT_SERVICES *BS;
+extern EFI_RUNTIME_SERVICES *RS;
+
+extern struct devsw efipart_fddev;
+extern struct devsw efipart_cddev;
+extern struct devsw efipart_hddev;
+extern struct devsw efinet_dev;
+extern struct netif_driver efinetif;
+
+/* EFI block device data, included here to help efi_zfs_probe() */
+typedef STAILQ_HEAD(pdinfo_list, pdinfo) pdinfo_list_t;
+
+typedef struct pdinfo
+{
+ STAILQ_ENTRY(pdinfo) pd_link; /* link in device list */
+ pdinfo_list_t pd_part; /* list of partitions */
+ EFI_HANDLE pd_handle;
+ EFI_HANDLE pd_alias;
+ EFI_DEVICE_PATH *pd_devpath;
+ EFI_BLOCK_IO *pd_blkio;
+ uint32_t pd_unit; /* unit number */
+ uint32_t pd_open; /* reference counter */
+ void *pd_bcache; /* buffer cache data */
+} pdinfo_t;
+
+pdinfo_list_t *efiblk_get_pdinfo_list(struct devsw *dev);
+pdinfo_t *efiblk_get_pdinfo(struct devdesc *dev);
+
+void *efi_get_table(EFI_GUID *tbl);
+
+int efi_getdev(void **vdev, const char *devspec, const char **path);
+char *efi_fmtdev(void *vdev);
+int efi_setcurrdev(struct env_var *ev, int flags, const void *value);
+
+
+int efi_register_handles(struct devsw *, EFI_HANDLE *, EFI_HANDLE *, int);
+EFI_HANDLE efi_find_handle(struct devsw *, int);
+int efi_handle_lookup(EFI_HANDLE, struct devsw **, int *, uint64_t *);
+int efi_handle_update_dev(EFI_HANDLE, struct devsw *, int, uint64_t);
+
+EFI_DEVICE_PATH *efi_lookup_image_devpath(EFI_HANDLE);
+EFI_DEVICE_PATH *efi_lookup_devpath(EFI_HANDLE);
+EFI_HANDLE efi_devpath_handle(EFI_DEVICE_PATH *);
+EFI_DEVICE_PATH *efi_devpath_last_node(EFI_DEVICE_PATH *);
+EFI_DEVICE_PATH *efi_devpath_trim(EFI_DEVICE_PATH *);
+bool efi_devpath_match(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *);
+bool efi_devpath_is_prefix(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *);
+CHAR16 *efi_devpath_name(EFI_DEVICE_PATH *);
+void efi_free_devpath_name(CHAR16 *);
+
+int efi_status_to_errno(EFI_STATUS);
+EFI_STATUS errno_to_efi_status(int errno);
+
+void efi_time_init(void);
+void efi_time_fini(void);
+
+EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab);
+
+EFI_STATUS main(int argc, CHAR16 *argv[]);
+void efi_exit(EFI_STATUS status) __dead2;
+void delay(int usecs);
+
+/* EFI environment initialization. */
+void efi_init_environment(void);
+
+/* CHAR16 utility functions. */
+int wcscmp(CHAR16 *, CHAR16 *);
+void cpy8to16(const char *, CHAR16 *, size_t);
+void cpy16to8(const CHAR16 *, char *, size_t);
+
+#endif /* _LOADER_EFILIB_H */
diff --git a/stand/efi/include/efinet.h b/stand/efi/include/efinet.h
new file mode 100644
index 0000000..3ac58b2
--- /dev/null
+++ b/stand/efi/include/efinet.h
@@ -0,0 +1,348 @@
+/* $FreeBSD$ */
+#ifndef _EFINET_H
+#define _EFINET_H
+
+
+/*++
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+ efinet.h
+
+Abstract:
+ EFI Simple Network protocol
+
+Revision History
+--*/
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Simple Network Protocol
+//
+
+#define EFI_SIMPLE_NETWORK_PROTOCOL \
+ { 0xA19832B9, 0xAC25, 0x11D3, {0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D} }
+
+
+INTERFACE_DECL(_EFI_SIMPLE_NETWORK);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+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;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_START) (
+ IN struct _EFI_SIMPLE_NETWORK *This
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_STOP) (
+ IN struct _EFI_SIMPLE_NETWORK *This
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_INITIALIZE) (
+ IN struct _EFI_SIMPLE_NETWORK *This,
+ IN UINTN ExtraRxBufferSize OPTIONAL,
+ IN UINTN ExtraTxBufferSize OPTIONAL
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_RESET) (
+ IN struct _EFI_SIMPLE_NETWORK *This,
+ IN BOOLEAN ExtendedVerification
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_SHUTDOWN) (
+ IN struct _EFI_SIMPLE_NETWORK *This
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_RECEIVE_FILTERS) (
+ IN struct _EFI_SIMPLE_NETWORK *This,
+ IN UINT32 Enable,
+ IN UINT32 Disable,
+ IN BOOLEAN ResetMCastFilter,
+ IN UINTN MCastFilterCnt OPTIONAL,
+ IN EFI_MAC_ADDRESS *MCastFilter OPTIONAL
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_STATION_ADDRESS) (
+ IN struct _EFI_SIMPLE_NETWORK *This,
+ IN BOOLEAN Reset,
+ IN EFI_MAC_ADDRESS *New OPTIONAL
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_STATISTICS) (
+ IN struct _EFI_SIMPLE_NETWORK *This,
+ IN BOOLEAN Reset,
+ IN OUT UINTN *StatisticsSize OPTIONAL,
+ OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_MCAST_IP_TO_MAC) (
+ IN struct _EFI_SIMPLE_NETWORK *This,
+ IN BOOLEAN IPv6,
+ IN EFI_IP_ADDRESS *IP,
+ OUT EFI_MAC_ADDRESS *MAC
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_NVDATA) (
+ IN struct _EFI_SIMPLE_NETWORK *This,
+ IN BOOLEAN ReadWrite,
+ IN UINTN Offset,
+ IN UINTN BufferSize,
+ IN OUT VOID *Buffer
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_GET_STATUS) (
+ IN struct _EFI_SIMPLE_NETWORK *This,
+ OUT UINT32 *InterruptStatus OPTIONAL,
+ OUT VOID **TxBuf OPTIONAL
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_TRANSMIT) (
+ IN struct _EFI_SIMPLE_NETWORK *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
+);
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SIMPLE_NETWORK_RECEIVE) (
+ IN struct _EFI_SIMPLE_NETWORK *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_INTERFACE_REVISION 0x00010000
+
+typedef struct _EFI_SIMPLE_NETWORK {
+ 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;
+ EFI_EVENT WaitForPacket;
+ EFI_SIMPLE_NETWORK_MODE *Mode;
+} EFI_SIMPLE_NETWORK;
+
+#endif /* _EFINET_H */
diff --git a/stand/efi/include/efipart.h b/stand/efi/include/efipart.h
new file mode 100644
index 0000000..ef1a870
--- /dev/null
+++ b/stand/efi/include/efipart.h
@@ -0,0 +1,69 @@
+/* $FreeBSD$ */
+#ifndef _EFI_PART_H
+#define _EFI_PART_H
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efipart.h
+
+Abstract:
+ Info about disk partitions and Master Boot Records
+
+
+
+
+Revision History
+
+--*/
+
+//
+//
+//
+
+#define EFI_PARTITION 0xef
+#define MBR_SIZE 512
+
+#pragma pack(1)
+
+typedef struct {
+ UINT8 BootIndicator;
+ UINT8 StartHead;
+ UINT8 StartSector;
+ UINT8 StartTrack;
+ UINT8 OSIndicator;
+ UINT8 EndHead;
+ UINT8 EndSector;
+ UINT8 EndTrack;
+ UINT8 StartingLBA[4];
+ UINT8 SizeInLBA[4];
+} MBR_PARTITION_RECORD;
+
+#define EXTRACT_UINT32(D) (UINT32)(D[0] | (D[1] << 8) | (D[2] << 16) | (D[3] << 24))
+
+#define MBR_SIGNATURE 0xaa55
+#define MIN_MBR_DEVICE_SIZE 0x80000
+#define MBR_ERRATA_PAD 0x40000 // 128 MB
+
+#define MAX_MBR_PARTITIONS 4
+typedef struct {
+ UINT8 BootStrapCode[440];
+ UINT8 UniqueMbrSignature[4];
+ UINT8 Unknown[2];
+ MBR_PARTITION_RECORD Partition[MAX_MBR_PARTITIONS];
+ UINT16 Signature;
+} MASTER_BOOT_RECORD;
+#pragma pack()
+
+
+#endif
diff --git a/stand/efi/include/efipciio.h b/stand/efi/include/efipciio.h
new file mode 100644
index 0000000..b00d6ec
--- /dev/null
+++ b/stand/efi/include/efipciio.h
@@ -0,0 +1,557 @@
+/* $FreeBSD$ */
+/** @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 - 2010, Intel Corporation. All rights reserved.<BR>
+ 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;
+
+///
+/// *******************************************************
+/// EFI_PCI_IO_PROTOCOL_WIDTH
+/// *******************************************************
+///
+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 writes 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 0x40000 ///< 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 {
+ ///
+ /// A read operation from system memory by a bus master.
+ ///
+ EfiPciIoOperationBusMasterRead,
+ ///
+ /// A write operation from system memory by a bus master.
+ ///
+ EfiPciIoOperationBusMasterWrite,
+ ///
+ /// Provides both read and write access to system memory by both the processor and a
+ /// bus master. The buffer is coherent from both the processor's and the bus master's point of view.
+ ///
+ EfiPciIoOperationBusMasterCommonBuffer,
+ EfiPciIoOperationMaximum
+} EFI_PCI_IO_PROTOCOL_OPERATION;
+
+///
+/// *******************************************************
+/// EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION
+/// *******************************************************
+///
+typedef enum {
+ ///
+ /// Retrieve the PCI controller's current attributes, and return them in Result.
+ ///
+ EfiPciIoAttributeOperationGet,
+ ///
+ /// Set the PCI controller's current attributes to Attributes.
+ ///
+ EfiPciIoAttributeOperationSet,
+ ///
+ /// Enable the attributes specified by the bits that are set in Attributes for this PCI controller.
+ ///
+ EfiPciIoAttributeOperationEnable,
+ ///
+ /// Disable the attributes specified by the bits that are set in Attributes for this PCI controller.
+ ///
+ EfiPciIoAttributeOperationDisable,
+ ///
+ /// Retrieve the PCI controller's supported attributes, and return them in Result.
+ ///
+ EfiPciIoAttributeOperationSupported,
+ EfiPciIoAttributeOperationMaximum
+} EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION;
+
+/**
+ Reads from the memory space of a PCI controller. Returns either when 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 {
+ ///
+ /// Read PCI controller registers in the PCI memory or I/O space.
+ ///
+ EFI_PCI_IO_PROTOCOL_IO_MEM Read;
+ ///
+ /// Write PCI controller registers in the PCI memory or I/O space.
+ ///
+ 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 {
+ ///
+ /// Read PCI controller registers in PCI configuration space.
+ ///
+ EFI_PCI_IO_PROTOCOL_CONFIG Read;
+ ///
+ /// Write PCI controller registers in PCI configuration space.
+ ///
+ 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-specific 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 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/stand/efi/include/efiprot.h b/stand/efi/include/efiprot.h
new file mode 100644
index 0000000..351b2d3
--- /dev/null
+++ b/stand/efi/include/efiprot.h
@@ -0,0 +1,636 @@
+/* $FreeBSD$ */
+#ifndef _EFI_PROT_H
+#define _EFI_PROT_H
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efiprot.h
+
+Abstract:
+
+ EFI Protocols
+
+
+
+Revision History
+
+--*/
+
+#include <efidef.h>
+
+//
+// Device Path protocol
+//
+
+#define DEVICE_PATH_PROTOCOL \
+ { 0x9576e91, 0x6d3f, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+
+
+//
+// Block IO protocol
+//
+
+#define BLOCK_IO_PROTOCOL \
+ { 0x964e5b21, 0x6459, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+#define EFI_BLOCK_IO_INTERFACE_REVISION 0x00010000
+
+INTERFACE_DECL(_EFI_BLOCK_IO);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_BLOCK_RESET) (
+ IN struct _EFI_BLOCK_IO *This,
+ IN BOOLEAN ExtendedVerification
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_BLOCK_READ) (
+ IN struct _EFI_BLOCK_IO *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA LBA,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ );
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_BLOCK_WRITE) (
+ IN struct _EFI_BLOCK_IO *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA LBA,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ );
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_BLOCK_FLUSH) (
+ IN struct _EFI_BLOCK_IO *This
+ );
+
+
+
+typedef struct {
+ UINT32 MediaId;
+ BOOLEAN RemovableMedia;
+ BOOLEAN MediaPresent;
+
+ BOOLEAN LogicalPartition;
+ BOOLEAN ReadOnly;
+ BOOLEAN WriteCaching;
+
+ UINT32 BlockSize;
+ UINT32 IoAlign;
+
+ EFI_LBA LastBlock;
+} EFI_BLOCK_IO_MEDIA;
+
+typedef struct _EFI_BLOCK_IO {
+ UINT64 Revision;
+
+ EFI_BLOCK_IO_MEDIA *Media;
+
+ EFI_BLOCK_RESET Reset;
+ EFI_BLOCK_READ ReadBlocks;
+ EFI_BLOCK_WRITE WriteBlocks;
+ EFI_BLOCK_FLUSH FlushBlocks;
+
+} EFI_BLOCK_IO;
+
+
+
+//
+// Disk Block IO protocol
+//
+
+#define DISK_IO_PROTOCOL \
+ { 0xce345171, 0xba0b, 0x11d2, {0x8e, 0x4f, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+#define EFI_DISK_IO_INTERFACE_REVISION 0x00010000
+
+INTERFACE_DECL(_EFI_DISK_IO);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_DISK_READ) (
+ IN struct _EFI_DISK_IO *This,
+ IN UINT32 MediaId,
+ IN UINT64 Offset,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ );
+
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_DISK_WRITE) (
+ IN struct _EFI_DISK_IO *This,
+ IN UINT32 MediaId,
+ IN UINT64 Offset,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ );
+
+
+typedef struct _EFI_DISK_IO {
+ UINT64 Revision;
+ EFI_DISK_READ ReadDisk;
+ EFI_DISK_WRITE WriteDisk;
+} EFI_DISK_IO;
+
+
+//
+// Simple file system protocol
+//
+
+#define SIMPLE_FILE_SYSTEM_PROTOCOL \
+ { 0x964e5b22, 0x6459, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+
+INTERFACE_DECL(_EFI_FILE_IO_INTERFACE);
+INTERFACE_DECL(_EFI_FILE_HANDLE);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_VOLUME_OPEN) (
+ IN struct _EFI_FILE_IO_INTERFACE *This,
+ OUT struct _EFI_FILE_HANDLE **Root
+ );
+
+#define EFI_FILE_IO_INTERFACE_REVISION 0x00010000
+
+typedef struct _EFI_FILE_IO_INTERFACE {
+ UINT64 Revision;
+ EFI_VOLUME_OPEN OpenVolume;
+} EFI_FILE_IO_INTERFACE;
+
+//
+//
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_OPEN) (
+ IN struct _EFI_FILE_HANDLE *File,
+ OUT struct _EFI_FILE_HANDLE **NewHandle,
+ IN CHAR16 *FileName,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes
+ );
+
+// Open modes
+#define EFI_FILE_MODE_READ 0x0000000000000001
+#define EFI_FILE_MODE_WRITE 0x0000000000000002
+#define EFI_FILE_MODE_CREATE 0x8000000000000000
+
+// File attributes
+#define EFI_FILE_READ_ONLY 0x0000000000000001
+#define EFI_FILE_HIDDEN 0x0000000000000002
+#define EFI_FILE_SYSTEM 0x0000000000000004
+#define EFI_FILE_RESERVIED 0x0000000000000008
+#define EFI_FILE_DIRECTORY 0x0000000000000010
+#define EFI_FILE_ARCHIVE 0x0000000000000020
+#define EFI_FILE_VALID_ATTR 0x0000000000000037
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_CLOSE) (
+ IN struct _EFI_FILE_HANDLE *File
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_DELETE) (
+ IN struct _EFI_FILE_HANDLE *File
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_READ) (
+ IN struct _EFI_FILE_HANDLE *File,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_WRITE) (
+ IN struct _EFI_FILE_HANDLE *File,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_SET_POSITION) (
+ IN struct _EFI_FILE_HANDLE *File,
+ IN UINT64 Position
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_GET_POSITION) (
+ IN struct _EFI_FILE_HANDLE *File,
+ OUT UINT64 *Position
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_GET_INFO) (
+ IN struct _EFI_FILE_HANDLE *File,
+ IN EFI_GUID *InformationType,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_SET_INFO) (
+ IN struct _EFI_FILE_HANDLE *File,
+ IN EFI_GUID *InformationType,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_FLUSH) (
+ IN struct _EFI_FILE_HANDLE *File
+ );
+
+
+
+#define EFI_FILE_HANDLE_REVISION 0x00010000
+typedef struct _EFI_FILE_HANDLE {
+ UINT64 Revision;
+ EFI_FILE_OPEN Open;
+ EFI_FILE_CLOSE Close;
+ EFI_FILE_DELETE Delete;
+ EFI_FILE_READ Read;
+ EFI_FILE_WRITE Write;
+ EFI_FILE_GET_POSITION GetPosition;
+ EFI_FILE_SET_POSITION SetPosition;
+ EFI_FILE_GET_INFO GetInfo;
+ EFI_FILE_SET_INFO SetInfo;
+ EFI_FILE_FLUSH Flush;
+} EFI_FILE, *EFI_FILE_HANDLE;
+
+
+//
+// File information types
+//
+
+#define EFI_FILE_INFO_ID \
+ { 0x9576e92, 0x6d3f, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+
+typedef struct {
+ UINT64 Size;
+ UINT64 FileSize;
+ UINT64 PhysicalSize;
+ EFI_TIME CreateTime;
+ EFI_TIME LastAccessTime;
+ EFI_TIME ModificationTime;
+ UINT64 Attribute;
+ CHAR16 FileName[1];
+} EFI_FILE_INFO;
+
+//
+// The FileName field of the EFI_FILE_INFO data structure is variable length.
+// Whenever code needs to know the size of the EFI_FILE_INFO data structure, it needs to
+// be the size of the data structure without the FileName field. The following macro
+// computes this size correctly no matter how big the FileName array is declared.
+// This is required to make the EFI_FILE_INFO data structure ANSI compilant.
+//
+
+#define SIZE_OF_EFI_FILE_INFO EFI_FIELD_OFFSET(EFI_FILE_INFO,FileName)
+
+#define EFI_FILE_SYSTEM_INFO_ID \
+ { 0x9576e93, 0x6d3f, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+
+typedef struct {
+ UINT64 Size;
+ BOOLEAN ReadOnly;
+ UINT64 VolumeSize;
+ UINT64 FreeSpace;
+ UINT32 BlockSize;
+ CHAR16 VolumeLabel[1];
+} EFI_FILE_SYSTEM_INFO;
+
+//
+// The VolumeLabel field of the EFI_FILE_SYSTEM_INFO data structure is variable length.
+// Whenever code needs to know the size of the EFI_FILE_SYSTEM_INFO data structure, it needs
+// to be the size of the data structure without the VolumeLable field. The following macro
+// computes this size correctly no matter how big the VolumeLable array is declared.
+// This is required to make the EFI_FILE_SYSTEM_INFO data structure ANSI compilant.
+//
+
+#define SIZE_OF_EFI_FILE_SYSTEM_INFO EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_INFO,VolumeLabel)
+
+#define EFI_FILE_SYSTEM_VOLUME_LABEL_INFO_ID \
+ { 0xDB47D7D3,0xFE81, 0x11d3, {0x9A, 0x35, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D} }
+
+typedef struct {
+ CHAR16 VolumeLabel[1];
+} EFI_FILE_SYSTEM_VOLUME_LABEL_INFO;
+
+#define SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_VOLUME_LABEL_INFO,VolumeLabel)
+
+//
+// Load file protocol
+//
+
+
+#define LOAD_FILE_PROTOCOL \
+ { 0x56EC3091, 0x954C, 0x11d2, {0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B} }
+
+INTERFACE_DECL(_EFI_LOAD_FILE_INTERFACE);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LOAD_FILE) (
+ IN struct _EFI_LOAD_FILE_INTERFACE *This,
+ IN EFI_DEVICE_PATH *FilePath,
+ IN BOOLEAN BootPolicy,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer OPTIONAL
+ );
+
+typedef struct _EFI_LOAD_FILE_INTERFACE {
+ EFI_LOAD_FILE LoadFile;
+} EFI_LOAD_FILE_INTERFACE;
+
+
+//
+// Device IO protocol
+//
+
+#define DEVICE_IO_PROTOCOL \
+ { 0xaf6ac311, 0x84c3, 0x11d2, {0x8e, 0x3c, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+
+INTERFACE_DECL(_EFI_DEVICE_IO_INTERFACE);
+
+typedef enum {
+ IO_UINT8,
+ IO_UINT16,
+ IO_UINT32,
+ IO_UINT64,
+//
+// Specification Change: Copy from MMIO to MMIO vs. MMIO to buffer, buffer to MMIO
+//
+ MMIO_COPY_UINT8,
+ MMIO_COPY_UINT16,
+ MMIO_COPY_UINT32,
+ MMIO_COPY_UINT64
+} EFI_IO_WIDTH;
+
+#define EFI_PCI_ADDRESS(bus,dev,func,reg) \
+ ( (UINT64) ( (((UINTN)bus) << 24) + (((UINTN)dev) << 16) + (((UINTN)func) << 8) + ((UINTN)reg) ))
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_DEVICE_IO) (
+ IN struct _EFI_DEVICE_IO_INTERFACE *This,
+ IN EFI_IO_WIDTH Width,
+ IN UINT64 Address,
+ IN UINTN Count,
+ IN OUT VOID *Buffer
+ );
+
+typedef struct {
+ EFI_DEVICE_IO Read;
+ EFI_DEVICE_IO Write;
+} EFI_IO_ACCESS;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PCI_DEVICE_PATH) (
+ IN struct _EFI_DEVICE_IO_INTERFACE *This,
+ IN UINT64 Address,
+ IN OUT EFI_DEVICE_PATH **PciDevicePath
+ );
+
+typedef enum {
+ EfiBusMasterRead,
+ EfiBusMasterWrite,
+ EfiBusMasterCommonBuffer
+} EFI_IO_OPERATION_TYPE;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IO_MAP) (
+ IN struct _EFI_DEVICE_IO_INTERFACE *This,
+ IN EFI_IO_OPERATION_TYPE Operation,
+ IN EFI_PHYSICAL_ADDRESS *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IO_UNMAP) (
+ IN struct _EFI_DEVICE_IO_INTERFACE *This,
+ IN VOID *Mapping
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IO_ALLOCATE_BUFFER) (
+ IN struct _EFI_DEVICE_IO_INTERFACE *This,
+ IN EFI_ALLOCATE_TYPE Type,
+ IN EFI_MEMORY_TYPE MemoryType,
+ IN UINTN Pages,
+ IN OUT EFI_PHYSICAL_ADDRESS *HostAddress
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IO_FLUSH) (
+ IN struct _EFI_DEVICE_IO_INTERFACE *This
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IO_FREE_BUFFER) (
+ IN struct _EFI_DEVICE_IO_INTERFACE *This,
+ IN UINTN Pages,
+ IN EFI_PHYSICAL_ADDRESS HostAddress
+ );
+
+typedef struct _EFI_DEVICE_IO_INTERFACE {
+ EFI_IO_ACCESS Mem;
+ EFI_IO_ACCESS Io;
+ EFI_IO_ACCESS Pci;
+ EFI_IO_MAP Map;
+ EFI_PCI_DEVICE_PATH PciDevicePath;
+ EFI_IO_UNMAP Unmap;
+ EFI_IO_ALLOCATE_BUFFER AllocateBuffer;
+ EFI_IO_FLUSH Flush;
+ EFI_IO_FREE_BUFFER FreeBuffer;
+} EFI_DEVICE_IO_INTERFACE;
+
+
+//
+// Unicode Collation protocol
+//
+
+#define UNICODE_COLLATION_PROTOCOL \
+ { 0x1d85cd7f, 0xf43d, 0x11d2, {0x9a, 0xc, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
+
+#define UNICODE_BYTE_ORDER_MARK (CHAR16)(0xfeff)
+
+INTERFACE_DECL(_EFI_UNICODE_COLLATION_INTERFACE);
+
+typedef
+INTN
+(EFIAPI *EFI_UNICODE_COLLATION_STRICOLL) (
+ IN struct _EFI_UNICODE_COLLATION_INTERFACE *This,
+ IN CHAR16 *s1,
+ IN CHAR16 *s2
+ );
+
+typedef
+BOOLEAN
+(EFIAPI *EFI_UNICODE_COLLATION_METAIMATCH) (
+ IN struct _EFI_UNICODE_COLLATION_INTERFACE *This,
+ IN CHAR16 *String,
+ IN CHAR16 *Pattern
+ );
+
+typedef
+VOID
+(EFIAPI *EFI_UNICODE_COLLATION_STRLWR) (
+ IN struct _EFI_UNICODE_COLLATION_INTERFACE *This,
+ IN OUT CHAR16 *Str
+ );
+
+typedef
+VOID
+(EFIAPI *EFI_UNICODE_COLLATION_STRUPR) (
+ IN struct _EFI_UNICODE_COLLATION_INTERFACE *This,
+ IN OUT CHAR16 *Str
+ );
+
+typedef
+VOID
+(EFIAPI *EFI_UNICODE_COLLATION_FATTOSTR) (
+ IN struct _EFI_UNICODE_COLLATION_INTERFACE *This,
+ IN UINTN FatSize,
+ IN CHAR8 *Fat,
+ OUT CHAR16 *String
+ );
+
+typedef
+BOOLEAN
+(EFIAPI *EFI_UNICODE_COLLATION_STRTOFAT) (
+ IN struct _EFI_UNICODE_COLLATION_INTERFACE *This,
+ IN CHAR16 *String,
+ IN UINTN FatSize,
+ OUT CHAR8 *Fat
+ );
+
+
+typedef struct _EFI_UNICODE_COLLATION_INTERFACE {
+
+ // general
+ EFI_UNICODE_COLLATION_STRICOLL StriColl;
+ EFI_UNICODE_COLLATION_METAIMATCH MetaiMatch;
+ EFI_UNICODE_COLLATION_STRLWR StrLwr;
+ EFI_UNICODE_COLLATION_STRUPR StrUpr;
+
+ // for supporting fat volumes
+ EFI_UNICODE_COLLATION_FATTOSTR FatToStr;
+ EFI_UNICODE_COLLATION_STRTOFAT StrToFat;
+
+ CHAR8 *SupportedLanguages;
+} EFI_UNICODE_COLLATION_INTERFACE;
+
+//
+// Driver Binding protocol
+//
+
+#define DRIVER_BINDING_PROTOCOL \
+ { 0x18a031ab, 0xb443, 0x4d1a, {0xa5, 0xc0, 0x0c, 0x09, 0x26, 0x1e, 0x9f, 0x71} }
+
+INTERFACE_DECL(_EFI_DRIVER_BINDING);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_DRIVER_BINDING_SUPPORTED) (
+ IN struct _EFI_DRIVER_BINDING *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH *RemainingPath
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_DRIVER_BINDING_START) (
+ IN struct _EFI_DRIVER_BINDING *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH *RemainingPath
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_DRIVER_BINDING_STOP) (
+ IN struct _EFI_DRIVER_BINDING *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+typedef struct _EFI_DRIVER_BINDING {
+ EFI_DRIVER_BINDING_SUPPORTED Supported;
+ EFI_DRIVER_BINDING_START Start;
+ EFI_DRIVER_BINDING_STOP Stop;
+ UINT32 Version;
+ EFI_HANDLE ImageHandle;
+ EFI_HANDLE DriverBindingHandle;
+} EFI_DRIVER_BINDING;
+
+//
+// Component Name Protocol 2
+//
+
+#define COMPONENT_NAME2_PROTOCOL \
+ { 0x6a7a5cff, 0xe8d9, 0x4f70, {0xba, 0xda, 0x75, 0xab, 0x30, 0x25, 0xce, 0x14 } }
+
+INTERFACE_DECL(_EFI_COMPONENT_NAME2);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_COMPONENT_NAME_GET_DRIVER_NAME) (
+ IN struct _EFI_COMPONENT_NAME2 *This,
+ IN CHAR8 * Language,
+ OUT CHAR16 **DriverName
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_COMPONENT_NAME_GET_CONTROLLER_NAME) (
+ IN struct _EFI_COMPONENT_NAME2 *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+typedef struct _EFI_COMPONENT_NAME2 {
+ EFI_COMPONENT_NAME_GET_DRIVER_NAME GetDriverName;
+ EFI_COMPONENT_NAME_GET_CONTROLLER_NAME GetControllerName;
+ CHAR8 **SupportedLanguages;
+} EFI_COMPONENT_NAME2;
+
+#endif
diff --git a/stand/efi/include/efipxebc.h b/stand/efi/include/efipxebc.h
new file mode 100644
index 0000000..ba0b2e9
--- /dev/null
+++ b/stand/efi/include/efipxebc.h
@@ -0,0 +1,472 @@
+/* $FreeBSD$ */
+#ifndef _EFIPXEBC_H
+#define _EFIPXEBC_H
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efipxebc.h
+
+Abstract:
+
+ EFI PXE Base Code Protocol
+
+
+
+Revision History
+
+--*/
+
+//
+// PXE Base Code protocol
+//
+
+#define EFI_PXE_BASE_CODE_PROTOCOL \
+ { 0x03c4e603, 0xac28, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
+
+INTERFACE_DECL(_EFI_PXE_BASE_CODE);
+
+#define DEFAULT_TTL 8
+#define DEFAULT_ToS 0
+//
+// Address definitions
+//
+
+typedef union {
+ UINT32 Addr[4];
+ EFI_IPv4_ADDRESS v4;
+ EFI_IPv6_ADDRESS v6;
+} EFI_IP_ADDRESS;
+
+typedef UINT16 EFI_PXE_BASE_CODE_UDP_PORT;
+
+//
+// Packet definitions
+//
+
+typedef struct {
+ UINT8 BootpOpcode;
+ UINT8 BootpHwType;
+ UINT8 BootpHwAddrLen;
+ UINT8 BootpGateHops;
+ UINT32 BootpIdent;
+ UINT16 BootpSeconds;
+ UINT16 BootpFlags;
+ UINT8 BootpCiAddr[4];
+ UINT8 BootpYiAddr[4];
+ UINT8 BootpSiAddr[4];
+ UINT8 BootpGiAddr[4];
+ UINT8 BootpHwAddr[16];
+ UINT8 BootpSrvName[64];
+ UINT8 BootpBootFile[128];
+ UINT32 DhcpMagik;
+ UINT8 DhcpOptions[56];
+} EFI_PXE_BASE_CODE_DHCPV4_PACKET;
+
+// TBD in EFI v1.1
+//typedef struct {
+// UINT8 reserved;
+//} EFI_PXE_BASE_CODE_DHCPV6_PACKET;
+
+typedef union {
+ UINT8 Raw[1472];
+ EFI_PXE_BASE_CODE_DHCPV4_PACKET Dhcpv4;
+// EFI_PXE_BASE_CODE_DHCPV6_PACKET Dhcpv6;
+} EFI_PXE_BASE_CODE_PACKET;
+
+typedef struct {
+ UINT8 Type;
+ UINT8 Code;
+ UINT16 Checksum;
+ union {
+ UINT32 reserved;
+ UINT32 Mtu;
+ UINT32 Pointer;
+ struct {
+ UINT16 Identifier;
+ UINT16 Sequence;
+ } Echo;
+ } u;
+ UINT8 Data[494];
+} EFI_PXE_BASE_CODE_ICMP_ERROR;
+
+typedef struct {
+ UINT8 ErrorCode;
+ CHAR8 ErrorString[127];
+} EFI_PXE_BASE_CODE_TFTP_ERROR;
+
+//
+// IP Receive Filter definitions
+//
+#define EFI_PXE_BASE_CODE_MAX_IPCNT 8
+typedef struct {
+ UINT8 Filters;
+ UINT8 IpCnt;
+ UINT16 reserved;
+ EFI_IP_ADDRESS IpList[EFI_PXE_BASE_CODE_MAX_IPCNT];
+} EFI_PXE_BASE_CODE_IP_FILTER;
+
+#define EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP 0x0001
+#define EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST 0x0002
+#define EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS 0x0004
+#define EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST 0x0008
+
+//
+// ARP Cache definitions
+//
+
+typedef struct {
+ EFI_IP_ADDRESS IpAddr;
+ EFI_MAC_ADDRESS MacAddr;
+} EFI_PXE_BASE_CODE_ARP_ENTRY;
+
+typedef struct {
+ EFI_IP_ADDRESS IpAddr;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_IP_ADDRESS GwAddr;
+} EFI_PXE_BASE_CODE_ROUTE_ENTRY;
+
+//
+// UDP definitions
+//
+
+#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP 0x0001
+#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT 0x0002
+#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP 0x0004
+#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT 0x0008
+#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER 0x0010
+#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT 0x0020
+
+//
+// Discover() definitions
+//
+
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP 0
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_MS_WINNT_RIS 1
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_INTEL_LCM 2
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_DOSUNDI 3
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_NEC_ESMPRO 4
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_IBM_WSoD 5
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_IBM_LCCM 6
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_CA_UNICENTER_TNG 7
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_HP_OPENVIEW 8
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_ALTIRIS_9 9
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_ALTIRIS_10 10
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_ALTIRIS_11 11
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_NOT_USED_12 12
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_REDHAT_INSTALL 13
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_REDHAT_BOOT 14
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_REMBO 15
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_BEOBOOT 16
+//
+// 17 through 32767 are reserved
+// 32768 through 65279 are for vendor use
+// 65280 through 65534 are reserved
+//
+#define EFI_PXE_BASE_CODE_BOOT_TYPE_PXETEST 65535
+
+#define EFI_PXE_BASE_CODE_BOOT_LAYER_MASK 0x7FFF
+#define EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL 0x0000
+#define EFI_PXE_BASE_CODE_BOOT_LAYER_CREDENTIALS 0x8000
+
+
+typedef struct {
+ UINT16 Type;
+ BOOLEAN AcceptAnyResponse;
+ UINT8 Reserved;
+ EFI_IP_ADDRESS IpAddr;
+} EFI_PXE_BASE_CODE_SRVLIST;
+
+typedef struct {
+ BOOLEAN UseMCast;
+ BOOLEAN UseBCast;
+ BOOLEAN UseUCast;
+ BOOLEAN MustUseList;
+ EFI_IP_ADDRESS ServerMCastIp;
+ UINT16 IpCnt;
+ EFI_PXE_BASE_CODE_SRVLIST SrvList[1];
+} EFI_PXE_BASE_CODE_DISCOVER_INFO;
+
+//
+// Mtftp() definitions
+//
+
+typedef enum {
+ EFI_PXE_BASE_CODE_TFTP_FIRST,
+ EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+ EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+ EFI_PXE_BASE_CODE_TFTP_WRITE_FILE,
+ EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY,
+ EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE,
+ EFI_PXE_BASE_CODE_MTFTP_READ_FILE,
+ EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY,
+ EFI_PXE_BASE_CODE_MTFTP_LAST
+} EFI_PXE_BASE_CODE_TFTP_OPCODE;
+
+typedef struct {
+ EFI_IP_ADDRESS MCastIp;
+ EFI_PXE_BASE_CODE_UDP_PORT CPort;
+ EFI_PXE_BASE_CODE_UDP_PORT SPort;
+ UINT16 ListenTimeout;
+ UINT16 TransmitTimeout;
+} EFI_PXE_BASE_CODE_MTFTP_INFO;
+
+//
+// PXE Base Code Mode structure
+//
+
+#define EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES 8
+#define EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES 8
+
+typedef struct {
+ BOOLEAN Started;
+ BOOLEAN Ipv6Available;
+ BOOLEAN Ipv6Supported;
+ BOOLEAN UsingIpv6;
+ BOOLEAN BisSupported;
+ BOOLEAN BisDetected;
+ BOOLEAN AutoArp;
+ BOOLEAN SendGUID;
+ BOOLEAN DhcpDiscoverValid;
+ BOOLEAN DhcpAckReceived;
+ BOOLEAN ProxyOfferReceived;
+ BOOLEAN PxeDiscoverValid;
+ BOOLEAN PxeReplyReceived;
+ BOOLEAN PxeBisReplyReceived;
+ BOOLEAN IcmpErrorReceived;
+ BOOLEAN TftpErrorReceived;
+ BOOLEAN MakeCallbacks;
+ UINT8 TTL;
+ UINT8 ToS;
+ EFI_IP_ADDRESS StationIp;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_PXE_BASE_CODE_PACKET DhcpDiscover;
+ EFI_PXE_BASE_CODE_PACKET DhcpAck;
+ EFI_PXE_BASE_CODE_PACKET ProxyOffer;
+ EFI_PXE_BASE_CODE_PACKET PxeDiscover;
+ EFI_PXE_BASE_CODE_PACKET PxeReply;
+ EFI_PXE_BASE_CODE_PACKET PxeBisReply;
+ EFI_PXE_BASE_CODE_IP_FILTER IpFilter;
+ UINT32 ArpCacheEntries;
+ EFI_PXE_BASE_CODE_ARP_ENTRY ArpCache[EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES];
+ UINT32 RouteTableEntries;
+ EFI_PXE_BASE_CODE_ROUTE_ENTRY RouteTable[EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES];
+ EFI_PXE_BASE_CODE_ICMP_ERROR IcmpError;
+ EFI_PXE_BASE_CODE_TFTP_ERROR TftpError;
+} EFI_PXE_BASE_CODE_MODE;
+
+//
+// PXE Base Code Interface Function definitions
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_START) (
+ IN struct _EFI_PXE_BASE_CODE *This,
+ IN BOOLEAN UseIpv6
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_STOP) (
+ IN struct _EFI_PXE_BASE_CODE *This
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_DHCP) (
+ IN struct _EFI_PXE_BASE_CODE *This,
+ IN BOOLEAN SortOffers
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_DISCOVER) (
+ IN struct _EFI_PXE_BASE_CODE *This,
+ IN UINT16 Type,
+ IN UINT16 *Layer,
+ IN BOOLEAN UseBis,
+ IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO *Info OPTIONAL
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_MTFTP) (
+ IN struct _EFI_PXE_BASE_CODE *This,
+ IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation,
+ IN OUT VOID *BufferPtr OPTIONAL,
+ IN BOOLEAN Overwrite,
+ IN OUT UINT64 *BufferSize,
+ IN UINTN *BlockSize OPTIONAL,
+ IN EFI_IP_ADDRESS *ServerIp,
+ IN UINT8 *Filename,
+ IN EFI_PXE_BASE_CODE_MTFTP_INFO *Info OPTIONAL,
+ IN BOOLEAN DontUseBuffer
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_UDP_WRITE) (
+ IN struct _EFI_PXE_BASE_CODE *This,
+ IN UINT16 OpFlags,
+ IN EFI_IP_ADDRESS *DestIp,
+ IN EFI_PXE_BASE_CODE_UDP_PORT *DestPort,
+ IN EFI_IP_ADDRESS *GatewayIp, OPTIONAL
+ IN EFI_IP_ADDRESS *SrcIp, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL
+ IN UINTN *HeaderSize, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN UINTN *BufferSize,
+ IN VOID *BufferPtr
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_UDP_READ) (
+ IN struct _EFI_PXE_BASE_CODE *This,
+ IN UINT16 OpFlags,
+ IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort, OPTIONAL
+ IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL
+ IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL
+ IN UINTN *HeaderSize, OPTIONAL
+ IN VOID *HeaderPtr, OPTIONAL
+ IN OUT UINTN *BufferSize,
+ IN VOID *BufferPtr
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_SET_IP_FILTER) (
+ IN struct _EFI_PXE_BASE_CODE *This,
+ IN EFI_PXE_BASE_CODE_IP_FILTER *NewFilter
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_ARP) (
+ IN struct _EFI_PXE_BASE_CODE *This,
+ IN EFI_IP_ADDRESS *IpAddr,
+ IN EFI_MAC_ADDRESS *MacAddr OPTIONAL
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_SET_PARAMETERS) (
+ IN struct _EFI_PXE_BASE_CODE *This,
+ IN BOOLEAN *NewAutoArp, OPTIONAL
+ IN BOOLEAN *NewSendGUID, OPTIONAL
+ IN UINT8 *NewTTL, OPTIONAL
+ IN UINT8 *NewToS, OPTIONAL
+ IN BOOLEAN *NewMakeCallback OPTIONAL
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_SET_STATION_IP) (
+ IN struct _EFI_PXE_BASE_CODE *This,
+ IN EFI_IP_ADDRESS *NewStationIp, OPTIONAL
+ IN EFI_IP_ADDRESS *NewSubnetMask OPTIONAL
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PXE_BASE_CODE_SET_PACKETS) (
+ IN struct _EFI_PXE_BASE_CODE *This,
+ BOOLEAN *NewDhcpDiscoverValid, OPTIONAL
+ BOOLEAN *NewDhcpAckReceived, OPTIONAL
+ BOOLEAN *NewProxyOfferReceived, OPTIONAL
+ BOOLEAN *NewPxeDiscoverValid, OPTIONAL
+ BOOLEAN *NewPxeReplyReceived, OPTIONAL
+ BOOLEAN *NewPxeBisReplyReceived,OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET *NewDhcpDiscover, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET *NewDhcpAck, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET *NewProxyOffer, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET *NewPxeDiscover, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET *NewPxeReply, OPTIONAL
+ IN EFI_PXE_BASE_CODE_PACKET *NewPxeBisReply OPTIONAL
+ );
+
+//
+// PXE Base Code Protocol structure
+//
+
+#define EFI_PXE_BASE_CODE_INTERFACE_REVISION 0x00010000
+
+typedef struct _EFI_PXE_BASE_CODE {
+ UINT64 Revision;
+ EFI_PXE_BASE_CODE_START Start;
+ EFI_PXE_BASE_CODE_STOP Stop;
+ EFI_PXE_BASE_CODE_DHCP Dhcp;
+ EFI_PXE_BASE_CODE_DISCOVER Discover;
+ EFI_PXE_BASE_CODE_MTFTP Mtftp;
+ EFI_PXE_BASE_CODE_UDP_WRITE UdpWrite;
+ EFI_PXE_BASE_CODE_UDP_READ UdpRead;
+ EFI_PXE_BASE_CODE_SET_IP_FILTER SetIpFilter;
+ EFI_PXE_BASE_CODE_ARP Arp;
+ EFI_PXE_BASE_CODE_SET_PARAMETERS SetParameters;
+ EFI_PXE_BASE_CODE_SET_STATION_IP SetStationIp;
+ EFI_PXE_BASE_CODE_SET_PACKETS SetPackets;
+ EFI_PXE_BASE_CODE_MODE *Mode;
+} EFI_PXE_BASE_CODE;
+
+//
+// Call Back Definitions
+//
+
+#define EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL \
+ { 0x245dca21, 0xfb7b, 0x11d3, {0x8f, 0x01, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+
+//
+// Revision Number
+//
+
+#define EFI_PXE_BASE_CODE_CALLBACK_INTERFACE_REVISION 0x00010000
+
+INTERFACE_DECL(_EFI_PXE_BASE_CODE_CALLBACK);
+
+typedef enum {
+ EFI_PXE_BASE_CODE_FUNCTION_FIRST,
+ EFI_PXE_BASE_CODE_FUNCTION_DHCP,
+ EFI_PXE_BASE_CODE_FUNCTION_DISCOVER,
+ EFI_PXE_BASE_CODE_FUNCTION_MTFTP,
+ EFI_PXE_BASE_CODE_FUNCTION_UDP_WRITE,
+ EFI_PXE_BASE_CODE_FUNCTION_UDP_READ,
+ EFI_PXE_BASE_CODE_FUNCTION_ARP,
+ EFI_PXE_BASE_CODE_FUNCTION_IGMP,
+ EFI_PXE_BASE_CODE_PXE_FUNCTION_LAST
+} EFI_PXE_BASE_CODE_FUNCTION;
+
+typedef enum {
+ EFI_PXE_BASE_CODE_CALLBACK_STATUS_FIRST,
+ EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+ EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT,
+ EFI_PXE_BASE_CODE_CALLBACK_STATUS_LAST
+} EFI_PXE_BASE_CODE_CALLBACK_STATUS;
+
+typedef
+EFI_PXE_BASE_CODE_CALLBACK_STATUS
+(EFIAPI *EFI_PXE_CALLBACK) (
+ IN struct _EFI_PXE_BASE_CODE_CALLBACK *This,
+ IN EFI_PXE_BASE_CODE_FUNCTION Function,
+ IN BOOLEAN Received,
+ IN UINT32 PacketLen,
+ IN EFI_PXE_BASE_CODE_PACKET *Packet OPTIONAL
+ );
+
+typedef struct _EFI_PXE_BASE_CODE_CALLBACK {
+ UINT64 Revision;
+ EFI_PXE_CALLBACK Callback;
+} EFI_PXE_BASE_CODE_CALLBACK;
+
+#endif /* _EFIPXEBC_H */
diff --git a/stand/efi/include/efiser.h b/stand/efi/include/efiser.h
new file mode 100644
index 0000000..e3d66e2
--- /dev/null
+++ b/stand/efi/include/efiser.h
@@ -0,0 +1,139 @@
+/* $FreeBSD$ */
+#ifndef _EFI_SER_H
+#define _EFI_SER_H
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efiser.h
+
+Abstract:
+
+ EFI serial protocol
+
+Revision History
+
+--*/
+
+//
+// Serial protocol
+//
+
+#define SERIAL_IO_PROTOCOL \
+ { 0xBB25CF6F, 0xF1D4, 0x11D2, {0x9A, 0x0C, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0xFD} }
+
+INTERFACE_DECL(_SERIAL_IO_INTERFACE);
+
+typedef enum {
+ DefaultParity,
+ NoParity,
+ EvenParity,
+ OddParity,
+ MarkParity,
+ SpaceParity
+} EFI_PARITY_TYPE;
+
+typedef enum {
+ DefaultStopBits,
+ OneStopBit, // 1 stop bit
+ OneFiveStopBits, // 1.5 stop bits
+ TwoStopBits // 2 stop bits
+} EFI_STOP_BITS_TYPE;
+
+#define EFI_SERIAL_CLEAR_TO_SEND 0x0010 // RO
+#define EFI_SERIAL_DATA_SET_READY 0x0020 // RO
+#define EFI_SERIAL_RING_INDICATE 0x0040 // RO
+#define EFI_SERIAL_CARRIER_DETECT 0x0080 // RO
+#define EFI_SERIAL_REQUEST_TO_SEND 0x0002 // WO
+#define EFI_SERIAL_DATA_TERMINAL_READY 0x0001 // WO
+#define EFI_SERIAL_INPUT_BUFFER_EMPTY 0x0100 // RO
+#define EFI_SERIAL_OUTPUT_BUFFER_EMPTY 0x0200 // RO
+#define EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE 0x1000 // RW
+#define EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE 0x2000 // RW
+#define EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE 0x4000 // RW
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SERIAL_RESET) (
+ IN struct _SERIAL_IO_INTERFACE *This
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SERIAL_SET_ATTRIBUTES) (
+ IN struct _SERIAL_IO_INTERFACE *This,
+ IN UINT64 BaudRate,
+ IN UINT32 ReceiveFifoDepth,
+ IN UINT32 Timeout,
+ IN EFI_PARITY_TYPE Parity,
+ IN UINT8 DataBits,
+ IN EFI_STOP_BITS_TYPE StopBits
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SERIAL_SET_CONTROL_BITS) (
+ IN struct _SERIAL_IO_INTERFACE *This,
+ IN UINT32 Control
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SERIAL_GET_CONTROL_BITS) (
+ IN struct _SERIAL_IO_INTERFACE *This,
+ OUT UINT32 *Control
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SERIAL_WRITE) (
+ IN struct _SERIAL_IO_INTERFACE *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SERIAL_READ) (
+ IN struct _SERIAL_IO_INTERFACE *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ );
+
+typedef struct {
+ UINT32 ControlMask;
+
+ // current Attributes
+ UINT32 Timeout;
+ UINT64 BaudRate;
+ UINT32 ReceiveFifoDepth;
+ UINT32 DataBits;
+ UINT32 Parity;
+ UINT32 StopBits;
+} SERIAL_IO_MODE;
+
+#define SERIAL_IO_INTERFACE_REVISION 0x00010000
+
+typedef struct _SERIAL_IO_INTERFACE {
+ UINT32 Revision;
+ EFI_SERIAL_RESET Reset;
+ EFI_SERIAL_SET_ATTRIBUTES SetAttributes;
+ EFI_SERIAL_SET_CONTROL_BITS SetControl;
+ EFI_SERIAL_GET_CONTROL_BITS GetControl;
+ EFI_SERIAL_WRITE Write;
+ EFI_SERIAL_READ Read;
+
+ SERIAL_IO_MODE *Mode;
+} SERIAL_IO_INTERFACE;
+
+#endif
diff --git a/stand/efi/include/efistdarg.h b/stand/efi/include/efistdarg.h
new file mode 100644
index 0000000..25f5569
--- /dev/null
+++ b/stand/efi/include/efistdarg.h
@@ -0,0 +1,39 @@
+/* $FreeBSD$ */
+#ifndef _EFISTDARG_H_
+#define _EFISTDARG_H_
+
+/*++
+
+Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ devpath.h
+
+Abstract:
+
+ Defines for parsing the EFI Device Path structures
+
+
+
+Revision History
+
+--*/
+
+#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(UINTN) - 1) & ~(sizeof(UINTN) - 1) )
+
+typedef CHAR8 * va_list;
+
+#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
+#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
+#define va_end(ap) ( ap = (va_list)0 )
+
+
+#endif /* _INC_STDARG */
diff --git a/stand/efi/include/efiuga.h b/stand/efi/include/efiuga.h
new file mode 100644
index 0000000..28c738e
--- /dev/null
+++ b/stand/efi/include/efiuga.h
@@ -0,0 +1,168 @@
+/* $FreeBSD$ */
+/** @file
+ UGA Draw protocol from the EFI 1.1 specification.
+
+ Abstraction of a very simple graphics device.
+
+ Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.<BR>
+
+ 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.
+
+ File name: UgaDraw.h
+
+**/
+
+#ifndef __UGA_DRAW_H__
+#define __UGA_DRAW_H__
+
+#define EFI_UGA_DRAW_PROTOCOL_GUID \
+ { 0x982c298b, 0xf4fa, 0x41cb, {0xb8, 0x38, 0x77, 0xaa, 0x68, 0x8f, 0xb8, 0x39} }
+
+typedef struct _EFI_UGA_DRAW_PROTOCOL EFI_UGA_DRAW_PROTOCOL;
+
+/**
+ Return the current video mode information.
+
+ @param This Protocol instance pointer.
+ @param HorizontalResolution Current video horizontal resolution in pixels
+ @param VerticalResolution Current video vertical resolution in pixels
+ @param ColorDepth Current video color depth in bits per pixel
+ @param RefreshRate Current video refresh rate in Hz.
+
+ @retval EFI_SUCCESS Mode information returned.
+ @retval EFI_NOT_STARTED Video display is not initialized. Call SetMode ()
+ @retval EFI_INVALID_PARAMETER One of the input args was NULL.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_UGA_DRAW_PROTOCOL_GET_MODE) (
+ IN EFI_UGA_DRAW_PROTOCOL *This,
+ OUT UINT32 *HorizontalResolution,
+ OUT UINT32 *VerticalResolution,
+ OUT UINT32 *ColorDepth,
+ OUT UINT32 *RefreshRate
+ )
+;
+
+/**
+ Return the current video mode information.
+
+ @param This Protocol instance pointer.
+ @param HorizontalResolution Current video horizontal resolution in pixels
+ @param VerticalResolution Current video vertical resolution in pixels
+ @param ColorDepth Current video color depth in bits per pixel
+ @param RefreshRate Current video refresh rate in Hz.
+
+ @retval EFI_SUCCESS Mode information returned.
+ @retval EFI_NOT_STARTED Video display is not initialized. Call SetMode ()
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_UGA_DRAW_PROTOCOL_SET_MODE) (
+ IN EFI_UGA_DRAW_PROTOCOL *This,
+ IN UINT32 HorizontalResolution,
+ IN UINT32 VerticalResolution,
+ IN UINT32 ColorDepth,
+ IN UINT32 RefreshRate
+ )
+;
+
+typedef struct {
+ UINT8 Blue;
+ UINT8 Green;
+ UINT8 Red;
+ UINT8 Reserved;
+} EFI_UGA_PIXEL;
+
+typedef union {
+ EFI_UGA_PIXEL Pixel;
+ UINT32 Raw;
+} EFI_UGA_PIXEL_UNION;
+
+typedef enum {
+ EfiUgaVideoFill,
+ EfiUgaVideoToBltBuffer,
+ EfiUgaBltBufferToVideo,
+ EfiUgaVideoToVideo,
+ EfiUgaBltMax
+} EFI_UGA_BLT_OPERATION;
+
+/**
+ Type specifying a pointer to a function to perform an UGA Blt operation.
+
+ The following table defines actions for BltOperations:
+
+ <B>EfiUgaVideoFill</B> - Write data from the BltBuffer pixel (SourceX, SourceY)
+ directly to every pixel of the video display rectangle
+ (DestinationX, DestinationY) (DestinationX + Width, DestinationY + Height).
+ Only one pixel will be used from the BltBuffer. Delta is NOT used.
+
+ <B>EfiUgaVideoToBltBuffer</B> - Read data from the video display rectangle
+ (SourceX, SourceY) (SourceX + Width, SourceY + Height) and place it in
+ the BltBuffer rectangle (DestinationX, DestinationY )
+ (DestinationX + Width, DestinationY + Height). If DestinationX or
+ DestinationY is not zero then Delta must be set to the length in bytes
+ of a row in the BltBuffer.
+
+ <B>EfiUgaBltBufferToVideo</B> - Write data from the BltBuffer rectangle
+ (SourceX, SourceY) (SourceX + Width, SourceY + Height) directly to the
+ video display rectangle (DestinationX, DestinationY)
+ (DestinationX + Width, DestinationY + Height). If SourceX or SourceY is
+ not zero then Delta must be set to the length in bytes of a row in the
+ BltBuffer.
+
+ <B>EfiUgaVideoToVideo</B> - Copy from the video display rectangle (SourceX, SourceY)
+ (SourceX + Width, SourceY + Height) .to the video display rectangle
+ (DestinationX, DestinationY) (DestinationX + Width, DestinationY + Height).
+ The BltBuffer and Delta are not used in this mode.
+
+
+ @param[in] This - Protocol instance pointer.
+ @param[in] BltBuffer - Buffer containing data to blit into video buffer. This
+ buffer has a size of Width*Height*sizeof(EFI_UGA_PIXEL)
+ @param[in] BltOperation - Operation to perform on BlitBuffer and video memory
+ @param[in] SourceX - X coordinate of source for the BltBuffer.
+ @param[in] SourceY - Y coordinate of source for the BltBuffer.
+ @param[in] DestinationX - X coordinate of destination for the BltBuffer.
+ @param[in] DestinationY - Y coordinate of destination for the BltBuffer.
+ @param[in] Width - Width of rectangle in BltBuffer in pixels.
+ @param[in] Height - Hight of rectangle in BltBuffer in pixels.
+ @param[in] Delta - OPTIONAL
+
+ @retval EFI_SUCCESS - The Blt operation completed.
+ @retval EFI_INVALID_PARAMETER - BltOperation is not valid.
+ @retval EFI_DEVICE_ERROR - A hardware error occurred writting to the video buffer.
+
+--*/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_UGA_DRAW_PROTOCOL_BLT) (
+ IN EFI_UGA_DRAW_PROTOCOL * This,
+ IN EFI_UGA_PIXEL * BltBuffer, OPTIONAL
+ IN EFI_UGA_BLT_OPERATION BltOperation,
+ IN UINTN SourceX,
+ IN UINTN SourceY,
+ IN UINTN DestinationX,
+ IN UINTN DestinationY,
+ IN UINTN Width,
+ IN UINTN Height,
+ IN UINTN Delta OPTIONAL
+ );
+
+struct _EFI_UGA_DRAW_PROTOCOL {
+ EFI_UGA_DRAW_PROTOCOL_GET_MODE GetMode;
+ EFI_UGA_DRAW_PROTOCOL_SET_MODE SetMode;
+ EFI_UGA_DRAW_PROTOCOL_BLT Blt;
+};
+
+extern EFI_GUID gEfiUgaDrawProtocolGuid;
+
+#endif
diff --git a/stand/efi/include/efizfs.h b/stand/efi/include/efizfs.h
new file mode 100644
index 0000000..1fd034f
--- /dev/null
+++ b/stand/efi/include/efizfs.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2016 Eric McCorkle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifndef _EFIZFS_H_
+#define _EFIZFS_H_
+
+#ifdef EFI_ZFS_BOOT
+typedef STAILQ_HEAD(zfsinfo_list, zfsinfo) zfsinfo_list_t;
+
+typedef struct zfsinfo
+{
+ STAILQ_ENTRY(zfsinfo) zi_link;
+ EFI_HANDLE zi_handle;
+ uint64_t zi_pool_guid;
+} zfsinfo_t;
+
+extern uint64_t pool_guid;
+
+extern void efi_zfs_probe(void);
+extern zfsinfo_list_t *efizfs_get_zfsinfo_list(void);
+extern bool efi_zfs_is_preferred(EFI_HANDLE *h);
+extern EFI_HANDLE efizfs_get_handle_by_guid(uint64_t);
+
+#endif
+
+#endif
diff --git a/stand/efi/include/i386/efibind.h b/stand/efi/include/i386/efibind.h
new file mode 100644
index 0000000..0f1d4f1
--- /dev/null
+++ b/stand/efi/include/i386/efibind.h
@@ -0,0 +1,267 @@
+/* $FreeBSD$ */
+/*++
+
+Copyright (c) 1999 - 2003 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+Module Name:
+
+ efefind.h
+
+Abstract:
+
+ EFI to compile bindings
+
+
+
+
+Revision History
+
+--*/
+
+#pragma pack()
+
+
+#ifdef __FreeBSD__
+#include <sys/stdint.h>
+#else
+//
+// Basic int types of various widths
+//
+
+#if (__STDC_VERSION__ < 199901L )
+
+ // No ANSI C 1999/2000 stdint.h integer width declarations
+
+ #ifdef _MSC_EXTENSIONS
+
+ // Use Microsoft C compiler integer width declarations
+
+ typedef unsigned __int64 uint64_t;
+ typedef __int64 int64_t;
+ typedef unsigned __int32 uint32_t;
+ typedef __int32 int32_t;
+ typedef unsigned short uint16_t;
+ typedef short int16_t;
+ typedef unsigned char uint8_t;
+ typedef char int8_t;
+ #else
+ #ifdef UNIX_LP64
+
+ // Use LP64 programming model from C_FLAGS for integer width declarations
+
+ typedef unsigned long uint64_t;
+ typedef long int64_t;
+ typedef unsigned int uint32_t;
+ typedef int int32_t;
+ typedef unsigned short uint16_t;
+ typedef short int16_t;
+ typedef unsigned char uint8_t;
+ typedef char int8_t;
+ #else
+
+ // Assume P64 programming model from C_FLAGS for integer width declarations
+
+ typedef unsigned long long uint64_t;
+ typedef long long int64_t;
+ typedef unsigned int uint32_t;
+ typedef int int32_t;
+ typedef unsigned short uint16_t;
+ typedef short int16_t;
+ typedef unsigned char uint8_t;
+ typedef char int8_t;
+ #endif
+ #endif
+#endif
+#endif /* __FreeBSD__ */
+
+//
+// Basic EFI types of various widths
+//
+
+#ifndef ACPI_THREAD_ID /* ACPI's definitions are fine, use those */
+#define ACPI_USE_SYSTEM_INTTYPES 1 /* Tell ACPI we've defined types */
+
+typedef uint64_t UINT64;
+typedef int64_t INT64;
+
+#ifndef _BASETSD_H_
+ typedef uint32_t UINT32;
+ typedef int32_t INT32;
+#endif
+
+typedef uint16_t UINT16;
+typedef int16_t INT16;
+typedef uint8_t UINT8;
+typedef int8_t INT8;
+
+#endif
+
+#undef VOID
+#define VOID void
+
+
+typedef int32_t INTN;
+typedef uint32_t UINTN;
+
+#ifdef EFI_NT_EMULATOR
+ #define POST_CODE(_Data)
+#else
+ #ifdef EFI_DEBUG
+#define POST_CODE(_Data) __asm mov eax,(_Data) __asm out 0x80,al
+ #else
+ #define POST_CODE(_Data)
+ #endif
+#endif
+
+#define EFIERR(a) (0x80000000 | a)
+#define EFI_ERROR_MASK 0x80000000
+#define EFIERR_OEM(a) (0xc0000000 | a)
+
+
+#define BAD_POINTER 0xFBFBFBFB
+#define MAX_ADDRESS 0xFFFFFFFF
+
+#define BREAKPOINT() __asm { int 3 }
+
+//
+// Pointers must be aligned to these address to function
+//
+
+#define MIN_ALIGNMENT_SIZE 4
+
+#define ALIGN_VARIABLE(Value ,Adjustment) \
+ (UINTN)Adjustment = 0; \
+ if((UINTN)Value % MIN_ALIGNMENT_SIZE) \
+ (UINTN)Adjustment = MIN_ALIGNMENT_SIZE - ((UINTN)Value % MIN_ALIGNMENT_SIZE); \
+ Value = (UINTN)Value + (UINTN)Adjustment
+
+
+//
+// Define macros to build data structure signatures from characters.
+//
+
+#define EFI_SIGNATURE_16(A,B) ((A) | (B<<8))
+#define EFI_SIGNATURE_32(A,B,C,D) (EFI_SIGNATURE_16(A,B) | (EFI_SIGNATURE_16(C,D) << 16))
+#define EFI_SIGNATURE_64(A,B,C,D,E,F,G,H) (EFI_SIGNATURE_32(A,B,C,D) | ((UINT64)(EFI_SIGNATURE_32(E,F,G,H)) << 32))
+
+//
+// EFIAPI - prototype calling convention for EFI function pointers
+// BOOTSERVICE - prototype for implementation of a boot service interface
+// RUNTIMESERVICE - prototype for implementation of a runtime service interface
+// RUNTIMEFUNCTION - prototype for implementation of a runtime function that is not a service
+// RUNTIME_CODE - pragma macro for declaring runtime code
+//
+
+#ifndef EFIAPI // Forces EFI calling conventions reguardless of compiler options
+ #ifdef _MSC_EXTENSIONS
+ #define EFIAPI __cdecl // Force C calling convention for Microsoft C compiler
+ #else
+ #define EFIAPI // Substitute expresion to force C calling convention
+ #endif
+#endif
+
+#define BOOTSERVICE
+//#define RUNTIMESERVICE(proto,a) alloc_text("rtcode",a); proto a
+//#define RUNTIMEFUNCTION(proto,a) alloc_text("rtcode",a); proto a
+#define RUNTIMESERVICE
+#define RUNTIMEFUNCTION
+
+
+#define RUNTIME_CODE(a) alloc_text("rtcode", a)
+#define BEGIN_RUNTIME_DATA() data_seg("rtdata")
+#define END_RUNTIME_DATA() data_seg()
+
+#define VOLATILE volatile
+
+#define MEMORY_FENCE()
+
+#ifdef EFI_NO_INTERFACE_DECL
+ #define EFI_FORWARD_DECLARATION(x)
+ #define EFI_INTERFACE_DECL(x)
+#else
+ #define EFI_FORWARD_DECLARATION(x) typedef struct _##x x
+ #define EFI_INTERFACE_DECL(x) typedef struct x
+#endif
+
+#ifdef EFI_NT_EMULATOR
+
+//
+// To help ensure proper coding of integrated drivers, they are
+// compiled as DLLs. In NT they require a dll init entry pointer.
+// The macro puts a stub entry point into the DLL so it will load.
+//
+
+#define EFI_DRIVER_ENTRY_POINT(InitFunction) \
+ EFI_STATUS \
+ InitFunction ( \
+ EFI_HANDLE ImageHandle, \
+ EFI_SYSTEM_TABLE *SystemTable \
+ ); \
+ \
+ UINTN \
+ __stdcall \
+ _DllMainCRTStartup ( \
+ UINTN Inst, \
+ UINTN reason_for_call, \
+ VOID *rserved \
+ ) \
+ { \
+ return 1; \
+ } \
+ \
+ int \
+ __declspec( dllexport ) \
+ __cdecl \
+ InitializeDriver ( \
+ void *ImageHandle, \
+ void *SystemTable \
+ ) \
+ { \
+ return InitFunction(ImageHandle, SystemTable); \
+ }
+
+
+ #define LOAD_INTERNAL_DRIVER(_if, type, name, entry) \
+ (_if)->LoadInternal(type, name, NULL)
+
+#else // EFI_NT_EMULATOR
+
+//
+// When build similar to FW, then link everything together as
+// one big module.
+//
+
+ #define EFI_DRIVER_ENTRY_POINT(InitFunction)
+
+ #define LOAD_INTERNAL_DRIVER(_if, type, name, entry) \
+ (_if)->LoadInternal(type, name, entry)
+
+#endif // EFI_FW_NT
+
+#ifdef __FreeBSD__
+#define INTERFACE_DECL(x) struct x
+#else
+//
+// Some compilers don't support the forward reference construct:
+// typedef struct XXXXX
+//
+// The following macro provide a workaround for such cases.
+//
+#ifdef NO_INTERFACE_DECL
+#define INTERFACE_DECL(x)
+#else
+#define INTERFACE_DECL(x) typedef struct x
+#endif
+#endif /* __FreeBSD__ */
+
+#ifdef _MSC_EXTENSIONS
+#pragma warning ( disable : 4731 ) // Suppress warnings about modification of EBP
+#endif
+
diff --git a/stand/efi/include/i386/pe.h b/stand/efi/include/i386/pe.h
new file mode 100644
index 0000000..e2ae25c
--- /dev/null
+++ b/stand/efi/include/i386/pe.h
@@ -0,0 +1,630 @@
+/* $FreeBSD$ */
+/*
+ PE32+ header file
+ */
+#ifndef _PE_H
+#define _PE_H
+
+#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
+#define IMAGE_OS2_SIGNATURE 0x454E // NE
+#define IMAGE_OS2_SIGNATURE_LE 0x454C // LE
+#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
+#define IMAGE_EDOS_SIGNATURE 0x44454550 // PEED
+
+
+typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
+ UINT16 e_magic; // Magic number
+ UINT16 e_cblp; // Bytes on last page of file
+ UINT16 e_cp; // Pages in file
+ UINT16 e_crlc; // Relocations
+ UINT16 e_cparhdr; // Size of header in paragraphs
+ UINT16 e_minalloc; // Minimum extra paragraphs needed
+ UINT16 e_maxalloc; // Maximum extra paragraphs needed
+ UINT16 e_ss; // Initial (relative) SS value
+ UINT16 e_sp; // Initial SP value
+ UINT16 e_csum; // Checksum
+ UINT16 e_ip; // Initial IP value
+ UINT16 e_cs; // Initial (relative) CS value
+ UINT16 e_lfarlc; // File address of relocation table
+ UINT16 e_ovno; // Overlay number
+ UINT16 e_res[4]; // Reserved words
+ UINT16 e_oemid; // OEM identifier (for e_oeminfo)
+ UINT16 e_oeminfo; // OEM information; e_oemid specific
+ UINT16 e_res2[10]; // Reserved words
+ UINT32 e_lfanew; // File address of new exe header
+ } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
+
+typedef struct _IMAGE_OS2_HEADER { // OS/2 .EXE header
+ UINT16 ne_magic; // Magic number
+ UINT8 ne_ver; // Version number
+ UINT8 ne_rev; // Revision number
+ UINT16 ne_enttab; // Offset of Entry Table
+ UINT16 ne_cbenttab; // Number of bytes in Entry Table
+ UINT32 ne_crc; // Checksum of whole file
+ UINT16 ne_flags; // Flag UINT16
+ UINT16 ne_autodata; // Automatic data segment number
+ UINT16 ne_heap; // Initial heap allocation
+ UINT16 ne_stack; // Initial stack allocation
+ UINT32 ne_csip; // Initial CS:IP setting
+ UINT32 ne_sssp; // Initial SS:SP setting
+ UINT16 ne_cseg; // Count of file segments
+ UINT16 ne_cmod; // Entries in Module Reference Table
+ UINT16 ne_cbnrestab; // Size of non-resident name table
+ UINT16 ne_segtab; // Offset of Segment Table
+ UINT16 ne_rsrctab; // Offset of Resource Table
+ UINT16 ne_restab; // Offset of resident name table
+ UINT16 ne_modtab; // Offset of Module Reference Table
+ UINT16 ne_imptab; // Offset of Imported Names Table
+ UINT32 ne_nrestab; // Offset of Non-resident Names Table
+ UINT16 ne_cmovent; // Count of movable entries
+ UINT16 ne_align; // Segment alignment shift count
+ UINT16 ne_cres; // Count of resource segments
+ UINT8 ne_exetyp; // Target Operating system
+ UINT8 ne_flagsothers; // Other .EXE flags
+ UINT16 ne_pretthunks; // offset to return thunks
+ UINT16 ne_psegrefbytes; // offset to segment ref. bytes
+ UINT16 ne_swaparea; // Minimum code swap area size
+ UINT16 ne_expver; // Expected Windows version number
+ } IMAGE_OS2_HEADER, *PIMAGE_OS2_HEADER;
+
+//
+// File header format.
+//
+
+typedef struct _IMAGE_FILE_HEADER {
+ UINT16 Machine;
+ UINT16 NumberOfSections;
+ UINT32 TimeDateStamp;
+ UINT32 PointerToSymbolTable;
+ UINT32 NumberOfSymbols;
+ UINT16 SizeOfOptionalHeader;
+ UINT16 Characteristics;
+} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
+
+#define IMAGE_SIZEOF_FILE_HEADER 20
+
+#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file.
+#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved externel references).
+#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file.
+#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file.
+#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed.
+#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine.
+#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in .DBG file
+#define IMAGE_FILE_SYSTEM 0x1000 // System File.
+#define IMAGE_FILE_DLL 0x2000 // File is a DLL.
+#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.
+
+#define IMAGE_FILE_MACHINE_UNKNOWN 0
+#define IMAGE_FILE_MACHINE_I386 0x14c // Intel 386.
+#define IMAGE_FILE_MACHINE_R3000 0x162 // MIPS little-endian, 0540 big-endian
+#define IMAGE_FILE_MACHINE_R4000 0x166 // MIPS little-endian
+#define IMAGE_FILE_MACHINE_ALPHA 0x184 // Alpha_AXP
+#define IMAGE_FILE_MACHINE_POWERPC 0x1F0 // IBM PowerPC Little-Endian
+#define IMAGE_FILE_MACHINE_TAHOE 0x7cc // Intel EM machine
+//
+// Directory format.
+//
+
+typedef struct _IMAGE_DATA_DIRECTORY {
+ UINT32 VirtualAddress;
+ UINT32 Size;
+} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
+
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
+
+//
+// Optional header format.
+//
+
+typedef struct _IMAGE_OPTIONAL_HEADER {
+ //
+ // Standard fields.
+ //
+
+ UINT16 Magic;
+ UINT8 MajorLinkerVersion;
+ UINT8 MinorLinkerVersion;
+ UINT32 SizeOfCode;
+ UINT32 SizeOfInitializedData;
+ UINT32 SizeOfUninitializedData;
+ UINT32 AddressOfEntryPoint;
+ UINT32 BaseOfCode;
+ UINT32 BaseOfData;
+
+ //
+ // NT additional fields.
+ //
+
+ UINT32 ImageBase;
+ UINT32 SectionAlignment;
+ UINT32 FileAlignment;
+ UINT16 MajorOperatingSystemVersion;
+ UINT16 MinorOperatingSystemVersion;
+ UINT16 MajorImageVersion;
+ UINT16 MinorImageVersion;
+ UINT16 MajorSubsystemVersion;
+ UINT16 MinorSubsystemVersion;
+ UINT32 Reserved1;
+ UINT32 SizeOfImage;
+ UINT32 SizeOfHeaders;
+ UINT32 CheckSum;
+ UINT16 Subsystem;
+ UINT16 DllCharacteristics;
+ UINT32 SizeOfStackReserve;
+ UINT32 SizeOfStackCommit;
+ UINT32 SizeOfHeapReserve;
+ UINT32 SizeOfHeapCommit;
+ UINT32 LoaderFlags;
+ UINT32 NumberOfRvaAndSizes;
+ IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
+
+typedef struct _IMAGE_ROM_OPTIONAL_HEADER {
+ UINT16 Magic;
+ UINT8 MajorLinkerVersion;
+ UINT8 MinorLinkerVersion;
+ UINT32 SizeOfCode;
+ UINT32 SizeOfInitializedData;
+ UINT32 SizeOfUninitializedData;
+ UINT32 AddressOfEntryPoint;
+ UINT32 BaseOfCode;
+ UINT32 BaseOfData;
+ UINT32 BaseOfBss;
+ UINT32 GprMask;
+ UINT32 CprMask[4];
+ UINT32 GpValue;
+} IMAGE_ROM_OPTIONAL_HEADER, *PIMAGE_ROM_OPTIONAL_HEADER;
+
+#define IMAGE_SIZEOF_ROM_OPTIONAL_HEADER 56
+#define IMAGE_SIZEOF_STD_OPTIONAL_HEADER 28
+#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER 224
+
+#define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x10b
+#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107
+
+typedef struct _IMAGE_NT_HEADERS {
+ UINT32 Signature;
+ IMAGE_FILE_HEADER FileHeader;
+ IMAGE_OPTIONAL_HEADER OptionalHeader;
+} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
+
+typedef struct _IMAGE_ROM_HEADERS {
+ IMAGE_FILE_HEADER FileHeader;
+ IMAGE_ROM_OPTIONAL_HEADER OptionalHeader;
+} IMAGE_ROM_HEADERS, *PIMAGE_ROM_HEADERS;
+
+#define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER) \
+ ((UINT32)ntheader + \
+ FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + \
+ ((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader \
+ ))
+
+
+// Subsystem Values
+
+#define IMAGE_SUBSYSTEM_UNKNOWN 0 // Unknown subsystem.
+#define IMAGE_SUBSYSTEM_NATIVE 1 // Image doesn't require a subsystem.
+#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem.
+#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem.
+#define IMAGE_SUBSYSTEM_OS2_CUI 5 // image runs in the OS/2 character subsystem.
+#define IMAGE_SUBSYSTEM_POSIX_CUI 7 // image run in the Posix character subsystem.
+
+
+// Directory Entries
+
+#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
+#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
+#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
+#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
+#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
+#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
+#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // Description String
+#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // Machine Value (MIPS GP)
+#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
+#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
+
+//
+// Section header format.
+//
+
+#define IMAGE_SIZEOF_SHORT_NAME 8
+
+typedef struct _IMAGE_SECTION_HEADER {
+ UINT8 Name[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;
+} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
+
+#define IMAGE_SIZEOF_SECTION_HEADER 40
+
+#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 // Reserved.
+
+#define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code.
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data.
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized data.
+
+#define IMAGE_SCN_LNK_OTHER 0x00000100 // Reserved.
+#define IMAGE_SCN_LNK_INFO 0x00000200 // Section contains comments or some other type of information.
+#define IMAGE_SCN_LNK_REMOVE 0x00000800 // Section contents will not become part of image.
+#define IMAGE_SCN_LNK_COMDAT 0x00001000 // Section contents comdat.
+
+#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 //
+#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 //
+#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 //
+#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 //
+#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 // Default alignment if no others are specified.
+#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 //
+#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 //
+
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 // Section can be discarded.
+#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // Section is not cachable.
+#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable.
+#define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable.
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable.
+#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
+#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable.
+
+//
+// Symbol format.
+//
+
+
+#define IMAGE_SIZEOF_SYMBOL 18
+
+//
+// Section values.
+//
+// Symbols have a section number of the section in which they are
+// defined. Otherwise, section numbers have the following meanings:
+//
+
+#define IMAGE_SYM_UNDEFINED (UINT16)0 // Symbol is undefined or is common.
+#define IMAGE_SYM_ABSOLUTE (UINT16)-1 // Symbol is an absolute value.
+#define IMAGE_SYM_DEBUG (UINT16)-2 // Symbol is a special debug item.
+
+//
+// Type (fundamental) values.
+//
+
+#define IMAGE_SYM_TYPE_NULL 0 // no type.
+#define IMAGE_SYM_TYPE_VOID 1 //
+#define IMAGE_SYM_TYPE_CHAR 2 // type character.
+#define IMAGE_SYM_TYPE_SHORT 3 // type short integer.
+#define IMAGE_SYM_TYPE_INT 4 //
+#define IMAGE_SYM_TYPE_LONG 5 //
+#define IMAGE_SYM_TYPE_FLOAT 6 //
+#define IMAGE_SYM_TYPE_DOUBLE 7 //
+#define IMAGE_SYM_TYPE_STRUCT 8 //
+#define IMAGE_SYM_TYPE_UNION 9 //
+#define IMAGE_SYM_TYPE_ENUM 10 // enumeration.
+#define IMAGE_SYM_TYPE_MOE 11 // member of enumeration.
+#define IMAGE_SYM_TYPE_BYTE 12 //
+#define IMAGE_SYM_TYPE_WORD 13 //
+#define IMAGE_SYM_TYPE_UINT 14 //
+#define IMAGE_SYM_TYPE_DWORD 15 //
+
+//
+// Type (derived) values.
+//
+
+#define IMAGE_SYM_DTYPE_NULL 0 // no derived type.
+#define IMAGE_SYM_DTYPE_POINTER 1 // pointer.
+#define IMAGE_SYM_DTYPE_FUNCTION 2 // function.
+#define IMAGE_SYM_DTYPE_ARRAY 3 // array.
+
+//
+// Storage classes.
+//
+
+#define IMAGE_SYM_CLASS_END_OF_FUNCTION (BYTE )-1
+#define IMAGE_SYM_CLASS_NULL 0
+#define IMAGE_SYM_CLASS_AUTOMATIC 1
+#define IMAGE_SYM_CLASS_EXTERNAL 2
+#define IMAGE_SYM_CLASS_STATIC 3
+#define IMAGE_SYM_CLASS_REGISTER 4
+#define IMAGE_SYM_CLASS_EXTERNAL_DEF 5
+#define IMAGE_SYM_CLASS_LABEL 6
+#define IMAGE_SYM_CLASS_UNDEFINED_LABEL 7
+#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8
+#define IMAGE_SYM_CLASS_ARGUMENT 9
+#define IMAGE_SYM_CLASS_STRUCT_TAG 10
+#define IMAGE_SYM_CLASS_MEMBER_OF_UNION 11
+#define IMAGE_SYM_CLASS_UNION_TAG 12
+#define IMAGE_SYM_CLASS_TYPE_DEFINITION 13
+#define IMAGE_SYM_CLASS_UNDEFINED_STATIC 14
+#define IMAGE_SYM_CLASS_ENUM_TAG 15
+#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16
+#define IMAGE_SYM_CLASS_REGISTER_PARAM 17
+#define IMAGE_SYM_CLASS_BIT_FIELD 18
+#define IMAGE_SYM_CLASS_BLOCK 100
+#define IMAGE_SYM_CLASS_FUNCTION 101
+#define IMAGE_SYM_CLASS_END_OF_STRUCT 102
+#define IMAGE_SYM_CLASS_FILE 103
+// new
+#define IMAGE_SYM_CLASS_SECTION 104
+#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 105
+
+// type packing constants
+
+#define N_BTMASK 017
+#define N_TMASK 060
+#define N_TMASK1 0300
+#define N_TMASK2 0360
+#define N_BTSHFT 4
+#define N_TSHIFT 2
+
+// MACROS
+
+//
+// Communal selection types.
+//
+
+#define IMAGE_COMDAT_SELECT_NODUPLICATES 1
+#define IMAGE_COMDAT_SELECT_ANY 2
+#define IMAGE_COMDAT_SELECT_SAME_SIZE 3
+#define IMAGE_COMDAT_SELECT_EXACT_MATCH 4
+#define IMAGE_COMDAT_SELECT_ASSOCIATIVE 5
+
+#define IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY 1
+#define IMAGE_WEAK_EXTERN_SEARCH_LIBRARY 2
+#define IMAGE_WEAK_EXTERN_SEARCH_ALIAS 3
+
+
+//
+// Relocation format.
+//
+
+typedef struct _IMAGE_RELOCATION {
+ UINT32 VirtualAddress;
+ UINT32 SymbolTableIndex;
+ UINT16 Type;
+} IMAGE_RELOCATION;
+
+#define IMAGE_SIZEOF_RELOCATION 10
+
+//
+// I386 relocation types.
+//
+
+#define IMAGE_REL_I386_ABSOLUTE 0 // Reference is absolute, no relocation is necessary
+#define IMAGE_REL_I386_DIR16 01 // Direct 16-bit reference to the symbols virtual address
+#define IMAGE_REL_I386_REL16 02 // PC-relative 16-bit reference to the symbols virtual address
+#define IMAGE_REL_I386_DIR32 06 // Direct 32-bit reference to the symbols virtual address
+#define IMAGE_REL_I386_DIR32NB 07 // Direct 32-bit reference to the symbols virtual address, base not included
+#define IMAGE_REL_I386_SEG12 011 // Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address
+#define IMAGE_REL_I386_SECTION 012
+#define IMAGE_REL_I386_SECREL 013
+#define IMAGE_REL_I386_REL32 024 // PC-relative 32-bit reference to the symbols virtual address
+
+//
+// MIPS relocation types.
+//
+
+#define IMAGE_REL_MIPS_ABSOLUTE 0 // Reference is absolute, no relocation is necessary
+#define IMAGE_REL_MIPS_REFHALF 01
+#define IMAGE_REL_MIPS_REFWORD 02
+#define IMAGE_REL_MIPS_JMPADDR 03
+#define IMAGE_REL_MIPS_REFHI 04
+#define IMAGE_REL_MIPS_REFLO 05
+#define IMAGE_REL_MIPS_GPREL 06
+#define IMAGE_REL_MIPS_LITERAL 07
+#define IMAGE_REL_MIPS_SECTION 012
+#define IMAGE_REL_MIPS_SECREL 013
+#define IMAGE_REL_MIPS_REFWORDNB 042
+#define IMAGE_REL_MIPS_PAIR 045
+
+//
+// Alpha Relocation types.
+//
+
+#define IMAGE_REL_ALPHA_ABSOLUTE 0x0
+#define IMAGE_REL_ALPHA_REFLONG 0x1
+#define IMAGE_REL_ALPHA_REFQUAD 0x2
+#define IMAGE_REL_ALPHA_GPREL32 0x3
+#define IMAGE_REL_ALPHA_LITERAL 0x4
+#define IMAGE_REL_ALPHA_LITUSE 0x5
+#define IMAGE_REL_ALPHA_GPDISP 0x6
+#define IMAGE_REL_ALPHA_BRADDR 0x7
+#define IMAGE_REL_ALPHA_HINT 0x8
+#define IMAGE_REL_ALPHA_INLINE_REFLONG 0x9
+#define IMAGE_REL_ALPHA_REFHI 0xA
+#define IMAGE_REL_ALPHA_REFLO 0xB
+#define IMAGE_REL_ALPHA_PAIR 0xC
+#define IMAGE_REL_ALPHA_MATCH 0xD
+#define IMAGE_REL_ALPHA_SECTION 0xE
+#define IMAGE_REL_ALPHA_SECREL 0xF
+#define IMAGE_REL_ALPHA_REFLONGNB 0x10
+
+//
+// IBM PowerPC relocation types.
+//
+
+#define IMAGE_REL_PPC_ABSOLUTE 0x0000 // NOP
+#define IMAGE_REL_PPC_ADDR64 0x0001 // 64-bit address
+#define IMAGE_REL_PPC_ADDR32 0x0002 // 32-bit address
+#define IMAGE_REL_PPC_ADDR24 0x0003 // 26-bit address, shifted left 2 (branch absolute)
+#define IMAGE_REL_PPC_ADDR16 0x0004 // 16-bit address
+#define IMAGE_REL_PPC_ADDR14 0x0005 // 16-bit address, shifted left 2 (load doubleword)
+#define IMAGE_REL_PPC_REL24 0x0006 // 26-bit PC-relative offset, shifted left 2 (branch relative)
+#define IMAGE_REL_PPC_REL14 0x0007 // 16-bit PC-relative offset, shifted left 2 (br cond relative)
+#define IMAGE_REL_PPC_TOCREL16 0x0008 // 16-bit offset from TOC base
+#define IMAGE_REL_PPC_TOCREL14 0x0009 // 16-bit offset from TOC base, shifted left 2 (load doubleword)
+
+#define IMAGE_REL_PPC_ADDR32NB 0x000A // 32-bit addr w/o image base
+#define IMAGE_REL_PPC_SECREL 0x000B // va of containing section (as in an image sectionhdr)
+#define IMAGE_REL_PPC_SECTION 0x000C // sectionheader number
+#define IMAGE_REL_PPC_IFGLUE 0x000D // substitute TOC restore instruction iff symbol is glue code
+#define IMAGE_REL_PPC_IMGLUE 0x000E // symbol is glue code; virtual address is TOC restore instruction
+
+#define IMAGE_REL_PPC_TYPEMASK 0x00FF // mask to isolate above values in IMAGE_RELOCATION.Type
+
+// Flag bits in IMAGE_RELOCATION.TYPE
+
+#define IMAGE_REL_PPC_NEG 0x0100 // subtract reloc value rather than adding it
+#define IMAGE_REL_PPC_BRTAKEN 0x0200 // fix branch prediction bit to predict branch taken
+#define IMAGE_REL_PPC_BRNTAKEN 0x0400 // fix branch prediction bit to predict branch not taken
+#define IMAGE_REL_PPC_TOCDEFN 0x0800 // toc slot defined in file (or, data in toc)
+
+//
+// Based relocation format.
+//
+
+typedef struct _IMAGE_BASE_RELOCATION {
+ UINT32 VirtualAddress;
+ UINT32 SizeOfBlock;
+// UINT16 TypeOffset[1];
+} IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
+
+#define IMAGE_SIZEOF_BASE_RELOCATION 8
+
+//
+// Based relocation types.
+//
+
+#define IMAGE_REL_BASED_ABSOLUTE 0
+#define IMAGE_REL_BASED_HIGH 1
+#define IMAGE_REL_BASED_LOW 2
+#define IMAGE_REL_BASED_HIGHLOW 3
+#define IMAGE_REL_BASED_HIGHADJ 4
+#define IMAGE_REL_BASED_MIPS_JMPADDR 5
+#define IMAGE_REL_BASED_DIR64 10
+
+//
+// Line number format.
+//
+
+typedef struct _IMAGE_LINENUMBER {
+ 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.
+} IMAGE_LINENUMBER;
+
+#define IMAGE_SIZEOF_LINENUMBER 6
+
+//
+// Archive format.
+//
+
+#define IMAGE_ARCHIVE_START_SIZE 8
+#define IMAGE_ARCHIVE_START "!<arch>\n"
+#define IMAGE_ARCHIVE_END "`\n"
+#define IMAGE_ARCHIVE_PAD "\n"
+#define IMAGE_ARCHIVE_LINKER_MEMBER "/ "
+#define IMAGE_ARCHIVE_LONGNAMES_MEMBER "// "
+
+typedef struct _IMAGE_ARCHIVE_MEMBER_HEADER {
+ 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.
+} IMAGE_ARCHIVE_MEMBER_HEADER, *PIMAGE_ARCHIVE_MEMBER_HEADER;
+
+#define IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60
+
+//
+// DLL support.
+//
+
+//
+// Export Format
+//
+
+typedef struct _IMAGE_EXPORT_DIRECTORY {
+ UINT32 Characteristics;
+ UINT32 TimeDateStamp;
+ UINT16 MajorVersion;
+ UINT16 MinorVersion;
+ UINT32 Name;
+ UINT32 Base;
+ UINT32 NumberOfFunctions;
+ UINT32 NumberOfNames;
+ UINT32 *AddressOfFunctions;
+ UINT32 *AddressOfNames;
+ UINT32 *AddressOfNameOrdinals;
+} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
+
+//
+// Import Format
+//
+
+typedef struct _IMAGE_IMPORT_BY_NAME {
+ UINT16 Hint;
+ UINT8 Name[1];
+} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
+
+typedef struct _IMAGE_THUNK_DATA {
+ union {
+ UINT32 Function;
+ UINT32 Ordinal;
+ PIMAGE_IMPORT_BY_NAME AddressOfData;
+ } u1;
+} IMAGE_THUNK_DATA, *PIMAGE_THUNK_DATA;
+
+#define IMAGE_ORDINAL_FLAG 0x80000000
+#define IMAGE_SNAP_BY_ORDINAL(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG) != 0)
+#define IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff)
+
+typedef struct _IMAGE_IMPORT_DESCRIPTOR {
+ UINT32 Characteristics;
+ UINT32 TimeDateStamp;
+ UINT32 ForwarderChain;
+ UINT32 Name;
+ PIMAGE_THUNK_DATA FirstThunk;
+} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;
+
+#define IMAGE_DEBUG_TYPE_CODEVIEW 2
+
+typedef struct {
+ UINT32 Characteristics;
+ UINT32 TimeDateStamp;
+ UINT16 MajorVersion;
+ UINT16 MinorVersion;
+ UINT32 Type;
+ UINT32 SizeOfData;
+ UINT32 RVA;
+ UINT32 FileOffset;
+} IMAGE_DEBUG_DIRECTORY_ENTRY;
+
+#define CODEVIEW_SIGNATURE_NB10 0x3031424E // "NB10"
+
+typedef struct {
+ UINT32 Signature; // "NB10"
+ UINT32 Unknown;
+ UINT32 Unknown2;
+ UINT32 Unknown3;
+ //
+ // Filename of .PDB goes here
+ //
+} EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY;
+
+#define CODEVIEW_SIGNATURE_RSDS 0x53445352 // "RSDS"
+
+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;
+
+#endif
diff --git a/stand/efi/libefi/Makefile b/stand/efi/libefi/Makefile
new file mode 100644
index 0000000..e0ffd7b
--- /dev/null
+++ b/stand/efi/libefi/Makefile
@@ -0,0 +1,57 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+.if ${MK_FORTH} != "no"
+.include "${BOOTSRC}/ficl.mk"
+.endif
+
+LIB= efi
+INTERNALLIB=
+WARNS?= 2
+
+SRCS= delay.c devpath.c efi_console.c efichar.c efinet.c efipart.c env.c errno.c \
+ handles.c wchar.c libefi.c efi_driver_utils.c efizfs.c devicename.c
+
+.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386"
+SRCS+= time.c
+.elif ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "arm"
+SRCS+= time_event.c
+.endif
+
+# We implement a slightly non-standard %S in that it always takes a
+# CHAR16 that's common in UEFI-land instead of a wchar_t. This only
+# seems to matter on arm64 where wchar_t defaults to an int instead
+# of a short. There's no good cast to use here so just ignore the
+# warnings for now.
+CWARNFLAGS.efinet.c+= -Wno-format
+CWARNFLAGS.efipart.c+= -Wno-format
+CWARNFLAGS.env.c+= -Wno-format
+
+.if ${MACHINE_CPUARCH} == "aarch64"
+CFLAGS+= -mgeneral-regs-only
+.endif
+.if ${MACHINE_ARCH} == "amd64"
+CFLAGS+= -fPIC -mno-red-zone
+.endif
+CFLAGS+= -I${EFIINC}
+CFLAGS+= -I${EFIINCMD}
+.if ${MK_ZFS} != "no"
+CFLAGS+= -I${ZFSSRC}
+CFLAGS+= -DEFI_ZFS_BOOT
+.endif
+
+# Pick up the bootstrap header for some interface items
+CFLAGS+= -I${LDRSRC}
+
+# Handle FreeBSD specific %b and %D printf format specifiers
+CFLAGS+= ${FORMAT_EXTENSIONS}
+
+# Do not use TERM_EMU on arm and arm64 as it doesn't behave well with serial console
+.if ${MACHINE_CPUARCH} != "arm" && ${MACHINE_CPUARCH} != "aarch64"
+CFLAGS+= -DTERM_EMU
+.endif
+
+CFLAGS+= -DLIBEFI
+
+.include <bsd.lib.mk>
diff --git a/stand/efi/libefi/Makefile.depend b/stand/efi/libefi/Makefile.depend
new file mode 100644
index 0000000..18be76b
--- /dev/null
+++ b/stand/efi/libefi/Makefile.depend
@@ -0,0 +1,13 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/efi/libefi/delay.c b/stand/efi/libefi/delay.c
new file mode 100644
index 0000000..723f681
--- /dev/null
+++ b/stand/efi/libefi/delay.c
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2001 Doug Rabson
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <efi.h>
+#include <efilib.h>
+
+void
+delay(int usecs)
+{
+ static EFI_EVENT ev = 0;
+ UINTN junk;
+
+ if (!ev) {
+ if (BS->CreateEvent(EVT_TIMER, TPL_APPLICATION, 0, 0, &ev)
+ != EFI_SUCCESS)
+ return;
+ }
+
+ BS->SetTimer(ev, TimerRelative, usecs * 10);
+ BS->WaitForEvent(1, &ev, &junk);
+}
diff --git a/stand/efi/libefi/devicename.c b/stand/efi/libefi/devicename.c
new file mode 100644
index 0000000..52e4799
--- /dev/null
+++ b/stand/efi/libefi/devicename.c
@@ -0,0 +1,219 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include <sys/disklabel.h>
+#include <sys/param.h>
+#include <bootstrap.h>
+#include <disk.h>
+#ifdef EFI_ZFS_BOOT
+#include <libzfs.h>
+#endif
+
+#include <efi.h>
+#include <efilib.h>
+
+static int efi_parsedev(struct devdesc **, const char *, const char **);
+
+/*
+ * Point (dev) at an allocated device specifier for the device matching the
+ * path in (devspec). If it contains an explicit device specification,
+ * use that. If not, use the default device.
+ */
+int
+efi_getdev(void **vdev, const char *devspec, const char **path)
+{
+ struct devdesc **dev = (struct devdesc **)vdev;
+ int rv;
+
+ /*
+ * If it looks like this is just a path and no device, then
+ * use the current device instead.
+ */
+ if (devspec == NULL || *devspec == '/' || !strchr(devspec, ':')) {
+ rv = efi_parsedev(dev, getenv("currdev"), NULL);
+ if (rv == 0 && path != NULL)
+ *path = devspec;
+ return (rv);
+ }
+
+ /* Parse the device name off the beginning of the devspec. */
+ return (efi_parsedev(dev, devspec, path));
+}
+
+/*
+ * Point (dev) at an allocated device specifier matching the string version
+ * at the beginning of (devspec). Return a pointer to the remaining
+ * text in (path).
+ *
+ * In all cases, the beginning of (devspec) is compared to the names
+ * of known devices in the device switch, and then any following text
+ * is parsed according to the rules applied to the device type.
+ *
+ * For disk-type devices, the syntax is:
+ *
+ * fs<unit>:
+ */
+static int
+efi_parsedev(struct devdesc **dev, const char *devspec, const char **path)
+{
+ struct devdesc *idev;
+ struct devsw *dv;
+ int i, unit, err;
+ char *cp;
+ const char *np;
+
+ /* minimum length check */
+ if (strlen(devspec) < 2)
+ return (EINVAL);
+
+ /* look for a device that matches */
+ for (i = 0; devsw[i] != NULL; i++) {
+ dv = devsw[i];
+ if (!strncmp(devspec, dv->dv_name, strlen(dv->dv_name)))
+ break;
+ }
+ if (devsw[i] == NULL)
+ return (ENOENT);
+
+ np = devspec + strlen(dv->dv_name);
+ idev = NULL;
+ err = 0;
+
+ switch (dv->dv_type) {
+ case DEVT_NONE:
+ break;
+
+ case DEVT_DISK:
+ idev = malloc(sizeof(struct disk_devdesc));
+ if (idev == NULL)
+ return (ENOMEM);
+
+ err = disk_parsedev((struct disk_devdesc *)idev, np, path);
+ if (err != 0)
+ goto fail;
+ break;
+
+#ifdef EFI_ZFS_BOOT
+ case DEVT_ZFS:
+ idev = malloc(sizeof(struct zfs_devdesc));
+ if (idev == NULL)
+ return (ENOMEM);
+
+ err = zfs_parsedev((struct zfs_devdesc*)idev, np, path);
+ if (err != 0)
+ goto fail;
+ break;
+#endif
+ default:
+ idev = malloc(sizeof(struct devdesc));
+ if (idev == NULL)
+ return (ENOMEM);
+
+ unit = 0;
+ cp = (char *)np;
+
+ if (*np != '\0' && *np != ':') {
+ errno = 0;
+ unit = strtol(np, &cp, 0);
+ if (errno != 0 || cp == np) {
+ err = EUNIT;
+ goto fail;
+ }
+ }
+ if (*cp != '\0' && *cp != ':') {
+ err = EINVAL;
+ goto fail;
+ }
+
+ idev->d_unit = unit;
+ if (path != NULL)
+ *path = (*cp == 0) ? cp : cp + 1;
+ break;
+ }
+
+ idev->d_dev = dv;
+ idev->d_type = dv->dv_type;
+
+ if (dev != NULL)
+ *dev = idev;
+ else
+ free(idev);
+ return (0);
+
+fail:
+ free(idev);
+ return (err);
+}
+
+char *
+efi_fmtdev(void *vdev)
+{
+ struct devdesc *dev = (struct devdesc *)vdev;
+ static char buf[SPECNAMELEN + 1];
+
+ switch(dev->d_type) {
+ case DEVT_NONE:
+ strcpy(buf, "(no device)");
+ break;
+
+ case DEVT_DISK:
+ return (disk_fmtdev(vdev));
+
+#ifdef EFI_ZFS_BOOT
+ case DEVT_ZFS:
+ return (zfs_fmtdev(dev));
+#endif
+ default:
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
+ break;
+ }
+
+ return (buf);
+}
+
+/*
+ * Set currdev to suit the value being supplied in (value)
+ */
+int
+efi_setcurrdev(struct env_var *ev, int flags, const void *value)
+{
+ struct devdesc *ncurr;
+ int rv;
+
+ rv = efi_parsedev(&ncurr, value, NULL);
+ if (rv != 0)
+ return (rv);
+
+ free(ncurr);
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return (0);
+}
diff --git a/stand/efi/libefi/devpath.c b/stand/efi/libefi/devpath.c
new file mode 100644
index 0000000..f881cfc
--- /dev/null
+++ b/stand/efi/libefi/devpath.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 2016 John Baldwin <jhb@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <efi.h>
+#include <efilib.h>
+
+static EFI_GUID ImageDevicePathGUID =
+ EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
+static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
+static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
+static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *textProtocol;
+
+EFI_DEVICE_PATH *
+efi_lookup_image_devpath(EFI_HANDLE handle)
+{
+ EFI_DEVICE_PATH *devpath;
+ EFI_STATUS status;
+
+ status = BS->HandleProtocol(handle, &ImageDevicePathGUID,
+ (VOID **)&devpath);
+ if (EFI_ERROR(status))
+ devpath = NULL;
+ return (devpath);
+}
+
+EFI_DEVICE_PATH *
+efi_lookup_devpath(EFI_HANDLE handle)
+{
+ EFI_DEVICE_PATH *devpath;
+ EFI_STATUS status;
+
+ status = BS->HandleProtocol(handle, &DevicePathGUID, (VOID **)&devpath);
+ if (EFI_ERROR(status))
+ devpath = NULL;
+ return (devpath);
+}
+
+CHAR16 *
+efi_devpath_name(EFI_DEVICE_PATH *devpath)
+{
+ static int once = 1;
+ EFI_STATUS status;
+
+ if (devpath == NULL)
+ return (NULL);
+ if (once) {
+ status = BS->LocateProtocol(&DevicePathToTextGUID, NULL,
+ (VOID **)&textProtocol);
+ if (EFI_ERROR(status))
+ textProtocol = NULL;
+ once = 0;
+ }
+ if (textProtocol == NULL)
+ return (NULL);
+
+ return (textProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE));
+}
+
+void
+efi_free_devpath_name(CHAR16 *text)
+{
+
+ BS->FreePool(text);
+}
+
+EFI_DEVICE_PATH *
+efi_devpath_last_node(EFI_DEVICE_PATH *devpath)
+{
+
+ if (IsDevicePathEnd(devpath))
+ return (NULL);
+ while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
+ devpath = NextDevicePathNode(devpath);
+ return (devpath);
+}
+
+EFI_DEVICE_PATH *
+efi_devpath_trim(EFI_DEVICE_PATH *devpath)
+{
+ EFI_DEVICE_PATH *node, *copy;
+ size_t prefix, len;
+
+ if ((node = efi_devpath_last_node(devpath)) == NULL)
+ return (NULL);
+ prefix = (UINT8 *)node - (UINT8 *)devpath;
+ if (prefix == 0)
+ return (NULL);
+ len = prefix + DevicePathNodeLength(NextDevicePathNode(node));
+ copy = malloc(len);
+ if (copy != NULL) {
+ memcpy(copy, devpath, prefix);
+ node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix);
+ SetDevicePathEndNode(node);
+ }
+ return (copy);
+}
+
+EFI_HANDLE
+efi_devpath_handle(EFI_DEVICE_PATH *devpath)
+{
+ EFI_STATUS status;
+ EFI_HANDLE h;
+
+ /*
+ * There isn't a standard way to locate a handle for a given
+ * device path. However, querying the EFI_DEVICE_PATH protocol
+ * for a given device path should give us a handle for the
+ * closest node in the path to the end that is valid.
+ */
+ status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h);
+ if (EFI_ERROR(status))
+ return (NULL);
+ return (h);
+}
+
+bool
+efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
+{
+ size_t len;
+
+ if (devpath1 == NULL || devpath2 == NULL)
+ return (false);
+
+ while (true) {
+ if (DevicePathType(devpath1) != DevicePathType(devpath2) ||
+ DevicePathSubType(devpath1) != DevicePathSubType(devpath2))
+ return (false);
+
+ len = DevicePathNodeLength(devpath1);
+ if (len != DevicePathNodeLength(devpath2))
+ return (false);
+
+ if (memcmp(devpath1, devpath2, len) != 0)
+ return (false);
+
+ if (IsDevicePathEnd(devpath1))
+ break;
+ devpath1 = NextDevicePathNode(devpath1);
+ devpath2 = NextDevicePathNode(devpath2);
+ }
+ return (true);
+}
+
+bool
+efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path)
+{
+ size_t len;
+
+ if (prefix == NULL || path == NULL)
+ return (false);
+
+ while (1) {
+ if (IsDevicePathEnd(prefix))
+ break;
+
+ if (DevicePathType(prefix) != DevicePathType(path) ||
+ DevicePathSubType(prefix) != DevicePathSubType(path))
+ return (false);
+
+ len = DevicePathNodeLength(prefix);
+ if (len != DevicePathNodeLength(path))
+ return (false);
+
+ if (memcmp(prefix, path, len) != 0)
+ return (false);
+
+ prefix = NextDevicePathNode(prefix);
+ path = NextDevicePathNode(path);
+ }
+ return (true);
+}
diff --git a/stand/efi/libefi/efi_console.c b/stand/efi/libefi/efi_console.c
new file mode 100644
index 0000000..450ed46
--- /dev/null
+++ b/stand/efi/libefi/efi_console.c
@@ -0,0 +1,518 @@
+/*-
+ * Copyright (c) 2000 Doug Rabson
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+
+static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
+static SIMPLE_INPUT_INTERFACE *conin;
+
+#ifdef TERM_EMU
+#define DEFAULT_FGCOLOR EFI_LIGHTGRAY
+#define DEFAULT_BGCOLOR EFI_BLACK
+
+#define MAXARGS 8
+static int args[MAXARGS], argc;
+static int fg_c, bg_c, curx, cury;
+static int esc;
+
+void get_pos(int *x, int *y);
+void curs_move(int *_x, int *_y, int x, int y);
+static void CL(int);
+void HO(void);
+void end_term(void);
+#endif
+
+static EFI_INPUT_KEY key_cur;
+static int key_pending;
+
+static void efi_cons_probe(struct console *);
+static int efi_cons_init(int);
+void efi_cons_putchar(int);
+int efi_cons_getchar(void);
+void efi_cons_efiputchar(int);
+int efi_cons_poll(void);
+
+struct console efi_console = {
+ "efi",
+ "EFI console",
+ C_WIDEOUT,
+ efi_cons_probe,
+ efi_cons_init,
+ efi_cons_putchar,
+ efi_cons_getchar,
+ efi_cons_poll
+};
+
+#ifdef TERM_EMU
+
+/* Get cursor position. */
+void
+get_pos(int *x, int *y)
+{
+ *x = conout->Mode->CursorColumn;
+ *y = conout->Mode->CursorRow;
+}
+
+/* Move cursor to x rows and y cols (0-based). */
+void
+curs_move(int *_x, int *_y, int x, int y)
+{
+ conout->SetCursorPosition(conout, x, y);
+ if (_x != NULL)
+ *_x = conout->Mode->CursorColumn;
+ if (_y != NULL)
+ *_y = conout->Mode->CursorRow;
+}
+
+/* Clear internal state of the terminal emulation code. */
+void
+end_term(void)
+{
+ esc = 0;
+ argc = -1;
+}
+
+#endif
+
+static void
+efi_cons_probe(struct console *cp)
+{
+ conout = ST->ConOut;
+ conin = ST->ConIn;
+ cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
+}
+
+static int
+efi_cons_init(int arg)
+{
+#ifdef TERM_EMU
+ conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
+ DEFAULT_BGCOLOR));
+ end_term();
+ get_pos(&curx, &cury);
+ curs_move(&curx, &cury, curx, cury);
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+#endif
+ conout->EnableCursor(conout, TRUE);
+ return 0;
+}
+
+static void
+efi_cons_rawputchar(int c)
+{
+ int i;
+ UINTN x, y;
+ conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
+
+ if (c == '\t')
+ /* XXX lame tab expansion */
+ for (i = 0; i < 8; i++)
+ efi_cons_rawputchar(' ');
+ else {
+#ifndef TERM_EMU
+ if (c == '\n')
+ efi_cons_efiputchar('\r');
+ efi_cons_efiputchar(c);
+#else
+ switch (c) {
+ case '\r':
+ curx = 0;
+ curs_move(&curx, &cury, curx, cury);
+ return;
+ case '\n':
+ cury++;
+ if (cury >= y) {
+ efi_cons_efiputchar('\n');
+ cury--;
+ } else
+ curs_move(&curx, &cury, curx, cury);
+ return;
+ case '\b':
+ if (curx > 0) {
+ curx--;
+ curs_move(&curx, &cury, curx, cury);
+ }
+ return;
+ default:
+ efi_cons_efiputchar(c);
+ curx++;
+ if (curx > x-1) {
+ curx = 0;
+ cury++;
+ }
+ if (cury > y-1) {
+ curx = 0;
+ cury--;
+ }
+ }
+ curs_move(&curx, &cury, curx, cury);
+#endif
+ }
+}
+
+#ifdef TERM_EMU
+/* Gracefully exit ESC-sequence processing in case of misunderstanding. */
+static void
+bail_out(int c)
+{
+ char buf[16], *ch;
+ int i;
+
+ if (esc) {
+ efi_cons_rawputchar('\033');
+ if (esc != '\033')
+ efi_cons_rawputchar(esc);
+ for (i = 0; i <= argc; ++i) {
+ sprintf(buf, "%d", args[i]);
+ ch = buf;
+ while (*ch)
+ efi_cons_rawputchar(*ch++);
+ }
+ }
+ efi_cons_rawputchar(c);
+ end_term();
+}
+
+/* Clear display from current position to end of screen. */
+static void
+CD(void) {
+ int i;
+ UINTN x, y;
+
+ get_pos(&curx, &cury);
+ if (curx == 0 && cury == 0) {
+ conout->ClearScreen(conout);
+ end_term();
+ return;
+ }
+
+ conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
+ CL(0); /* clear current line from cursor to end */
+ for (i = cury + 1; i < y-1; i++) {
+ curs_move(NULL, NULL, 0, i);
+ CL(0);
+ }
+ curs_move(NULL, NULL, curx, cury);
+ end_term();
+}
+
+/*
+ * Absolute cursor move to args[0] rows and args[1] columns
+ * (the coordinates are 1-based).
+ */
+static void
+CM(void)
+{
+ if (args[0] > 0)
+ args[0]--;
+ if (args[1] > 0)
+ args[1]--;
+ curs_move(&curx, &cury, args[1], args[0]);
+ end_term();
+}
+
+/* Home cursor (left top corner), also called from mode command. */
+void
+HO(void)
+{
+ argc = 1;
+ args[0] = args[1] = 1;
+ CM();
+}
+
+/* Clear line from current position to end of line */
+static void
+CL(int direction)
+{
+ int i, len;
+ UINTN x, y;
+ CHAR16 *line;
+
+ conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
+ switch (direction) {
+ case 0: /* from cursor to end */
+ len = x - curx + 1;
+ break;
+ case 1: /* from beginning to cursor */
+ len = curx;
+ break;
+ case 2: /* entire line */
+ len = x;
+ break;
+ default: /* NOTREACHED */
+ __unreachable();
+ }
+
+ if (cury == y - 1)
+ len--;
+
+ line = malloc(len * sizeof (CHAR16));
+ if (line == NULL) {
+ printf("out of memory\n");
+ return;
+ }
+ for (i = 0; i < len; i++)
+ line[i] = ' ';
+ line[len-1] = 0;
+
+ if (direction != 0)
+ curs_move(NULL, NULL, 0, cury);
+
+ conout->OutputString(conout, line);
+ /* restore cursor position */
+ curs_move(NULL, NULL, curx, cury);
+ free(line);
+ end_term();
+}
+
+static void
+get_arg(int c)
+{
+ if (argc < 0)
+ argc = 0;
+ args[argc] *= 10;
+ args[argc] += c - '0';
+}
+
+/* Emulate basic capabilities of cons25 terminal */
+static void
+efi_term_emu(int c)
+{
+ static int ansi_col[] = {
+ 0, 4, 2, 6, 1, 5, 3, 7
+ };
+ int t, i;
+
+ switch (esc) {
+ case 0:
+ switch (c) {
+ case '\033':
+ esc = c;
+ break;
+ default:
+ efi_cons_rawputchar(c);
+ break;
+ }
+ break;
+ case '\033':
+ switch (c) {
+ case '[':
+ esc = c;
+ args[0] = 0;
+ argc = -1;
+ break;
+ default:
+ bail_out(c);
+ break;
+ }
+ break;
+ case '[':
+ switch (c) {
+ case ';':
+ if (argc < 0)
+ argc = 0;
+ else if (argc + 1 >= MAXARGS)
+ bail_out(c);
+ else
+ args[++argc] = 0;
+ break;
+ case 'H': /* ho = \E[H */
+ if (argc < 0)
+ HO();
+ else if (argc == 1)
+ CM();
+ else
+ bail_out(c);
+ break;
+ case 'J': /* cd = \E[J */
+ if (argc < 0)
+ CD();
+ else
+ bail_out(c);
+ break;
+ case 'm':
+ if (argc < 0) {
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+ }
+ for (i = 0; i <= argc; ++i) {
+ switch (args[i]) {
+ case 0: /* back to normal */
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+ break;
+ case 1: /* bold */
+ fg_c |= 0x8;
+ break;
+ case 4: /* underline */
+ case 5: /* blink */
+ bg_c |= 0x8;
+ break;
+ case 7: /* reverse */
+ t = fg_c;
+ fg_c = bg_c;
+ bg_c = t;
+ break;
+ case 30: case 31: case 32: case 33:
+ case 34: case 35: case 36: case 37:
+ fg_c = ansi_col[args[i] - 30];
+ break;
+ case 39: /* normal */
+ fg_c = DEFAULT_FGCOLOR;
+ break;
+ case 40: case 41: case 42: case 43:
+ case 44: case 45: case 46: case 47:
+ bg_c = ansi_col[args[i] - 40];
+ break;
+ case 49: /* normal */
+ bg_c = DEFAULT_BGCOLOR;
+ break;
+ }
+ }
+ conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
+ end_term();
+ break;
+ default:
+ if (isdigit(c))
+ get_arg(c);
+ else
+ bail_out(c);
+ break;
+ }
+ break;
+ default:
+ bail_out(c);
+ break;
+ }
+}
+#else
+void
+HO(void)
+{
+}
+#endif
+
+void
+efi_cons_putchar(int c)
+{
+#ifdef TERM_EMU
+ efi_term_emu(c);
+#else
+ efi_cons_rawputchar(c);
+#endif
+}
+
+int
+efi_cons_getchar()
+{
+ EFI_INPUT_KEY key;
+ EFI_STATUS status;
+ UINTN junk;
+
+ if (key_pending) {
+ key = key_cur;
+ key_pending = 0;
+ } else {
+ /* Try to read a key stroke. We wait for one if none is pending. */
+ status = conin->ReadKeyStroke(conin, &key);
+ while (status == EFI_NOT_READY) {
+ /* Some EFI implementation (u-boot for example) do not support WaitForKey */
+ if (conin->WaitForKey != NULL)
+ BS->WaitForEvent(1, &conin->WaitForKey, &junk);
+ status = conin->ReadKeyStroke(conin, &key);
+ }
+ }
+
+ switch (key.ScanCode) {
+ case 0x17: /* ESC */
+ return (0x1b); /* esc */
+ }
+
+ /* this can return */
+ return (key.UnicodeChar);
+}
+
+int
+efi_cons_poll()
+{
+ EFI_INPUT_KEY key;
+ EFI_STATUS status;
+
+ if (conin->WaitForKey == NULL) {
+ if (key_pending)
+ return (1);
+ status = conin->ReadKeyStroke(conin, &key);
+ if (status == EFI_SUCCESS) {
+ key_cur = key;
+ key_pending = 1;
+ }
+ return (key_pending);
+ }
+
+ /* This can clear the signaled state. */
+ return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
+}
+
+/* Plain direct access to EFI OutputString(). */
+void
+efi_cons_efiputchar(int c)
+{
+ CHAR16 buf[2];
+
+ /*
+ * translate box chars to unicode
+ */
+ switch (c) {
+ /* single frame */
+ case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
+ case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
+ case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
+ case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
+ case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
+ case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
+
+ /* double frame */
+ case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
+ case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
+ case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
+ case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
+ case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
+ case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
+
+ default:
+ buf[0] = c;
+ }
+ buf[1] = 0; /* terminate string */
+
+ conout->OutputString(conout, buf);
+}
diff --git a/stand/efi/libefi/efi_driver_utils.c b/stand/efi/libefi/efi_driver_utils.c
new file mode 100644
index 0000000..0edea5c
--- /dev/null
+++ b/stand/efi/libefi/efi_driver_utils.c
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 2017 Eric McCorkle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdbool.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "efi_driver_utils.h"
+
+static EFI_GUID DriverBindingProtocolGUID = DRIVER_BINDING_PROTOCOL;
+
+EFI_STATUS
+connect_controllers(EFI_GUID *filter)
+{
+ EFI_STATUS status;
+ EFI_HANDLE *handles;
+ UINTN nhandles, i, hsize;
+
+ nhandles = 0;
+ hsize = 0;
+ status = BS->LocateHandle(ByProtocol, filter, NULL,
+ &hsize, NULL);
+
+ if(status != EFI_BUFFER_TOO_SMALL) {
+ return (status);
+ }
+
+ handles = malloc(hsize);
+ nhandles = hsize / sizeof(EFI_HANDLE);
+
+ status = BS->LocateHandle(ByProtocol, filter, NULL,
+ &hsize, handles);
+
+ if(EFI_ERROR(status)) {
+ return (status);
+ }
+
+ for(i = 0; i < nhandles; i++) {
+ BS->ConnectController(handles[i], NULL, NULL, true);
+ }
+
+ free(handles);
+
+ return (status);
+}
+
+EFI_STATUS
+install_driver(EFI_DRIVER_BINDING *driver)
+{
+ EFI_STATUS status;
+
+ driver->ImageHandle = IH;
+ driver->DriverBindingHandle = NULL;
+ status = BS->InstallMultipleProtocolInterfaces(
+ &(driver->DriverBindingHandle),
+ &DriverBindingProtocolGUID, driver,
+ NULL);
+
+ if (EFI_ERROR(status)) {
+ printf("Failed to install driver (%ld)!\n",
+ EFI_ERROR_CODE(status));
+ }
+
+ return (status);
+}
diff --git a/stand/efi/libefi/efichar.c b/stand/efi/libefi/efichar.c
new file mode 100644
index 0000000..dad4e96
--- /dev/null
+++ b/stand/efi/libefi/efichar.c
@@ -0,0 +1,201 @@
+/*-
+ * Copyright (c) 2010 Marcel Moolenaar
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <errno.h>
+#ifdef LIBEFI
+#include <stand.h>
+#else
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#endif
+#include <sys/efi.h>
+#include <machine/efi.h>
+
+#include "efichar.h"
+
+int
+ucs2len(const efi_char *str)
+{
+ int i;
+
+ i = 0;
+ while (*str++)
+ i++;
+ return (i);
+}
+
+/*
+ * If nm were converted to utf8, what what would strlen
+ * return on the resulting string?
+ */
+static size_t
+utf8_len_of_ucs2(const efi_char *nm)
+{
+ size_t len;
+ efi_char c;
+
+ len = 0;
+ while (*nm) {
+ c = *nm++;
+ if (c > 0x7ff)
+ len += 3;
+ else if (c > 0x7f)
+ len += 2;
+ else
+ len++;
+ }
+
+ return (len);
+}
+
+int
+ucs2_to_utf8(const efi_char *nm, char **name)
+{
+ size_t len, sz;
+ efi_char c;
+ char *cp;
+ int freeit = *name == NULL;
+
+ sz = utf8_len_of_ucs2(nm) + 1;
+ len = 0;
+ if (*name != NULL)
+ cp = *name;
+ else
+ cp = *name = malloc(sz);
+ if (*name == NULL)
+ return (ENOMEM);
+
+ while (*nm) {
+ c = *nm++;
+ if (c > 0x7ff) {
+ if (len++ < sz)
+ *cp++ = (char)(0xE0 | (c >> 12));
+ if (len++ < sz)
+ *cp++ = (char)(0x80 | ((c >> 6) & 0x3f));
+ if (len++ < sz)
+ *cp++ = (char)(0x80 | (c & 0x3f));
+ } else if (c > 0x7f) {
+ if (len++ < sz)
+ *cp++ = (char)(0xC0 | ((c >> 6) & 0x1f));
+ if (len++ < sz)
+ *cp++ = (char)(0x80 | (c & 0x3f));
+ } else {
+ if (len++ < sz)
+ *cp++ = (char)(c & 0x7f);
+ }
+ }
+
+ if (len >= sz) {
+ /* Absent bugs, we'll never return EOVERFLOW */
+ if (freeit)
+ free(*name);
+ return (EOVERFLOW);
+ }
+ *cp++ = '\0';
+
+ return (0);
+}
+
+int
+utf8_to_ucs2(const char *name, efi_char **nmp, size_t *len)
+{
+ efi_char *nm;
+ size_t sz;
+ uint32_t ucs4;
+ int c, bytes;
+ int freeit = *nmp == NULL;
+
+ sz = strlen(name) * 2 + 2;
+ if (*nmp == NULL)
+ *nmp = malloc(sz);
+ nm = *nmp;
+ *len = sz;
+
+ ucs4 = 0;
+ bytes = 0;
+ while (sz > 1 && *name != '\0') {
+ c = *name++;
+ /*
+ * Conditionalize on the two major character types:
+ * initial and followup characters.
+ */
+ if ((c & 0xc0) != 0x80) {
+ /* Initial characters. */
+ if (bytes != 0) {
+ if (freeit)
+ free(nm);
+ return (EILSEQ);
+ }
+ if ((c & 0xf8) == 0xf0) {
+ ucs4 = c & 0x07;
+ bytes = 3;
+ } else if ((c & 0xf0) == 0xe0) {
+ ucs4 = c & 0x0f;
+ bytes = 2;
+ } else if ((c & 0xe0) == 0xc0) {
+ ucs4 = c & 0x1f;
+ bytes = 1;
+ } else {
+ ucs4 = c & 0x7f;
+ bytes = 0;
+ }
+ } else {
+ /* Followup characters. */
+ if (bytes > 0) {
+ ucs4 = (ucs4 << 6) + (c & 0x3f);
+ bytes--;
+ } else if (bytes == 0) {
+ if (freeit)
+ free(nm);
+ return (EILSEQ);
+ }
+ }
+ if (bytes == 0) {
+ if (ucs4 > 0xffff) {
+ if (freeit)
+ free(nm);
+ return (EILSEQ);
+ }
+ *nm++ = (efi_char)ucs4;
+ sz -= 2;
+ }
+ }
+ if (sz < 2) {
+ if (freeit)
+ free(nm);
+ return (EDOOFUS);
+ }
+ sz -= 2;
+ *nm = 0;
+ *len -= sz;
+ return (0);
+}
diff --git a/stand/efi/libefi/efinet.c b/stand/efi/libefi/efinet.c
new file mode 100644
index 0000000..cdb63c9
--- /dev/null
+++ b/stand/efi/libefi/efinet.c
@@ -0,0 +1,390 @@
+/*-
+ * Copyright (c) 2001 Doug Rabson
+ * Copyright (c) 2002, 2006 Marcel Moolenaar
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <stand.h>
+#include <net.h>
+#include <netif.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+static EFI_GUID sn_guid = EFI_SIMPLE_NETWORK_PROTOCOL;
+
+static void efinet_end(struct netif *);
+static ssize_t efinet_get(struct iodesc *, void **, time_t);
+static void efinet_init(struct iodesc *, void *);
+static int efinet_match(struct netif *, void *);
+static int efinet_probe(struct netif *, void *);
+static ssize_t efinet_put(struct iodesc *, void *, size_t);
+
+struct netif_driver efinetif = {
+ .netif_bname = "efinet",
+ .netif_match = efinet_match,
+ .netif_probe = efinet_probe,
+ .netif_init = efinet_init,
+ .netif_get = efinet_get,
+ .netif_put = efinet_put,
+ .netif_end = efinet_end,
+ .netif_ifs = NULL,
+ .netif_nifs = 0
+};
+
+#ifdef EFINET_DEBUG
+static void
+dump_mode(EFI_SIMPLE_NETWORK_MODE *mode)
+{
+ int i;
+
+ printf("State = %x\n", mode->State);
+ printf("HwAddressSize = %u\n", mode->HwAddressSize);
+ printf("MediaHeaderSize = %u\n", mode->MediaHeaderSize);
+ printf("MaxPacketSize = %u\n", mode->MaxPacketSize);
+ printf("NvRamSize = %u\n", mode->NvRamSize);
+ printf("NvRamAccessSize = %u\n", mode->NvRamAccessSize);
+ printf("ReceiveFilterMask = %x\n", mode->ReceiveFilterMask);
+ printf("ReceiveFilterSetting = %u\n", mode->ReceiveFilterSetting);
+ printf("MaxMCastFilterCount = %u\n", mode->MaxMCastFilterCount);
+ printf("MCastFilterCount = %u\n", mode->MCastFilterCount);
+ printf("MCastFilter = {");
+ for (i = 0; i < mode->MCastFilterCount; i++)
+ printf(" %s", ether_sprintf(mode->MCastFilter[i].Addr));
+ printf(" }\n");
+ printf("CurrentAddress = %s\n",
+ ether_sprintf(mode->CurrentAddress.Addr));
+ printf("BroadcastAddress = %s\n",
+ ether_sprintf(mode->BroadcastAddress.Addr));
+ printf("PermanentAddress = %s\n",
+ ether_sprintf(mode->PermanentAddress.Addr));
+ printf("IfType = %u\n", mode->IfType);
+ printf("MacAddressChangeable = %d\n", mode->MacAddressChangeable);
+ printf("MultipleTxSupported = %d\n", mode->MultipleTxSupported);
+ printf("MediaPresentSupported = %d\n", mode->MediaPresentSupported);
+ printf("MediaPresent = %d\n", mode->MediaPresent);
+}
+#endif
+
+static int
+efinet_match(struct netif *nif, void *machdep_hint)
+{
+ struct devdesc *dev = machdep_hint;
+
+ if (dev->d_unit == nif->nif_unit)
+ return (1);
+ return(0);
+}
+
+static int
+efinet_probe(struct netif *nif, void *machdep_hint)
+{
+
+ return (0);
+}
+
+static ssize_t
+efinet_put(struct iodesc *desc, void *pkt, size_t len)
+{
+ struct netif *nif = desc->io_netif;
+ EFI_SIMPLE_NETWORK *net;
+ EFI_STATUS status;
+ void *buf;
+
+ net = nif->nif_devdata;
+ if (net == NULL)
+ return (-1);
+
+ status = net->Transmit(net, 0, len, pkt, NULL, NULL, NULL);
+ if (status != EFI_SUCCESS)
+ return (-1);
+
+ /* Wait for the buffer to be transmitted */
+ do {
+ buf = NULL; /* XXX Is this needed? */
+ status = net->GetStatus(net, NULL, &buf);
+ /*
+ * XXX EFI1.1 and the E1000 card returns a different
+ * address than we gave. Sigh.
+ */
+ } while (status == EFI_SUCCESS && buf == NULL);
+
+ /* XXX How do we deal with status != EFI_SUCCESS now? */
+ return ((status == EFI_SUCCESS) ? len : -1);
+}
+
+static ssize_t
+efinet_get(struct iodesc *desc, void **pkt, time_t timeout)
+{
+ struct netif *nif = desc->io_netif;
+ EFI_SIMPLE_NETWORK *net;
+ EFI_STATUS status;
+ UINTN bufsz;
+ time_t t;
+ char *buf, *ptr;
+ ssize_t ret = -1;
+
+ net = nif->nif_devdata;
+ if (net == NULL)
+ return (ret);
+
+ bufsz = net->Mode->MaxPacketSize + ETHER_HDR_LEN + ETHER_CRC_LEN;
+ buf = malloc(bufsz + ETHER_ALIGN);
+ if (buf == NULL)
+ return (ret);
+ ptr = buf + ETHER_ALIGN;
+
+ t = getsecs();
+ while ((getsecs() - t) < timeout) {
+ status = net->Receive(net, NULL, &bufsz, ptr, NULL, NULL, NULL);
+ if (status == EFI_SUCCESS) {
+ *pkt = buf;
+ ret = (ssize_t)bufsz;
+ break;
+ }
+ if (status != EFI_NOT_READY)
+ break;
+ }
+
+ if (ret == -1)
+ free(buf);
+ return (ret);
+}
+
+static void
+efinet_init(struct iodesc *desc, void *machdep_hint)
+{
+ struct netif *nif = desc->io_netif;
+ EFI_SIMPLE_NETWORK *net;
+ EFI_HANDLE h;
+ EFI_STATUS status;
+ UINT32 mask;
+
+ if (nif->nif_driver->netif_ifs[nif->nif_unit].dif_unit < 0) {
+ printf("Invalid network interface %d\n", nif->nif_unit);
+ return;
+ }
+
+ h = nif->nif_driver->netif_ifs[nif->nif_unit].dif_private;
+ status = BS->HandleProtocol(h, &sn_guid, (VOID **)&nif->nif_devdata);
+ if (status != EFI_SUCCESS) {
+ printf("net%d: cannot fetch interface data (status=%lu)\n",
+ nif->nif_unit, EFI_ERROR_CODE(status));
+ return;
+ }
+
+ net = nif->nif_devdata;
+ if (net->Mode->State == EfiSimpleNetworkStopped) {
+ status = net->Start(net);
+ if (status != EFI_SUCCESS) {
+ printf("net%d: cannot start interface (status=%lu)\n",
+ nif->nif_unit, EFI_ERROR_CODE(status));
+ return;
+ }
+ }
+
+ if (net->Mode->State != EfiSimpleNetworkInitialized) {
+ status = net->Initialize(net, 0, 0);
+ if (status != EFI_SUCCESS) {
+ printf("net%d: cannot init. interface (status=%lu)\n",
+ nif->nif_unit, EFI_ERROR_CODE(status));
+ return;
+ }
+ }
+
+ mask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
+ EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+
+ status = net->ReceiveFilters(net, mask, 0, FALSE, 0, NULL);
+ if (status != EFI_SUCCESS) {
+ printf("net%d: cannot set rx. filters (status=%lu)\n",
+ nif->nif_unit, EFI_ERROR_CODE(status));
+ return;
+ }
+
+#ifdef EFINET_DEBUG
+ dump_mode(net->Mode);
+#endif
+
+ bcopy(net->Mode->CurrentAddress.Addr, desc->myea, 6);
+ desc->xid = 1;
+}
+
+static void
+efinet_end(struct netif *nif)
+{
+ EFI_SIMPLE_NETWORK *net = nif->nif_devdata;
+
+ if (net == NULL)
+ return;
+
+ net->Shutdown(net);
+}
+
+static int efinet_dev_init(void);
+static int efinet_dev_print(int);
+
+struct devsw efinet_dev = {
+ .dv_name = "net",
+ .dv_type = DEVT_NET,
+ .dv_init = efinet_dev_init,
+ .dv_strategy = NULL, /* Will be set in efinet_dev_init */
+ .dv_open = NULL, /* Will be set in efinet_dev_init */
+ .dv_close = NULL, /* Will be set in efinet_dev_init */
+ .dv_ioctl = noioctl,
+ .dv_print = efinet_dev_print,
+ .dv_cleanup = NULL
+};
+
+static int
+efinet_dev_init()
+{
+ struct netif_dif *dif;
+ struct netif_stats *stats;
+ EFI_DEVICE_PATH *devpath, *node;
+ EFI_SIMPLE_NETWORK *net;
+ EFI_HANDLE *handles, *handles2;
+ EFI_STATUS status;
+ UINTN sz;
+ int err, i, nifs;
+ extern struct devsw netdev;
+
+ sz = 0;
+ handles = NULL;
+ status = BS->LocateHandle(ByProtocol, &sn_guid, NULL, &sz, NULL);
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ handles = (EFI_HANDLE *)malloc(sz);
+ status = BS->LocateHandle(ByProtocol, &sn_guid, NULL, &sz,
+ handles);
+ if (EFI_ERROR(status))
+ free(handles);
+ }
+ if (EFI_ERROR(status))
+ return (efi_status_to_errno(status));
+ handles2 = (EFI_HANDLE *)malloc(sz);
+ if (handles2 == NULL) {
+ free(handles);
+ return (ENOMEM);
+ }
+ nifs = 0;
+ for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
+ devpath = efi_lookup_devpath(handles[i]);
+ if (devpath == NULL)
+ continue;
+ if ((node = efi_devpath_last_node(devpath)) == NULL)
+ continue;
+
+ if (DevicePathType(node) != MESSAGING_DEVICE_PATH ||
+ DevicePathSubType(node) != MSG_MAC_ADDR_DP)
+ continue;
+
+ /*
+ * Open the network device in exclusive mode. Without this
+ * we will be racing with the UEFI network stack. It will
+ * pull packets off the network leading to lost packets.
+ */
+ status = BS->OpenProtocol(handles[i], &sn_guid, (void **)&net,
+ IH, NULL, EFI_OPEN_PROTOCOL_EXCLUSIVE);
+ if (status != EFI_SUCCESS) {
+ printf("Unable to open network interface %d for "
+ "exclusive access: %lu\n", i,
+ EFI_ERROR_CODE(status));
+ }
+
+ handles2[nifs] = handles[i];
+ nifs++;
+ }
+ free(handles);
+ if (nifs == 0) {
+ err = ENOENT;
+ goto done;
+ }
+
+ err = efi_register_handles(&efinet_dev, handles2, NULL, nifs);
+ if (err != 0)
+ goto done;
+
+ efinetif.netif_ifs = calloc(nifs, sizeof(struct netif_dif));
+ stats = calloc(nifs, sizeof(struct netif_stats));
+ if (efinetif.netif_ifs == NULL || stats == NULL) {
+ free(efinetif.netif_ifs);
+ free(stats);
+ efinetif.netif_ifs = NULL;
+ err = ENOMEM;
+ goto done;
+ }
+ efinetif.netif_nifs = nifs;
+
+ for (i = 0; i < nifs; i++) {
+
+ dif = &efinetif.netif_ifs[i];
+ dif->dif_unit = i;
+ dif->dif_nsel = 1;
+ dif->dif_stats = &stats[i];
+ dif->dif_private = handles2[i];
+ }
+
+ efinet_dev.dv_open = netdev.dv_open;
+ efinet_dev.dv_close = netdev.dv_close;
+ efinet_dev.dv_strategy = netdev.dv_strategy;
+
+done:
+ free(handles2);
+ return (err);
+}
+
+static int
+efinet_dev_print(int verbose)
+{
+ CHAR16 *text;
+ EFI_HANDLE h;
+ int unit, ret = 0;
+
+ printf("%s devices:", efinet_dev.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ for (unit = 0, h = efi_find_handle(&efinet_dev, 0);
+ h != NULL; h = efi_find_handle(&efinet_dev, ++unit)) {
+ printf(" %s%d:", efinet_dev.dv_name, unit);
+ if (verbose) {
+ text = efi_devpath_name(efi_lookup_devpath(h));
+ if (text != NULL) {
+ printf(" %S", text);
+ efi_free_devpath_name(text);
+ }
+ }
+ if ((ret = pager_output("\n")) != 0)
+ break;
+ }
+ return (ret);
+}
diff --git a/stand/efi/libefi/efipart.c b/stand/efi/libefi/efipart.c
new file mode 100644
index 0000000..724233c
--- /dev/null
+++ b/stand/efi/libefi/efipart.c
@@ -0,0 +1,984 @@
+/*-
+ * Copyright (c) 2010 Marcel Moolenaar
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/disk.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <stddef.h>
+#include <stdarg.h>
+
+#include <bootstrap.h>
+
+#include <efi.h>
+#include <efilib.h>
+#include <efiprot.h>
+#include <disk.h>
+
+static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
+
+static int efipart_initfd(void);
+static int efipart_initcd(void);
+static int efipart_inithd(void);
+
+static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
+static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
+
+static int efipart_open(struct open_file *, ...);
+static int efipart_close(struct open_file *);
+static int efipart_ioctl(struct open_file *, u_long, void *);
+
+static int efipart_printfd(int);
+static int efipart_printcd(int);
+static int efipart_printhd(int);
+
+/* EISA PNP ID's for floppy controllers */
+#define PNP0604 0x604
+#define PNP0700 0x700
+#define PNP0701 0x701
+
+struct devsw efipart_fddev = {
+ .dv_name = "fd",
+ .dv_type = DEVT_FD,
+ .dv_init = efipart_initfd,
+ .dv_strategy = efipart_strategy,
+ .dv_open = efipart_open,
+ .dv_close = efipart_close,
+ .dv_ioctl = efipart_ioctl,
+ .dv_print = efipart_printfd,
+ .dv_cleanup = NULL
+};
+
+struct devsw efipart_cddev = {
+ .dv_name = "cd",
+ .dv_type = DEVT_CD,
+ .dv_init = efipart_initcd,
+ .dv_strategy = efipart_strategy,
+ .dv_open = efipart_open,
+ .dv_close = efipart_close,
+ .dv_ioctl = efipart_ioctl,
+ .dv_print = efipart_printcd,
+ .dv_cleanup = NULL
+};
+
+struct devsw efipart_hddev = {
+ .dv_name = "disk",
+ .dv_type = DEVT_DISK,
+ .dv_init = efipart_inithd,
+ .dv_strategy = efipart_strategy,
+ .dv_open = efipart_open,
+ .dv_close = efipart_close,
+ .dv_ioctl = efipart_ioctl,
+ .dv_print = efipart_printhd,
+ .dv_cleanup = NULL
+};
+
+static pdinfo_list_t fdinfo;
+static pdinfo_list_t cdinfo;
+static pdinfo_list_t hdinfo;
+
+static EFI_HANDLE *efipart_handles = NULL;
+static UINTN efipart_nhandles = 0;
+
+pdinfo_list_t *
+efiblk_get_pdinfo_list(struct devsw *dev)
+{
+ if (dev->dv_type == DEVT_DISK)
+ return (&hdinfo);
+ if (dev->dv_type == DEVT_CD)
+ return (&cdinfo);
+ if (dev->dv_type == DEVT_FD)
+ return (&fdinfo);
+ return (NULL);
+}
+
+pdinfo_t *
+efiblk_get_pdinfo(struct devdesc *dev)
+{
+ pdinfo_list_t *pdi;
+ pdinfo_t *pd = NULL;
+
+ pdi = efiblk_get_pdinfo_list(dev->d_dev);
+ if (pdi == NULL)
+ return (pd);
+
+ STAILQ_FOREACH(pd, pdi, pd_link) {
+ if (pd->pd_unit == dev->d_unit)
+ return (pd);
+ }
+ return (pd);
+}
+
+static int
+efiblk_pdinfo_count(pdinfo_list_t *pdi)
+{
+ pdinfo_t *pd;
+ int i = 0;
+
+ STAILQ_FOREACH(pd, pdi, pd_link) {
+ i++;
+ }
+ return (i);
+}
+
+static int
+efipart_inithandles(void)
+{
+ UINTN sz;
+ EFI_HANDLE *hin;
+ EFI_STATUS status;
+
+ if (efipart_nhandles != 0) {
+ free(efipart_handles);
+ efipart_handles = NULL;
+ efipart_nhandles = 0;
+ }
+
+ sz = 0;
+ hin = NULL;
+ status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ hin = malloc(sz);
+ status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
+ hin);
+ if (EFI_ERROR(status))
+ free(hin);
+ }
+ if (EFI_ERROR(status))
+ return (efi_status_to_errno(status));
+
+ efipart_handles = hin;
+ efipart_nhandles = sz;
+ return (0);
+}
+
+static ACPI_HID_DEVICE_PATH *
+efipart_floppy(EFI_DEVICE_PATH *node)
+{
+ ACPI_HID_DEVICE_PATH *acpi;
+
+ if (DevicePathType(node) == ACPI_DEVICE_PATH &&
+ DevicePathSubType(node) == ACPI_DP) {
+ acpi = (ACPI_HID_DEVICE_PATH *) node;
+ if (acpi->HID == EISA_PNP_ID(PNP0604) ||
+ acpi->HID == EISA_PNP_ID(PNP0700) ||
+ acpi->HID == EISA_PNP_ID(PNP0701)) {
+ return (acpi);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Add or update entries with new handle data.
+ */
+static int
+efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath)
+{
+ pdinfo_t *fd;
+
+ fd = calloc(1, sizeof(pdinfo_t));
+ if (fd == NULL) {
+ printf("Failed to register floppy %d, out of memory\n", uid);
+ return (ENOMEM);
+ }
+ STAILQ_INIT(&fd->pd_part);
+
+ fd->pd_unit = uid;
+ fd->pd_handle = handle;
+ fd->pd_devpath = devpath;
+ STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
+ return (0);
+}
+
+static void
+efipart_updatefd(void)
+{
+ EFI_DEVICE_PATH *devpath, *node;
+ ACPI_HID_DEVICE_PATH *acpi;
+ int i, nin;
+
+ nin = efipart_nhandles / sizeof (*efipart_handles);
+ for (i = 0; i < nin; i++) {
+ devpath = efi_lookup_devpath(efipart_handles[i]);
+ if (devpath == NULL)
+ continue;
+
+ if ((node = efi_devpath_last_node(devpath)) == NULL)
+ continue;
+ if ((acpi = efipart_floppy(node)) != NULL) {
+ efipart_fdinfo_add(efipart_handles[i], acpi->UID,
+ devpath);
+ }
+ }
+}
+
+static int
+efipart_initfd(void)
+{
+ int rv;
+
+ rv = efipart_inithandles();
+ if (rv != 0)
+ return (rv);
+ STAILQ_INIT(&fdinfo);
+
+ efipart_updatefd();
+
+ bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
+ return (0);
+}
+
+/*
+ * Add or update entries with new handle data.
+ */
+static int
+efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias,
+ EFI_DEVICE_PATH *devpath)
+{
+ int unit;
+ pdinfo_t *cd;
+ pdinfo_t *pd;
+
+ unit = 0;
+ STAILQ_FOREACH(pd, &cdinfo, pd_link) {
+ if (efi_devpath_match(pd->pd_devpath, devpath) == true) {
+ pd->pd_handle = handle;
+ pd->pd_alias = alias;
+ return (0);
+ }
+ unit++;
+ }
+
+ cd = calloc(1, sizeof(pdinfo_t));
+ if (cd == NULL) {
+ printf("Failed to add cd %d, out of memory\n", unit);
+ return (ENOMEM);
+ }
+ STAILQ_INIT(&cd->pd_part);
+
+ cd->pd_handle = handle;
+ cd->pd_unit = unit;
+ cd->pd_alias = alias;
+ cd->pd_devpath = devpath;
+ STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
+ return (0);
+}
+
+static void
+efipart_updatecd(void)
+{
+ int i, nin;
+ EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
+ EFI_HANDLE handle;
+ EFI_BLOCK_IO *blkio;
+ EFI_STATUS status;
+
+ nin = efipart_nhandles / sizeof (*efipart_handles);
+ for (i = 0; i < nin; i++) {
+ devpath = efi_lookup_devpath(efipart_handles[i]);
+ if (devpath == NULL)
+ continue;
+
+ if ((node = efi_devpath_last_node(devpath)) == NULL)
+ continue;
+ if (efipart_floppy(node) != NULL)
+ continue;
+
+ status = BS->HandleProtocol(efipart_handles[i],
+ &blkio_guid, (void **)&blkio);
+ if (EFI_ERROR(status))
+ continue;
+ /*
+ * If we come across a logical partition of subtype CDROM
+ * it doesn't refer to the CD filesystem itself, but rather
+ * to any usable El Torito boot image on it. In this case
+ * we try to find the parent device and add that instead as
+ * that will be the CD filesystem.
+ */
+ if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
+ DevicePathSubType(node) == MEDIA_CDROM_DP) {
+ devpathcpy = efi_devpath_trim(devpath);
+ if (devpathcpy == NULL)
+ continue;
+ tmpdevpath = devpathcpy;
+ status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
+ &handle);
+ free(devpathcpy);
+ if (EFI_ERROR(status))
+ continue;
+ devpath = efi_lookup_devpath(handle);
+ efipart_cdinfo_add(handle, efipart_handles[i],
+ devpath);
+ continue;
+ }
+
+ if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
+ DevicePathSubType(node) == MSG_ATAPI_DP) {
+ efipart_cdinfo_add(efipart_handles[i], NULL,
+ devpath);
+ continue;
+ }
+
+ /* USB or SATA cd without the media. */
+ if (blkio->Media->RemovableMedia &&
+ !blkio->Media->MediaPresent) {
+ efipart_cdinfo_add(efipart_handles[i], NULL,
+ devpath);
+ }
+ }
+}
+
+static int
+efipart_initcd(void)
+{
+ int rv;
+
+ rv = efipart_inithandles();
+ if (rv != 0)
+ return (rv);
+ STAILQ_INIT(&cdinfo);
+
+ efipart_updatecd();
+
+ bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
+ return (0);
+}
+
+static int
+efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle)
+{
+ EFI_DEVICE_PATH *disk_devpath, *part_devpath;
+ HARDDRIVE_DEVICE_PATH *node;
+ int unit;
+ pdinfo_t *hd, *pd, *last;
+
+ disk_devpath = efi_lookup_devpath(disk_handle);
+ part_devpath = efi_lookup_devpath(part_handle);
+ if (disk_devpath == NULL || part_devpath == NULL) {
+ return (ENOENT);
+ }
+ node = (HARDDRIVE_DEVICE_PATH *)efi_devpath_last_node(part_devpath);
+ if (node == NULL)
+ return (ENOENT); /* This should not happen. */
+
+ pd = calloc(1, sizeof(pdinfo_t));
+ if (pd == NULL) {
+ printf("Failed to add disk, out of memory\n");
+ return (ENOMEM);
+ }
+ STAILQ_INIT(&pd->pd_part);
+
+ STAILQ_FOREACH(hd, &hdinfo, pd_link) {
+ if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) {
+ /* Add the partition. */
+ pd->pd_handle = part_handle;
+ pd->pd_unit = node->PartitionNumber;
+ pd->pd_devpath = part_devpath;
+ STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
+ return (0);
+ }
+ }
+
+ last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
+ if (last != NULL)
+ unit = last->pd_unit + 1;
+ else
+ unit = 0;
+
+ /* Add the disk. */
+ hd = pd;
+ hd->pd_handle = disk_handle;
+ hd->pd_unit = unit;
+ hd->pd_devpath = disk_devpath;
+ STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
+
+ pd = calloc(1, sizeof(pdinfo_t));
+ if (pd == NULL) {
+ printf("Failed to add partition, out of memory\n");
+ return (ENOMEM);
+ }
+ STAILQ_INIT(&pd->pd_part);
+
+ /* Add the partition. */
+ pd->pd_handle = part_handle;
+ pd->pd_unit = node->PartitionNumber;
+ pd->pd_devpath = part_devpath;
+ STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
+
+ return (0);
+}
+
+/*
+ * The MEDIA_FILEPATH_DP has device name.
+ * From U-Boot sources it looks like names are in the form
+ * of typeN:M, where type is interface type, N is disk id
+ * and M is partition id.
+ */
+static int
+efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
+{
+ EFI_DEVICE_PATH *devpath;
+ FILEPATH_DEVICE_PATH *node;
+ char *pathname, *p;
+ int unit, len;
+ pdinfo_t *pd, *last;
+
+ /* First collect and verify all the data */
+ if ((devpath = efi_lookup_devpath(disk_handle)) == NULL)
+ return (ENOENT);
+ node = (FILEPATH_DEVICE_PATH *)efi_devpath_last_node(devpath);
+ if (node == NULL)
+ return (ENOENT); /* This should not happen. */
+
+ pd = calloc(1, sizeof(pdinfo_t));
+ if (pd == NULL) {
+ printf("Failed to add disk, out of memory\n");
+ return (ENOMEM);
+ }
+ STAILQ_INIT(&pd->pd_part);
+ last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
+ if (last != NULL)
+ unit = last->pd_unit + 1;
+ else
+ unit = 0;
+
+ /* FILEPATH_DEVICE_PATH has 0 terminated string */
+ for (len = 0; node->PathName[len] != 0; len++)
+ ;
+ if ((pathname = malloc(len + 1)) == NULL) {
+ printf("Failed to add disk, out of memory\n");
+ free(pd);
+ return (ENOMEM);
+ }
+ cpy16to8(node->PathName, pathname, len + 1);
+ p = strchr(pathname, ':');
+
+ /*
+ * Assume we are receiving handles in order, first disk handle,
+ * then partitions for this disk. If this assumption proves
+ * false, this code would need update.
+ */
+ if (p == NULL) { /* no colon, add the disk */
+ pd->pd_handle = disk_handle;
+ pd->pd_unit = unit;
+ pd->pd_devpath = devpath;
+ STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link);
+ free(pathname);
+ return (0);
+ }
+ p++; /* skip the colon */
+ errno = 0;
+ unit = (int)strtol(p, NULL, 0);
+ if (errno != 0) {
+ printf("Bad unit number for partition \"%s\"\n", pathname);
+ free(pathname);
+ free(pd);
+ return (EUNIT);
+ }
+
+ /*
+ * We should have disk registered, if not, we are receiving
+ * handles out of order, and this code should be reworked
+ * to create "blank" disk for partition, and to find the
+ * disk based on PathName compares.
+ */
+ if (last == NULL) {
+ printf("BUG: No disk for partition \"%s\"\n", pathname);
+ free(pathname);
+ free(pd);
+ return (EINVAL);
+ }
+ /* Add the partition. */
+ pd->pd_handle = disk_handle;
+ pd->pd_unit = unit;
+ pd->pd_devpath = devpath;
+ STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link);
+ free(pathname);
+ return (0);
+}
+
+static void
+efipart_updatehd(void)
+{
+ int i, nin;
+ EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
+ EFI_HANDLE handle;
+ EFI_BLOCK_IO *blkio;
+ EFI_STATUS status;
+
+ nin = efipart_nhandles / sizeof (*efipart_handles);
+ for (i = 0; i < nin; i++) {
+ devpath = efi_lookup_devpath(efipart_handles[i]);
+ if (devpath == NULL)
+ continue;
+
+ if ((node = efi_devpath_last_node(devpath)) == NULL)
+ continue;
+ if (efipart_floppy(node) != NULL)
+ continue;
+
+ status = BS->HandleProtocol(efipart_handles[i],
+ &blkio_guid, (void **)&blkio);
+ if (EFI_ERROR(status))
+ continue;
+
+ if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
+ DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) {
+ devpathcpy = efi_devpath_trim(devpath);
+ if (devpathcpy == NULL)
+ continue;
+ tmpdevpath = devpathcpy;
+ status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
+ &handle);
+ free(devpathcpy);
+ if (EFI_ERROR(status))
+ continue;
+ /*
+ * We do not support nested partitions.
+ */
+ devpathcpy = efi_lookup_devpath(handle);
+ if (devpathcpy == NULL)
+ continue;
+ if ((node = efi_devpath_last_node(devpathcpy)) == NULL)
+ continue;
+ if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
+ DevicePathSubType(node) == MEDIA_HARDDRIVE_DP)
+ continue;
+ efipart_hdinfo_add(handle, efipart_handles[i]);
+ continue;
+ }
+
+ if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
+ DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
+ efipart_hdinfo_add_filepath(efipart_handles[i]);
+ continue;
+ }
+ }
+}
+
+static int
+efipart_inithd(void)
+{
+ int rv;
+
+ rv = efipart_inithandles();
+ if (rv != 0)
+ return (rv);
+ STAILQ_INIT(&hdinfo);
+
+ efipart_updatehd();
+
+ bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
+ return (0);
+}
+
+static int
+efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
+{
+ int ret = 0;
+ EFI_BLOCK_IO *blkio;
+ EFI_STATUS status;
+ EFI_HANDLE h;
+ pdinfo_t *pd;
+ CHAR16 *text;
+ struct disk_devdesc pd_dev;
+ char line[80];
+
+ if (STAILQ_EMPTY(pdlist))
+ return (0);
+
+ printf("%s devices:", dev->dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ STAILQ_FOREACH(pd, pdlist, pd_link) {
+ h = pd->pd_handle;
+ if (verbose) { /* Output the device path. */
+ text = efi_devpath_name(efi_lookup_devpath(h));
+ if (text != NULL) {
+ printf(" %S", text);
+ efi_free_devpath_name(text);
+ if ((ret = pager_output("\n")) != 0)
+ break;
+ }
+ }
+ snprintf(line, sizeof(line),
+ " %s%d", dev->dv_name, pd->pd_unit);
+ printf("%s:", line);
+ status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio);
+ if (!EFI_ERROR(status)) {
+ printf(" %llu",
+ blkio->Media->LastBlock == 0? 0:
+ (unsigned long long) (blkio->Media->LastBlock + 1));
+ if (blkio->Media->LastBlock != 0) {
+ printf(" X %u", blkio->Media->BlockSize);
+ }
+ printf(" blocks");
+ if (blkio->Media->MediaPresent) {
+ if (blkio->Media->RemovableMedia)
+ printf(" (removable)");
+ } else {
+ printf(" (no media)");
+ }
+ if ((ret = pager_output("\n")) != 0)
+ break;
+ if (!blkio->Media->MediaPresent)
+ continue;
+
+ pd->pd_blkio = blkio;
+ pd_dev.d_dev = dev;
+ pd_dev.d_unit = pd->pd_unit;
+ pd_dev.d_slice = -1;
+ pd_dev.d_partition = -1;
+ pd_dev.d_opendata = blkio;
+ ret = disk_open(&pd_dev, blkio->Media->BlockSize *
+ (blkio->Media->LastBlock + 1),
+ blkio->Media->BlockSize);
+ if (ret == 0) {
+ ret = disk_print(&pd_dev, line, verbose);
+ disk_close(&pd_dev);
+ if (ret != 0)
+ return (ret);
+ } else {
+ /* Do not fail from disk_open() */
+ ret = 0;
+ }
+ } else {
+ if ((ret = pager_output("\n")) != 0)
+ break;
+ }
+ }
+ return (ret);
+}
+
+static int
+efipart_printfd(int verbose)
+{
+ return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
+}
+
+static int
+efipart_printcd(int verbose)
+{
+ return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
+}
+
+static int
+efipart_printhd(int verbose)
+{
+ return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
+}
+
+static int
+efipart_open(struct open_file *f, ...)
+{
+ va_list args;
+ struct disk_devdesc *dev;
+ pdinfo_t *pd;
+ EFI_BLOCK_IO *blkio;
+ EFI_STATUS status;
+
+ va_start(args, f);
+ dev = va_arg(args, struct disk_devdesc*);
+ va_end(args);
+ if (dev == NULL)
+ return (EINVAL);
+
+ pd = efiblk_get_pdinfo((struct devdesc *)dev);
+ if (pd == NULL)
+ return (EIO);
+
+ if (pd->pd_blkio == NULL) {
+ status = BS->HandleProtocol(pd->pd_handle, &blkio_guid,
+ (void **)&pd->pd_blkio);
+ if (EFI_ERROR(status))
+ return (efi_status_to_errno(status));
+ }
+
+ blkio = pd->pd_blkio;
+ if (!blkio->Media->MediaPresent)
+ return (EAGAIN);
+
+ pd->pd_open++;
+ if (pd->pd_bcache == NULL)
+ pd->pd_bcache = bcache_allocate();
+
+ if (dev->d_dev->dv_type == DEVT_DISK) {
+ int rc;
+
+ rc = disk_open(dev,
+ blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
+ blkio->Media->BlockSize);
+ if (rc != 0) {
+ pd->pd_open--;
+ if (pd->pd_open == 0) {
+ pd->pd_blkio = NULL;
+ bcache_free(pd->pd_bcache);
+ pd->pd_bcache = NULL;
+ }
+ }
+ return (rc);
+ }
+ return (0);
+}
+
+static int
+efipart_close(struct open_file *f)
+{
+ struct disk_devdesc *dev;
+ pdinfo_t *pd;
+
+ dev = (struct disk_devdesc *)(f->f_devdata);
+ if (dev == NULL)
+ return (EINVAL);
+
+ pd = efiblk_get_pdinfo((struct devdesc *)dev);
+ if (pd == NULL)
+ return (EINVAL);
+
+ pd->pd_open--;
+ if (pd->pd_open == 0) {
+ pd->pd_blkio = NULL;
+ bcache_free(pd->pd_bcache);
+ pd->pd_bcache = NULL;
+ }
+ if (dev->d_dev->dv_type == DEVT_DISK)
+ return (disk_close(dev));
+ return (0);
+}
+
+static int
+efipart_ioctl(struct open_file *f, u_long cmd, void *data)
+{
+ struct disk_devdesc *dev;
+ pdinfo_t *pd;
+ int rc;
+
+ dev = (struct disk_devdesc *)(f->f_devdata);
+ if (dev == NULL)
+ return (EINVAL);
+
+ pd = efiblk_get_pdinfo((struct devdesc *)dev);
+ if (pd == NULL)
+ return (EINVAL);
+
+ if (dev->d_dev->dv_type == DEVT_DISK) {
+ rc = disk_ioctl(dev, cmd, data);
+ if (rc != ENOTTY)
+ return (rc);
+ }
+
+ switch (cmd) {
+ case DIOCGSECTORSIZE:
+ *(u_int *)data = pd->pd_blkio->Media->BlockSize;
+ break;
+ case DIOCGMEDIASIZE:
+ *(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
+ (pd->pd_blkio->Media->LastBlock + 1);
+ break;
+ default:
+ return (ENOTTY);
+ }
+
+ return (0);
+}
+
+/*
+ * efipart_readwrite()
+ * Internal equivalent of efipart_strategy(), which operates on the
+ * media-native block size. This function expects all I/O requests
+ * to be within the media size and returns an error if such is not
+ * the case.
+ */
+static int
+efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
+ char *buf)
+{
+ EFI_STATUS status;
+
+ if (blkio == NULL)
+ return (ENXIO);
+ if (blk < 0 || blk > blkio->Media->LastBlock)
+ return (EIO);
+ if ((blk + nblks - 1) > blkio->Media->LastBlock)
+ return (EIO);
+
+ switch (rw & F_MASK) {
+ case F_READ:
+ status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
+ nblks * blkio->Media->BlockSize, buf);
+ break;
+ case F_WRITE:
+ if (blkio->Media->ReadOnly)
+ return (EROFS);
+ status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
+ nblks * blkio->Media->BlockSize, buf);
+ break;
+ default:
+ return (ENOSYS);
+ }
+
+ if (EFI_ERROR(status)) {
+ printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
+ blk, nblks, EFI_ERROR_CODE(status));
+ }
+ return (efi_status_to_errno(status));
+}
+
+static int
+efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct bcache_devdata bcd;
+ struct disk_devdesc *dev;
+ pdinfo_t *pd;
+
+ dev = (struct disk_devdesc *)devdata;
+ if (dev == NULL)
+ return (EINVAL);
+
+ pd = efiblk_get_pdinfo((struct devdesc *)dev);
+ if (pd == NULL)
+ return (EINVAL);
+
+ if (pd->pd_blkio->Media->RemovableMedia &&
+ !pd->pd_blkio->Media->MediaPresent)
+ return (ENXIO);
+
+ bcd.dv_strategy = efipart_realstrategy;
+ bcd.dv_devdata = devdata;
+ bcd.dv_cache = pd->pd_bcache;
+
+ if (dev->d_dev->dv_type == DEVT_DISK) {
+ daddr_t offset;
+
+ offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
+ offset /= 512;
+ return (bcache_strategy(&bcd, rw, blk + offset,
+ size, buf, rsize));
+ }
+ return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
+}
+
+static int
+efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
+ pdinfo_t *pd;
+ EFI_BLOCK_IO *blkio;
+ uint64_t off, disk_blocks, d_offset = 0;
+ char *blkbuf;
+ size_t blkoff, blksz;
+ int error;
+ size_t diskend, readstart;
+
+ if (dev == NULL || blk < 0)
+ return (EINVAL);
+
+ pd = efiblk_get_pdinfo((struct devdesc *)dev);
+ if (pd == NULL)
+ return (EINVAL);
+
+ blkio = pd->pd_blkio;
+ if (blkio == NULL)
+ return (ENXIO);
+
+ if (size == 0 || (size % 512) != 0)
+ return (EIO);
+
+ off = blk * 512;
+ /*
+ * Get disk blocks, this value is either for whole disk or for
+ * partition.
+ */
+ disk_blocks = 0;
+ if (dev->d_dev->dv_type == DEVT_DISK) {
+ if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
+ /* DIOCGMEDIASIZE does return bytes. */
+ disk_blocks /= blkio->Media->BlockSize;
+ }
+ d_offset = dev->d_offset;
+ }
+ if (disk_blocks == 0)
+ disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
+
+ /* make sure we don't read past disk end */
+ if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
+ diskend = d_offset + disk_blocks;
+ readstart = off / blkio->Media->BlockSize;
+
+ if (diskend <= readstart) {
+ if (rsize != NULL)
+ *rsize = 0;
+
+ return (EIO);
+ }
+ size = diskend - readstart;
+ size = size * blkio->Media->BlockSize;
+ }
+
+ if (rsize != NULL)
+ *rsize = size;
+
+ if ((size % blkio->Media->BlockSize == 0) &&
+ (off % blkio->Media->BlockSize == 0))
+ return (efipart_readwrite(blkio, rw,
+ off / blkio->Media->BlockSize,
+ size / blkio->Media->BlockSize, buf));
+
+ /*
+ * The block size of the media is not a multiple of I/O.
+ */
+ blkbuf = malloc(blkio->Media->BlockSize);
+ if (blkbuf == NULL)
+ return (ENOMEM);
+
+ error = 0;
+ blk = off / blkio->Media->BlockSize;
+ blkoff = off % blkio->Media->BlockSize;
+ blksz = blkio->Media->BlockSize - blkoff;
+ while (size > 0) {
+ error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
+ if (error)
+ break;
+ if (size < blksz)
+ blksz = size;
+ bcopy(blkbuf + blkoff, buf, blksz);
+ buf += blksz;
+ size -= blksz;
+ blk++;
+ blkoff = 0;
+ blksz = blkio->Media->BlockSize;
+ }
+
+ free(blkbuf);
+ return (error);
+}
diff --git a/stand/efi/libefi/efizfs.c b/stand/efi/libefi/efizfs.c
new file mode 100644
index 0000000..7c43476
--- /dev/null
+++ b/stand/efi/libefi/efizfs.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 2008-2010 Rui Paulo
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/disk.h>
+#include <stdint.h>
+
+#ifdef EFI_ZFS_BOOT
+#include <libzfs.h>
+#endif
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "efizfs.h"
+
+#ifdef EFI_ZFS_BOOT
+static zfsinfo_list_t zfsinfo;
+
+uint64_t pool_guid;
+
+zfsinfo_list_t *
+efizfs_get_zfsinfo_list(void)
+{
+ return (&zfsinfo);
+}
+
+EFI_HANDLE
+efizfs_get_handle_by_guid(uint64_t guid)
+{
+ zfsinfo_t *zi;
+
+ STAILQ_FOREACH(zi, &zfsinfo, zi_link) {
+ if (zi->zi_pool_guid == guid) {
+ return (zi->zi_handle);
+ }
+ }
+ return (NULL);
+}
+
+static void
+insert_zfs(EFI_HANDLE handle, uint64_t guid)
+{
+ zfsinfo_t *zi;
+
+ zi = malloc(sizeof(zfsinfo_t));
+ zi->zi_handle = handle;
+ zi->zi_pool_guid = guid;
+ STAILQ_INSERT_TAIL(&zfsinfo, zi, zi_link);
+}
+
+void
+efi_zfs_probe(void)
+{
+ pdinfo_list_t *hdi;
+ pdinfo_t *hd, *pd = NULL;
+ char devname[SPECNAMELEN + 1];
+ uint64_t guid;
+
+ hdi = efiblk_get_pdinfo_list(&efipart_hddev);
+ STAILQ_INIT(&zfsinfo);
+
+ /*
+ * Find the handle for the boot device. The boot1 did find the
+ * device with loader binary, now we need to search for the
+ * same device and if it is part of the zfs pool, we record the
+ * pool GUID for currdev setup.
+ */
+ STAILQ_FOREACH(hd, hdi, pd_link) {
+ STAILQ_FOREACH(pd, &hd->pd_part, pd_link) {
+
+ snprintf(devname, sizeof(devname), "%s%dp%d:",
+ efipart_hddev.dv_name, hd->pd_unit, pd->pd_unit);
+
+ if (zfs_probe_dev(devname, &guid) == 0) {
+ insert_zfs(pd->pd_handle, guid);
+
+ if (efi_zfs_is_preferred(pd->pd_handle))
+ pool_guid = guid;
+ }
+
+ }
+ }
+}
+
+uint64_t
+ldi_get_size(void *priv)
+{
+ int fd = (uintptr_t) priv;
+ uint64_t size;
+
+ ioctl(fd, DIOCGMEDIASIZE, &size);
+ return (size);
+}
+#endif
diff --git a/stand/efi/libefi/env.c b/stand/efi/libefi/env.c
new file mode 100644
index 0000000..ceec7b2
--- /dev/null
+++ b/stand/efi/libefi/env.c
@@ -0,0 +1,534 @@
+/*
+ * Copyright (c) 2015 Netflix, Inc. 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <stand.h>
+#include <string.h>
+#include <efi.h>
+#include <efilib.h>
+#include <uuid.h>
+#include <stdbool.h>
+#include "bootstrap.h"
+#ifdef BOOT_FORTH
+#include "ficl.h"
+#endif
+
+/*
+ * Simple wrappers to the underlying UEFI functions.
+ * See http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES
+ * for details.
+ */
+EFI_STATUS
+efi_get_next_variable_name(UINTN *variable_name_size, CHAR16 *variable_name,
+ EFI_GUID *vendor_guid)
+{
+ return (RS->GetNextVariableName(variable_name_size, variable_name,
+ vendor_guid));
+}
+
+EFI_STATUS
+efi_get_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid,
+ UINT32 *attributes, UINTN *data_size, void *data)
+{
+ return (RS->GetVariable(variable_name, vendor_guid, attributes,
+ data_size, data));
+}
+
+EFI_STATUS
+efi_set_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid,
+ UINT32 attributes, UINTN data_size, void *data)
+{
+ return (RS->SetVariable(variable_name, vendor_guid, attributes,
+ data_size, data));
+}
+
+void
+efi_init_environment(void)
+{
+ char var[128];
+
+ snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16,
+ ST->Hdr.Revision & 0xffff);
+ env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset);
+}
+
+COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show);
+
+static int
+efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag)
+{
+ UINTN datasz, i;
+ EFI_STATUS status;
+ UINT32 attr;
+ CHAR16 *data;
+ char *str;
+ uint32_t uuid_status;
+ int is_ascii;
+
+ datasz = 0;
+ status = RS->GetVariable(varnamearg, matchguid, &attr,
+ &datasz, NULL);
+ if (status != EFI_BUFFER_TOO_SMALL) {
+ printf("Can't get the variable: error %#lx\n",
+ EFI_ERROR_CODE(status));
+ return (CMD_ERROR);
+ }
+ data = malloc(datasz);
+ status = RS->GetVariable(varnamearg, matchguid, &attr,
+ &datasz, data);
+ if (status != EFI_SUCCESS) {
+ printf("Can't get the variable: error %#lx\n",
+ EFI_ERROR_CODE(status));
+ return (CMD_ERROR);
+ }
+ uuid_to_string((uuid_t *)matchguid, &str, &uuid_status);
+ if (lflag) {
+ printf("%s 0x%x %S", str, attr, varnamearg);
+ } else {
+ printf("%s 0x%x %S=", str, attr, varnamearg);
+ is_ascii = 1;
+ free(str);
+ str = (char *)data;
+ for (i = 0; i < datasz - 1; i++) {
+ /* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */
+ if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) {
+ is_ascii = 0;
+ break;
+ }
+ }
+ if (str[datasz - 1] != '\0')
+ is_ascii = 0;
+ if (is_ascii)
+ printf("%s", str);
+ else {
+ for (i = 0; i < datasz / 2; i++) {
+ if (isalnum(data[i]) || isspace(data[i]))
+ printf("%c", data[i]);
+ else
+ printf("\\x%02x", data[i]);
+ }
+ }
+ }
+ free(data);
+ if (pager_output("\n"))
+ return (CMD_WARN);
+ return (CMD_OK);
+}
+
+static int
+command_efi_show(int argc, char *argv[])
+{
+ /*
+ * efi-show [-a]
+ * print all the env
+ * efi-show -u UUID
+ * print all the env vars tagged with UUID
+ * efi-show -v var
+ * search all the env vars and print the ones matching var
+ * eif-show -u UUID -v var
+ * eif-show UUID var
+ * print all the env vars that match UUID and var
+ */
+ /* NB: We assume EFI_GUID is the same as uuid_t */
+ int aflag = 0, gflag = 0, lflag = 0, vflag = 0;
+ int ch, rv;
+ unsigned i;
+ EFI_STATUS status;
+ EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} };
+ EFI_GUID matchguid = { 0,0,0,{0,0,0,0,0,0,0,0} };
+ uint32_t uuid_status;
+ CHAR16 *varname;
+ CHAR16 *newnm;
+ CHAR16 varnamearg[128];
+ UINTN varalloc;
+ UINTN varsz;
+
+ while ((ch = getopt(argc, argv, "ag:lv:")) != -1) {
+ switch (ch) {
+ case 'a':
+ aflag = 1;
+ break;
+ case 'g':
+ gflag = 1;
+ uuid_from_string(optarg, (uuid_t *)&matchguid,
+ &uuid_status);
+ if (uuid_status != uuid_s_ok) {
+ printf("uid %s could not be parsed\n", optarg);
+ return (CMD_ERROR);
+ }
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ if (strlen(optarg) >= nitems(varnamearg)) {
+ printf("Variable %s is longer than %zd characters\n",
+ optarg, nitems(varnamearg));
+ return (CMD_ERROR);
+ }
+ for (i = 0; i < strlen(optarg); i++)
+ varnamearg[i] = optarg[i];
+ varnamearg[i] = 0;
+ break;
+ default:
+ printf("Invalid argument %c\n", ch);
+ return (CMD_ERROR);
+ }
+ }
+
+ if (aflag && (gflag || vflag)) {
+ printf("-a isn't compatible with -v or -u\n");
+ return (CMD_ERROR);
+ }
+
+ if (aflag && optind < argc) {
+ printf("-a doesn't take any args\n");
+ return (CMD_ERROR);
+ }
+
+ if (optind == argc)
+ aflag = 1;
+
+ argc -= optind;
+ argv += optind;
+
+ pager_open();
+ if (vflag && gflag) {
+ rv = efi_print_var(varnamearg, &matchguid, lflag);
+ pager_close();
+ return (rv);
+ }
+
+ if (argc == 2) {
+ optarg = argv[0];
+ if (strlen(optarg) >= nitems(varnamearg)) {
+ printf("Variable %s is longer than %zd characters\n",
+ optarg, nitems(varnamearg));
+ pager_close();
+ return (CMD_ERROR);
+ }
+ for (i = 0; i < strlen(optarg); i++)
+ varnamearg[i] = optarg[i];
+ varnamearg[i] = 0;
+ optarg = argv[1];
+ uuid_from_string(optarg, (uuid_t *)&matchguid,
+ &uuid_status);
+ if (uuid_status != uuid_s_ok) {
+ printf("uid %s could not be parsed\n", optarg);
+ pager_close();
+ return (CMD_ERROR);
+ }
+ rv = efi_print_var(varnamearg, &matchguid, lflag);
+ pager_close();
+ return (rv);
+ }
+
+ if (argc > 0) {
+ printf("Too many args %d\n", argc);
+ pager_close();
+ return (CMD_ERROR);
+ }
+
+ /*
+ * Initiate the search -- note the standard takes pain
+ * to specify the initial call must be a poiner to a NULL
+ * character.
+ */
+ varalloc = 1024;
+ varname = malloc(varalloc);
+ if (varname == NULL) {
+ printf("Can't allocate memory to get variables\n");
+ pager_close();
+ return (CMD_ERROR);
+ }
+ varname[0] = 0;
+ while (1) {
+ varsz = varalloc;
+ status = RS->GetNextVariableName(&varsz, varname, &varguid);
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ varalloc = varsz;
+ newnm = realloc(varname, varalloc);
+ if (newnm == NULL) {
+ printf("Can't allocate memory to get variables\n");
+ free(varname);
+ pager_close();
+ return (CMD_ERROR);
+ }
+ varname = newnm;
+ continue; /* Try again with bigger buffer */
+ }
+ if (status != EFI_SUCCESS)
+ break;
+ if (aflag) {
+ if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
+ break;
+ continue;
+ }
+ if (vflag) {
+ if (wcscmp(varnamearg, varname) == 0) {
+ if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
+ break;
+ continue;
+ }
+ }
+ if (gflag) {
+ if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) {
+ if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
+ break;
+ continue;
+ }
+ }
+ }
+ free(varname);
+ pager_close();
+
+ return (CMD_OK);
+}
+
+COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set);
+
+static int
+command_efi_set(int argc, char *argv[])
+{
+ char *uuid, *var, *val;
+ CHAR16 wvar[128];
+ EFI_GUID guid;
+ uint32_t status;
+ EFI_STATUS err;
+
+ if (argc != 4) {
+ printf("efi-set uuid var new-value\n");
+ return (CMD_ERROR);
+ }
+ uuid = argv[1];
+ var = argv[2];
+ val = argv[3];
+ uuid_from_string(uuid, (uuid_t *)&guid, &status);
+ if (status != uuid_s_ok) {
+ printf("Invalid uuid %s %d\n", uuid, status);
+ return (CMD_ERROR);
+ }
+ cpy8to16(var, wvar, sizeof(wvar));
+ err = RS->SetVariable(wvar, &guid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ strlen(val) + 1, val);
+ if (EFI_ERROR(err)) {
+ printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err));
+ return (CMD_ERROR);
+ }
+ return (CMD_OK);
+}
+
+COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset);
+
+static int
+command_efi_unset(int argc, char *argv[])
+{
+ char *uuid, *var;
+ CHAR16 wvar[128];
+ EFI_GUID guid;
+ uint32_t status;
+ EFI_STATUS err;
+
+ if (argc != 3) {
+ printf("efi-unset uuid var\n");
+ return (CMD_ERROR);
+ }
+ uuid = argv[1];
+ var = argv[2];
+ uuid_from_string(uuid, (uuid_t *)&guid, &status);
+ if (status != uuid_s_ok) {
+ printf("Invalid uuid %s\n", uuid);
+ return (CMD_ERROR);
+ }
+ cpy8to16(var, wvar, sizeof(wvar));
+ err = RS->SetVariable(wvar, &guid, 0, 0, NULL);
+ if (EFI_ERROR(err)) {
+ printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err));
+ return (CMD_ERROR);
+ }
+ return (CMD_OK);
+}
+
+#ifdef BOOT_FORTH
+/*
+ * FreeBSD's loader interaction words and extras
+ *
+ * efi-setenv ( value n name n guid n attr -- 0 | -1)
+ * efi-getenv ( guid n addr n -- addr' n' | -1 )
+ * efi-unsetenv ( name n guid n'' -- )
+ */
+
+/*
+ * efi-setenv
+ * efi-setenv ( value n name n guid n attr -- 0 | -1)
+ *
+ * Set environment variables using the SetVariable EFI runtime service.
+ *
+ * Value and guid are passed through in binary form (so guid needs to be
+ * converted to binary form from its string form). Name is converted from
+ * ASCII to CHAR16. Since ficl doesn't have support for internationalization,
+ * there's no native CHAR16 interface provided.
+ *
+ * attr is an int in the bitmask of the following attributes for this variable.
+ *
+ * 1 Non volatile
+ * 2 Boot service access
+ * 4 Run time access
+ * (corresponding to the same bits in the UEFI spec).
+ */
+static void
+ficlEfiSetenv(FICL_VM *pVM)
+{
+ char *value = NULL, *guid = NULL;
+ CHAR16 *name = NULL;
+ int i;
+ char *namep, *valuep, *guidp;
+ int names, values, guids, attr;
+ EFI_STATUS status;
+ uuid_t u;
+ uint32_t ustatus;
+ bool error = true;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 6, 0);
+#endif
+ attr = stackPopINT(pVM->pStack);
+ guids = stackPopINT(pVM->pStack);
+ guidp = (char*)stackPopPtr(pVM->pStack);
+ names = stackPopINT(pVM->pStack);
+ namep = (char*)stackPopPtr(pVM->pStack);
+ values = stackPopINT(pVM->pStack);
+ valuep = (char*)stackPopPtr(pVM->pStack);
+
+ guid = (char*)ficlMalloc(guids);
+ if (guid == NULL)
+ goto out;
+ memcpy(guid, guidp, guids);
+ uuid_from_string(guid, &u, &ustatus);
+ if (ustatus != uuid_s_ok) {
+ stackPushINT(pVM->pStack, -1);
+ goto out;
+ }
+
+ name = ficlMalloc((names + 1) * sizeof(CHAR16));
+ if (name == NULL)
+ goto out;
+ for (i = 0; i < names; i++)
+ name[i] = namep[i];
+ name[names] = 0;
+
+ value = ficlMalloc(values + 1);
+ if (value == NULL)
+ goto out;
+ memcpy(value, valuep, values);
+
+ status = efi_set_variable(name, (EFI_GUID *)&u, attr, values, value);
+ if (status == EFI_SUCCESS)
+ stackPushINT(pVM->pStack, 0);
+ else
+ stackPushINT(pVM->pStack, -1);
+ error = false;
+out:
+ ficlFree(name);
+ ficlFree(value);
+ ficlFree(guid);
+
+ if (error == true)
+ vmThrowErr(pVM, "Error: out of memory");
+}
+
+static void
+ficlEfiGetenv(FICL_VM *pVM)
+{
+ char *name, *value;
+ char *namep;
+ int names;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+ names = stackPopINT(pVM->pStack);
+ namep = (char*) stackPopPtr(pVM->pStack);
+
+ name = (char*) ficlMalloc(names+1);
+ if (name == NULL)
+ vmThrowErr(pVM, "Error: out of memory");
+ strncpy(name, namep, names);
+ name[names] = '\0';
+
+ value = getenv(name);
+ ficlFree(name);
+
+ if(value != NULL) {
+ stackPushPtr(pVM->pStack, value);
+ stackPushINT(pVM->pStack, strlen(value));
+ } else
+ stackPushINT(pVM->pStack, -1);
+}
+
+static void
+ficlEfiUnsetenv(FICL_VM *pVM)
+{
+ char *name;
+ char *namep;
+ int names;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ names = stackPopINT(pVM->pStack);
+ namep = (char*) stackPopPtr(pVM->pStack);
+
+ name = (char*) ficlMalloc(names+1);
+ if (name == NULL)
+ vmThrowErr(pVM, "Error: out of memory");
+ strncpy(name, namep, names);
+ name[names] = '\0';
+
+ unsetenv(name);
+ ficlFree(name);
+}
+
+/**************************************************************************
+** Add FreeBSD UEFI platform extensions into the system dictionary
+**************************************************************************/
+void ficlEfiCompilePlatform(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+ dictAppendWord(dp, "efi-setenv", ficlEfiSetenv, FW_DEFAULT);
+ dictAppendWord(dp, "efi-getenv", ficlEfiGetenv, FW_DEFAULT);
+ dictAppendWord(dp, "efi-unsetenv", ficlEfiUnsetenv, FW_DEFAULT);
+}
+
+FICL_COMPILE_SET(ficlEfiCompilePlatform);
+
+#endif /* BOOT_FORTH */
diff --git a/stand/efi/libefi/errno.c b/stand/efi/libefi/errno.c
new file mode 100644
index 0000000..0f354c3
--- /dev/null
+++ b/stand/efi/libefi/errno.c
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <efi.h>
+#include <efilib.h>
+
+EFI_STATUS
+errno_to_efi_status(int errno)
+{
+ EFI_STATUS status;
+
+ switch (errno) {
+ case EPERM:
+ status = EFI_ACCESS_DENIED;
+ break;
+
+ case EOVERFLOW:
+ status = EFI_BUFFER_TOO_SMALL;
+ break;
+
+ case EIO:
+ status = EFI_DEVICE_ERROR;
+ break;
+
+ case EINVAL:
+ status = EFI_INVALID_PARAMETER;
+ break;
+
+ case ESTALE:
+ status = EFI_MEDIA_CHANGED;
+ break;
+
+ case ENXIO:
+ status = EFI_NO_MEDIA;
+ break;
+
+ case ENOENT:
+ status = EFI_NOT_FOUND;
+ break;
+
+ case ENOMEM:
+ status = EFI_OUT_OF_RESOURCES;
+ break;
+
+ case ENOTSUP:
+ case ENODEV:
+ status = EFI_UNSUPPORTED;
+ break;
+
+ case ENOSPC:
+ status = EFI_VOLUME_FULL;
+ break;
+
+ case EACCES:
+ status = EFI_WRITE_PROTECTED;
+ break;
+
+ case 0:
+ status = EFI_SUCCESS;
+ break;
+
+ default:
+ status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ return (status);
+}
+
+int
+efi_status_to_errno(EFI_STATUS status)
+{
+ int errno;
+
+ switch (status) {
+ case EFI_ACCESS_DENIED:
+ errno = EPERM;
+ break;
+
+ case EFI_BUFFER_TOO_SMALL:
+ errno = EOVERFLOW;
+ break;
+
+ case EFI_DEVICE_ERROR:
+ case EFI_VOLUME_CORRUPTED:
+ errno = EIO;
+ break;
+
+ case EFI_INVALID_PARAMETER:
+ errno = EINVAL;
+ break;
+
+ case EFI_MEDIA_CHANGED:
+ errno = ESTALE;
+ break;
+
+ case EFI_NO_MEDIA:
+ errno = ENXIO;
+ break;
+
+ case EFI_NOT_FOUND:
+ errno = ENOENT;
+ break;
+
+ case EFI_OUT_OF_RESOURCES:
+ errno = ENOMEM;
+ break;
+
+ case EFI_UNSUPPORTED:
+ errno = ENODEV;
+ break;
+
+ case EFI_VOLUME_FULL:
+ errno = ENOSPC;
+ break;
+
+ case EFI_WRITE_PROTECTED:
+ errno = EACCES;
+ break;
+
+ case 0:
+ errno = 0;
+ break;
+
+ default:
+ errno = EDOOFUS;
+ break;
+ }
+
+ return (errno);
+}
diff --git a/stand/efi/libefi/handles.c b/stand/efi/libefi/handles.c
new file mode 100644
index 0000000..1e4ef6f
--- /dev/null
+++ b/stand/efi/libefi/handles.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <efi.h>
+#include <efilib.h>
+
+struct entry {
+ EFI_HANDLE handle;
+ EFI_HANDLE alias;
+ struct devsw *dev;
+ int unit;
+ uint64_t extra;
+};
+
+struct entry *entry;
+int nentries;
+
+int
+efi_register_handles(struct devsw *sw, EFI_HANDLE *handles,
+ EFI_HANDLE *aliases, int count)
+{
+ size_t sz;
+ int idx, unit;
+
+ idx = nentries;
+ nentries += count;
+ sz = nentries * sizeof(struct entry);
+ entry = (entry == NULL) ? malloc(sz) : realloc(entry, sz);
+ for (unit = 0; idx < nentries; idx++, unit++) {
+ entry[idx].handle = handles[unit];
+ if (aliases != NULL)
+ entry[idx].alias = aliases[unit];
+ else
+ entry[idx].alias = NULL;
+ entry[idx].dev = sw;
+ entry[idx].unit = unit;
+ }
+ return (0);
+}
+
+EFI_HANDLE
+efi_find_handle(struct devsw *dev, int unit)
+{
+ int idx;
+
+ for (idx = 0; idx < nentries; idx++) {
+ if (entry[idx].dev != dev)
+ continue;
+ if (entry[idx].unit != unit)
+ continue;
+ return (entry[idx].handle);
+ }
+ return (NULL);
+}
+
+int
+efi_handle_lookup(EFI_HANDLE h, struct devsw **dev, int *unit, uint64_t *extra)
+{
+ int idx;
+
+ for (idx = 0; idx < nentries; idx++) {
+ if (entry[idx].handle != h && entry[idx].alias != h)
+ continue;
+ if (dev != NULL)
+ *dev = entry[idx].dev;
+ if (unit != NULL)
+ *unit = entry[idx].unit;
+ if (extra != NULL)
+ *extra = entry[idx].extra;
+ return (0);
+ }
+ return (ENOENT);
+}
+
+int
+efi_handle_update_dev(EFI_HANDLE h, struct devsw *dev, int unit,
+ uint64_t guid)
+{
+ int idx;
+
+ for (idx = 0; idx < nentries; idx++) {
+ if (entry[idx].handle != h)
+ continue;
+ entry[idx].dev = dev;
+ entry[idx].unit = unit;
+ entry[idx].alias = NULL;
+ entry[idx].extra = guid;
+ return (0);
+ }
+
+ return (ENOENT);
+}
diff --git a/stand/efi/libefi/libefi.c b/stand/efi/libefi/libefi.c
new file mode 100644
index 0000000..e0a721f
--- /dev/null
+++ b/stand/efi/libefi/libefi.c
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2000 Doug Rabson
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <efi.h>
+#include <eficonsctl.h>
+#include <efilib.h>
+#include <stand.h>
+
+EFI_HANDLE IH;
+EFI_SYSTEM_TABLE *ST;
+EFI_BOOT_SERVICES *BS;
+EFI_RUNTIME_SERVICES *RS;
+
+void *
+efi_get_table(EFI_GUID *tbl)
+{
+ EFI_GUID *id;
+ int i;
+
+ for (i = 0; i < ST->NumberOfTableEntries; i++) {
+ id = &ST->ConfigurationTable[i].VendorGuid;
+ if (!memcmp(id, tbl, sizeof(EFI_GUID)))
+ return (ST->ConfigurationTable[i].VendorTable);
+ }
+ return (NULL);
+}
diff --git a/stand/efi/libefi/time.c b/stand/efi/libefi/time.c
new file mode 100644
index 0000000..fe0d2ef
--- /dev/null
+++ b/stand/efi/libefi/time.c
@@ -0,0 +1,283 @@
+/*-
+ * Copyright (c) 1999, 2000
+ * 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Intel Corporation and
+ * its contributors.
+ *
+ * 4. Neither the name of Intel Corporation or its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION 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 INTEL CORPORATION 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <time.h>
+#include <sys/time.h>
+
+/*
+ * Accurate only for the past couple of centuries;
+ * that will probably do.
+ *
+ * (#defines From FreeBSD 3.2 lib/libc/stdtime/tzfile.h)
+ */
+
+#define isleap(y) (((y) % 4) == 0 && \
+ (((y) % 100) != 0 || ((y) % 400) == 0))
+#define SECSPERHOUR (60*60)
+#define SECSPERDAY (24 * SECSPERHOUR)
+
+/*
+ * These arrays give the cumulative number of days up to the first of the
+ * month number used as the index (1 -> 12) for regular and leap years.
+ * The value at index 13 is for the whole year.
+ */
+static const time_t CumulativeDays[2][14] = {
+ {0,
+ 0,
+ 31,
+ 31 + 28,
+ 31 + 28 + 31,
+ 31 + 28 + 31 + 30,
+ 31 + 28 + 31 + 30 + 31,
+ 31 + 28 + 31 + 30 + 31 + 30,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 },
+ {0,
+ 0,
+ 31,
+ 31 + 29,
+ 31 + 29 + 31,
+ 31 + 29 + 31 + 30,
+ 31 + 29 + 31 + 30 + 31,
+ 31 + 29 + 31 + 30 + 31 + 30,
+ 31 + 29 + 31 + 30 + 31 + 30 + 31,
+ 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31,
+ 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
+ 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
+ 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
+ 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 }};
+
+void
+efi_time_init(void)
+{
+}
+
+void
+efi_time_fini(void)
+{
+}
+
+void
+to_efi_time(EFI_TIME *efi_time, time_t time)
+{
+ int lyear, month;
+ time_t seconds;
+
+ if (time >= 0) {
+ efi_time->Year = 1970;
+ lyear = isleap(efi_time->Year);
+ month = 13;
+ seconds = CumulativeDays[lyear][month] * SECSPERDAY;
+ while (time > seconds) {
+ time -= seconds;
+ efi_time->Year++;
+ lyear = isleap(efi_time->Year);
+ seconds = CumulativeDays[lyear][month] * SECSPERDAY;
+ }
+
+ efi_time->Month = 0;
+ while (time >
+ CumulativeDays[lyear][month] * SECSPERDAY) {
+ efi_time->Month++;
+ }
+
+ month = efi_time->Month - 1;
+ time -= CumulativeDays[lyear][month] * SECSPERDAY;
+
+ for (efi_time->Day = 0; time > SECSPERDAY; efi_time->Day++)
+ time -= SECSPERDAY;
+
+ for (efi_time->Hour = 0; time > SECSPERHOUR; efi_time->Hour++)
+ time -= SECSPERHOUR;
+
+ for (efi_time->Minute = 0; time > 60; efi_time->Minute++)
+ time -= 60;
+
+ efi_time->Second = time;
+ efi_time->Nanosecond = 0;
+ efi_time->TimeZone = 0;
+ efi_time->Daylight = 0;
+ } else {
+ memset(efi_time, 0, sizeof(EFI_TIME));
+ }
+}
+
+time_t
+from_efi_time(EFI_TIME *ETime)
+{
+ time_t UTime;
+ int Year;
+
+ /*
+ * Do a santity check
+ */
+ if (ETime->Year < 1998 || ETime->Year > 2099 ||
+ ETime->Month == 0 || ETime->Month > 12 ||
+ ETime->Day == 0 || ETime->Month > 31 ||
+ ETime->Hour > 23 || ETime->Minute > 59 ||
+ ETime->Second > 59 || ETime->TimeZone < -1440 ||
+ (ETime->TimeZone > 1440 && ETime->TimeZone != 2047)) {
+ return (0);
+ }
+
+ /*
+ * Years
+ */
+ UTime = 0;
+ for (Year = 1970; Year != ETime->Year; ++Year) {
+ UTime += (CumulativeDays[isleap(Year)][13] * SECSPERDAY);
+ }
+
+ /*
+ * UTime should now be set to 00:00:00 on Jan 1 of the file's year.
+ *
+ * Months
+ */
+ UTime += (CumulativeDays[isleap(ETime->Year)][ETime->Month] *
+ SECSPERDAY);
+
+ /*
+ * UTime should now be set to 00:00:00 on the first of the file's
+ * month and year.
+ *
+ * Days -- Don't count the file's day
+ */
+ UTime += (((ETime->Day > 0) ? ETime->Day-1:0) * SECSPERDAY);
+
+ /*
+ * Hours
+ */
+ UTime += (ETime->Hour * SECSPERHOUR);
+
+ /*
+ * Minutes
+ */
+ UTime += (ETime->Minute * 60);
+
+ /*
+ * Seconds
+ */
+ UTime += ETime->Second;
+
+ /*
+ * EFI time is repored in local time. Adjust for any time zone
+ * offset to get true UT
+ */
+ if (ETime->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
+ /*
+ * TimeZone is kept in minues...
+ */
+ UTime += (ETime->TimeZone * 60);
+ }
+
+ return (UTime);
+}
+
+static int
+EFI_GetTimeOfDay(OUT struct timeval *tp, OUT struct timezone *tzp)
+{
+ EFI_TIME EfiTime;
+ EFI_TIME_CAPABILITIES Capabilities;
+ EFI_STATUS Status;
+
+ /*
+ * Get time from EFI
+ */
+
+ Status = RS->GetTime(&EfiTime, &Capabilities);
+ if (EFI_ERROR(Status))
+ return (-1);
+
+ /*
+ * Convert to UNIX time (ie seconds since the epoch
+ */
+
+ tp->tv_sec = from_efi_time(&EfiTime);
+ tp->tv_usec = 0; /* EfiTime.Nanosecond * 1000; */
+
+ /*
+ * Do something with the timezone if needed
+ */
+
+ if (tzp != NULL) {
+ if (EfiTime.TimeZone == EFI_UNSPECIFIED_TIMEZONE)
+ tzp->tz_minuteswest = 0;
+ else
+ tzp->tz_minuteswest = EfiTime.TimeZone;
+ /*
+ * This isn't quit right since it doesn't deal with
+ * EFI_TIME_IN_DAYLIGHT
+ */
+ tzp->tz_dsttime =
+ EfiTime.Daylight & EFI_TIME_ADJUST_DAYLIGHT ? 1 : 0;
+ }
+
+ return (0);
+}
+
+time_t
+time(time_t *tloc)
+{
+ struct timeval tv;
+
+ memset(&tv, 0, sizeof(tv));
+ EFI_GetTimeOfDay(&tv, NULL);
+
+ if (tloc)
+ *tloc = tv.tv_sec;
+ return (tv.tv_sec);
+}
+
+time_t
+getsecs(void)
+{
+
+ return (time(NULL));
+}
diff --git a/stand/efi/libefi/time_event.c b/stand/efi/libefi/time_event.c
new file mode 100644
index 0000000..f96f1d8
--- /dev/null
+++ b/stand/efi/libefi/time_event.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 2016 Andrew Turner
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <time.h>
+#include <sys/time.h>
+
+static EFI_EVENT time_event;
+static uint64_t curtime;
+
+static void
+time_update(EFI_EVENT event, void *context)
+{
+
+ curtime += 10;
+}
+
+void
+efi_time_init(void)
+{
+
+ /* Create a timer event */
+ BS->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ time_update, 0, &time_event);
+ /* Use a 10ms timer */
+ BS->SetTimer(time_event, TimerPeriodic, 100000);
+}
+
+void
+efi_time_fini(void)
+{
+
+ /* Cancel the timer */
+ BS->SetTimer(time_event, TimerCancel, 0);
+ BS->CloseEvent(time_event);
+}
+
+time_t
+time(time_t *tloc)
+{
+ time_t t;
+
+ t = curtime / 1000;
+ if (tloc != NULL)
+ *tloc = t;
+
+ return (t);
+}
+
+time_t
+getsecs(void)
+{
+ return time(0);
+}
diff --git a/stand/efi/libefi/wchar.c b/stand/efi/libefi/wchar.c
new file mode 100644
index 0000000..d8d81ac
--- /dev/null
+++ b/stand/efi/libefi/wchar.c
@@ -0,0 +1,73 @@
+/*-
+ * Copyright 2016 Netflix, Inc. 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <efi.h>
+#include <efilib.h>
+
+/*
+ * CHAR16 related functions moved from loader.
+ * Perhaps we should move those to libstand afterall, but they are
+ * needed only by UEFI.
+ */
+
+int
+wcscmp(CHAR16 *a, CHAR16 *b)
+{
+
+ while (*a && *b && *a == *b) {
+ a++;
+ b++;
+ }
+ return *a - *b;
+}
+
+/*
+ * cpy8to16 copies a traditional C string into a CHAR16 string and
+ * 0 terminates it. len is the size of *dst in bytes.
+ */
+void
+cpy8to16(const char *src, CHAR16 *dst, size_t len)
+{
+ len <<= 1; /* Assume CHAR16 is 2 bytes */
+ while (len > 0 && *src) {
+ *dst++ = *src++;
+ len--;
+ }
+ *dst++ = (CHAR16)0;
+}
+
+void
+cpy16to8(const CHAR16 *src, char *dst, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len && src[i]; i++)
+ dst[i] = (char)src[i];
+ if (i < len)
+ dst[i] = '\0';
+}
diff --git a/stand/efi/loader/Makefile b/stand/efi/loader/Makefile
new file mode 100644
index 0000000..3b4c507
--- /dev/null
+++ b/stand/efi/loader/Makefile
@@ -0,0 +1,137 @@
+# $FreeBSD$
+
+MAN=
+
+LOADER_NET_SUPPORT?= yes
+LOADER_MSDOS_SUPPORT?= yes
+LOADER_UFS_SUPPORT?= yes
+LOADER_CD9660_SUPPORT?= no
+LOADER_EXT2FS_SUPPORT?= no
+
+.include <bsd.init.mk>
+
+MK_SSP= no
+
+PROG= loader.sym
+INTERNALPROG=
+WARNS?= 3
+
+# architecture-specific loader code
+SRCS= autoload.c \
+ bootinfo.c \
+ conf.c \
+ copy.c \
+ efi_main.c \
+ framebuffer.c \
+ main.c \
+ self_reloc.c \
+ smbios.c \
+ vers.c
+
+.if ${MK_ZFS} != "no"
+LIBZFSBOOT= ${BOOTOBJ}/zfs/libzfsboot.a
+CFLAGS+= -I${ZFSSRC}
+CFLAGS+= -DEFI_ZFS_BOOT
+.endif
+
+.if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} > 40201
+CWARNFLAGS.self_reloc.c+= -Wno-error=maybe-uninitialized
+.endif
+
+# We implement a slightly non-standard %S in that it always takes a
+# CHAR16 that's common in UEFI-land instead of a wchar_t. This only
+# seems to matter on arm64 where wchar_t defaults to an int instead
+# of a short. There's no good cast to use here so just ignore the
+# warnings for now.
+CWARNFLAGS.main.c+= -Wno-format
+
+.PATH: ${.CURDIR}/arch/${MACHINE}
+# For smbios.c
+.PATH: ${BOOTSRC}/i386/libi386
+.include "${.CURDIR}/arch/${MACHINE}/Makefile.inc"
+
+CFLAGS+= -I${.CURDIR}
+CFLAGS+= -I${.CURDIR}/arch/${MACHINE}
+CFLAGS+= -I${EFISRC}/include
+CFLAGS+= -I${EFISRC}/include/${MACHINE}
+CFLAGS+= -I${SYSDIR}/contrib/dev/acpica/include
+CFLAGS+= -I${BOOTSRC}/i386/libi386
+CFLAGS+= -DNO_PCI -DEFI
+
+# make buildenv doesn't set DESTDIR, this means LIBSTAND
+# will be wrong when crossbuilding.
+.if exists(${.OBJDIR}/../../../../lib/libstand/libstand.a)
+LIBSTAND= ${.OBJDIR}/../../../../lib/libstand/libstand.a
+.endif
+
+.if !defined(BOOT_HIDE_SERIAL_NUMBERS)
+# Export serial numbers, UUID, and asset tag from loader.
+CFLAGS+= -DSMBIOS_SERIAL_NUMBERS
+.if defined(BOOT_LITTLE_ENDIAN_UUID)
+# Use little-endian UUID format as defined in SMBIOS 2.6.
+CFLAGS+= -DSMBIOS_LITTLE_ENDIAN_UUID
+.elif defined(BOOT_NETWORK_ENDIAN_UUID)
+# Use network-endian UUID format for backward compatibility.
+CFLAGS+= -DSMBIOS_NETWORK_ENDIAN_UUID
+.endif
+.endif
+
+.if defined(HAVE_FDT) && ${MK_FDT} != "no"
+.include "${BOOTSRC}/fdt.mk"
+LIBEFI_FDT= ${BOOTOBJ}/efi/fdt/libefi_fdt.a
+.endif
+
+# Include bcache code.
+HAVE_BCACHE= yes
+
+.if defined(EFI_STAGING_SIZE)
+CFLAGS+= -DEFI_STAGING_SIZE=${EFI_STAGING_SIZE}
+.endif
+
+# Always add MI sources
+HELP_FILES=
+.include "${BOOTSRC}/loader.mk"
+
+FILES+= loader.efi
+FILESMODE_loader.efi= ${BINMODE}
+
+LDSCRIPT= ${.CURDIR}/arch/${MACHINE}/ldscript.${MACHINE}
+LDFLAGS+= -Wl,-T${LDSCRIPT},-Bsymbolic,-znotext -shared
+
+CLEANFILES+= loader.efi
+
+NEWVERSWHAT= "EFI loader" ${MACHINE}
+
+NM?= nm
+OBJCOPY?= objcopy
+
+.if ${MACHINE_CPUARCH} == "amd64"
+EFI_TARGET= efi-app-x86_64
+.elif ${MACHINE_CPUARCH} == "i386"
+EFI_TARGET= efi-app-ia32
+.else
+EFI_TARGET= binary
+.endif
+
+# Arbitrarily set the PE/COFF header timestamps to 1 Jan 2016 00:00:00
+# for build reproducibility.
+SOURCE_DATE_EPOCH?=1451606400
+loader.efi: ${PROG}
+ if ${NM} ${.ALLSRC} | grep ' U '; then \
+ echo "Undefined symbols in ${.ALLSRC}"; \
+ exit 1; \
+ fi
+ SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \
+ ${OBJCOPY} -j .peheader -j .text -j .sdata -j .data \
+ -j .dynamic -j .dynsym -j .rel.dyn \
+ -j .rela.dyn -j .reloc -j .eh_frame -j set_Xcommand_set \
+ -j set_Xficl_compile_set \
+ --output-target=${EFI_TARGET} ${.ALLSRC} ${.TARGET}
+
+LIBEFI= ${BOOTOBJ}/efi/libefi/libefi.a
+
+DPADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBZFSBOOT} ${LIBSA} \
+ ${LDSCRIPT}
+LDADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBZFSBOOT} ${LIBSA}
+
+.include <bsd.prog.mk>
diff --git a/stand/efi/loader/Makefile.depend b/stand/efi/loader/Makefile.depend
new file mode 100644
index 0000000..cca8fc0
--- /dev/null
+++ b/stand/efi/loader/Makefile.depend
@@ -0,0 +1,16 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libstand \
+ sys/boot/efi/libefi \
+ sys/boot/ficl \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/efi/loader/arch/amd64/Makefile.inc b/stand/efi/loader/arch/amd64/Makefile.inc
new file mode 100644
index 0000000..b6d824c
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/Makefile.inc
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+SRCS+= amd64_tramp.S \
+ start.S \
+ elf64_freebsd.c \
+ trap.c \
+ exc.S
+
+.PATH: ${BOOTSRC}/i386/libi386
+SRCS+= nullconsole.c \
+ comconsole.c \
+ spinconsole.c
+
+CFLAGS+= -fPIC -DTERM_EMU
+LDFLAGS+= -Wl,-znocombreloc
diff --git a/stand/efi/loader/arch/amd64/amd64_tramp.S b/stand/efi/loader/arch/amd64/amd64_tramp.S
new file mode 100644
index 0000000..c102d92
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/amd64_tramp.S
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Benno Rice under sponsorship from
+ * the FreeBSD Foundation.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asmacros.h>
+
+ .text
+ .globl amd64_tramp
+
+/*
+ * void amd64_tramp(uint64_t stack, void *copy_finish, uint64_t kernend,
+ * uint64_t modulep, uint64_t pagetable, uint64_t entry)
+ */
+amd64_tramp:
+ cli /* Make sure we don't get interrupted. */
+ movq %rdi,%rsp /* Switch to our temporary stack. */
+
+ movq %rdx,%r12 /* Stash the kernel values for later. */
+ movq %rcx,%r13
+ movq %r8,%r14
+ movq %r9,%r15
+
+ callq *%rsi /* Call copy_finish so we're all ready to go. */
+
+ pushq %r12 /* Push kernend. */
+ salq $32,%r13 /* Shift modulep and push it. */
+ pushq %r13
+ pushq %r15 /* Push the entry address. */
+ movq %r14,%cr3 /* Switch page tables. */
+ ret /* "Return" to kernel entry. */
+
+ ALIGN_TEXT
+amd64_tramp_end:
+
+ .data
+ .globl amd64_tramp_size
+amd64_tramp_size:
+ .long amd64_tramp_end-amd64_tramp
diff --git a/stand/efi/loader/arch/amd64/elf64_freebsd.c b/stand/efi/loader/arch/amd64/elf64_freebsd.c
new file mode 100644
index 0000000..37e9fe1
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/elf64_freebsd.c
@@ -0,0 +1,208 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <string.h>
+#include <machine/elf.h>
+#include <stand.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+
+#include "platform/acfreebsd.h"
+#include "acconfig.h"
+#define ACPI_SYSTEM_XFACE
+#include "actypes.h"
+#include "actbl.h"
+
+#include "loader_efi.h"
+
+static EFI_GUID acpi_guid = ACPI_TABLE_GUID;
+static EFI_GUID acpi20_guid = ACPI_20_TABLE_GUID;
+
+extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp);
+
+static int elf64_exec(struct preloaded_file *amp);
+static int elf64_obj_exec(struct preloaded_file *amp);
+
+static struct file_format amd64_elf = {
+ .l_load = elf64_loadfile,
+ .l_exec = elf64_exec,
+};
+static struct file_format amd64_elf_obj = {
+ .l_load = elf64_obj_loadfile,
+ .l_exec = elf64_obj_exec,
+};
+
+struct file_format *file_formats[] = {
+ &amd64_elf,
+ &amd64_elf_obj,
+ NULL
+};
+
+static pml4_entry_t *PT4;
+static pdp_entry_t *PT3;
+static pd_entry_t *PT2;
+
+static void (*trampoline)(uint64_t stack, void *copy_finish, uint64_t kernend,
+ uint64_t modulep, pml4_entry_t *pagetable, uint64_t entry);
+
+extern uintptr_t amd64_tramp;
+extern uint32_t amd64_tramp_size;
+
+/*
+ * There is an ELF kernel and one or more ELF modules loaded.
+ * We wish to start executing the kernel image, so make such
+ * preparations as are required, and do so.
+ */
+static int
+elf64_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *md;
+ Elf_Ehdr *ehdr;
+ vm_offset_t modulep, kernend, trampcode, trampstack;
+ int err, i;
+ ACPI_TABLE_RSDP *rsdp;
+ char buf[24];
+ int revision;
+
+ /*
+ * Report the RSDP to the kernel. While this can be found with
+ * a BIOS boot, the RSDP may be elsewhere when booted from UEFI.
+ * The old code used the 'hints' method to communite this to
+ * the kernel. However, while convenient, the 'hints' method
+ * is fragile and does not work when static hints are compiled
+ * into the kernel. Instead, move to setting different tunables
+ * that start with acpi. The old 'hints' can be removed before
+ * we branch for FreeBSD 12.
+ */
+
+ rsdp = efi_get_table(&acpi20_guid);
+ if (rsdp == NULL) {
+ rsdp = efi_get_table(&acpi_guid);
+ }
+ if (rsdp != NULL) {
+ sprintf(buf, "0x%016llx", (unsigned long long)rsdp);
+ setenv("hint.acpi.0.rsdp", buf, 1);
+ setenv("acpi.rsdp", buf, 1);
+ revision = rsdp->Revision;
+ if (revision == 0)
+ revision = 1;
+ sprintf(buf, "%d", revision);
+ setenv("hint.acpi.0.revision", buf, 1);
+ setenv("acpi.revision", buf, 1);
+ strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId));
+ buf[sizeof(rsdp->OemId)] = '\0';
+ setenv("hint.acpi.0.oem", buf, 1);
+ setenv("acpi.oem", buf, 1);
+ sprintf(buf, "0x%016x", rsdp->RsdtPhysicalAddress);
+ setenv("hint.acpi.0.rsdt", buf, 1);
+ setenv("acpi.rsdt", buf, 1);
+ if (revision >= 2) {
+ /* XXX extended checksum? */
+ sprintf(buf, "0x%016llx",
+ (unsigned long long)rsdp->XsdtPhysicalAddress);
+ setenv("hint.acpi.0.xsdt", buf, 1);
+ setenv("acpi.xsdt", buf, 1);
+ sprintf(buf, "%d", rsdp->Length);
+ setenv("hint.acpi.0.xsdt_length", buf, 1);
+ setenv("acpi.xsdt_length", buf, 1);
+ }
+ }
+
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return(EFTYPE);
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+
+ trampcode = (vm_offset_t)0x0000000040000000;
+ err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 1,
+ (EFI_PHYSICAL_ADDRESS *)&trampcode);
+ bzero((void *)trampcode, EFI_PAGE_SIZE);
+ trampstack = trampcode + EFI_PAGE_SIZE - 8;
+ bcopy((void *)&amd64_tramp, (void *)trampcode, amd64_tramp_size);
+ trampoline = (void *)trampcode;
+
+ PT4 = (pml4_entry_t *)0x0000000040000000;
+ err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 3,
+ (EFI_PHYSICAL_ADDRESS *)&PT4);
+ bzero(PT4, 3 * EFI_PAGE_SIZE);
+
+ PT3 = &PT4[512];
+ PT2 = &PT3[512];
+
+ /*
+ * This is kinda brutal, but every single 1GB VM memory segment points
+ * to the same first 1GB of physical memory. But it is more than
+ * adequate.
+ */
+ for (i = 0; i < 512; i++) {
+ /* Each slot of the L4 pages points to the same L3 page. */
+ PT4[i] = (pml4_entry_t)PT3;
+ PT4[i] |= PG_V | PG_RW | PG_U;
+
+ /* Each slot of the L3 pages points to the same L2 page. */
+ PT3[i] = (pdp_entry_t)PT2;
+ PT3[i] |= PG_V | PG_RW | PG_U;
+
+ /* The L2 page slots are mapped with 2MB pages for 1GB. */
+ PT2[i] = i * (2 * 1024 * 1024);
+ PT2[i] |= PG_V | PG_RW | PG_PS | PG_U;
+ }
+
+ printf("Start @ 0x%lx ...\n", ehdr->e_entry);
+
+ efi_time_fini();
+ err = bi_load(fp->f_args, &modulep, &kernend);
+ if (err != 0) {
+ efi_time_init();
+ return(err);
+ }
+
+ dev_cleanup();
+
+ trampoline(trampstack, efi_copy_finish, kernend, modulep, PT4,
+ ehdr->e_entry);
+
+ panic("exec returned");
+}
+
+static int
+elf64_obj_exec(struct preloaded_file *fp)
+{
+
+ return (EFTYPE);
+}
diff --git a/stand/efi/loader/arch/amd64/exc.S b/stand/efi/loader/arch/amd64/exc.S
new file mode 100644
index 0000000..0035d4a
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/exc.S
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Konstantin Belousov under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+ .macro EH N, err=1
+ .align 8
+ .globl EXC\N\()_handler
+EXC\N\()_handler:
+ .if \err != 1
+ pushq $0
+ .endif
+ pushq %rax
+ pushq %rdx
+ pushq %rcx
+ movl $\N,%ecx
+ jmp all_handlers
+ .endm
+
+ .text
+ EH 0,0
+ EH 1,0
+ EH 2,0
+ EH 3,0
+ EH 4,0
+ EH 5,0
+ EH 6,0
+ EH 7,0
+ EH 8
+ EH 9,0
+ EH 10
+ EH 11
+ EH 12
+ EH 13
+ EH 14
+ EH 16,0
+ EH 17
+ EH 18,0
+ EH 19,0
+ EH 20,0
+
+ .globl exc_rsp
+all_handlers:
+ cmpq %rsp,exc_rsp(%rip)
+ je exception
+
+ /*
+ * Interrupt, not exception.
+ * First, copy the hardware interrupt frame to the previous stack.
+ * Our handler always has private IST stack.
+ */
+ movq (6*8)(%rsp),%rax /* saved %rsp value, AKA old stack */
+ subq (5*8),%rax
+ movq (3*8)(%rsp),%rdx /* copy %rip to old stack */
+ movq %rdx,(%rax)
+ movq (4*8)(%rsp),%rdx /* copy %cs */
+ movq %rdx,(1*8)(%rax)
+ movq (5*8)(%rsp),%rdx /* copy %rflags */
+ movq %rdx,(2*8)(%rax)
+ movq (6*8)(%rsp),%rdx /* copy %rsp */
+ movq %rdx,(3*8)(%rax)
+ movq (7*8)(%rsp),%rdx /* copy %ss */
+ movq %rdx,(4*8)(%rax)
+
+ /*
+ * Now simulate invocation of the original interrupt handler
+ * with retq. We switch stacks and execute retq from the old
+ * stack since there is no free registers at the last moment.
+ */
+ subq $16,%rax
+ leaq fw_intr_handlers(%rip),%rdx
+ movq (%rdx,%rcx,8),%rdx /* push intr handler address on old stack */
+ movq %rdx,8(%rax)
+ movq (2*8)(%rsp),%rcx /* saved %rax is put on top of old stack */
+ movq %rcx,(%rax)
+ movq (%rsp),%rcx
+ movq 8(%rsp),%rdx
+
+ movq 32(%rsp),%rsp /* switch to old stack */
+ popq %rax
+ retq
+
+exception:
+ /*
+ * Form the struct trapframe on our IST stack.
+ * Skip three words, which are currently busy with temporal
+ * saves.
+ */
+ pushq %r15
+ pushq %r14
+ pushq %r13
+ pushq %r12
+ pushq %r11
+ pushq %r10
+ pushq %rbp
+ pushq %rbx
+ pushq $0 /* %rax */
+ pushq %r9
+ pushq %r8
+ pushq $0 /* %rcx */
+ pushq $0 /* %rdx */
+ pushq %rsi
+ pushq %rdi
+
+ /*
+ * Move %rax, %rdx, %rcx values into the final location,
+ * from the three words which were skipped above.
+ */
+ movq 0x88(%rsp),%rax
+ movq %rax,0x30(%rsp) /* tf_rax */
+ movq 0x78(%rsp),%rax
+ movq %rax,0x18(%rsp) /* tf_rcx */
+ movq 0x80(%rsp),%rax
+ movq %rax,0x10(%rsp) /* tf_rdx */
+
+ /*
+ * And fill the three words themself.
+ */
+ movq %cr2,%rax
+ movq %rax,0x80(%rsp) /* tf_addr */
+ movl %ecx,0x78(%rsp) /* tf_trapno */
+ movw %ds,0x8e(%rsp)
+ movw %es,0x8c(%rsp)
+ movw %fs,0x7c(%rsp)
+ movw %gs,0x7e(%rsp)
+ movw $0,0x88(%rsp) /* tf_flags */
+
+ /*
+ * Call dump routine.
+ */
+ movq %rsp,%rdi
+ callq report_exc
+
+ /*
+ * Hang after reporting. Interrupts are already disabled.
+ */
+1:
+ hlt
+ jmp 1b
diff --git a/stand/efi/loader/arch/amd64/ldscript.amd64 b/stand/efi/loader/arch/amd64/ldscript.amd64
new file mode 100644
index 0000000..874df9b
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/ldscript.amd64
@@ -0,0 +1,72 @@
+/* $FreeBSD$ */
+OUTPUT_FORMAT("elf64-x86-64-freebsd", "elf64-x86-64-freebsd", "elf64-x86-64-freebsd")
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0;
+ ImageBase = .;
+ .hash : { *(.hash) } /* this MUST come first! */
+ . = ALIGN(4096);
+ .eh_frame :
+ {
+ *(.eh_frame)
+ }
+ . = ALIGN(4096);
+ .text : {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.plt)
+ } =0xCCCCCCCC
+ . = ALIGN(4096);
+ .data : {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ *(.rodata1)
+ *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+ *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
+ *(.opd)
+ *(.data .data.* .gnu.linkonce.d.*)
+ *(.data1)
+ *(.plabel)
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ }
+ . = ALIGN(4096);
+ set_Xcommand_set : {
+ __start_set_Xcommand_set = .;
+ *(set_Xcommand_set)
+ __stop_set_Xcommand_set = .;
+ }
+ set_Xficl_compile_set : {
+ __start_set_Xficl_compile_set = .;
+ *(set_Xficl_compile_set)
+ __stop_set_Xficl_compile_set = .;
+ }
+ . = ALIGN(4096);
+ __gp = .;
+ .sdata : {
+ *(.got.plt .got)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ *(dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ }
+ . = ALIGN(4096);
+ .dynamic : { *(.dynamic) }
+ . = ALIGN(4096);
+ .rela.dyn : {
+ *(.rela.data*)
+ *(.rela.got)
+ *(.rela.stab)
+ *(.relaset_*)
+ }
+ . = ALIGN(4096);
+ .reloc : { *(.reloc) }
+ . = ALIGN(4096);
+ .dynsym : { *(.dynsym) }
+ . = ALIGN(4096);
+ .dynstr : { *(.dynstr) }
+}
diff --git a/stand/efi/loader/arch/amd64/start.S b/stand/efi/loader/arch/amd64/start.S
new file mode 100644
index 0000000..774ef4f
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/start.S
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (C) 1999 Hewlett-Packard Co.
+ * Contributed by David Mosberger <davidm@hpl.hp.com>.
+ * Copyright (C) 2005 Intel Co.
+ * Contributed by Fenghua Yu <fenghua.yu@intel.com>.
+ * 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 Hewlett-Packard Co. 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.
+ */
+
+/*
+ * crt0-efi-x86_64.S - x86_64 EFI startup code.
+ * $FreeBSD$
+ */
+
+ .text
+ .align 4
+
+ .globl _start
+_start:
+ subq $8, %rsp
+ pushq %rcx
+ pushq %rdx
+
+0:
+ lea ImageBase(%rip), %rdi
+ lea _DYNAMIC(%rip), %rsi
+
+ popq %rcx
+ popq %rdx
+ pushq %rcx
+ pushq %rdx
+ call self_reloc
+
+ popq %rdi
+ popq %rsi
+
+ call efi_main
+ addq $8, %rsp
+
+.exit:
+ ret
+
+ /*
+ * hand-craft a dummy .reloc section so EFI knows it's a relocatable
+ * executable:
+ */
+
+ .data
+ .section .reloc, "a"
+ .long 0
+ .long 10
+ .word 0
diff --git a/stand/efi/loader/arch/amd64/trap.c b/stand/efi/loader/arch/amd64/trap.c
new file mode 100644
index 0000000..e8cf188
--- /dev/null
+++ b/stand/efi/loader/arch/amd64/trap.c
@@ -0,0 +1,408 @@
+/*-
+ * Copyright (c) 2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Konstantin Belousov under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include <sys/param.h>
+#include <machine/cpufunc.h>
+#include <machine/psl.h>
+#include <machine/segments.h>
+#include <machine/frame.h>
+#include <machine/tss.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+#include "loader_efi.h"
+
+#define NUM_IST 8
+#define NUM_EXC 32
+
+/*
+ * This code catches exceptions but forwards hardware interrupts to
+ * handlers installed by firmware. It differentiates exceptions
+ * vs. interrupts by presence of the error code on the stack, which
+ * causes different stack pointer value on trap handler entry.
+ *
+ * Use kernel layout for the trapframe just to not be original.
+ *
+ * Use free IST slot in existing TSS, or create our own TSS if
+ * firmware did not configured any, to have stack switched to
+ * IST-specified one, e.g. to handle #SS. If hand-off cannot find
+ * unused IST slot, or create a new descriptor in GDT, we bail out.
+ */
+
+static struct region_descriptor fw_idt; /* Descriptor for pristine fw IDT */
+static struct region_descriptor loader_idt;/* Descriptor for loader
+ shadow IDT */
+static EFI_PHYSICAL_ADDRESS lidt_pa; /* Address of loader shadow IDT */
+static EFI_PHYSICAL_ADDRESS tss_pa; /* Address of TSS */
+static EFI_PHYSICAL_ADDRESS exc_stack_pa;/* Address of IST stack for loader */
+EFI_PHYSICAL_ADDRESS exc_rsp; /* %rsp value on our IST stack when
+ exception happens */
+EFI_PHYSICAL_ADDRESS fw_intr_handlers[NUM_EXC]; /* fw handlers for < 32 IDT
+ vectors */
+static int intercepted[NUM_EXC];
+static int ist; /* IST for exception handlers */
+static uint32_t tss_fw_seg; /* Fw TSS segment */
+static uint32_t loader_tss; /* Loader TSS segment */
+static struct region_descriptor fw_gdt; /* Descriptor of pristine GDT */
+static EFI_PHYSICAL_ADDRESS loader_gdt_pa; /* Address of loader shadow GDT */
+
+void report_exc(struct trapframe *tf);
+void
+report_exc(struct trapframe *tf)
+{
+
+ /*
+ * printf() depends on loader runtime and UEFI firmware health
+ * to produce the console output, in case of exception, the
+ * loader or firmware runtime may fail to support the printf().
+ */
+ printf("===================================================="
+ "============================\n");
+ printf("Exception %u\n", tf->tf_trapno);
+ printf("ss 0x%04hx cs 0x%04hx ds 0x%04hx es 0x%04hx fs 0x%04hx "
+ "gs 0x%04hx\n",
+ (uint16_t)tf->tf_ss, (uint16_t)tf->tf_cs, (uint16_t)tf->tf_ds,
+ (uint16_t)tf->tf_es, (uint16_t)tf->tf_fs, (uint16_t)tf->tf_gs);
+ printf("err 0x%08x rfl 0x%08x addr 0x%016lx\n"
+ "rsp 0x%016lx rip 0x%016lx\n",
+ (uint32_t)tf->tf_err, (uint32_t)tf->tf_rflags, tf->tf_addr,
+ tf->tf_rsp, tf->tf_rip);
+ printf(
+ "rdi 0x%016lx rsi 0x%016lx rdx 0x%016lx\n"
+ "rcx 0x%016lx r8 0x%016lx r9 0x%016lx\n"
+ "rax 0x%016lx rbx 0x%016lx rbp 0x%016lx\n"
+ "r10 0x%016lx r11 0x%016lx r12 0x%016lx\n"
+ "r13 0x%016lx r14 0x%016lx r15 0x%016lx\n",
+ tf->tf_rdi, tf->tf_rsi, tf->tf_rdx, tf->tf_rcx, tf->tf_r8,
+ tf->tf_r9, tf->tf_rax, tf->tf_rbx, tf->tf_rbp, tf->tf_r10,
+ tf->tf_r11, tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15);
+ printf("Machine stopped.\n");
+}
+
+static void
+prepare_exception(unsigned idx, uint64_t my_handler,
+ int ist_use_table[static NUM_IST])
+{
+ struct gate_descriptor *fw_idt_e, *loader_idt_e;
+
+ fw_idt_e = &((struct gate_descriptor *)fw_idt.rd_base)[idx];
+ loader_idt_e = &((struct gate_descriptor *)loader_idt.rd_base)[idx];
+ fw_intr_handlers[idx] = fw_idt_e->gd_looffset +
+ (fw_idt_e->gd_hioffset << 16);
+ intercepted[idx] = 1;
+ ist_use_table[fw_idt_e->gd_ist]++;
+ loader_idt_e->gd_looffset = my_handler;
+ loader_idt_e->gd_hioffset = my_handler >> 16;
+ /*
+ * We reuse uefi selector for the code segment for the exception
+ * handler code, while the reason for the fault might be the
+ * corruption of that gdt entry. On the other hand, allocating
+ * our own descriptor might be not much better, if gdt is corrupted.
+ */
+ loader_idt_e->gd_selector = fw_idt_e->gd_selector;
+ loader_idt_e->gd_ist = 0;
+ loader_idt_e->gd_type = SDT_SYSIGT;
+ loader_idt_e->gd_dpl = 0;
+ loader_idt_e->gd_p = 1;
+ loader_idt_e->gd_xx = 0;
+ loader_idt_e->sd_xx1 = 0;
+}
+#define PREPARE_EXCEPTION(N) \
+ extern char EXC##N##_handler[]; \
+ prepare_exception(N, (uintptr_t)EXC##N##_handler, ist_use_table);
+
+static void
+free_tables(void)
+{
+
+ if (lidt_pa != 0) {
+ BS->FreePages(lidt_pa, EFI_SIZE_TO_PAGES(fw_idt.rd_limit));
+ lidt_pa = 0;
+ }
+ if (exc_stack_pa != 0) {
+ BS->FreePages(exc_stack_pa, 1);
+ exc_stack_pa = 0;
+ }
+ if (tss_pa != 0 && tss_fw_seg == 0) {
+ BS->FreePages(tss_pa, EFI_SIZE_TO_PAGES(sizeof(struct
+ amd64tss)));
+ tss_pa = 0;
+ }
+ if (loader_gdt_pa != 0) {
+ BS->FreePages(tss_pa, 2);
+ loader_gdt_pa = 0;
+ }
+ ist = 0;
+ loader_tss = 0;
+}
+
+static int
+efi_setup_tss(struct region_descriptor *gdt, uint32_t loader_tss_idx,
+ struct amd64tss **tss)
+{
+ EFI_STATUS status;
+ struct system_segment_descriptor *tss_desc;
+
+ tss_desc = (struct system_segment_descriptor *)(gdt->rd_base +
+ (loader_tss_idx << 3));
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+ EFI_SIZE_TO_PAGES(sizeof(struct amd64tss)), &tss_pa);
+ if (EFI_ERROR(status)) {
+ printf("efi_setup_tss: AllocatePages tss error %lu\n",
+ EFI_ERROR_CODE(status));
+ return (0);
+ }
+ *tss = (struct amd64tss *)tss_pa;
+ bzero(*tss, sizeof(**tss));
+ tss_desc->sd_lolimit = sizeof(struct amd64tss);
+ tss_desc->sd_lobase = tss_pa;
+ tss_desc->sd_type = SDT_SYSTSS;
+ tss_desc->sd_dpl = 0;
+ tss_desc->sd_p = 1;
+ tss_desc->sd_hilimit = sizeof(struct amd64tss) >> 16;
+ tss_desc->sd_gran = 0;
+ tss_desc->sd_hibase = tss_pa >> 24;
+ tss_desc->sd_xx0 = 0;
+ tss_desc->sd_xx1 = 0;
+ tss_desc->sd_mbz = 0;
+ tss_desc->sd_xx2 = 0;
+ return (1);
+}
+
+static int
+efi_redirect_exceptions(void)
+{
+ int ist_use_table[NUM_IST];
+ struct gate_descriptor *loader_idt_e;
+ struct system_segment_descriptor *tss_desc, *gdt_desc;
+ struct amd64tss *tss;
+ struct region_descriptor *gdt_rd, loader_gdt;
+ uint32_t i;
+ EFI_STATUS status;
+ register_t rfl;
+
+ sidt(&fw_idt);
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+ EFI_SIZE_TO_PAGES(fw_idt.rd_limit), &lidt_pa);
+ if (EFI_ERROR(status)) {
+ printf("efi_redirect_exceptions: AllocatePages IDT error %lu\n",
+ EFI_ERROR_CODE(status));
+ lidt_pa = 0;
+ return (0);
+ }
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 1,
+ &exc_stack_pa);
+ if (EFI_ERROR(status)) {
+ printf("efi_redirect_exceptions: AllocatePages stk error %lu\n",
+ EFI_ERROR_CODE(status));
+ exc_stack_pa = 0;
+ free_tables();
+ return (0);
+ }
+ loader_idt.rd_limit = fw_idt.rd_limit;
+ bcopy((void *)fw_idt.rd_base, (void *)loader_idt.rd_base,
+ loader_idt.rd_limit);
+ bzero(ist_use_table, sizeof(ist_use_table));
+ bzero(fw_intr_handlers, sizeof(fw_intr_handlers));
+ bzero(intercepted, sizeof(intercepted));
+
+ sgdt(&fw_gdt);
+ tss_fw_seg = read_tr();
+ gdt_rd = NULL;
+ if (tss_fw_seg == 0) {
+ for (i = 2; (i << 3) + sizeof(*gdt_desc) <= fw_gdt.rd_limit;
+ i += 2) {
+ gdt_desc = (struct system_segment_descriptor *)(
+ fw_gdt.rd_base + (i << 3));
+ if (gdt_desc->sd_type == 0 && gdt_desc->sd_mbz == 0) {
+ gdt_rd = &fw_gdt;
+ break;
+ }
+ }
+ if (gdt_rd == NULL) {
+ if (i >= 8190) {
+ printf("efi_redirect_exceptions: all slots "
+ "in gdt are used\n");
+ free_tables();
+ return (0);
+ }
+ loader_gdt.rd_limit = roundup2(fw_gdt.rd_limit +
+ sizeof(struct system_segment_descriptor),
+ sizeof(struct system_segment_descriptor)) - 1;
+ i = (loader_gdt.rd_limit + 1 -
+ sizeof(struct system_segment_descriptor)) /
+ sizeof(struct system_segment_descriptor) * 2;
+ status = BS->AllocatePages(AllocateAnyPages,
+ EfiLoaderData,
+ EFI_SIZE_TO_PAGES(loader_gdt.rd_limit),
+ &loader_gdt_pa);
+ if (EFI_ERROR(status)) {
+ printf("efi_setup_tss: AllocatePages gdt error "
+ "%lu\n", EFI_ERROR_CODE(status));
+ loader_gdt_pa = 0;
+ free_tables();
+ return (0);
+ }
+ loader_gdt.rd_base = loader_gdt_pa;
+ bzero((void *)loader_gdt.rd_base, loader_gdt.rd_limit);
+ bcopy((void *)fw_gdt.rd_base,
+ (void *)loader_gdt.rd_base, fw_gdt.rd_limit);
+ gdt_rd = &loader_gdt;
+ }
+ loader_tss = i << 3;
+ if (!efi_setup_tss(gdt_rd, i, &tss)) {
+ tss_pa = 0;
+ free_tables();
+ return (0);
+ }
+ } else {
+ tss_desc = (struct system_segment_descriptor *)((char *)
+ fw_gdt.rd_base + tss_fw_seg);
+ if (tss_desc->sd_type != SDT_SYSTSS &&
+ tss_desc->sd_type != SDT_SYSBSY) {
+ printf("LTR points to non-TSS descriptor\n");
+ free_tables();
+ return (0);
+ }
+ tss_pa = tss_desc->sd_lobase + (tss_desc->sd_hibase << 16);
+ tss = (struct amd64tss *)tss_pa;
+ tss_desc->sd_type = SDT_SYSTSS; /* unbusy */
+ }
+
+ PREPARE_EXCEPTION(0);
+ PREPARE_EXCEPTION(1);
+ PREPARE_EXCEPTION(2);
+ PREPARE_EXCEPTION(3);
+ PREPARE_EXCEPTION(4);
+ PREPARE_EXCEPTION(5);
+ PREPARE_EXCEPTION(6);
+ PREPARE_EXCEPTION(7);
+ PREPARE_EXCEPTION(8);
+ PREPARE_EXCEPTION(9);
+ PREPARE_EXCEPTION(10);
+ PREPARE_EXCEPTION(11);
+ PREPARE_EXCEPTION(12);
+ PREPARE_EXCEPTION(13);
+ PREPARE_EXCEPTION(14);
+ PREPARE_EXCEPTION(16);
+ PREPARE_EXCEPTION(17);
+ PREPARE_EXCEPTION(18);
+ PREPARE_EXCEPTION(19);
+ PREPARE_EXCEPTION(20);
+
+ exc_rsp = exc_stack_pa + PAGE_SIZE -
+ (6 /* hw exception frame */ + 3 /* scratch regs */) * 8;
+
+ /* Find free IST and use it */
+ for (ist = 1; ist < NUM_IST; ist++) {
+ if (ist_use_table[ist] == 0)
+ break;
+ }
+ if (ist == NUM_IST) {
+ printf("efi_redirect_exceptions: all ISTs used\n");
+ free_tables();
+ lidt_pa = 0;
+ return (0);
+ }
+ for (i = 0; i < NUM_EXC; i++) {
+ loader_idt_e = &((struct gate_descriptor *)loader_idt.
+ rd_base)[i];
+ if (intercepted[i])
+ loader_idt_e->gd_ist = ist;
+ }
+ (&(tss->tss_ist1))[ist - 1] = exc_stack_pa + PAGE_SIZE;
+
+ /* Switch to new IDT */
+ rfl = intr_disable();
+ if (loader_gdt_pa != 0)
+ bare_lgdt(&loader_gdt);
+ if (loader_tss != 0)
+ ltr(loader_tss);
+ lidt(&loader_idt);
+ intr_restore(rfl);
+ return (1);
+}
+
+static void
+efi_unredirect_exceptions(void)
+{
+ register_t rfl;
+
+ if (lidt_pa == 0)
+ return;
+
+ rfl = intr_disable();
+ if (ist != 0)
+ (&(((struct amd64tss *)tss_pa)->tss_ist1))[ist - 1] = 0;
+ if (loader_gdt_pa != 0)
+ bare_lgdt(&fw_gdt);
+ if (loader_tss != 0)
+ ltr(tss_fw_seg);
+ lidt(&fw_idt);
+ intr_restore(rfl);
+ free_tables();
+}
+
+static int
+command_grab_faults(int argc, char *argv[])
+{
+ int res;
+
+ res = efi_redirect_exceptions();
+ if (!res)
+ printf("failed\n");
+ return (CMD_OK);
+}
+COMMAND_SET(grap_faults, "grab_faults", "grab faults", command_grab_faults);
+
+static int
+command_ungrab_faults(int argc, char *argv[])
+{
+
+ efi_unredirect_exceptions();
+ return (CMD_OK);
+}
+COMMAND_SET(ungrab_faults, "ungrab_faults", "ungrab faults",
+ command_ungrab_faults);
+
+static int
+command_fault(int argc, char *argv[])
+{
+
+ __asm("ud2");
+ return (CMD_OK);
+}
+COMMAND_SET(fault, "fault", "generate fault", command_fault);
diff --git a/stand/efi/loader/arch/arm/Makefile.inc b/stand/efi/loader/arch/arm/Makefile.inc
new file mode 100644
index 0000000..74e6616
--- /dev/null
+++ b/stand/efi/loader/arch/arm/Makefile.inc
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SRCS+= exec.c \
+ start.S
+
+HAVE_FDT=yes
diff --git a/stand/efi/loader/arch/arm/exec.c b/stand/efi/loader/arch/arm/exec.c
new file mode 100644
index 0000000..83d3f2b
--- /dev/null
+++ b/stand/efi/loader/arch/arm/exec.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 2001 Benno Rice <benno@FreeBSD.org>
+ * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/linker.h>
+
+#include <machine/md_var.h>
+#include <machine/metadata.h>
+#include <machine/elf.h>
+
+#include <stand.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+#include "loader_efi.h"
+
+extern vm_offset_t md_load(char *, vm_offset_t *);
+extern int bi_load(char *, vm_offset_t *, vm_offset_t *);
+
+static int
+__elfN(arm_load)(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ int r;
+
+ r = __elfN(loadfile)(filename, dest, result);
+ if (r != 0)
+ return (r);
+
+ return (0);
+}
+
+static int
+__elfN(arm_exec)(struct preloaded_file *fp)
+{
+ struct file_metadata *fmp;
+ vm_offset_t modulep, kernend;
+ Elf_Ehdr *e;
+ int error;
+ void (*entry)(void *);
+
+ if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return (EFTYPE);
+
+ e = (Elf_Ehdr *)&fmp->md_data;
+
+ efi_time_fini();
+ if ((error = bi_load(fp->f_args, &modulep, &kernend)) != 0) {
+ efi_time_init();
+ return (error);
+ }
+
+ entry = efi_translate(e->e_entry);
+ printf("Kernel entry at 0x%x...\n", (unsigned)entry);
+ printf("Kernel args: %s\n", fp->f_args);
+ printf("modulep: %#x\n", modulep);
+ printf("relocation_offset %llx\n", __elfN(relocation_offset));
+
+ dev_cleanup();
+
+ (*entry)((void *)modulep);
+ panic("exec returned");
+}
+
+static struct file_format arm_elf = {
+ __elfN(arm_load),
+ __elfN(arm_exec)
+};
+
+struct file_format *file_formats[] = {
+ &arm_elf,
+ NULL
+};
+
diff --git a/stand/efi/loader/arch/arm/ldscript.arm b/stand/efi/loader/arch/arm/ldscript.arm
new file mode 100644
index 0000000..9f6e7c2
--- /dev/null
+++ b/stand/efi/loader/arch/arm/ldscript.arm
@@ -0,0 +1,67 @@
+/* $FreeBSD$ */
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0;
+ ImageBase = .;
+ .text : {
+ *(.peheader)
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ PROVIDE (etext = .);
+ . = ALIGN(16);
+ .data :
+ {
+ *(.data .data.*)
+ *(.gnu.linkonce.d*)
+ *(.rodata)
+ *(.rodata.*)
+ CONSTRUCTORS
+
+ . = ALIGN(4);
+ PROVIDE (__bss_start = .);
+ *(.sbss)
+ *(.scommon)
+ *(.dynsbss)
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ . = ALIGN(4);
+ PROVIDE (__bss_end = .);
+ }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : {
+ *(.got.plt .got)
+ *(.sdata*.sdata.* .gnu.linkonce.s.*)
+ }
+ set_Xcommand_set : {
+ __start_set_Xcommand_set = .;
+ *(set_Xcommand_set)
+ __stop_set_Xcommand_set = .;
+ }
+ set_Xficl_compile_set : {
+ __start_set_Xficl_compile_set = .;
+ *(set_Xficl_compile_set)
+ __stop_set_Xficl_compile_set = .;
+ }
+ __gp = .;
+ .plt : { *(.plt) }
+ .dynamic : { *(.dynamic) }
+ .reloc : { *(.reloc) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .rel.dyn : {
+ *(.rel.*)
+ *(.relset_*)
+ }
+ _edata = .;
+ .hash : { *(.hash) }
+}
diff --git a/stand/efi/loader/arch/arm/start.S b/stand/efi/loader/arch/arm/start.S
new file mode 100644
index 0000000..5b6182d
--- /dev/null
+++ b/stand/efi/loader/arch/arm/start.S
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 2014, 2015 Andrew Turner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+/*
+ * We need to be a PE32 file for EFI. On some architectures we can use
+ * objcopy to create the correct file, however on arm we need to do
+ * it ourselves.
+ */
+
+#define IMAGE_FILE_MACHINE_ARM 0x01c2
+
+#define IMAGE_SCN_CNT_CODE 0x00000020
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000
+#define IMAGE_SCN_MEM_READ 0x40000000
+
+ .section .peheader,"a"
+efi_start:
+ /* The MS-DOS Stub, only used to get the offset of the COFF header */
+ .ascii "MZ"
+ .short 0
+ .space 0x38
+ .long pe_sig - efi_start
+
+ /* The PE32 Signature. Needs to be 8-byte aligned */
+ .align 3
+pe_sig:
+ .ascii "PE"
+ .short 0
+coff_head:
+ .short IMAGE_FILE_MACHINE_ARM /* ARM file */
+ .short 2 /* 2 Sections */
+ .long 0 /* Timestamp */
+ .long 0 /* No symbol table */
+ .long 0 /* No symbols */
+ .short section_table - optional_header /* Optional header size */
+ .short 0 /* Characteristics TODO: Fill in */
+
+optional_header:
+ .short 0x010b /* PE32 (32-bit addressing) */
+ .byte 0 /* Major linker version */
+ .byte 0 /* Minor linker version */
+ .long _edata - _end_header /* Code size */
+ .long 0 /* No initialized data */
+ .long 0 /* No uninitialized data */
+ .long _start - efi_start /* Entry point */
+ .long _end_header - efi_start /* Start of code */
+ .long 0 /* Start of data */
+
+optional_windows_header:
+ .long 0 /* Image base */
+ .long 32 /* Section Alignment */
+ .long 8 /* File alignment */
+ .short 0 /* Major OS version */
+ .short 0 /* Minor OS version */
+ .short 0 /* Major image version */
+ .short 0 /* Minor image version */
+ .short 0 /* Major subsystem version */
+ .short 0 /* Minor subsystem version */
+ .long 0 /* Win32 version */
+ .long _edata - efi_start /* Image size */
+ .long _end_header - efi_start /* Header size */
+ .long 0 /* Checksum */
+ .short 0xa /* Subsystem (EFI app) */
+ .short 0 /* DLL Characteristics */
+ .long 0 /* Stack reserve */
+ .long 0 /* Stack commit */
+ .long 0 /* Heap reserve */
+ .long 0 /* Heap commit */
+ .long 0 /* Loader flags */
+ .long 6 /* Number of RVAs */
+
+ /* RVAs: */
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+
+section_table:
+ /* We need a .reloc section for EFI */
+ .ascii ".reloc"
+ .byte 0
+ .byte 0 /* Pad to 8 bytes */
+ .long 0 /* Virtual size */
+ .long 0 /* Virtual address */
+ .long 0 /* Size of raw data */
+ .long 0 /* Pointer to raw data */
+ .long 0 /* Pointer to relocations */
+ .long 0 /* Pointer to line numbers */
+ .short 0 /* Number of relocations */
+ .short 0 /* Number of line numbers */
+ .long (IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | \
+ IMAGE_SCN_MEM_DISCARDABLE) /* Characteristics */
+
+ /* The contents of the loader */
+ .ascii ".text"
+ .byte 0
+ .byte 0
+ .byte 0 /* Pad to 8 bytes */
+ .long _edata - _end_header /* Virtual size */
+ .long _end_header - efi_start /* Virtual address */
+ .long _edata - _end_header /* Size of raw data */
+ .long _end_header - efi_start /* Pointer to raw data */
+ .long 0 /* Pointer to relocations */
+ .long 0 /* Pointer to line numbers */
+ .short 0 /* Number of relocations */
+ .short 0 /* Number of line numbers */
+ .long (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | \
+ IMAGE_SCN_MEM_READ) /* Characteristics */
+_end_header:
+
+ .text
+_start:
+ /* Save the boot params to the stack */
+ push {r0, r1}
+
+ adr r0, .Lbase
+ ldr r1, [r0]
+ sub r5, r0, r1
+
+ ldr r0, .Limagebase
+ add r0, r0, r5
+ ldr r1, .Ldynamic
+ add r1, r1, r5
+
+ bl _C_LABEL(self_reloc)
+
+ /* Zero the BSS, _reloc fixed the values for us */
+ ldr r0, .Lbss
+ ldr r1, .Lbssend
+ mov r2, #0
+
+1: cmp r0, r1
+ bge 2f
+ str r2, [r0], #4
+ b 1b
+2:
+
+ pop {r0, r1}
+ bl _C_LABEL(efi_main)
+
+1: b 1b
+
+.Lbase:
+ .word .
+.Limagebase:
+ .word ImageBase
+.Ldynamic:
+ .word _DYNAMIC
+.Lbss:
+ .word __bss_start
+.Lbssend:
+ .word __bss_end
+
+.align 3
+stack:
+ .space 512
+stack_end:
+
diff --git a/stand/efi/loader/arch/arm64/Makefile.inc b/stand/efi/loader/arch/arm64/Makefile.inc
new file mode 100644
index 0000000..a71bcc2
--- /dev/null
+++ b/stand/efi/loader/arch/arm64/Makefile.inc
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+HAVE_FDT=yes
+
+SRCS+= exec.c \
+ start.S
+
+.PATH: ${BOOTSRC}/arm64/libarm64
+CFLAGS+=-I${BOOTSRC}/arm64/libarm64
+SRCS+= cache.c
+
+CFLAGS+= -mgeneral-regs-only
diff --git a/stand/efi/loader/arch/arm64/exec.c b/stand/efi/loader/arch/arm64/exec.c
new file mode 100644
index 0000000..4532126
--- /dev/null
+++ b/stand/efi/loader/arch/arm64/exec.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <machine/elf.h>
+
+#include <bootstrap.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "loader_efi.h"
+#include "cache.h"
+
+#include "platform/acfreebsd.h"
+#include "acconfig.h"
+#define ACPI_SYSTEM_XFACE
+#define ACPI_USE_SYSTEM_INTTYPES
+#include "actypes.h"
+#include "actbl.h"
+
+static EFI_GUID acpi_guid = ACPI_TABLE_GUID;
+static EFI_GUID acpi20_guid = ACPI_20_TABLE_GUID;
+
+static int elf64_exec(struct preloaded_file *amp);
+static int elf64_obj_exec(struct preloaded_file *amp);
+
+int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp);
+
+static struct file_format arm64_elf = {
+ elf64_loadfile,
+ elf64_exec
+};
+
+struct file_format *file_formats[] = {
+ &arm64_elf,
+ NULL
+};
+
+static int
+elf64_exec(struct preloaded_file *fp)
+{
+ vm_offset_t modulep, kernendp;
+ vm_offset_t clean_addr;
+ size_t clean_size;
+ struct file_metadata *md;
+ ACPI_TABLE_RSDP *rsdp;
+ Elf_Ehdr *ehdr;
+ char buf[24];
+ int err, revision;
+ void (*entry)(vm_offset_t);
+
+ rsdp = efi_get_table(&acpi20_guid);
+ if (rsdp == NULL) {
+ rsdp = efi_get_table(&acpi_guid);
+ }
+ if (rsdp != NULL) {
+ sprintf(buf, "0x%016llx", (unsigned long long)rsdp);
+ setenv("hint.acpi.0.rsdp", buf, 1);
+ revision = rsdp->Revision;
+ if (revision == 0)
+ revision = 1;
+ sprintf(buf, "%d", revision);
+ setenv("hint.acpi.0.revision", buf, 1);
+ strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId));
+ buf[sizeof(rsdp->OemId)] = '\0';
+ setenv("hint.acpi.0.oem", buf, 1);
+ sprintf(buf, "0x%016x", rsdp->RsdtPhysicalAddress);
+ setenv("hint.acpi.0.rsdt", buf, 1);
+ if (revision >= 2) {
+ /* XXX extended checksum? */
+ sprintf(buf, "0x%016llx",
+ (unsigned long long)rsdp->XsdtPhysicalAddress);
+ setenv("hint.acpi.0.xsdt", buf, 1);
+ sprintf(buf, "%d", rsdp->Length);
+ setenv("hint.acpi.0.xsdt_length", buf, 1);
+ }
+ }
+
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return(EFTYPE);
+
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+ entry = efi_translate(ehdr->e_entry);
+
+ efi_time_fini();
+ err = bi_load(fp->f_args, &modulep, &kernendp);
+ if (err != 0) {
+ efi_time_init();
+ return (err);
+ }
+
+ dev_cleanup();
+
+ /* Clean D-cache under kernel area and invalidate whole I-cache */
+ clean_addr = (vm_offset_t)efi_translate(fp->f_addr);
+ clean_size = (vm_offset_t)efi_translate(kernendp) - clean_addr;
+
+ cpu_flush_dcache((void *)clean_addr, clean_size);
+ cpu_inval_icache(NULL, 0);
+
+ (*entry)(modulep);
+ panic("exec returned");
+}
+
+static int
+elf64_obj_exec(struct preloaded_file *fp)
+{
+
+ printf("%s called for preloaded file %p (=%s):\n", __func__, fp,
+ fp->f_name);
+ return (ENOSYS);
+}
+
diff --git a/stand/efi/loader/arch/arm64/ldscript.arm64 b/stand/efi/loader/arch/arm64/ldscript.arm64
new file mode 100644
index 0000000..685c096
--- /dev/null
+++ b/stand/efi/loader/arch/arm64/ldscript.arm64
@@ -0,0 +1,85 @@
+/* $FreeBSD$ */
+/*
+OUTPUT_FORMAT("elf64-aarch64-freebsd", "elf64-aarch64-freebsd", "elf64-aarch64-freebsd")
+*/
+OUTPUT_ARCH(aarch64)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0;
+ ImageBase = .;
+ .text : {
+ *(.peheader)
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.plt)
+ } =0xD4200000
+ . = ALIGN(16);
+ .data : {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ *(.rodata1)
+ *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+ *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
+ *(.opd)
+ *(.data .data.* .gnu.linkonce.d.*)
+ *(.data1)
+ *(.plabel)
+
+ . = ALIGN(16);
+ __bss_start = .;
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ *(.dynbss)
+ *(.bss *.bss.*)
+ *(COMMON)
+ . = ALIGN(16);
+ __bss_end = .;
+ }
+ . = ALIGN(16);
+ set_Xcommand_set : {
+ __start_set_Xcommand_set = .;
+ *(set_Xcommand_set)
+ __stop_set_Xcommand_set = .;
+ }
+ set_Xficl_compile_set : {
+ __start_set_Xficl_compile_set = .;
+ *(set_Xficl_compile_set)
+ __stop_set_Xficl_compile_set = .;
+ }
+ . = ALIGN(16);
+ __gp = .;
+ .sdata : {
+ *(.got.plt .got)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ *(dynsbss)
+ *(.scommon)
+ }
+ . = ALIGN(16);
+ .dynamic : { *(.dynamic) }
+ . = ALIGN(16);
+ .rela.dyn : {
+ *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+ *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+ *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+ *(.rela.got)
+ *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
+ *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
+ *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
+ *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
+ *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+ *(.rela.plt)
+ *(.relset_*)
+ *(.rela.dyn .rela.dyn.*)
+ }
+ . = ALIGN(16);
+ .reloc : { *(.reloc) }
+ . = ALIGN(16);
+ .dynsym : { *(.dynsym) }
+ _edata = .;
+
+ /* Unused sections */
+ .dynstr : { *(.dynstr) }
+ .hash : { *(.hash) }
+}
diff --git a/stand/efi/loader/arch/arm64/start.S b/stand/efi/loader/arch/arm64/start.S
new file mode 100644
index 0000000..b58c2c5
--- /dev/null
+++ b/stand/efi/loader/arch/arm64/start.S
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 2014 Andrew Turner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * We need to be a PE32+ file for EFI. On some architectures we can use
+ * objcopy to create the correct file, however on arm64 we need to do
+ * it ourselves.
+ */
+
+#define IMAGE_FILE_MACHINE_ARM64 0xaa64
+
+#define IMAGE_SCN_CNT_CODE 0x00000020
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000
+#define IMAGE_SCN_MEM_READ 0x40000000
+
+ .section .peheader,"a"
+efi_start:
+ /* The MS-DOS Stub, only used to get the offset of the COFF header */
+ .ascii "MZ"
+ .short 0
+ .space 0x38
+ .long pe_sig - efi_start
+
+ /* The PE32 Signature. Needs to be 8-byte aligned */
+ .align 3
+pe_sig:
+ .ascii "PE"
+ .short 0
+coff_head:
+ .short IMAGE_FILE_MACHINE_ARM64 /* AArch64 file */
+ .short 2 /* 2 Sections */
+ .long 0 /* Timestamp */
+ .long 0 /* No symbol table */
+ .long 0 /* No symbols */
+ .short section_table - optional_header /* Optional header size */
+ .short 0 /* Characteristics TODO: Fill in */
+
+optional_header:
+ .short 0x020b /* PE32+ (64-bit addressing) */
+ .byte 0 /* Major linker version */
+ .byte 0 /* Minor linker version */
+ .long _edata - _end_header /* Code size */
+ .long 0 /* No initialized data */
+ .long 0 /* No uninitialized data */
+ .long _start - efi_start /* Entry point */
+ .long _end_header - efi_start /* Start of code */
+
+optional_windows_header:
+ .quad 0 /* Image base */
+ .long 32 /* Section Alignment */
+ .long 8 /* File alignment */
+ .short 0 /* Major OS version */
+ .short 0 /* Minor OS version */
+ .short 0 /* Major image version */
+ .short 0 /* Minor image version */
+ .short 0 /* Major subsystem version */
+ .short 0 /* Minor subsystem version */
+ .long 0 /* Win32 version */
+ .long _edata - efi_start /* Image size */
+ .long _end_header - efi_start /* Header size */
+ .long 0 /* Checksum */
+ .short 0xa /* Subsystem (EFI app) */
+ .short 0 /* DLL Characteristics */
+ .quad 0 /* Stack reserve */
+ .quad 0 /* Stack commit */
+ .quad 0 /* Heap reserve */
+ .quad 0 /* Heap commit */
+ .long 0 /* Loader flags */
+ .long 6 /* Number of RVAs */
+
+ /* RVAs: */
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+
+section_table:
+ /* We need a .reloc section for EFI */
+ .ascii ".reloc"
+ .byte 0
+ .byte 0 /* Pad to 8 bytes */
+ .long 0 /* Virtual size */
+ .long 0 /* Virtual address */
+ .long 0 /* Size of raw data */
+ .long 0 /* Pointer to raw data */
+ .long 0 /* Pointer to relocations */
+ .long 0 /* Pointer to line numbers */
+ .short 0 /* Number of relocations */
+ .short 0 /* Number of line numbers */
+ .long (IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | \
+ IMAGE_SCN_MEM_DISCARDABLE) /* Characteristics */
+
+ /* The contents of the loader */
+ .ascii ".text"
+ .byte 0
+ .byte 0
+ .byte 0 /* Pad to 8 bytes */
+ .long _edata - _end_header /* Virtual size */
+ .long _end_header - efi_start /* Virtual address */
+ .long _edata - _end_header /* Size of raw data */
+ .long _end_header - efi_start /* Pointer to raw data */
+ .long 0 /* Pointer to relocations */
+ .long 0 /* Pointer to line numbers */
+ .short 0 /* Number of relocations */
+ .short 0 /* Number of line numbers */
+ .long (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | \
+ IMAGE_SCN_MEM_READ) /* Characteristics */
+_end_header:
+
+ .text
+ .globl _start
+_start:
+ /* Save the boot params to the stack */
+ stp x0, x1, [sp, #-16]!
+
+ adr x0, __bss_start
+ adr x1, __bss_end
+
+ b 2f
+
+1:
+ stp xzr, xzr, [x0], #16
+2:
+ cmp x0, x1
+ b.lo 1b
+
+ adr x0, ImageBase
+ adr x1, _DYNAMIC
+
+ bl self_reloc
+
+ ldp x0, x1, [sp], #16
+
+ bl efi_main
+
+1: b 1b
diff --git a/stand/efi/loader/arch/i386/Makefile.inc b/stand/efi/loader/arch/i386/Makefile.inc
new file mode 100644
index 0000000..70d2848
--- /dev/null
+++ b/stand/efi/loader/arch/i386/Makefile.inc
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+SRCS+= start.S \
+ efimd.c \
+ elf32_freebsd.c \
+ exec.c
+
+.PATH: ${BOOTSRC}/i386/libi386
+SRCS+= nullconsole.c \
+ comconsole.c \
+ spinconsole.c
+
+CFLAGS+= -fPIC -DTERM_EMU
+LDFLAGS+= -Wl,-znocombreloc
diff --git a/stand/efi/loader/arch/i386/bootinfo.c b/stand/efi/loader/arch/i386/bootinfo.c
new file mode 100644
index 0000000..cbd6e4e
--- /dev/null
+++ b/stand/efi/loader/arch/i386/bootinfo.c
@@ -0,0 +1,275 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/boot.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+#include "libi386.h"
+#include <machine/bootinfo.h>
+
+static const char howto_switches[] = "aCdrgDmphsv";
+static int howto_masks[] = {
+ RB_ASKNAME, RB_CDROM, RB_KDB, RB_DFLTROOT, RB_GDB, RB_MULTIPLE,
+ RB_MUTE, RB_PAUSE, RB_SERIAL, RB_SINGLE, RB_VERBOSE
+};
+
+int
+bi_getboothowto(char *kargs)
+{
+ const char *sw;
+ char *opts;
+ int howto, i;
+
+ howto = 0;
+
+ /* Get the boot options from the environment first. */
+ for (i = 0; howto_names[i].ev != NULL; i++) {
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+ }
+
+ /* Parse kargs */
+ if (kargs == NULL)
+ return (howto);
+
+ opts = strchr(kargs, '-');
+ while (opts != NULL) {
+ while (*(++opts) != '\0') {
+ sw = strchr(howto_switches, *opts);
+ if (sw == NULL)
+ break;
+ howto |= howto_masks[sw - howto_switches];
+ }
+ opts = strchr(opts, '-');
+ }
+
+ return (howto);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+vm_offset_t
+bi_copyenv(vm_offset_t start)
+{
+ struct env_var *ep;
+ vm_offset_t addr, last;
+ size_t len;
+
+ addr = last = start;
+
+ /* Traverse the environment. */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ len = strlen(ep->ev_name);
+ if (i386_copyin(ep->ev_name, addr, len) != len)
+ break;
+ addr += len;
+ if (i386_copyin("=", addr, 1) != 1)
+ break;
+ addr++;
+ if (ep->ev_value != NULL) {
+ len = strlen(ep->ev_value);
+ if (i386_copyin(ep->ev_value, addr, len) != len)
+ break;
+ addr += len;
+ }
+ if (i386_copyin("", addr, 1) != 1)
+ break;
+ last = ++addr;
+ }
+
+ if (i386_copyin("", last++, 1) != 1)
+ last = start;
+ return(last);
+}
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a) { \
+ u_int32_t x = (v); \
+ i386_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s) { \
+ COPY32(t, a); \
+ COPY32(strlen(s) + 1, a); \
+ i386_copyin(s, a, strlen(s) + 1); \
+ a += roundup(strlen(s) + 1, sizeof(u_int64_t));\
+}
+
+#define MOD_NAME(a, s) MOD_STR(MODINFO_NAME, a, s)
+#define MOD_TYPE(a, s) MOD_STR(MODINFO_TYPE, a, s)
+#define MOD_ARGS(a, s) MOD_STR(MODINFO_ARGS, a, s)
+
+#define MOD_VAR(t, a, s) { \
+ COPY32(t, a); \
+ COPY32(sizeof(s), a); \
+ i386_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(u_int64_t)); \
+}
+
+#define MOD_ADDR(a, s) MOD_VAR(MODINFO_ADDR, a, s)
+#define MOD_SIZE(a, s) MOD_VAR(MODINFO_SIZE, a, s)
+
+#define MOD_METADATA(a, mm) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a); \
+ COPY32(mm->md_size, a); \
+ i386_copyin(mm->md_data, a, mm->md_size); \
+ a += roundup(mm->md_size, sizeof(u_int64_t));\
+}
+
+#define MOD_END(a) { \
+ COPY32(MODINFO_END, a); \
+ COPY32(0, a); \
+}
+
+vm_offset_t
+bi_copymodules(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+
+ /* Start with the first module on the list, should be the kernel. */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+ /* The name field must come first. */
+ MOD_NAME(addr, fp->f_name);
+ MOD_TYPE(addr, fp->f_type);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args);
+ MOD_ADDR(addr, fp->f_addr);
+ MOD_SIZE(addr, fp->f_size);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next) {
+ if (!(md->md_type & MODINFOMD_NOCOPY))
+ MOD_METADATA(addr, md);
+ }
+ }
+ MOD_END(addr);
+ return(addr);
+}
+
+/*
+ * Load the information expected by the kernel.
+ *
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+bi_load(struct preloaded_file *fp, uint64_t *bi_addr)
+{
+ struct bootinfo bi;
+ struct preloaded_file *xp;
+ struct file_metadata *md;
+ struct devdesc *rootdev;
+ char *rootdevname;
+ vm_offset_t addr, ssym, esym;
+
+ bzero(&bi, sizeof(struct bootinfo));
+ bi.bi_version = 1;
+// bi.bi_boothowto = bi_getboothowto(fp->f_args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied
+ * device. This should perhaps go to MI code and/or have $rootdev
+ * tested/set by MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ i386_getdev((void**)&rootdev, rootdevname, NULL);
+ if (rootdev != NULL) {
+ /* Try reading /etc/fstab to select the root device. */
+ getrootmount(i386_fmtdev(rootdev));
+ free(rootdev);
+ }
+
+ md = file_findmetadata(fp, MODINFOMD_SSYM);
+ ssym = (md != NULL) ? *((vm_offset_t *)&(md->md_data)) : 0;
+ md = file_findmetadata(fp, MODINFOMD_ESYM);
+ esym = (md != NULL) ? *((vm_offset_t *)&(md->md_data)) : 0;
+ if (ssym != 0 && esym != 0) {
+ bi.bi_symtab = ssym;
+ bi.bi_esymtab = esym;
+ }
+
+ /* Find the last module in the chain. */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+
+ addr = (addr + 15) & ~15;
+
+ /* Copy module list and metadata. */
+ bi.bi_modulep = addr;
+ addr = bi_copymodules(addr);
+ if (addr <= bi.bi_modulep) {
+ addr = bi.bi_modulep;
+ bi.bi_modulep = 0;
+ }
+
+ addr = (addr + 15) & ~15;
+
+ /* Copy our environment. */
+ bi.bi_envp = addr;
+ addr = bi_copyenv(addr);
+ if (addr <= bi.bi_envp) {
+ addr = bi.bi_envp;
+ bi.bi_envp = 0;
+ }
+
+ addr = (addr + PAGE_MASK) & ~PAGE_MASK;
+ bi.bi_kernend = addr;
+
+ return (ldr_bootinfo(&bi, bi_addr));
+}
diff --git a/stand/efi/loader/arch/i386/efimd.c b/stand/efi/loader/arch/i386/efimd.c
new file mode 100644
index 0000000..8e1d850
--- /dev/null
+++ b/stand/efi/loader/arch/i386/efimd.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2004, 2006 Marcel Moolenaar
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <libi386.h>
+#include <machine/bootinfo.h>
+
+#define EFI_INTEL_FPSWA \
+ {0xc41b6531,0x97b9,0x11d3,{0x9a,0x29,0x00,0x90,0x27,0x3f,0xc1,0x4d}}
+
+static EFI_GUID fpswa_guid = EFI_INTEL_FPSWA;
+
+/* DIG64 Headless Console & Debug Port Table. */
+#define HCDP_TABLE_GUID \
+ {0xf951938d,0x620b,0x42ef,{0x82,0x79,0xa8,0x4b,0x79,0x61,0x78,0x98}}
+
+static EFI_GUID hcdp_guid = HCDP_TABLE_GUID;
+
+static UINTN mapkey;
+
+int ldr_bootinfo(struct bootinfo *, uint64_t *);
+int ldr_enter(const char *);
+
+static uint64_t
+ldr_alloc(vm_offset_t va)
+{
+
+ return (0);
+}
+
+int
+ldr_bootinfo(struct bootinfo *bi, uint64_t *bi_addr)
+{
+ VOID *fpswa;
+ EFI_MEMORY_DESCRIPTOR *mm;
+ EFI_PHYSICAL_ADDRESS addr;
+ EFI_HANDLE handle;
+ EFI_STATUS status;
+ size_t bisz;
+ UINTN mmsz, pages, sz;
+ UINT32 mmver;
+
+ bi->bi_systab = (uint64_t)ST;
+ bi->bi_hcdp = (uint64_t)efi_get_table(&hcdp_guid);
+
+ sz = sizeof(EFI_HANDLE);
+ status = BS->LocateHandle(ByProtocol, &fpswa_guid, 0, &sz, &handle);
+ if (status == 0)
+ status = BS->HandleProtocol(handle, &fpswa_guid, &fpswa);
+ bi->bi_fpswa = (status == 0) ? (uint64_t)fpswa : 0;
+
+ bisz = (sizeof(struct bootinfo) + 0x0f) & ~0x0f;
+
+ /*
+ * Allocate enough pages to hold the bootinfo block and the memory
+ * map EFI will return to us. The memory map has an unknown size,
+ * so we have to determine that first. Note that the AllocatePages
+ * call can itself modify the memory map, so we have to take that
+ * into account as well. The changes to the memory map are caused
+ * by splitting a range of free memory into two (AFAICT), so that
+ * one is marked as being loader data.
+ */
+ sz = 0;
+ BS->GetMemoryMap(&sz, NULL, &mapkey, &mmsz, &mmver);
+ sz += mmsz;
+ sz = (sz + 15) & ~15;
+ pages = EFI_SIZE_TO_PAGES(sz + bisz);
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, pages,
+ &addr);
+ if (EFI_ERROR(status)) {
+ printf("%s: AllocatePages() returned 0x%lx\n", __func__,
+ (long)status);
+ return (ENOMEM);
+ }
+
+ /*
+ * Read the memory map and stash it after bootinfo. Align the
+ * memory map on a 16-byte boundary (the bootinfo block is page
+ * aligned).
+ */
+ *bi_addr = addr;
+ mm = (void *)(addr + bisz);
+ sz = (EFI_PAGE_SIZE * pages) - bisz;
+ status = BS->GetMemoryMap(&sz, mm, &mapkey, &mmsz, &mmver);
+ if (EFI_ERROR(status)) {
+ printf("%s: GetMemoryMap() returned 0x%lx\n", __func__,
+ (long)status);
+ return (EINVAL);
+ }
+ bi->bi_memmap = (uint64_t)mm;
+ bi->bi_memmap_size = sz;
+ bi->bi_memdesc_size = mmsz;
+ bi->bi_memdesc_version = mmver;
+
+ bcopy(bi, (void *)(*bi_addr), sizeof(*bi));
+ return (0);
+}
+
+int
+ldr_enter(const char *kernel)
+{
+ EFI_STATUS status;
+
+ status = BS->ExitBootServices(IH, mapkey);
+ if (EFI_ERROR(status)) {
+ printf("%s: ExitBootServices() returned 0x%lx\n", __func__,
+ (long)status);
+ return (EINVAL);
+ }
+
+ return (0);
+}
diff --git a/stand/efi/loader/arch/i386/elf32_freebsd.c b/stand/efi/loader/arch/i386/elf32_freebsd.c
new file mode 100644
index 0000000..d3c4261
--- /dev/null
+++ b/stand/efi/loader/arch/i386/elf32_freebsd.c
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <string.h>
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <stand.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+#include "../libi386/libi386.h"
+#include "../btx/lib/btxv86.h"
+
+extern void __exec(caddr_t addr, ...);
+extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp);
+extern int ldr_enter(const char *kernel);
+
+static int elf32_exec(struct preloaded_file *amp);
+static int elf32_obj_exec(struct preloaded_file *amp);
+
+struct file_format i386_elf = { elf32_loadfile, elf32_exec };
+struct file_format i386_elf_obj = { elf32_obj_loadfile, elf32_obj_exec };
+
+struct file_format *file_formats[] = {
+ &i386_elf,
+ &i386_elf_obj,
+ NULL
+};
+
+/*
+ * There is an ELF kernel and one or more ELF modules loaded.
+ * We wish to start executing the kernel image, so make such
+ * preparations as are required, and do so.
+ */
+static int
+elf32_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *md;
+ Elf_Ehdr *ehdr;
+ vm_offset_t entry, bootinfop, modulep, kernend;
+ int boothowto, err, bootdev;
+
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return(EFTYPE);
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+
+ efi_time_fini();
+ err = bi_load(fp->f_args, &modulep, &kernend);
+ if (err != 0) {
+ efi_time_init();
+ return(err);
+ }
+ entry = ehdr->e_entry & 0xffffff;
+
+ printf("Start @ 0x%x ...\n", entry);
+
+ ldr_enter(fp->f_name);
+
+ dev_cleanup();
+ __exec((void *)entry, boothowto, bootdev, 0, 0, 0, bootinfop, modulep, kernend);
+
+ panic("exec returned");
+}
+
+static int
+elf32_obj_exec(struct preloaded_file *fp)
+{
+ return (EFTYPE);
+}
diff --git a/stand/efi/loader/arch/i386/exec.c b/stand/efi/loader/arch/i386/exec.c
new file mode 100644
index 0000000..579f559
--- /dev/null
+++ b/stand/efi/loader/arch/i386/exec.c
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <machine/elf.h>
+#include "../btx/lib/btxv86.h"
+
+#include "../../common/bootstrap.h"
+
+uint32_t __base;
+struct __v86 __v86;
+
+void
+__v86int()
+{
+ printf("%s\n", __func__);
+ exit(1);
+}
+
+void
+__exec(caddr_t addr, ...)
+{
+}
diff --git a/stand/efi/loader/arch/i386/i386_copy.c b/stand/efi/loader/arch/i386/i386_copy.c
new file mode 100644
index 0000000..522913f
--- /dev/null
+++ b/stand/efi/loader/arch/i386/i386_copy.c
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * MD primitives supporting placement of module data
+ *
+ * XXX should check load address/size against memory top.
+ */
+#include <stand.h>
+
+#include "libi386.h"
+#include "btxv86.h"
+
+ssize_t
+i386_copyin(const void *src, vm_offset_t dest, const size_t len)
+{
+ bcopy(src, PTOV(dest), len);
+ return(len);
+}
+
+ssize_t
+i386_copyout(const vm_offset_t src, void *dest, const size_t len)
+{
+ bcopy(PTOV(src), dest, len);
+ return(len);
+}
+
+
+ssize_t
+i386_readin(const int fd, vm_offset_t dest, const size_t len)
+{
+ return (read(fd, PTOV(dest), len));
+}
diff --git a/stand/efi/loader/arch/i386/ldscript.i386 b/stand/efi/loader/arch/i386/ldscript.i386
new file mode 100644
index 0000000..e17212a
--- /dev/null
+++ b/stand/efi/loader/arch/i386/ldscript.i386
@@ -0,0 +1,77 @@
+/* $FreeBSD$ */
+OUTPUT_FORMAT("elf32-i386-freebsd", "elf32-i386-freebsd", "elf32-i386-freebsd")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0;
+ ImageBase = .;
+ . = SIZEOF_HEADERS;
+ . = ALIGN(4096);
+ .text : {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.plt)
+ } =0xCCCCCCCC
+ . = ALIGN(4096);
+ .data : {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ *(.rodata1)
+ *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+ *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
+ *(.opd)
+ *(.data .data.* .gnu.linkonce.d.*)
+ *(.data1)
+ *(.plabel)
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ }
+ . = ALIGN(4096);
+ set_Xcommand_set : {
+ __start_set_Xcommand_set = .;
+ *(set_Xcommand_set)
+ __stop_set_Xcommand_set = .;
+ }
+ set_Xficl_compile_set : {
+ __start_set_Xficl_compile_set = .;
+ *(set_Xficl_compile_set)
+ __stop_set_Xficl_compile_set = .;
+ }
+ . = ALIGN(4096);
+ __gp = .;
+ .sdata : {
+ *(.got.plt .got)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ *(dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ }
+ . = ALIGN(4096);
+ .dynamic : { *(.dynamic) }
+ . = ALIGN(4096);
+ .rel.dyn : {
+ *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+ *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+ *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+ *(.rel.got)
+ *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*)
+ *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*)
+ *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*)
+ *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*)
+ *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+ *(.rel.plt)
+ *(.relset_*)
+ *(.rel.dyn .rel.dyn.*)
+ }
+ . = ALIGN(4096);
+ .reloc : { *(.reloc) }
+ . = ALIGN(4096);
+ .hash : { *(.hash) }
+ . = ALIGN(4096);
+ .dynsym : { *(.dynsym) }
+ . = ALIGN(4096);
+ .dynstr : { *(.dynstr) }
+}
diff --git a/stand/efi/loader/arch/i386/start.S b/stand/efi/loader/arch/i386/start.S
new file mode 100644
index 0000000..b597f41
--- /dev/null
+++ b/stand/efi/loader/arch/i386/start.S
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2008-2010 Rui Paulo <rpaulo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+ .text
+
+#include <machine/asm.h>
+
+#define EFI_SUCCESS 0
+
+/*
+ * EFI entry point.
+ * _start(EFI_IMAGE image_handle, EFI_SYSTEM_TABLE *system_table);
+ *
+ * We calculate the base address along with _DYNAMIC, relocate us and finally
+ * pass control to efi_main.
+ */
+
+ENTRY(_start)
+ pushl %ebp
+ movl %esp, %ebp
+
+ pushl 12(%ebp) /* image_handle */
+ pushl 8(%ebp) /* system_table */
+ call 0f
+0: popl %eax
+ movl %eax, %ebx
+ addl $ImageBase-0b, %eax
+ addl $_DYNAMIC-0b, %ebx
+ pushl %ebx /* dynamic */
+ pushl %eax /* ImageBase */
+ call self_reloc
+ popl %ebx /* remove ImageBase from the stack */
+ popl %ebx /* remove dynamic from the stack */
+ call efi_main
+1: leave
+ ret
+END(_start)
+
+ .data
+ .section .reloc, "a"
+ .long 0
+ .long 10
+ .word 0
diff --git a/stand/efi/loader/autoload.c b/stand/efi/loader/autoload.c
new file mode 100644
index 0000000..c1eb849
--- /dev/null
+++ b/stand/efi/loader/autoload.c
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "loader_efi.h"
+
+int
+efi_autoload(void)
+{
+
+ return (0);
+}
diff --git a/stand/efi/loader/bootinfo.c b/stand/efi/loader/bootinfo.c
new file mode 100644
index 0000000..ca06a61
--- /dev/null
+++ b/stand/efi/loader/bootinfo.c
@@ -0,0 +1,471 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2004, 2006 Marcel Moolenaar
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/boot.h>
+#include <machine/cpufunc.h>
+#include <machine/elf.h>
+#include <machine/metadata.h>
+#include <machine/psl.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "bootstrap.h"
+#include "loader_efi.h"
+
+#if defined(__amd64__)
+#include <machine/specialreg.h>
+#endif
+
+#include "framebuffer.h"
+
+#if defined(LOADER_FDT_SUPPORT)
+#include <fdt_platform.h>
+#endif
+
+int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp);
+
+extern EFI_SYSTEM_TABLE *ST;
+
+static const char howto_switches[] = "aCdrgDmphsv";
+static int howto_masks[] = {
+ RB_ASKNAME, RB_CDROM, RB_KDB, RB_DFLTROOT, RB_GDB, RB_MULTIPLE,
+ RB_MUTE, RB_PAUSE, RB_SERIAL, RB_SINGLE, RB_VERBOSE
+};
+
+static int
+bi_getboothowto(char *kargs)
+{
+ const char *sw;
+ char *opts;
+ char *console;
+ int howto, i;
+
+ howto = 0;
+
+ /* Get the boot options from the environment first. */
+ for (i = 0; howto_names[i].ev != NULL; i++) {
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+ }
+
+ console = getenv("console");
+ if (console != NULL) {
+ if (strcmp(console, "comconsole") == 0)
+ howto |= RB_SERIAL;
+ if (strcmp(console, "nullconsole") == 0)
+ howto |= RB_MUTE;
+ }
+
+ /* Parse kargs */
+ if (kargs == NULL)
+ return (howto);
+
+ opts = strchr(kargs, '-');
+ while (opts != NULL) {
+ while (*(++opts) != '\0') {
+ sw = strchr(howto_switches, *opts);
+ if (sw == NULL)
+ break;
+ howto |= howto_masks[sw - howto_switches];
+ }
+ opts = strchr(opts, '-');
+ }
+
+ return (howto);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+static vm_offset_t
+bi_copyenv(vm_offset_t start)
+{
+ struct env_var *ep;
+ vm_offset_t addr, last;
+ size_t len;
+
+ addr = last = start;
+
+ /* Traverse the environment. */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ len = strlen(ep->ev_name);
+ if ((size_t)archsw.arch_copyin(ep->ev_name, addr, len) != len)
+ break;
+ addr += len;
+ if (archsw.arch_copyin("=", addr, 1) != 1)
+ break;
+ addr++;
+ if (ep->ev_value != NULL) {
+ len = strlen(ep->ev_value);
+ if ((size_t)archsw.arch_copyin(ep->ev_value, addr, len) != len)
+ break;
+ addr += len;
+ }
+ if (archsw.arch_copyin("", addr, 1) != 1)
+ break;
+ last = ++addr;
+ }
+
+ if (archsw.arch_copyin("", last++, 1) != 1)
+ last = start;
+ return(last);
+}
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a, c) { \
+ uint32_t x = (v); \
+ if (c) \
+ archsw.arch_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c); \
+ if (c) \
+ archsw.arch_copyin(s, a, strlen(s) + 1); \
+ a += roundup(strlen(s) + 1, sizeof(u_long)); \
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ archsw.arch_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(u_long)); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c); \
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ archsw.arch_copyin(mm->md_data, a, mm->md_size); \
+ a += roundup(mm->md_size, sizeof(u_long)); \
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+static vm_offset_t
+bi_copymodules(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ int c;
+ uint64_t v;
+
+ c = addr != 0;
+ /* Start with the first module on the list, should be the kernel. */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+ MOD_NAME(addr, fp->f_name, c); /* This must come first. */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ v = fp->f_addr;
+#if defined(__arm__)
+ v -= __elfN(relocation_offset);
+#endif
+ MOD_ADDR(addr, v, c);
+ v = fp->f_size;
+ MOD_SIZE(addr, v, c);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next)
+ if (!(md->md_type & MODINFOMD_NOCOPY))
+ MOD_METADATA(addr, md, c);
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+static int
+bi_load_efi_data(struct preloaded_file *kfp)
+{
+ EFI_MEMORY_DESCRIPTOR *mm;
+ EFI_PHYSICAL_ADDRESS addr;
+ EFI_STATUS status;
+ size_t efisz;
+ UINTN efi_mapkey;
+ UINTN mmsz, pages, retry, sz;
+ UINT32 mmver;
+ struct efi_map_header *efihdr;
+
+#if defined(__amd64__) || defined(__aarch64__)
+ struct efi_fb efifb;
+
+ if (efi_find_framebuffer(&efifb) == 0) {
+ printf("EFI framebuffer information:\n");
+ printf("addr, size 0x%jx, 0x%jx\n", efifb.fb_addr,
+ efifb.fb_size);
+ printf("dimensions %d x %d\n", efifb.fb_width,
+ efifb.fb_height);
+ printf("stride %d\n", efifb.fb_stride);
+ printf("masks 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
+ efifb.fb_mask_red, efifb.fb_mask_green, efifb.fb_mask_blue,
+ efifb.fb_mask_reserved);
+
+ file_addmetadata(kfp, MODINFOMD_EFI_FB, sizeof(efifb), &efifb);
+ }
+#endif
+
+ efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
+
+ /*
+ * Assgin size of EFI_MEMORY_DESCRIPTOR to keep compatible with
+ * u-boot which doesn't fill this value when buffer for memory
+ * descriptors is too small (eg. 0 to obtain memory map size)
+ */
+ mmsz = sizeof(EFI_MEMORY_DESCRIPTOR);
+
+ /*
+ * It is possible that the first call to ExitBootServices may change
+ * the map key. Fetch a new map key and retry ExitBootServices in that
+ * case.
+ */
+ for (retry = 2; retry > 0; retry--) {
+ /*
+ * Allocate enough pages to hold the bootinfo block and the
+ * memory map EFI will return to us. The memory map has an
+ * unknown size, so we have to determine that first. Note that
+ * the AllocatePages call can itself modify the memory map, so
+ * we have to take that into account as well. The changes to
+ * the memory map are caused by splitting a range of free
+ * memory into two (AFAICT), so that one is marked as being
+ * loader data.
+ */
+ sz = 0;
+ BS->GetMemoryMap(&sz, NULL, &efi_mapkey, &mmsz, &mmver);
+ sz += mmsz;
+ sz = (sz + 0xf) & ~0xf;
+ pages = EFI_SIZE_TO_PAGES(sz + efisz);
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+ pages, &addr);
+ if (EFI_ERROR(status)) {
+ printf("%s: AllocatePages error %lu\n", __func__,
+ EFI_ERROR_CODE(status));
+ return (ENOMEM);
+ }
+
+ /*
+ * Read the memory map and stash it after bootinfo. Align the
+ * memory map on a 16-byte boundary (the bootinfo block is page
+ * aligned).
+ */
+ efihdr = (struct efi_map_header *)addr;
+ mm = (void *)((uint8_t *)efihdr + efisz);
+ sz = (EFI_PAGE_SIZE * pages) - efisz;
+
+ status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver);
+ if (EFI_ERROR(status)) {
+ printf("%s: GetMemoryMap error %lu\n", __func__,
+ EFI_ERROR_CODE(status));
+ return (EINVAL);
+ }
+ status = BS->ExitBootServices(IH, efi_mapkey);
+ if (EFI_ERROR(status) == 0) {
+ efihdr->memory_size = sz;
+ efihdr->descriptor_size = mmsz;
+ efihdr->descriptor_version = mmver;
+ file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz,
+ efihdr);
+ return (0);
+ }
+ BS->FreePages(addr, pages);
+ }
+ printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status));
+ return (EINVAL);
+}
+
+/*
+ * Load the information expected by an amd64 kernel.
+ *
+ * - The 'boothowto' argument is constructed.
+ * - The 'bootdev' argument is constructed.
+ * - The 'bootinfo' struct is constructed, and copied into the kernel space.
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp)
+{
+ struct preloaded_file *xp, *kfp;
+ struct devdesc *rootdev;
+ struct file_metadata *md;
+ vm_offset_t addr;
+ uint64_t kernend;
+ uint64_t envp;
+ vm_offset_t size;
+ char *rootdevname;
+ int howto;
+#if defined(LOADER_FDT_SUPPORT)
+ vm_offset_t dtbp;
+ int dtb_size;
+#endif
+#if defined(__arm__)
+ vm_offset_t vaddr;
+ size_t i;
+ /*
+ * These metadata addreses must be converted for kernel after
+ * relocation.
+ */
+ uint32_t mdt[] = {
+ MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND,
+ MODINFOMD_ENVP,
+#if defined(LOADER_FDT_SUPPORT)
+ MODINFOMD_DTBP
+#endif
+ };
+#endif
+
+ howto = bi_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied
+ * device. This should perhaps go to MI code and/or have $rootdev
+ * tested/set by MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ archsw.arch_getdev((void**)(&rootdev), rootdevname, NULL);
+ if (rootdev == NULL) {
+ printf("Can't determine root device.\n");
+ return(EINVAL);
+ }
+
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(efi_fmtdev((void *)rootdev));
+
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+
+ /* Pad to a page boundary. */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* Copy our environment. */
+ envp = addr;
+ addr = bi_copyenv(addr);
+
+ /* Pad to a page boundary. */
+ addr = roundup(addr, PAGE_SIZE);
+
+#if defined(LOADER_FDT_SUPPORT)
+ /* Handle device tree blob */
+ dtbp = addr;
+ dtb_size = fdt_copy(addr);
+
+ /* Pad to a page boundary */
+ if (dtb_size)
+ addr += roundup(dtb_size, PAGE_SIZE);
+#endif
+
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf64 kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ kernend = 0; /* fill it in later */
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+#if defined(LOADER_FDT_SUPPORT)
+ if (dtb_size)
+ file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp);
+ else
+ printf("WARNING! Trying to fire up the kernel, but no "
+ "device tree blob found!\n");
+#endif
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST);
+
+ bi_load_efi_data(kfp);
+
+ /* Figure out the size and location of the metadata. */
+ *modulep = addr;
+ size = bi_copymodules(0);
+ kernend = roundup(addr + size, PAGE_SIZE);
+ *kernendp = kernend;
+
+ /* patch MODINFOMD_KERNEND */
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ bcopy(&kernend, md->md_data, sizeof kernend);
+
+#if defined(__arm__)
+ *modulep -= __elfN(relocation_offset);
+
+ /* Do relocation fixup on metadata of each module. */
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ for (i = 0; i < nitems(mdt); i++) {
+ md = file_findmetadata(xp, mdt[i]);
+ if (md) {
+ bcopy(md->md_data, &vaddr, sizeof vaddr);
+ vaddr -= __elfN(relocation_offset);
+ bcopy(&vaddr, md->md_data, sizeof vaddr);
+ }
+ }
+ }
+#endif
+
+ /* Copy module list and metadata. */
+ (void)bi_copymodules(addr);
+
+ return (0);
+}
diff --git a/stand/efi/loader/conf.c b/stand/efi/loader/conf.c
new file mode 100644
index 0000000..cea95b3
--- /dev/null
+++ b/stand/efi/loader/conf.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include <efi.h>
+#include <efilib.h>
+#ifdef EFI_ZFS_BOOT
+#include <libzfs.h>
+#endif
+
+struct devsw *devsw[] = {
+ &efipart_fddev,
+ &efipart_cddev,
+ &efipart_hddev,
+ &efinet_dev,
+#ifdef EFI_ZFS_BOOT
+ &zfs_dev,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#ifdef EFI_ZFS_BOOT
+ &zfs_fsops,
+#endif
+ &dosfs_fsops,
+ &ufs_fsops,
+ &cd9660_fsops,
+ &tftp_fsops,
+ &nfs_fsops,
+ &gzipfs_fsops,
+ &bzipfs_fsops,
+ NULL
+};
+
+struct netif_driver *netif_drivers[] = {
+ &efinetif,
+ NULL
+};
+
+extern struct console efi_console;
+#if defined(__amd64__) || defined(__i386__)
+extern struct console comconsole;
+extern struct console nullconsole;
+extern struct console spinconsole;
+#endif
+
+struct console *consoles[] = {
+ &efi_console,
+#if defined(__amd64__) || defined(__i386__)
+ &comconsole,
+ &nullconsole,
+ &spinconsole,
+#endif
+ NULL
+};
diff --git a/stand/efi/loader/copy.c b/stand/efi/loader/copy.c
new file mode 100644
index 0000000..efa42b9
--- /dev/null
+++ b/stand/efi/loader/copy.c
@@ -0,0 +1,287 @@
+/*-
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Benno Rice under sponsorship from
+ * the FreeBSD Foundation.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <stand.h>
+#include <bootstrap.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "loader_efi.h"
+
+#if defined(__i386__) || defined(__amd64__)
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+
+/*
+ * The code is excerpted from sys/x86/x86/identcpu.c: identify_cpu(),
+ * identify_hypervisor(), and dev/hyperv/vmbus/hyperv.c: hyperv_identify().
+ */
+#define CPUID_LEAF_HV_MAXLEAF 0x40000000
+#define CPUID_LEAF_HV_INTERFACE 0x40000001
+#define CPUID_LEAF_HV_FEATURES 0x40000003
+#define CPUID_LEAF_HV_LIMITS 0x40000005
+#define CPUID_HV_IFACE_HYPERV 0x31237648 /* HV#1 */
+#define CPUID_HV_MSR_TIME_REFCNT 0x0002 /* MSR_HV_TIME_REF_COUNT */
+#define CPUID_HV_MSR_HYPERCALL 0x0020
+
+static int
+running_on_hyperv(void)
+{
+ char hv_vendor[16];
+ uint32_t regs[4];
+
+ do_cpuid(1, regs);
+ if ((regs[2] & CPUID2_HV) == 0)
+ return (0);
+
+ do_cpuid(CPUID_LEAF_HV_MAXLEAF, regs);
+ if (regs[0] < CPUID_LEAF_HV_LIMITS)
+ return (0);
+
+ ((uint32_t *)&hv_vendor)[0] = regs[1];
+ ((uint32_t *)&hv_vendor)[1] = regs[2];
+ ((uint32_t *)&hv_vendor)[2] = regs[3];
+ hv_vendor[12] = '\0';
+ if (strcmp(hv_vendor, "Microsoft Hv") != 0)
+ return (0);
+
+ do_cpuid(CPUID_LEAF_HV_INTERFACE, regs);
+ if (regs[0] != CPUID_HV_IFACE_HYPERV)
+ return (0);
+
+ do_cpuid(CPUID_LEAF_HV_FEATURES, regs);
+ if ((regs[0] & CPUID_HV_MSR_HYPERCALL) == 0)
+ return (0);
+ if ((regs[0] & CPUID_HV_MSR_TIME_REFCNT) == 0)
+ return (0);
+
+ return (1);
+}
+
+#define KERNEL_PHYSICAL_BASE (2*1024*1024)
+
+static void
+efi_verify_staging_size(unsigned long *nr_pages)
+{
+ UINTN sz;
+ EFI_MEMORY_DESCRIPTOR *map, *p;
+ EFI_PHYSICAL_ADDRESS start, end;
+ UINTN key, dsz;
+ UINT32 dver;
+ EFI_STATUS status;
+ int i, ndesc;
+ unsigned long available_pages = 0;
+
+ sz = 0;
+ status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
+ if (status != EFI_BUFFER_TOO_SMALL) {
+ printf("Can't determine memory map size\n");
+ return;
+ }
+
+ map = malloc(sz);
+ status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
+ if (EFI_ERROR(status)) {
+ printf("Can't read memory map\n");
+ goto out;
+ }
+
+ ndesc = sz / dsz;
+ for (i = 0, p = map; i < ndesc;
+ i++, p = NextMemoryDescriptor(p, dsz)) {
+ start = p->PhysicalStart;
+ end = start + p->NumberOfPages * EFI_PAGE_SIZE;
+
+ if (KERNEL_PHYSICAL_BASE < start ||
+ KERNEL_PHYSICAL_BASE >= end)
+ continue;
+
+ available_pages = p->NumberOfPages -
+ ((KERNEL_PHYSICAL_BASE - start) >> EFI_PAGE_SHIFT);
+ break;
+ }
+
+ if (available_pages == 0) {
+ printf("Can't find valid memory map for staging area!\n");
+ goto out;
+ }
+
+ i++;
+ p = NextMemoryDescriptor(p, dsz);
+
+ for ( ; i < ndesc;
+ i++, p = NextMemoryDescriptor(p, dsz)) {
+ if (p->Type != EfiConventionalMemory &&
+ p->Type != EfiLoaderData)
+ break;
+
+ if (p->PhysicalStart != end)
+ break;
+
+ end = p->PhysicalStart + p->NumberOfPages * EFI_PAGE_SIZE;
+
+ available_pages += p->NumberOfPages;
+ }
+
+ if (*nr_pages > available_pages) {
+ printf("Staging area's size is reduced: %ld -> %ld!\n",
+ *nr_pages, available_pages);
+ *nr_pages = available_pages;
+ }
+out:
+ free(map);
+}
+#endif /* __i386__ || __amd64__ */
+
+#ifndef EFI_STAGING_SIZE
+#define EFI_STAGING_SIZE 64
+#endif
+
+EFI_PHYSICAL_ADDRESS staging, staging_end;
+int stage_offset_set = 0;
+ssize_t stage_offset;
+
+int
+efi_copy_init(void)
+{
+ EFI_STATUS status;
+
+ unsigned long nr_pages;
+
+ nr_pages = EFI_SIZE_TO_PAGES((EFI_STAGING_SIZE) * 1024 * 1024);
+
+#if defined(__i386__) || defined(__amd64__)
+ /*
+ * We'll decrease nr_pages, if it's too big. Currently we only
+ * apply this to FreeBSD VM running on Hyper-V. Why? Please see
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=211746#c28
+ */
+ if (running_on_hyperv())
+ efi_verify_staging_size(&nr_pages);
+
+ /*
+ * The staging area must reside in the the first 1GB physical
+ * memory: see elf64_exec() in
+ * boot/efi/loader/arch/amd64/elf64_freebsd.c.
+ */
+ staging = 1024*1024*1024;
+ status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
+ nr_pages, &staging);
+#else
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+ nr_pages, &staging);
+#endif
+ if (EFI_ERROR(status)) {
+ printf("failed to allocate staging area: %lu\n",
+ EFI_ERROR_CODE(status));
+ return (status);
+ }
+ staging_end = staging + nr_pages * EFI_PAGE_SIZE;
+
+#if defined(__aarch64__) || defined(__arm__)
+ /*
+ * Round the kernel load address to a 2MiB value. This is needed
+ * because the kernel builds a page table based on where it has
+ * been loaded in physical address space. As the kernel will use
+ * either a 1MiB or 2MiB page for this we need to make sure it
+ * is correctly aligned for both cases.
+ */
+ staging = roundup2(staging, 2 * 1024 * 1024);
+#endif
+
+ return (0);
+}
+
+void *
+efi_translate(vm_offset_t ptr)
+{
+
+ return ((void *)(ptr + stage_offset));
+}
+
+ssize_t
+efi_copyin(const void *src, vm_offset_t dest, const size_t len)
+{
+
+ if (!stage_offset_set) {
+ stage_offset = (vm_offset_t)staging - dest;
+ stage_offset_set = 1;
+ }
+
+ /* XXX: Callers do not check for failure. */
+ if (dest + stage_offset + len > staging_end) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ bcopy(src, (void *)(dest + stage_offset), len);
+ return (len);
+}
+
+ssize_t
+efi_copyout(const vm_offset_t src, void *dest, const size_t len)
+{
+
+ /* XXX: Callers do not check for failure. */
+ if (src + stage_offset + len > staging_end) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ bcopy((void *)(src + stage_offset), dest, len);
+ return (len);
+}
+
+
+ssize_t
+efi_readin(const int fd, vm_offset_t dest, const size_t len)
+{
+
+ if (dest + stage_offset + len > staging_end) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ return (read(fd, (void *)(dest + stage_offset), len));
+}
+
+void
+efi_copy_finish(void)
+{
+ uint64_t *src, *dst, *last;
+
+ src = (uint64_t *)staging;
+ dst = (uint64_t *)(staging - stage_offset);
+ last = (uint64_t *)staging_end;
+
+ while (src < last)
+ *dst++ = *src++;
+}
diff --git a/stand/efi/loader/efi_main.c b/stand/efi/loader/efi_main.c
new file mode 100644
index 0000000..e424d89
--- /dev/null
+++ b/stand/efi/loader/efi_main.c
@@ -0,0 +1,188 @@
+/*-
+ * Copyright (c) 2000 Doug Rabson
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <efi.h>
+#include <eficonsctl.h>
+#include <efilib.h>
+#include <stand.h>
+
+static EFI_PHYSICAL_ADDRESS heap;
+static UINTN heapsize;
+
+void
+efi_exit(EFI_STATUS exit_code)
+{
+
+ BS->FreePages(heap, EFI_SIZE_TO_PAGES(heapsize));
+ BS->Exit(IH, exit_code, 0, NULL);
+}
+
+void
+exit(int status)
+{
+
+ efi_exit(EFI_LOAD_ERROR);
+}
+
+static CHAR16 *
+arg_skipsep(CHAR16 *argp)
+{
+
+ while (*argp == ' ' || *argp == '\t' || *argp == '\n')
+ argp++;
+ return (argp);
+}
+
+static CHAR16 *
+arg_skipword(CHAR16 *argp)
+{
+
+ while (*argp && *argp != ' ' && *argp != '\t' && *argp != '\n')
+ argp++;
+ return (argp);
+}
+
+EFI_STATUS
+efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
+{
+ static EFI_GUID image_protocol = LOADED_IMAGE_PROTOCOL;
+ static EFI_GUID console_control_protocol =
+ EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
+ EFI_CONSOLE_CONTROL_PROTOCOL *console_control = NULL;
+ EFI_LOADED_IMAGE *img;
+ CHAR16 *argp, *args, **argv;
+ EFI_STATUS status;
+ int argc, addprog;
+
+ IH = image_handle;
+ ST = system_table;
+ BS = ST->BootServices;
+ RS = ST->RuntimeServices;
+
+ status = BS->LocateProtocol(&console_control_protocol, NULL,
+ (VOID **)&console_control);
+ if (status == EFI_SUCCESS)
+ (void)console_control->SetMode(console_control,
+ EfiConsoleControlScreenText);
+
+ heapsize = 64 * 1024 * 1024;
+ status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+ EFI_SIZE_TO_PAGES(heapsize), &heap);
+ if (status != EFI_SUCCESS)
+ BS->Exit(IH, status, 0, NULL);
+
+ setheap((void *)(uintptr_t)heap, (void *)(uintptr_t)(heap + heapsize));
+
+ /* Use efi_exit() from here on... */
+
+ status = BS->HandleProtocol(IH, &image_protocol, (VOID**)&img);
+ if (status != EFI_SUCCESS)
+ efi_exit(status);
+
+ /*
+ * Pre-process the (optional) load options. If the option string
+ * is given as an ASCII string, we use a poor man's ASCII to
+ * Unicode-16 translation. The size of the option string as given
+ * to us includes the terminating null character. We assume the
+ * string is an ASCII string if strlen() plus the terminating
+ * '\0' is less than LoadOptionsSize. Even if all Unicode-16
+ * characters have the upper 8 bits non-zero, the terminating
+ * null character will cause a one-off.
+ * If the string is already in Unicode-16, we make a copy so that
+ * we know we can always modify the string.
+ */
+ if (img->LoadOptionsSize > 0 && img->LoadOptions != NULL) {
+ if (img->LoadOptionsSize == strlen(img->LoadOptions) + 1) {
+ args = malloc(img->LoadOptionsSize << 1);
+ for (argc = 0; argc < (int)img->LoadOptionsSize; argc++)
+ args[argc] = ((char*)img->LoadOptions)[argc];
+ } else {
+ args = malloc(img->LoadOptionsSize);
+ memcpy(args, img->LoadOptions, img->LoadOptionsSize);
+ }
+ } else
+ args = NULL;
+
+ /*
+ * Use a quick and dirty algorithm to build the argv vector. We
+ * first count the number of words. Then, after allocating the
+ * vector, we split the string up. We don't deal with quotes or
+ * other more advanced shell features.
+ * The EFI shell will pass the name of the image as the first
+ * word in the argument list. This does not happen if we're
+ * loaded by the boot manager. This is not so easy to figure
+ * out though. The ParentHandle is not always NULL, because
+ * there can be a function (=image) that will perform the task
+ * for the boot manager.
+ */
+ /* Part 1: Figure out if we need to add our program name. */
+ addprog = (args == NULL || img->ParentHandle == NULL ||
+ img->FilePath == NULL) ? 1 : 0;
+ if (!addprog) {
+ addprog =
+ (DevicePathType(img->FilePath) != MEDIA_DEVICE_PATH ||
+ DevicePathSubType(img->FilePath) != MEDIA_FILEPATH_DP ||
+ DevicePathNodeLength(img->FilePath) <=
+ sizeof(FILEPATH_DEVICE_PATH)) ? 1 : 0;
+ if (!addprog) {
+ /* XXX todo. */
+ }
+ }
+ /* Part 2: count words. */
+ argc = (addprog) ? 1 : 0;
+ argp = args;
+ while (argp != NULL && *argp != 0) {
+ argp = arg_skipsep(argp);
+ if (*argp == 0)
+ break;
+ argc++;
+ argp = arg_skipword(argp);
+ }
+ /* Part 3: build vector. */
+ argv = malloc((argc + 1) * sizeof(CHAR16*));
+ argc = 0;
+ if (addprog)
+ argv[argc++] = (CHAR16 *)L"loader.efi";
+ argp = args;
+ while (argp != NULL && *argp != 0) {
+ argp = arg_skipsep(argp);
+ if (*argp == 0)
+ break;
+ argv[argc++] = argp;
+ argp = arg_skipword(argp);
+ /* Terminate the words. */
+ if (*argp != 0)
+ *argp++ = 0;
+ }
+ argv[argc] = NULL;
+
+ status = main(argc, argv);
+ efi_exit(status);
+ return (status);
+}
diff --git a/stand/efi/loader/framebuffer.c b/stand/efi/loader/framebuffer.c
new file mode 100644
index 0000000..37999ea
--- /dev/null
+++ b/stand/efi/loader/framebuffer.c
@@ -0,0 +1,568 @@
+/*-
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Benno Rice under sponsorship from
+ * the FreeBSD Foundation.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <bootstrap.h>
+#include <sys/endian.h>
+#include <stand.h>
+
+#include <efi.h>
+#include <efilib.h>
+#include <efiuga.h>
+#include <efipciio.h>
+#include <machine/metadata.h>
+
+#include "framebuffer.h"
+
+static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID;
+static EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID;
+
+static u_int
+efifb_color_depth(struct efi_fb *efifb)
+{
+ uint32_t mask;
+ u_int depth;
+
+ mask = efifb->fb_mask_red | efifb->fb_mask_green |
+ efifb->fb_mask_blue | efifb->fb_mask_reserved;
+ if (mask == 0)
+ return (0);
+ for (depth = 1; mask != 1; depth++)
+ mask >>= 1;
+ return (depth);
+}
+
+static int
+efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt,
+ EFI_PIXEL_BITMASK *pixinfo)
+{
+ int result;
+
+ result = 0;
+ switch (pixfmt) {
+ case PixelRedGreenBlueReserved8BitPerColor:
+ efifb->fb_mask_red = 0x000000ff;
+ efifb->fb_mask_green = 0x0000ff00;
+ efifb->fb_mask_blue = 0x00ff0000;
+ efifb->fb_mask_reserved = 0xff000000;
+ break;
+ case PixelBlueGreenRedReserved8BitPerColor:
+ efifb->fb_mask_red = 0x00ff0000;
+ efifb->fb_mask_green = 0x0000ff00;
+ efifb->fb_mask_blue = 0x000000ff;
+ efifb->fb_mask_reserved = 0xff000000;
+ break;
+ case PixelBitMask:
+ efifb->fb_mask_red = pixinfo->RedMask;
+ efifb->fb_mask_green = pixinfo->GreenMask;
+ efifb->fb_mask_blue = pixinfo->BlueMask;
+ efifb->fb_mask_reserved = pixinfo->ReservedMask;
+ break;
+ default:
+ result = 1;
+ break;
+ }
+ return (result);
+}
+
+static int
+efifb_from_gop(struct efi_fb *efifb, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode,
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
+{
+ int result;
+
+ efifb->fb_addr = mode->FrameBufferBase;
+ efifb->fb_size = mode->FrameBufferSize;
+ efifb->fb_height = info->VerticalResolution;
+ efifb->fb_width = info->HorizontalResolution;
+ efifb->fb_stride = info->PixelsPerScanLine;
+ result = efifb_mask_from_pixfmt(efifb, info->PixelFormat,
+ &info->PixelInformation);
+ return (result);
+}
+
+static ssize_t
+efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL *uga, u_int line,
+ EFI_PCI_IO_PROTOCOL *pciio, uint64_t addr, uint64_t size)
+{
+ EFI_UGA_PIXEL pix0, pix1;
+ uint8_t *data1, *data2;
+ size_t count, maxcount = 1024;
+ ssize_t ofs;
+ EFI_STATUS status;
+ u_int idx;
+
+ status = uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer,
+ 0, line, 0, 0, 1, 1, 0);
+ if (EFI_ERROR(status)) {
+ printf("UGA BLT operation failed (video->buffer)");
+ return (-1);
+ }
+ pix1.Red = ~pix0.Red;
+ pix1.Green = ~pix0.Green;
+ pix1.Blue = ~pix0.Blue;
+ pix1.Reserved = 0;
+
+ data1 = calloc(maxcount, 2);
+ if (data1 == NULL) {
+ printf("Unable to allocate memory");
+ return (-1);
+ }
+ data2 = data1 + maxcount;
+
+ ofs = 0;
+ while (size > 0) {
+ count = min(size, maxcount);
+
+ status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32,
+ EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2,
+ data1);
+ if (EFI_ERROR(status)) {
+ printf("Error reading frame buffer (before)");
+ goto fail;
+ }
+ status = uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo,
+ 0, 0, 0, line, 1, 1, 0);
+ if (EFI_ERROR(status)) {
+ printf("UGA BLT operation failed (modify)");
+ goto fail;
+ }
+ status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32,
+ EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2,
+ data2);
+ if (EFI_ERROR(status)) {
+ printf("Error reading frame buffer (after)");
+ goto fail;
+ }
+ status = uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo,
+ 0, 0, 0, line, 1, 1, 0);
+ if (EFI_ERROR(status)) {
+ printf("UGA BLT operation failed (restore)");
+ goto fail;
+ }
+ for (idx = 0; idx < count; idx++) {
+ if (data1[idx] != data2[idx]) {
+ free(data1);
+ return (ofs + (idx & ~3));
+ }
+ }
+ ofs += count;
+ size -= count;
+ }
+ printf("No change detected in frame buffer");
+
+ fail:
+ printf(" -- error %lu\n", EFI_ERROR_CODE(status));
+ free(data1);
+ return (-1);
+}
+
+static EFI_PCI_IO_PROTOCOL *
+efifb_uga_get_pciio(void)
+{
+ EFI_PCI_IO_PROTOCOL *pciio;
+ EFI_HANDLE *buf, *hp;
+ EFI_STATUS status;
+ UINTN bufsz;
+
+ /* Get all handles that support the UGA protocol. */
+ bufsz = 0;
+ status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, NULL);
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return (NULL);
+ buf = malloc(bufsz);
+ status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, buf);
+ if (status != EFI_SUCCESS) {
+ free(buf);
+ return (NULL);
+ }
+ bufsz /= sizeof(EFI_HANDLE);
+
+ /* Get the PCI I/O interface of the first handle that supports it. */
+ pciio = NULL;
+ for (hp = buf; hp < buf + bufsz; hp++) {
+ status = BS->HandleProtocol(*hp, &pciio_guid, (void **)&pciio);
+ if (status == EFI_SUCCESS) {
+ free(buf);
+ return (pciio);
+ }
+ }
+ free(buf);
+ return (NULL);
+}
+
+static EFI_STATUS
+efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL *pciio, uint64_t *addrp,
+ uint64_t *sizep)
+{
+ uint8_t *resattr;
+ uint64_t addr, size;
+ EFI_STATUS status;
+ u_int bar;
+
+ if (pciio == NULL)
+ return (EFI_DEVICE_ERROR);
+
+ /* Attempt to get the frame buffer address (imprecise). */
+ *addrp = 0;
+ *sizep = 0;
+ for (bar = 0; bar < 6; bar++) {
+ status = pciio->GetBarAttributes(pciio, bar, NULL,
+ (void **)&resattr);
+ if (status != EFI_SUCCESS)
+ continue;
+ /* XXX magic offsets and constants. */
+ if (resattr[0] == 0x87 && resattr[3] == 0) {
+ /* 32-bit address space descriptor (MEMIO) */
+ addr = le32dec(resattr + 10);
+ size = le32dec(resattr + 22);
+ } else if (resattr[0] == 0x8a && resattr[3] == 0) {
+ /* 64-bit address space descriptor (MEMIO) */
+ addr = le64dec(resattr + 14);
+ size = le64dec(resattr + 38);
+ } else {
+ addr = 0;
+ size = 0;
+ }
+ BS->FreePool(resattr);
+ if (addr == 0 || size == 0)
+ continue;
+
+ /* We assume the largest BAR is the frame buffer. */
+ if (size > *sizep) {
+ *addrp = addr;
+ *sizep = size;
+ }
+ }
+ return ((*addrp == 0 || *sizep == 0) ? EFI_DEVICE_ERROR : 0);
+}
+
+static int
+efifb_from_uga(struct efi_fb *efifb, EFI_UGA_DRAW_PROTOCOL *uga)
+{
+ EFI_PCI_IO_PROTOCOL *pciio;
+ char *ev, *p;
+ EFI_STATUS status;
+ ssize_t offset;
+ uint64_t fbaddr;
+ uint32_t horiz, vert, stride;
+ uint32_t np, depth, refresh;
+
+ status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh);
+ if (EFI_ERROR(status))
+ return (1);
+ efifb->fb_height = vert;
+ efifb->fb_width = horiz;
+ /* Paranoia... */
+ if (efifb->fb_height == 0 || efifb->fb_width == 0)
+ return (1);
+
+ /* The color masks are fixed AFAICT. */
+ efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor,
+ NULL);
+
+ /* pciio can be NULL on return! */
+ pciio = efifb_uga_get_pciio();
+
+ /* Try to find the frame buffer. */
+ status = efifb_uga_locate_framebuffer(pciio, &efifb->fb_addr,
+ &efifb->fb_size);
+ if (EFI_ERROR(status)) {
+ efifb->fb_addr = 0;
+ efifb->fb_size = 0;
+ }
+
+ /*
+ * There's no reliable way to detect the frame buffer or the
+ * offset within the frame buffer of the visible region, nor
+ * the stride. Our only option is to look at the system and
+ * fill in the blanks based on that. Luckily, UGA was mostly
+ * only used on Apple hardware.
+ */
+ offset = -1;
+ ev = getenv("smbios.system.maker");
+ if (ev != NULL && !strcmp(ev, "Apple Inc.")) {
+ ev = getenv("smbios.system.product");
+ if (ev != NULL && !strcmp(ev, "iMac7,1")) {
+ /* These are the expected values we should have. */
+ horiz = 1680;
+ vert = 1050;
+ fbaddr = 0xc0000000;
+ /* These are the missing bits. */
+ offset = 0x10000;
+ stride = 1728;
+ } else if (ev != NULL && !strcmp(ev, "MacBook3,1")) {
+ /* These are the expected values we should have. */
+ horiz = 1280;
+ vert = 800;
+ fbaddr = 0xc0000000;
+ /* These are the missing bits. */
+ offset = 0x0;
+ stride = 2048;
+ }
+ }
+
+ /*
+ * If this is hardware we know, make sure that it looks familiar
+ * before we accept our hardcoded values.
+ */
+ if (offset >= 0 && efifb->fb_width == horiz &&
+ efifb->fb_height == vert && efifb->fb_addr == fbaddr) {
+ efifb->fb_addr += offset;
+ efifb->fb_size -= offset;
+ efifb->fb_stride = stride;
+ return (0);
+ } else if (offset >= 0) {
+ printf("Hardware make/model known, but graphics not "
+ "as expected.\n");
+ printf("Console may not work!\n");
+ }
+
+ /*
+ * The stride is equal or larger to the width. Often it's the
+ * next larger power of two. We'll start with that...
+ */
+ efifb->fb_stride = efifb->fb_width;
+ do {
+ np = efifb->fb_stride & (efifb->fb_stride - 1);
+ if (np) {
+ efifb->fb_stride |= (np - 1);
+ efifb->fb_stride++;
+ }
+ } while (np);
+
+ ev = getenv("hw.efifb.address");
+ if (ev == NULL) {
+ if (efifb->fb_addr == 0) {
+ printf("Please set hw.efifb.address and "
+ "hw.efifb.stride.\n");
+ return (1);
+ }
+
+ /*
+ * The visible part of the frame buffer may not start at
+ * offset 0, so try to detect it. Note that we may not
+ * always be able to read from the frame buffer, which
+ * means that we may not be able to detect anything. In
+ * that case, we would take a long time scanning for a
+ * pixel change in the frame buffer, which would have it
+ * appear that we're hanging, so we limit the scan to
+ * 1/256th of the frame buffer. This number is mostly
+ * based on PR 202730 and the fact that on a MacBoook,
+ * where we can't read from the frame buffer the offset
+ * of the visible region is 0. In short: we want to scan
+ * enough to handle all adapters that have an offset
+ * larger than 0 and we want to scan as little as we can
+ * to not appear to hang when we can't read from the
+ * frame buffer.
+ */
+ offset = efifb_uga_find_pixel(uga, 0, pciio, efifb->fb_addr,
+ efifb->fb_size >> 8);
+ if (offset == -1) {
+ printf("Unable to reliably detect frame buffer.\n");
+ } else if (offset > 0) {
+ efifb->fb_addr += offset;
+ efifb->fb_size -= offset;
+ }
+ } else {
+ offset = 0;
+ efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
+ efifb->fb_addr = strtoul(ev, &p, 0);
+ if (*p != '\0')
+ return (1);
+ }
+
+ ev = getenv("hw.efifb.stride");
+ if (ev == NULL) {
+ if (pciio != NULL && offset != -1) {
+ /* Determine the stride. */
+ offset = efifb_uga_find_pixel(uga, 1, pciio,
+ efifb->fb_addr, horiz * 8);
+ if (offset != -1)
+ efifb->fb_stride = offset >> 2;
+ } else {
+ printf("Unable to reliably detect the stride.\n");
+ }
+ } else {
+ efifb->fb_stride = strtoul(ev, &p, 0);
+ if (*p != '\0')
+ return (1);
+ }
+
+ /*
+ * We finalized on the stride, so recalculate the size of the
+ * frame buffer.
+ */
+ efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
+ return (0);
+}
+
+int
+efi_find_framebuffer(struct efi_fb *efifb)
+{
+ EFI_GRAPHICS_OUTPUT *gop;
+ EFI_UGA_DRAW_PROTOCOL *uga;
+ EFI_STATUS status;
+
+ status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop);
+ if (status == EFI_SUCCESS)
+ return (efifb_from_gop(efifb, gop->Mode, gop->Mode->Info));
+
+ status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
+ if (status == EFI_SUCCESS)
+ return (efifb_from_uga(efifb, uga));
+
+ return (1);
+}
+
+static void
+print_efifb(int mode, struct efi_fb *efifb, int verbose)
+{
+ u_int depth;
+
+ if (mode >= 0)
+ printf("mode %d: ", mode);
+ depth = efifb_color_depth(efifb);
+ printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height,
+ depth, efifb->fb_stride);
+ if (verbose) {
+ printf("\n frame buffer: address=%jx, size=%jx",
+ (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size);
+ printf("\n color mask: R=%08x, G=%08x, B=%08x\n",
+ efifb->fb_mask_red, efifb->fb_mask_green,
+ efifb->fb_mask_blue);
+ }
+}
+
+COMMAND_SET(gop, "gop", "graphics output protocol", command_gop);
+
+static int
+command_gop(int argc, char *argv[])
+{
+ struct efi_fb efifb;
+ EFI_GRAPHICS_OUTPUT *gop;
+ EFI_STATUS status;
+ u_int mode;
+
+ status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop);
+ if (EFI_ERROR(status)) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "%s: Graphics Output Protocol not present (error=%lu)",
+ argv[0], EFI_ERROR_CODE(status));
+ return (CMD_ERROR);
+ }
+
+ if (argc < 2)
+ goto usage;
+
+ if (!strcmp(argv[1], "set")) {
+ char *cp;
+
+ if (argc != 3)
+ goto usage;
+ mode = strtol(argv[2], &cp, 0);
+ if (cp[0] != '\0') {
+ sprintf(command_errbuf, "mode is an integer");
+ return (CMD_ERROR);
+ }
+ status = gop->SetMode(gop, mode);
+ if (EFI_ERROR(status)) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "%s: Unable to set mode to %u (error=%lu)",
+ argv[0], mode, EFI_ERROR_CODE(status));
+ return (CMD_ERROR);
+ }
+ } else if (!strcmp(argv[1], "get")) {
+ if (argc != 2)
+ goto usage;
+ efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
+ print_efifb(gop->Mode->Mode, &efifb, 1);
+ printf("\n");
+ } else if (!strcmp(argv[1], "list")) {
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
+ UINTN infosz;
+
+ if (argc != 2)
+ goto usage;
+ pager_open();
+ for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
+ status = gop->QueryMode(gop, mode, &infosz, &info);
+ if (EFI_ERROR(status))
+ continue;
+ efifb_from_gop(&efifb, gop->Mode, info);
+ print_efifb(mode, &efifb, 0);
+ if (pager_output("\n"))
+ break;
+ }
+ pager_close();
+ }
+ return (CMD_OK);
+
+ usage:
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "usage: %s [list | get | set <mode>]", argv[0]);
+ return (CMD_ERROR);
+}
+
+COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga);
+
+static int
+command_uga(int argc, char *argv[])
+{
+ struct efi_fb efifb;
+ EFI_UGA_DRAW_PROTOCOL *uga;
+ EFI_STATUS status;
+
+ status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
+ if (EFI_ERROR(status)) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "%s: UGA Protocol not present (error=%lu)",
+ argv[0], EFI_ERROR_CODE(status));
+ return (CMD_ERROR);
+ }
+
+ if (argc != 1)
+ goto usage;
+
+ if (efifb_from_uga(&efifb, uga) != CMD_OK) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "%s: Unable to get UGA information", argv[0]);
+ return (CMD_ERROR);
+ }
+
+ print_efifb(-1, &efifb, 1);
+ printf("\n");
+ return (CMD_OK);
+
+ usage:
+ snprintf(command_errbuf, sizeof(command_errbuf), "usage: %s", argv[0]);
+ return (CMD_ERROR);
+}
diff --git a/stand/efi/loader/framebuffer.h b/stand/efi/loader/framebuffer.h
new file mode 100644
index 0000000..2ec9017
--- /dev/null
+++ b/stand/efi/loader/framebuffer.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Benno Rice under sponsorship from
+ * the FreeBSD Foundation.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _EFIFB_H_
+#define _EFIFB_H_
+
+int efi_find_framebuffer(struct efi_fb *efifb);
+
+#endif /* _EFIFB_H_ */
diff --git a/stand/efi/loader/loader_efi.h b/stand/efi/loader/loader_efi.h
new file mode 100644
index 0000000..780fbfe
--- /dev/null
+++ b/stand/efi/loader/loader_efi.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Benno Rice under sponsorship from
+ * the FreeBSD Foundation.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LOADER_EFI_COPY_H_
+#define _LOADER_EFI_COPY_H_
+
+#include <stand.h>
+
+int efi_autoload(void);
+
+int efi_copy_init(void);
+
+ssize_t efi_copyin(const void *src, vm_offset_t dest, const size_t len);
+ssize_t efi_copyout(const vm_offset_t src, void *dest, const size_t len);
+ssize_t efi_readin(const int fd, vm_offset_t dest, const size_t len);
+void * efi_translate(vm_offset_t ptr);
+
+void efi_copy_finish(void);
+
+#endif /* _LOADER_EFI_COPY_H_ */
diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
new file mode 100644
index 0000000..129fd72
--- /dev/null
+++ b/stand/efi/loader/main.c
@@ -0,0 +1,936 @@
+/*-
+ * Copyright (c) 2008-2010 Rui Paulo
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/disk.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/boot.h>
+#include <inttypes.h>
+#include <stand.h>
+#include <string.h>
+#include <setjmp.h>
+#include <disk.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <uuid.h>
+
+#include <bootstrap.h>
+#include <smbios.h>
+
+#ifdef EFI_ZFS_BOOT
+#include <libzfs.h>
+
+#include "efizfs.h"
+#endif
+
+#include "loader_efi.h"
+
+extern char bootprog_info[];
+
+struct arch_switch archsw; /* MI/MD interface boundary */
+
+EFI_GUID acpi = ACPI_TABLE_GUID;
+EFI_GUID acpi20 = ACPI_20_TABLE_GUID;
+EFI_GUID devid = DEVICE_PATH_PROTOCOL;
+EFI_GUID imgid = LOADED_IMAGE_PROTOCOL;
+EFI_GUID mps = MPS_TABLE_GUID;
+EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL;
+EFI_GUID smbios = SMBIOS_TABLE_GUID;
+EFI_GUID dxe = DXE_SERVICES_TABLE_GUID;
+EFI_GUID hoblist = HOB_LIST_TABLE_GUID;
+EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID;
+EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID;
+EFI_GUID fdtdtb = FDT_TABLE_GUID;
+EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL;
+
+static EFI_LOADED_IMAGE *img;
+
+#ifdef EFI_ZFS_BOOT
+bool
+efi_zfs_is_preferred(EFI_HANDLE *h)
+{
+ return (h == img->DeviceHandle);
+}
+#endif
+
+static int
+has_keyboard(void)
+{
+ EFI_STATUS status;
+ EFI_DEVICE_PATH *path;
+ EFI_HANDLE *hin, *hin_end, *walker;
+ UINTN sz;
+ int retval = 0;
+
+ /*
+ * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and
+ * do the typical dance to get the right sized buffer.
+ */
+ sz = 0;
+ hin = NULL;
+ status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0);
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ hin = (EFI_HANDLE *)malloc(sz);
+ status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz,
+ hin);
+ if (EFI_ERROR(status))
+ free(hin);
+ }
+ if (EFI_ERROR(status))
+ return retval;
+
+ /*
+ * Look at each of the handles. If it supports the device path protocol,
+ * use it to get the device path for this handle. Then see if that
+ * device path matches either the USB device path for keyboards or the
+ * legacy device path for keyboards.
+ */
+ hin_end = &hin[sz / sizeof(*hin)];
+ for (walker = hin; walker < hin_end; walker++) {
+ status = BS->HandleProtocol(*walker, &devid, (VOID **)&path);
+ if (EFI_ERROR(status))
+ continue;
+
+ while (!IsDevicePathEnd(path)) {
+ /*
+ * Check for the ACPI keyboard node. All PNP3xx nodes
+ * are keyboards of different flavors. Note: It is
+ * unclear of there's always a keyboard node when
+ * there's a keyboard controller, or if there's only one
+ * when a keyboard is detected at boot.
+ */
+ if (DevicePathType(path) == ACPI_DEVICE_PATH &&
+ (DevicePathSubType(path) == ACPI_DP ||
+ DevicePathSubType(path) == ACPI_EXTENDED_DP)) {
+ ACPI_HID_DEVICE_PATH *acpi;
+
+ acpi = (ACPI_HID_DEVICE_PATH *)(void *)path;
+ if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 &&
+ (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) {
+ retval = 1;
+ goto out;
+ }
+ /*
+ * Check for USB keyboard node, if present. Unlike a
+ * PS/2 keyboard, these definitely only appear when
+ * connected to the system.
+ */
+ } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH &&
+ DevicePathSubType(path) == MSG_USB_CLASS_DP) {
+ USB_CLASS_DEVICE_PATH *usb;
+
+ usb = (USB_CLASS_DEVICE_PATH *)(void *)path;
+ if (usb->DeviceClass == 3 && /* HID */
+ usb->DeviceSubClass == 1 && /* Boot devices */
+ usb->DeviceProtocol == 1) { /* Boot keyboards */
+ retval = 1;
+ goto out;
+ }
+ }
+ path = NextDevicePathNode(path);
+ }
+ }
+out:
+ free(hin);
+ return retval;
+}
+
+static void
+set_devdesc_currdev(struct devsw *dev, int unit)
+{
+ struct devdesc currdev;
+ char *devname;
+
+ currdev.d_dev = dev;
+ currdev.d_type = currdev.d_dev->dv_type;
+ currdev.d_unit = unit;
+ currdev.d_opendata = NULL;
+ devname = efi_fmtdev(&currdev);
+
+ env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
+ env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset);
+}
+
+static int
+find_currdev(EFI_LOADED_IMAGE *img)
+{
+ pdinfo_list_t *pdi_list;
+ pdinfo_t *dp, *pp;
+ EFI_DEVICE_PATH *devpath, *copy;
+ EFI_HANDLE h;
+ char *devname;
+ struct devsw *dev;
+ int unit;
+ uint64_t extra;
+
+#ifdef EFI_ZFS_BOOT
+ /* Did efi_zfs_probe() detect the boot pool? */
+ if (pool_guid != 0) {
+ struct zfs_devdesc currdev;
+
+ currdev.d_dev = &zfs_dev;
+ currdev.d_unit = 0;
+ currdev.d_type = currdev.d_dev->dv_type;
+ currdev.d_opendata = NULL;
+ currdev.pool_guid = pool_guid;
+ currdev.root_guid = 0;
+ devname = efi_fmtdev(&currdev);
+
+ env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
+ env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, devname, env_noset,
+ env_nounset);
+ init_zfs_bootenv(devname);
+ return (0);
+ }
+#endif /* EFI_ZFS_BOOT */
+
+ /* We have device lists for hd, cd, fd, walk them all. */
+ pdi_list = efiblk_get_pdinfo_list(&efipart_hddev);
+ STAILQ_FOREACH(dp, pdi_list, pd_link) {
+ struct disk_devdesc currdev;
+
+ currdev.d_dev = &efipart_hddev;
+ currdev.d_type = currdev.d_dev->dv_type;
+ currdev.d_unit = dp->pd_unit;
+ currdev.d_opendata = NULL;
+ currdev.d_slice = -1;
+ currdev.d_partition = -1;
+
+ if (dp->pd_handle == img->DeviceHandle) {
+ devname = efi_fmtdev(&currdev);
+
+ env_setenv("currdev", EV_VOLATILE, devname,
+ efi_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, devname,
+ env_noset, env_nounset);
+ return (0);
+ }
+ /* Assuming GPT partitioning. */
+ STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
+ if (pp->pd_handle == img->DeviceHandle) {
+ currdev.d_slice = pp->pd_unit;
+ currdev.d_partition = 255;
+ devname = efi_fmtdev(&currdev);
+
+ env_setenv("currdev", EV_VOLATILE, devname,
+ efi_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, devname,
+ env_noset, env_nounset);
+ return (0);
+ }
+ }
+ }
+
+ pdi_list = efiblk_get_pdinfo_list(&efipart_cddev);
+ STAILQ_FOREACH(dp, pdi_list, pd_link) {
+ if (dp->pd_handle == img->DeviceHandle ||
+ dp->pd_alias == img->DeviceHandle) {
+ set_devdesc_currdev(&efipart_cddev, dp->pd_unit);
+ return (0);
+ }
+ }
+
+ pdi_list = efiblk_get_pdinfo_list(&efipart_fddev);
+ STAILQ_FOREACH(dp, pdi_list, pd_link) {
+ if (dp->pd_handle == img->DeviceHandle) {
+ set_devdesc_currdev(&efipart_fddev, dp->pd_unit);
+ return (0);
+ }
+ }
+
+ /*
+ * Try the device handle from our loaded image first. If that
+ * fails, use the device path from the loaded image and see if
+ * any of the nodes in that path match one of the enumerated
+ * handles.
+ */
+ if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) {
+ set_devdesc_currdev(dev, unit);
+ return (0);
+ }
+
+ copy = NULL;
+ devpath = efi_lookup_image_devpath(IH);
+ while (devpath != NULL) {
+ h = efi_devpath_handle(devpath);
+ if (h == NULL)
+ break;
+
+ free(copy);
+ copy = NULL;
+
+ if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) {
+ set_devdesc_currdev(dev, unit);
+ return (0);
+ }
+
+ devpath = efi_lookup_devpath(h);
+ if (devpath != NULL) {
+ copy = efi_devpath_trim(devpath);
+ devpath = copy;
+ }
+ }
+ free(copy);
+
+ return (ENOENT);
+}
+
+EFI_STATUS
+main(int argc, CHAR16 *argv[])
+{
+ char var[128];
+ EFI_GUID *guid;
+ int i, j, vargood, howto;
+ UINTN k;
+ int has_kbd;
+#if !defined(__arm__)
+ char buf[40];
+#endif
+
+ archsw.arch_autoload = efi_autoload;
+ archsw.arch_getdev = efi_getdev;
+ archsw.arch_copyin = efi_copyin;
+ archsw.arch_copyout = efi_copyout;
+ archsw.arch_readin = efi_readin;
+#ifdef EFI_ZFS_BOOT
+ /* Note this needs to be set before ZFS init. */
+ archsw.arch_zfs_probe = efi_zfs_probe;
+#endif
+
+ /* Get our loaded image protocol interface structure. */
+ BS->HandleProtocol(IH, &imgid, (VOID**)&img);
+
+ /* Init the time source */
+ efi_time_init();
+
+ has_kbd = has_keyboard();
+
+ /*
+ * XXX Chicken-and-egg problem; we want to have console output
+ * early, but some console attributes may depend on reading from
+ * eg. the boot device, which we can't do yet. We can use
+ * printf() etc. once this is done.
+ */
+ cons_probe();
+
+ /*
+ * Initialise the block cache. Set the upper limit.
+ */
+ bcache_init(32768, 512);
+
+ /*
+ * Parse the args to set the console settings, etc
+ * boot1.efi passes these in, if it can read /boot.config or /boot/config
+ * or iPXE may be setup to pass these in.
+ *
+ * Loop through the args, and for each one that contains an '=' that is
+ * not the first character, add it to the environment. This allows
+ * loader and kernel env vars to be passed on the command line. Convert
+ * args from UCS-2 to ASCII (16 to 8 bit) as they are copied.
+ */
+ howto = 0;
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ for (j = 1; argv[i][j] != 0; j++) {
+ int ch;
+
+ ch = argv[i][j];
+ switch (ch) {
+ case 'a':
+ howto |= RB_ASKNAME;
+ break;
+ case 'd':
+ howto |= RB_KDB;
+ break;
+ case 'D':
+ howto |= RB_MULTIPLE;
+ break;
+ case 'h':
+ howto |= RB_SERIAL;
+ break;
+ case 'm':
+ howto |= RB_MUTE;
+ break;
+ case 'p':
+ howto |= RB_PAUSE;
+ break;
+ case 'P':
+ if (!has_kbd)
+ howto |= RB_SERIAL | RB_MULTIPLE;
+ break;
+ case 'r':
+ howto |= RB_DFLTROOT;
+ break;
+ case 's':
+ howto |= RB_SINGLE;
+ break;
+ case 'S':
+ if (argv[i][j + 1] == 0) {
+ if (i + 1 == argc) {
+ setenv("comconsole_speed", "115200", 1);
+ } else {
+ cpy16to8(&argv[i + 1][0], var,
+ sizeof(var));
+ setenv("comconsole_speed", var, 1);
+ }
+ i++;
+ break;
+ } else {
+ cpy16to8(&argv[i][j + 1], var,
+ sizeof(var));
+ setenv("comconsole_speed", var, 1);
+ break;
+ }
+ case 'v':
+ howto |= RB_VERBOSE;
+ break;
+ }
+ }
+ } else {
+ vargood = 0;
+ for (j = 0; argv[i][j] != 0; j++) {
+ if (j == sizeof(var)) {
+ vargood = 0;
+ break;
+ }
+ if (j > 0 && argv[i][j] == '=')
+ vargood = 1;
+ var[j] = (char)argv[i][j];
+ }
+ if (vargood) {
+ var[j] = 0;
+ putenv(var);
+ }
+ }
+ }
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (howto & howto_names[i].mask)
+ setenv(howto_names[i].ev, "YES", 1);
+ if (howto & RB_MULTIPLE) {
+ if (howto & RB_SERIAL)
+ setenv("console", "comconsole efi" , 1);
+ else
+ setenv("console", "efi comconsole" , 1);
+ } else if (howto & RB_SERIAL) {
+ setenv("console", "comconsole" , 1);
+ }
+
+ if (efi_copy_init()) {
+ printf("failed to allocate staging area\n");
+ return (EFI_BUFFER_TOO_SMALL);
+ }
+
+ /*
+ * March through the device switch probing for things.
+ */
+ for (i = 0; devsw[i] != NULL; i++)
+ if (devsw[i]->dv_init != NULL)
+ (devsw[i]->dv_init)();
+
+ printf("Command line arguments:");
+ for (i = 0; i < argc; i++)
+ printf(" %S", argv[i]);
+ printf("\n");
+
+ printf("Image base: 0x%lx\n", (u_long)img->ImageBase);
+ printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16,
+ ST->Hdr.Revision & 0xffff);
+ printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor,
+ ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+
+ printf("\n%s", bootprog_info);
+
+ /*
+ * Disable the watchdog timer. By default the boot manager sets
+ * the timer to 5 minutes before invoking a boot option. If we
+ * want to return to the boot manager, we have to disable the
+ * watchdog timer and since we're an interactive program, we don't
+ * want to wait until the user types "quit". The timer may have
+ * fired by then. We don't care if this fails. It does not prevent
+ * normal functioning in any way...
+ */
+ BS->SetWatchdogTimer(0, 0, 0, NULL);
+
+ if (find_currdev(img) != 0)
+ return (EFI_NOT_FOUND);
+
+ efi_init_environment();
+ setenv("LINES", "24", 1); /* optional */
+
+ for (k = 0; k < ST->NumberOfTableEntries; k++) {
+ guid = &ST->ConfigurationTable[k].VendorGuid;
+#if !defined(__arm__)
+ if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) {
+ snprintf(buf, sizeof(buf), "%p",
+ ST->ConfigurationTable[k].VendorTable);
+ setenv("hint.smbios.0.mem", buf, 1);
+ smbios_detect(ST->ConfigurationTable[k].VendorTable);
+ break;
+ }
+#endif
+ }
+
+ interact(NULL); /* doesn't return */
+
+ return (EFI_SUCCESS); /* keep compiler happy */
+}
+
+COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
+
+static int
+command_reboot(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; devsw[i] != NULL; ++i)
+ if (devsw[i]->dv_cleanup != NULL)
+ (devsw[i]->dv_cleanup)();
+
+ RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
+
+ /* NOTREACHED */
+ return (CMD_ERROR);
+}
+
+COMMAND_SET(quit, "quit", "exit the loader", command_quit);
+
+static int
+command_quit(int argc, char *argv[])
+{
+ exit(0);
+ return (CMD_OK);
+}
+
+COMMAND_SET(memmap, "memmap", "print memory map", command_memmap);
+
+static int
+command_memmap(int argc, char *argv[])
+{
+ UINTN sz;
+ EFI_MEMORY_DESCRIPTOR *map, *p;
+ UINTN key, dsz;
+ UINT32 dver;
+ EFI_STATUS status;
+ int i, ndesc;
+ char line[80];
+ static char *types[] = {
+ "Reserved",
+ "LoaderCode",
+ "LoaderData",
+ "BootServicesCode",
+ "BootServicesData",
+ "RuntimeServicesCode",
+ "RuntimeServicesData",
+ "ConventionalMemory",
+ "UnusableMemory",
+ "ACPIReclaimMemory",
+ "ACPIMemoryNVS",
+ "MemoryMappedIO",
+ "MemoryMappedIOPortSpace",
+ "PalCode"
+ };
+
+ sz = 0;
+ status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
+ if (status != EFI_BUFFER_TOO_SMALL) {
+ printf("Can't determine memory map size\n");
+ return (CMD_ERROR);
+ }
+ map = malloc(sz);
+ status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
+ if (EFI_ERROR(status)) {
+ printf("Can't read memory map\n");
+ return (CMD_ERROR);
+ }
+
+ ndesc = sz / dsz;
+ snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n",
+ "Type", "Physical", "Virtual", "#Pages", "Attr");
+ pager_open();
+ if (pager_output(line)) {
+ pager_close();
+ return (CMD_OK);
+ }
+
+ for (i = 0, p = map; i < ndesc;
+ i++, p = NextMemoryDescriptor(p, dsz)) {
+ printf("%23s %012jx %012jx %08jx ", types[p->Type],
+ (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart,
+ (uintmax_t)p->NumberOfPages);
+ if (p->Attribute & EFI_MEMORY_UC)
+ printf("UC ");
+ if (p->Attribute & EFI_MEMORY_WC)
+ printf("WC ");
+ if (p->Attribute & EFI_MEMORY_WT)
+ printf("WT ");
+ if (p->Attribute & EFI_MEMORY_WB)
+ printf("WB ");
+ if (p->Attribute & EFI_MEMORY_UCE)
+ printf("UCE ");
+ if (p->Attribute & EFI_MEMORY_WP)
+ printf("WP ");
+ if (p->Attribute & EFI_MEMORY_RP)
+ printf("RP ");
+ if (p->Attribute & EFI_MEMORY_XP)
+ printf("XP ");
+ if (pager_output("\n"))
+ break;
+ }
+
+ pager_close();
+ return (CMD_OK);
+}
+
+COMMAND_SET(configuration, "configuration", "print configuration tables",
+ command_configuration);
+
+static const char *
+guid_to_string(EFI_GUID *guid)
+{
+ static char buf[40];
+
+ sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ guid->Data1, guid->Data2, guid->Data3, guid->Data4[0],
+ guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4],
+ guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+ return (buf);
+}
+
+static int
+command_configuration(int argc, char *argv[])
+{
+ char line[80];
+ UINTN i;
+
+ snprintf(line, sizeof(line), "NumberOfTableEntries=%lu\n",
+ (unsigned long)ST->NumberOfTableEntries);
+ pager_open();
+ if (pager_output(line)) {
+ pager_close();
+ return (CMD_OK);
+ }
+
+ for (i = 0; i < ST->NumberOfTableEntries; i++) {
+ EFI_GUID *guid;
+
+ printf(" ");
+ guid = &ST->ConfigurationTable[i].VendorGuid;
+ if (!memcmp(guid, &mps, sizeof(EFI_GUID)))
+ printf("MPS Table");
+ else if (!memcmp(guid, &acpi, sizeof(EFI_GUID)))
+ printf("ACPI Table");
+ else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID)))
+ printf("ACPI 2.0 Table");
+ else if (!memcmp(guid, &smbios, sizeof(EFI_GUID)))
+ printf("SMBIOS Table %p",
+ ST->ConfigurationTable[i].VendorTable);
+ else if (!memcmp(guid, &dxe, sizeof(EFI_GUID)))
+ printf("DXE Table");
+ else if (!memcmp(guid, &hoblist, sizeof(EFI_GUID)))
+ printf("HOB List Table");
+ else if (!memcmp(guid, &memtype, sizeof(EFI_GUID)))
+ printf("Memory Type Information Table");
+ else if (!memcmp(guid, &debugimg, sizeof(EFI_GUID)))
+ printf("Debug Image Info Table");
+ else if (!memcmp(guid, &fdtdtb, sizeof(EFI_GUID)))
+ printf("FDT Table");
+ else
+ printf("Unknown Table (%s)", guid_to_string(guid));
+ snprintf(line, sizeof(line), " at %p\n",
+ ST->ConfigurationTable[i].VendorTable);
+ if (pager_output(line))
+ break;
+ }
+
+ pager_close();
+ return (CMD_OK);
+}
+
+
+COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode);
+
+static int
+command_mode(int argc, char *argv[])
+{
+ UINTN cols, rows;
+ unsigned int mode;
+ int i;
+ char *cp;
+ char rowenv[8];
+ EFI_STATUS status;
+ SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
+ extern void HO(void);
+
+ conout = ST->ConOut;
+
+ if (argc > 1) {
+ mode = strtol(argv[1], &cp, 0);
+ if (cp[0] != '\0') {
+ printf("Invalid mode\n");
+ return (CMD_ERROR);
+ }
+ status = conout->QueryMode(conout, mode, &cols, &rows);
+ if (EFI_ERROR(status)) {
+ printf("invalid mode %d\n", mode);
+ return (CMD_ERROR);
+ }
+ status = conout->SetMode(conout, mode);
+ if (EFI_ERROR(status)) {
+ printf("couldn't set mode %d\n", mode);
+ return (CMD_ERROR);
+ }
+ sprintf(rowenv, "%u", (unsigned)rows);
+ setenv("LINES", rowenv, 1);
+ HO(); /* set cursor */
+ return (CMD_OK);
+ }
+
+ printf("Current mode: %d\n", conout->Mode->Mode);
+ for (i = 0; i <= conout->Mode->MaxMode; i++) {
+ status = conout->QueryMode(conout, i, &cols, &rows);
+ if (EFI_ERROR(status))
+ continue;
+ printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols,
+ (unsigned)rows);
+ }
+
+ if (i != 0)
+ printf("Select a mode with the command \"mode <number>\"\n");
+
+ return (CMD_OK);
+}
+
+#ifdef EFI_ZFS_BOOT
+COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
+ command_lszfs);
+
+static int
+command_lszfs(int argc, char *argv[])
+{
+ int err;
+
+ if (argc != 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ err = zfs_list(argv[1]);
+ if (err != 0) {
+ command_errmsg = strerror(err);
+ return (CMD_ERROR);
+ }
+ return (CMD_OK);
+}
+
+COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments",
+ command_reloadbe);
+
+static int
+command_reloadbe(int argc, char *argv[])
+{
+ int err;
+ char *root;
+
+ if (argc > 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ if (argc == 2) {
+ err = zfs_bootenv(argv[1]);
+ } else {
+ root = getenv("zfs_be_root");
+ if (root == NULL) {
+ return (CMD_OK);
+ }
+ err = zfs_bootenv(root);
+ }
+
+ if (err != 0) {
+ command_errmsg = strerror(err);
+ return (CMD_ERROR);
+ }
+
+ return (CMD_OK);
+}
+#endif
+
+#ifdef LOADER_FDT_SUPPORT
+extern int command_fdt_internal(int argc, char *argv[]);
+
+/*
+ * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
+ * and declaring it as extern is in contradiction with COMMAND_SET() macro
+ * (which uses static pointer), we're defining wrapper function, which
+ * calls the proper fdt handling routine.
+ */
+static int
+command_fdt(int argc, char *argv[])
+{
+
+ return (command_fdt_internal(argc, argv));
+}
+
+COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
+#endif
+
+/*
+ * Chain load another efi loader.
+ */
+static int
+command_chain(int argc, char *argv[])
+{
+ EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
+ EFI_HANDLE loaderhandle;
+ EFI_LOADED_IMAGE *loaded_image;
+ EFI_STATUS status;
+ struct stat st;
+ struct devdesc *dev;
+ char *name, *path;
+ void *buf;
+ int fd;
+
+ if (argc < 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ name = argv[1];
+
+ if ((fd = open(name, O_RDONLY)) < 0) {
+ command_errmsg = "no such file";
+ return (CMD_ERROR);
+ }
+
+ if (fstat(fd, &st) < -1) {
+ command_errmsg = "stat failed";
+ close(fd);
+ return (CMD_ERROR);
+ }
+
+ status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "failed to allocate buffer";
+ close(fd);
+ return (CMD_ERROR);
+ }
+ if (read(fd, buf, st.st_size) != st.st_size) {
+ command_errmsg = "error while reading the file";
+ (void)BS->FreePool(buf);
+ close(fd);
+ return (CMD_ERROR);
+ }
+ close(fd);
+ status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle);
+ (void)BS->FreePool(buf);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "LoadImage failed";
+ return (CMD_ERROR);
+ }
+ status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
+ (void **)&loaded_image);
+
+ if (argc > 2) {
+ int i, len = 0;
+ CHAR16 *argp;
+
+ for (i = 2; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+
+ len *= sizeof (*argp);
+ loaded_image->LoadOptions = argp = malloc (len);
+ loaded_image->LoadOptionsSize = len;
+ for (i = 2; i < argc; i++) {
+ char *ptr = argv[i];
+ while (*ptr)
+ *(argp++) = *(ptr++);
+ *(argp++) = ' ';
+ }
+ *(--argv) = 0;
+ }
+
+ if (efi_getdev((void **)&dev, name, (const char **)&path) == 0) {
+#ifdef EFI_ZFS_BOOT
+ struct zfs_devdesc *z_dev;
+#endif
+ struct disk_devdesc *d_dev;
+ pdinfo_t *hd, *pd;
+
+ switch (dev->d_type) {
+#ifdef EFI_ZFS_BOOT
+ case DEVT_ZFS:
+ z_dev = (struct zfs_devdesc *)dev;
+ loaded_image->DeviceHandle =
+ efizfs_get_handle_by_guid(z_dev->pool_guid);
+ break;
+#endif
+ case DEVT_NET:
+ loaded_image->DeviceHandle =
+ efi_find_handle(dev->d_dev, dev->d_unit);
+ break;
+ default:
+ hd = efiblk_get_pdinfo(dev);
+ if (STAILQ_EMPTY(&hd->pd_part)) {
+ loaded_image->DeviceHandle = hd->pd_handle;
+ break;
+ }
+ d_dev = (struct disk_devdesc *)dev;
+ STAILQ_FOREACH(pd, &hd->pd_part, pd_link) {
+ /*
+ * d_partition should be 255
+ */
+ if (pd->pd_unit == (uint32_t)d_dev->d_slice) {
+ loaded_image->DeviceHandle =
+ pd->pd_handle;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ dev_cleanup();
+ status = BS->StartImage(loaderhandle, NULL, NULL);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "StartImage failed";
+ free(loaded_image->LoadOptions);
+ loaded_image->LoadOptions = NULL;
+ status = BS->UnloadImage(loaded_image);
+ return (CMD_ERROR);
+ }
+
+ return (CMD_ERROR); /* not reached */
+}
+
+COMMAND_SET(chain, "chain", "chain load file", command_chain);
diff --git a/stand/efi/loader/version b/stand/efi/loader/version
new file mode 100644
index 0000000..3a4c47c
--- /dev/null
+++ b/stand/efi/loader/version
@@ -0,0 +1,7 @@
+$FreeBSD$
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+1.1: Keep in sync with i386 version.
+0.1: Initial i386 version. Derived from ia64.
diff --git a/stand/fdt.mk b/stand/fdt.mk
new file mode 100644
index 0000000..4d4794d
--- /dev/null
+++ b/stand/fdt.mk
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.if ${MK_FDT} == "yes"
+CFLAGS+= -I${FDTSRC}
+CFLAGS+= -I${BOOTOBJ}/fdt
+CFLAGS+= -I${SYSDIR}/contrib/libfdt
+CFLAGS+= -DLOADER_FDT_SUPPORT
+LIBFDT= ${BOOTOBJ}/fdt/libfdt.a
+.endif
diff --git a/stand/fdt/Makefile b/stand/fdt/Makefile
new file mode 100644
index 0000000..dd31aac
--- /dev/null
+++ b/stand/fdt/Makefile
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+.PATH: ${SYSDIR}/contrib/libfdt/
+
+LIB= fdt
+INTERNALLIB=
+
+# Vendor sources of libfdt.
+SRCS+= fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c \
+ fdt_empty_tree.c fdt_addresses.c fdt_overlay.c
+
+# Loader's fdt commands extension sources.
+SRCS+= fdt_loader_cmd.c
+
+CFLAGS+= -I${SYSDIR}/contrib/libfdt/ -I${LDRSRC}
+
+CFLAGS+= -ffreestanding
+
+.if ${MACHINE_CPUARCH} == "powerpc" || ${MACHINE_CPUARCH} == "arm" || ${MACHINE_CPUARCH} == "mips"
+CFLAGS+= -msoft-float
+.endif
+
+CFLAGS+= -Wformat -Wall
+
+.include <bsd.stand.mk>
+.include <bsd.lib.mk>
diff --git a/stand/fdt/Makefile.depend b/stand/fdt/Makefile.depend
new file mode 100644
index 0000000..2984414
--- /dev/null
+++ b/stand/fdt/Makefile.depend
@@ -0,0 +1,14 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libstand \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/fdt/fdt_loader_cmd.c b/stand/fdt/fdt_loader_cmd.c
new file mode 100644
index 0000000..1b2737a
--- /dev/null
+++ b/stand/fdt/fdt_loader_cmd.c
@@ -0,0 +1,1796 @@
+/*-
+ * Copyright (c) 2009-2010 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <libfdt.h>
+#include <fdt.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <machine/elf.h>
+
+#include "bootstrap.h"
+#include "fdt_platform.h"
+
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf("%s(): ", __func__); \
+ printf(fmt,##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+#define FDT_CWD_LEN 256
+#define FDT_MAX_DEPTH 12
+
+#define FDT_PROP_SEP " = "
+
+#define COPYOUT(s,d,l) archsw.arch_copyout(s, d, l)
+#define COPYIN(s,d,l) archsw.arch_copyin(s, d, l)
+
+#define FDT_STATIC_DTB_SYMBOL "fdt_static_dtb"
+
+#define CMD_REQUIRES_BLOB 0x01
+
+/* Location of FDT yet to be loaded. */
+/* This may be in read-only memory, so can't be manipulated directly. */
+static struct fdt_header *fdt_to_load = NULL;
+/* Location of FDT on heap. */
+/* This is the copy we actually manipulate. */
+static struct fdt_header *fdtp = NULL;
+/* Size of FDT blob */
+static size_t fdtp_size = 0;
+/* Location of FDT in kernel or module. */
+/* This won't be set if FDT is loaded from disk or memory. */
+/* If it is set, we'll update it when fdt_copy() gets called. */
+static vm_offset_t fdtp_va = 0;
+
+static int fdt_load_dtb(vm_offset_t va);
+static void fdt_print_overlay_load_error(int err, const char *filename);
+
+static int fdt_cmd_nyi(int argc, char *argv[]);
+static int fdt_load_dtb_overlays_string(const char * filenames);
+
+static int fdt_cmd_addr(int argc, char *argv[]);
+static int fdt_cmd_mkprop(int argc, char *argv[]);
+static int fdt_cmd_cd(int argc, char *argv[]);
+static int fdt_cmd_hdr(int argc, char *argv[]);
+static int fdt_cmd_ls(int argc, char *argv[]);
+static int fdt_cmd_prop(int argc, char *argv[]);
+static int fdt_cmd_pwd(int argc, char *argv[]);
+static int fdt_cmd_rm(int argc, char *argv[]);
+static int fdt_cmd_mknode(int argc, char *argv[]);
+static int fdt_cmd_mres(int argc, char *argv[]);
+
+typedef int cmdf_t(int, char *[]);
+
+struct cmdtab {
+ const char *name;
+ cmdf_t *handler;
+ int flags;
+};
+
+static const struct cmdtab commands[] = {
+ { "addr", &fdt_cmd_addr, 0 },
+ { "alias", &fdt_cmd_nyi, 0 },
+ { "cd", &fdt_cmd_cd, CMD_REQUIRES_BLOB },
+ { "header", &fdt_cmd_hdr, CMD_REQUIRES_BLOB },
+ { "ls", &fdt_cmd_ls, CMD_REQUIRES_BLOB },
+ { "mknode", &fdt_cmd_mknode, CMD_REQUIRES_BLOB },
+ { "mkprop", &fdt_cmd_mkprop, CMD_REQUIRES_BLOB },
+ { "mres", &fdt_cmd_mres, CMD_REQUIRES_BLOB },
+ { "prop", &fdt_cmd_prop, CMD_REQUIRES_BLOB },
+ { "pwd", &fdt_cmd_pwd, CMD_REQUIRES_BLOB },
+ { "rm", &fdt_cmd_rm, CMD_REQUIRES_BLOB },
+ { NULL, NULL }
+};
+
+static char cwd[FDT_CWD_LEN] = "/";
+
+static vm_offset_t
+fdt_find_static_dtb()
+{
+ Elf_Ehdr *ehdr;
+ Elf_Shdr *shdr;
+ Elf_Sym sym;
+ vm_offset_t strtab, symtab, fdt_start;
+ uint64_t offs;
+ struct preloaded_file *kfp;
+ struct file_metadata *md;
+ char *strp;
+ int i, sym_count;
+
+ debugf("fdt_find_static_dtb()\n");
+
+ sym_count = symtab = strtab = 0;
+ strp = NULL;
+
+ offs = __elfN(relocation_offset);
+
+ kfp = file_findfile(NULL, NULL);
+ if (kfp == NULL)
+ return (0);
+
+ /* Locate the dynamic symbols and strtab. */
+ md = file_findmetadata(kfp, MODINFOMD_ELFHDR);
+ if (md == NULL)
+ return (0);
+ ehdr = (Elf_Ehdr *)md->md_data;
+
+ md = file_findmetadata(kfp, MODINFOMD_SHDR);
+ if (md == NULL)
+ return (0);
+ shdr = (Elf_Shdr *)md->md_data;
+
+ for (i = 0; i < ehdr->e_shnum; ++i) {
+ if (shdr[i].sh_type == SHT_DYNSYM && symtab == 0) {
+ symtab = shdr[i].sh_addr + offs;
+ sym_count = shdr[i].sh_size / sizeof(Elf_Sym);
+ } else if (shdr[i].sh_type == SHT_STRTAB && strtab == 0) {
+ strtab = shdr[i].sh_addr + offs;
+ }
+ }
+
+ /*
+ * The most efficient way to find a symbol would be to calculate a
+ * hash, find proper bucket and chain, and thus find a symbol.
+ * However, that would involve code duplication (e.g. for hash
+ * function). So we're using simpler and a bit slower way: we're
+ * iterating through symbols, searching for the one which name is
+ * 'equal' to 'fdt_static_dtb'. To speed up the process a little bit,
+ * we are eliminating symbols type of which is not STT_NOTYPE, or(and)
+ * those which binding attribute is not STB_GLOBAL.
+ */
+ fdt_start = 0;
+ while (sym_count > 0 && fdt_start == 0) {
+ COPYOUT(symtab, &sym, sizeof(sym));
+ symtab += sizeof(sym);
+ --sym_count;
+ if (ELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
+ ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
+ continue;
+ strp = strdupout(strtab + sym.st_name);
+ if (strcmp(strp, FDT_STATIC_DTB_SYMBOL) == 0)
+ fdt_start = (vm_offset_t)sym.st_value + offs;
+ free(strp);
+ }
+ return (fdt_start);
+}
+
+static int
+fdt_load_dtb(vm_offset_t va)
+{
+ struct fdt_header header;
+ int err;
+
+ debugf("fdt_load_dtb(0x%08jx)\n", (uintmax_t)va);
+
+ COPYOUT(va, &header, sizeof(header));
+ err = fdt_check_header(&header);
+ if (err < 0) {
+ if (err == -FDT_ERR_BADVERSION) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "incompatible blob version: %d, should be: %d",
+ fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION);
+ } else {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "error validating blob: %s", fdt_strerror(err));
+ }
+ return (1);
+ }
+
+ /*
+ * Release previous blob
+ */
+ if (fdtp)
+ free(fdtp);
+
+ fdtp_size = fdt_totalsize(&header);
+ fdtp = malloc(fdtp_size);
+
+ if (fdtp == NULL) {
+ command_errmsg = "can't allocate memory for device tree copy";
+ return (1);
+ }
+
+ fdtp_va = va;
+ COPYOUT(va, fdtp, fdtp_size);
+ debugf("DTB blob found at 0x%jx, size: 0x%jx\n", (uintmax_t)va, (uintmax_t)fdtp_size);
+
+ return (0);
+}
+
+int
+fdt_load_dtb_addr(struct fdt_header *header)
+{
+ int err;
+
+ debugf("fdt_load_dtb_addr(%p)\n", header);
+
+ fdtp_size = fdt_totalsize(header);
+ err = fdt_check_header(header);
+ if (err < 0) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "error validating blob: %s", fdt_strerror(err));
+ return (err);
+ }
+ free(fdtp);
+ if ((fdtp = malloc(fdtp_size)) == NULL) {
+ command_errmsg = "can't allocate memory for device tree copy";
+ return (1);
+ }
+
+ fdtp_va = 0; // Don't write this back into module or kernel.
+ bcopy(header, fdtp, fdtp_size);
+ return (0);
+}
+
+int
+fdt_load_dtb_file(const char * filename)
+{
+ struct preloaded_file *bfp, *oldbfp;
+ int err;
+
+ debugf("fdt_load_dtb_file(%s)\n", filename);
+
+ oldbfp = file_findfile(NULL, "dtb");
+
+ /* Attempt to load and validate a new dtb from a file. */
+ if ((bfp = file_loadraw(filename, "dtb", 1)) == NULL) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "failed to load file '%s'", filename);
+ return (1);
+ }
+ if ((err = fdt_load_dtb(bfp->f_addr)) != 0) {
+ file_discard(bfp);
+ return (err);
+ }
+
+ /* A new dtb was validated, discard any previous file. */
+ if (oldbfp)
+ file_discard(oldbfp);
+ return (0);
+}
+
+static int
+fdt_load_dtb_overlay(const char * filename)
+{
+ struct preloaded_file *bfp;
+ struct fdt_header header;
+ int err;
+
+ debugf("fdt_load_dtb_overlay(%s)\n", filename);
+
+ /* Attempt to load and validate a new dtb from a file. FDT_ERR_NOTFOUND
+ * is normally a libfdt error code, but libfdt would actually return
+ * -FDT_ERR_NOTFOUND. We re-purpose the error code here to convey a
+ * similar meaning: the file itself was not found, which can still be
+ * considered an error dealing with FDT pieces.
+ */
+ if ((bfp = file_loadraw(filename, "dtbo", 1)) == NULL)
+ return (FDT_ERR_NOTFOUND);
+
+ COPYOUT(bfp->f_addr, &header, sizeof(header));
+ err = fdt_check_header(&header);
+
+ if (err < 0) {
+ file_discard(bfp);
+ return (err);
+ }
+
+ return (0);
+}
+
+static void
+fdt_print_overlay_load_error(int err, const char *filename)
+{
+
+ switch (err) {
+ case FDT_ERR_NOTFOUND:
+ printf("%s: failed to load file\n", filename);
+ break;
+ case -FDT_ERR_BADVERSION:
+ printf("%s: incompatible blob version: %d, should be: %d\n",
+ filename, fdt_version(fdtp),
+ FDT_LAST_SUPPORTED_VERSION);
+ break;
+ default:
+ /* libfdt errs are negative */
+ if (err < 0)
+ printf("%s: error validating blob: %s\n",
+ filename, fdt_strerror(err));
+ else
+ printf("%s: unknown load error\n", filename);
+ break;
+ }
+}
+
+static int
+fdt_load_dtb_overlays_string(const char * filenames)
+{
+ char *names;
+ char *name, *name_ext;
+ char *comaptr;
+ int err, namesz;
+
+ debugf("fdt_load_dtb_overlays_string(%s)\n", filenames);
+
+ names = strdup(filenames);
+ if (names == NULL)
+ return (1);
+ name = names;
+ do {
+ comaptr = strchr(name, ',');
+ if (comaptr)
+ *comaptr = '\0';
+ err = fdt_load_dtb_overlay(name);
+ if (err == FDT_ERR_NOTFOUND) {
+ /* Allocate enough to append ".dtbo" */
+ namesz = strlen(name) + 6;
+ name_ext = malloc(namesz);
+ if (name_ext == NULL) {
+ fdt_print_overlay_load_error(err, name);
+ name = comaptr + 1;
+ continue;
+ }
+ snprintf(name_ext, namesz, "%s.dtbo", name);
+ err = fdt_load_dtb_overlay(name_ext);
+ free(name_ext);
+ }
+ /* Catch error with either initial load or fallback load */
+ if (err != 0)
+ fdt_print_overlay_load_error(err, name);
+ name = comaptr + 1;
+ } while(comaptr);
+
+ free(names);
+ return (0);
+}
+
+void
+fdt_apply_overlays()
+{
+ struct preloaded_file *fp;
+ size_t max_overlay_size, next_fdtp_size;
+ size_t current_fdtp_size;
+ void *current_fdtp;
+ void *next_fdtp;
+ void *overlay;
+ int rv;
+
+ if ((fdtp == NULL) || (fdtp_size == 0))
+ return;
+
+ max_overlay_size = 0;
+ for (fp = file_findfile(NULL, "dtbo"); fp != NULL; fp = fp->f_next) {
+ if (max_overlay_size < fp->f_size)
+ max_overlay_size = fp->f_size;
+ }
+
+ /* Nothing to apply */
+ if (max_overlay_size == 0)
+ return;
+
+ overlay = malloc(max_overlay_size);
+ if (overlay == NULL) {
+ printf("failed to allocate memory for DTB blob with overlays\n");
+ return;
+ }
+ current_fdtp = fdtp;
+ current_fdtp_size = fdtp_size;
+ for (fp = file_findfile(NULL, "dtbo"); fp != NULL; fp = fp->f_next) {
+ printf("applying DTB overlay '%s'\n", fp->f_name);
+ next_fdtp_size = current_fdtp_size + fp->f_size;
+ next_fdtp = malloc(next_fdtp_size);
+ if (next_fdtp == NULL) {
+ /*
+ * Output warning, then move on to applying other
+ * overlays in case this one is simply too large.
+ */
+ printf("failed to allocate memory for overlay base\n");
+ continue;
+ }
+ rv = fdt_open_into(current_fdtp, next_fdtp, next_fdtp_size);
+ if (rv != 0) {
+ free(next_fdtp);
+ printf("failed to open base dtb into overlay base\n");
+ continue;
+ }
+ COPYOUT(fp->f_addr, overlay, fp->f_size);
+ /* Both overlay and next_fdtp may be modified in place */
+ rv = fdt_overlay_apply(next_fdtp, overlay);
+ if (rv == 0) {
+ /* Rotate next -> current */
+ if (current_fdtp != fdtp)
+ free(current_fdtp);
+ current_fdtp = next_fdtp;
+ current_fdtp_size = next_fdtp_size;
+ } else {
+ /*
+ * Assume here that the base we tried to apply on is
+ * either trashed or in an inconsistent state. Trying to
+ * load it might work, but it's better to discard it and
+ * play it safe. */
+ free(next_fdtp);
+ printf("failed to apply overlay: %s\n",
+ fdt_strerror(rv));
+ }
+ }
+ /* We could have failed to apply all overlays; then we do nothing */
+ if (current_fdtp != fdtp) {
+ free(fdtp);
+ fdtp = current_fdtp;
+ fdtp_size = current_fdtp_size;
+ }
+ free(overlay);
+}
+
+int
+fdt_setup_fdtp()
+{
+ struct preloaded_file *bfp;
+ vm_offset_t va;
+
+ debugf("fdt_setup_fdtp()\n");
+
+ /* If we already loaded a file, use it. */
+ if ((bfp = file_findfile(NULL, "dtb")) != NULL) {
+ if (fdt_load_dtb(bfp->f_addr) == 0) {
+ printf("Using DTB from loaded file '%s'.\n",
+ bfp->f_name);
+ return (0);
+ }
+ }
+
+ /* If we were given the address of a valid blob in memory, use it. */
+ if (fdt_to_load != NULL) {
+ if (fdt_load_dtb_addr(fdt_to_load) == 0) {
+ printf("Using DTB from memory address %p.\n",
+ fdt_to_load);
+ return (0);
+ }
+ }
+
+ if (fdt_platform_load_dtb() == 0)
+ return (0);
+
+ /* If there is a dtb compiled into the kernel, use it. */
+ if ((va = fdt_find_static_dtb()) != 0) {
+ if (fdt_load_dtb(va) == 0) {
+ printf("Using DTB compiled into kernel.\n");
+ return (0);
+ }
+ }
+
+ command_errmsg = "No device tree blob found!\n";
+ return (1);
+}
+
+#define fdt_strtovect(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
+ (cellbuf), (lim), (cellsize), 0);
+
+/* Force using base 16 */
+#define fdt_strtovectx(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
+ (cellbuf), (lim), (cellsize), 16);
+
+static int
+_fdt_strtovect(const char *str, void *cellbuf, int lim, unsigned char cellsize,
+ uint8_t base)
+{
+ const char *buf = str;
+ const char *end = str + strlen(str) - 2;
+ uint32_t *u32buf = NULL;
+ uint8_t *u8buf = NULL;
+ int cnt = 0;
+
+ if (cellsize == sizeof(uint32_t))
+ u32buf = (uint32_t *)cellbuf;
+ else
+ u8buf = (uint8_t *)cellbuf;
+
+ if (lim == 0)
+ return (0);
+
+ while (buf < end) {
+
+ /* Skip white whitespace(s)/separators */
+ while (!isxdigit(*buf) && buf < end)
+ buf++;
+
+ if (u32buf != NULL)
+ u32buf[cnt] =
+ cpu_to_fdt32((uint32_t)strtol(buf, NULL, base));
+
+ else
+ u8buf[cnt] = (uint8_t)strtol(buf, NULL, base);
+
+ if (cnt + 1 <= lim - 1)
+ cnt++;
+ else
+ break;
+ buf++;
+ /* Find another number */
+ while ((isxdigit(*buf) || *buf == 'x') && buf < end)
+ buf++;
+ }
+ return (cnt);
+}
+
+void
+fdt_fixup_ethernet(const char *str, char *ethstr, int len)
+{
+ uint8_t tmp_addr[6];
+
+ /* Convert macaddr string into a vector of uints */
+ fdt_strtovectx(str, &tmp_addr, 6, sizeof(uint8_t));
+ /* Set actual property to a value from vect */
+ fdt_setprop(fdtp, fdt_path_offset(fdtp, ethstr),
+ "local-mac-address", &tmp_addr, 6 * sizeof(uint8_t));
+}
+
+void
+fdt_fixup_cpubusfreqs(unsigned long cpufreq, unsigned long busfreq)
+{
+ int lo, o = 0, o2, maxo = 0, depth;
+ const uint32_t zero = 0;
+
+ /* We want to modify every subnode of /cpus */
+ o = fdt_path_offset(fdtp, "/cpus");
+ if (o < 0)
+ return;
+
+ /* maxo should contain offset of node next to /cpus */
+ depth = 0;
+ maxo = o;
+ while (depth != -1)
+ maxo = fdt_next_node(fdtp, maxo, &depth);
+
+ /* Find CPU frequency properties */
+ o = fdt_node_offset_by_prop_value(fdtp, o, "clock-frequency",
+ &zero, sizeof(uint32_t));
+
+ o2 = fdt_node_offset_by_prop_value(fdtp, o, "bus-frequency", &zero,
+ sizeof(uint32_t));
+
+ lo = MIN(o, o2);
+
+ while (o != -FDT_ERR_NOTFOUND && o2 != -FDT_ERR_NOTFOUND) {
+
+ o = fdt_node_offset_by_prop_value(fdtp, lo,
+ "clock-frequency", &zero, sizeof(uint32_t));
+
+ o2 = fdt_node_offset_by_prop_value(fdtp, lo, "bus-frequency",
+ &zero, sizeof(uint32_t));
+
+ /* We're only interested in /cpus subnode(s) */
+ if (lo > maxo)
+ break;
+
+ fdt_setprop_inplace_cell(fdtp, lo, "clock-frequency",
+ (uint32_t)cpufreq);
+
+ fdt_setprop_inplace_cell(fdtp, lo, "bus-frequency",
+ (uint32_t)busfreq);
+
+ lo = MIN(o, o2);
+ }
+}
+
+#ifdef notyet
+static int
+fdt_reg_valid(uint32_t *reg, int len, int addr_cells, int size_cells)
+{
+ int cells_in_tuple, i, tuples, tuple_size;
+ uint32_t cur_start, cur_size;
+
+ cells_in_tuple = (addr_cells + size_cells);
+ tuple_size = cells_in_tuple * sizeof(uint32_t);
+ tuples = len / tuple_size;
+ if (tuples == 0)
+ return (EINVAL);
+
+ for (i = 0; i < tuples; i++) {
+ if (addr_cells == 2)
+ cur_start = fdt64_to_cpu(reg[i * cells_in_tuple]);
+ else
+ cur_start = fdt32_to_cpu(reg[i * cells_in_tuple]);
+
+ if (size_cells == 2)
+ cur_size = fdt64_to_cpu(reg[i * cells_in_tuple + 2]);
+ else
+ cur_size = fdt32_to_cpu(reg[i * cells_in_tuple + 1]);
+
+ if (cur_size == 0)
+ return (EINVAL);
+
+ debugf(" reg#%d (start: 0x%0x size: 0x%0x) valid!\n",
+ i, cur_start, cur_size);
+ }
+ return (0);
+}
+#endif
+
+void
+fdt_fixup_memory(struct fdt_mem_region *region, size_t num)
+{
+ struct fdt_mem_region *curmr;
+ uint32_t addr_cells, size_cells;
+ uint32_t *addr_cellsp, *size_cellsp;
+ int err, i, len, memory, root;
+ size_t realmrno;
+ uint8_t *buf, *sb;
+ uint64_t rstart, rsize;
+ int reserved;
+
+ root = fdt_path_offset(fdtp, "/");
+ if (root < 0) {
+ sprintf(command_errbuf, "Could not find root node !");
+ return;
+ }
+
+ memory = fdt_path_offset(fdtp, "/memory");
+ if (memory <= 0) {
+ /* Create proper '/memory' node. */
+ memory = fdt_add_subnode(fdtp, root, "memory");
+ if (memory <= 0) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "Could not fixup '/memory' "
+ "node, error code : %d!\n", memory);
+ return;
+ }
+
+ err = fdt_setprop(fdtp, memory, "device_type", "memory",
+ sizeof("memory"));
+
+ if (err < 0)
+ return;
+ }
+
+ addr_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#address-cells",
+ NULL);
+ size_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#size-cells", NULL);
+
+ if (addr_cellsp == NULL || size_cellsp == NULL) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "Could not fixup '/memory' node : "
+ "%s %s property not found in root node!\n",
+ (!addr_cellsp) ? "#address-cells" : "",
+ (!size_cellsp) ? "#size-cells" : "");
+ return;
+ }
+
+ addr_cells = fdt32_to_cpu(*addr_cellsp);
+ size_cells = fdt32_to_cpu(*size_cellsp);
+
+ /*
+ * Convert memreserve data to memreserve property
+ * Check if property already exists
+ */
+ reserved = fdt_num_mem_rsv(fdtp);
+ if (reserved &&
+ (fdt_getprop(fdtp, root, "memreserve", NULL) == NULL)) {
+ len = (addr_cells + size_cells) * reserved * sizeof(uint32_t);
+ sb = buf = (uint8_t *)malloc(len);
+ if (!buf)
+ return;
+
+ bzero(buf, len);
+
+ for (i = 0; i < reserved; i++) {
+ if (fdt_get_mem_rsv(fdtp, i, &rstart, &rsize))
+ break;
+ if (rsize) {
+ /* Ensure endianness, and put cells into a buffer */
+ if (addr_cells == 2)
+ *(uint64_t *)buf =
+ cpu_to_fdt64(rstart);
+ else
+ *(uint32_t *)buf =
+ cpu_to_fdt32(rstart);
+
+ buf += sizeof(uint32_t) * addr_cells;
+ if (size_cells == 2)
+ *(uint64_t *)buf =
+ cpu_to_fdt64(rsize);
+ else
+ *(uint32_t *)buf =
+ cpu_to_fdt32(rsize);
+
+ buf += sizeof(uint32_t) * size_cells;
+ }
+ }
+
+ /* Set property */
+ if ((err = fdt_setprop(fdtp, root, "memreserve", sb, len)) < 0)
+ printf("Could not fixup 'memreserve' property.\n");
+
+ free(sb);
+ }
+
+ /* Count valid memory regions entries in sysinfo. */
+ realmrno = num;
+ for (i = 0; i < num; i++)
+ if (region[i].start == 0 && region[i].size == 0)
+ realmrno--;
+
+ if (realmrno == 0) {
+ sprintf(command_errbuf, "Could not fixup '/memory' node : "
+ "sysinfo doesn't contain valid memory regions info!\n");
+ return;
+ }
+
+ len = (addr_cells + size_cells) * realmrno * sizeof(uint32_t);
+ sb = buf = (uint8_t *)malloc(len);
+ if (!buf)
+ return;
+
+ bzero(buf, len);
+
+ for (i = 0; i < num; i++) {
+ curmr = &region[i];
+ if (curmr->size != 0) {
+ /* Ensure endianness, and put cells into a buffer */
+ if (addr_cells == 2)
+ *(uint64_t *)buf =
+ cpu_to_fdt64(curmr->start);
+ else
+ *(uint32_t *)buf =
+ cpu_to_fdt32(curmr->start);
+
+ buf += sizeof(uint32_t) * addr_cells;
+ if (size_cells == 2)
+ *(uint64_t *)buf =
+ cpu_to_fdt64(curmr->size);
+ else
+ *(uint32_t *)buf =
+ cpu_to_fdt32(curmr->size);
+
+ buf += sizeof(uint32_t) * size_cells;
+ }
+ }
+
+ /* Set property */
+ if ((err = fdt_setprop(fdtp, memory, "reg", sb, len)) < 0)
+ sprintf(command_errbuf, "Could not fixup '/memory' node.\n");
+
+ free(sb);
+}
+
+void
+fdt_fixup_stdout(const char *str)
+{
+ char *ptr;
+ int serialno;
+ int len, no, sero;
+ const struct fdt_property *prop;
+ char *tmp[10];
+
+ ptr = (char *)str + strlen(str) - 1;
+ while (ptr > str && isdigit(*(str - 1)))
+ str--;
+
+ if (ptr == str)
+ return;
+
+ serialno = (int)strtol(ptr, NULL, 0);
+ no = fdt_path_offset(fdtp, "/chosen");
+ if (no < 0)
+ return;
+
+ prop = fdt_get_property(fdtp, no, "stdout", &len);
+
+ /* If /chosen/stdout does not extist, create it */
+ if (prop == NULL || (prop != NULL && len == 0)) {
+
+ bzero(tmp, 10 * sizeof(char));
+ strcpy((char *)&tmp, "serial");
+ if (strlen(ptr) > 3)
+ /* Serial number too long */
+ return;
+
+ strncpy((char *)tmp + 6, ptr, 3);
+ sero = fdt_path_offset(fdtp, (const char *)tmp);
+ if (sero < 0)
+ /*
+ * If serial device we're trying to assign
+ * stdout to doesn't exist in DT -- return.
+ */
+ return;
+
+ fdt_setprop(fdtp, no, "stdout", &tmp,
+ strlen((char *)&tmp) + 1);
+ fdt_setprop(fdtp, no, "stdin", &tmp,
+ strlen((char *)&tmp) + 1);
+ }
+}
+
+void
+fdt_load_dtb_overlays(const char *extras)
+{
+ const char *s;
+
+ /* Any extra overlays supplied by pre-loader environment */
+ if (extras != NULL && *extras != '\0') {
+ printf("Loading DTB overlays: '%s'\n", extras);
+ fdt_load_dtb_overlays_string(extras);
+ }
+
+ /* Any overlays supplied by loader environment */
+ s = getenv("fdt_overlays");
+ if (s != NULL && *s != '\0') {
+ printf("Loading DTB overlays: '%s'\n", s);
+ fdt_load_dtb_overlays_string(s);
+ }
+}
+
+/*
+ * Locate the blob, fix it up and return its location.
+ */
+static int
+fdt_fixup(void)
+{
+ int chosen, len;
+
+ len = 0;
+
+ debugf("fdt_fixup()\n");
+
+ if (fdtp == NULL && fdt_setup_fdtp() != 0)
+ return (0);
+
+ /* Create /chosen node (if not exists) */
+ if ((chosen = fdt_subnode_offset(fdtp, 0, "chosen")) ==
+ -FDT_ERR_NOTFOUND)
+ chosen = fdt_add_subnode(fdtp, 0, "chosen");
+
+ /* Value assigned to fixup-applied does not matter. */
+ if (fdt_getprop(fdtp, chosen, "fixup-applied", NULL))
+ return (1);
+
+ fdt_platform_fixups();
+
+ fdt_setprop(fdtp, chosen, "fixup-applied", NULL, 0);
+ return (1);
+}
+
+/*
+ * Copy DTB blob to specified location and return size
+ */
+int
+fdt_copy(vm_offset_t va)
+{
+ int err;
+ debugf("fdt_copy va 0x%08x\n", va);
+ if (fdtp == NULL) {
+ err = fdt_setup_fdtp();
+ if (err) {
+ printf("No valid device tree blob found!\n");
+ return (0);
+ }
+ }
+
+ if (fdt_fixup() == 0)
+ return (0);
+
+ if (fdtp_va != 0) {
+ /* Overwrite the FDT with the fixed version. */
+ /* XXX Is this really appropriate? */
+ COPYIN(fdtp, fdtp_va, fdtp_size);
+ }
+ COPYIN(fdtp, va, fdtp_size);
+ return (fdtp_size);
+}
+
+
+
+int
+command_fdt_internal(int argc, char *argv[])
+{
+ cmdf_t *cmdh;
+ int flags;
+ char *cmd;
+ int i, err;
+
+ if (argc < 2) {
+ command_errmsg = "usage is 'fdt <command> [<args>]";
+ return (CMD_ERROR);
+ }
+
+ /*
+ * Validate fdt <command>.
+ */
+ cmd = strdup(argv[1]);
+ i = 0;
+ cmdh = NULL;
+ while (!(commands[i].name == NULL)) {
+ if (strcmp(cmd, commands[i].name) == 0) {
+ /* found it */
+ cmdh = commands[i].handler;
+ flags = commands[i].flags;
+ break;
+ }
+ i++;
+ }
+ if (cmdh == NULL) {
+ command_errmsg = "unknown command";
+ return (CMD_ERROR);
+ }
+
+ if (flags & CMD_REQUIRES_BLOB) {
+ /*
+ * Check if uboot env vars were parsed already. If not, do it now.
+ */
+ if (fdt_fixup() == 0)
+ return (CMD_ERROR);
+ }
+
+ /*
+ * Call command handler.
+ */
+ err = (*cmdh)(argc, argv);
+
+ return (err);
+}
+
+static int
+fdt_cmd_addr(int argc, char *argv[])
+{
+ struct preloaded_file *fp;
+ struct fdt_header *hdr;
+ const char *addr;
+ char *cp;
+
+ fdt_to_load = NULL;
+
+ if (argc > 2)
+ addr = argv[2];
+ else {
+ sprintf(command_errbuf, "no address specified");
+ return (CMD_ERROR);
+ }
+
+ hdr = (struct fdt_header *)strtoul(addr, &cp, 16);
+ if (cp == addr) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "Invalid address: %s", addr);
+ return (CMD_ERROR);
+ }
+
+ while ((fp = file_findfile(NULL, "dtb")) != NULL) {
+ file_discard(fp);
+ }
+
+ fdt_to_load = hdr;
+ return (CMD_OK);
+}
+
+static int
+fdt_cmd_cd(int argc, char *argv[])
+{
+ char *path;
+ char tmp[FDT_CWD_LEN];
+ int len, o;
+
+ path = (argc > 2) ? argv[2] : "/";
+
+ if (path[0] == '/') {
+ len = strlen(path);
+ if (len >= FDT_CWD_LEN)
+ goto fail;
+ } else {
+ /* Handle path specification relative to cwd */
+ len = strlen(cwd) + strlen(path) + 1;
+ if (len >= FDT_CWD_LEN)
+ goto fail;
+
+ strcpy(tmp, cwd);
+ strcat(tmp, "/");
+ strcat(tmp, path);
+ path = tmp;
+ }
+
+ o = fdt_path_offset(fdtp, path);
+ if (o < 0) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "could not find node: '%s'", path);
+ return (CMD_ERROR);
+ }
+
+ strcpy(cwd, path);
+ return (CMD_OK);
+
+fail:
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "path too long: %d, max allowed: %d", len, FDT_CWD_LEN - 1);
+ return (CMD_ERROR);
+}
+
+static int
+fdt_cmd_hdr(int argc __unused, char *argv[] __unused)
+{
+ char line[80];
+ int ver;
+
+ if (fdtp == NULL) {
+ command_errmsg = "no device tree blob pointer?!";
+ return (CMD_ERROR);
+ }
+
+ ver = fdt_version(fdtp);
+ pager_open();
+ sprintf(line, "\nFlattened device tree header (%p):\n", fdtp);
+ if (pager_output(line))
+ goto out;
+ sprintf(line, " magic = 0x%08x\n", fdt_magic(fdtp));
+ if (pager_output(line))
+ goto out;
+ sprintf(line, " size = %d\n", fdt_totalsize(fdtp));
+ if (pager_output(line))
+ goto out;
+ sprintf(line, " off_dt_struct = 0x%08x\n",
+ fdt_off_dt_struct(fdtp));
+ if (pager_output(line))
+ goto out;
+ sprintf(line, " off_dt_strings = 0x%08x\n",
+ fdt_off_dt_strings(fdtp));
+ if (pager_output(line))
+ goto out;
+ sprintf(line, " off_mem_rsvmap = 0x%08x\n",
+ fdt_off_mem_rsvmap(fdtp));
+ if (pager_output(line))
+ goto out;
+ sprintf(line, " version = %d\n", ver);
+ if (pager_output(line))
+ goto out;
+ sprintf(line, " last compatible version = %d\n",
+ fdt_last_comp_version(fdtp));
+ if (pager_output(line))
+ goto out;
+ if (ver >= 2) {
+ sprintf(line, " boot_cpuid = %d\n",
+ fdt_boot_cpuid_phys(fdtp));
+ if (pager_output(line))
+ goto out;
+ }
+ if (ver >= 3) {
+ sprintf(line, " size_dt_strings = %d\n",
+ fdt_size_dt_strings(fdtp));
+ if (pager_output(line))
+ goto out;
+ }
+ if (ver >= 17) {
+ sprintf(line, " size_dt_struct = %d\n",
+ fdt_size_dt_struct(fdtp));
+ if (pager_output(line))
+ goto out;
+ }
+out:
+ pager_close();
+
+ return (CMD_OK);
+}
+
+static int
+fdt_cmd_ls(int argc, char *argv[])
+{
+ const char *prevname[FDT_MAX_DEPTH] = { NULL };
+ const char *name;
+ char *path;
+ int i, o, depth;
+
+ path = (argc > 2) ? argv[2] : NULL;
+ if (path == NULL)
+ path = cwd;
+
+ o = fdt_path_offset(fdtp, path);
+ if (o < 0) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "could not find node: '%s'", path);
+ return (CMD_ERROR);
+ }
+
+ for (depth = 0;
+ (o >= 0) && (depth >= 0);
+ o = fdt_next_node(fdtp, o, &depth)) {
+
+ name = fdt_get_name(fdtp, o, NULL);
+
+ if (depth > FDT_MAX_DEPTH) {
+ printf("max depth exceeded: %d\n", depth);
+ continue;
+ }
+
+ prevname[depth] = name;
+
+ /* Skip root (i = 1) when printing devices */
+ for (i = 1; i <= depth; i++) {
+ if (prevname[i] == NULL)
+ break;
+
+ if (strcmp(cwd, "/") == 0)
+ printf("/");
+ printf("%s", prevname[i]);
+ }
+ printf("\n");
+ }
+
+ return (CMD_OK);
+}
+
+static __inline int
+isprint(int c)
+{
+
+ return (c >= ' ' && c <= 0x7e);
+}
+
+static int
+fdt_isprint(const void *data, int len, int *count)
+{
+ const char *d;
+ char ch;
+ int yesno, i;
+
+ if (len == 0)
+ return (0);
+
+ d = (const char *)data;
+ if (d[len - 1] != '\0')
+ return (0);
+
+ *count = 0;
+ yesno = 1;
+ for (i = 0; i < len; i++) {
+ ch = *(d + i);
+ if (isprint(ch) || (ch == '\0' && i > 0)) {
+ /* Count strings */
+ if (ch == '\0')
+ (*count)++;
+ continue;
+ }
+
+ yesno = 0;
+ break;
+ }
+
+ return (yesno);
+}
+
+static int
+fdt_data_str(const void *data, int len, int count, char **buf)
+{
+ char *b, *tmp;
+ const char *d;
+ int buf_len, i, l;
+
+ /*
+ * Calculate the length for the string and allocate memory.
+ *
+ * Note that 'len' already includes at least one terminator.
+ */
+ buf_len = len;
+ if (count > 1) {
+ /*
+ * Each token had already a terminator buried in 'len', but we
+ * only need one eventually, don't count space for these.
+ */
+ buf_len -= count - 1;
+
+ /* Each consecutive token requires a ", " separator. */
+ buf_len += count * 2;
+ }
+
+ /* Add some space for surrounding double quotes. */
+ buf_len += count * 2;
+
+ /* Note that string being put in 'tmp' may be as big as 'buf_len'. */
+ b = (char *)malloc(buf_len);
+ tmp = (char *)malloc(buf_len);
+ if (b == NULL)
+ goto error;
+
+ if (tmp == NULL) {
+ free(b);
+ goto error;
+ }
+
+ b[0] = '\0';
+
+ /*
+ * Now that we have space, format the string.
+ */
+ i = 0;
+ do {
+ d = (const char *)data + i;
+ l = strlen(d) + 1;
+
+ sprintf(tmp, "\"%s\"%s", d,
+ (i + l) < len ? ", " : "");
+ strcat(b, tmp);
+
+ i += l;
+
+ } while (i < len);
+ *buf = b;
+
+ free(tmp);
+
+ return (0);
+error:
+ return (1);
+}
+
+static int
+fdt_data_cell(const void *data, int len, char **buf)
+{
+ char *b, *tmp;
+ const uint32_t *c;
+ int count, i, l;
+
+ /* Number of cells */
+ count = len / 4;
+
+ /*
+ * Calculate the length for the string and allocate memory.
+ */
+
+ /* Each byte translates to 2 output characters */
+ l = len * 2;
+ if (count > 1) {
+ /* Each consecutive cell requires a " " separator. */
+ l += (count - 1) * 1;
+ }
+ /* Each cell will have a "0x" prefix */
+ l += count * 2;
+ /* Space for surrounding <> and terminator */
+ l += 3;
+
+ b = (char *)malloc(l);
+ tmp = (char *)malloc(l);
+ if (b == NULL)
+ goto error;
+
+ if (tmp == NULL) {
+ free(b);
+ goto error;
+ }
+
+ b[0] = '\0';
+ strcat(b, "<");
+
+ for (i = 0; i < len; i += 4) {
+ c = (const uint32_t *)((const uint8_t *)data + i);
+ sprintf(tmp, "0x%08x%s", fdt32_to_cpu(*c),
+ i < (len - 4) ? " " : "");
+ strcat(b, tmp);
+ }
+ strcat(b, ">");
+ *buf = b;
+
+ free(tmp);
+
+ return (0);
+error:
+ return (1);
+}
+
+static int
+fdt_data_bytes(const void *data, int len, char **buf)
+{
+ char *b, *tmp;
+ const char *d;
+ int i, l;
+
+ /*
+ * Calculate the length for the string and allocate memory.
+ */
+
+ /* Each byte translates to 2 output characters */
+ l = len * 2;
+ if (len > 1)
+ /* Each consecutive byte requires a " " separator. */
+ l += (len - 1) * 1;
+ /* Each byte will have a "0x" prefix */
+ l += len * 2;
+ /* Space for surrounding [] and terminator. */
+ l += 3;
+
+ b = (char *)malloc(l);
+ tmp = (char *)malloc(l);
+ if (b == NULL)
+ goto error;
+
+ if (tmp == NULL) {
+ free(b);
+ goto error;
+ }
+
+ b[0] = '\0';
+ strcat(b, "[");
+
+ for (i = 0, d = data; i < len; i++) {
+ sprintf(tmp, "0x%02x%s", d[i], i < len - 1 ? " " : "");
+ strcat(b, tmp);
+ }
+ strcat(b, "]");
+ *buf = b;
+
+ free(tmp);
+
+ return (0);
+error:
+ return (1);
+}
+
+static int
+fdt_data_fmt(const void *data, int len, char **buf)
+{
+ int count;
+
+ if (len == 0) {
+ *buf = NULL;
+ return (1);
+ }
+
+ if (fdt_isprint(data, len, &count))
+ return (fdt_data_str(data, len, count, buf));
+
+ else if ((len % 4) == 0)
+ return (fdt_data_cell(data, len, buf));
+
+ else
+ return (fdt_data_bytes(data, len, buf));
+}
+
+static int
+fdt_prop(int offset)
+{
+ char *line, *buf;
+ const struct fdt_property *prop;
+ const char *name;
+ const void *data;
+ int len, rv;
+
+ line = NULL;
+ prop = fdt_offset_ptr(fdtp, offset, sizeof(*prop));
+ if (prop == NULL)
+ return (1);
+
+ name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff));
+ len = fdt32_to_cpu(prop->len);
+
+ rv = 0;
+ buf = NULL;
+ if (len == 0) {
+ /* Property without value */
+ line = (char *)malloc(strlen(name) + 2);
+ if (line == NULL) {
+ rv = 2;
+ goto out2;
+ }
+ sprintf(line, "%s\n", name);
+ goto out1;
+ }
+
+ /*
+ * Process property with value
+ */
+ data = prop->data;
+
+ if (fdt_data_fmt(data, len, &buf) != 0) {
+ rv = 3;
+ goto out2;
+ }
+
+ line = (char *)malloc(strlen(name) + strlen(FDT_PROP_SEP) +
+ strlen(buf) + 2);
+ if (line == NULL) {
+ sprintf(command_errbuf, "could not allocate space for string");
+ rv = 4;
+ goto out2;
+ }
+
+ sprintf(line, "%s" FDT_PROP_SEP "%s\n", name, buf);
+
+out1:
+ pager_open();
+ pager_output(line);
+ pager_close();
+
+out2:
+ if (buf)
+ free(buf);
+
+ if (line)
+ free(line);
+
+ return (rv);
+}
+
+static int
+fdt_modprop(int nodeoff, char *propname, void *value, char mode)
+{
+ uint32_t cells[100];
+ const char *buf;
+ int len, rv;
+ const struct fdt_property *p;
+
+ p = fdt_get_property(fdtp, nodeoff, propname, NULL);
+
+ if (p != NULL) {
+ if (mode == 1) {
+ /* Adding inexistant value in mode 1 is forbidden */
+ sprintf(command_errbuf, "property already exists!");
+ return (CMD_ERROR);
+ }
+ } else if (mode == 0) {
+ sprintf(command_errbuf, "property does not exist!");
+ return (CMD_ERROR);
+ }
+ len = strlen(value);
+ rv = 0;
+ buf = value;
+
+ switch (*buf) {
+ case '&':
+ /* phandles */
+ break;
+ case '<':
+ /* Data cells */
+ len = fdt_strtovect(buf, (void *)&cells, 100,
+ sizeof(uint32_t));
+
+ rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
+ len * sizeof(uint32_t));
+ break;
+ case '[':
+ /* Data bytes */
+ len = fdt_strtovect(buf, (void *)&cells, 100,
+ sizeof(uint8_t));
+
+ rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
+ len * sizeof(uint8_t));
+ break;
+ case '"':
+ default:
+ /* Default -- string */
+ rv = fdt_setprop_string(fdtp, nodeoff, propname, value);
+ break;
+ }
+
+ if (rv != 0) {
+ if (rv == -FDT_ERR_NOSPACE)
+ sprintf(command_errbuf,
+ "Device tree blob is too small!\n");
+ else
+ sprintf(command_errbuf,
+ "Could not add/modify property!\n");
+ }
+ return (rv);
+}
+
+/* Merge strings from argv into a single string */
+static int
+fdt_merge_strings(int argc, char *argv[], int start, char **buffer)
+{
+ char *buf;
+ int i, idx, sz;
+
+ *buffer = NULL;
+ sz = 0;
+
+ for (i = start; i < argc; i++)
+ sz += strlen(argv[i]);
+
+ /* Additional bytes for whitespaces between args */
+ sz += argc - start;
+
+ buf = (char *)malloc(sizeof(char) * sz);
+ if (buf == NULL) {
+ sprintf(command_errbuf, "could not allocate space "
+ "for string");
+ return (1);
+ }
+ bzero(buf, sizeof(char) * sz);
+
+ idx = 0;
+ for (i = start, idx = 0; i < argc; i++) {
+ strcpy(buf + idx, argv[i]);
+ idx += strlen(argv[i]);
+ buf[idx] = ' ';
+ idx++;
+ }
+ buf[sz - 1] = '\0';
+ *buffer = buf;
+ return (0);
+}
+
+/* Extract offset and name of node/property from a given path */
+static int
+fdt_extract_nameloc(char **pathp, char **namep, int *nodeoff)
+{
+ int o;
+ char *path = *pathp, *name = NULL, *subpath = NULL;
+
+ subpath = strrchr(path, '/');
+ if (subpath == NULL) {
+ o = fdt_path_offset(fdtp, cwd);
+ name = path;
+ path = (char *)&cwd;
+ } else {
+ *subpath = '\0';
+ if (strlen(path) == 0)
+ path = cwd;
+
+ name = subpath + 1;
+ o = fdt_path_offset(fdtp, path);
+ }
+
+ if (strlen(name) == 0) {
+ sprintf(command_errbuf, "name not specified");
+ return (1);
+ }
+ if (o < 0) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "could not find node: '%s'", path);
+ return (1);
+ }
+ *namep = name;
+ *nodeoff = o;
+ *pathp = path;
+ return (0);
+}
+
+static int
+fdt_cmd_prop(int argc, char *argv[])
+{
+ char *path, *propname, *value;
+ int o, next, depth, rv;
+ uint32_t tag;
+
+ path = (argc > 2) ? argv[2] : NULL;
+
+ value = NULL;
+
+ if (argc > 3) {
+ /* Merge property value strings into one */
+ if (fdt_merge_strings(argc, argv, 3, &value) != 0)
+ return (CMD_ERROR);
+ } else
+ value = NULL;
+
+ if (path == NULL)
+ path = cwd;
+
+ rv = CMD_OK;
+
+ if (value) {
+ /* If value is specified -- try to modify prop. */
+ if (fdt_extract_nameloc(&path, &propname, &o) != 0)
+ return (CMD_ERROR);
+
+ rv = fdt_modprop(o, propname, value, 0);
+ if (rv)
+ return (CMD_ERROR);
+ return (CMD_OK);
+
+ }
+ /* User wants to display properties */
+ o = fdt_path_offset(fdtp, path);
+
+ if (o < 0) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "could not find node: '%s'", path);
+ rv = CMD_ERROR;
+ goto out;
+ }
+
+ depth = 0;
+ while (depth >= 0) {
+ tag = fdt_next_tag(fdtp, o, &next);
+ switch (tag) {
+ case FDT_NOP:
+ break;
+ case FDT_PROP:
+ if (depth > 1)
+ /* Don't process properties of nested nodes */
+ break;
+
+ if (fdt_prop(o) != 0) {
+ sprintf(command_errbuf, "could not process "
+ "property");
+ rv = CMD_ERROR;
+ goto out;
+ }
+ break;
+ case FDT_BEGIN_NODE:
+ depth++;
+ if (depth > FDT_MAX_DEPTH) {
+ printf("warning: nesting too deep: %d\n",
+ depth);
+ goto out;
+ }
+ break;
+ case FDT_END_NODE:
+ depth--;
+ if (depth == 0)
+ /*
+ * This is the end of our starting node, force
+ * the loop finish.
+ */
+ depth--;
+ break;
+ }
+ o = next;
+ }
+out:
+ return (rv);
+}
+
+static int
+fdt_cmd_mkprop(int argc, char *argv[])
+{
+ int o;
+ char *path, *propname, *value;
+
+ path = (argc > 2) ? argv[2] : NULL;
+
+ value = NULL;
+
+ if (argc > 3) {
+ /* Merge property value strings into one */
+ if (fdt_merge_strings(argc, argv, 3, &value) != 0)
+ return (CMD_ERROR);
+ } else
+ value = NULL;
+
+ if (fdt_extract_nameloc(&path, &propname, &o) != 0)
+ return (CMD_ERROR);
+
+ if (fdt_modprop(o, propname, value, 1))
+ return (CMD_ERROR);
+
+ return (CMD_OK);
+}
+
+static int
+fdt_cmd_rm(int argc, char *argv[])
+{
+ int o, rv;
+ char *path = NULL, *propname;
+
+ if (argc > 2)
+ path = argv[2];
+ else {
+ sprintf(command_errbuf, "no node/property name specified");
+ return (CMD_ERROR);
+ }
+
+ o = fdt_path_offset(fdtp, path);
+ if (o < 0) {
+ /* If node not found -- try to find & delete property */
+ if (fdt_extract_nameloc(&path, &propname, &o) != 0)
+ return (CMD_ERROR);
+
+ if ((rv = fdt_delprop(fdtp, o, propname)) != 0) {
+ snprintf(command_errbuf, sizeof(command_errbuf),
+ "could not delete %s\n",
+ (rv == -FDT_ERR_NOTFOUND) ?
+ "(property/node does not exist)" : "");
+ return (CMD_ERROR);
+
+ } else
+ return (CMD_OK);
+ }
+ /* If node exists -- remove node */
+ rv = fdt_del_node(fdtp, o);
+ if (rv) {
+ sprintf(command_errbuf, "could not delete node");
+ return (CMD_ERROR);
+ }
+ return (CMD_OK);
+}
+
+static int
+fdt_cmd_mknode(int argc, char *argv[])
+{
+ int o, rv;
+ char *path = NULL, *nodename = NULL;
+
+ if (argc > 2)
+ path = argv[2];
+ else {
+ sprintf(command_errbuf, "no node name specified");
+ return (CMD_ERROR);
+ }
+
+ if (fdt_extract_nameloc(&path, &nodename, &o) != 0)
+ return (CMD_ERROR);
+
+ rv = fdt_add_subnode(fdtp, o, nodename);
+
+ if (rv < 0) {
+ if (rv == -FDT_ERR_NOSPACE)
+ sprintf(command_errbuf,
+ "Device tree blob is too small!\n");
+ else
+ sprintf(command_errbuf,
+ "Could not add node!\n");
+ return (CMD_ERROR);
+ }
+ return (CMD_OK);
+}
+
+static int
+fdt_cmd_pwd(int argc, char *argv[])
+{
+ char line[FDT_CWD_LEN];
+
+ pager_open();
+ sprintf(line, "%s\n", cwd);
+ pager_output(line);
+ pager_close();
+ return (CMD_OK);
+}
+
+static int
+fdt_cmd_mres(int argc, char *argv[])
+{
+ uint64_t start, size;
+ int i, total;
+ char line[80];
+
+ pager_open();
+ total = fdt_num_mem_rsv(fdtp);
+ if (total > 0) {
+ if (pager_output("Reserved memory regions:\n"))
+ goto out;
+ for (i = 0; i < total; i++) {
+ fdt_get_mem_rsv(fdtp, i, &start, &size);
+ sprintf(line, "reg#%d: (start: 0x%jx, size: 0x%jx)\n",
+ i, start, size);
+ if (pager_output(line))
+ goto out;
+ }
+ } else
+ pager_output("No reserved memory regions\n");
+out:
+ pager_close();
+
+ return (CMD_OK);
+}
+
+static int
+fdt_cmd_nyi(int argc, char *argv[])
+{
+
+ printf("command not yet implemented\n");
+ return (CMD_ERROR);
+}
diff --git a/stand/fdt/fdt_platform.h b/stand/fdt/fdt_platform.h
new file mode 100644
index 0000000..8930340
--- /dev/null
+++ b/stand/fdt/fdt_platform.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2014 Andrew Turner <andrew@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef FDT_PLATFORM_H
+#define FDT_PLATFORM_H
+
+struct fdt_header;
+
+struct fdt_mem_region {
+ unsigned long start;
+ unsigned long size;
+};
+
+#define TMP_MAX_ETH 8
+
+int fdt_copy(vm_offset_t);
+void fdt_fixup_cpubusfreqs(unsigned long, unsigned long);
+void fdt_fixup_ethernet(const char *, char *, int);
+void fdt_fixup_memory(struct fdt_mem_region *, size_t);
+void fdt_fixup_stdout(const char *);
+void fdt_apply_overlays(void);
+int fdt_load_dtb_addr(struct fdt_header *);
+int fdt_load_dtb_file(const char *);
+void fdt_load_dtb_overlays(const char *);
+int fdt_setup_fdtp(void);
+
+/* The platform library needs to implement these functions */
+int fdt_platform_load_dtb(void);
+void fdt_platform_fixups(void);
+
+#endif /* FDT_PLATFORM_H */
diff --git a/stand/fdt/help.fdt b/stand/fdt/help.fdt
new file mode 100644
index 0000000..665f63c
--- /dev/null
+++ b/stand/fdt/help.fdt
@@ -0,0 +1,93 @@
+$FreeBSD$
+###############################################################################
+# Tfdt Dfdt manipulation commands
+
+ fdt <subcommand> <arguments>
+
+ Facilities for loading and manipulating device tree data.
+
+###############################################################################
+# Tfdt Saddr Dload fdt from an address in memory
+
+ fdt addr <address>
+
+ Copies compiled device tree from a particular location
+ in memory.
+
+###############################################################################
+# Tfdt Salias DXXX
+
+ fdt alias <address>
+
+ Not Yet Implemented
+
+###############################################################################
+# Tfdt Scd DSelect a particular node for future commands
+
+ fdt cd <path>
+
+ Changes the current node to the node specified by the path.
+ Path elements are separated by '/'; a leading '/' represents
+ the root node.
+
+###############################################################################
+# Tfdt Sheader DDump the header of the compiled device tree
+
+ fdt header
+
+ Dumps DTB size, format and other key values.
+
+###############################################################################
+# Tfdt Sls DList subnodes of the current node
+
+ fdt ls <path>
+
+ Lists the nodes under the specified path.
+ If no path is specified, lists nodes under the current path.
+
+###############################################################################
+# Tfdt Smknode DCreate a new node in the device tree
+
+ fdt mknode <name>
+
+ Creates a new node with the specified name.
+
+###############################################################################
+# Tfdt Smkprop DAdd a new property to the current node
+
+ fdt mkprop <name> <value> ...
+
+ Creates a new property with the specified name and values.
+ Multiple values can be specified and will be concatenated.
+
+###############################################################################
+# Tfdt Smres DXXX
+
+ fdt mres
+
+ Dumps the list of reserved memory regions.
+
+###############################################################################
+# Tfdt Sprop DDump value of a particular property
+
+ fdt prop <name> <value> ...
+
+ If value is specified, set the given property to the indicated value.
+ Otherwise, print the value of the property.
+
+###############################################################################
+# Tfdt Spwd DPrint path to current node in device tree
+
+ fdt pwd
+
+ Print path to the current node in the device tree.
+ The current node can be changed with "fdt cd".
+
+###############################################################################
+# Tfdt Srm DRemove node or property from device tree
+
+ fdt rm <name>
+
+ The named node or property will be removed from the device tree.
+
+###############################################################################
diff --git a/stand/ficl.mk b/stand/ficl.mk
new file mode 100644
index 0000000..a0a1320
--- /dev/null
+++ b/stand/ficl.mk
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+# Common flags to build FICL related files
+
+.include "defs.mk"
+
+.if ${MACHINE_CPUARCH} == "amd64" && ${DO32:U0} == 1
+FICL_CPUARCH= i386
+.elif ${MACHINE_ARCH:Mmips64*} != ""
+FICL_CPUARCH= mips64
+.else
+FICL_CPUARCH= ${MACHINE_CPUARCH}
+.endif
+
+.PATH: ${FICLSRC} ${FICLSRC}/${FICL_CPUARCH}
+
+.if ${MACHINE_CPUARCH} == "amd64" && ${DO32:U0} == 0
+CFLAGS+= -fPIC
+.endif
+
+CFLAGS+= -I${FICLSRC} -I${FICLSRC}/${FICL_CPUARCH} -I${LDRSRC}
+CFLAGS+= -DBOOT_FORTH
+CFLAGS+= -DBF_DICTSIZE=15000
diff --git a/stand/ficl/Makefile b/stand/ficl/Makefile
new file mode 100644
index 0000000..9a13614
--- /dev/null
+++ b/stand/ficl/Makefile
@@ -0,0 +1,32 @@
+# $FreeBSD$
+#
+
+.include <bsd.init.mk>
+.include "${BOOTSRC}/ficl.mk"
+
+BASE_SRCS= dict.c ficl.c fileaccess.c float.c loader.c math64.c \
+ prefix.c search.c stack.c tools.c vm.c words.c
+
+SRCS= ${BASE_SRCS} sysdep.c softcore.c
+CLEANFILES+= softcore.c testmain testmain.o
+
+.include <bsd.stand.mk>
+.ifmake testmain
+CFLAGS+= -DTESTMAIN -D_TESTMAIN
+SRCS+= testmain.c
+PROG= testmain
+.include <bsd.prog.mk>
+.else
+LIB= ficl
+INTERNALLIB=
+.include <bsd.lib.mk>
+.endif
+
+# Standard softwords
+.PATH: ${FICLSRC}/softwords
+SOFTWORDS= softcore.fr jhlocal.fr marker.fr freebsd.fr ficllocal.fr \
+ ifbrack.fr
+
+softcore.c: ${SOFTWORDS} softcore.awk
+ (cd ${FICLSRC}/softwords; cat ${SOFTWORDS} \
+ | awk -f softcore.awk -v datestamp="`LC_ALL=C date`") > ${.TARGET}
diff --git a/stand/ficl/Makefile.depend b/stand/ficl/Makefile.depend
new file mode 100644
index 0000000..c04b7c3
--- /dev/null
+++ b/stand/ficl/Makefile.depend
@@ -0,0 +1,15 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libstand \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/ficl/aarch64/sysdep.c b/stand/ficl/aarch64/sysdep.c
new file mode 100644
index 0000000..ad38660
--- /dev/null
+++ b/stand/ficl/aarch64/sysdep.c
@@ -0,0 +1,99 @@
+/*******************************************************************
+** s y s d e p . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Implementations of FICL external interface functions...
+**
+*******************************************************************/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include <stand.h>
+#endif
+#include "ficl.h"
+
+/*
+******************* FreeBSD P O R T B E G I N S H E R E ******************** Michael Smith
+*/
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ u_int64_t qx;
+
+ qx = (u_int64_t)x * (u_int64_t) y;
+
+ q.hi = (u_int32_t)( qx >> 32 );
+ q.lo = (u_int32_t)( qx & 0xFFFFFFFFL);
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ u_int64_t qx, qh;
+
+ qh = q.hi;
+ qx = (qh << 32) | q.lo;
+
+ result.quot = qx / y;
+ result.rem = qx % y;
+
+ return result;
+}
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ while(*msg != 0)
+ putchar(*(msg++));
+ if (fNewline)
+ putchar('\n');
+
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+void *ficlRealloc (void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
diff --git a/stand/ficl/aarch64/sysdep.h b/stand/ficl/aarch64/sysdep.h
new file mode 100644
index 0000000..3726b9e
--- /dev/null
+++ b/stand/ficl/aarch64/sysdep.h
@@ -0,0 +1,411 @@
+/*******************************************************************
+ s y s d e p . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Ficl system dependent types and prototypes...
+**
+** Note: Ficl also depends on the use of "assert" when
+** FICL_ROBUST is enabled. This may require some consideration
+** in firmware systems since assert often
+** assumes stderr/stdout.
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** 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.
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please send
+** contact me by email at the address above.
+**
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+** $FreeBSD$
+*/
+
+#if !defined (__SYSDEP_H__)
+#define __SYSDEP_H__
+
+#include <sys/types.h>
+
+#include <stddef.h> /* size_t, NULL */
+#include <setjmp.h>
+#include <assert.h>
+
+#if !defined IGNORE /* Macro to silence unused param warnings */
+#define IGNORE(x) (void)(x)
+#endif
+
+/*
+** TRUE and FALSE for C boolean operations, and
+** portable 32 bit types for CELLs
+**
+*/
+#if !defined TRUE
+#define TRUE 1
+#endif
+#if !defined FALSE
+#define FALSE 0
+#endif
+
+
+/*
+** System dependent data type declarations...
+*/
+#if !defined INT32
+#define INT32 int
+#endif
+
+#if !defined UNS32
+#define UNS32 unsigned int
+#endif
+
+#if !defined UNS16
+#define UNS16 unsigned short
+#endif
+
+#if !defined UNS8
+#define UNS8 unsigned char
+#endif
+
+#if !defined NULL
+#define NULL ((void *)0)
+#endif
+
+/*
+** FICL_UNS and FICL_INT must have the same size as a void* on
+** the target system. A CELL is a union of void*, FICL_UNS, and
+** FICL_INT.
+** (11/2000: same for FICL_FLOAT)
+*/
+#if !defined FICL_INT
+#define FICL_INT long
+#endif
+
+#if !defined FICL_UNS
+#define FICL_UNS unsigned long
+#endif
+
+#if !defined FICL_FLOAT
+#define FICL_FLOAT float
+#endif
+
+/*
+** Ficl presently supports values of 32 and 64 for BITS_PER_CELL
+*/
+#if !defined BITS_PER_CELL
+#define BITS_PER_CELL 64
+#endif
+
+#if ((BITS_PER_CELL != 32) && (BITS_PER_CELL != 64))
+ Error!
+#endif
+
+typedef struct
+{
+ FICL_UNS hi;
+ FICL_UNS lo;
+} DPUNS;
+
+typedef struct
+{
+ FICL_UNS quot;
+ FICL_UNS rem;
+} UNSQR;
+
+typedef struct
+{
+ FICL_INT hi;
+ FICL_INT lo;
+} DPINT;
+
+typedef struct
+{
+ FICL_INT quot;
+ FICL_INT rem;
+} INTQR;
+
+
+/*
+** B U I L D C O N T R O L S
+*/
+
+#if !defined (FICL_MINIMAL)
+#define FICL_MINIMAL 0
+#endif
+#if (FICL_MINIMAL)
+#define FICL_WANT_SOFTWORDS 0
+#define FICL_WANT_FLOAT 0
+#define FICL_WANT_USER 0
+#define FICL_WANT_LOCALS 0
+#define FICL_WANT_DEBUGGER 0
+#define FICL_WANT_OOP 0
+#define FICL_PLATFORM_EXTEND 0
+#define FICL_MULTITHREAD 0
+#define FICL_ROBUST 0
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_PLATFORM_EXTEND
+** Includes words defined in ficlCompilePlatform
+*/
+#if !defined (FICL_PLATFORM_EXTEND)
+#define FICL_PLATFORM_EXTEND 1
+#endif
+
+/*
+** FICL_WANT_FLOAT
+** Includes a floating point stack for the VM, and words to do float operations.
+** Contributed by Guy Carver
+*/
+#if !defined (FICL_WANT_FLOAT)
+#define FICL_WANT_FLOAT 0
+#endif
+
+/*
+** FICL_WANT_DEBUGGER
+** Inludes a simple source level debugger
+*/
+#if !defined (FICL_WANT_DEBUGGER)
+#define FICL_WANT_DEBUGGER 1
+#endif
+
+/*
+** User variables: per-instance variables bound to the VM.
+** Kinda like thread-local storage. Could be implemented in a
+** VM private dictionary, but I've chosen the lower overhead
+** approach of an array of CELLs instead.
+*/
+#if !defined FICL_WANT_USER
+#define FICL_WANT_USER 1
+#endif
+
+#if !defined FICL_USER_CELLS
+#define FICL_USER_CELLS 16
+#endif
+
+/*
+** FICL_WANT_LOCALS controls the creation of the LOCALS wordset and
+** a private dictionary for local variable compilation.
+*/
+#if !defined FICL_WANT_LOCALS
+#define FICL_WANT_LOCALS 1
+#endif
+
+/* Max number of local variables per definition */
+#if !defined FICL_MAX_LOCALS
+#define FICL_MAX_LOCALS 16
+#endif
+
+/*
+** FICL_WANT_OOP
+** Inludes object oriented programming support (in softwords)
+** OOP support requires locals and user variables!
+*/
+#if !(FICL_WANT_LOCALS) || !(FICL_WANT_USER)
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 0
+#endif
+#endif
+
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 1
+#endif
+
+/*
+** FICL_WANT_SOFTWORDS
+** Controls inclusion of all softwords in softcore.c
+*/
+#if !defined (FICL_WANT_SOFTWORDS)
+#define FICL_WANT_SOFTWORDS 1
+#endif
+
+/*
+** FICL_MULTITHREAD enables dictionary mutual exclusion
+** wia the ficlLockDictionary system dependent function.
+** Note: this implementation is experimental and poorly
+** tested. Further, it's unnecessary unless you really
+** intend to have multiple SESSIONS (poor choice of name
+** on my part) - that is, threads that modify the dictionary
+** at the same time.
+*/
+#if !defined FICL_MULTITHREAD
+#define FICL_MULTITHREAD 0
+#endif
+
+/*
+** PORTABLE_LONGMULDIV causes ficlLongMul and ficlLongDiv to be
+** defined in C in sysdep.c. Use this if you cannot easily
+** generate an inline asm definition
+*/
+#if !defined (PORTABLE_LONGMULDIV)
+#define PORTABLE_LONGMULDIV 0
+#endif
+
+/*
+** INLINE_INNER_LOOP causes the inner interpreter to be inline code
+** instead of a function call. This is mainly because MS VC++ 5
+** chokes with an internal compiler error on the function version.
+** in release mode. Sheesh.
+*/
+#if !defined INLINE_INNER_LOOP
+#if defined _DEBUG
+#define INLINE_INNER_LOOP 0
+#else
+#define INLINE_INNER_LOOP 1
+#endif
+#endif
+
+/*
+** FICL_ROBUST enables bounds checking of stacks and the dictionary.
+** This will detect stack over and underflows and dictionary overflows.
+** Any exceptional condition will result in an assertion failure.
+** (As generated by the ANSI assert macro)
+** FICL_ROBUST == 1 --> stack checking in the outer interpreter
+** FICL_ROBUST == 2 also enables checking in many primitives
+*/
+
+#if !defined FICL_ROBUST
+#define FICL_ROBUST 2
+#endif
+
+/*
+** FICL_DEFAULT_STACK Specifies the default size (in CELLs) of
+** a new virtual machine's stacks, unless overridden at
+** create time.
+*/
+#if !defined FICL_DEFAULT_STACK
+#define FICL_DEFAULT_STACK 128
+#endif
+
+/*
+** FICL_DEFAULT_DICT specifies the number of CELLs to allocate
+** for the system dictionary by default. The value
+** can be overridden at startup time as well.
+** FICL_DEFAULT_ENV specifies the number of cells to allot
+** for the environment-query dictionary.
+*/
+#if !defined FICL_DEFAULT_DICT
+#define FICL_DEFAULT_DICT 12288
+#endif
+
+#if !defined FICL_DEFAULT_ENV
+#define FICL_DEFAULT_ENV 260
+#endif
+
+/*
+** FICL_DEFAULT_VOCS specifies the maximum number of wordlists in
+** the dictionary search order. See Forth DPANS sec 16.3.3
+** (file://dpans16.htm#16.3.3)
+*/
+#if !defined FICL_DEFAULT_VOCS
+#define FICL_DEFAULT_VOCS 16
+#endif
+
+/*
+** FICL_MAX_PARSE_STEPS controls the size of an array in the FICL_SYSTEM structure
+** that stores pointers to parser extension functions. I would never expect to have
+** more than 8 of these, so that's the default limit. Too many of these functions
+** will probably exact a nasty performance penalty.
+*/
+#if !defined FICL_MAX_PARSE_STEPS
+#define FICL_MAX_PARSE_STEPS 8
+#endif
+
+/*
+** FICL_EXTENDED_PREFIX enables a bunch of extra prefixes in prefix.c and prefix.fr (if
+** included as part of softcore.c)
+*/
+#if !defined FICL_EXTENDED_PREFIX
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_ALIGN is the power of two to which the dictionary
+** pointer address must be aligned. This value is usually
+** either 1 or 2, depending on the memory architecture
+** of the target system; 2 is safe on any 16 or 32 bit
+** machine. 3 would be appropriate for a 64 bit machine.
+*/
+#if !defined FICL_ALIGN
+#define FICL_ALIGN 3
+#define FICL_ALIGN_ADD ((1 << FICL_ALIGN) - 1)
+#endif
+
+/*
+** System dependent routines --
+** edit the implementations in sysdep.c to be compatible
+** with your runtime environment...
+** ficlTextOut sends a NULL terminated string to the
+** default output device - used for system error messages
+** ficlMalloc and ficlFree have the same semantics as malloc and free
+** in standard C
+** ficlLongMul multiplies two UNS32s and returns a 64 bit unsigned
+** product
+** ficlLongDiv divides an UNS64 by an UNS32 and returns UNS32 quotient
+** and remainder
+*/
+struct vm;
+void ficlTextOut(struct vm *pVM, char *msg, int fNewline);
+void *ficlMalloc (size_t size);
+void ficlFree (void *p);
+void *ficlRealloc(void *p, size_t size);
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** must be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** before timeout (optional - could also block forever)
+**
+** NOTE: this function must be implemented with lock counting
+** semantics: nested calls must behave properly.
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock);
+#else
+#define ficlLockDictionary(x) 0 /* ignore */
+#endif
+
+/*
+** 64 bit integer math support routines: multiply two UNS32s
+** to get a 64 bit product, & divide the product by an UNS32
+** to get an UNS32 quotient and remainder. Much easier in asm
+** on a 32 bit CPU than in C, which usually doesn't support
+** the double length result (but it should).
+*/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y);
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y);
+
+#endif /*__SYSDEP_H__*/
diff --git a/stand/ficl/amd64/sysdep.c b/stand/ficl/amd64/sysdep.c
new file mode 100644
index 0000000..5957b71
--- /dev/null
+++ b/stand/ficl/amd64/sysdep.c
@@ -0,0 +1,99 @@
+/*******************************************************************
+** s y s d e p . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Implementations of FICL external interface functions...
+**
+*******************************************************************/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include <stand.h>
+#endif
+#include "ficl.h"
+
+/*
+******************* FreeBSD P O R T B E G I N S H E R E ******************** Michael Smith
+*/
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ u_int64_t qx;
+
+ qx = (u_int64_t)x * (u_int64_t) y;
+
+ q.hi = (u_int32_t)( qx >> 32 );
+ q.lo = (u_int32_t)( qx & 0xFFFFFFFFL);
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ u_int64_t qx, qh;
+
+ qh = q.hi;
+ qx = (qh << 32) | q.lo;
+
+ result.quot = qx / y;
+ result.rem = qx % y;
+
+ return result;
+}
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ while(*msg != 0)
+ putchar((unsigned char)*(msg++));
+ if (fNewline)
+ putchar('\n');
+
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+void *ficlRealloc (void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
diff --git a/stand/ficl/amd64/sysdep.h b/stand/ficl/amd64/sysdep.h
new file mode 100644
index 0000000..08bc0e1
--- /dev/null
+++ b/stand/ficl/amd64/sysdep.h
@@ -0,0 +1,434 @@
+/*******************************************************************
+ s y s d e p . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Ficl system dependent types and prototypes...
+**
+** Note: Ficl also depends on the use of "assert" when
+** FICL_ROBUST is enabled. This may require some consideration
+** in firmware systems since assert often
+** assumes stderr/stdout.
+** $Id: sysdep.h,v 1.11 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** 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.
+**
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+*/
+
+/* $FreeBSD$ */
+
+#if !defined (__SYSDEP_H__)
+#define __SYSDEP_H__
+
+#include <sys/types.h>
+
+#include <stddef.h> /* size_t, NULL */
+#include <setjmp.h>
+#include <assert.h>
+
+#if !defined IGNORE /* Macro to silence unused param warnings */
+#define IGNORE(x) &x
+#endif
+
+/*
+** TRUE and FALSE for C boolean operations, and
+** portable 32 bit types for CELLs
+**
+*/
+#if !defined TRUE
+#define TRUE 1
+#endif
+#if !defined FALSE
+#define FALSE 0
+#endif
+
+/*
+** System dependent data type declarations...
+*/
+#if !defined INT32
+#define INT32 int
+#endif
+
+#if !defined UNS32
+#define UNS32 unsigned int
+#endif
+
+#if !defined UNS16
+#define UNS16 unsigned short
+#endif
+
+#if !defined UNS8
+#define UNS8 unsigned char
+#endif
+
+#if !defined NULL
+#define NULL ((void *)0)
+#endif
+
+/*
+** FICL_UNS and FICL_INT must have the same size as a void* on
+** the target system. A CELL is a union of void*, FICL_UNS, and
+** FICL_INT.
+** (11/2000: same for FICL_FLOAT)
+*/
+#if !defined FICL_INT
+#define FICL_INT long
+#endif
+
+#if !defined FICL_UNS
+#define FICL_UNS unsigned long
+#endif
+
+#if !defined FICL_FLOAT
+#define FICL_FLOAT float
+#endif
+
+/*
+** Ficl presently supports values of 32 and 64 for BITS_PER_CELL
+*/
+#if !defined BITS_PER_CELL
+#define BITS_PER_CELL 64
+#endif
+
+#if ((BITS_PER_CELL != 32) && (BITS_PER_CELL != 64))
+ Error!
+#endif
+
+typedef struct
+{
+ FICL_UNS hi;
+ FICL_UNS lo;
+} DPUNS;
+
+typedef struct
+{
+ FICL_UNS quot;
+ FICL_UNS rem;
+} UNSQR;
+
+typedef struct
+{
+ FICL_INT hi;
+ FICL_INT lo;
+} DPINT;
+
+typedef struct
+{
+ FICL_INT quot;
+ FICL_INT rem;
+} INTQR;
+
+
+/*
+** B U I L D C O N T R O L S
+*/
+
+#if !defined (FICL_MINIMAL)
+#define FICL_MINIMAL 0
+#endif
+#if (FICL_MINIMAL)
+#define FICL_WANT_SOFTWORDS 0
+#define FICL_WANT_FILE 0
+#define FICL_WANT_FLOAT 0
+#define FICL_WANT_USER 0
+#define FICL_WANT_LOCALS 0
+#define FICL_WANT_DEBUGGER 0
+#define FICL_WANT_OOP 0
+#define FICL_PLATFORM_EXTEND 0
+#define FICL_MULTITHREAD 0
+#define FICL_ROBUST 0
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_PLATFORM_EXTEND
+** Includes words defined in ficlCompilePlatform
+*/
+#if !defined (FICL_PLATFORM_EXTEND)
+#define FICL_PLATFORM_EXTEND 1
+#endif
+
+
+/*
+** FICL_WANT_FILE
+** Includes the FILE and FILE-EXT wordset and associated code. Turn this off if you do not
+** have a filesystem!
+** Contributed by Larry Hastings
+*/
+#if !defined (FICL_WANT_FILE)
+#define FICL_WANT_FILE 0
+#endif
+
+/*
+** FICL_WANT_FLOAT
+** Includes a floating point stack for the VM, and words to do float operations.
+** Contributed by Guy Carver
+*/
+#if !defined (FICL_WANT_FLOAT)
+#define FICL_WANT_FLOAT 0
+#endif
+
+/*
+** FICL_WANT_DEBUGGER
+** Inludes a simple source level debugger
+*/
+#if !defined (FICL_WANT_DEBUGGER)
+#define FICL_WANT_DEBUGGER 1
+#endif
+
+/*
+** FICL_EXTENDED_PREFIX enables a bunch of extra prefixes in prefix.c and prefix.fr (if
+** included as part of softcore.c)
+*/
+#if !defined FICL_EXTENDED_PREFIX
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** User variables: per-instance variables bound to the VM.
+** Kinda like thread-local storage. Could be implemented in a
+** VM private dictionary, but I've chosen the lower overhead
+** approach of an array of CELLs instead.
+*/
+#if !defined FICL_WANT_USER
+#define FICL_WANT_USER 1
+#endif
+
+#if !defined FICL_USER_CELLS
+#define FICL_USER_CELLS 16
+#endif
+
+/*
+** FICL_WANT_LOCALS controls the creation of the LOCALS wordset and
+** a private dictionary for local variable compilation.
+*/
+#if !defined FICL_WANT_LOCALS
+#define FICL_WANT_LOCALS 1
+#endif
+
+/* Max number of local variables per definition */
+#if !defined FICL_MAX_LOCALS
+#define FICL_MAX_LOCALS 16
+#endif
+
+/*
+** FICL_WANT_OOP
+** Inludes object oriented programming support (in softwords)
+** OOP support requires locals and user variables!
+*/
+#if !(FICL_WANT_LOCALS) || !(FICL_WANT_USER)
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 0
+#endif
+#endif
+
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 1
+#endif
+
+/*
+** FICL_WANT_SOFTWORDS
+** Controls inclusion of all softwords in softcore.c
+*/
+#if !defined (FICL_WANT_SOFTWORDS)
+#define FICL_WANT_SOFTWORDS 1
+#endif
+
+/*
+** FICL_MULTITHREAD enables dictionary mutual exclusion
+** wia the ficlLockDictionary system dependent function.
+** Note: this implementation is experimental and poorly
+** tested. Further, it's unnecessary unless you really
+** intend to have multiple SESSIONS (poor choice of name
+** on my part) - that is, threads that modify the dictionary
+** at the same time.
+*/
+#if !defined FICL_MULTITHREAD
+#define FICL_MULTITHREAD 0
+#endif
+
+/*
+** PORTABLE_LONGMULDIV causes ficlLongMul and ficlLongDiv to be
+** defined in C in sysdep.c. Use this if you cannot easily
+** generate an inline asm definition
+*/
+#if !defined (PORTABLE_LONGMULDIV)
+#define PORTABLE_LONGMULDIV 0
+#endif
+
+/*
+** INLINE_INNER_LOOP causes the inner interpreter to be inline code
+** instead of a function call. This is mainly because MS VC++ 5
+** chokes with an internal compiler error on the function version.
+** in release mode. Sheesh.
+*/
+#if !defined INLINE_INNER_LOOP
+#if defined _DEBUG
+#define INLINE_INNER_LOOP 0
+#else
+#define INLINE_INNER_LOOP 1
+#endif
+#endif
+
+/*
+** FICL_ROBUST enables bounds checking of stacks and the dictionary.
+** This will detect stack over and underflows and dictionary overflows.
+** Any exceptional condition will result in an assertion failure.
+** (As generated by the ANSI assert macro)
+** FICL_ROBUST == 1 --> stack checking in the outer interpreter
+** FICL_ROBUST == 2 also enables checking in many primitives
+*/
+
+#if !defined FICL_ROBUST
+#define FICL_ROBUST 2
+#endif
+
+/*
+** FICL_DEFAULT_STACK Specifies the default size (in CELLs) of
+** a new virtual machine's stacks, unless overridden at
+** create time.
+*/
+#if !defined FICL_DEFAULT_STACK
+#define FICL_DEFAULT_STACK 128
+#endif
+
+/*
+** FICL_DEFAULT_DICT specifies the number of CELLs to allocate
+** for the system dictionary by default. The value
+** can be overridden at startup time as well.
+** FICL_DEFAULT_ENV specifies the number of cells to allot
+** for the environment-query dictionary.
+*/
+#if !defined FICL_DEFAULT_DICT
+#define FICL_DEFAULT_DICT 12288
+#endif
+
+#if !defined FICL_DEFAULT_ENV
+#define FICL_DEFAULT_ENV 260
+#endif
+
+/*
+** FICL_DEFAULT_VOCS specifies the maximum number of wordlists in
+** the dictionary search order. See Forth DPANS sec 16.3.3
+** (file://dpans16.htm#16.3.3)
+*/
+#if !defined FICL_DEFAULT_VOCS
+#define FICL_DEFAULT_VOCS 16
+#endif
+
+/*
+** FICL_MAX_PARSE_STEPS controls the size of an array in the FICL_SYSTEM structure
+** that stores pointers to parser extension functions. I would never expect to have
+** more than 8 of these, so that's the default limit. Too many of these functions
+** will probably exact a nasty performance penalty.
+*/
+#if !defined FICL_MAX_PARSE_STEPS
+#define FICL_MAX_PARSE_STEPS 8
+#endif
+
+/*
+** FICL_ALIGN is the power of two to which the dictionary
+** pointer address must be aligned. This value is usually
+** either 1 or 2, depending on the memory architecture
+** of the target system; 2 is safe on any 16 or 32 bit
+** machine. 3 would be appropriate for a 64 bit machine.
+*/
+#if !defined FICL_ALIGN
+#define FICL_ALIGN 3
+#define FICL_ALIGN_ADD ((1 << FICL_ALIGN) - 1)
+#endif
+
+/*
+** System dependent routines --
+** edit the implementations in sysdep.c to be compatible
+** with your runtime environment...
+** ficlTextOut sends a NULL terminated string to the
+** default output device - used for system error messages
+** ficlMalloc and ficlFree have the same semantics as malloc and free
+** in standard C
+** ficlLongMul multiplies two UNS32s and returns a 64 bit unsigned
+** product
+** ficlLongDiv divides an UNS64 by an UNS32 and returns UNS32 quotient
+** and remainder
+*/
+struct vm;
+void ficlTextOut(struct vm *pVM, char *msg, int fNewline);
+void *ficlMalloc (size_t size);
+void ficlFree (void *p);
+void *ficlRealloc(void *p, size_t size);
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** must be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** before timeout (optional - could also block forever)
+**
+** NOTE: this function must be implemented with lock counting
+** semantics: nested calls must behave properly.
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock);
+#else
+#define ficlLockDictionary(x) 0 /* ignore */
+#endif
+
+/*
+** 64 bit integer math support routines: multiply two UNS32s
+** to get a 64 bit product, & divide the product by an UNS32
+** to get an UNS32 quotient and remainder. Much easier in asm
+** on a 32 bit CPU than in C, which usually doesn't support
+** the double length result (but it should).
+*/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y);
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y);
+
+
+/*
+** FICL_HAVE_FTRUNCATE indicates whether the current OS supports
+** the ftruncate() function (available on most UNIXes). This
+** function is necessary to provide the complete File-Access wordset.
+*/
+#if !defined (FICL_HAVE_FTRUNCATE)
+#define FICL_HAVE_FTRUNCATE 0
+#endif
+
+
+#endif /*__SYSDEP_H__*/
diff --git a/stand/ficl/arm/sysdep.c b/stand/ficl/arm/sysdep.c
new file mode 100644
index 0000000..ad38660
--- /dev/null
+++ b/stand/ficl/arm/sysdep.c
@@ -0,0 +1,99 @@
+/*******************************************************************
+** s y s d e p . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Implementations of FICL external interface functions...
+**
+*******************************************************************/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include <stand.h>
+#endif
+#include "ficl.h"
+
+/*
+******************* FreeBSD P O R T B E G I N S H E R E ******************** Michael Smith
+*/
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ u_int64_t qx;
+
+ qx = (u_int64_t)x * (u_int64_t) y;
+
+ q.hi = (u_int32_t)( qx >> 32 );
+ q.lo = (u_int32_t)( qx & 0xFFFFFFFFL);
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ u_int64_t qx, qh;
+
+ qh = q.hi;
+ qx = (qh << 32) | q.lo;
+
+ result.quot = qx / y;
+ result.rem = qx % y;
+
+ return result;
+}
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ while(*msg != 0)
+ putchar(*(msg++));
+ if (fNewline)
+ putchar('\n');
+
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+void *ficlRealloc (void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
diff --git a/stand/ficl/arm/sysdep.h b/stand/ficl/arm/sysdep.h
new file mode 100644
index 0000000..00718ee
--- /dev/null
+++ b/stand/ficl/arm/sysdep.h
@@ -0,0 +1,432 @@
+/*******************************************************************
+ s y s d e p . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Ficl system dependent types and prototypes...
+**
+** Note: Ficl also depends on the use of "assert" when
+** FICL_ROBUST is enabled. This may require some consideration
+** in firmware systems since assert often
+** assumes stderr/stdout.
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** 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.
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please send
+** contact me by email at the address above.
+**
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+** $FreeBSD$
+*/
+
+#if !defined (__SYSDEP_H__)
+#define __SYSDEP_H__
+
+#include <sys/types.h>
+
+#include <stddef.h> /* size_t, NULL */
+#include <setjmp.h>
+#include <assert.h>
+
+#if !defined IGNORE /* Macro to silence unused param warnings */
+#define IGNORE(x) (void)(x)
+#endif
+
+/*
+** TRUE and FALSE for C boolean operations, and
+** portable 32 bit types for CELLs
+**
+*/
+#if !defined TRUE
+#define TRUE 1
+#endif
+#if !defined FALSE
+#define FALSE 0
+#endif
+
+
+/*
+** System dependent data type declarations...
+*/
+#if !defined INT32
+#define INT32 int
+#endif
+
+#if !defined UNS32
+#define UNS32 unsigned int
+#endif
+
+#if !defined UNS16
+#define UNS16 unsigned short
+#endif
+
+#if !defined UNS8
+#define UNS8 unsigned char
+#endif
+
+#if !defined NULL
+#define NULL ((void *)0)
+#endif
+
+/*
+** FICL_UNS and FICL_INT must have the same size as a void* on
+** the target system. A CELL is a union of void*, FICL_UNS, and
+** FICL_INT.
+** (11/2000: same for FICL_FLOAT)
+*/
+#if !defined FICL_INT
+#define FICL_INT INT32
+#endif
+
+#if !defined FICL_UNS
+#define FICL_UNS UNS32
+#endif
+
+#if !defined FICL_FLOAT
+#define FICL_FLOAT float
+#endif
+
+/*
+** Ficl presently supports values of 32 and 64 for BITS_PER_CELL
+*/
+#if !defined BITS_PER_CELL
+#define BITS_PER_CELL 32
+#endif
+
+#if ((BITS_PER_CELL != 32) && (BITS_PER_CELL != 64))
+ Error!
+#endif
+
+typedef struct
+{
+ FICL_UNS hi;
+ FICL_UNS lo;
+} DPUNS;
+
+typedef struct
+{
+ FICL_UNS quot;
+ FICL_UNS rem;
+} UNSQR;
+
+typedef struct
+{
+ FICL_INT hi;
+ FICL_INT lo;
+} DPINT;
+
+typedef struct
+{
+ FICL_INT quot;
+ FICL_INT rem;
+} INTQR;
+
+
+/*
+** B U I L D C O N T R O L S
+*/
+
+#if !defined (FICL_MINIMAL)
+#define FICL_MINIMAL 0
+#endif
+#if (FICL_MINIMAL)
+#define FICL_WANT_SOFTWORDS 0
+#define FICL_WANT_FILE 0
+#define FICL_WANT_FLOAT 0
+#define FICL_WANT_USER 0
+#define FICL_WANT_LOCALS 0
+#define FICL_WANT_DEBUGGER 0
+#define FICL_WANT_OOP 0
+#define FICL_PLATFORM_EXTEND 0
+#define FICL_MULTITHREAD 0
+#define FICL_ROBUST 1
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_PLATFORM_EXTEND
+** Includes words defined in ficlCompilePlatform
+*/
+#if !defined (FICL_PLATFORM_EXTEND)
+#define FICL_PLATFORM_EXTEND 1
+#endif
+
+/*
+** FICL_WANT_FILE
+** Includes the FILE and FILE-EXT wordset and associated code. Turn this off if you do not
+** have a filesystem!
+** Contributed by Larry Hastings
+*/
+#if !defined (FICL_WANT_FILE)
+#define FICL_WANT_FILE 0
+#endif
+
+/*
+** FICL_WANT_FLOAT
+** Includes a floating point stack for the VM, and words to do float operations.
+** Contributed by Guy Carver
+*/
+#if !defined (FICL_WANT_FLOAT)
+#define FICL_WANT_FLOAT 0
+#endif
+
+/*
+** FICL_WANT_DEBUGGER
+** Inludes a simple source level debugger
+*/
+#if !defined (FICL_WANT_DEBUGGER)
+#define FICL_WANT_DEBUGGER 1
+#endif
+
+/*
+** User variables: per-instance variables bound to the VM.
+** Kinda like thread-local storage. Could be implemented in a
+** VM private dictionary, but I've chosen the lower overhead
+** approach of an array of CELLs instead.
+*/
+#if !defined FICL_WANT_USER
+#define FICL_WANT_USER 1
+#endif
+
+#if !defined FICL_USER_CELLS
+#define FICL_USER_CELLS 16
+#endif
+
+/*
+** FICL_WANT_LOCALS controls the creation of the LOCALS wordset and
+** a private dictionary for local variable compilation.
+*/
+#if !defined FICL_WANT_LOCALS
+#define FICL_WANT_LOCALS 1
+#endif
+
+/* Max number of local variables per definition */
+#if !defined FICL_MAX_LOCALS
+#define FICL_MAX_LOCALS 16
+#endif
+
+/*
+** FICL_WANT_OOP
+** Inludes object oriented programming support (in softwords)
+** OOP support requires locals and user variables!
+*/
+#if !(FICL_WANT_LOCALS) || !(FICL_WANT_USER)
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 0
+#endif
+#endif
+
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 1
+#endif
+
+/*
+** FICL_WANT_SOFTWORDS
+** Controls inclusion of all softwords in softcore.c
+*/
+#if !defined (FICL_WANT_SOFTWORDS)
+#define FICL_WANT_SOFTWORDS 1
+#endif
+
+/*
+** FICL_MULTITHREAD enables dictionary mutual exclusion
+** wia the ficlLockDictionary system dependent function.
+** Note: this implementation is experimental and poorly
+** tested. Further, it's unnecessary unless you really
+** intend to have multiple SESSIONS (poor choice of name
+** on my part) - that is, threads that modify the dictionary
+** at the same time.
+*/
+#if !defined FICL_MULTITHREAD
+#define FICL_MULTITHREAD 0
+#endif
+
+/*
+** PORTABLE_LONGMULDIV causes ficlLongMul and ficlLongDiv to be
+** defined in C in sysdep.c. Use this if you cannot easily
+** generate an inline asm definition
+*/
+#if !defined (PORTABLE_LONGMULDIV)
+#define PORTABLE_LONGMULDIV 0
+#endif
+
+/*
+** INLINE_INNER_LOOP causes the inner interpreter to be inline code
+** instead of a function call. This is mainly because MS VC++ 5
+** chokes with an internal compiler error on the function version.
+** in release mode. Sheesh.
+*/
+#if !defined INLINE_INNER_LOOP
+#if defined _DEBUG
+#define INLINE_INNER_LOOP 0
+#else
+#define INLINE_INNER_LOOP 1
+#endif
+#endif
+
+/*
+** FICL_ROBUST enables bounds checking of stacks and the dictionary.
+** This will detect stack over and underflows and dictionary overflows.
+** Any exceptional condition will result in an assertion failure.
+** (As generated by the ANSI assert macro)
+** FICL_ROBUST == 1 --> stack checking in the outer interpreter
+** FICL_ROBUST == 2 also enables checking in many primitives
+*/
+
+#if !defined FICL_ROBUST
+#define FICL_ROBUST 2
+#endif
+
+/*
+** FICL_DEFAULT_STACK Specifies the default size (in CELLs) of
+** a new virtual machine's stacks, unless overridden at
+** create time.
+*/
+#if !defined FICL_DEFAULT_STACK
+#define FICL_DEFAULT_STACK 128
+#endif
+
+/*
+** FICL_DEFAULT_DICT specifies the number of CELLs to allocate
+** for the system dictionary by default. The value
+** can be overridden at startup time as well.
+** FICL_DEFAULT_ENV specifies the number of cells to allot
+** for the environment-query dictionary.
+*/
+#if !defined FICL_DEFAULT_DICT
+#define FICL_DEFAULT_DICT 12288
+#endif
+
+#if !defined FICL_DEFAULT_ENV
+#define FICL_DEFAULT_ENV 260
+#endif
+
+/*
+** FICL_DEFAULT_VOCS specifies the maximum number of wordlists in
+** the dictionary search order. See Forth DPANS sec 16.3.3
+** (file://dpans16.htm#16.3.3)
+*/
+#if !defined FICL_DEFAULT_VOCS
+#define FICL_DEFAULT_VOCS 16
+#endif
+
+/*
+** FICL_MAX_PARSE_STEPS controls the size of an array in the FICL_SYSTEM structure
+** that stores pointers to parser extension functions. I would never expect to have
+** more than 8 of these, so that's the default limit. Too many of these functions
+** will probably exact a nasty performance penalty.
+*/
+#if !defined FICL_MAX_PARSE_STEPS
+#define FICL_MAX_PARSE_STEPS 8
+#endif
+
+/*
+** FICL_EXTENDED_PREFIX enables a bunch of extra prefixes in prefix.c and prefix.fr (if
+** included as part of softcore.c)
+*/
+#if !defined FICL_EXTENDED_PREFIX
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_ALIGN is the power of two to which the dictionary
+** pointer address must be aligned. This value is usually
+** either 1 or 2, depending on the memory architecture
+** of the target system; 2 is safe on any 16 or 32 bit
+** machine. 3 would be appropriate for a 64 bit machine.
+*/
+#if !defined FICL_ALIGN
+#define FICL_ALIGN 2
+#define FICL_ALIGN_ADD ((1 << FICL_ALIGN) - 1)
+#endif
+
+/*
+** System dependent routines --
+** edit the implementations in sysdep.c to be compatible
+** with your runtime environment...
+** ficlTextOut sends a NULL terminated string to the
+** default output device - used for system error messages
+** ficlMalloc and ficlFree have the same semantics as malloc and free
+** in standard C
+** ficlLongMul multiplies two UNS32s and returns a 64 bit unsigned
+** product
+** ficlLongDiv divides an UNS64 by an UNS32 and returns UNS32 quotient
+** and remainder
+*/
+struct vm;
+void ficlTextOut(struct vm *pVM, char *msg, int fNewline);
+void *ficlMalloc (size_t size);
+void ficlFree (void *p);
+void *ficlRealloc(void *p, size_t size);
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** must be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** before timeout (optional - could also block forever)
+**
+** NOTE: this function must be implemented with lock counting
+** semantics: nested calls must behave properly.
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock);
+#else
+#define ficlLockDictionary(x) /* ignore */
+#endif
+
+/*
+** 64 bit integer math support routines: multiply two UNS32s
+** to get a 64 bit product, & divide the product by an UNS32
+** to get an UNS32 quotient and remainder. Much easier in asm
+** on a 32 bit CPU than in C, which usually doesn't support
+** the double length result (but it should).
+*/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y);
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y);
+
+/*
+** FICL_HAVE_FTRUNCATE indicates whether the current OS supports
+** the ftruncate() function (available on most UNIXes). This
+** function is necessary to provide the complete File-Access wordset.
+*/
+#if !defined (FICL_HAVE_FTRUNCATE)
+#define FICL_HAVE_FTRUNCATE 0
+#endif
+
+
+#endif /*__SYSDEP_H__*/
diff --git a/stand/ficl/dict.c b/stand/ficl/dict.c
new file mode 100644
index 0000000..b76d925
--- /dev/null
+++ b/stand/ficl/dict.c
@@ -0,0 +1,864 @@
+/*******************************************************************
+** d i c t . c
+** Forth Inspired Command Language - dictionary methods
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 19 July 1997
+** $Id: dict.c,v 1.14 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** This file implements the dictionary -- FICL's model of
+** memory management. All FICL words are stored in the
+** dictionary. A word is a named chunk of data with its
+** associated code. FICL treats all words the same, even
+** precompiled ones, so your words become first-class
+** extensions of the language. You can even define new
+** control structures.
+**
+** 29 jun 1998 (sadler) added variable sized hash table support
+*/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdio.h>
+#include <ctype.h>
+#else
+#include <stand.h>
+#endif
+#include <string.h>
+#include "ficl.h"
+
+/* Dictionary on-demand resizing control variables */
+CELL dictThreshold;
+CELL dictIncrease;
+
+
+static char *dictCopyName(FICL_DICT *pDict, STRINGINFO si);
+
+/**************************************************************************
+ d i c t A b o r t D e f i n i t i o n
+** Abort a definition in process: reclaim its memory and unlink it
+** from the dictionary list. Assumes that there is a smudged
+** definition in process...otherwise does nothing.
+** NOTE: this function is not smart enough to unlink a word that
+** has been successfully defined (ie linked into a hash). It
+** only works for defs in process. If the def has been unsmudged,
+** nothing happens.
+**************************************************************************/
+void dictAbortDefinition(FICL_DICT *pDict)
+{
+ FICL_WORD *pFW;
+ ficlLockDictionary(TRUE);
+ pFW = pDict->smudge;
+
+ if (pFW->flags & FW_SMUDGE)
+ pDict->here = (CELL *)pFW->name;
+
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ a l i g n P t r
+** Aligns the given pointer to FICL_ALIGN address units.
+** Returns the aligned pointer value.
+**************************************************************************/
+void *alignPtr(void *ptr)
+{
+#if FICL_ALIGN > 0
+ char *cp;
+ CELL c;
+ cp = (char *)ptr + FICL_ALIGN_ADD;
+ c.p = (void *)cp;
+ c.u = c.u & (~FICL_ALIGN_ADD);
+ ptr = (CELL *)c.p;
+#endif
+ return ptr;
+}
+
+
+/**************************************************************************
+ d i c t A l i g n
+** Align the dictionary's free space pointer
+**************************************************************************/
+void dictAlign(FICL_DICT *pDict)
+{
+ pDict->here = alignPtr(pDict->here);
+}
+
+
+/**************************************************************************
+ d i c t A l l o t
+** Allocate or remove n chars of dictionary space, with
+** checks for underrun and overrun
+**************************************************************************/
+int dictAllot(FICL_DICT *pDict, int n)
+{
+ char *cp = (char *)pDict->here;
+#if FICL_ROBUST
+ if (n > 0)
+ {
+ if ((unsigned)n <= dictCellsAvail(pDict) * sizeof (CELL))
+ cp += n;
+ else
+ return 1; /* dict is full */
+ }
+ else
+ {
+ n = -n;
+ if ((unsigned)n <= dictCellsUsed(pDict) * sizeof (CELL))
+ cp -= n;
+ else /* prevent underflow */
+ cp -= dictCellsUsed(pDict) * sizeof (CELL);
+ }
+#else
+ cp += n;
+#endif
+ pDict->here = PTRtoCELL cp;
+ return 0;
+}
+
+
+/**************************************************************************
+ d i c t A l l o t C e l l s
+** Reserve space for the requested number of cells in the
+** dictionary. If nCells < 0 , removes space from the dictionary.
+**************************************************************************/
+int dictAllotCells(FICL_DICT *pDict, int nCells)
+{
+#if FICL_ROBUST
+ if (nCells > 0)
+ {
+ if (nCells <= dictCellsAvail(pDict))
+ pDict->here += nCells;
+ else
+ return 1; /* dict is full */
+ }
+ else
+ {
+ nCells = -nCells;
+ if (nCells <= dictCellsUsed(pDict))
+ pDict->here -= nCells;
+ else /* prevent underflow */
+ pDict->here -= dictCellsUsed(pDict);
+ }
+#else
+ pDict->here += nCells;
+#endif
+ return 0;
+}
+
+
+/**************************************************************************
+ d i c t A p p e n d C e l l
+** Append the specified cell to the dictionary
+**************************************************************************/
+void dictAppendCell(FICL_DICT *pDict, CELL c)
+{
+ *pDict->here++ = c;
+ return;
+}
+
+
+/**************************************************************************
+ d i c t A p p e n d C h a r
+** Append the specified char to the dictionary
+**************************************************************************/
+void dictAppendChar(FICL_DICT *pDict, char c)
+{
+ char *cp = (char *)pDict->here;
+ *cp++ = c;
+ pDict->here = PTRtoCELL cp;
+ return;
+}
+
+
+/**************************************************************************
+ d i c t A p p e n d W o r d
+** Create a new word in the dictionary with the specified
+** name, code, and flags. Name must be NULL-terminated.
+**************************************************************************/
+FICL_WORD *dictAppendWord(FICL_DICT *pDict,
+ char *name,
+ FICL_CODE pCode,
+ UNS8 flags)
+{
+ STRINGINFO si;
+ SI_SETLEN(si, strlen(name));
+ SI_SETPTR(si, name);
+ return dictAppendWord2(pDict, si, pCode, flags);
+}
+
+
+/**************************************************************************
+ d i c t A p p e n d W o r d 2
+** Create a new word in the dictionary with the specified
+** STRINGINFO, code, and flags. Does not require a NULL-terminated
+** name.
+**************************************************************************/
+FICL_WORD *dictAppendWord2(FICL_DICT *pDict,
+ STRINGINFO si,
+ FICL_CODE pCode,
+ UNS8 flags)
+{
+ FICL_COUNT len = (FICL_COUNT)SI_COUNT(si);
+ char *pName;
+ FICL_WORD *pFW;
+
+ ficlLockDictionary(TRUE);
+
+ /*
+ ** NOTE: dictCopyName advances "here" as a side-effect.
+ ** It must execute before pFW is initialized.
+ */
+ pName = dictCopyName(pDict, si);
+ pFW = (FICL_WORD *)pDict->here;
+ pDict->smudge = pFW;
+ pFW->hash = hashHashCode(si);
+ pFW->code = pCode;
+ pFW->flags = (UNS8)(flags | FW_SMUDGE);
+ pFW->nName = (char)len;
+ pFW->name = pName;
+ /*
+ ** Point "here" to first cell of new word's param area...
+ */
+ pDict->here = pFW->param;
+
+ if (!(flags & FW_SMUDGE))
+ dictUnsmudge(pDict);
+
+ ficlLockDictionary(FALSE);
+ return pFW;
+}
+
+
+/**************************************************************************
+ d i c t A p p e n d U N S
+** Append the specified FICL_UNS to the dictionary
+**************************************************************************/
+void dictAppendUNS(FICL_DICT *pDict, FICL_UNS u)
+{
+ *pDict->here++ = LVALUEtoCELL(u);
+ return;
+}
+
+
+/**************************************************************************
+ d i c t C e l l s A v a i l
+** Returns the number of empty cells left in the dictionary
+**************************************************************************/
+int dictCellsAvail(FICL_DICT *pDict)
+{
+ return pDict->size - dictCellsUsed(pDict);
+}
+
+
+/**************************************************************************
+ d i c t C e l l s U s e d
+** Returns the number of cells consumed in the dicionary
+**************************************************************************/
+int dictCellsUsed(FICL_DICT *pDict)
+{
+ return pDict->here - pDict->dict;
+}
+
+
+/**************************************************************************
+ d i c t C h e c k
+** Checks the dictionary for corruption and throws appropriate
+** errors.
+** Input: +n number of ADDRESS UNITS (not Cells) proposed to allot
+** -n number of ADDRESS UNITS proposed to de-allot
+** 0 just do a consistency check
+**************************************************************************/
+void dictCheck(FICL_DICT *pDict, FICL_VM *pVM, int n)
+{
+ if ((n >= 0) && (dictCellsAvail(pDict) * (int)sizeof(CELL) < n))
+ {
+ vmThrowErr(pVM, "Error: dictionary full");
+ }
+
+ if ((n <= 0) && (dictCellsUsed(pDict) * (int)sizeof(CELL) < -n))
+ {
+ vmThrowErr(pVM, "Error: dictionary underflow");
+ }
+
+ if (pDict->nLists > FICL_DEFAULT_VOCS)
+ {
+ dictResetSearchOrder(pDict);
+ vmThrowErr(pVM, "Error: search order overflow");
+ }
+ else if (pDict->nLists < 0)
+ {
+ dictResetSearchOrder(pDict);
+ vmThrowErr(pVM, "Error: search order underflow");
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ d i c t C o p y N a m e
+** Copy up to nFICLNAME characters of the name specified by si into
+** the dictionary starting at "here", then NULL-terminate the name,
+** point "here" to the next available byte, and return the address of
+** the beginning of the name. Used by dictAppendWord.
+** N O T E S :
+** 1. "here" is guaranteed to be aligned after this operation.
+** 2. If the string has zero length, align and return "here"
+**************************************************************************/
+static char *dictCopyName(FICL_DICT *pDict, STRINGINFO si)
+{
+ char *oldCP = (char *)pDict->here;
+ char *cp = oldCP;
+ char *name = SI_PTR(si);
+ int i = SI_COUNT(si);
+
+ if (i == 0)
+ {
+ dictAlign(pDict);
+ return (char *)pDict->here;
+ }
+
+ if (i > nFICLNAME)
+ i = nFICLNAME;
+
+ for (; i > 0; --i)
+ {
+ *cp++ = *name++;
+ }
+
+ *cp++ = '\0';
+
+ pDict->here = PTRtoCELL cp;
+ dictAlign(pDict);
+ return oldCP;
+}
+
+
+/**************************************************************************
+ d i c t C r e a t e
+** Create and initialize a dictionary with the specified number
+** of cells capacity, and no hashing (hash size == 1).
+**************************************************************************/
+FICL_DICT *dictCreate(unsigned nCells)
+{
+ return dictCreateHashed(nCells, 1);
+}
+
+
+FICL_DICT *dictCreateHashed(unsigned nCells, unsigned nHash)
+{
+ FICL_DICT *pDict;
+ size_t nAlloc;
+
+ nAlloc = sizeof (FICL_HASH) + nCells * sizeof (CELL)
+ + (nHash - 1) * sizeof (FICL_WORD *);
+
+ pDict = ficlMalloc(sizeof (FICL_DICT));
+ assert(pDict);
+ memset(pDict, 0, sizeof (FICL_DICT));
+ pDict->dict = ficlMalloc(nAlloc);
+ assert(pDict->dict);
+
+ pDict->size = nCells;
+ dictEmpty(pDict, nHash);
+ return pDict;
+}
+
+
+/**************************************************************************
+ d i c t C r e a t e W o r d l i s t
+** Create and initialize an anonymous wordlist
+**************************************************************************/
+FICL_HASH *dictCreateWordlist(FICL_DICT *dp, int nBuckets)
+{
+ FICL_HASH *pHash;
+
+ dictAlign(dp);
+ pHash = (FICL_HASH *)dp->here;
+ dictAllot(dp, sizeof (FICL_HASH)
+ + (nBuckets-1) * sizeof (FICL_WORD *));
+
+ pHash->size = nBuckets;
+ hashReset(pHash);
+ return pHash;
+}
+
+
+/**************************************************************************
+ d i c t D e l e t e
+** Free all memory allocated for the given dictionary
+**************************************************************************/
+void dictDelete(FICL_DICT *pDict)
+{
+ assert(pDict);
+ ficlFree(pDict);
+ return;
+}
+
+
+/**************************************************************************
+ d i c t E m p t y
+** Empty the dictionary, reset its hash table, and reset its search order.
+** Clears and (re-)creates the hash table with the size specified by nHash.
+**************************************************************************/
+void dictEmpty(FICL_DICT *pDict, unsigned nHash)
+{
+ FICL_HASH *pHash;
+
+ pDict->here = pDict->dict;
+
+ dictAlign(pDict);
+ pHash = (FICL_HASH *)pDict->here;
+ dictAllot(pDict,
+ sizeof (FICL_HASH) + (nHash - 1) * sizeof (FICL_WORD *));
+
+ pHash->size = nHash;
+ hashReset(pHash);
+
+ pDict->pForthWords = pHash;
+ pDict->smudge = NULL;
+ dictResetSearchOrder(pDict);
+ return;
+}
+
+
+/**************************************************************************
+ d i c t H a s h S u m m a r y
+** Calculate a figure of merit for the dictionary hash table based
+** on the average search depth for all the words in the dictionary,
+** assuming uniform distribution of target keys. The figure of merit
+** is the ratio of the total search depth for all keys in the table
+** versus a theoretical optimum that would be achieved if the keys
+** were distributed into the table as evenly as possible.
+** The figure would be worse if the hash table used an open
+** addressing scheme (i.e. collisions resolved by searching the
+** table for an empty slot) for a given size table.
+**************************************************************************/
+#if FICL_WANT_FLOAT
+void dictHashSummary(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_HASH *pFHash;
+ FICL_WORD **pHash;
+ unsigned size;
+ FICL_WORD *pFW;
+ unsigned i;
+ int nMax = 0;
+ int nWords = 0;
+ int nFilled;
+ double avg = 0.0;
+ double best;
+ int nAvg, nRem, nDepth;
+
+ dictCheck(dp, pVM, 0);
+
+ pFHash = dp->pSearch[dp->nLists - 1];
+ pHash = pFHash->table;
+ size = pFHash->size;
+ nFilled = size;
+
+ for (i = 0; i < size; i++)
+ {
+ int n = 0;
+ pFW = pHash[i];
+
+ while (pFW)
+ {
+ ++n;
+ ++nWords;
+ pFW = pFW->link;
+ }
+
+ avg += (double)(n * (n+1)) / 2.0;
+
+ if (n > nMax)
+ nMax = n;
+ if (n == 0)
+ --nFilled;
+ }
+
+ /* Calc actual avg search depth for this hash */
+ avg = avg / nWords;
+
+ /* Calc best possible performance with this size hash */
+ nAvg = nWords / size;
+ nRem = nWords % size;
+ nDepth = size * (nAvg * (nAvg+1))/2 + (nAvg+1)*nRem;
+ best = (double)nDepth/nWords;
+
+ sprintf(pVM->pad,
+ "%d bins, %2.0f%% filled, Depth: Max=%d, Avg=%2.1f, Best=%2.1f, Score: %2.0f%%",
+ size,
+ (double)nFilled * 100.0 / size, nMax,
+ avg,
+ best,
+ 100.0 * best / avg);
+
+ ficlTextOut(pVM, pVM->pad, 1);
+
+ return;
+}
+#endif
+
+/**************************************************************************
+ d i c t I n c l u d e s
+** Returns TRUE iff the given pointer is within the address range of
+** the dictionary.
+**************************************************************************/
+int dictIncludes(FICL_DICT *pDict, void *p)
+{
+ return ((p >= (void *) &pDict->dict)
+ && (p < (void *)(&pDict->dict + pDict->size))
+ );
+}
+
+/**************************************************************************
+ d i c t L o o k u p
+** Find the FICL_WORD that matches the given name and length.
+** If found, returns the word's address. Otherwise returns NULL.
+** Uses the search order list to search multiple wordlists.
+**************************************************************************/
+FICL_WORD *dictLookup(FICL_DICT *pDict, STRINGINFO si)
+{
+ FICL_WORD *pFW = NULL;
+ FICL_HASH *pHash;
+ int i;
+ UNS16 hashCode = hashHashCode(si);
+
+ assert(pDict);
+
+ ficlLockDictionary(1);
+
+ for (i = (int)pDict->nLists - 1; (i >= 0) && (!pFW); --i)
+ {
+ pHash = pDict->pSearch[i];
+ pFW = hashLookup(pHash, si, hashCode);
+ }
+
+ ficlLockDictionary(0);
+ return pFW;
+}
+
+
+/**************************************************************************
+ f i c l L o o k u p L o c
+** Same as dictLookup, but looks in system locals dictionary first...
+** Assumes locals dictionary has only one wordlist...
+**************************************************************************/
+#if FICL_WANT_LOCALS
+FICL_WORD *ficlLookupLoc(FICL_SYSTEM *pSys, STRINGINFO si)
+{
+ FICL_WORD *pFW = NULL;
+ FICL_DICT *pDict = pSys->dp;
+ FICL_HASH *pHash = ficlGetLoc(pSys)->pForthWords;
+ int i;
+ UNS16 hashCode = hashHashCode(si);
+
+ assert(pHash);
+ assert(pDict);
+
+ ficlLockDictionary(1);
+ /*
+ ** check the locals dict first...
+ */
+ pFW = hashLookup(pHash, si, hashCode);
+
+ /*
+ ** If no joy, (!pFW) --------------------------v
+ ** iterate over the search list in the main dict
+ */
+ for (i = (int)pDict->nLists - 1; (i >= 0) && (!pFW); --i)
+ {
+ pHash = pDict->pSearch[i];
+ pFW = hashLookup(pHash, si, hashCode);
+ }
+
+ ficlLockDictionary(0);
+ return pFW;
+}
+#endif
+
+
+/**************************************************************************
+ d i c t R e s e t S e a r c h O r d e r
+** Initialize the dictionary search order list to sane state
+**************************************************************************/
+void dictResetSearchOrder(FICL_DICT *pDict)
+{
+ assert(pDict);
+ pDict->pCompile = pDict->pForthWords;
+ pDict->nLists = 1;
+ pDict->pSearch[0] = pDict->pForthWords;
+ return;
+}
+
+
+/**************************************************************************
+ d i c t S e t F l a g s
+** Changes the flags field of the most recently defined word:
+** Set all bits that are ones in the set parameter, clear all bits
+** that are ones in the clr parameter. Clear wins in case the same bit
+** is set in both parameters.
+**************************************************************************/
+void dictSetFlags(FICL_DICT *pDict, UNS8 set, UNS8 clr)
+{
+ assert(pDict->smudge);
+ pDict->smudge->flags |= set;
+ pDict->smudge->flags &= ~clr;
+ return;
+}
+
+
+/**************************************************************************
+ d i c t S e t I m m e d i a t e
+** Set the most recently defined word as IMMEDIATE
+**************************************************************************/
+void dictSetImmediate(FICL_DICT *pDict)
+{
+ assert(pDict->smudge);
+ pDict->smudge->flags |= FW_IMMEDIATE;
+ return;
+}
+
+
+/**************************************************************************
+ d i c t U n s m u d g e
+** Completes the definition of a word by linking it
+** into the main list
+**************************************************************************/
+void dictUnsmudge(FICL_DICT *pDict)
+{
+ FICL_WORD *pFW = pDict->smudge;
+ FICL_HASH *pHash = pDict->pCompile;
+
+ assert(pHash);
+ assert(pFW);
+ /*
+ ** :noname words never get linked into the list...
+ */
+ if (pFW->nName > 0)
+ hashInsertWord(pHash, pFW);
+ pFW->flags &= ~(FW_SMUDGE);
+ return;
+}
+
+
+/**************************************************************************
+ d i c t W h e r e
+** Returns the value of the HERE pointer -- the address
+** of the next free cell in the dictionary
+**************************************************************************/
+CELL *dictWhere(FICL_DICT *pDict)
+{
+ return pDict->here;
+}
+
+
+/**************************************************************************
+ h a s h F o r g e t
+** Unlink all words in the hash that have addresses greater than or
+** equal to the address supplied. Implementation factor for FORGET
+** and MARKER.
+**************************************************************************/
+void hashForget(FICL_HASH *pHash, void *where)
+{
+ FICL_WORD *pWord;
+ unsigned i;
+
+ assert(pHash);
+ assert(where);
+
+ for (i = 0; i < pHash->size; i++)
+ {
+ pWord = pHash->table[i];
+
+ while ((void *)pWord >= where)
+ {
+ pWord = pWord->link;
+ }
+
+ pHash->table[i] = pWord;
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ h a s h H a s h C o d e
+**
+** Generate a 16 bit hashcode from a character string using a rolling
+** shift and add stolen from PJ Weinberger of Bell Labs fame. Case folds
+** the name before hashing it...
+** N O T E : If string has zero length, returns zero.
+**************************************************************************/
+UNS16 hashHashCode(STRINGINFO si)
+{
+ /* hashPJW */
+ UNS8 *cp;
+ UNS16 code = (UNS16)si.count;
+ UNS16 shift = 0;
+
+ if (si.count == 0)
+ return 0;
+
+ /* changed to run without errors under Purify -- lch */
+ for (cp = (UNS8 *)si.cp; si.count && *cp; cp++, si.count--)
+ {
+ code = (UNS16)((code << 4) + tolower(*cp));
+ shift = (UNS16)(code & 0xf000);
+ if (shift)
+ {
+ code ^= (UNS16)(shift >> 8);
+ code ^= (UNS16)shift;
+ }
+ }
+
+ return (UNS16)code;
+}
+
+
+
+
+/**************************************************************************
+ h a s h I n s e r t W o r d
+** Put a word into the hash table using the word's hashcode as
+** an index (modulo the table size).
+**************************************************************************/
+void hashInsertWord(FICL_HASH *pHash, FICL_WORD *pFW)
+{
+ FICL_WORD **pList;
+
+ assert(pHash);
+ assert(pFW);
+
+ if (pHash->size == 1)
+ {
+ pList = pHash->table;
+ }
+ else
+ {
+ pList = pHash->table + (pFW->hash % pHash->size);
+ }
+
+ pFW->link = *pList;
+ *pList = pFW;
+ return;
+}
+
+
+/**************************************************************************
+ h a s h L o o k u p
+** Find a name in the hash table given the hashcode and text of the name.
+** Returns the address of the corresponding FICL_WORD if found,
+** otherwise NULL.
+** Note: outer loop on link field supports inheritance in wordlists.
+** It's not part of ANS Forth - ficl only. hashReset creates wordlists
+** with NULL link fields.
+**************************************************************************/
+FICL_WORD *hashLookup(FICL_HASH *pHash, STRINGINFO si, UNS16 hashCode)
+{
+ FICL_UNS nCmp = si.count;
+ FICL_WORD *pFW;
+ UNS16 hashIdx;
+
+ if (nCmp > nFICLNAME)
+ nCmp = nFICLNAME;
+
+ for (; pHash != NULL; pHash = pHash->link)
+ {
+ if (pHash->size > 1)
+ hashIdx = (UNS16)(hashCode % pHash->size);
+ else /* avoid the modulo op for single threaded lists */
+ hashIdx = 0;
+
+ for (pFW = pHash->table[hashIdx]; pFW; pFW = pFW->link)
+ {
+ if ( (pFW->nName == si.count)
+ && (!strincmp(si.cp, pFW->name, nCmp)) )
+ return pFW;
+#if FICL_ROBUST
+ assert(pFW != pFW->link);
+#endif
+ }
+ }
+
+ return NULL;
+}
+
+
+/**************************************************************************
+ h a s h R e s e t
+** Initialize a FICL_HASH to empty state.
+**************************************************************************/
+void hashReset(FICL_HASH *pHash)
+{
+ unsigned i;
+
+ assert(pHash);
+
+ for (i = 0; i < pHash->size; i++)
+ {
+ pHash->table[i] = NULL;
+ }
+
+ pHash->link = NULL;
+ pHash->name = NULL;
+ return;
+}
+
+/**************************************************************************
+ d i c t C h e c k T h r e s h o l d
+** Verify if an increase in the dictionary size is warranted, and do it if
+** so.
+**************************************************************************/
+
+void dictCheckThreshold(FICL_DICT* dp)
+{
+ if( dictCellsAvail(dp) < dictThreshold.u ) {
+ dp->dict = ficlMalloc( dictIncrease.u * sizeof (CELL) );
+ assert(dp->dict);
+ dp->here = dp->dict;
+ dp->size = dictIncrease.u;
+ dictAlign(dp);
+ }
+}
+
diff --git a/stand/ficl/ficl.c b/stand/ficl/ficl.c
new file mode 100644
index 0000000..219cf84
--- /dev/null
+++ b/stand/ficl/ficl.c
@@ -0,0 +1,696 @@
+/*******************************************************************
+** f i c l . c
+** Forth Inspired Command Language - external interface
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 19 July 1997
+** $Id: ficl.c,v 1.16 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** This is an ANS Forth interpreter written in C.
+** Ficl uses Forth syntax for its commands, but turns the Forth
+** model on its head in other respects.
+** Ficl provides facilities for interoperating
+** with programs written in C: C functions can be exported to Ficl,
+** and Ficl commands can be executed via a C calling interface. The
+** interpreter is re-entrant, so it can be used in multiple instances
+** in a multitasking system. Unlike Forth, Ficl's outer interpreter
+** expects a text block as input, and returns to the caller after each
+** text block, so the data pump is somewhere in external code in the
+** style of TCL.
+**
+** Code is written in ANSI C for portability.
+*/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdlib.h>
+#else
+#include <stand.h>
+#endif
+#include <string.h>
+#include "ficl.h"
+
+
+/*
+** System statics
+** Each FICL_SYSTEM builds a global dictionary during its start
+** sequence. This is shared by all virtual machines of that system.
+** Therefore only one VM can update the dictionary
+** at a time. The system imports a locking function that
+** you can override in order to control update access to
+** the dictionary. The function is stubbed out by default,
+** but you can insert one: #define FICL_MULTITHREAD 1
+** and supply your own version of ficlLockDictionary.
+*/
+static int defaultStack = FICL_DEFAULT_STACK;
+
+
+static void ficlSetVersionEnv(FICL_SYSTEM *pSys);
+
+
+/**************************************************************************
+ f i c l I n i t S y s t e m
+** Binds a global dictionary to the interpreter system.
+** You specify the address and size of the allocated area.
+** After that, ficl manages it.
+** First step is to set up the static pointers to the area.
+** Then write the "precompiled" portion of the dictionary in.
+** The dictionary needs to be at least large enough to hold the
+** precompiled part. Try 1K cells minimum. Use "words" to find
+** out how much of the dictionary is used at any time.
+**************************************************************************/
+FICL_SYSTEM *ficlInitSystemEx(FICL_SYSTEM_INFO *fsi)
+{
+ int nDictCells;
+ int nEnvCells;
+ FICL_SYSTEM *pSys = ficlMalloc(sizeof (FICL_SYSTEM));
+
+ assert(pSys);
+ assert(fsi->size == sizeof (FICL_SYSTEM_INFO));
+
+ memset(pSys, 0, sizeof (FICL_SYSTEM));
+
+ nDictCells = fsi->nDictCells;
+ if (nDictCells <= 0)
+ nDictCells = FICL_DEFAULT_DICT;
+
+ nEnvCells = fsi->nEnvCells;
+ if (nEnvCells <= 0)
+ nEnvCells = FICL_DEFAULT_DICT;
+
+ pSys->dp = dictCreateHashed((unsigned)nDictCells, HASHSIZE);
+ pSys->dp->pForthWords->name = "forth-wordlist";
+
+ pSys->envp = dictCreate((unsigned)nEnvCells);
+ pSys->envp->pForthWords->name = "environment";
+
+ pSys->textOut = fsi->textOut;
+ pSys->pExtend = fsi->pExtend;
+
+#if FICL_WANT_LOCALS
+ /*
+ ** The locals dictionary is only searched while compiling,
+ ** but this is where speed is most important. On the other
+ ** hand, the dictionary gets emptied after each use of locals
+ ** The need to balance search speed with the cost of the 'empty'
+ ** operation led me to select a single-threaded list...
+ */
+ pSys->localp = dictCreate((unsigned)FICL_MAX_LOCALS * CELLS_PER_WORD);
+#endif
+
+ /*
+ ** Build the precompiled dictionary and load softwords. We need a temporary
+ ** VM to do this - ficlNewVM links one to the head of the system VM list.
+ ** ficlCompilePlatform (defined in win32.c, for example) adds platform specific words.
+ */
+ ficlCompileCore(pSys);
+ ficlCompilePrefix(pSys);
+#if FICL_WANT_FLOAT
+ ficlCompileFloat(pSys);
+#endif
+#if FICL_PLATFORM_EXTEND
+ ficlCompilePlatform(pSys);
+#endif
+ ficlSetVersionEnv(pSys);
+
+ /*
+ ** Establish the parse order. Note that prefixes precede numbers -
+ ** this allows constructs like "0b101010" which might parse as a
+ ** hex value otherwise.
+ */
+ ficlAddPrecompiledParseStep(pSys, "?prefix", ficlParsePrefix);
+ ficlAddPrecompiledParseStep(pSys, "?number", ficlParseNumber);
+#if FICL_WANT_FLOAT
+ ficlAddPrecompiledParseStep(pSys, ">float", ficlParseFloatNumber);
+#endif
+
+ /*
+ ** Now create a temporary VM to compile the softwords. Since all VMs are
+ ** linked into the vmList of FICL_SYSTEM, we don't have to pass the VM
+ ** to ficlCompileSoftCore -- it just hijacks whatever it finds in the VM list.
+ ** ficl 2.05: vmCreate no longer depends on the presence of INTERPRET in the
+ ** dictionary, so a VM can be created before the dictionary is built. It just
+ ** can't do much...
+ */
+ ficlNewVM(pSys);
+ ficlCompileSoftCore(pSys);
+ ficlFreeVM(pSys->vmList);
+
+
+ return pSys;
+}
+
+
+FICL_SYSTEM *ficlInitSystem(int nDictCells)
+{
+ FICL_SYSTEM_INFO fsi;
+ ficlInitInfo(&fsi);
+ fsi.nDictCells = nDictCells;
+ return ficlInitSystemEx(&fsi);
+}
+
+
+/**************************************************************************
+ f i c l A d d P a r s e S t e p
+** Appends a parse step function to the end of the parse list (see
+** FICL_PARSE_STEP notes in ficl.h for details). Returns 0 if successful,
+** nonzero if there's no more room in the list.
+**************************************************************************/
+int ficlAddParseStep(FICL_SYSTEM *pSys, FICL_WORD *pFW)
+{
+ int i;
+ for (i = 0; i < FICL_MAX_PARSE_STEPS; i++)
+ {
+ if (pSys->parseList[i] == NULL)
+ {
+ pSys->parseList[i] = pFW;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+/*
+** Compile a word into the dictionary that invokes the specified FICL_PARSE_STEP
+** function. It is up to the user (as usual in Forth) to make sure the stack
+** preconditions are valid (there needs to be a counted string on top of the stack)
+** before using the resulting word.
+*/
+void ficlAddPrecompiledParseStep(FICL_SYSTEM *pSys, char *name, FICL_PARSE_STEP pStep)
+{
+ FICL_DICT *dp = pSys->dp;
+ FICL_WORD *pFW = dictAppendWord(dp, name, parseStepParen, FW_DEFAULT);
+ dictAppendCell(dp, LVALUEtoCELL(pStep));
+ ficlAddParseStep(pSys, pFW);
+}
+
+
+/*
+** This word lists the parse steps in order
+*/
+void ficlListParseSteps(FICL_VM *pVM)
+{
+ int i;
+ FICL_SYSTEM *pSys = pVM->pSys;
+ assert(pSys);
+
+ vmTextOut(pVM, "Parse steps:", 1);
+ vmTextOut(pVM, "lookup", 1);
+
+ for (i = 0; i < FICL_MAX_PARSE_STEPS; i++)
+ {
+ if (pSys->parseList[i] != NULL)
+ {
+ vmTextOut(pVM, pSys->parseList[i]->name, 1);
+ }
+ else break;
+ }
+ return;
+}
+
+
+/**************************************************************************
+ f i c l N e w V M
+** Create a new virtual machine and link it into the system list
+** of VMs for later cleanup by ficlTermSystem.
+**************************************************************************/
+FICL_VM *ficlNewVM(FICL_SYSTEM *pSys)
+{
+ FICL_VM *pVM = vmCreate(NULL, defaultStack, defaultStack);
+ pVM->link = pSys->vmList;
+ pVM->pSys = pSys;
+ pVM->pExtend = pSys->pExtend;
+ vmSetTextOut(pVM, pSys->textOut);
+
+ pSys->vmList = pVM;
+ return pVM;
+}
+
+
+/**************************************************************************
+ f i c l F r e e V M
+** Removes the VM in question from the system VM list and deletes the
+** memory allocated to it. This is an optional call, since ficlTermSystem
+** will do this cleanup for you. This function is handy if you're going to
+** do a lot of dynamic creation of VMs.
+**************************************************************************/
+void ficlFreeVM(FICL_VM *pVM)
+{
+ FICL_SYSTEM *pSys = pVM->pSys;
+ FICL_VM *pList = pSys->vmList;
+
+ assert(pVM != NULL);
+
+ if (pSys->vmList == pVM)
+ {
+ pSys->vmList = pSys->vmList->link;
+ }
+ else for (; pList != NULL; pList = pList->link)
+ {
+ if (pList->link == pVM)
+ {
+ pList->link = pVM->link;
+ break;
+ }
+ }
+
+ if (pList)
+ vmDelete(pVM);
+ return;
+}
+
+
+/**************************************************************************
+ f i c l B u i l d
+** Builds a word into the dictionary.
+** Preconditions: system must be initialized, and there must
+** be enough space for the new word's header! Operation is
+** controlled by ficlLockDictionary, so any initialization
+** required by your version of the function (if you overrode
+** it) must be complete at this point.
+** Parameters:
+** name -- duh, the name of the word
+** code -- code to execute when the word is invoked - must take a single param
+** pointer to a FICL_VM
+** flags -- 0 or more of F_IMMEDIATE, F_COMPILE, use bitwise OR!
+**
+**************************************************************************/
+int ficlBuild(FICL_SYSTEM *pSys, char *name, FICL_CODE code, char flags)
+{
+#if FICL_MULTITHREAD
+ int err = ficlLockDictionary(TRUE);
+ if (err) return err;
+#endif /* FICL_MULTITHREAD */
+
+ assert(dictCellsAvail(pSys->dp) > sizeof (FICL_WORD) / sizeof (CELL));
+ dictAppendWord(pSys->dp, name, code, flags);
+
+ ficlLockDictionary(FALSE);
+ return 0;
+}
+
+
+/**************************************************************************
+ f i c l E v a l u a t e
+** Wrapper for ficlExec() which sets SOURCE-ID to -1.
+**************************************************************************/
+int ficlEvaluate(FICL_VM *pVM, char *pText)
+{
+ int returnValue;
+ CELL id = pVM->sourceID;
+ pVM->sourceID.i = -1;
+ returnValue = ficlExecC(pVM, pText, -1);
+ pVM->sourceID = id;
+ return returnValue;
+}
+
+
+/**************************************************************************
+ f i c l E x e c
+** Evaluates a block of input text in the context of the
+** specified interpreter. Emits any requested output to the
+** interpreter's output function.
+**
+** Contains the "inner interpreter" code in a tight loop
+**
+** Returns one of the VM_XXXX codes defined in ficl.h:
+** VM_OUTOFTEXT is the normal exit condition
+** VM_ERREXIT means that the interp encountered a syntax error
+** and the vm has been reset to recover (some or all
+** of the text block got ignored
+** VM_USEREXIT means that the user executed the "bye" command
+** to shut down the interpreter. This would be a good
+** time to delete the vm, etc -- or you can ignore this
+** signal.
+**************************************************************************/
+int ficlExec(FICL_VM *pVM, char *pText)
+{
+ return ficlExecC(pVM, pText, -1);
+}
+
+int ficlExecC(FICL_VM *pVM, char *pText, FICL_INT size)
+{
+ FICL_SYSTEM *pSys = pVM->pSys;
+ FICL_DICT *dp = pSys->dp;
+
+ int except;
+ jmp_buf vmState;
+ jmp_buf *oldState;
+ TIB saveTib;
+
+ assert(pVM);
+ assert(pSys->pInterp[0]);
+
+ if (size < 0)
+ size = strlen(pText);
+
+ vmPushTib(pVM, pText, size, &saveTib);
+
+ /*
+ ** Save and restore VM's jmp_buf to enable nested calls to ficlExec
+ */
+ oldState = pVM->pState;
+ pVM->pState = &vmState; /* This has to come before the setjmp! */
+ except = setjmp(vmState);
+
+ switch (except)
+ {
+ case 0:
+ if (pVM->fRestart)
+ {
+ pVM->runningWord->code(pVM);
+ pVM->fRestart = 0;
+ }
+ else
+ { /* set VM up to interpret text */
+ vmPushIP(pVM, &(pSys->pInterp[0]));
+ }
+
+ vmInnerLoop(pVM);
+ break;
+
+ case VM_RESTART:
+ pVM->fRestart = 1;
+ except = VM_OUTOFTEXT;
+ break;
+
+ case VM_OUTOFTEXT:
+ vmPopIP(pVM);
+#ifdef TESTMAIN
+ if ((pVM->state != COMPILE) && (pVM->sourceID.i == 0))
+ ficlTextOut(pVM, FICL_PROMPT, 0);
+#endif
+ break;
+
+ case VM_USEREXIT:
+ case VM_INNEREXIT:
+ case VM_BREAK:
+ break;
+
+ case VM_QUIT:
+ if (pVM->state == COMPILE)
+ {
+ dictAbortDefinition(dp);
+#if FICL_WANT_LOCALS
+ dictEmpty(pSys->localp, pSys->localp->pForthWords->size);
+#endif
+ }
+ vmQuit(pVM);
+ break;
+
+ case VM_ERREXIT:
+ case VM_ABORT:
+ case VM_ABORTQ:
+ default: /* user defined exit code?? */
+ if (pVM->state == COMPILE)
+ {
+ dictAbortDefinition(dp);
+#if FICL_WANT_LOCALS
+ dictEmpty(pSys->localp, pSys->localp->pForthWords->size);
+#endif
+ }
+ dictResetSearchOrder(dp);
+ vmReset(pVM);
+ break;
+ }
+
+ pVM->pState = oldState;
+ vmPopTib(pVM, &saveTib);
+ return (except);
+}
+
+
+/**************************************************************************
+ f i c l E x e c X T
+** Given a pointer to a FICL_WORD, push an inner interpreter and
+** execute the word to completion. This is in contrast with vmExecute,
+** which does not guarantee that the word will have completed when
+** the function returns (ie in the case of colon definitions, which
+** need an inner interpreter to finish)
+**
+** Returns one of the VM_XXXX exception codes listed in ficl.h. Normal
+** exit condition is VM_INNEREXIT, ficl's private signal to exit the
+** inner loop under normal circumstances. If another code is thrown to
+** exit the loop, this function will re-throw it if it's nested under
+** itself or ficlExec.
+**
+** NOTE: this function is intended so that C code can execute ficlWords
+** given their address in the dictionary (xt).
+**************************************************************************/
+int ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord)
+{
+ int except;
+ jmp_buf vmState;
+ jmp_buf *oldState;
+ FICL_WORD *oldRunningWord;
+
+ assert(pVM);
+ assert(pVM->pSys->pExitInner);
+
+ /*
+ ** Save the runningword so that RESTART behaves correctly
+ ** over nested calls.
+ */
+ oldRunningWord = pVM->runningWord;
+ /*
+ ** Save and restore VM's jmp_buf to enable nested calls
+ */
+ oldState = pVM->pState;
+ pVM->pState = &vmState; /* This has to come before the setjmp! */
+ except = setjmp(vmState);
+
+ if (except)
+ vmPopIP(pVM);
+ else
+ vmPushIP(pVM, &(pVM->pSys->pExitInner));
+
+ switch (except)
+ {
+ case 0:
+ vmExecute(pVM, pWord);
+ vmInnerLoop(pVM);
+ break;
+
+ case VM_INNEREXIT:
+ case VM_BREAK:
+ break;
+
+ case VM_RESTART:
+ case VM_OUTOFTEXT:
+ case VM_USEREXIT:
+ case VM_QUIT:
+ case VM_ERREXIT:
+ case VM_ABORT:
+ case VM_ABORTQ:
+ default: /* user defined exit code?? */
+ if (oldState)
+ {
+ pVM->pState = oldState;
+ vmThrow(pVM, except);
+ }
+ break;
+ }
+
+ pVM->pState = oldState;
+ pVM->runningWord = oldRunningWord;
+ return (except);
+}
+
+
+/**************************************************************************
+ f i c l L o o k u p
+** Look in the system dictionary for a match to the given name. If
+** found, return the address of the corresponding FICL_WORD. Otherwise
+** return NULL.
+**************************************************************************/
+FICL_WORD *ficlLookup(FICL_SYSTEM *pSys, char *name)
+{
+ STRINGINFO si;
+ SI_PSZ(si, name);
+ return dictLookup(pSys->dp, si);
+}
+
+
+/**************************************************************************
+ f i c l G e t D i c t
+** Returns the address of the system dictionary
+**************************************************************************/
+FICL_DICT *ficlGetDict(FICL_SYSTEM *pSys)
+{
+ return pSys->dp;
+}
+
+
+/**************************************************************************
+ f i c l G e t E n v
+** Returns the address of the system environment space
+**************************************************************************/
+FICL_DICT *ficlGetEnv(FICL_SYSTEM *pSys)
+{
+ return pSys->envp;
+}
+
+
+/**************************************************************************
+ f i c l S e t E n v
+** Create an environment variable with a one-CELL payload. ficlSetEnvD
+** makes one with a two-CELL payload.
+**************************************************************************/
+void ficlSetEnv(FICL_SYSTEM *pSys, char *name, FICL_UNS value)
+{
+ STRINGINFO si;
+ FICL_WORD *pFW;
+ FICL_DICT *envp = pSys->envp;
+
+ SI_PSZ(si, name);
+ pFW = dictLookup(envp, si);
+
+ if (pFW == NULL)
+ {
+ dictAppendWord(envp, name, constantParen, FW_DEFAULT);
+ dictAppendCell(envp, LVALUEtoCELL(value));
+ }
+ else
+ {
+ pFW->param[0] = LVALUEtoCELL(value);
+ }
+
+ return;
+}
+
+void ficlSetEnvD(FICL_SYSTEM *pSys, char *name, FICL_UNS hi, FICL_UNS lo)
+{
+ FICL_WORD *pFW;
+ STRINGINFO si;
+ FICL_DICT *envp = pSys->envp;
+ SI_PSZ(si, name);
+ pFW = dictLookup(envp, si);
+
+ if (pFW == NULL)
+ {
+ dictAppendWord(envp, name, twoConstParen, FW_DEFAULT);
+ dictAppendCell(envp, LVALUEtoCELL(lo));
+ dictAppendCell(envp, LVALUEtoCELL(hi));
+ }
+ else
+ {
+ pFW->param[0] = LVALUEtoCELL(lo);
+ pFW->param[1] = LVALUEtoCELL(hi);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ f i c l G e t L o c
+** Returns the address of the system locals dictionary. This dict is
+** only used during compilation, and is shared by all VMs.
+**************************************************************************/
+#if FICL_WANT_LOCALS
+FICL_DICT *ficlGetLoc(FICL_SYSTEM *pSys)
+{
+ return pSys->localp;
+}
+#endif
+
+
+
+/**************************************************************************
+ f i c l S e t S t a c k S i z e
+** Set the stack sizes (return and parameter) to be used for all
+** subsequently created VMs. Returns actual stack size to be used.
+**************************************************************************/
+int ficlSetStackSize(int nStackCells)
+{
+ if (nStackCells >= FICL_DEFAULT_STACK)
+ defaultStack = nStackCells;
+ else
+ defaultStack = FICL_DEFAULT_STACK;
+
+ return defaultStack;
+}
+
+
+/**************************************************************************
+ f i c l T e r m S y s t e m
+** Tear the system down by deleting the dictionaries and all VMs.
+** This saves you from having to keep track of all that stuff.
+**************************************************************************/
+void ficlTermSystem(FICL_SYSTEM *pSys)
+{
+ if (pSys->dp)
+ dictDelete(pSys->dp);
+ pSys->dp = NULL;
+
+ if (pSys->envp)
+ dictDelete(pSys->envp);
+ pSys->envp = NULL;
+
+#if FICL_WANT_LOCALS
+ if (pSys->localp)
+ dictDelete(pSys->localp);
+ pSys->localp = NULL;
+#endif
+
+ while (pSys->vmList != NULL)
+ {
+ FICL_VM *pVM = pSys->vmList;
+ pSys->vmList = pSys->vmList->link;
+ vmDelete(pVM);
+ }
+
+ ficlFree(pSys);
+ pSys = NULL;
+ return;
+}
+
+
+/**************************************************************************
+ f i c l S e t V e r s i o n E n v
+** Create a double cell environment constant for the version ID
+**************************************************************************/
+static void ficlSetVersionEnv(FICL_SYSTEM *pSys)
+{
+ ficlSetEnvD(pSys, "ficl-version", FICL_VER_MAJOR, FICL_VER_MINOR);
+ ficlSetEnv (pSys, "ficl-robust", FICL_ROBUST);
+ return;
+}
+
diff --git a/stand/ficl/ficl.h b/stand/ficl/ficl.h
new file mode 100644
index 0000000..8829cce
--- /dev/null
+++ b/stand/ficl/ficl.h
@@ -0,0 +1,1164 @@
+/*******************************************************************
+** f i c l . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 19 July 1997
+** Dedicated to RHS, in loving memory
+** $Id: ficl.h,v 1.18 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#if !defined (__FICL_H__)
+#define __FICL_H__
+/*
+** Ficl (Forth-inspired command language) is an ANS Forth
+** interpreter written in C. Unlike traditional Forths, this
+** interpreter is designed to be embedded into other systems
+** as a command/macro/development prototype language.
+**
+** Where Forths usually view themselves as the center of the system
+** and expect the rest of the system to be coded in Forth, Ficl
+** acts as a component of the system. It is easy to export
+** code written in C or ASM to Ficl in the style of TCL, or to invoke
+** Ficl code from a compiled module. This allows you to do incremental
+** development in a way that combines the best features of threaded
+** languages (rapid development, quick code/test/debug cycle,
+** reasonably fast) with the best features of C (everyone knows it,
+** easier to support large blocks of code, efficient, type checking).
+**
+** Ficl provides facilities for interoperating
+** with programs written in C: C functions can be exported to Ficl,
+** and Ficl commands can be executed via a C calling interface. The
+** interpreter is re-entrant, so it can be used in multiple instances
+** in a multitasking system. Unlike Forth, Ficl's outer interpreter
+** expects a text block as input, and returns to the caller after each
+** text block, so the "data pump" is somewhere in external code. This
+** is more like TCL than Forth, which usually expcets to be at the center
+** of the system, requesting input at its convenience. Each Ficl virtual
+** machine can be bound to a different I/O channel, and is independent
+** of all others in in the same address space except that all virtual
+** machines share a common dictionary (a sort or open symbol table that
+** defines all of the elements of the language).
+**
+** Code is written in ANSI C for portability.
+**
+** Summary of Ficl features and constraints:
+** - Standard: Implements the ANSI Forth CORE word set and part
+** of the CORE EXT word-set, SEARCH and SEARCH EXT, TOOLS and
+** TOOLS EXT, LOCAL and LOCAL ext and various extras.
+** - Extensible: you can export code written in Forth, C,
+** or asm in a straightforward way. Ficl provides open
+** facilities for extending the language in an application
+** specific way. You can even add new control structures!
+** - Ficl and C can interact in two ways: Ficl can encapsulate
+** C code, or C code can invoke Ficl code.
+** - Thread-safe, re-entrant: The shared system dictionary
+** uses a locking mechanism that you can either supply
+** or stub out to provide exclusive access. Each Ficl
+** virtual machine has an otherwise complete state, and
+** each can be bound to a separate I/O channel (or none at all).
+** - Simple encapsulation into existing systems: a basic implementation
+** requires three function calls (see the example program in testmain.c).
+** - ROMable: Ficl is designed to work in RAM-based and ROM code / RAM data
+** environments. It does require somewhat more memory than a pure
+** ROM implementation because it builds its system dictionary in
+** RAM at startup time.
+** - Written an ANSI C to be as simple as I can make it to understand,
+** support, debug, and port. Compiles without complaint at /Az /W4
+** (require ANSI C, max warnings) under Microsoft VC++ 5.
+** - Does full 32 bit math (but you need to implement
+** two mixed precision math primitives (see sysdep.c))
+** - Indirect threaded interpreter is not the fastest kind of
+** Forth there is (see pForth 68K for a really fast subroutine
+** threaded interpreter), but it's the cleanest match to a
+** pure C implementation.
+**
+** P O R T I N G F i c l
+**
+** To install Ficl on your target system, you need an ANSI C compiler
+** and its runtime library. Inspect the system dependent macros and
+** functions in sysdep.h and sysdep.c and edit them to suit your
+** system. For example, INT16 is a short on some compilers and an
+** int on others. Check the default CELL alignment controlled by
+** FICL_ALIGN. If necessary, add new definitions of ficlMalloc, ficlFree,
+** ficlLockDictionary, and ficlTextOut to work with your operating system.
+** Finally, use testmain.c as a guide to installing the Ficl system and
+** one or more virtual machines into your code. You do not need to include
+** testmain.c in your build.
+**
+** T o D o L i s t
+**
+** 1. Unimplemented system dependent CORE word: key
+** 2. Ficl uses the PAD in some CORE words - this violates the standard,
+** but it's cleaner for a multithreaded system. I'll have to make a
+** second pad for reference by the word PAD to fix this.
+**
+** F o r M o r e I n f o r m a t i o n
+**
+** Web home of ficl
+** http://ficl.sourceforge.net
+** Check this website for Forth literature (including the ANSI standard)
+** http://www.taygeta.com/forthlit.html
+** and here for software and more links
+** http://www.taygeta.com/forth.html
+**
+** Obvious Performance enhancement opportunities
+** Compile speed
+** - work on interpret speed
+** - turn off locals (FICL_WANT_LOCALS)
+** Interpret speed
+** - Change inner interpreter (and everything else)
+** so that a definition is a list of pointers to functions
+** and inline data rather than pointers to words. This gets
+** rid of vm->runningWord and a level of indirection in the
+** inner loop. I'll look at it for ficl 3.0
+** - Make the main hash table a bigger prime (HASHSIZE)
+** - FORGET about twiddling the hash function - my experience is
+** that that is a waste of time.
+** - Eliminate the need to pass the pVM parameter on the stack
+** by dedicating a register to it. Most words need access to the
+** vm, but the parameter passing overhead can be reduced. One way
+** requires that the host OS have a task switch callout. Create
+** a global variable for the running VM and refer to it in words
+** that need VM access. Alternative: use thread local storage.
+** For single threaded implementations, you can just use a global.
+** The first two solutions create portability problems, so I
+** haven't considered doing them. Another possibility is to
+** declare the pVm parameter to be "register", and hope the compiler
+** pays attention.
+**
+*/
+
+/*
+** Revision History:
+**
+** 15 Apr 1999 (sadler) Merged FreeBSD changes for exception wordset and
+** counted strings in ficlExec.
+** 12 Jan 1999 (sobral) Corrected EVALUATE behavior. Now TIB has an
+** "end" field, and all words respect this. ficlExec is passed a "size"
+** of TIB, as well as vmPushTib. This size is used to calculate the "end"
+** of the string, ie, base+size. If the size is not known, pass -1.
+**
+** 10 Jan 1999 (sobral) EXCEPTION word set has been added, and existing
+** words has been modified to conform to EXCEPTION EXT word set.
+**
+** 27 Aug 1998 (sadler) testing and corrections for LOCALS, LOCALS EXT,
+** SEARCH / SEARCH EXT, TOOLS / TOOLS EXT.
+** Added .X to display in hex, PARSE and PARSE-WORD to supplement WORD,
+** EMPTY to clear stack.
+**
+** 29 jun 1998 (sadler) added variable sized hash table support
+** and ANS Forth optional SEARCH & SEARCH EXT word set.
+** 26 May 1998 (sadler)
+** FICL_PROMPT macro
+** 14 April 1998 (sadler) V1.04
+** Ficlwin: Windows version, Skip Carter's Linux port
+** 5 March 1998 (sadler) V1.03
+** Bug fixes -- passes John Ryan's ANS test suite "core.fr"
+**
+** 24 February 1998 (sadler) V1.02
+** -Fixed bugs in <# # #>
+** -Changed FICL_WORD so that storage for the name characters
+** can be allocated from the dictionary as needed rather than
+** reserving 32 bytes in each word whether needed or not -
+** this saved 50% of the dictionary storage requirement.
+** -Added words in testmain for Win32 functions system,chdir,cwd,
+** also added a word that loads and evaluates a file.
+**
+** December 1997 (sadler)
+** -Added VM_RESTART exception handling in ficlExec -- this lets words
+** that require additional text to succeed (like :, create, variable...)
+** recover gracefully from an empty input buffer rather than emitting
+** an error message. Definitions can span multiple input blocks with
+** no restrictions.
+** -Changed #include order so that <assert.h> is included in sysdep.h,
+** and sysdep is included in all other files. This lets you define
+** NDEBUG in sysdep.h to disable assertions if you want to.
+** -Make PC specific system dependent code conditional on _M_IX86
+** defined so that ports can coexist in sysdep.h/sysdep.c
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sysdep.h"
+#include <limits.h> /* UCHAR_MAX */
+
+/*
+** Forward declarations... read on.
+*/
+struct ficl_word;
+typedef struct ficl_word FICL_WORD;
+struct vm;
+typedef struct vm FICL_VM;
+struct ficl_dict;
+typedef struct ficl_dict FICL_DICT;
+struct ficl_system;
+typedef struct ficl_system FICL_SYSTEM;
+struct ficl_system_info;
+typedef struct ficl_system_info FICL_SYSTEM_INFO;
+
+/*
+** the Good Stuff starts here...
+*/
+#define FICL_VER "3.03"
+#define FICL_VER_MAJOR 3
+#define FICL_VER_MINOR 3
+#if !defined (FICL_PROMPT)
+#define FICL_PROMPT "ok> "
+#endif
+
+/*
+** ANS Forth requires false to be zero, and true to be the ones
+** complement of false... that unifies logical and bitwise operations
+** nicely.
+*/
+#define FICL_TRUE ((unsigned long)~(0L))
+#define FICL_FALSE (0)
+#define FICL_BOOL(x) ((x) ? FICL_TRUE : FICL_FALSE)
+
+
+/*
+** A CELL is the main storage type. It must be large enough
+** to contain a pointer or a scalar. In order to accommodate
+** 32 bit and 64 bit processors, use abstract types for int,
+** unsigned, and float.
+*/
+typedef union _cell
+{
+ FICL_INT i;
+ FICL_UNS u;
+#if (FICL_WANT_FLOAT)
+ FICL_FLOAT f;
+#endif
+ void *p;
+ void (*fn)(void);
+} CELL;
+
+/*
+** LVALUEtoCELL does a little pointer trickery to cast any CELL sized
+** lvalue (informal definition: an expression whose result has an
+** address) to CELL. Remember that constants and casts are NOT
+** themselves lvalues!
+*/
+#define LVALUEtoCELL(v) (*(CELL *)&v)
+
+/*
+** PTRtoCELL is a cast through void * intended to satisfy the
+** most outrageously pedantic compiler... (I won't mention
+** its name)
+*/
+#define PTRtoCELL (CELL *)(void *)
+#define PTRtoSTRING (FICL_STRING *)(void *)
+
+/*
+** Strings in FICL are stored in Pascal style - with a count
+** preceding the text. We'll also NULL-terminate them so that
+** they work with the usual C lib string functions. (Belt &
+** suspenders? You decide.)
+** STRINGINFO hides the implementation with a couple of
+** macros for use in internal routines.
+*/
+
+typedef unsigned char FICL_COUNT;
+#define FICL_STRING_MAX UCHAR_MAX
+typedef struct _ficl_string
+{
+ FICL_COUNT count;
+ char text[1];
+} FICL_STRING;
+
+typedef struct
+{
+ FICL_UNS count;
+ char *cp;
+} STRINGINFO;
+
+#define SI_COUNT(si) (si.count)
+#define SI_PTR(si) (si.cp)
+#define SI_SETLEN(si, len) (si.count = (FICL_UNS)(len))
+#define SI_SETPTR(si, ptr) (si.cp = (char *)(ptr))
+/*
+** Init a STRINGINFO from a pointer to NULL-terminated string
+*/
+#define SI_PSZ(si, psz) \
+ {si.cp = psz; si.count = (FICL_COUNT)strlen(psz);}
+/*
+** Init a STRINGINFO from a pointer to FICL_STRING
+*/
+#define SI_PFS(si, pfs) \
+ {si.cp = pfs->text; si.count = pfs->count;}
+
+/*
+** Ficl uses this little structure to hold the address of
+** the block of text it's working on and an index to the next
+** unconsumed character in the string. Traditionally, this is
+** done by a Text Input Buffer, so I've called this struct TIB.
+**
+** Since this structure also holds the size of the input buffer,
+** and since evaluate requires that, let's put the size here.
+** The size is stored as an end-pointer because that is what the
+** null-terminated string aware functions find most easy to deal
+** with.
+** Notice, though, that nobody really uses this except evaluate,
+** so it might just be moved to FICL_VM instead. (sobral)
+*/
+typedef struct
+{
+ FICL_INT index;
+ char *end;
+ char *cp;
+} TIB;
+
+
+/*
+** Stacks get heavy use in Ficl and Forth...
+** Each virtual machine implements two of them:
+** one holds parameters (data), and the other holds return
+** addresses and control flow information for the virtual
+** machine. (Note: C's automatic stack is implicitly used,
+** but not modeled because it doesn't need to be...)
+** Here's an abstract type for a stack
+*/
+typedef struct _ficlStack
+{
+ FICL_UNS nCells; /* size of the stack */
+ CELL *pFrame; /* link reg for stack frame */
+ CELL *sp; /* stack pointer */
+ CELL base[1]; /* Top of stack */
+} FICL_STACK;
+
+/*
+** Stack methods... many map closely to required Forth words.
+*/
+FICL_STACK *stackCreate (unsigned nCells);
+void stackDelete (FICL_STACK *pStack);
+int stackDepth (FICL_STACK *pStack);
+void stackDrop (FICL_STACK *pStack, int n);
+CELL stackFetch (FICL_STACK *pStack, int n);
+CELL stackGetTop (FICL_STACK *pStack);
+void stackLink (FICL_STACK *pStack, int nCells);
+void stackPick (FICL_STACK *pStack, int n);
+CELL stackPop (FICL_STACK *pStack);
+void *stackPopPtr (FICL_STACK *pStack);
+FICL_UNS stackPopUNS (FICL_STACK *pStack);
+FICL_INT stackPopINT (FICL_STACK *pStack);
+void stackPush (FICL_STACK *pStack, CELL c);
+void stackPushPtr (FICL_STACK *pStack, void *ptr);
+void stackPushUNS (FICL_STACK *pStack, FICL_UNS u);
+void stackPushINT (FICL_STACK *pStack, FICL_INT i);
+void stackReset (FICL_STACK *pStack);
+void stackRoll (FICL_STACK *pStack, int n);
+void stackSetTop (FICL_STACK *pStack, CELL c);
+void stackStore (FICL_STACK *pStack, int n, CELL c);
+void stackUnlink (FICL_STACK *pStack);
+
+#if (FICL_WANT_FLOAT)
+float stackPopFloat (FICL_STACK *pStack);
+void stackPushFloat(FICL_STACK *pStack, FICL_FLOAT f);
+#endif
+
+/*
+** Shortcuts (Guy Carver)
+*/
+#define PUSHPTR(p) stackPushPtr(pVM->pStack,p)
+#define PUSHUNS(u) stackPushUNS(pVM->pStack,u)
+#define PUSHINT(i) stackPushINT(pVM->pStack,i)
+#define PUSHFLOAT(f) stackPushFloat(pVM->fStack,f)
+#define PUSH(c) stackPush(pVM->pStack,c)
+#define POPPTR() stackPopPtr(pVM->pStack)
+#define POPUNS() stackPopUNS(pVM->pStack)
+#define POPINT() stackPopINT(pVM->pStack)
+#define POPFLOAT() stackPopFloat(pVM->fStack)
+#define POP() stackPop(pVM->pStack)
+#define GETTOP() stackGetTop(pVM->pStack)
+#define SETTOP(c) stackSetTop(pVM->pStack,LVALUEtoCELL(c))
+#define GETTOPF() stackGetTop(pVM->fStack)
+#define SETTOPF(c) stackSetTop(pVM->fStack,LVALUEtoCELL(c))
+#define STORE(n,c) stackStore(pVM->pStack,n,LVALUEtoCELL(c))
+#define DEPTH() stackDepth(pVM->pStack)
+#define DROP(n) stackDrop(pVM->pStack,n)
+#define DROPF(n) stackDrop(pVM->fStack,n)
+#define FETCH(n) stackFetch(pVM->pStack,n)
+#define PICK(n) stackPick(pVM->pStack,n)
+#define PICKF(n) stackPick(pVM->fStack,n)
+#define ROLL(n) stackRoll(pVM->pStack,n)
+#define ROLLF(n) stackRoll(pVM->fStack,n)
+
+/*
+** The virtual machine (VM) contains the state for one interpreter.
+** Defined operations include:
+** Create & initialize
+** Delete
+** Execute a block of text
+** Parse a word out of the input stream
+** Call return, and branch
+** Text output
+** Throw an exception
+*/
+
+typedef FICL_WORD ** IPTYPE; /* the VM's instruction pointer */
+
+/*
+** Each VM has a placeholder for an output function -
+** this makes it possible to have each VM do I/O
+** through a different device. If you specify no
+** OUTFUNC, it defaults to ficlTextOut.
+*/
+typedef void (*OUTFUNC)(FICL_VM *pVM, char *text, int fNewline);
+
+/*
+** Each VM operates in one of two non-error states: interpreting
+** or compiling. When interpreting, words are simply executed.
+** When compiling, most words in the input stream have their
+** addresses inserted into the word under construction. Some words
+** (known as IMMEDIATE) are executed in the compile state, too.
+*/
+/* values of STATE */
+#define INTERPRET 0
+#define COMPILE 1
+
+/*
+** The pad is a small scratch area for text manipulation. ANS Forth
+** requires it to hold at least 84 characters.
+*/
+#if !defined nPAD
+#define nPAD 256
+#endif
+
+/*
+** ANS Forth requires that a word's name contain {1..31} characters.
+*/
+#if !defined nFICLNAME
+#define nFICLNAME 31
+#endif
+
+/*
+** OK - now we can really define the VM...
+*/
+struct vm
+{
+ FICL_SYSTEM *pSys; /* Which system this VM belongs to */
+ FICL_VM *link; /* Ficl keeps a VM list for simple teardown */
+ jmp_buf *pState; /* crude exception mechanism... */
+ OUTFUNC textOut; /* Output callback - see sysdep.c */
+ void * pExtend; /* vm extension pointer for app use - initialized from FICL_SYSTEM */
+ short fRestart; /* Set TRUE to restart runningWord */
+ IPTYPE ip; /* instruction pointer */
+ FICL_WORD *runningWord;/* address of currently running word (often just *(ip-1) ) */
+ FICL_UNS state; /* compiling or interpreting */
+ FICL_UNS base; /* number conversion base */
+ FICL_STACK *pStack; /* param stack */
+ FICL_STACK *rStack; /* return stack */
+#if FICL_WANT_FLOAT
+ FICL_STACK *fStack; /* float stack (optional) */
+#endif
+ CELL sourceID; /* -1 if EVALUATE, 0 if normal input */
+ TIB tib; /* address of incoming text string */
+#if FICL_WANT_USER
+ CELL user[FICL_USER_CELLS];
+#endif
+ char pad[nPAD]; /* the scratch area (see above) */
+};
+
+/*
+** A FICL_CODE points to a function that gets called to help execute
+** a word in the dictionary. It always gets passed a pointer to the
+** running virtual machine, and from there it can get the address
+** of the parameter area of the word it's supposed to operate on.
+** For precompiled words, the code is all there is. For user defined
+** words, the code assumes that the word's parameter area is a list
+** of pointers to the code fields of other words to execute, and
+** may also contain inline data. The first parameter is always
+** a pointer to a code field.
+*/
+typedef void (*FICL_CODE)(FICL_VM *pVm);
+
+#if 0
+#define VM_ASSERT(pVM) assert((*(pVM->ip - 1)) == pVM->runningWord)
+#else
+#define VM_ASSERT(pVM)
+#endif
+
+/*
+** Ficl models memory as a contiguous space divided into
+** words in a linked list called the dictionary.
+** A FICL_WORD starts each entry in the list.
+** Version 1.02: space for the name characters is allotted from
+** the dictionary ahead of the word struct, rather than using
+** a fixed size array for each name.
+*/
+struct ficl_word
+{
+ struct ficl_word *link; /* Previous word in the dictionary */
+ UNS16 hash;
+ UNS8 flags; /* Immediate, Smudge, Compile-only */
+ FICL_COUNT nName; /* Number of chars in word name */
+ char *name; /* First nFICLNAME chars of word name */
+ FICL_CODE code; /* Native code to execute the word */
+ CELL param[1]; /* First data cell of the word */
+};
+
+/*
+** Worst-case size of a word header: nFICLNAME chars in name
+*/
+#define CELLS_PER_WORD \
+ ( (sizeof (FICL_WORD) + nFICLNAME + sizeof (CELL)) \
+ / (sizeof (CELL)) )
+
+int wordIsImmediate(FICL_WORD *pFW);
+int wordIsCompileOnly(FICL_WORD *pFW);
+
+/* flag values for word header */
+#define FW_IMMEDIATE 1 /* execute me even if compiling */
+#define FW_COMPILE 2 /* error if executed when not compiling */
+#define FW_SMUDGE 4 /* definition in progress - hide me */
+#define FW_ISOBJECT 8 /* word is an object or object member variable */
+
+#define FW_COMPIMMED (FW_IMMEDIATE | FW_COMPILE)
+#define FW_DEFAULT 0
+
+
+/*
+** Exit codes for vmThrow
+*/
+#define VM_INNEREXIT -256 /* tell ficlExecXT to exit inner loop */
+#define VM_OUTOFTEXT -257 /* hungry - normal exit */
+#define VM_RESTART -258 /* word needs more text to succeed - re-run it */
+#define VM_USEREXIT -259 /* user wants to quit */
+#define VM_ERREXIT -260 /* interp found an error */
+#define VM_BREAK -261 /* debugger breakpoint */
+#define VM_ABORT -1 /* like errexit -- abort */
+#define VM_ABORTQ -2 /* like errexit -- abort" */
+#define VM_QUIT -56 /* like errexit, but leave pStack & base alone */
+
+
+void vmBranchRelative(FICL_VM *pVM, int offset);
+FICL_VM * vmCreate (FICL_VM *pVM, unsigned nPStack, unsigned nRStack);
+void vmDelete (FICL_VM *pVM);
+void vmExecute (FICL_VM *pVM, FICL_WORD *pWord);
+FICL_DICT *vmGetDict (FICL_VM *pVM);
+char * vmGetString (FICL_VM *pVM, FICL_STRING *spDest, char delimiter);
+STRINGINFO vmGetWord (FICL_VM *pVM);
+STRINGINFO vmGetWord0 (FICL_VM *pVM);
+int vmGetWordToPad (FICL_VM *pVM);
+STRINGINFO vmParseString (FICL_VM *pVM, char delimiter);
+STRINGINFO vmParseStringEx(FICL_VM *pVM, char delimiter, char fSkipLeading);
+CELL vmPop (FICL_VM *pVM);
+void vmPush (FICL_VM *pVM, CELL c);
+void vmPopIP (FICL_VM *pVM);
+void vmPushIP (FICL_VM *pVM, IPTYPE newIP);
+void vmQuit (FICL_VM *pVM);
+void vmReset (FICL_VM *pVM);
+void vmSetTextOut (FICL_VM *pVM, OUTFUNC textOut);
+void vmTextOut (FICL_VM *pVM, char *text, int fNewline);
+void vmTextOut (FICL_VM *pVM, char *text, int fNewline);
+void vmThrow (FICL_VM *pVM, int except);
+void vmThrowErr (FICL_VM *pVM, char *fmt, ...);
+
+#define vmGetRunningWord(pVM) ((pVM)->runningWord)
+
+
+/*
+** The inner interpreter - coded as a macro (see note for
+** INLINE_INNER_LOOP in sysdep.h for complaints about VC++ 5
+*/
+#define M_VM_STEP(pVM) \
+ FICL_WORD *tempFW = *(pVM)->ip++; \
+ (pVM)->runningWord = tempFW; \
+ tempFW->code(pVM);
+
+#define M_INNER_LOOP(pVM) \
+ for (;;) { M_VM_STEP(pVM) }
+
+
+#if INLINE_INNER_LOOP != 0
+#define vmInnerLoop(pVM) M_INNER_LOOP(pVM)
+#else
+void vmInnerLoop(FICL_VM *pVM);
+#endif
+
+/*
+** vmCheckStack needs a vm pointer because it might have to say
+** something if it finds a problem. Parms popCells and pushCells
+** correspond to the number of parameters on the left and right of
+** a word's stack effect comment.
+*/
+void vmCheckStack(FICL_VM *pVM, int popCells, int pushCells);
+#if FICL_WANT_FLOAT
+void vmCheckFStack(FICL_VM *pVM, int popCells, int pushCells);
+#endif
+
+/*
+** TIB access routines...
+** ANS forth seems to require the input buffer to be represented
+** as a pointer to the start of the buffer, and an index to the
+** next character to read.
+** PushTib points the VM to a new input string and optionally
+** returns a copy of the current state
+** PopTib restores the TIB state given a saved TIB from PushTib
+** GetInBuf returns a pointer to the next unused char of the TIB
+*/
+void vmPushTib (FICL_VM *pVM, char *text, FICL_INT nChars, TIB *pSaveTib);
+void vmPopTib (FICL_VM *pVM, TIB *pTib);
+#define vmGetInBuf(pVM) ((pVM)->tib.cp + (pVM)->tib.index)
+#define vmGetInBufLen(pVM) ((pVM)->tib.end - (pVM)->tib.cp)
+#define vmGetInBufEnd(pVM) ((pVM)->tib.end)
+#define vmGetTibIndex(pVM) (pVM)->tib.index
+#define vmSetTibIndex(pVM, i) (pVM)->tib.index = i
+#define vmUpdateTib(pVM, str) (pVM)->tib.index = (str) - (pVM)->tib.cp
+
+/*
+** Generally useful string manipulators omitted by ANSI C...
+** ltoa complements strtol
+*/
+#if defined(_WIN32) && !FICL_MAIN
+/* #SHEESH
+** Why do Microsoft Meatballs insist on contaminating
+** my namespace with their string functions???
+*/
+#pragma warning(disable: 4273)
+#endif
+
+int isPowerOfTwo(FICL_UNS u);
+
+char *ltoa( FICL_INT value, char *string, int radix );
+char *ultoa(FICL_UNS value, char *string, int radix );
+char digit_to_char(int value);
+char *strrev( char *string );
+char *skipSpace(char *cp, char *end);
+char *caseFold(char *cp);
+int strincmp(char *cp1, char *cp2, FICL_UNS count);
+
+#if defined(_WIN32) && !FICL_MAIN
+#pragma warning(default: 4273)
+#endif
+
+/*
+** Ficl hash table - variable size.
+** assert(size > 0)
+** If size is 1, the table degenerates into a linked list.
+** A WORDLIST (see the search order word set in DPANS) is
+** just a pointer to a FICL_HASH in this implementation.
+*/
+#if !defined HASHSIZE /* Default size of hash table. For most uniform */
+#define HASHSIZE 241 /* performance, use a prime number! */
+#endif
+
+typedef struct ficl_hash
+{
+ struct ficl_hash *link; /* link to parent class wordlist for OO */
+ char *name; /* optional pointer to \0 terminated wordlist name */
+ unsigned size; /* number of buckets in the hash */
+ FICL_WORD *table[1];
+} FICL_HASH;
+
+void hashForget (FICL_HASH *pHash, void *where);
+UNS16 hashHashCode (STRINGINFO si);
+void hashInsertWord(FICL_HASH *pHash, FICL_WORD *pFW);
+FICL_WORD *hashLookup (FICL_HASH *pHash, STRINGINFO si, UNS16 hashCode);
+void hashReset (FICL_HASH *pHash);
+
+/*
+** A Dictionary is a linked list of FICL_WORDs. It is also Ficl's
+** memory model. Description of fields:
+**
+** here -- points to the next free byte in the dictionary. This
+** pointer is forced to be CELL-aligned before a definition is added.
+** Do not assume any specific alignment otherwise - Use dictAlign().
+**
+** smudge -- pointer to word currently being defined (or last defined word)
+** If the definition completes successfully, the word will be
+** linked into the hash table. If unsuccessful, dictUnsmudge
+** uses this pointer to restore the previous state of the dictionary.
+** Smudge prevents unintentional recursion as a side-effect: the
+** dictionary search algo examines only completed definitions, so a
+** word cannot invoke itself by name. See the ficl word "recurse".
+** NOTE: smudge always points to the last word defined. IMMEDIATE
+** makes use of this fact. Smudge is initially NULL.
+**
+** pForthWords -- pointer to the default wordlist (FICL_HASH).
+** This is the initial compilation list, and contains all
+** ficl's precompiled words.
+**
+** pCompile -- compilation wordlist - initially equal to pForthWords
+** pSearch -- array of pointers to wordlists. Managed as a stack.
+** Highest index is the first list in the search order.
+** nLists -- number of lists in pSearch. nLists-1 is the highest
+** filled slot in pSearch, and points to the first wordlist
+** in the search order
+** size -- number of cells in the dictionary (total)
+** dict -- start of data area. Must be at the end of the struct.
+*/
+struct ficl_dict
+{
+ CELL *here;
+ FICL_WORD *smudge;
+ FICL_HASH *pForthWords;
+ FICL_HASH *pCompile;
+ FICL_HASH *pSearch[FICL_DEFAULT_VOCS];
+ int nLists;
+ unsigned size; /* Number of cells in dict (total)*/
+ CELL *dict; /* Base of dictionary memory */
+};
+
+void *alignPtr(void *ptr);
+void dictAbortDefinition(FICL_DICT *pDict);
+void dictAlign (FICL_DICT *pDict);
+int dictAllot (FICL_DICT *pDict, int n);
+int dictAllotCells (FICL_DICT *pDict, int nCells);
+void dictAppendCell (FICL_DICT *pDict, CELL c);
+void dictAppendChar (FICL_DICT *pDict, char c);
+FICL_WORD *dictAppendWord (FICL_DICT *pDict,
+ char *name,
+ FICL_CODE pCode,
+ UNS8 flags);
+FICL_WORD *dictAppendWord2(FICL_DICT *pDict,
+ STRINGINFO si,
+ FICL_CODE pCode,
+ UNS8 flags);
+void dictAppendUNS (FICL_DICT *pDict, FICL_UNS u);
+int dictCellsAvail (FICL_DICT *pDict);
+int dictCellsUsed (FICL_DICT *pDict);
+void dictCheck (FICL_DICT *pDict, FICL_VM *pVM, int n);
+void dictCheckThreshold(FICL_DICT* dp);
+FICL_DICT *dictCreate(unsigned nCELLS);
+FICL_DICT *dictCreateHashed(unsigned nCells, unsigned nHash);
+FICL_HASH *dictCreateWordlist(FICL_DICT *dp, int nBuckets);
+void dictDelete (FICL_DICT *pDict);
+void dictEmpty (FICL_DICT *pDict, unsigned nHash);
+#if FICL_WANT_FLOAT
+void dictHashSummary(FICL_VM *pVM);
+#endif
+int dictIncludes (FICL_DICT *pDict, void *p);
+FICL_WORD *dictLookup (FICL_DICT *pDict, STRINGINFO si);
+#if FICL_WANT_LOCALS
+FICL_WORD *ficlLookupLoc (FICL_SYSTEM *pSys, STRINGINFO si);
+#endif
+void dictResetSearchOrder(FICL_DICT *pDict);
+void dictSetFlags (FICL_DICT *pDict, UNS8 set, UNS8 clr);
+void dictSetImmediate(FICL_DICT *pDict);
+void dictUnsmudge (FICL_DICT *pDict);
+CELL *dictWhere (FICL_DICT *pDict);
+
+
+/*
+** P A R S E S T E P
+** (New for 2.05)
+** See words.c: interpWord
+** By default, ficl goes through two attempts to parse each token from its input
+** stream: it first attempts to match it with a word in the dictionary, and
+** if that fails, it attempts to convert it into a number. This mechanism is now
+** extensible by additional steps. This allows extensions like floating point and
+** double number support to be factored cleanly.
+**
+** Each parse step is a function that receives the next input token as a STRINGINFO.
+** If the parse step matches the token, it must apply semantics to the token appropriate
+** to the present value of VM.state (compiling or interpreting), and return FICL_TRUE.
+** Otherwise it returns FICL_FALSE. See words.c: isNumber for an example
+**
+** Note: for the sake of efficiency, it's a good idea both to limit the number
+** of parse steps and to code each parse step so that it rejects tokens that
+** do not match as quickly as possible.
+*/
+
+typedef int (*FICL_PARSE_STEP)(FICL_VM *pVM, STRINGINFO si);
+
+/*
+** Appends a parse step function to the end of the parse list (see
+** FICL_PARSE_STEP notes in ficl.h for details). Returns 0 if successful,
+** nonzero if there's no more room in the list. Each parse step is a word in
+** the dictionary. Precompiled parse steps can use (PARSE-STEP) as their
+** CFA - see parenParseStep in words.c.
+*/
+int ficlAddParseStep(FICL_SYSTEM *pSys, FICL_WORD *pFW); /* ficl.c */
+void ficlAddPrecompiledParseStep(FICL_SYSTEM *pSys, char *name, FICL_PARSE_STEP pStep);
+void ficlListParseSteps(FICL_VM *pVM);
+
+/*
+** FICL_BREAKPOINT record.
+** origXT - if NULL, this breakpoint is unused. Otherwise it stores the xt
+** that the breakpoint overwrote. This is restored to the dictionary when the
+** BP executes or gets cleared
+** address - the location of the breakpoint (address of the instruction that
+** has been replaced with the breakpoint trap
+** origXT - The original contents of the location with the breakpoint
+** Note: address is NULL when this breakpoint is empty
+*/
+typedef struct FICL_BREAKPOINT
+{
+ void *address;
+ FICL_WORD *origXT;
+} FICL_BREAKPOINT;
+
+
+/*
+** F I C L _ S Y S T E M
+** The top level data structure of the system - ficl_system ties a list of
+** virtual machines with their corresponding dictionaries. Ficl 3.0 will
+** support multiple Ficl systems, allowing multiple concurrent sessions
+** to separate dictionaries with some constraints.
+** The present model allows multiple sessions to one dictionary provided
+** you implement ficlLockDictionary() as specified in sysdep.h
+** Note: the pExtend pointer is there to provide context for applications. It is copied
+** to each VM's pExtend field as that VM is created.
+*/
+struct ficl_system
+{
+ FICL_SYSTEM *link;
+ void *pExtend; /* Initializes VM's pExtend pointer (for application use) */
+ FICL_VM *vmList;
+ FICL_DICT *dp;
+ FICL_DICT *envp;
+#ifdef FICL_WANT_LOCALS
+ FICL_DICT *localp;
+#endif
+ FICL_WORD *pInterp[3];
+ FICL_WORD *parseList[FICL_MAX_PARSE_STEPS];
+ OUTFUNC textOut;
+
+ FICL_WORD *pBranchParen;
+ FICL_WORD *pDoParen;
+ FICL_WORD *pDoesParen;
+ FICL_WORD *pExitInner;
+ FICL_WORD *pExitParen;
+ FICL_WORD *pBranch0;
+ FICL_WORD *pInterpret;
+ FICL_WORD *pLitParen;
+ FICL_WORD *pTwoLitParen;
+ FICL_WORD *pLoopParen;
+ FICL_WORD *pPLoopParen;
+ FICL_WORD *pQDoParen;
+ FICL_WORD *pSemiParen;
+ FICL_WORD *pOfParen;
+ FICL_WORD *pStore;
+ FICL_WORD *pDrop;
+ FICL_WORD *pCStringLit;
+ FICL_WORD *pStringLit;
+
+#if FICL_WANT_LOCALS
+ FICL_WORD *pGetLocalParen;
+ FICL_WORD *pGet2LocalParen;
+ FICL_WORD *pGetLocal0;
+ FICL_WORD *pGetLocal1;
+ FICL_WORD *pToLocalParen;
+ FICL_WORD *pTo2LocalParen;
+ FICL_WORD *pToLocal0;
+ FICL_WORD *pToLocal1;
+ FICL_WORD *pLinkParen;
+ FICL_WORD *pUnLinkParen;
+ FICL_INT nLocals;
+ CELL *pMarkLocals;
+#endif
+
+ FICL_BREAKPOINT bpStep;
+};
+
+struct ficl_system_info
+{
+ int size; /* structure size tag for versioning */
+ int nDictCells; /* Size of system's Dictionary */
+ OUTFUNC textOut; /* default textOut function */
+ void *pExtend; /* Initializes VM's pExtend pointer - for application use */
+ int nEnvCells; /* Size of Environment dictionary */
+};
+
+
+#define ficlInitInfo(x) { memset((x), 0, sizeof(FICL_SYSTEM_INFO)); \
+ (x)->size = sizeof(FICL_SYSTEM_INFO); }
+
+/*
+** External interface to FICL...
+*/
+/*
+** f i c l I n i t S y s t e m
+** Binds a global dictionary to the interpreter system and initializes
+** the dict to contain the ANSI CORE wordset.
+** You can specify the address and size of the allocated area.
+** Using ficlInitSystemEx you can also specify the text output function.
+** After that, ficl manages it.
+** First step is to set up the static pointers to the area.
+** Then write the "precompiled" portion of the dictionary in.
+** The dictionary needs to be at least large enough to hold the
+** precompiled part. Try 1K cells minimum. Use "words" to find
+** out how much of the dictionary is used at any time.
+*/
+FICL_SYSTEM *ficlInitSystemEx(FICL_SYSTEM_INFO *fsi);
+
+/* Deprecated call */
+FICL_SYSTEM *ficlInitSystem(int nDictCells);
+
+/*
+** f i c l T e r m S y s t e m
+** Deletes the system dictionary and all virtual machines that
+** were created with ficlNewVM (see below). Call this function to
+** reclaim all memory used by the dictionary and VMs.
+*/
+void ficlTermSystem(FICL_SYSTEM *pSys);
+
+/*
+** f i c l E v a l u a t e
+** Evaluates a block of input text in the context of the
+** specified interpreter. Also sets SOURCE-ID properly.
+**
+** PLEASE USE THIS FUNCTION when throwing a hard-coded
+** string to the FICL interpreter.
+*/
+int ficlEvaluate(FICL_VM *pVM, char *pText);
+
+/*
+** f i c l E x e c
+** Evaluates a block of input text in the context of the
+** specified interpreter. Emits any requested output to the
+** interpreter's output function. If the input string is NULL
+** terminated, you can pass -1 as nChars rather than count it.
+** Execution returns when the text block has been executed,
+** or an error occurs.
+** Returns one of the VM_XXXX codes defined in ficl.h:
+** VM_OUTOFTEXT is the normal exit condition
+** VM_ERREXIT means that the interp encountered a syntax error
+** and the vm has been reset to recover (some or all
+** of the text block got ignored
+** VM_USEREXIT means that the user executed the "bye" command
+** to shut down the interpreter. This would be a good
+** time to delete the vm, etc -- or you can ignore this
+** signal.
+** VM_ABORT and VM_ABORTQ are generated by 'abort' and 'abort"'
+** commands.
+** Preconditions: successful execution of ficlInitSystem,
+** Successful creation and init of the VM by ficlNewVM (or equiv)
+**
+** If you call ficlExec() or one of its brothers, you MUST
+** ensure pVM->sourceID was set to a sensible value.
+** ficlExec() explicitly DOES NOT manage SOURCE-ID for you.
+*/
+int ficlExec (FICL_VM *pVM, char *pText);
+int ficlExecC(FICL_VM *pVM, char *pText, FICL_INT nChars);
+int ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord);
+
+/*
+** ficlExecFD(FICL_VM *pVM, int fd);
+ * Evaluates text from file passed in via fd.
+ * Execution returns when all of file has been executed or an
+ * error occurs.
+ */
+int ficlExecFD(FICL_VM *pVM, int fd);
+
+/*
+** Create a new VM from the heap, and link it into the system VM list.
+** Initializes the VM and binds default sized stacks to it. Returns the
+** address of the VM, or NULL if an error occurs.
+** Precondition: successful execution of ficlInitSystem
+*/
+FICL_VM *ficlNewVM(FICL_SYSTEM *pSys);
+
+/*
+** Force deletion of a VM. You do not need to do this
+** unless you're creating and discarding a lot of VMs.
+** For systems that use a constant pool of VMs for the life
+** of the system, ficltermSystem takes care of VM cleanup
+** automatically.
+*/
+void ficlFreeVM(FICL_VM *pVM);
+
+
+/*
+** Set the stack sizes (return and parameter) to be used for all
+** subsequently created VMs. Returns actual stack size to be used.
+*/
+int ficlSetStackSize(int nStackCells);
+
+/*
+** Returns the address of the most recently defined word in the system
+** dictionary with the given name, or NULL if no match.
+** Precondition: successful execution of ficlInitSystem
+*/
+FICL_WORD *ficlLookup(FICL_SYSTEM *pSys, char *name);
+
+/*
+** f i c l G e t D i c t
+** Utility function - returns the address of the system dictionary.
+** Precondition: successful execution of ficlInitSystem
+*/
+FICL_DICT *ficlGetDict(FICL_SYSTEM *pSys);
+FICL_DICT *ficlGetEnv (FICL_SYSTEM *pSys);
+void ficlSetEnv (FICL_SYSTEM *pSys, char *name, FICL_UNS value);
+void ficlSetEnvD(FICL_SYSTEM *pSys, char *name, FICL_UNS hi, FICL_UNS lo);
+#if FICL_WANT_LOCALS
+FICL_DICT *ficlGetLoc (FICL_SYSTEM *pSys);
+#endif
+/*
+** f i c l B u i l d
+** Builds a word into the system default dictionary in a thread-safe way.
+** Preconditions: system must be initialized, and there must
+** be enough space for the new word's header! Operation is
+** controlled by ficlLockDictionary, so any initialization
+** required by your version of the function (if you "overrode"
+** it) must be complete at this point.
+** Parameters:
+** name -- the name of the word to be built
+** code -- code to execute when the word is invoked - must take a single param
+** pointer to a FICL_VM
+** flags -- 0 or more of FW_IMMEDIATE, FW_COMPILE, use bitwise OR!
+** Most words can use FW_DEFAULT.
+** nAllot - number of extra cells to allocate in the parameter area (usually zero)
+*/
+int ficlBuild(FICL_SYSTEM *pSys, char *name, FICL_CODE code, char flags);
+
+/*
+** f i c l C o m p i l e C o r e
+** Builds the ANS CORE wordset into the dictionary - called by
+** ficlInitSystem - no need to waste dict space by doing it again.
+*/
+void ficlCompileCore(FICL_SYSTEM *pSys);
+void ficlCompilePrefix(FICL_SYSTEM *pSys);
+void ficlCompileSearch(FICL_SYSTEM *pSys);
+void ficlCompileSoftCore(FICL_SYSTEM *pSys);
+void ficlCompileTools(FICL_SYSTEM *pSys);
+void ficlCompileFile(FICL_SYSTEM *pSys);
+#if FICL_WANT_FLOAT
+void ficlCompileFloat(FICL_SYSTEM *pSys);
+int ficlParseFloatNumber( FICL_VM *pVM, STRINGINFO si ); /* float.c */
+#endif
+#if FICL_PLATFORM_EXTEND
+void ficlCompilePlatform(FICL_SYSTEM *pSys);
+#endif
+int ficlParsePrefix(FICL_VM *pVM, STRINGINFO si);
+
+/*
+** from words.c...
+*/
+void constantParen(FICL_VM *pVM);
+void twoConstParen(FICL_VM *pVM);
+int ficlParseNumber(FICL_VM *pVM, STRINGINFO si);
+void ficlTick(FICL_VM *pVM);
+void parseStepParen(FICL_VM *pVM);
+
+/*
+** From tools.c
+*/
+int isAFiclWord(FICL_DICT *pd, FICL_WORD *pFW);
+
+/*
+** The following supports SEE and the debugger.
+*/
+typedef enum
+{
+ BRANCH,
+ COLON,
+ CONSTANT,
+ CREATE,
+ DO,
+ DOES,
+ IF,
+ LITERAL,
+ LOOP,
+ OF,
+ PLOOP,
+ PRIMITIVE,
+ QDO,
+ STRINGLIT,
+ CSTRINGLIT,
+#if FICL_WANT_USER
+ USER,
+#endif
+ VARIABLE,
+} WORDKIND;
+
+WORDKIND ficlWordClassify(FICL_WORD *pFW);
+
+/*
+** Dictionary on-demand resizing
+*/
+extern CELL dictThreshold;
+extern CELL dictIncrease;
+
+/*
+** Various FreeBSD goodies
+*/
+
+#if defined(__i386__) && !defined(TESTMAIN)
+extern void ficlOutb(FICL_VM *pVM);
+extern void ficlInb(FICL_VM *pVM);
+#endif
+
+extern void ficlSetenv(FICL_VM *pVM);
+extern void ficlSetenvq(FICL_VM *pVM);
+extern void ficlGetenv(FICL_VM *pVM);
+extern void ficlUnsetenv(FICL_VM *pVM);
+extern void ficlCopyin(FICL_VM *pVM);
+extern void ficlCopyout(FICL_VM *pVM);
+extern void ficlFindfile(FICL_VM *pVM);
+extern void ficlCcall(FICL_VM *pVM);
+#if !defined(TESTMAIN)
+extern void ficlPnpdevices(FICL_VM *pVM);
+extern void ficlPnphandlers(FICL_VM *pVM);
+#endif
+
+/*
+** Used with File-Access wordset.
+*/
+#define FICL_FAM_READ 1
+#define FICL_FAM_WRITE 2
+#define FICL_FAM_APPEND 4
+#define FICL_FAM_BINARY 8
+
+#define FICL_FAM_OPEN_MODE(fam) ((fam) & (FICL_FAM_READ | FICL_FAM_WRITE | FICL_FAM_APPEND))
+
+
+#if (FICL_WANT_FILE)
+typedef struct ficlFILE
+{
+ FILE *f;
+ char filename[256];
+} ficlFILE;
+#endif
+
+#include <sys/linker_set.h>
+
+typedef void ficlCompileFcn(FICL_SYSTEM *);
+#define FICL_COMPILE_SET(func) \
+ DATA_SET(Xficl_compile_set, func)
+SET_DECLARE(Xficl_compile_set, ficlCompileFcn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FICL_H__ */
diff --git a/stand/ficl/fileaccess.c b/stand/ficl/fileaccess.c
new file mode 100644
index 0000000..2b981c8
--- /dev/null
+++ b/stand/ficl/fileaccess.c
@@ -0,0 +1,425 @@
+/* $FreeBSD$ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include "ficl.h"
+
+#if FICL_WANT_FILE
+/*
+**
+** fileaccess.c
+**
+** Implements all of the File Access word set that can be implemented in portable C.
+**
+*/
+
+static void pushIor(FICL_VM *pVM, int success)
+{
+ int ior;
+ if (success)
+ ior = 0;
+ else
+ ior = errno;
+ stackPushINT(pVM->pStack, ior);
+}
+
+
+
+static void ficlFopen(FICL_VM *pVM, char *writeMode) /* ( c-addr u fam -- fileid ior ) */
+{
+ int fam = stackPopINT(pVM->pStack);
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+ char mode[4];
+ FILE *f;
+
+ char *filename = (char *)alloca(length + 1);
+ memcpy(filename, address, length);
+ filename[length] = 0;
+
+ *mode = 0;
+
+ switch (FICL_FAM_OPEN_MODE(fam))
+ {
+ case 0:
+ stackPushPtr(pVM->pStack, NULL);
+ stackPushINT(pVM->pStack, EINVAL);
+ return;
+ case FICL_FAM_READ:
+ strcat(mode, "r");
+ break;
+ case FICL_FAM_WRITE:
+ strcat(mode, writeMode);
+ break;
+ case FICL_FAM_READ | FICL_FAM_WRITE:
+ strcat(mode, writeMode);
+ strcat(mode, "+");
+ break;
+ }
+
+ strcat(mode, (fam & FICL_FAM_BINARY) ? "b" : "t");
+
+ f = fopen(filename, mode);
+ if (f == NULL)
+ stackPushPtr(pVM->pStack, NULL);
+ else
+ {
+ ficlFILE *ff = (ficlFILE *)malloc(sizeof(ficlFILE));
+ strcpy(ff->filename, filename);
+ ff->f = f;
+ stackPushPtr(pVM->pStack, ff);
+
+ fseek(f, 0, SEEK_SET);
+ }
+ pushIor(pVM, f != NULL);
+}
+
+
+
+static void ficlOpenFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
+{
+ ficlFopen(pVM, "a");
+}
+
+
+static void ficlCreateFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
+{
+ ficlFopen(pVM, "w");
+}
+
+
+static int closeFiclFILE(ficlFILE *ff) /* ( fileid -- ior ) */
+{
+ FILE *f = ff->f;
+ free(ff);
+ return !fclose(f);
+}
+
+static void ficlCloseFile(FICL_VM *pVM) /* ( fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ pushIor(pVM, closeFiclFILE(ff));
+}
+
+static void ficlDeleteFile(FICL_VM *pVM) /* ( c-addr u -- ior ) */
+{
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ char *filename = (char *)alloca(length + 1);
+ memcpy(filename, address, length);
+ filename[length] = 0;
+
+ pushIor(pVM, !unlink(filename));
+}
+
+static void ficlRenameFile(FICL_VM *pVM) /* ( c-addr1 u1 c-addr2 u2 -- ior ) */
+{
+ int length;
+ void *address;
+ char *from;
+ char *to;
+
+ length = stackPopINT(pVM->pStack);
+ address = (void *)stackPopPtr(pVM->pStack);
+ to = (char *)alloca(length + 1);
+ memcpy(to, address, length);
+ to[length] = 0;
+
+ length = stackPopINT(pVM->pStack);
+ address = (void *)stackPopPtr(pVM->pStack);
+
+ from = (char *)alloca(length + 1);
+ memcpy(from, address, length);
+ from[length] = 0;
+
+ pushIor(pVM, !rename(from, to));
+}
+
+static void ficlFileStatus(FICL_VM *pVM) /* ( c-addr u -- x ior ) */
+{
+ struct stat statbuf;
+
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ char *filename = (char *)alloca(length + 1);
+ memcpy(filename, address, length);
+ filename[length] = 0;
+
+ if (stat(filename, &statbuf) == 0)
+ {
+ /*
+ ** the "x" left on the stack is implementation-defined.
+ ** I push the file's access mode (readable, writeable, is directory, etc)
+ ** as defined by ANSI C.
+ */
+ stackPushINT(pVM->pStack, statbuf.st_mode);
+ stackPushINT(pVM->pStack, 0);
+ }
+ else
+ {
+ stackPushINT(pVM->pStack, -1);
+ stackPushINT(pVM->pStack, ENOENT);
+ }
+}
+
+
+static void ficlFilePosition(FICL_VM *pVM) /* ( fileid -- ud ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ long ud = ftell(ff->f);
+ stackPushINT(pVM->pStack, ud);
+ pushIor(pVM, ud != -1);
+}
+
+
+
+static long fileSize(FILE *f)
+{
+ struct stat statbuf;
+ statbuf.st_size = -1;
+ if (fstat(fileno(f), &statbuf) != 0)
+ return -1;
+ return statbuf.st_size;
+}
+
+
+
+static void ficlFileSize(FICL_VM *pVM) /* ( fileid -- ud ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ long ud = fileSize(ff->f);
+ stackPushINT(pVM->pStack, ud);
+ pushIor(pVM, ud != -1);
+}
+
+
+
+#define nLINEBUF 256
+static void ficlIncludeFile(FICL_VM *pVM) /* ( i*x fileid -- j*x ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ CELL id = pVM->sourceID;
+ int result = VM_OUTOFTEXT;
+ long currentPosition, totalSize;
+ long size;
+ pVM->sourceID.p = (void *)ff;
+
+ currentPosition = ftell(ff->f);
+ totalSize = fileSize(ff->f);
+ size = totalSize - currentPosition;
+
+ if ((totalSize != -1) && (currentPosition != -1) && (size > 0))
+ {
+ char *buffer = (char *)malloc(size);
+ long got = fread(buffer, 1, size, ff->f);
+ if (got == size)
+ result = ficlExecC(pVM, buffer, size);
+ }
+
+#if 0
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ CELL id = pVM->sourceID;
+ char cp[nLINEBUF];
+ int nLine = 0;
+ int keepGoing;
+ int result;
+ pVM->sourceID.p = (void *)ff;
+
+ /* feed each line to ficlExec */
+ keepGoing = TRUE;
+ while (keepGoing && fgets(cp, nLINEBUF, ff->f))
+ {
+ int len = strlen(cp) - 1;
+
+ nLine++;
+ if (len <= 0)
+ continue;
+
+ if (cp[len] == '\n')
+ cp[len] = '\0';
+
+ result = ficlExec(pVM, cp);
+
+ switch (result)
+ {
+ case VM_OUTOFTEXT:
+ case VM_USEREXIT:
+ break;
+
+ default:
+ pVM->sourceID = id;
+ keepGoing = FALSE;
+ break;
+ }
+ }
+#endif /* 0 */
+ /*
+ ** Pass an empty line with SOURCE-ID == -1 to flush
+ ** any pending REFILLs (as required by FILE wordset)
+ */
+ pVM->sourceID.i = -1;
+ ficlExec(pVM, "");
+
+ pVM->sourceID = id;
+ closeFiclFILE(ff);
+}
+
+
+
+static void ficlReadFile(FICL_VM *pVM) /* ( c-addr u1 fileid -- u2 ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+ int result;
+
+ clearerr(ff->f);
+ result = fread(address, 1, length, ff->f);
+
+ stackPushINT(pVM->pStack, result);
+ pushIor(pVM, ferror(ff->f) == 0);
+}
+
+
+
+static void ficlReadLine(FICL_VM *pVM) /* ( c-addr u1 fileid -- u2 flag ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ int length = stackPopINT(pVM->pStack);
+ char *address = (char *)stackPopPtr(pVM->pStack);
+ int error;
+ int flag;
+
+ if (feof(ff->f))
+ {
+ stackPushINT(pVM->pStack, -1);
+ stackPushINT(pVM->pStack, 0);
+ stackPushINT(pVM->pStack, 0);
+ return;
+ }
+
+ clearerr(ff->f);
+ *address = 0;
+ fgets(address, length, ff->f);
+
+ error = ferror(ff->f);
+ if (error != 0)
+ {
+ stackPushINT(pVM->pStack, -1);
+ stackPushINT(pVM->pStack, 0);
+ stackPushINT(pVM->pStack, error);
+ return;
+ }
+
+ length = strlen(address);
+ flag = (length > 0);
+ if (length && ((address[length - 1] == '\r') || (address[length - 1] == '\n')))
+ length--;
+
+ stackPushINT(pVM->pStack, length);
+ stackPushINT(pVM->pStack, flag);
+ stackPushINT(pVM->pStack, 0); /* ior */
+}
+
+
+
+static void ficlWriteFile(FICL_VM *pVM) /* ( c-addr u1 fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ clearerr(ff->f);
+ fwrite(address, 1, length, ff->f);
+ pushIor(pVM, ferror(ff->f) == 0);
+}
+
+
+
+static void ficlWriteLine(FICL_VM *pVM) /* ( c-addr u1 fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ size_t length = (size_t)stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ clearerr(ff->f);
+ if (fwrite(address, 1, length, ff->f) == length)
+ fwrite("\n", 1, 1, ff->f);
+ pushIor(pVM, ferror(ff->f) == 0);
+}
+
+
+
+static void ficlRepositionFile(FICL_VM *pVM) /* ( ud fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ size_t ud = (size_t)stackPopINT(pVM->pStack);
+
+ pushIor(pVM, fseek(ff->f, ud, SEEK_SET) == 0);
+}
+
+
+
+static void ficlFlushFile(FICL_VM *pVM) /* ( fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ pushIor(pVM, fflush(ff->f) == 0);
+}
+
+
+
+#if FICL_HAVE_FTRUNCATE
+
+static void ficlResizeFile(FICL_VM *pVM) /* ( ud fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ size_t ud = (size_t)stackPopINT(pVM->pStack);
+
+ pushIor(pVM, ftruncate(fileno(ff->f), ud) == 0);
+}
+
+#endif /* FICL_HAVE_FTRUNCATE */
+
+#endif /* FICL_WANT_FILE */
+
+
+
+void ficlCompileFile(FICL_SYSTEM *pSys)
+{
+#if FICL_WANT_FILE
+ FICL_DICT *dp = pSys->dp;
+ assert(dp);
+
+ dictAppendWord(dp, "create-file", ficlCreateFile, FW_DEFAULT);
+ dictAppendWord(dp, "open-file", ficlOpenFile, FW_DEFAULT);
+ dictAppendWord(dp, "close-file", ficlCloseFile, FW_DEFAULT);
+ dictAppendWord(dp, "include-file", ficlIncludeFile, FW_DEFAULT);
+ dictAppendWord(dp, "read-file", ficlReadFile, FW_DEFAULT);
+ dictAppendWord(dp, "read-line", ficlReadLine, FW_DEFAULT);
+ dictAppendWord(dp, "write-file", ficlWriteFile, FW_DEFAULT);
+ dictAppendWord(dp, "write-line", ficlWriteLine, FW_DEFAULT);
+ dictAppendWord(dp, "file-position", ficlFilePosition, FW_DEFAULT);
+ dictAppendWord(dp, "file-size", ficlFileSize, FW_DEFAULT);
+ dictAppendWord(dp, "reposition-file", ficlRepositionFile, FW_DEFAULT);
+ dictAppendWord(dp, "file-status", ficlFileStatus, FW_DEFAULT);
+ dictAppendWord(dp, "flush-file", ficlFlushFile, FW_DEFAULT);
+
+ dictAppendWord(dp, "delete-file", ficlDeleteFile, FW_DEFAULT);
+ dictAppendWord(dp, "rename-file", ficlRenameFile, FW_DEFAULT);
+
+#ifdef FICL_HAVE_FTRUNCATE
+ dictAppendWord(dp, "resize-file", ficlResizeFile, FW_DEFAULT);
+
+ ficlSetEnv(pSys, "file", FICL_TRUE);
+ ficlSetEnv(pSys, "file-ext", FICL_TRUE);
+#endif /* FICL_HAVE_FTRUNCATE */
+#else
+ (void)pSys;
+#endif /* FICL_WANT_FILE */
+}
diff --git a/stand/ficl/float.c b/stand/ficl/float.c
new file mode 100644
index 0000000..d757b23
--- /dev/null
+++ b/stand/ficl/float.c
@@ -0,0 +1,1067 @@
+/*******************************************************************
+** f l o a t . c
+** Forth Inspired Command Language
+** ANS Forth FLOAT word-set written in C
+** Author: Guy Carver & John Sadler (john_sadler@alum.mit.edu)
+** Created: Apr 2001
+** $Id: float.c,v 1.8 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include "ficl.h"
+
+#if FICL_WANT_FLOAT
+
+/*******************************************************************
+** Do float addition r1 + r2.
+** f+ ( r1 r2 -- r )
+*******************************************************************/
+static void Fadd(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 1);
+#endif
+
+ f = POPFLOAT();
+ f += GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float subtraction r1 - r2.
+** f- ( r1 r2 -- r )
+*******************************************************************/
+static void Fsub(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 1);
+#endif
+
+ f = POPFLOAT();
+ f = GETTOPF().f - f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float multiplication r1 * r2.
+** f* ( r1 r2 -- r )
+*******************************************************************/
+static void Fmul(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 1);
+#endif
+
+ f = POPFLOAT();
+ f *= GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float negation.
+** fnegate ( r -- r )
+*******************************************************************/
+static void Fnegate(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+#endif
+
+ f = -GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float division r1 / r2.
+** f/ ( r1 r2 -- r )
+*******************************************************************/
+static void Fdiv(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 1);
+#endif
+
+ f = POPFLOAT();
+ f = GETTOPF().f / f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float + integer r + n.
+** f+i ( r n -- r )
+*******************************************************************/
+static void Faddi(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = (FICL_FLOAT)POPINT();
+ f += GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float - integer r - n.
+** f-i ( r n -- r )
+*******************************************************************/
+static void Fsubi(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = GETTOPF().f;
+ f -= (FICL_FLOAT)POPINT();
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float * integer r * n.
+** f*i ( r n -- r )
+*******************************************************************/
+static void Fmuli(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = (FICL_FLOAT)POPINT();
+ f *= GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float / integer r / n.
+** f/i ( r n -- r )
+*******************************************************************/
+static void Fdivi(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = GETTOPF().f;
+ f /= (FICL_FLOAT)POPINT();
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do integer - float n - r.
+** i-f ( n r -- r )
+*******************************************************************/
+static void isubf(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = (FICL_FLOAT)POPINT();
+ f -= GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do integer / float n / r.
+** i/f ( n r -- r )
+*******************************************************************/
+static void idivf(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1,1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = (FICL_FLOAT)POPINT();
+ f /= GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do integer to float conversion.
+** int>float ( n -- r )
+*******************************************************************/
+static void itof(FICL_VM *pVM)
+{
+ float f;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+ vmCheckFStack(pVM, 0, 1);
+#endif
+
+ f = (float)POPINT();
+ PUSHFLOAT(f);
+}
+
+/*******************************************************************
+** Do float to integer conversion.
+** float>int ( r -- n )
+*******************************************************************/
+static void Ftoi(FICL_VM *pVM)
+{
+ FICL_INT i;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ i = (FICL_INT)POPFLOAT();
+ PUSHINT(i);
+}
+
+/*******************************************************************
+** Floating point constant execution word.
+*******************************************************************/
+void FconstantParen(FICL_VM *pVM)
+{
+ FICL_WORD *pFW = pVM->runningWord;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 0, 1);
+#endif
+
+ PUSHFLOAT(pFW->param[0].f);
+}
+
+/*******************************************************************
+** Create a floating point constant.
+** fconstant ( r -"name"- )
+*******************************************************************/
+static void Fconstant(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ dictAppendWord2(dp, si, FconstantParen, FW_DEFAULT);
+ dictAppendCell(dp, stackPop(pVM->fStack));
+}
+
+/*******************************************************************
+** Display a float in decimal format.
+** f. ( r -- )
+*******************************************************************/
+static void FDot(FICL_VM *pVM)
+{
+ float f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ f = POPFLOAT();
+ sprintf(pVM->pad,"%#f ",f);
+ vmTextOut(pVM, pVM->pad, 0);
+}
+
+/*******************************************************************
+** Display a float in engineering format.
+** fe. ( r -- )
+*******************************************************************/
+static void EDot(FICL_VM *pVM)
+{
+ float f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ f = POPFLOAT();
+ sprintf(pVM->pad,"%#e ",f);
+ vmTextOut(pVM, pVM->pad, 0);
+}
+
+/**************************************************************************
+ d i s p l a y FS t a c k
+** Display the parameter stack (code for "f.s")
+** f.s ( -- )
+**************************************************************************/
+static void displayFStack(FICL_VM *pVM)
+{
+ int d = stackDepth(pVM->fStack);
+ int i;
+ CELL *pCell;
+
+ vmCheckFStack(pVM, 0, 0);
+
+ vmTextOut(pVM, "F:", 0);
+
+ if (d == 0)
+ vmTextOut(pVM, "[0]", 0);
+ else
+ {
+ ltoa(d, &pVM->pad[1], pVM->base);
+ pVM->pad[0] = '[';
+ strcat(pVM->pad,"] ");
+ vmTextOut(pVM,pVM->pad,0);
+
+ pCell = pVM->fStack->sp - d;
+ for (i = 0; i < d; i++)
+ {
+ sprintf(pVM->pad,"%#f ",(*pCell++).f);
+ vmTextOut(pVM,pVM->pad,0);
+ }
+ }
+}
+
+/*******************************************************************
+** Do float stack depth.
+** fdepth ( -- n )
+*******************************************************************/
+static void Fdepth(FICL_VM *pVM)
+{
+ int i;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ i = stackDepth(pVM->fStack);
+ PUSHINT(i);
+}
+
+/*******************************************************************
+** Do float stack drop.
+** fdrop ( r -- )
+*******************************************************************/
+static void Fdrop(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ DROPF(1);
+}
+
+/*******************************************************************
+** Do float stack 2drop.
+** f2drop ( r r -- )
+*******************************************************************/
+static void FtwoDrop(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 0);
+#endif
+
+ DROPF(2);
+}
+
+/*******************************************************************
+** Do float stack dup.
+** fdup ( r -- r r )
+*******************************************************************/
+static void Fdup(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 2);
+#endif
+
+ PICKF(0);
+}
+
+/*******************************************************************
+** Do float stack 2dup.
+** f2dup ( r1 r2 -- r1 r2 r1 r2 )
+*******************************************************************/
+static void FtwoDup(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 4);
+#endif
+
+ PICKF(1);
+ PICKF(1);
+}
+
+/*******************************************************************
+** Do float stack over.
+** fover ( r1 r2 -- r1 r2 r1 )
+*******************************************************************/
+static void Fover(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 3);
+#endif
+
+ PICKF(1);
+}
+
+/*******************************************************************
+** Do float stack 2over.
+** f2over ( r1 r2 r3 -- r1 r2 r3 r1 r2 )
+*******************************************************************/
+static void FtwoOver(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 4, 6);
+#endif
+
+ PICKF(3);
+ PICKF(3);
+}
+
+/*******************************************************************
+** Do float stack pick.
+** fpick ( n -- r )
+*******************************************************************/
+static void Fpick(FICL_VM *pVM)
+{
+ CELL c = POP();
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, c.i+1, c.i+2);
+#endif
+
+ PICKF(c.i);
+}
+
+/*******************************************************************
+** Do float stack ?dup.
+** f?dup ( r -- r )
+*******************************************************************/
+static void FquestionDup(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 2);
+#endif
+
+ c = GETTOPF();
+ if (c.f != 0)
+ PICKF(0);
+}
+
+/*******************************************************************
+** Do float stack roll.
+** froll ( n -- )
+*******************************************************************/
+static void Froll(FICL_VM *pVM)
+{
+ int i = POP().i;
+ i = (i > 0) ? i : 0;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, i+1, i+1);
+#endif
+
+ ROLLF(i);
+}
+
+/*******************************************************************
+** Do float stack -roll.
+** f-roll ( n -- )
+*******************************************************************/
+static void FminusRoll(FICL_VM *pVM)
+{
+ int i = POP().i;
+ i = (i > 0) ? i : 0;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, i+1, i+1);
+#endif
+
+ ROLLF(-i);
+}
+
+/*******************************************************************
+** Do float stack rot.
+** frot ( r1 r2 r3 -- r2 r3 r1 )
+*******************************************************************/
+static void Frot(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 3, 3);
+#endif
+
+ ROLLF(2);
+}
+
+/*******************************************************************
+** Do float stack -rot.
+** f-rot ( r1 r2 r3 -- r3 r1 r2 )
+*******************************************************************/
+static void Fminusrot(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 3, 3);
+#endif
+
+ ROLLF(-2);
+}
+
+/*******************************************************************
+** Do float stack swap.
+** fswap ( r1 r2 -- r2 r1 )
+*******************************************************************/
+static void Fswap(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 2);
+#endif
+
+ ROLLF(1);
+}
+
+/*******************************************************************
+** Do float stack 2swap
+** f2swap ( r1 r2 r3 r4 -- r3 r4 r1 r2 )
+*******************************************************************/
+static void FtwoSwap(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 4, 4);
+#endif
+
+ ROLLF(3);
+ ROLLF(3);
+}
+
+/*******************************************************************
+** Get a floating point number from a variable.
+** f@ ( n -- r )
+*******************************************************************/
+static void Ffetch(FICL_VM *pVM)
+{
+ CELL *pCell;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 0, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ pCell = (CELL *)POPPTR();
+ PUSHFLOAT(pCell->f);
+}
+
+/*******************************************************************
+** Store a floating point number into a variable.
+** f! ( r n -- )
+*******************************************************************/
+static void Fstore(FICL_VM *pVM)
+{
+ CELL *pCell;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ pCell = (CELL *)POPPTR();
+ pCell->f = POPFLOAT();
+}
+
+/*******************************************************************
+** Add a floating point number to contents of a variable.
+** f+! ( r n -- )
+*******************************************************************/
+static void FplusStore(FICL_VM *pVM)
+{
+ CELL *pCell;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ pCell = (CELL *)POPPTR();
+ pCell->f += POPFLOAT();
+}
+
+/*******************************************************************
+** Floating point literal execution word.
+*******************************************************************/
+static void fliteralParen(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ PUSHFLOAT(*(float*)(pVM->ip));
+ vmBranchRelative(pVM, 1);
+}
+
+/*******************************************************************
+** Compile a floating point literal.
+*******************************************************************/
+static void fliteralIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *pfLitParen = ficlLookup(pVM->pSys, "(fliteral)");
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ dictAppendCell(dp, LVALUEtoCELL(pfLitParen));
+ dictAppendCell(dp, stackPop(pVM->fStack));
+}
+
+/*******************************************************************
+** Do float 0= comparison r = 0.0.
+** f0= ( r -- T/F )
+*******************************************************************/
+static void FzeroEquals(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0); /* Make sure something on float stack. */
+ vmCheckStack(pVM, 0, 1); /* Make sure room for result. */
+#endif
+
+ c.i = FICL_BOOL(POPFLOAT() == 0);
+ PUSH(c);
+}
+
+/*******************************************************************
+** Do float 0< comparison r < 0.0.
+** f0< ( r -- T/F )
+*******************************************************************/
+static void FzeroLess(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0); /* Make sure something on float stack. */
+ vmCheckStack(pVM, 0, 1); /* Make sure room for result. */
+#endif
+
+ c.i = FICL_BOOL(POPFLOAT() < 0);
+ PUSH(c);
+}
+
+/*******************************************************************
+** Do float 0> comparison r > 0.0.
+** f0> ( r -- T/F )
+*******************************************************************/
+static void FzeroGreater(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ c.i = FICL_BOOL(POPFLOAT() > 0);
+ PUSH(c);
+}
+
+/*******************************************************************
+** Do float = comparison r1 = r2.
+** f= ( r1 r2 -- T/F )
+*******************************************************************/
+static void FisEqual(FICL_VM *pVM)
+{
+ float x, y;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 0);
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ x = POPFLOAT();
+ y = POPFLOAT();
+ PUSHINT(FICL_BOOL(x == y));
+}
+
+/*******************************************************************
+** Do float < comparison r1 < r2.
+** f< ( r1 r2 -- T/F )
+*******************************************************************/
+static void FisLess(FICL_VM *pVM)
+{
+ float x, y;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 0);
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ y = POPFLOAT();
+ x = POPFLOAT();
+ PUSHINT(FICL_BOOL(x < y));
+}
+
+/*******************************************************************
+** Do float > comparison r1 > r2.
+** f> ( r1 r2 -- T/F )
+*******************************************************************/
+static void FisGreater(FICL_VM *pVM)
+{
+ float x, y;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 0);
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ y = POPFLOAT();
+ x = POPFLOAT();
+ PUSHINT(FICL_BOOL(x > y));
+}
+
+
+/*******************************************************************
+** Move float to param stack (assumes they both fit in a single CELL)
+** f>s
+*******************************************************************/
+static void FFrom(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ c = stackPop(pVM->fStack);
+ stackPush(pVM->pStack, c);
+ return;
+}
+
+static void ToF(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 0, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ c = stackPop(pVM->pStack);
+ stackPush(pVM->fStack, c);
+ return;
+}
+
+
+/**************************************************************************
+ F l o a t P a r s e S t a t e
+** Enum to determine the current segement of a floating point number
+** being parsed.
+**************************************************************************/
+#define NUMISNEG 1
+#define EXPISNEG 2
+
+typedef enum _floatParseState
+{
+ FPS_START,
+ FPS_ININT,
+ FPS_INMANT,
+ FPS_STARTEXP,
+ FPS_INEXP
+} FloatParseState;
+
+/**************************************************************************
+ f i c l P a r s e F l o a t N u m b e r
+** pVM -- Virtual Machine pointer.
+** si -- String to parse.
+** Returns 1 if successful, 0 if not.
+**************************************************************************/
+int ficlParseFloatNumber( FICL_VM *pVM, STRINGINFO si )
+{
+ unsigned char ch, digit;
+ char *cp;
+ FICL_COUNT count;
+ float power;
+ float accum = 0.0f;
+ float mant = 0.1f;
+ FICL_INT exponent = 0;
+ char flag = 0;
+ FloatParseState estate = FPS_START;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 0, 1);
+#endif
+
+ /*
+ ** floating point numbers only allowed in base 10
+ */
+ if (pVM->base != 10)
+ return(0);
+
+
+ cp = SI_PTR(si);
+ count = (FICL_COUNT)SI_COUNT(si);
+
+ /* Loop through the string's characters. */
+ while ((count--) && ((ch = *cp++) != 0))
+ {
+ switch (estate)
+ {
+ /* At start of the number so look for a sign. */
+ case FPS_START:
+ {
+ estate = FPS_ININT;
+ if (ch == '-')
+ {
+ flag |= NUMISNEG;
+ break;
+ }
+ if (ch == '+')
+ {
+ break;
+ }
+ } /* Note! Drop through to FPS_ININT */
+ /*
+ **Converting integer part of number.
+ ** Only allow digits, decimal and 'E'.
+ */
+ case FPS_ININT:
+ {
+ if (ch == '.')
+ {
+ estate = FPS_INMANT;
+ }
+ else if ((ch == 'e') || (ch == 'E'))
+ {
+ estate = FPS_STARTEXP;
+ }
+ else
+ {
+ digit = (unsigned char)(ch - '0');
+ if (digit > 9)
+ return(0);
+
+ accum = accum * 10 + digit;
+
+ }
+ break;
+ }
+ /*
+ ** Processing the fraction part of number.
+ ** Only allow digits and 'E'
+ */
+ case FPS_INMANT:
+ {
+ if ((ch == 'e') || (ch == 'E'))
+ {
+ estate = FPS_STARTEXP;
+ }
+ else
+ {
+ digit = (unsigned char)(ch - '0');
+ if (digit > 9)
+ return(0);
+
+ accum += digit * mant;
+ mant *= 0.1f;
+ }
+ break;
+ }
+ /* Start processing the exponent part of number. */
+ /* Look for sign. */
+ case FPS_STARTEXP:
+ {
+ estate = FPS_INEXP;
+
+ if (ch == '-')
+ {
+ flag |= EXPISNEG;
+ break;
+ }
+ else if (ch == '+')
+ {
+ break;
+ }
+ } /* Note! Drop through to FPS_INEXP */
+ /*
+ ** Processing the exponent part of number.
+ ** Only allow digits.
+ */
+ case FPS_INEXP:
+ {
+ digit = (unsigned char)(ch - '0');
+ if (digit > 9)
+ return(0);
+
+ exponent = exponent * 10 + digit;
+
+ break;
+ }
+ }
+ }
+
+ /* If parser never made it to the exponent this is not a float. */
+ if (estate < FPS_STARTEXP)
+ return(0);
+
+ /* Set the sign of the number. */
+ if (flag & NUMISNEG)
+ accum = -accum;
+
+ /* If exponent is not 0 then adjust number by it. */
+ if (exponent != 0)
+ {
+ /* Determine if exponent is negative. */
+ if (flag & EXPISNEG)
+ {
+ exponent = -exponent;
+ }
+ /* power = 10^x */
+ power = (float)pow(10.0, exponent);
+ accum *= power;
+ }
+
+ PUSHFLOAT(accum);
+ if (pVM->state == COMPILE)
+ fliteralIm(pVM);
+
+ return(1);
+}
+
+#endif /* FICL_WANT_FLOAT */
+
+/**************************************************************************
+** Add float words to a system's dictionary.
+** pSys -- Pointer to the FICL sytem to add float words to.
+**************************************************************************/
+void ficlCompileFloat(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert(dp);
+
+#if FICL_WANT_FLOAT
+ dictAppendWord(dp, ">float", ToF, FW_DEFAULT);
+ /* d>f */
+ dictAppendWord(dp, "f!", Fstore, FW_DEFAULT);
+ dictAppendWord(dp, "f*", Fmul, FW_DEFAULT);
+ dictAppendWord(dp, "f+", Fadd, FW_DEFAULT);
+ dictAppendWord(dp, "f-", Fsub, FW_DEFAULT);
+ dictAppendWord(dp, "f/", Fdiv, FW_DEFAULT);
+ dictAppendWord(dp, "f0<", FzeroLess, FW_DEFAULT);
+ dictAppendWord(dp, "f0=", FzeroEquals, FW_DEFAULT);
+ dictAppendWord(dp, "f<", FisLess, FW_DEFAULT);
+ /*
+ f>d
+ */
+ dictAppendWord(dp, "f@", Ffetch, FW_DEFAULT);
+ /*
+ falign
+ faligned
+ */
+ dictAppendWord(dp, "fconstant", Fconstant, FW_DEFAULT);
+ dictAppendWord(dp, "fdepth", Fdepth, FW_DEFAULT);
+ dictAppendWord(dp, "fdrop", Fdrop, FW_DEFAULT);
+ dictAppendWord(dp, "fdup", Fdup, FW_DEFAULT);
+ dictAppendWord(dp, "fliteral", fliteralIm, FW_IMMEDIATE);
+/*
+ float+
+ floats
+ floor
+ fmax
+ fmin
+*/
+ dictAppendWord(dp, "f?dup", FquestionDup, FW_DEFAULT);
+ dictAppendWord(dp, "f=", FisEqual, FW_DEFAULT);
+ dictAppendWord(dp, "f>", FisGreater, FW_DEFAULT);
+ dictAppendWord(dp, "f0>", FzeroGreater, FW_DEFAULT);
+ dictAppendWord(dp, "f2drop", FtwoDrop, FW_DEFAULT);
+ dictAppendWord(dp, "f2dup", FtwoDup, FW_DEFAULT);
+ dictAppendWord(dp, "f2over", FtwoOver, FW_DEFAULT);
+ dictAppendWord(dp, "f2swap", FtwoSwap, FW_DEFAULT);
+ dictAppendWord(dp, "f+!", FplusStore, FW_DEFAULT);
+ dictAppendWord(dp, "f+i", Faddi, FW_DEFAULT);
+ dictAppendWord(dp, "f-i", Fsubi, FW_DEFAULT);
+ dictAppendWord(dp, "f*i", Fmuli, FW_DEFAULT);
+ dictAppendWord(dp, "f/i", Fdivi, FW_DEFAULT);
+ dictAppendWord(dp, "int>float", itof, FW_DEFAULT);
+ dictAppendWord(dp, "float>int", Ftoi, FW_DEFAULT);
+ dictAppendWord(dp, "f.", FDot, FW_DEFAULT);
+ dictAppendWord(dp, "f.s", displayFStack, FW_DEFAULT);
+ dictAppendWord(dp, "fe.", EDot, FW_DEFAULT);
+ dictAppendWord(dp, "fover", Fover, FW_DEFAULT);
+ dictAppendWord(dp, "fnegate", Fnegate, FW_DEFAULT);
+ dictAppendWord(dp, "fpick", Fpick, FW_DEFAULT);
+ dictAppendWord(dp, "froll", Froll, FW_DEFAULT);
+ dictAppendWord(dp, "frot", Frot, FW_DEFAULT);
+ dictAppendWord(dp, "fswap", Fswap, FW_DEFAULT);
+ dictAppendWord(dp, "i-f", isubf, FW_DEFAULT);
+ dictAppendWord(dp, "i/f", idivf, FW_DEFAULT);
+
+ dictAppendWord(dp, "float>", FFrom, FW_DEFAULT);
+
+ dictAppendWord(dp, "f-roll", FminusRoll, FW_DEFAULT);
+ dictAppendWord(dp, "f-rot", Fminusrot, FW_DEFAULT);
+ dictAppendWord(dp, "(fliteral)", fliteralParen, FW_COMPILE);
+
+ ficlSetEnv(pSys, "floating", FICL_FALSE); /* not all required words are present */
+ ficlSetEnv(pSys, "floating-ext", FICL_FALSE);
+ ficlSetEnv(pSys, "floating-stack", FICL_DEFAULT_STACK);
+#endif
+ return;
+}
+
diff --git a/stand/ficl/i386/sysdep.c b/stand/ficl/i386/sysdep.c
new file mode 100644
index 0000000..ea594e6
--- /dev/null
+++ b/stand/ficl/i386/sysdep.c
@@ -0,0 +1,149 @@
+/*******************************************************************
+** s y s d e p . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Implementations of FICL external interface functions...
+**
+*******************************************************************/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include <stand.h>
+#ifdef __i386__
+#include <machine/cpufunc.h>
+#endif
+#endif
+#include "ficl.h"
+
+/*
+******************* FreeBSD P O R T B E G I N S H E R E ******************** Michael Smith
+*/
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ u_int64_t qx;
+
+ qx = (u_int64_t)x * (u_int64_t) y;
+
+ q.hi = (u_int32_t)( qx >> 32 );
+ q.lo = (u_int32_t)( qx & 0xFFFFFFFFL);
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ u_int64_t qx, qh;
+
+ qh = q.hi;
+ qx = (qh << 32) | q.lo;
+
+ result.quot = qx / y;
+ result.rem = qx % y;
+
+ return result;
+}
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ while(*msg != 0)
+ putchar((unsigned char)*(msg++));
+ if (fNewline)
+ putchar('\n');
+
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+void *ficlRealloc (void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+#ifndef TESTMAIN
+/*
+ * outb ( port# c -- )
+ * Store a byte to I/O port number port#
+ */
+void
+ficlOutb(FICL_VM *pVM)
+{
+ u_char c;
+ u_int32_t port;
+
+ port=stackPopUNS(pVM->pStack);
+ c=(u_char)stackPopINT(pVM->pStack);
+ outb(port,c);
+}
+
+/*
+ * inb ( port# -- c )
+ * Fetch a byte from I/O port number port#
+ */
+void
+ficlInb(FICL_VM *pVM)
+{
+ u_char c;
+ u_int32_t port;
+
+ port=stackPopUNS(pVM->pStack);
+ c=inb(port);
+ stackPushINT(pVM->pStack,c);
+}
+
+/*
+ * Glue function to add the appropriate forth words to access x86 special cpu
+ * functionality.
+ */
+static void ficlCompileCpufunc(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+ dictAppendWord(dp, "outb", ficlOutb, FW_DEFAULT);
+ dictAppendWord(dp, "inb", ficlInb, FW_DEFAULT);
+}
+
+FICL_COMPILE_SET(ficlCompileCpufunc);
+
+#endif
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
diff --git a/stand/ficl/i386/sysdep.h b/stand/ficl/i386/sysdep.h
new file mode 100644
index 0000000..94fda20
--- /dev/null
+++ b/stand/ficl/i386/sysdep.h
@@ -0,0 +1,432 @@
+/*******************************************************************
+ s y s d e p . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Ficl system dependent types and prototypes...
+**
+** Note: Ficl also depends on the use of "assert" when
+** FICL_ROBUST is enabled. This may require some consideration
+** in firmware systems since assert often
+** assumes stderr/stdout.
+** $Id: sysdep.h,v 1.11 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#if !defined (__SYSDEP_H__)
+#define __SYSDEP_H__
+
+#include <sys/types.h>
+
+#include <stddef.h> /* size_t, NULL */
+#include <setjmp.h>
+#include <assert.h>
+
+#if !defined IGNORE /* Macro to silence unused param warnings */
+#define IGNORE(x) (void)x
+#endif
+
+/*
+** TRUE and FALSE for C boolean operations, and
+** portable 32 bit types for CELLs
+**
+*/
+#if !defined TRUE
+#define TRUE 1
+#endif
+#if !defined FALSE
+#define FALSE 0
+#endif
+
+/*
+** System dependent data type declarations...
+*/
+#if !defined INT32
+#define INT32 long
+#endif
+
+#if !defined UNS32
+#define UNS32 unsigned long
+#endif
+
+#if !defined UNS16
+#define UNS16 unsigned short
+#endif
+
+#if !defined UNS8
+#define UNS8 unsigned char
+#endif
+
+#if !defined NULL
+#define NULL ((void *)0)
+#endif
+
+/*
+** FICL_UNS and FICL_INT must have the same size as a void* on
+** the target system. A CELL is a union of void*, FICL_UNS, and
+** FICL_INT.
+** (11/2000: same for FICL_FLOAT)
+*/
+#if !defined FICL_INT
+#define FICL_INT INT32
+#endif
+
+#if !defined FICL_UNS
+#define FICL_UNS UNS32
+#endif
+
+#if !defined FICL_FLOAT
+#define FICL_FLOAT float
+#endif
+
+/*
+** Ficl presently supports values of 32 and 64 for BITS_PER_CELL
+*/
+#if !defined BITS_PER_CELL
+#define BITS_PER_CELL 32
+#endif
+
+#if ((BITS_PER_CELL != 32) && (BITS_PER_CELL != 64))
+ Error!
+#endif
+
+typedef struct
+{
+ FICL_UNS hi;
+ FICL_UNS lo;
+} DPUNS;
+
+typedef struct
+{
+ FICL_UNS quot;
+ FICL_UNS rem;
+} UNSQR;
+
+typedef struct
+{
+ FICL_INT hi;
+ FICL_INT lo;
+} DPINT;
+
+typedef struct
+{
+ FICL_INT quot;
+ FICL_INT rem;
+} INTQR;
+
+
+/*
+** B U I L D C O N T R O L S
+*/
+
+#if !defined (FICL_MINIMAL)
+#define FICL_MINIMAL 0
+#endif
+#if (FICL_MINIMAL)
+#define FICL_WANT_SOFTWORDS 0
+#define FICL_WANT_FILE 0
+#define FICL_WANT_FLOAT 0
+#define FICL_WANT_USER 0
+#define FICL_WANT_LOCALS 0
+#define FICL_WANT_DEBUGGER 0
+#define FICL_WANT_OOP 0
+#define FICL_PLATFORM_EXTEND 0
+#define FICL_MULTITHREAD 0
+#define FICL_ROBUST 0
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_PLATFORM_EXTEND
+** Includes words defined in ficlCompilePlatform
+*/
+#if !defined (FICL_PLATFORM_EXTEND)
+#define FICL_PLATFORM_EXTEND 1
+#endif
+
+
+/*
+** FICL_WANT_FILE
+** Includes the FILE and FILE-EXT wordset and associated code. Turn this off if you do not
+** have a filesystem!
+** Contributed by Larry Hastings
+*/
+#if !defined (FICL_WANT_FILE)
+#define FICL_WANT_FILE 0
+#endif
+
+/*
+** FICL_WANT_FLOAT
+** Includes a floating point stack for the VM, and words to do float operations.
+** Contributed by Guy Carver
+*/
+#if !defined (FICL_WANT_FLOAT)
+#define FICL_WANT_FLOAT 0
+#endif
+
+/*
+** FICL_WANT_DEBUGGER
+** Inludes a simple source level debugger
+*/
+#if !defined (FICL_WANT_DEBUGGER)
+#define FICL_WANT_DEBUGGER 1
+#endif
+
+/*
+** FICL_EXTENDED_PREFIX enables a bunch of extra prefixes in prefix.c and prefix.fr (if
+** included as part of softcore.c)
+*/
+#if !defined FICL_EXTENDED_PREFIX
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** User variables: per-instance variables bound to the VM.
+** Kinda like thread-local storage. Could be implemented in a
+** VM private dictionary, but I've chosen the lower overhead
+** approach of an array of CELLs instead.
+*/
+#if !defined FICL_WANT_USER
+#define FICL_WANT_USER 1
+#endif
+
+#if !defined FICL_USER_CELLS
+#define FICL_USER_CELLS 16
+#endif
+
+/*
+** FICL_WANT_LOCALS controls the creation of the LOCALS wordset and
+** a private dictionary for local variable compilation.
+*/
+#if !defined FICL_WANT_LOCALS
+#define FICL_WANT_LOCALS 1
+#endif
+
+/* Max number of local variables per definition */
+#if !defined FICL_MAX_LOCALS
+#define FICL_MAX_LOCALS 16
+#endif
+
+/*
+** FICL_WANT_OOP
+** Inludes object oriented programming support (in softwords)
+** OOP support requires locals and user variables!
+*/
+#if !(FICL_WANT_LOCALS) || !(FICL_WANT_USER)
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 0
+#endif
+#endif
+
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 1
+#endif
+
+/*
+** FICL_WANT_SOFTWORDS
+** Controls inclusion of all softwords in softcore.c
+*/
+#if !defined (FICL_WANT_SOFTWORDS)
+#define FICL_WANT_SOFTWORDS 1
+#endif
+
+/*
+** FICL_MULTITHREAD enables dictionary mutual exclusion
+** wia the ficlLockDictionary system dependent function.
+** Note: this implementation is experimental and poorly
+** tested. Further, it's unnecessary unless you really
+** intend to have multiple SESSIONS (poor choice of name
+** on my part) - that is, threads that modify the dictionary
+** at the same time.
+*/
+#if !defined FICL_MULTITHREAD
+#define FICL_MULTITHREAD 0
+#endif
+
+/*
+** PORTABLE_LONGMULDIV causes ficlLongMul and ficlLongDiv to be
+** defined in C in sysdep.c. Use this if you cannot easily
+** generate an inline asm definition
+*/
+#if !defined (PORTABLE_LONGMULDIV)
+#define PORTABLE_LONGMULDIV 0
+#endif
+
+/*
+** INLINE_INNER_LOOP causes the inner interpreter to be inline code
+** instead of a function call. This is mainly because MS VC++ 5
+** chokes with an internal compiler error on the function version.
+** in release mode. Sheesh.
+*/
+#if !defined INLINE_INNER_LOOP
+#if defined _DEBUG
+#define INLINE_INNER_LOOP 0
+#else
+#define INLINE_INNER_LOOP 1
+#endif
+#endif
+
+/*
+** FICL_ROBUST enables bounds checking of stacks and the dictionary.
+** This will detect stack over and underflows and dictionary overflows.
+** Any exceptional condition will result in an assertion failure.
+** (As generated by the ANSI assert macro)
+** FICL_ROBUST == 1 --> stack checking in the outer interpreter
+** FICL_ROBUST == 2 also enables checking in many primitives
+*/
+
+#if !defined FICL_ROBUST
+#define FICL_ROBUST 2
+#endif
+
+/*
+** FICL_DEFAULT_STACK Specifies the default size (in CELLs) of
+** a new virtual machine's stacks, unless overridden at
+** create time.
+*/
+#if !defined FICL_DEFAULT_STACK
+#define FICL_DEFAULT_STACK 128
+#endif
+
+/*
+** FICL_DEFAULT_DICT specifies the number of CELLs to allocate
+** for the system dictionary by default. The value
+** can be overridden at startup time as well.
+** FICL_DEFAULT_ENV specifies the number of cells to allot
+** for the environment-query dictionary.
+*/
+#if !defined FICL_DEFAULT_DICT
+#define FICL_DEFAULT_DICT 12288
+#endif
+
+#if !defined FICL_DEFAULT_ENV
+#define FICL_DEFAULT_ENV 260
+#endif
+
+/*
+** FICL_DEFAULT_VOCS specifies the maximum number of wordlists in
+** the dictionary search order. See Forth DPANS sec 16.3.3
+** (file://dpans16.htm#16.3.3)
+*/
+#if !defined FICL_DEFAULT_VOCS
+#define FICL_DEFAULT_VOCS 16
+#endif
+
+/*
+** FICL_MAX_PARSE_STEPS controls the size of an array in the FICL_SYSTEM structure
+** that stores pointers to parser extension functions. I would never expect to have
+** more than 8 of these, so that's the default limit. Too many of these functions
+** will probably exact a nasty performance penalty.
+*/
+#if !defined FICL_MAX_PARSE_STEPS
+#define FICL_MAX_PARSE_STEPS 8
+#endif
+
+/*
+** FICL_ALIGN is the power of two to which the dictionary
+** pointer address must be aligned. This value is usually
+** either 1 or 2, depending on the memory architecture
+** of the target system; 2 is safe on any 16 or 32 bit
+** machine. 3 would be appropriate for a 64 bit machine.
+*/
+#if !defined FICL_ALIGN
+#define FICL_ALIGN 2
+#define FICL_ALIGN_ADD ((1 << FICL_ALIGN) - 1)
+#endif
+
+/*
+** System dependent routines --
+** edit the implementations in sysdep.c to be compatible
+** with your runtime environment...
+** ficlTextOut sends a NULL terminated string to the
+** default output device - used for system error messages
+** ficlMalloc and ficlFree have the same semantics as malloc and free
+** in standard C
+** ficlLongMul multiplies two UNS32s and returns a 64 bit unsigned
+** product
+** ficlLongDiv divides an UNS64 by an UNS32 and returns UNS32 quotient
+** and remainder
+*/
+struct vm;
+void ficlTextOut(struct vm *pVM, char *msg, int fNewline);
+void *ficlMalloc (size_t size);
+void ficlFree (void *p);
+void *ficlRealloc(void *p, size_t size);
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** must be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** before timeout (optional - could also block forever)
+**
+** NOTE: this function must be implemented with lock counting
+** semantics: nested calls must behave properly.
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock);
+#else
+#define ficlLockDictionary(x) /* ignore */
+#endif
+
+/*
+** 64 bit integer math support routines: multiply two UNS32s
+** to get a 64 bit product, & divide the product by an UNS32
+** to get an UNS32 quotient and remainder. Much easier in asm
+** on a 32 bit CPU than in C, which usually doesn't support
+** the double length result (but it should).
+*/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y);
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y);
+
+
+/*
+** FICL_HAVE_FTRUNCATE indicates whether the current OS supports
+** the ftruncate() function (available on most UNIXes). This
+** function is necessary to provide the complete File-Access wordset.
+*/
+#if !defined (FICL_HAVE_FTRUNCATE)
+#define FICL_HAVE_FTRUNCATE 0
+#endif
+
+
+#endif /*__SYSDEP_H__*/
diff --git a/stand/ficl/loader.c b/stand/ficl/loader.c
new file mode 100644
index 0000000..2246190
--- /dev/null
+++ b/stand/ficl/loader.c
@@ -0,0 +1,841 @@
+/*-
+ * Copyright (c) 2000 Daniel Capo Sobral
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*******************************************************************
+** l o a d e r . c
+** Additional FICL words designed for FreeBSD's loader
+**
+*******************************************************************/
+
+#ifdef TESTMAIN
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#else
+#include <stand.h>
+#endif
+#include "bootstrap.h"
+#include <string.h>
+#include <uuid.h>
+#include "ficl.h"
+
+/* FreeBSD's loader interaction words and extras
+ *
+ * setenv ( value n name n' -- )
+ * setenv? ( value n name n' flag -- )
+ * getenv ( addr n -- addr' n' | -1 )
+ * unsetenv ( addr n -- )
+ * copyin ( addr addr' len -- )
+ * copyout ( addr addr' len -- )
+ * findfile ( name len type len' -- addr )
+ * pnpdevices ( -- addr )
+ * pnphandlers ( -- addr )
+ * ccall ( [[...[p10] p9] ... p1] n addr -- result )
+ * uuid-from-string ( addr n -- addr' )
+ * uuid-to-string ( addr' -- addr n )
+ * .# ( value -- )
+ */
+
+void
+ficlSetenv(FICL_VM *pVM)
+{
+#ifndef TESTMAIN
+ char *name, *value;
+#endif
+ char *namep, *valuep;
+ int names, values;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 0);
+#endif
+ names = stackPopINT(pVM->pStack);
+ namep = (char*) stackPopPtr(pVM->pStack);
+ values = stackPopINT(pVM->pStack);
+ valuep = (char*) stackPopPtr(pVM->pStack);
+
+#ifndef TESTMAIN
+ name = (char*) ficlMalloc(names+1);
+ if (!name)
+ vmThrowErr(pVM, "Error: out of memory");
+ strncpy(name, namep, names);
+ name[names] = '\0';
+ value = (char*) ficlMalloc(values+1);
+ if (!value)
+ vmThrowErr(pVM, "Error: out of memory");
+ strncpy(value, valuep, values);
+ value[values] = '\0';
+
+ setenv(name, value, 1);
+ ficlFree(name);
+ ficlFree(value);
+#endif
+
+ return;
+}
+
+void
+ficlSetenvq(FICL_VM *pVM)
+{
+#ifndef TESTMAIN
+ char *name, *value;
+#endif
+ char *namep, *valuep;
+ int names, values, overwrite;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 5, 0);
+#endif
+ overwrite = stackPopINT(pVM->pStack);
+ names = stackPopINT(pVM->pStack);
+ namep = (char*) stackPopPtr(pVM->pStack);
+ values = stackPopINT(pVM->pStack);
+ valuep = (char*) stackPopPtr(pVM->pStack);
+
+#ifndef TESTMAIN
+ name = (char*) ficlMalloc(names+1);
+ if (!name)
+ vmThrowErr(pVM, "Error: out of memory");
+ strncpy(name, namep, names);
+ name[names] = '\0';
+ value = (char*) ficlMalloc(values+1);
+ if (!value)
+ vmThrowErr(pVM, "Error: out of memory");
+ strncpy(value, valuep, values);
+ value[values] = '\0';
+
+ setenv(name, value, overwrite);
+ ficlFree(name);
+ ficlFree(value);
+#endif
+
+ return;
+}
+
+void
+ficlGetenv(FICL_VM *pVM)
+{
+#ifndef TESTMAIN
+ char *name, *value;
+#endif
+ char *namep;
+ int names;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+ names = stackPopINT(pVM->pStack);
+ namep = (char*) stackPopPtr(pVM->pStack);
+
+#ifndef TESTMAIN
+ name = (char*) ficlMalloc(names+1);
+ if (!name)
+ vmThrowErr(pVM, "Error: out of memory");
+ strncpy(name, namep, names);
+ name[names] = '\0';
+
+ value = getenv(name);
+ ficlFree(name);
+
+ if(value != NULL) {
+ stackPushPtr(pVM->pStack, value);
+ stackPushINT(pVM->pStack, strlen(value));
+ } else
+#endif
+ stackPushINT(pVM->pStack, -1);
+
+ return;
+}
+
+void
+ficlUnsetenv(FICL_VM *pVM)
+{
+#ifndef TESTMAIN
+ char *name;
+#endif
+ char *namep;
+ int names;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ names = stackPopINT(pVM->pStack);
+ namep = (char*) stackPopPtr(pVM->pStack);
+
+#ifndef TESTMAIN
+ name = (char*) ficlMalloc(names+1);
+ if (!name)
+ vmThrowErr(pVM, "Error: out of memory");
+ strncpy(name, namep, names);
+ name[names] = '\0';
+
+ unsetenv(name);
+ ficlFree(name);
+#endif
+
+ return;
+}
+
+void
+ficlCopyin(FICL_VM *pVM)
+{
+ void* src;
+ vm_offset_t dest;
+ size_t len;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 0);
+#endif
+
+ len = stackPopINT(pVM->pStack);
+ dest = stackPopINT(pVM->pStack);
+ src = stackPopPtr(pVM->pStack);
+
+#ifndef TESTMAIN
+ archsw.arch_copyin(src, dest, len);
+#endif
+
+ return;
+}
+
+void
+ficlCopyout(FICL_VM *pVM)
+{
+ void* dest;
+ vm_offset_t src;
+ size_t len;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 0);
+#endif
+
+ len = stackPopINT(pVM->pStack);
+ dest = stackPopPtr(pVM->pStack);
+ src = stackPopINT(pVM->pStack);
+
+#ifndef TESTMAIN
+ archsw.arch_copyout(src, dest, len);
+#endif
+
+ return;
+}
+
+void
+ficlFindfile(FICL_VM *pVM)
+{
+#ifndef TESTMAIN
+ char *name, *type;
+#endif
+ char *namep, *typep;
+ struct preloaded_file* fp;
+ int names, types;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 1);
+#endif
+
+ types = stackPopINT(pVM->pStack);
+ typep = (char*) stackPopPtr(pVM->pStack);
+ names = stackPopINT(pVM->pStack);
+ namep = (char*) stackPopPtr(pVM->pStack);
+#ifndef TESTMAIN
+ name = (char*) ficlMalloc(names+1);
+ if (!name)
+ vmThrowErr(pVM, "Error: out of memory");
+ strncpy(name, namep, names);
+ name[names] = '\0';
+ type = (char*) ficlMalloc(types+1);
+ if (!type)
+ vmThrowErr(pVM, "Error: out of memory");
+ strncpy(type, typep, types);
+ type[types] = '\0';
+
+ fp = file_findfile(name, type);
+#else
+ fp = NULL;
+#endif
+ stackPushPtr(pVM->pStack, fp);
+
+ return;
+}
+
+void
+ficlCcall(FICL_VM *pVM)
+{
+ int (*func)(int, ...);
+ int result, p[10];
+ int nparam, i;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+
+ func = stackPopPtr(pVM->pStack);
+ nparam = stackPopINT(pVM->pStack);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, nparam, 1);
+#endif
+
+ for (i = 0; i < nparam; i++)
+ p[i] = stackPopINT(pVM->pStack);
+
+ result = func(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8],
+ p[9]);
+
+ stackPushINT(pVM->pStack, result);
+
+ return;
+}
+
+void
+ficlUuidFromString(FICL_VM *pVM)
+{
+#ifndef TESTMAIN
+ char *uuid;
+ uint32_t status;
+#endif
+ char *uuidp;
+ int uuids;
+ uuid_t *u;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+
+ uuids = stackPopINT(pVM->pStack);
+ uuidp = (char *) stackPopPtr(pVM->pStack);
+
+#ifndef TESTMAIN
+ uuid = (char *)ficlMalloc(uuids + 1);
+ if (!uuid)
+ vmThrowErr(pVM, "Error: out of memory");
+ strncpy(uuid, uuidp, uuids);
+ uuid[uuids] = '\0';
+
+ u = (uuid_t *)ficlMalloc(sizeof (*u));
+
+ uuid_from_string(uuid, u, &status);
+ ficlFree(uuid);
+ if (status != uuid_s_ok) {
+ ficlFree(u);
+ u = NULL;
+ }
+#else
+ u = NULL;
+#endif
+ stackPushPtr(pVM->pStack, u);
+
+
+ return;
+}
+
+void
+ficlUuidToString(FICL_VM *pVM)
+{
+#ifndef TESTMAIN
+ char *uuid;
+ uint32_t status;
+#endif
+ uuid_t *u;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ u = (uuid_t *)stackPopPtr(pVM->pStack);
+
+#ifndef TESTMAIN
+ uuid_to_string(u, &uuid, &status);
+ if (status != uuid_s_ok) {
+ stackPushPtr(pVM->pStack, uuid);
+ stackPushINT(pVM->pStack, strlen(uuid));
+ } else
+#endif
+ stackPushINT(pVM->pStack, -1);
+
+ return;
+}
+
+/**************************************************************************
+ f i c l E x e c F D
+** reads in text from file fd and passes it to ficlExec()
+ * returns VM_OUTOFTEXT on success or the ficlExec() error code on
+ * failure.
+ */
+#define nLINEBUF 256
+int ficlExecFD(FICL_VM *pVM, int fd)
+{
+ char cp[nLINEBUF];
+ int nLine = 0, rval = VM_OUTOFTEXT;
+ char ch;
+ CELL id;
+
+ id = pVM->sourceID;
+ pVM->sourceID.i = fd;
+
+ /* feed each line to ficlExec */
+ while (1) {
+ int status, i;
+
+ i = 0;
+ while ((status = read(fd, &ch, 1)) > 0 && ch != '\n')
+ cp[i++] = ch;
+ nLine++;
+ if (!i) {
+ if (status < 1)
+ break;
+ continue;
+ }
+ rval = ficlExecC(pVM, cp, i);
+ if(rval != VM_QUIT && rval != VM_USEREXIT && rval != VM_OUTOFTEXT)
+ {
+ pVM->sourceID = id;
+ return rval;
+ }
+ }
+ /*
+ ** Pass an empty line with SOURCE-ID == -1 to flush
+ ** any pending REFILLs (as required by FILE wordset)
+ */
+ pVM->sourceID.i = -1;
+ ficlExec(pVM, "");
+
+ pVM->sourceID = id;
+ return rval;
+}
+
+static void displayCellNoPad(FICL_VM *pVM)
+{
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ c = stackPop(pVM->pStack);
+ ltoa((c).i, pVM->pad, pVM->base);
+ vmTextOut(pVM, pVM->pad, 0);
+ return;
+}
+
+/* isdir? - Return whether an fd corresponds to a directory.
+ *
+ * isdir? ( fd -- bool )
+ */
+static void isdirQuestion(FICL_VM *pVM)
+{
+ struct stat sb;
+ FICL_INT flag;
+ int fd;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+
+ fd = stackPopINT(pVM->pStack);
+ flag = FICL_FALSE;
+ do {
+ if (fd < 0)
+ break;
+ if (fstat(fd, &sb) < 0)
+ break;
+ if (!S_ISDIR(sb.st_mode))
+ break;
+ flag = FICL_TRUE;
+ } while (0);
+ stackPushINT(pVM->pStack, flag);
+}
+
+/* fopen - open a file and return new fd on stack.
+ *
+ * fopen ( ptr count mode -- fd )
+ */
+static void pfopen(FICL_VM *pVM)
+{
+ int mode, fd, count;
+ char *ptr, *name;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 1);
+#endif
+
+ mode = stackPopINT(pVM->pStack); /* get mode */
+ count = stackPopINT(pVM->pStack); /* get count */
+ ptr = stackPopPtr(pVM->pStack); /* get ptr */
+
+ if ((count < 0) || (ptr == NULL)) {
+ stackPushINT(pVM->pStack, -1);
+ return;
+ }
+
+ /* ensure that the string is null terminated */
+ name = (char *)malloc(count+1);
+ bcopy(ptr,name,count);
+ name[count] = 0;
+
+ /* open the file */
+ fd = open(name, mode);
+ free(name);
+ stackPushINT(pVM->pStack, fd);
+ return;
+}
+
+/* fclose - close a file who's fd is on stack.
+ *
+ * fclose ( fd -- )
+ */
+static void pfclose(FICL_VM *pVM)
+{
+ int fd;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ fd = stackPopINT(pVM->pStack); /* get fd */
+ if (fd != -1)
+ close(fd);
+ return;
+}
+
+/* fread - read file contents
+ *
+ * fread ( fd buf nbytes -- nread )
+ */
+static void pfread(FICL_VM *pVM)
+{
+ int fd, len;
+ char *buf;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 1);
+#endif
+ len = stackPopINT(pVM->pStack); /* get number of bytes to read */
+ buf = stackPopPtr(pVM->pStack); /* get buffer */
+ fd = stackPopINT(pVM->pStack); /* get fd */
+ if (len > 0 && buf && fd != -1)
+ stackPushINT(pVM->pStack, read(fd, buf, len));
+ else
+ stackPushINT(pVM->pStack, -1);
+ return;
+}
+
+/* freaddir - read directory contents
+ *
+ * freaddir ( fd -- ptr len TRUE | FALSE )
+ */
+static void pfreaddir(FICL_VM *pVM)
+{
+#ifdef TESTMAIN
+ static struct dirent dirent;
+ struct stat sb;
+ char *buf;
+ off_t off, ptr;
+ u_int blksz;
+ int bufsz;
+#endif
+ struct dirent *d;
+ int fd;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 3);
+#endif
+
+ fd = stackPopINT(pVM->pStack);
+#if TESTMAIN
+ /*
+ * The readdirfd() function is specific to the loader environment.
+ * We do the best we can to make freaddir work, but it's not at
+ * all guaranteed.
+ */
+ d = NULL;
+ buf = NULL;
+ do {
+ if (fd == -1)
+ break;
+ if (fstat(fd, &sb) == -1)
+ break;
+ blksz = (sb.st_blksize) ? sb.st_blksize : getpagesize();
+ if ((blksz & (blksz - 1)) != 0)
+ break;
+ buf = malloc(blksz);
+ if (buf == NULL)
+ break;
+ off = lseek(fd, 0LL, SEEK_CUR);
+ if (off == -1)
+ break;
+ ptr = off;
+ if (lseek(fd, 0, SEEK_SET) == -1)
+ break;
+ bufsz = getdents(fd, buf, blksz);
+ while (bufsz > 0 && bufsz <= ptr) {
+ ptr -= bufsz;
+ bufsz = getdents(fd, buf, blksz);
+ }
+ if (bufsz <= 0)
+ break;
+ d = (void *)(buf + ptr);
+ dirent = *d;
+ off += d->d_reclen;
+ d = (lseek(fd, off, SEEK_SET) != off) ? NULL : &dirent;
+ } while (0);
+ if (buf != NULL)
+ free(buf);
+#else
+ d = readdirfd(fd);
+#endif
+ if (d != NULL) {
+ stackPushPtr(pVM->pStack, d->d_name);
+ stackPushINT(pVM->pStack, strlen(d->d_name));
+ stackPushINT(pVM->pStack, FICL_TRUE);
+ } else {
+ stackPushINT(pVM->pStack, FICL_FALSE);
+ }
+}
+
+/* fload - interpret file contents
+ *
+ * fload ( fd -- )
+ */
+static void pfload(FICL_VM *pVM)
+{
+ int fd;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ fd = stackPopINT(pVM->pStack); /* get fd */
+ if (fd != -1)
+ ficlExecFD(pVM, fd);
+ return;
+}
+
+/* fwrite - write file contents
+ *
+ * fwrite ( fd buf nbytes -- nwritten )
+ */
+static void pfwrite(FICL_VM *pVM)
+{
+ int fd, len;
+ char *buf;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 1);
+#endif
+ len = stackPopINT(pVM->pStack); /* get number of bytes to read */
+ buf = stackPopPtr(pVM->pStack); /* get buffer */
+ fd = stackPopINT(pVM->pStack); /* get fd */
+ if (len > 0 && buf && fd != -1)
+ stackPushINT(pVM->pStack, write(fd, buf, len));
+ else
+ stackPushINT(pVM->pStack, -1);
+ return;
+}
+
+/* fseek - seek to a new position in a file
+ *
+ * fseek ( fd ofs whence -- pos )
+ */
+static void pfseek(FICL_VM *pVM)
+{
+ int fd, pos, whence;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 1);
+#endif
+ whence = stackPopINT(pVM->pStack);
+ pos = stackPopINT(pVM->pStack);
+ fd = stackPopINT(pVM->pStack);
+ stackPushINT(pVM->pStack, lseek(fd, pos, whence));
+ return;
+}
+
+/* key - get a character from stdin
+ *
+ * key ( -- char )
+ */
+static void key(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+ stackPushINT(pVM->pStack, getchar());
+ return;
+}
+
+/* key? - check for a character from stdin (FACILITY)
+ *
+ * key? ( -- flag )
+ */
+static void keyQuestion(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+#ifdef TESTMAIN
+ /* XXX Since we don't fiddle with termios, let it always succeed... */
+ stackPushINT(pVM->pStack, FICL_TRUE);
+#else
+ /* But here do the right thing. */
+ stackPushINT(pVM->pStack, ischar()? FICL_TRUE : FICL_FALSE);
+#endif
+ return;
+}
+
+/* seconds - gives number of seconds since beginning of time
+ *
+ * beginning of time is defined as:
+ *
+ * BTX - number of seconds since midnight
+ * FreeBSD - number of seconds since Jan 1 1970
+ *
+ * seconds ( -- u )
+ */
+static void pseconds(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,0,1);
+#endif
+ stackPushUNS(pVM->pStack, (FICL_UNS) time(NULL));
+ return;
+}
+
+/* ms - wait at least that many milliseconds (FACILITY)
+ *
+ * ms ( u -- )
+ *
+ */
+static void ms(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,0);
+#endif
+#ifdef TESTMAIN
+ usleep(stackPopUNS(pVM->pStack)*1000);
+#else
+ delay(stackPopUNS(pVM->pStack)*1000);
+#endif
+ return;
+}
+
+/* fkey - get a character from a file
+ *
+ * fkey ( file -- char )
+ */
+static void fkey(FICL_VM *pVM)
+{
+ int i, fd;
+ char ch;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ fd = stackPopINT(pVM->pStack);
+ i = read(fd, &ch, 1);
+ stackPushINT(pVM->pStack, i > 0 ? ch : -1);
+ return;
+}
+
+
+/*
+** Retrieves free space remaining on the dictionary
+*/
+
+static void freeHeap(FICL_VM *pVM)
+{
+ stackPushINT(pVM->pStack, dictCellsAvail(ficlGetDict(pVM->pSys)));
+}
+
+
+/******************* Increase dictionary size on-demand ******************/
+
+static void ficlDictThreshold(FICL_VM *pVM)
+{
+ stackPushPtr(pVM->pStack, &dictThreshold);
+}
+
+static void ficlDictIncrease(FICL_VM *pVM)
+{
+ stackPushPtr(pVM->pStack, &dictIncrease);
+}
+
+/**************************************************************************
+ f i c l C o m p i l e P l a t f o r m
+** Build FreeBSD platform extensions into the system dictionary
+**************************************************************************/
+void ficlCompilePlatform(FICL_SYSTEM *pSys)
+{
+ ficlCompileFcn **fnpp;
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+ dictAppendWord(dp, ".#", displayCellNoPad, FW_DEFAULT);
+ dictAppendWord(dp, "isdir?", isdirQuestion, FW_DEFAULT);
+ dictAppendWord(dp, "fopen", pfopen, FW_DEFAULT);
+ dictAppendWord(dp, "fclose", pfclose, FW_DEFAULT);
+ dictAppendWord(dp, "fread", pfread, FW_DEFAULT);
+ dictAppendWord(dp, "freaddir", pfreaddir, FW_DEFAULT);
+ dictAppendWord(dp, "fload", pfload, FW_DEFAULT);
+ dictAppendWord(dp, "fkey", fkey, FW_DEFAULT);
+ dictAppendWord(dp, "fseek", pfseek, FW_DEFAULT);
+ dictAppendWord(dp, "fwrite", pfwrite, FW_DEFAULT);
+ dictAppendWord(dp, "key", key, FW_DEFAULT);
+ dictAppendWord(dp, "key?", keyQuestion, FW_DEFAULT);
+ dictAppendWord(dp, "ms", ms, FW_DEFAULT);
+ dictAppendWord(dp, "seconds", pseconds, FW_DEFAULT);
+ dictAppendWord(dp, "heap?", freeHeap, FW_DEFAULT);
+ dictAppendWord(dp, "dictthreshold", ficlDictThreshold, FW_DEFAULT);
+ dictAppendWord(dp, "dictincrease", ficlDictIncrease, FW_DEFAULT);
+
+ dictAppendWord(dp, "setenv", ficlSetenv, FW_DEFAULT);
+ dictAppendWord(dp, "setenv?", ficlSetenvq, FW_DEFAULT);
+ dictAppendWord(dp, "getenv", ficlGetenv, FW_DEFAULT);
+ dictAppendWord(dp, "unsetenv", ficlUnsetenv, FW_DEFAULT);
+ dictAppendWord(dp, "copyin", ficlCopyin, FW_DEFAULT);
+ dictAppendWord(dp, "copyout", ficlCopyout, FW_DEFAULT);
+ dictAppendWord(dp, "findfile", ficlFindfile, FW_DEFAULT);
+ dictAppendWord(dp, "ccall", ficlCcall, FW_DEFAULT);
+ dictAppendWord(dp, "uuid-from-string", ficlUuidFromString, FW_DEFAULT);
+ dictAppendWord(dp, "uuid-to-string", ficlUuidToString, FW_DEFAULT);
+
+ SET_FOREACH(fnpp, Xficl_compile_set)
+ (*fnpp)(pSys);
+
+#if defined(PC98)
+ ficlSetEnv(pSys, "arch-pc98", FICL_TRUE);
+#elif defined(__i386__)
+ ficlSetEnv(pSys, "arch-i386", FICL_TRUE);
+ ficlSetEnv(pSys, "arch-powerpc", FICL_FALSE);
+#elif defined(__powerpc__)
+ ficlSetEnv(pSys, "arch-i386", FICL_FALSE);
+ ficlSetEnv(pSys, "arch-powerpc", FICL_TRUE);
+#endif
+
+ return;
+}
diff --git a/stand/ficl/math64.c b/stand/ficl/math64.c
new file mode 100644
index 0000000..6e50458
--- /dev/null
+++ b/stand/ficl/math64.c
@@ -0,0 +1,561 @@
+/*******************************************************************
+** m a t h 6 4 . c
+** Forth Inspired Command Language - 64 bit math support routines
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 25 January 1998
+** Rev 2.03: Support for 128 bit DP math. This file really ouught to
+** be renamed!
+** $Id: math64.c,v 1.9 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#include "ficl.h"
+#include "math64.h"
+
+
+/**************************************************************************
+ m 6 4 A b s
+** Returns the absolute value of an DPINT
+**************************************************************************/
+DPINT m64Abs(DPINT x)
+{
+ if (m64IsNegative(x))
+ x = m64Negate(x);
+
+ return x;
+}
+
+
+/**************************************************************************
+ m 6 4 F l o o r e d D i v I
+**
+** FROM THE FORTH ANS...
+** Floored division is integer division in which the remainder carries
+** the sign of the divisor or is zero, and the quotient is rounded to
+** its arithmetic floor. Symmetric division is integer division in which
+** the remainder carries the sign of the dividend or is zero and the
+** quotient is the mathematical quotient rounded towards zero or
+** truncated. Examples of each are shown in tables 3.3 and 3.4.
+**
+** Table 3.3 - Floored Division Example
+** Dividend Divisor Remainder Quotient
+** -------- ------- --------- --------
+** 10 7 3 1
+** -10 7 4 -2
+** 10 -7 -4 -2
+** -10 -7 -3 1
+**
+**
+** Table 3.4 - Symmetric Division Example
+** Dividend Divisor Remainder Quotient
+** -------- ------- --------- --------
+** 10 7 3 1
+** -10 7 -3 -1
+** 10 -7 3 -1
+** -10 -7 -3 1
+**************************************************************************/
+INTQR m64FlooredDivI(DPINT num, FICL_INT den)
+{
+ INTQR qr;
+ UNSQR uqr;
+ int signRem = 1;
+ int signQuot = 1;
+
+ if (m64IsNegative(num))
+ {
+ num = m64Negate(num);
+ signQuot = -signQuot;
+ }
+
+ if (den < 0)
+ {
+ den = -den;
+ signRem = -signRem;
+ signQuot = -signQuot;
+ }
+
+ uqr = ficlLongDiv(m64CastIU(num), (FICL_UNS)den);
+ qr = m64CastQRUI(uqr);
+ if (signQuot < 0)
+ {
+ qr.quot = -qr.quot;
+ if (qr.rem != 0)
+ {
+ qr.quot--;
+ qr.rem = den - qr.rem;
+ }
+ }
+
+ if (signRem < 0)
+ qr.rem = -qr.rem;
+
+ return qr;
+}
+
+
+/**************************************************************************
+ m 6 4 I s N e g a t i v e
+** Returns TRUE if the specified DPINT has its sign bit set.
+**************************************************************************/
+int m64IsNegative(DPINT x)
+{
+ return (x.hi < 0);
+}
+
+
+/**************************************************************************
+ m 6 4 M a c
+** Mixed precision multiply and accumulate primitive for number building.
+** Multiplies DPUNS u by FICL_UNS mul and adds FICL_UNS add. Mul is typically
+** the numeric base, and add represents a digit to be appended to the
+** growing number.
+** Returns the result of the operation
+**************************************************************************/
+DPUNS m64Mac(DPUNS u, FICL_UNS mul, FICL_UNS add)
+{
+ DPUNS resultLo = ficlLongMul(u.lo, mul);
+ DPUNS resultHi = ficlLongMul(u.hi, mul);
+ resultLo.hi += resultHi.lo;
+ resultHi.lo = resultLo.lo + add;
+
+ if (resultHi.lo < resultLo.lo)
+ resultLo.hi++;
+
+ resultLo.lo = resultHi.lo;
+
+ return resultLo;
+}
+
+
+/**************************************************************************
+ m 6 4 M u l I
+** Multiplies a pair of FICL_INTs and returns an DPINT result.
+**************************************************************************/
+DPINT m64MulI(FICL_INT x, FICL_INT y)
+{
+ DPUNS prod;
+ int sign = 1;
+
+ if (x < 0)
+ {
+ sign = -sign;
+ x = -x;
+ }
+
+ if (y < 0)
+ {
+ sign = -sign;
+ y = -y;
+ }
+
+ prod = ficlLongMul(x, y);
+ if (sign > 0)
+ return m64CastUI(prod);
+ else
+ return m64Negate(m64CastUI(prod));
+}
+
+
+/**************************************************************************
+ m 6 4 N e g a t e
+** Negates an DPINT by complementing and incrementing.
+**************************************************************************/
+DPINT m64Negate(DPINT x)
+{
+ x.hi = ~x.hi;
+ x.lo = ~x.lo;
+ x.lo ++;
+ if (x.lo == 0)
+ x.hi++;
+
+ return x;
+}
+
+
+/**************************************************************************
+ m 6 4 P u s h
+** Push an DPINT onto the specified stack in the order required
+** by ANS Forth (most significant cell on top)
+** These should probably be macros...
+**************************************************************************/
+void i64Push(FICL_STACK *pStack, DPINT i64)
+{
+ stackPushINT(pStack, i64.lo);
+ stackPushINT(pStack, i64.hi);
+ return;
+}
+
+void u64Push(FICL_STACK *pStack, DPUNS u64)
+{
+ stackPushINT(pStack, u64.lo);
+ stackPushINT(pStack, u64.hi);
+ return;
+}
+
+
+/**************************************************************************
+ m 6 4 P o p
+** Pops an DPINT off the stack in the order required by ANS Forth
+** (most significant cell on top)
+** These should probably be macros...
+**************************************************************************/
+DPINT i64Pop(FICL_STACK *pStack)
+{
+ DPINT ret;
+ ret.hi = stackPopINT(pStack);
+ ret.lo = stackPopINT(pStack);
+ return ret;
+}
+
+DPUNS u64Pop(FICL_STACK *pStack)
+{
+ DPUNS ret;
+ ret.hi = stackPopINT(pStack);
+ ret.lo = stackPopINT(pStack);
+ return ret;
+}
+
+
+/**************************************************************************
+ m 6 4 S y m m e t r i c D i v
+** Divide an DPINT by a FICL_INT and return a FICL_INT quotient and a
+** FICL_INT remainder. The absolute values of quotient and remainder are not
+** affected by the signs of the numerator and denominator (the operation
+** is symmetric on the number line)
+**************************************************************************/
+INTQR m64SymmetricDivI(DPINT num, FICL_INT den)
+{
+ INTQR qr;
+ UNSQR uqr;
+ int signRem = 1;
+ int signQuot = 1;
+
+ if (m64IsNegative(num))
+ {
+ num = m64Negate(num);
+ signRem = -signRem;
+ signQuot = -signQuot;
+ }
+
+ if (den < 0)
+ {
+ den = -den;
+ signQuot = -signQuot;
+ }
+
+ uqr = ficlLongDiv(m64CastIU(num), (FICL_UNS)den);
+ qr = m64CastQRUI(uqr);
+ if (signRem < 0)
+ qr.rem = -qr.rem;
+
+ if (signQuot < 0)
+ qr.quot = -qr.quot;
+
+ return qr;
+}
+
+
+/**************************************************************************
+ m 6 4 U M o d
+** Divides a DPUNS by base (an UNS16) and returns an UNS16 remainder.
+** Writes the quotient back to the original DPUNS as a side effect.
+** This operation is typically used to convert an DPUNS to a text string
+** in any base. See words.c:numberSignS, for example.
+** Mechanics: performs 4 ficlLongDivs, each of which produces 16 bits
+** of the quotient. C does not provide a way to divide an FICL_UNS by an
+** UNS16 and get an FICL_UNS quotient (ldiv is closest, but it's signed,
+** unfortunately), so I've used ficlLongDiv.
+**************************************************************************/
+#if (BITS_PER_CELL == 32)
+
+#define UMOD_SHIFT 16
+#define UMOD_MASK 0x0000ffff
+
+#elif (BITS_PER_CELL == 64)
+
+#define UMOD_SHIFT 32
+#define UMOD_MASK 0x00000000ffffffff
+
+#endif
+
+UNS16 m64UMod(DPUNS *pUD, UNS16 base)
+{
+ DPUNS ud;
+ UNSQR qr;
+ DPUNS result;
+
+ result.hi = result.lo = 0;
+
+ ud.hi = 0;
+ ud.lo = pUD->hi >> UMOD_SHIFT;
+ qr = ficlLongDiv(ud, (FICL_UNS)base);
+ result.hi = qr.quot << UMOD_SHIFT;
+
+ ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->hi & UMOD_MASK);
+ qr = ficlLongDiv(ud, (FICL_UNS)base);
+ result.hi |= qr.quot & UMOD_MASK;
+
+ ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->lo >> UMOD_SHIFT);
+ qr = ficlLongDiv(ud, (FICL_UNS)base);
+ result.lo = qr.quot << UMOD_SHIFT;
+
+ ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->lo & UMOD_MASK);
+ qr = ficlLongDiv(ud, (FICL_UNS)base);
+ result.lo |= qr.quot & UMOD_MASK;
+
+ *pUD = result;
+
+ return (UNS16)(qr.rem);
+}
+
+
+/**************************************************************************
+** Contributed by
+** Michael A. Gauland gaulandm@mdhost.cse.tek.com
+**************************************************************************/
+#if PORTABLE_LONGMULDIV != 0
+/**************************************************************************
+ m 6 4 A d d
+**
+**************************************************************************/
+DPUNS m64Add(DPUNS x, DPUNS y)
+{
+ DPUNS result;
+ int carry;
+
+ result.hi = x.hi + y.hi;
+ result.lo = x.lo + y.lo;
+
+
+ carry = ((x.lo | y.lo) & CELL_HI_BIT) && !(result.lo & CELL_HI_BIT);
+ carry |= ((x.lo & y.lo) & CELL_HI_BIT);
+
+ if (carry)
+ {
+ result.hi++;
+ }
+
+ return result;
+}
+
+
+/**************************************************************************
+ m 6 4 S u b
+**
+**************************************************************************/
+DPUNS m64Sub(DPUNS x, DPUNS y)
+{
+ DPUNS result;
+
+ result.hi = x.hi - y.hi;
+ result.lo = x.lo - y.lo;
+
+ if (x.lo < y.lo)
+ {
+ result.hi--;
+ }
+
+ return result;
+}
+
+
+/**************************************************************************
+ m 6 4 A S L
+** 64 bit left shift
+**************************************************************************/
+DPUNS m64ASL( DPUNS x )
+{
+ DPUNS result;
+
+ result.hi = x.hi << 1;
+ if (x.lo & CELL_HI_BIT)
+ {
+ result.hi++;
+ }
+
+ result.lo = x.lo << 1;
+
+ return result;
+}
+
+
+/**************************************************************************
+ m 6 4 A S R
+** 64 bit right shift (unsigned - no sign extend)
+**************************************************************************/
+DPUNS m64ASR( DPUNS x )
+{
+ DPUNS result;
+
+ result.lo = x.lo >> 1;
+ if (x.hi & 1)
+ {
+ result.lo |= CELL_HI_BIT;
+ }
+
+ result.hi = x.hi >> 1;
+ return result;
+}
+
+
+/**************************************************************************
+ m 6 4 O r
+** 64 bit bitwise OR
+**************************************************************************/
+DPUNS m64Or( DPUNS x, DPUNS y )
+{
+ DPUNS result;
+
+ result.hi = x.hi | y.hi;
+ result.lo = x.lo | y.lo;
+
+ return result;
+}
+
+
+/**************************************************************************
+ m 6 4 C o m p a r e
+** Return -1 if x < y; 0 if x==y, and 1 if x > y.
+**************************************************************************/
+int m64Compare(DPUNS x, DPUNS y)
+{
+ int result;
+
+ if (x.hi > y.hi)
+ {
+ result = +1;
+ }
+ else if (x.hi < y.hi)
+ {
+ result = -1;
+ }
+ else
+ {
+ /* High parts are equal */
+ if (x.lo > y.lo)
+ {
+ result = +1;
+ }
+ else if (x.lo < y.lo)
+ {
+ result = -1;
+ }
+ else
+ {
+ result = 0;
+ }
+ }
+
+ return result;
+}
+
+
+/**************************************************************************
+ f i c l L o n g M u l
+** Portable versions of ficlLongMul and ficlLongDiv in C
+** Contributed by:
+** Michael A. Gauland gaulandm@mdhost.cse.tek.com
+**************************************************************************/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS result = { 0, 0 };
+ DPUNS addend;
+
+ addend.lo = y;
+ addend.hi = 0; /* No sign extension--arguments are unsigned */
+
+ while (x != 0)
+ {
+ if ( x & 1)
+ {
+ result = m64Add(result, addend);
+ }
+ x >>= 1;
+ addend = m64ASL(addend);
+ }
+ return result;
+}
+
+
+/**************************************************************************
+ f i c l L o n g D i v
+** Portable versions of ficlLongMul and ficlLongDiv in C
+** Contributed by:
+** Michael A. Gauland gaulandm@mdhost.cse.tek.com
+**************************************************************************/
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ DPUNS quotient;
+ DPUNS subtrahend;
+ DPUNS mask;
+
+ quotient.lo = 0;
+ quotient.hi = 0;
+
+ subtrahend.lo = y;
+ subtrahend.hi = 0;
+
+ mask.lo = 1;
+ mask.hi = 0;
+
+ while ((m64Compare(subtrahend, q) < 0) &&
+ (subtrahend.hi & CELL_HI_BIT) == 0)
+ {
+ mask = m64ASL(mask);
+ subtrahend = m64ASL(subtrahend);
+ }
+
+ while (mask.lo != 0 || mask.hi != 0)
+ {
+ if (m64Compare(subtrahend, q) <= 0)
+ {
+ q = m64Sub( q, subtrahend);
+ quotient = m64Or(quotient, mask);
+ }
+ mask = m64ASR(mask);
+ subtrahend = m64ASR(subtrahend);
+ }
+
+ result.quot = quotient.lo;
+ result.rem = q.lo;
+ return result;
+}
+
+#endif
+
diff --git a/stand/ficl/math64.h b/stand/ficl/math64.h
new file mode 100644
index 0000000..a4e5636
--- /dev/null
+++ b/stand/ficl/math64.h
@@ -0,0 +1,88 @@
+/*******************************************************************
+** m a t h 6 4 . h
+** Forth Inspired Command Language - 64 bit math support routines
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 25 January 1998
+** $Id: math64.h,v 1.9 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#if !defined (__MATH64_H__)
+#define __MATH64_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+DPINT m64Abs(DPINT x);
+int m64IsNegative(DPINT x);
+DPUNS m64Mac(DPUNS u, FICL_UNS mul, FICL_UNS add);
+DPINT m64MulI(FICL_INT x, FICL_INT y);
+DPINT m64Negate(DPINT x);
+INTQR m64FlooredDivI(DPINT num, FICL_INT den);
+void i64Push(FICL_STACK *pStack, DPINT i64);
+DPINT i64Pop(FICL_STACK *pStack);
+void u64Push(FICL_STACK *pStack, DPUNS u64);
+DPUNS u64Pop(FICL_STACK *pStack);
+INTQR m64SymmetricDivI(DPINT num, FICL_INT den);
+UNS16 m64UMod(DPUNS *pUD, UNS16 base);
+
+
+#if PORTABLE_LONGMULDIV != 0 /* see sysdep.h */
+DPUNS m64Add(DPUNS x, DPUNS y);
+DPUNS m64ASL( DPUNS x );
+DPUNS m64ASR( DPUNS x );
+int m64Compare(DPUNS x, DPUNS y);
+DPUNS m64Or( DPUNS x, DPUNS y );
+DPUNS m64Sub(DPUNS x, DPUNS y);
+#endif
+
+#define i64Extend(i64) (i64).hi = ((i64).lo < 0) ? -1L : 0
+#define m64CastIU(i64) (*(DPUNS *)(&(i64)))
+#define m64CastUI(u64) (*(DPINT *)(&(u64)))
+#define m64CastQRIU(iqr) (*(UNSQR *)(&(iqr)))
+#define m64CastQRUI(uqr) (*(INTQR *)(&(uqr)))
+
+#define CELL_HI_BIT (1L << (BITS_PER_CELL-1))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/stand/ficl/mips/sysdep.c b/stand/ficl/mips/sysdep.c
new file mode 100644
index 0000000..ad38660
--- /dev/null
+++ b/stand/ficl/mips/sysdep.c
@@ -0,0 +1,99 @@
+/*******************************************************************
+** s y s d e p . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Implementations of FICL external interface functions...
+**
+*******************************************************************/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include <stand.h>
+#endif
+#include "ficl.h"
+
+/*
+******************* FreeBSD P O R T B E G I N S H E R E ******************** Michael Smith
+*/
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ u_int64_t qx;
+
+ qx = (u_int64_t)x * (u_int64_t) y;
+
+ q.hi = (u_int32_t)( qx >> 32 );
+ q.lo = (u_int32_t)( qx & 0xFFFFFFFFL);
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ u_int64_t qx, qh;
+
+ qh = q.hi;
+ qx = (qh << 32) | q.lo;
+
+ result.quot = qx / y;
+ result.rem = qx % y;
+
+ return result;
+}
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ while(*msg != 0)
+ putchar(*(msg++));
+ if (fNewline)
+ putchar('\n');
+
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+void *ficlRealloc (void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
diff --git a/stand/ficl/mips/sysdep.h b/stand/ficl/mips/sysdep.h
new file mode 100644
index 0000000..3ae748e
--- /dev/null
+++ b/stand/ficl/mips/sysdep.h
@@ -0,0 +1,432 @@
+/*******************************************************************
+ s y s d e p . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Ficl system dependent types and prototypes...
+**
+** Note: Ficl also depends on the use of "assert" when
+** FICL_ROBUST is enabled. This may require some consideration
+** in firmware systems since assert often
+** assumes stderr/stdout.
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** 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.
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please send
+** contact me by email at the address above.
+**
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+** $FreeBSD$
+*/
+
+#if !defined (__SYSDEP_H__)
+#define __SYSDEP_H__
+
+#include <sys/types.h>
+
+#include <stddef.h> /* size_t, NULL */
+#include <setjmp.h>
+#include <assert.h>
+
+#if !defined IGNORE /* Macro to silence unused param warnings */
+#define IGNORE(x) &x
+#endif
+
+/*
+** TRUE and FALSE for C boolean operations, and
+** portable 32 bit types for CELLs
+**
+*/
+#if !defined TRUE
+#define TRUE 1
+#endif
+#if !defined FALSE
+#define FALSE 0
+#endif
+
+
+/*
+** System dependent data type declarations...
+*/
+#if !defined INT32
+#define INT32 int
+#endif
+
+#if !defined UNS32
+#define UNS32 unsigned int
+#endif
+
+#if !defined UNS16
+#define UNS16 unsigned short
+#endif
+
+#if !defined UNS8
+#define UNS8 unsigned char
+#endif
+
+#if !defined NULL
+#define NULL ((void *)0)
+#endif
+
+/*
+** FICL_UNS and FICL_INT must have the same size as a void* on
+** the target system. A CELL is a union of void*, FICL_UNS, and
+** FICL_INT.
+** (11/2000: same for FICL_FLOAT)
+*/
+#if !defined FICL_INT
+#define FICL_INT INT32
+#endif
+
+#if !defined FICL_UNS
+#define FICL_UNS UNS32
+#endif
+
+#if !defined FICL_FLOAT
+#define FICL_FLOAT float
+#endif
+
+/*
+** Ficl presently supports values of 32 and 64 for BITS_PER_CELL
+*/
+#if !defined BITS_PER_CELL
+#define BITS_PER_CELL 32
+#endif
+
+#if ((BITS_PER_CELL != 32) && (BITS_PER_CELL != 64))
+ Error!
+#endif
+
+typedef struct
+{
+ FICL_UNS hi;
+ FICL_UNS lo;
+} DPUNS;
+
+typedef struct
+{
+ FICL_UNS quot;
+ FICL_UNS rem;
+} UNSQR;
+
+typedef struct
+{
+ FICL_INT hi;
+ FICL_INT lo;
+} DPINT;
+
+typedef struct
+{
+ FICL_INT quot;
+ FICL_INT rem;
+} INTQR;
+
+
+/*
+** B U I L D C O N T R O L S
+*/
+
+#if !defined (FICL_MINIMAL)
+#define FICL_MINIMAL 0
+#endif
+#if (FICL_MINIMAL)
+#define FICL_WANT_SOFTWORDS 0
+#define FICL_WANT_FILE 0
+#define FICL_WANT_FLOAT 0
+#define FICL_WANT_USER 0
+#define FICL_WANT_LOCALS 0
+#define FICL_WANT_DEBUGGER 0
+#define FICL_WANT_OOP 0
+#define FICL_PLATFORM_EXTEND 0
+#define FICL_MULTITHREAD 0
+#define FICL_ROBUST 1
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_PLATFORM_EXTEND
+** Includes words defined in ficlCompilePlatform
+*/
+#if !defined (FICL_PLATFORM_EXTEND)
+#define FICL_PLATFORM_EXTEND 1
+#endif
+
+/*
+** FICL_WANT_FILE
+** Includes the FILE and FILE-EXT wordset and associated code. Turn this off if you do not
+** have a filesystem!
+** Contributed by Larry Hastings
+*/
+#if !defined (FICL_WANT_FILE)
+#define FICL_WANT_FILE 0
+#endif
+
+/*
+** FICL_WANT_FLOAT
+** Includes a floating point stack for the VM, and words to do float operations.
+** Contributed by Guy Carver
+*/
+#if !defined (FICL_WANT_FLOAT)
+#define FICL_WANT_FLOAT 0
+#endif
+
+/*
+** FICL_WANT_DEBUGGER
+** Inludes a simple source level debugger
+*/
+#if !defined (FICL_WANT_DEBUGGER)
+#define FICL_WANT_DEBUGGER 1
+#endif
+
+/*
+** User variables: per-instance variables bound to the VM.
+** Kinda like thread-local storage. Could be implemented in a
+** VM private dictionary, but I've chosen the lower overhead
+** approach of an array of CELLs instead.
+*/
+#if !defined FICL_WANT_USER
+#define FICL_WANT_USER 1
+#endif
+
+#if !defined FICL_USER_CELLS
+#define FICL_USER_CELLS 16
+#endif
+
+/*
+** FICL_WANT_LOCALS controls the creation of the LOCALS wordset and
+** a private dictionary for local variable compilation.
+*/
+#if !defined FICL_WANT_LOCALS
+#define FICL_WANT_LOCALS 1
+#endif
+
+/* Max number of local variables per definition */
+#if !defined FICL_MAX_LOCALS
+#define FICL_MAX_LOCALS 16
+#endif
+
+/*
+** FICL_WANT_OOP
+** Inludes object oriented programming support (in softwords)
+** OOP support requires locals and user variables!
+*/
+#if !(FICL_WANT_LOCALS) || !(FICL_WANT_USER)
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 0
+#endif
+#endif
+
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 1
+#endif
+
+/*
+** FICL_WANT_SOFTWORDS
+** Controls inclusion of all softwords in softcore.c
+*/
+#if !defined (FICL_WANT_SOFTWORDS)
+#define FICL_WANT_SOFTWORDS 1
+#endif
+
+/*
+** FICL_MULTITHREAD enables dictionary mutual exclusion
+** wia the ficlLockDictionary system dependent function.
+** Note: this implementation is experimental and poorly
+** tested. Further, it's unnecessary unless you really
+** intend to have multiple SESSIONS (poor choice of name
+** on my part) - that is, threads that modify the dictionary
+** at the same time.
+*/
+#if !defined FICL_MULTITHREAD
+#define FICL_MULTITHREAD 0
+#endif
+
+/*
+** PORTABLE_LONGMULDIV causes ficlLongMul and ficlLongDiv to be
+** defined in C in sysdep.c. Use this if you cannot easily
+** generate an inline asm definition
+*/
+#if !defined (PORTABLE_LONGMULDIV)
+#define PORTABLE_LONGMULDIV 0
+#endif
+
+/*
+** INLINE_INNER_LOOP causes the inner interpreter to be inline code
+** instead of a function call. This is mainly because MS VC++ 5
+** chokes with an internal compiler error on the function version.
+** in release mode. Sheesh.
+*/
+#if !defined INLINE_INNER_LOOP
+#if defined _DEBUG
+#define INLINE_INNER_LOOP 0
+#else
+#define INLINE_INNER_LOOP 1
+#endif
+#endif
+
+/*
+** FICL_ROBUST enables bounds checking of stacks and the dictionary.
+** This will detect stack over and underflows and dictionary overflows.
+** Any exceptional condition will result in an assertion failure.
+** (As generated by the ANSI assert macro)
+** FICL_ROBUST == 1 --> stack checking in the outer interpreter
+** FICL_ROBUST == 2 also enables checking in many primitives
+*/
+
+#if !defined FICL_ROBUST
+#define FICL_ROBUST 2
+#endif
+
+/*
+** FICL_DEFAULT_STACK Specifies the default size (in CELLs) of
+** a new virtual machine's stacks, unless overridden at
+** create time.
+*/
+#if !defined FICL_DEFAULT_STACK
+#define FICL_DEFAULT_STACK 128
+#endif
+
+/*
+** FICL_DEFAULT_DICT specifies the number of CELLs to allocate
+** for the system dictionary by default. The value
+** can be overridden at startup time as well.
+** FICL_DEFAULT_ENV specifies the number of cells to allot
+** for the environment-query dictionary.
+*/
+#if !defined FICL_DEFAULT_DICT
+#define FICL_DEFAULT_DICT 12288
+#endif
+
+#if !defined FICL_DEFAULT_ENV
+#define FICL_DEFAULT_ENV 260
+#endif
+
+/*
+** FICL_DEFAULT_VOCS specifies the maximum number of wordlists in
+** the dictionary search order. See Forth DPANS sec 16.3.3
+** (file://dpans16.htm#16.3.3)
+*/
+#if !defined FICL_DEFAULT_VOCS
+#define FICL_DEFAULT_VOCS 16
+#endif
+
+/*
+** FICL_MAX_PARSE_STEPS controls the size of an array in the FICL_SYSTEM structure
+** that stores pointers to parser extension functions. I would never expect to have
+** more than 8 of these, so that's the default limit. Too many of these functions
+** will probably exact a nasty performance penalty.
+*/
+#if !defined FICL_MAX_PARSE_STEPS
+#define FICL_MAX_PARSE_STEPS 8
+#endif
+
+/*
+** FICL_EXTENDED_PREFIX enables a bunch of extra prefixes in prefix.c and prefix.fr (if
+** included as part of softcore.c)
+*/
+#if !defined FICL_EXTENDED_PREFIX
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_ALIGN is the power of two to which the dictionary
+** pointer address must be aligned. This value is usually
+** either 1 or 2, depending on the memory architecture
+** of the target system; 2 is safe on any 16 or 32 bit
+** machine. 3 would be appropriate for a 64 bit machine.
+*/
+#if !defined FICL_ALIGN
+#define FICL_ALIGN 2
+#define FICL_ALIGN_ADD ((1 << FICL_ALIGN) - 1)
+#endif
+
+/*
+** System dependent routines --
+** edit the implementations in sysdep.c to be compatible
+** with your runtime environment...
+** ficlTextOut sends a NULL terminated string to the
+** default output device - used for system error messages
+** ficlMalloc and ficlFree have the same semantics as malloc and free
+** in standard C
+** ficlLongMul multiplies two UNS32s and returns a 64 bit unsigned
+** product
+** ficlLongDiv divides an UNS64 by an UNS32 and returns UNS32 quotient
+** and remainder
+*/
+struct vm;
+void ficlTextOut(struct vm *pVM, char *msg, int fNewline);
+void *ficlMalloc (size_t size);
+void ficlFree (void *p);
+void *ficlRealloc(void *p, size_t size);
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** must be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** before timeout (optional - could also block forever)
+**
+** NOTE: this function must be implemented with lock counting
+** semantics: nested calls must behave properly.
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock);
+#else
+#define ficlLockDictionary(x) 0 /* ignore */
+#endif
+
+/*
+** 64 bit integer math support routines: multiply two UNS32s
+** to get a 64 bit product, & divide the product by an UNS32
+** to get an UNS32 quotient and remainder. Much easier in asm
+** on a 32 bit CPU than in C, which usually doesn't support
+** the double length result (but it should).
+*/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y);
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y);
+
+/*
+** FICL_HAVE_FTRUNCATE indicates whether the current OS supports
+** the ftruncate() function (available on most UNIXes). This
+** function is necessary to provide the complete File-Access wordset.
+*/
+#if !defined (FICL_HAVE_FTRUNCATE)
+#define FICL_HAVE_FTRUNCATE 0
+#endif
+
+
+#endif /*__SYSDEP_H__*/
diff --git a/stand/ficl/mips64/sysdep.c b/stand/ficl/mips64/sysdep.c
new file mode 100644
index 0000000..ad38660
--- /dev/null
+++ b/stand/ficl/mips64/sysdep.c
@@ -0,0 +1,99 @@
+/*******************************************************************
+** s y s d e p . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Implementations of FICL external interface functions...
+**
+*******************************************************************/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include <stand.h>
+#endif
+#include "ficl.h"
+
+/*
+******************* FreeBSD P O R T B E G I N S H E R E ******************** Michael Smith
+*/
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ u_int64_t qx;
+
+ qx = (u_int64_t)x * (u_int64_t) y;
+
+ q.hi = (u_int32_t)( qx >> 32 );
+ q.lo = (u_int32_t)( qx & 0xFFFFFFFFL);
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ u_int64_t qx, qh;
+
+ qh = q.hi;
+ qx = (qh << 32) | q.lo;
+
+ result.quot = qx / y;
+ result.rem = qx % y;
+
+ return result;
+}
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ while(*msg != 0)
+ putchar(*(msg++));
+ if (fNewline)
+ putchar('\n');
+
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+void *ficlRealloc (void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
diff --git a/stand/ficl/mips64/sysdep.h b/stand/ficl/mips64/sysdep.h
new file mode 100644
index 0000000..5c9e163
--- /dev/null
+++ b/stand/ficl/mips64/sysdep.h
@@ -0,0 +1,432 @@
+/*******************************************************************
+ s y s d e p . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Ficl system dependent types and prototypes...
+**
+** Note: Ficl also depends on the use of "assert" when
+** FICL_ROBUST is enabled. This may require some consideration
+** in firmware systems since assert often
+** assumes stderr/stdout.
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** 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.
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please send
+** contact me by email at the address above.
+**
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+** $FreeBSD$
+*/
+
+#if !defined (__SYSDEP_H__)
+#define __SYSDEP_H__
+
+#include <sys/types.h>
+
+#include <stddef.h> /* size_t, NULL */
+#include <setjmp.h>
+#include <assert.h>
+
+#if !defined IGNORE /* Macro to silence unused param warnings */
+#define IGNORE(x) &x
+#endif
+
+/*
+** TRUE and FALSE for C boolean operations, and
+** portable 32 bit types for CELLs
+**
+*/
+#if !defined TRUE
+#define TRUE 1
+#endif
+#if !defined FALSE
+#define FALSE 0
+#endif
+
+
+/*
+** System dependent data type declarations...
+*/
+#if !defined INT32
+#define INT32 int
+#endif
+
+#if !defined UNS32
+#define UNS32 unsigned int
+#endif
+
+#if !defined UNS16
+#define UNS16 unsigned short
+#endif
+
+#if !defined UNS8
+#define UNS8 unsigned char
+#endif
+
+#if !defined NULL
+#define NULL ((void *)0)
+#endif
+
+/*
+** FICL_UNS and FICL_INT must have the same size as a void* on
+** the target system. A CELL is a union of void*, FICL_UNS, and
+** FICL_INT.
+** (11/2000: same for FICL_FLOAT)
+*/
+#if !defined FICL_INT
+#define FICL_INT long
+#endif
+
+#if !defined FICL_UNS
+#define FICL_UNS unsigned long
+#endif
+
+#if !defined FICL_FLOAT
+#define FICL_FLOAT float
+#endif
+
+/*
+** Ficl presently supports values of 32 and 64 for BITS_PER_CELL
+*/
+#if !defined BITS_PER_CELL
+#define BITS_PER_CELL 64
+#endif
+
+#if ((BITS_PER_CELL != 32) && (BITS_PER_CELL != 64))
+ Error!
+#endif
+
+typedef struct
+{
+ FICL_UNS hi;
+ FICL_UNS lo;
+} DPUNS;
+
+typedef struct
+{
+ FICL_UNS quot;
+ FICL_UNS rem;
+} UNSQR;
+
+typedef struct
+{
+ FICL_INT hi;
+ FICL_INT lo;
+} DPINT;
+
+typedef struct
+{
+ FICL_INT quot;
+ FICL_INT rem;
+} INTQR;
+
+
+/*
+** B U I L D C O N T R O L S
+*/
+
+#if !defined (FICL_MINIMAL)
+#define FICL_MINIMAL 0
+#endif
+#if (FICL_MINIMAL)
+#define FICL_WANT_SOFTWORDS 0
+#define FICL_WANT_FILE 0
+#define FICL_WANT_FLOAT 0
+#define FICL_WANT_USER 0
+#define FICL_WANT_LOCALS 0
+#define FICL_WANT_DEBUGGER 0
+#define FICL_WANT_OOP 0
+#define FICL_PLATFORM_EXTEND 0
+#define FICL_MULTITHREAD 0
+#define FICL_ROBUST 1
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_PLATFORM_EXTEND
+** Includes words defined in ficlCompilePlatform
+*/
+#if !defined (FICL_PLATFORM_EXTEND)
+#define FICL_PLATFORM_EXTEND 1
+#endif
+
+/*
+** FICL_WANT_FILE
+** Includes the FILE and FILE-EXT wordset and associated code. Turn this off if you do not
+** have a filesystem!
+** Contributed by Larry Hastings
+*/
+#if !defined (FICL_WANT_FILE)
+#define FICL_WANT_FILE 0
+#endif
+
+/*
+** FICL_WANT_FLOAT
+** Includes a floating point stack for the VM, and words to do float operations.
+** Contributed by Guy Carver
+*/
+#if !defined (FICL_WANT_FLOAT)
+#define FICL_WANT_FLOAT 0
+#endif
+
+/*
+** FICL_WANT_DEBUGGER
+** Inludes a simple source level debugger
+*/
+#if !defined (FICL_WANT_DEBUGGER)
+#define FICL_WANT_DEBUGGER 1
+#endif
+
+/*
+** User variables: per-instance variables bound to the VM.
+** Kinda like thread-local storage. Could be implemented in a
+** VM private dictionary, but I've chosen the lower overhead
+** approach of an array of CELLs instead.
+*/
+#if !defined FICL_WANT_USER
+#define FICL_WANT_USER 1
+#endif
+
+#if !defined FICL_USER_CELLS
+#define FICL_USER_CELLS 16
+#endif
+
+/*
+** FICL_WANT_LOCALS controls the creation of the LOCALS wordset and
+** a private dictionary for local variable compilation.
+*/
+#if !defined FICL_WANT_LOCALS
+#define FICL_WANT_LOCALS 1
+#endif
+
+/* Max number of local variables per definition */
+#if !defined FICL_MAX_LOCALS
+#define FICL_MAX_LOCALS 16
+#endif
+
+/*
+** FICL_WANT_OOP
+** Inludes object oriented programming support (in softwords)
+** OOP support requires locals and user variables!
+*/
+#if !(FICL_WANT_LOCALS) || !(FICL_WANT_USER)
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 0
+#endif
+#endif
+
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 1
+#endif
+
+/*
+** FICL_WANT_SOFTWORDS
+** Controls inclusion of all softwords in softcore.c
+*/
+#if !defined (FICL_WANT_SOFTWORDS)
+#define FICL_WANT_SOFTWORDS 1
+#endif
+
+/*
+** FICL_MULTITHREAD enables dictionary mutual exclusion
+** wia the ficlLockDictionary system dependent function.
+** Note: this implementation is experimental and poorly
+** tested. Further, it's unnecessary unless you really
+** intend to have multiple SESSIONS (poor choice of name
+** on my part) - that is, threads that modify the dictionary
+** at the same time.
+*/
+#if !defined FICL_MULTITHREAD
+#define FICL_MULTITHREAD 0
+#endif
+
+/*
+** PORTABLE_LONGMULDIV causes ficlLongMul and ficlLongDiv to be
+** defined in C in sysdep.c. Use this if you cannot easily
+** generate an inline asm definition
+*/
+#if !defined (PORTABLE_LONGMULDIV)
+#define PORTABLE_LONGMULDIV 0
+#endif
+
+/*
+** INLINE_INNER_LOOP causes the inner interpreter to be inline code
+** instead of a function call. This is mainly because MS VC++ 5
+** chokes with an internal compiler error on the function version.
+** in release mode. Sheesh.
+*/
+#if !defined INLINE_INNER_LOOP
+#if defined _DEBUG
+#define INLINE_INNER_LOOP 0
+#else
+#define INLINE_INNER_LOOP 1
+#endif
+#endif
+
+/*
+** FICL_ROBUST enables bounds checking of stacks and the dictionary.
+** This will detect stack over and underflows and dictionary overflows.
+** Any exceptional condition will result in an assertion failure.
+** (As generated by the ANSI assert macro)
+** FICL_ROBUST == 1 --> stack checking in the outer interpreter
+** FICL_ROBUST == 2 also enables checking in many primitives
+*/
+
+#if !defined FICL_ROBUST
+#define FICL_ROBUST 2
+#endif
+
+/*
+** FICL_DEFAULT_STACK Specifies the default size (in CELLs) of
+** a new virtual machine's stacks, unless overridden at
+** create time.
+*/
+#if !defined FICL_DEFAULT_STACK
+#define FICL_DEFAULT_STACK 128
+#endif
+
+/*
+** FICL_DEFAULT_DICT specifies the number of CELLs to allocate
+** for the system dictionary by default. The value
+** can be overridden at startup time as well.
+** FICL_DEFAULT_ENV specifies the number of cells to allot
+** for the environment-query dictionary.
+*/
+#if !defined FICL_DEFAULT_DICT
+#define FICL_DEFAULT_DICT 12288
+#endif
+
+#if !defined FICL_DEFAULT_ENV
+#define FICL_DEFAULT_ENV 260
+#endif
+
+/*
+** FICL_DEFAULT_VOCS specifies the maximum number of wordlists in
+** the dictionary search order. See Forth DPANS sec 16.3.3
+** (file://dpans16.htm#16.3.3)
+*/
+#if !defined FICL_DEFAULT_VOCS
+#define FICL_DEFAULT_VOCS 16
+#endif
+
+/*
+** FICL_MAX_PARSE_STEPS controls the size of an array in the FICL_SYSTEM structure
+** that stores pointers to parser extension functions. I would never expect to have
+** more than 8 of these, so that's the default limit. Too many of these functions
+** will probably exact a nasty performance penalty.
+*/
+#if !defined FICL_MAX_PARSE_STEPS
+#define FICL_MAX_PARSE_STEPS 8
+#endif
+
+/*
+** FICL_EXTENDED_PREFIX enables a bunch of extra prefixes in prefix.c and prefix.fr (if
+** included as part of softcore.c)
+*/
+#if !defined FICL_EXTENDED_PREFIX
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_ALIGN is the power of two to which the dictionary
+** pointer address must be aligned. This value is usually
+** either 1 or 2, depending on the memory architecture
+** of the target system; 2 is safe on any 16 or 32 bit
+** machine. 3 would be appropriate for a 64 bit machine.
+*/
+#if !defined FICL_ALIGN
+#define FICL_ALIGN 3
+#define FICL_ALIGN_ADD ((1 << FICL_ALIGN) - 1)
+#endif
+
+/*
+** System dependent routines --
+** edit the implementations in sysdep.c to be compatible
+** with your runtime environment...
+** ficlTextOut sends a NULL terminated string to the
+** default output device - used for system error messages
+** ficlMalloc and ficlFree have the same semantics as malloc and free
+** in standard C
+** ficlLongMul multiplies two UNS32s and returns a 64 bit unsigned
+** product
+** ficlLongDiv divides an UNS64 by an UNS32 and returns UNS32 quotient
+** and remainder
+*/
+struct vm;
+void ficlTextOut(struct vm *pVM, char *msg, int fNewline);
+void *ficlMalloc (size_t size);
+void ficlFree (void *p);
+void *ficlRealloc(void *p, size_t size);
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** must be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** before timeout (optional - could also block forever)
+**
+** NOTE: this function must be implemented with lock counting
+** semantics: nested calls must behave properly.
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock);
+#else
+#define ficlLockDictionary(x) 0 /* ignore */
+#endif
+
+/*
+** 64 bit integer math support routines: multiply two UNS32s
+** to get a 64 bit product, & divide the product by an UNS32
+** to get an UNS32 quotient and remainder. Much easier in asm
+** on a 32 bit CPU than in C, which usually doesn't support
+** the double length result (but it should).
+*/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y);
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y);
+
+/*
+** FICL_HAVE_FTRUNCATE indicates whether the current OS supports
+** the ftruncate() function (available on most UNIXes). This
+** function is necessary to provide the complete File-Access wordset.
+*/
+#if !defined (FICL_HAVE_FTRUNCATE)
+#define FICL_HAVE_FTRUNCATE 0
+#endif
+
+
+#endif /*__SYSDEP_H__*/
diff --git a/stand/ficl/powerpc/sysdep.c b/stand/ficl/powerpc/sysdep.c
new file mode 100644
index 0000000..ad38660
--- /dev/null
+++ b/stand/ficl/powerpc/sysdep.c
@@ -0,0 +1,99 @@
+/*******************************************************************
+** s y s d e p . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Implementations of FICL external interface functions...
+**
+*******************************************************************/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include <stand.h>
+#endif
+#include "ficl.h"
+
+/*
+******************* FreeBSD P O R T B E G I N S H E R E ******************** Michael Smith
+*/
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ u_int64_t qx;
+
+ qx = (u_int64_t)x * (u_int64_t) y;
+
+ q.hi = (u_int32_t)( qx >> 32 );
+ q.lo = (u_int32_t)( qx & 0xFFFFFFFFL);
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ u_int64_t qx, qh;
+
+ qh = q.hi;
+ qx = (qh << 32) | q.lo;
+
+ result.quot = qx / y;
+ result.rem = qx % y;
+
+ return result;
+}
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ while(*msg != 0)
+ putchar(*(msg++));
+ if (fNewline)
+ putchar('\n');
+
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+void *ficlRealloc (void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
diff --git a/stand/ficl/powerpc/sysdep.h b/stand/ficl/powerpc/sysdep.h
new file mode 100644
index 0000000..3ae748e
--- /dev/null
+++ b/stand/ficl/powerpc/sysdep.h
@@ -0,0 +1,432 @@
+/*******************************************************************
+ s y s d e p . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Ficl system dependent types and prototypes...
+**
+** Note: Ficl also depends on the use of "assert" when
+** FICL_ROBUST is enabled. This may require some consideration
+** in firmware systems since assert often
+** assumes stderr/stdout.
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** 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.
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please send
+** contact me by email at the address above.
+**
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+** $FreeBSD$
+*/
+
+#if !defined (__SYSDEP_H__)
+#define __SYSDEP_H__
+
+#include <sys/types.h>
+
+#include <stddef.h> /* size_t, NULL */
+#include <setjmp.h>
+#include <assert.h>
+
+#if !defined IGNORE /* Macro to silence unused param warnings */
+#define IGNORE(x) &x
+#endif
+
+/*
+** TRUE and FALSE for C boolean operations, and
+** portable 32 bit types for CELLs
+**
+*/
+#if !defined TRUE
+#define TRUE 1
+#endif
+#if !defined FALSE
+#define FALSE 0
+#endif
+
+
+/*
+** System dependent data type declarations...
+*/
+#if !defined INT32
+#define INT32 int
+#endif
+
+#if !defined UNS32
+#define UNS32 unsigned int
+#endif
+
+#if !defined UNS16
+#define UNS16 unsigned short
+#endif
+
+#if !defined UNS8
+#define UNS8 unsigned char
+#endif
+
+#if !defined NULL
+#define NULL ((void *)0)
+#endif
+
+/*
+** FICL_UNS and FICL_INT must have the same size as a void* on
+** the target system. A CELL is a union of void*, FICL_UNS, and
+** FICL_INT.
+** (11/2000: same for FICL_FLOAT)
+*/
+#if !defined FICL_INT
+#define FICL_INT INT32
+#endif
+
+#if !defined FICL_UNS
+#define FICL_UNS UNS32
+#endif
+
+#if !defined FICL_FLOAT
+#define FICL_FLOAT float
+#endif
+
+/*
+** Ficl presently supports values of 32 and 64 for BITS_PER_CELL
+*/
+#if !defined BITS_PER_CELL
+#define BITS_PER_CELL 32
+#endif
+
+#if ((BITS_PER_CELL != 32) && (BITS_PER_CELL != 64))
+ Error!
+#endif
+
+typedef struct
+{
+ FICL_UNS hi;
+ FICL_UNS lo;
+} DPUNS;
+
+typedef struct
+{
+ FICL_UNS quot;
+ FICL_UNS rem;
+} UNSQR;
+
+typedef struct
+{
+ FICL_INT hi;
+ FICL_INT lo;
+} DPINT;
+
+typedef struct
+{
+ FICL_INT quot;
+ FICL_INT rem;
+} INTQR;
+
+
+/*
+** B U I L D C O N T R O L S
+*/
+
+#if !defined (FICL_MINIMAL)
+#define FICL_MINIMAL 0
+#endif
+#if (FICL_MINIMAL)
+#define FICL_WANT_SOFTWORDS 0
+#define FICL_WANT_FILE 0
+#define FICL_WANT_FLOAT 0
+#define FICL_WANT_USER 0
+#define FICL_WANT_LOCALS 0
+#define FICL_WANT_DEBUGGER 0
+#define FICL_WANT_OOP 0
+#define FICL_PLATFORM_EXTEND 0
+#define FICL_MULTITHREAD 0
+#define FICL_ROBUST 1
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_PLATFORM_EXTEND
+** Includes words defined in ficlCompilePlatform
+*/
+#if !defined (FICL_PLATFORM_EXTEND)
+#define FICL_PLATFORM_EXTEND 1
+#endif
+
+/*
+** FICL_WANT_FILE
+** Includes the FILE and FILE-EXT wordset and associated code. Turn this off if you do not
+** have a filesystem!
+** Contributed by Larry Hastings
+*/
+#if !defined (FICL_WANT_FILE)
+#define FICL_WANT_FILE 0
+#endif
+
+/*
+** FICL_WANT_FLOAT
+** Includes a floating point stack for the VM, and words to do float operations.
+** Contributed by Guy Carver
+*/
+#if !defined (FICL_WANT_FLOAT)
+#define FICL_WANT_FLOAT 0
+#endif
+
+/*
+** FICL_WANT_DEBUGGER
+** Inludes a simple source level debugger
+*/
+#if !defined (FICL_WANT_DEBUGGER)
+#define FICL_WANT_DEBUGGER 1
+#endif
+
+/*
+** User variables: per-instance variables bound to the VM.
+** Kinda like thread-local storage. Could be implemented in a
+** VM private dictionary, but I've chosen the lower overhead
+** approach of an array of CELLs instead.
+*/
+#if !defined FICL_WANT_USER
+#define FICL_WANT_USER 1
+#endif
+
+#if !defined FICL_USER_CELLS
+#define FICL_USER_CELLS 16
+#endif
+
+/*
+** FICL_WANT_LOCALS controls the creation of the LOCALS wordset and
+** a private dictionary for local variable compilation.
+*/
+#if !defined FICL_WANT_LOCALS
+#define FICL_WANT_LOCALS 1
+#endif
+
+/* Max number of local variables per definition */
+#if !defined FICL_MAX_LOCALS
+#define FICL_MAX_LOCALS 16
+#endif
+
+/*
+** FICL_WANT_OOP
+** Inludes object oriented programming support (in softwords)
+** OOP support requires locals and user variables!
+*/
+#if !(FICL_WANT_LOCALS) || !(FICL_WANT_USER)
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 0
+#endif
+#endif
+
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 1
+#endif
+
+/*
+** FICL_WANT_SOFTWORDS
+** Controls inclusion of all softwords in softcore.c
+*/
+#if !defined (FICL_WANT_SOFTWORDS)
+#define FICL_WANT_SOFTWORDS 1
+#endif
+
+/*
+** FICL_MULTITHREAD enables dictionary mutual exclusion
+** wia the ficlLockDictionary system dependent function.
+** Note: this implementation is experimental and poorly
+** tested. Further, it's unnecessary unless you really
+** intend to have multiple SESSIONS (poor choice of name
+** on my part) - that is, threads that modify the dictionary
+** at the same time.
+*/
+#if !defined FICL_MULTITHREAD
+#define FICL_MULTITHREAD 0
+#endif
+
+/*
+** PORTABLE_LONGMULDIV causes ficlLongMul and ficlLongDiv to be
+** defined in C in sysdep.c. Use this if you cannot easily
+** generate an inline asm definition
+*/
+#if !defined (PORTABLE_LONGMULDIV)
+#define PORTABLE_LONGMULDIV 0
+#endif
+
+/*
+** INLINE_INNER_LOOP causes the inner interpreter to be inline code
+** instead of a function call. This is mainly because MS VC++ 5
+** chokes with an internal compiler error on the function version.
+** in release mode. Sheesh.
+*/
+#if !defined INLINE_INNER_LOOP
+#if defined _DEBUG
+#define INLINE_INNER_LOOP 0
+#else
+#define INLINE_INNER_LOOP 1
+#endif
+#endif
+
+/*
+** FICL_ROBUST enables bounds checking of stacks and the dictionary.
+** This will detect stack over and underflows and dictionary overflows.
+** Any exceptional condition will result in an assertion failure.
+** (As generated by the ANSI assert macro)
+** FICL_ROBUST == 1 --> stack checking in the outer interpreter
+** FICL_ROBUST == 2 also enables checking in many primitives
+*/
+
+#if !defined FICL_ROBUST
+#define FICL_ROBUST 2
+#endif
+
+/*
+** FICL_DEFAULT_STACK Specifies the default size (in CELLs) of
+** a new virtual machine's stacks, unless overridden at
+** create time.
+*/
+#if !defined FICL_DEFAULT_STACK
+#define FICL_DEFAULT_STACK 128
+#endif
+
+/*
+** FICL_DEFAULT_DICT specifies the number of CELLs to allocate
+** for the system dictionary by default. The value
+** can be overridden at startup time as well.
+** FICL_DEFAULT_ENV specifies the number of cells to allot
+** for the environment-query dictionary.
+*/
+#if !defined FICL_DEFAULT_DICT
+#define FICL_DEFAULT_DICT 12288
+#endif
+
+#if !defined FICL_DEFAULT_ENV
+#define FICL_DEFAULT_ENV 260
+#endif
+
+/*
+** FICL_DEFAULT_VOCS specifies the maximum number of wordlists in
+** the dictionary search order. See Forth DPANS sec 16.3.3
+** (file://dpans16.htm#16.3.3)
+*/
+#if !defined FICL_DEFAULT_VOCS
+#define FICL_DEFAULT_VOCS 16
+#endif
+
+/*
+** FICL_MAX_PARSE_STEPS controls the size of an array in the FICL_SYSTEM structure
+** that stores pointers to parser extension functions. I would never expect to have
+** more than 8 of these, so that's the default limit. Too many of these functions
+** will probably exact a nasty performance penalty.
+*/
+#if !defined FICL_MAX_PARSE_STEPS
+#define FICL_MAX_PARSE_STEPS 8
+#endif
+
+/*
+** FICL_EXTENDED_PREFIX enables a bunch of extra prefixes in prefix.c and prefix.fr (if
+** included as part of softcore.c)
+*/
+#if !defined FICL_EXTENDED_PREFIX
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_ALIGN is the power of two to which the dictionary
+** pointer address must be aligned. This value is usually
+** either 1 or 2, depending on the memory architecture
+** of the target system; 2 is safe on any 16 or 32 bit
+** machine. 3 would be appropriate for a 64 bit machine.
+*/
+#if !defined FICL_ALIGN
+#define FICL_ALIGN 2
+#define FICL_ALIGN_ADD ((1 << FICL_ALIGN) - 1)
+#endif
+
+/*
+** System dependent routines --
+** edit the implementations in sysdep.c to be compatible
+** with your runtime environment...
+** ficlTextOut sends a NULL terminated string to the
+** default output device - used for system error messages
+** ficlMalloc and ficlFree have the same semantics as malloc and free
+** in standard C
+** ficlLongMul multiplies two UNS32s and returns a 64 bit unsigned
+** product
+** ficlLongDiv divides an UNS64 by an UNS32 and returns UNS32 quotient
+** and remainder
+*/
+struct vm;
+void ficlTextOut(struct vm *pVM, char *msg, int fNewline);
+void *ficlMalloc (size_t size);
+void ficlFree (void *p);
+void *ficlRealloc(void *p, size_t size);
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** must be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** before timeout (optional - could also block forever)
+**
+** NOTE: this function must be implemented with lock counting
+** semantics: nested calls must behave properly.
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock);
+#else
+#define ficlLockDictionary(x) 0 /* ignore */
+#endif
+
+/*
+** 64 bit integer math support routines: multiply two UNS32s
+** to get a 64 bit product, & divide the product by an UNS32
+** to get an UNS32 quotient and remainder. Much easier in asm
+** on a 32 bit CPU than in C, which usually doesn't support
+** the double length result (but it should).
+*/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y);
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y);
+
+/*
+** FICL_HAVE_FTRUNCATE indicates whether the current OS supports
+** the ftruncate() function (available on most UNIXes). This
+** function is necessary to provide the complete File-Access wordset.
+*/
+#if !defined (FICL_HAVE_FTRUNCATE)
+#define FICL_HAVE_FTRUNCATE 0
+#endif
+
+
+#endif /*__SYSDEP_H__*/
diff --git a/stand/ficl/prefix.c b/stand/ficl/prefix.c
new file mode 100644
index 0000000..a34fc6c
--- /dev/null
+++ b/stand/ficl/prefix.c
@@ -0,0 +1,199 @@
+/*******************************************************************
+** p r e f i x . c
+** Forth Inspired Command Language
+** Parser extensions for Ficl
+** Authors: Larry Hastings & John Sadler (john_sadler@alum.mit.edu)
+** Created: April 2001
+** $Id: prefix.c,v 1.6 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#include <string.h>
+#include <ctype.h>
+#include "ficl.h"
+#include "math64.h"
+
+/*
+** (jws) revisions:
+** A prefix is a word in a dedicated wordlist (name stored in list_name below)
+** that is searched in a special way by the prefix parse step. When a prefix
+** matches the beginning of an incoming token, push the non-prefix part of the
+** token back onto the input stream and execute the prefix code.
+**
+** The parse step is called ficlParsePrefix.
+** Storing prefix entries in the dictionary greatly simplifies
+** the process of matching and dispatching prefixes, avoids the
+** need to clean up a dynamically allocated prefix list when the system
+** goes away, but still allows prefixes to be allocated at runtime.
+*/
+
+static char list_name[] = "<prefixes>";
+
+/**************************************************************************
+ f i c l P a r s e P r e f i x
+** This is the parse step for prefixes - it checks an incoming word
+** to see if it starts with a prefix, and if so runs the corrseponding
+** code against the remainder of the word and returns true.
+**************************************************************************/
+int ficlParsePrefix(FICL_VM *pVM, STRINGINFO si)
+{
+ int i;
+ FICL_HASH *pHash;
+ FICL_WORD *pFW = ficlLookup(pVM->pSys, list_name);
+
+ /*
+ ** Make sure we found the prefix dictionary - otherwise silently fail
+ ** If forth-wordlist is not in the search order, we won't find the prefixes.
+ */
+ if (!pFW)
+ return FICL_FALSE;
+
+ pHash = (FICL_HASH *)(pFW->param[0].p);
+ /*
+ ** Walk the list looking for a match with the beginning of the incoming token
+ */
+ for (i = 0; i < (int)pHash->size; i++)
+ {
+ pFW = pHash->table[i];
+ while (pFW != NULL)
+ {
+ int n;
+ n = pFW->nName;
+ /*
+ ** If we find a match, adjust the TIB to give back the non-prefix characters
+ ** and execute the prefix word.
+ */
+ if (!strincmp(SI_PTR(si), pFW->name, (FICL_UNS)n))
+ {
+ /* (sadler) fixed off-by-one error when the token has no trailing space in the TIB */
+ vmSetTibIndex(pVM, si.cp + n - pVM->tib.cp );
+ vmExecute(pVM, pFW);
+
+ return (int)FICL_TRUE;
+ }
+ pFW = pFW->link;
+ }
+ }
+
+ return FICL_FALSE;
+}
+
+
+static void tempBase(FICL_VM *pVM, int base)
+{
+ int oldbase = pVM->base;
+ STRINGINFO si = vmGetWord0(pVM);
+
+ pVM->base = base;
+ if (!ficlParseNumber(pVM, si))
+ {
+ int i = SI_COUNT(si);
+ vmThrowErr(pVM, "%.*s not recognized", i, SI_PTR(si));
+ }
+
+ pVM->base = oldbase;
+ return;
+}
+
+static void fTempBase(FICL_VM *pVM)
+{
+ int base = stackPopINT(pVM->pStack);
+ tempBase(pVM, base);
+ return;
+}
+
+static void prefixHex(FICL_VM *pVM)
+{
+ tempBase(pVM, 16);
+}
+
+static void prefixTen(FICL_VM *pVM)
+{
+ tempBase(pVM, 10);
+}
+
+
+/**************************************************************************
+ f i c l C o m p i l e P r e f i x
+** Build prefix support into the dictionary and the parser
+** Note: since prefixes always execute, they are effectively IMMEDIATE.
+** If they need to generate code in compile state you must add
+** this code explicitly.
+**************************************************************************/
+void ficlCompilePrefix(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ FICL_HASH *pHash;
+ FICL_HASH *pPrevCompile = dp->pCompile;
+#if (FICL_EXTENDED_PREFIX)
+ FICL_WORD *pFW;
+#endif
+
+ /*
+ ** Create a named wordlist for prefixes to reside in...
+ ** Since we're doing a special kind of search, make it
+ ** a single bucket hashtable - hashing does not help here.
+ */
+ pHash = dictCreateWordlist(dp, 1);
+ pHash->name = list_name;
+ dictAppendWord(dp, list_name, constantParen, FW_DEFAULT);
+ dictAppendCell(dp, LVALUEtoCELL(pHash));
+
+ /*
+ ** Put __tempbase in the forth-wordlist
+ */
+ dictAppendWord(dp, "__tempbase", fTempBase, FW_DEFAULT);
+
+ /*
+ ** Temporarily make the prefix list the compile wordlist so that
+ ** we can create some precompiled prefixes.
+ */
+ dp->pCompile = pHash;
+ dictAppendWord(dp, "0x", prefixHex, FW_DEFAULT);
+ dictAppendWord(dp, "0d", prefixTen, FW_DEFAULT);
+#if (FICL_EXTENDED_PREFIX)
+ pFW = ficlLookup(pSys, "\\");
+ if (pFW)
+ {
+ dictAppendWord(dp, "//", pFW->code, FW_DEFAULT);
+ }
+#endif
+ dp->pCompile = pPrevCompile;
+
+ return;
+}
diff --git a/stand/ficl/riscv/sysdep.c b/stand/ficl/riscv/sysdep.c
new file mode 100644
index 0000000..ad38660
--- /dev/null
+++ b/stand/ficl/riscv/sysdep.c
@@ -0,0 +1,99 @@
+/*******************************************************************
+** s y s d e p . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Implementations of FICL external interface functions...
+**
+*******************************************************************/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include <stand.h>
+#endif
+#include "ficl.h"
+
+/*
+******************* FreeBSD P O R T B E G I N S H E R E ******************** Michael Smith
+*/
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ u_int64_t qx;
+
+ qx = (u_int64_t)x * (u_int64_t) y;
+
+ q.hi = (u_int32_t)( qx >> 32 );
+ q.lo = (u_int32_t)( qx & 0xFFFFFFFFL);
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ u_int64_t qx, qh;
+
+ qh = q.hi;
+ qx = (qh << 32) | q.lo;
+
+ result.quot = qx / y;
+ result.rem = qx % y;
+
+ return result;
+}
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ while(*msg != 0)
+ putchar(*(msg++));
+ if (fNewline)
+ putchar('\n');
+
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+void *ficlRealloc (void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
diff --git a/stand/ficl/riscv/sysdep.h b/stand/ficl/riscv/sysdep.h
new file mode 100644
index 0000000..3726b9e
--- /dev/null
+++ b/stand/ficl/riscv/sysdep.h
@@ -0,0 +1,411 @@
+/*******************************************************************
+ s y s d e p . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Ficl system dependent types and prototypes...
+**
+** Note: Ficl also depends on the use of "assert" when
+** FICL_ROBUST is enabled. This may require some consideration
+** in firmware systems since assert often
+** assumes stderr/stdout.
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** 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.
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please send
+** contact me by email at the address above.
+**
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+** $FreeBSD$
+*/
+
+#if !defined (__SYSDEP_H__)
+#define __SYSDEP_H__
+
+#include <sys/types.h>
+
+#include <stddef.h> /* size_t, NULL */
+#include <setjmp.h>
+#include <assert.h>
+
+#if !defined IGNORE /* Macro to silence unused param warnings */
+#define IGNORE(x) (void)(x)
+#endif
+
+/*
+** TRUE and FALSE for C boolean operations, and
+** portable 32 bit types for CELLs
+**
+*/
+#if !defined TRUE
+#define TRUE 1
+#endif
+#if !defined FALSE
+#define FALSE 0
+#endif
+
+
+/*
+** System dependent data type declarations...
+*/
+#if !defined INT32
+#define INT32 int
+#endif
+
+#if !defined UNS32
+#define UNS32 unsigned int
+#endif
+
+#if !defined UNS16
+#define UNS16 unsigned short
+#endif
+
+#if !defined UNS8
+#define UNS8 unsigned char
+#endif
+
+#if !defined NULL
+#define NULL ((void *)0)
+#endif
+
+/*
+** FICL_UNS and FICL_INT must have the same size as a void* on
+** the target system. A CELL is a union of void*, FICL_UNS, and
+** FICL_INT.
+** (11/2000: same for FICL_FLOAT)
+*/
+#if !defined FICL_INT
+#define FICL_INT long
+#endif
+
+#if !defined FICL_UNS
+#define FICL_UNS unsigned long
+#endif
+
+#if !defined FICL_FLOAT
+#define FICL_FLOAT float
+#endif
+
+/*
+** Ficl presently supports values of 32 and 64 for BITS_PER_CELL
+*/
+#if !defined BITS_PER_CELL
+#define BITS_PER_CELL 64
+#endif
+
+#if ((BITS_PER_CELL != 32) && (BITS_PER_CELL != 64))
+ Error!
+#endif
+
+typedef struct
+{
+ FICL_UNS hi;
+ FICL_UNS lo;
+} DPUNS;
+
+typedef struct
+{
+ FICL_UNS quot;
+ FICL_UNS rem;
+} UNSQR;
+
+typedef struct
+{
+ FICL_INT hi;
+ FICL_INT lo;
+} DPINT;
+
+typedef struct
+{
+ FICL_INT quot;
+ FICL_INT rem;
+} INTQR;
+
+
+/*
+** B U I L D C O N T R O L S
+*/
+
+#if !defined (FICL_MINIMAL)
+#define FICL_MINIMAL 0
+#endif
+#if (FICL_MINIMAL)
+#define FICL_WANT_SOFTWORDS 0
+#define FICL_WANT_FLOAT 0
+#define FICL_WANT_USER 0
+#define FICL_WANT_LOCALS 0
+#define FICL_WANT_DEBUGGER 0
+#define FICL_WANT_OOP 0
+#define FICL_PLATFORM_EXTEND 0
+#define FICL_MULTITHREAD 0
+#define FICL_ROBUST 0
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_PLATFORM_EXTEND
+** Includes words defined in ficlCompilePlatform
+*/
+#if !defined (FICL_PLATFORM_EXTEND)
+#define FICL_PLATFORM_EXTEND 1
+#endif
+
+/*
+** FICL_WANT_FLOAT
+** Includes a floating point stack for the VM, and words to do float operations.
+** Contributed by Guy Carver
+*/
+#if !defined (FICL_WANT_FLOAT)
+#define FICL_WANT_FLOAT 0
+#endif
+
+/*
+** FICL_WANT_DEBUGGER
+** Inludes a simple source level debugger
+*/
+#if !defined (FICL_WANT_DEBUGGER)
+#define FICL_WANT_DEBUGGER 1
+#endif
+
+/*
+** User variables: per-instance variables bound to the VM.
+** Kinda like thread-local storage. Could be implemented in a
+** VM private dictionary, but I've chosen the lower overhead
+** approach of an array of CELLs instead.
+*/
+#if !defined FICL_WANT_USER
+#define FICL_WANT_USER 1
+#endif
+
+#if !defined FICL_USER_CELLS
+#define FICL_USER_CELLS 16
+#endif
+
+/*
+** FICL_WANT_LOCALS controls the creation of the LOCALS wordset and
+** a private dictionary for local variable compilation.
+*/
+#if !defined FICL_WANT_LOCALS
+#define FICL_WANT_LOCALS 1
+#endif
+
+/* Max number of local variables per definition */
+#if !defined FICL_MAX_LOCALS
+#define FICL_MAX_LOCALS 16
+#endif
+
+/*
+** FICL_WANT_OOP
+** Inludes object oriented programming support (in softwords)
+** OOP support requires locals and user variables!
+*/
+#if !(FICL_WANT_LOCALS) || !(FICL_WANT_USER)
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 0
+#endif
+#endif
+
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 1
+#endif
+
+/*
+** FICL_WANT_SOFTWORDS
+** Controls inclusion of all softwords in softcore.c
+*/
+#if !defined (FICL_WANT_SOFTWORDS)
+#define FICL_WANT_SOFTWORDS 1
+#endif
+
+/*
+** FICL_MULTITHREAD enables dictionary mutual exclusion
+** wia the ficlLockDictionary system dependent function.
+** Note: this implementation is experimental and poorly
+** tested. Further, it's unnecessary unless you really
+** intend to have multiple SESSIONS (poor choice of name
+** on my part) - that is, threads that modify the dictionary
+** at the same time.
+*/
+#if !defined FICL_MULTITHREAD
+#define FICL_MULTITHREAD 0
+#endif
+
+/*
+** PORTABLE_LONGMULDIV causes ficlLongMul and ficlLongDiv to be
+** defined in C in sysdep.c. Use this if you cannot easily
+** generate an inline asm definition
+*/
+#if !defined (PORTABLE_LONGMULDIV)
+#define PORTABLE_LONGMULDIV 0
+#endif
+
+/*
+** INLINE_INNER_LOOP causes the inner interpreter to be inline code
+** instead of a function call. This is mainly because MS VC++ 5
+** chokes with an internal compiler error on the function version.
+** in release mode. Sheesh.
+*/
+#if !defined INLINE_INNER_LOOP
+#if defined _DEBUG
+#define INLINE_INNER_LOOP 0
+#else
+#define INLINE_INNER_LOOP 1
+#endif
+#endif
+
+/*
+** FICL_ROBUST enables bounds checking of stacks and the dictionary.
+** This will detect stack over and underflows and dictionary overflows.
+** Any exceptional condition will result in an assertion failure.
+** (As generated by the ANSI assert macro)
+** FICL_ROBUST == 1 --> stack checking in the outer interpreter
+** FICL_ROBUST == 2 also enables checking in many primitives
+*/
+
+#if !defined FICL_ROBUST
+#define FICL_ROBUST 2
+#endif
+
+/*
+** FICL_DEFAULT_STACK Specifies the default size (in CELLs) of
+** a new virtual machine's stacks, unless overridden at
+** create time.
+*/
+#if !defined FICL_DEFAULT_STACK
+#define FICL_DEFAULT_STACK 128
+#endif
+
+/*
+** FICL_DEFAULT_DICT specifies the number of CELLs to allocate
+** for the system dictionary by default. The value
+** can be overridden at startup time as well.
+** FICL_DEFAULT_ENV specifies the number of cells to allot
+** for the environment-query dictionary.
+*/
+#if !defined FICL_DEFAULT_DICT
+#define FICL_DEFAULT_DICT 12288
+#endif
+
+#if !defined FICL_DEFAULT_ENV
+#define FICL_DEFAULT_ENV 260
+#endif
+
+/*
+** FICL_DEFAULT_VOCS specifies the maximum number of wordlists in
+** the dictionary search order. See Forth DPANS sec 16.3.3
+** (file://dpans16.htm#16.3.3)
+*/
+#if !defined FICL_DEFAULT_VOCS
+#define FICL_DEFAULT_VOCS 16
+#endif
+
+/*
+** FICL_MAX_PARSE_STEPS controls the size of an array in the FICL_SYSTEM structure
+** that stores pointers to parser extension functions. I would never expect to have
+** more than 8 of these, so that's the default limit. Too many of these functions
+** will probably exact a nasty performance penalty.
+*/
+#if !defined FICL_MAX_PARSE_STEPS
+#define FICL_MAX_PARSE_STEPS 8
+#endif
+
+/*
+** FICL_EXTENDED_PREFIX enables a bunch of extra prefixes in prefix.c and prefix.fr (if
+** included as part of softcore.c)
+*/
+#if !defined FICL_EXTENDED_PREFIX
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_ALIGN is the power of two to which the dictionary
+** pointer address must be aligned. This value is usually
+** either 1 or 2, depending on the memory architecture
+** of the target system; 2 is safe on any 16 or 32 bit
+** machine. 3 would be appropriate for a 64 bit machine.
+*/
+#if !defined FICL_ALIGN
+#define FICL_ALIGN 3
+#define FICL_ALIGN_ADD ((1 << FICL_ALIGN) - 1)
+#endif
+
+/*
+** System dependent routines --
+** edit the implementations in sysdep.c to be compatible
+** with your runtime environment...
+** ficlTextOut sends a NULL terminated string to the
+** default output device - used for system error messages
+** ficlMalloc and ficlFree have the same semantics as malloc and free
+** in standard C
+** ficlLongMul multiplies two UNS32s and returns a 64 bit unsigned
+** product
+** ficlLongDiv divides an UNS64 by an UNS32 and returns UNS32 quotient
+** and remainder
+*/
+struct vm;
+void ficlTextOut(struct vm *pVM, char *msg, int fNewline);
+void *ficlMalloc (size_t size);
+void ficlFree (void *p);
+void *ficlRealloc(void *p, size_t size);
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** must be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** before timeout (optional - could also block forever)
+**
+** NOTE: this function must be implemented with lock counting
+** semantics: nested calls must behave properly.
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock);
+#else
+#define ficlLockDictionary(x) 0 /* ignore */
+#endif
+
+/*
+** 64 bit integer math support routines: multiply two UNS32s
+** to get a 64 bit product, & divide the product by an UNS32
+** to get an UNS32 quotient and remainder. Much easier in asm
+** on a 32 bit CPU than in C, which usually doesn't support
+** the double length result (but it should).
+*/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y);
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y);
+
+#endif /*__SYSDEP_H__*/
diff --git a/stand/ficl/search.c b/stand/ficl/search.c
new file mode 100644
index 0000000..d445cb3
--- /dev/null
+++ b/stand/ficl/search.c
@@ -0,0 +1,393 @@
+/*******************************************************************
+** s e a r c h . c
+** Forth Inspired Command Language
+** ANS Forth SEARCH and SEARCH-EXT word-set written in C
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 6 June 2000
+** $Id: search.c,v 1.9 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#include <string.h>
+#include "ficl.h"
+#include "math64.h"
+
+/**************************************************************************
+ d e f i n i t i o n s
+** SEARCH ( -- )
+** Make the compilation word list the same as the first word list in the
+** search order. Specifies that the names of subsequent definitions will
+** be placed in the compilation word list. Subsequent changes in the search
+** order will not affect the compilation word list.
+**************************************************************************/
+static void definitions(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+
+ assert(pDict);
+ if (pDict->nLists < 1)
+ {
+ vmThrowErr(pVM, "DEFINITIONS error - empty search order");
+ }
+
+ pDict->pCompile = pDict->pSearch[pDict->nLists-1];
+ return;
+}
+
+
+/**************************************************************************
+ f o r t h - w o r d l i s t
+** SEARCH ( -- wid )
+** Return wid, the identifier of the word list that includes all standard
+** words provided by the implementation. This word list is initially the
+** compilation word list and is part of the initial search order.
+**************************************************************************/
+static void forthWordlist(FICL_VM *pVM)
+{
+ FICL_HASH *pHash = vmGetDict(pVM)->pForthWords;
+ stackPushPtr(pVM->pStack, pHash);
+ return;
+}
+
+
+/**************************************************************************
+ g e t - c u r r e n t
+** SEARCH ( -- wid )
+** Return wid, the identifier of the compilation word list.
+**************************************************************************/
+static void getCurrent(FICL_VM *pVM)
+{
+ ficlLockDictionary(TRUE);
+ stackPushPtr(pVM->pStack, vmGetDict(pVM)->pCompile);
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ g e t - o r d e r
+** SEARCH ( -- widn ... wid1 n )
+** Returns the number of word lists n in the search order and the word list
+** identifiers widn ... wid1 identifying these word lists. wid1 identifies
+** the word list that is searched first, and widn the word list that is
+** searched last. The search order is unaffected.
+**************************************************************************/
+static void getOrder(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ int nLists = pDict->nLists;
+ int i;
+
+ ficlLockDictionary(TRUE);
+ for (i = 0; i < nLists; i++)
+ {
+ stackPushPtr(pVM->pStack, pDict->pSearch[i]);
+ }
+
+ stackPushUNS(pVM->pStack, nLists);
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ s e a r c h - w o r d l i s t
+** SEARCH ( c-addr u wid -- 0 | xt 1 | xt -1 )
+** Find the definition identified by the string c-addr u in the word list
+** identified by wid. If the definition is not found, return zero. If the
+** definition is found, return its execution token xt and one (1) if the
+** definition is immediate, minus-one (-1) otherwise.
+**************************************************************************/
+static void searchWordlist(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ UNS16 hashCode;
+ FICL_WORD *pFW;
+ FICL_HASH *pHash = stackPopPtr(pVM->pStack);
+
+ si.count = (FICL_COUNT)stackPopUNS(pVM->pStack);
+ si.cp = stackPopPtr(pVM->pStack);
+ hashCode = hashHashCode(si);
+
+ ficlLockDictionary(TRUE);
+ pFW = hashLookup(pHash, si, hashCode);
+ ficlLockDictionary(FALSE);
+
+ if (pFW)
+ {
+ stackPushPtr(pVM->pStack, pFW);
+ stackPushINT(pVM->pStack, (wordIsImmediate(pFW) ? 1 : -1));
+ }
+ else
+ {
+ stackPushUNS(pVM->pStack, 0);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ s e t - c u r r e n t
+** SEARCH ( wid -- )
+** Set the compilation word list to the word list identified by wid.
+**************************************************************************/
+static void setCurrent(FICL_VM *pVM)
+{
+ FICL_HASH *pHash = stackPopPtr(pVM->pStack);
+ FICL_DICT *pDict = vmGetDict(pVM);
+ ficlLockDictionary(TRUE);
+ pDict->pCompile = pHash;
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ s e t - o r d e r
+** SEARCH ( widn ... wid1 n -- )
+** Set the search order to the word lists identified by widn ... wid1.
+** Subsequently, word list wid1 will be searched first, and word list
+** widn searched last. If n is zero, empty the search order. If n is minus
+** one, set the search order to the implementation-defined minimum
+** search order. The minimum search order shall include the words
+** FORTH-WORDLIST and SET-ORDER. A system shall allow n to
+** be at least eight.
+**************************************************************************/
+static void setOrder(FICL_VM *pVM)
+{
+ int i;
+ int nLists = stackPopINT(pVM->pStack);
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ if (nLists > FICL_DEFAULT_VOCS)
+ {
+ vmThrowErr(pVM, "set-order error: list would be too large");
+ }
+
+ ficlLockDictionary(TRUE);
+
+ if (nLists >= 0)
+ {
+ dp->nLists = nLists;
+ for (i = nLists-1; i >= 0; --i)
+ {
+ dp->pSearch[i] = stackPopPtr(pVM->pStack);
+ }
+ }
+ else
+ {
+ dictResetSearchOrder(dp);
+ }
+
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ f i c l - w o r d l i s t
+** SEARCH ( -- wid )
+** Create a new empty word list, returning its word list identifier wid.
+** The new word list may be returned from a pool of preallocated word
+** lists or may be dynamically allocated in data space. A system shall
+** allow the creation of at least 8 new word lists in addition to any
+** provided as part of the system.
+** Notes:
+** 1. ficl creates a new single-list hash in the dictionary and returns
+** its address.
+** 2. ficl-wordlist takes an arg off the stack indicating the number of
+** hash entries in the wordlist. Ficl 2.02 and later define WORDLIST as
+** : wordlist 1 ficl-wordlist ;
+**************************************************************************/
+static void ficlWordlist(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_HASH *pHash;
+ FICL_UNS nBuckets;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ nBuckets = stackPopUNS(pVM->pStack);
+ pHash = dictCreateWordlist(dp, nBuckets);
+ stackPushPtr(pVM->pStack, pHash);
+ return;
+}
+
+
+/**************************************************************************
+ S E A R C H >
+** ficl ( -- wid )
+** Pop wid off the search order. Error if the search order is empty
+**************************************************************************/
+static void searchPop(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ int nLists;
+
+ ficlLockDictionary(TRUE);
+ nLists = dp->nLists;
+ if (nLists == 0)
+ {
+ vmThrowErr(pVM, "search> error: empty search order");
+ }
+ stackPushPtr(pVM->pStack, dp->pSearch[--dp->nLists]);
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ > S E A R C H
+** ficl ( wid -- )
+** Push wid onto the search order. Error if the search order is full.
+**************************************************************************/
+static void searchPush(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ ficlLockDictionary(TRUE);
+ if (dp->nLists > FICL_DEFAULT_VOCS)
+ {
+ vmThrowErr(pVM, ">search error: search order overflow");
+ }
+ dp->pSearch[dp->nLists++] = stackPopPtr(pVM->pStack);
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ W I D - G E T - N A M E
+** ficl ( wid -- c-addr u )
+** Get wid's (optional) name and push onto stack as a counted string
+**************************************************************************/
+static void widGetName(FICL_VM *pVM)
+{
+ FICL_HASH *pHash = vmPop(pVM).p;
+ char *cp = pHash->name;
+ FICL_INT len = 0;
+
+ if (cp)
+ len = strlen(cp);
+
+ vmPush(pVM, LVALUEtoCELL(cp));
+ vmPush(pVM, LVALUEtoCELL(len));
+ return;
+}
+
+/**************************************************************************
+ W I D - S E T - N A M E
+** ficl ( wid c-addr -- )
+** Set wid's name pointer to the \0 terminated string address supplied
+**************************************************************************/
+static void widSetName(FICL_VM *pVM)
+{
+ char *cp = (char *)vmPop(pVM).p;
+ FICL_HASH *pHash = vmPop(pVM).p;
+ pHash->name = cp;
+ return;
+}
+
+
+/**************************************************************************
+ setParentWid
+** FICL
+** setparentwid ( parent-wid wid -- )
+** Set WID's link field to the parent-wid. search-wordlist will
+** iterate through all the links when finding words in the child wid.
+**************************************************************************/
+static void setParentWid(FICL_VM *pVM)
+{
+ FICL_HASH *parent, *child;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ child = (FICL_HASH *)stackPopPtr(pVM->pStack);
+ parent = (FICL_HASH *)stackPopPtr(pVM->pStack);
+
+ child->link = parent;
+ return;
+}
+
+
+/**************************************************************************
+ f i c l C o m p i l e S e a r c h
+** Builds the primitive wordset and the environment-query namespace.
+**************************************************************************/
+
+void ficlCompileSearch(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+ /*
+ ** optional SEARCH-ORDER word set
+ */
+ dictAppendWord(dp, ">search", searchPush, FW_DEFAULT);
+ dictAppendWord(dp, "search>", searchPop, FW_DEFAULT);
+ dictAppendWord(dp, "definitions",
+ definitions, FW_DEFAULT);
+ dictAppendWord(dp, "forth-wordlist",
+ forthWordlist, FW_DEFAULT);
+ dictAppendWord(dp, "get-current",
+ getCurrent, FW_DEFAULT);
+ dictAppendWord(dp, "get-order", getOrder, FW_DEFAULT);
+ dictAppendWord(dp, "search-wordlist",
+ searchWordlist, FW_DEFAULT);
+ dictAppendWord(dp, "set-current",
+ setCurrent, FW_DEFAULT);
+ dictAppendWord(dp, "set-order", setOrder, FW_DEFAULT);
+ dictAppendWord(dp, "ficl-wordlist",
+ ficlWordlist, FW_DEFAULT);
+
+ /*
+ ** Set SEARCH environment query values
+ */
+ ficlSetEnv(pSys, "search-order", FICL_TRUE);
+ ficlSetEnv(pSys, "search-order-ext", FICL_TRUE);
+ ficlSetEnv(pSys, "wordlists", FICL_DEFAULT_VOCS);
+
+ dictAppendWord(dp, "wid-get-name", widGetName, FW_DEFAULT);
+ dictAppendWord(dp, "wid-set-name", widSetName, FW_DEFAULT);
+ dictAppendWord(dp, "wid-set-super",
+ setParentWid, FW_DEFAULT);
+ return;
+}
+
diff --git a/stand/ficl/softwords/classes.fr b/stand/ficl/softwords/classes.fr
new file mode 100644
index 0000000..b56da37
--- /dev/null
+++ b/stand/ficl/softwords/classes.fr
@@ -0,0 +1,173 @@
+\ #if (FICL_WANT_OOP)
+\ ** ficl/softwords/classes.fr
+\ ** F I C L 2 . 0 C L A S S E S
+\ john sadler 1 sep 98
+\ Needs oop.fr
+\
+\ $FreeBSD$
+
+also oop definitions
+
+\ REF subclass holds a pointer to an object. It's
+\ mainly for aggregation to help in making data structures.
+\
+object subclass c-ref
+ cell: .class
+ cell: .instance
+
+ : get ( inst class -- refinst refclass )
+ drop 2@ ;
+ : set ( refinst refclass inst class -- )
+ drop 2! ;
+end-class
+
+object subclass c-byte
+ char: .payload
+
+ : get drop c@ ;
+ : set drop c! ;
+end-class
+
+object subclass c-2byte
+ 2 chars: .payload
+
+ : get drop w@ ;
+ : set drop w! ;
+end-class
+
+object subclass c-4byte
+ 4 chars: .payload
+
+ : get drop q@ ;
+ : set drop q! ;
+end-class
+
+
+object subclass c-cell
+ cell: .payload
+
+ : get drop @ ;
+ : set drop ! ;
+end-class
+
+
+\ ** C - P T R
+\ Base class for pointers to scalars (not objects).
+\ Note: use c-ref to make references to objects. C-ptr
+\ subclasses refer to untyped quantities of various sizes.
+
+\ Derived classes must specify the size of the thing
+\ they point to, and supply get and set methods.
+
+\ All derived classes must define the @size method:
+\ @size ( inst class -- addr-units )
+\ Returns the size in address units of the thing the pointer
+\ refers to.
+object subclass c-ptr
+ c-cell obj: .addr
+
+ \ get the value of the pointer
+ : get-ptr ( inst class -- addr )
+ c-ptr => .addr
+ c-cell => get
+ ;
+
+ \ set the pointer to address supplied
+ : set-ptr ( addr inst class -- )
+ c-ptr => .addr
+ c-cell => set
+ ;
+
+ \ force the pointer to be null
+ : clr-ptr
+ 0 -rot c-ptr => .addr c-cell => set
+ ;
+
+ \ return flag indicating null-ness
+ : ?null ( inst class -- flag )
+ c-ptr => get-ptr 0=
+ ;
+
+ \ increment the pointer in place
+ : inc-ptr ( inst class -- )
+ 2dup 2dup ( i c i c i c )
+ c-ptr => get-ptr -rot ( i c addr i c )
+ --> @size + -rot ( addr' i c )
+ c-ptr => set-ptr
+ ;
+
+ \ decrement the pointer in place
+ : dec-ptr ( inst class -- )
+ 2dup 2dup ( i c i c i c )
+ c-ptr => get-ptr -rot ( i c addr i c )
+ --> @size - -rot ( addr' i c )
+ c-ptr => set-ptr
+ ;
+
+ \ index the pointer in place
+ : index-ptr { index 2:this -- }
+ this --> get-ptr ( addr )
+ this --> @size index * + ( addr' )
+ this --> set-ptr
+ ;
+
+end-class
+
+
+\ ** C - C E L L P T R
+\ Models a pointer to cell (a 32 or 64 bit scalar).
+c-ptr subclass c-cellPtr
+ : @size 2drop 1 cells ;
+ \ fetch and store through the pointer
+ : get ( inst class -- cell )
+ c-ptr => get-ptr @
+ ;
+ : set ( value inst class -- )
+ c-ptr => get-ptr !
+ ;
+end-class
+
+
+\ ** C - 4 B Y T E P T R
+\ Models a pointer to a quadbyte scalar
+c-ptr subclass c-4bytePtr
+ : @size 2drop 4 ;
+ \ fetch and store through the pointer
+ : get ( inst class -- value )
+ c-ptr => get-ptr q@
+ ;
+ : set ( value inst class -- )
+ c-ptr => get-ptr q!
+ ;
+ end-class
+
+\ ** C - 2 B Y T E P T R
+\ Models a pointer to a 16 bit scalar
+c-ptr subclass c-2bytePtr
+ : @size 2drop 2 ;
+ \ fetch and store through the pointer
+ : get ( inst class -- value )
+ c-ptr => get-ptr w@
+ ;
+ : set ( value inst class -- )
+ c-ptr => get-ptr w!
+ ;
+end-class
+
+
+\ ** C - B Y T E P T R
+\ Models a pointer to an 8 bit scalar
+c-ptr subclass c-bytePtr
+ : @size 2drop 1 ;
+ \ fetch and store through the pointer
+ : get ( inst class -- value )
+ c-ptr => get-ptr c@
+ ;
+ : set ( value inst class -- )
+ c-ptr => get-ptr c!
+ ;
+end-class
+
+
+previous definitions
+\ #endif
diff --git a/stand/ficl/softwords/ficlclass.fr b/stand/ficl/softwords/ficlclass.fr
new file mode 100644
index 0000000..6d75efb
--- /dev/null
+++ b/stand/ficl/softwords/ficlclass.fr
@@ -0,0 +1,86 @@
+\ #if (FICL_WANT_OOP)
+\ ** ficl/softwords/ficlclass.fr
+\ Classes to model ficl data structures in objects
+\ This is a demo!
+\ John Sadler 14 Sep 1998
+\
+\ ** C - W O R D
+\ Models a FICL_WORD
+\
+\ $FreeBSD$
+
+object subclass c-word
+ c-word ref: .link
+ c-2byte obj: .hashcode
+ c-byte obj: .flags
+ c-byte obj: .nName
+ c-bytePtr obj: .pName
+ c-cellPtr obj: .pCode
+ c-4byte obj: .param0
+
+ \ Push word's name...
+ : get-name ( inst class -- c-addr u )
+ 2dup
+ my=[ .pName get-ptr ] -rot
+ my=[ .nName get ]
+ ;
+
+ : next ( inst class -- link-inst class )
+ my=> .link ;
+
+ : ?
+ ." c-word: "
+ 2dup --> get-name type cr
+ ;
+
+end-class
+
+\ ** C - W O R D L I S T
+\ Models a FICL_HASH
+\ Example of use:
+\ get-current c-wordlist --> ref current
+\ current --> ?
+\ current --> .hash --> ?
+\ current --> .hash --> next --> ?
+
+object subclass c-wordlist
+ c-wordlist ref: .parent
+ c-ptr obj: .name
+ c-cell obj: .size
+ c-word ref: .hash ( first entry in hash table )
+
+ : ?
+ --> get-name ." ficl wordlist " type cr ;
+ : push drop >search ;
+ : pop 2drop previous ;
+ : set-current drop set-current ;
+ : get-name drop wid-get-name ;
+ : words { 2:this -- }
+ this my=[ .size get ] 0 do
+ i this my=[ .hash index ] ( 2list-head )
+ begin
+ 2dup --> get-name type space
+ --> next over
+ 0= until 2drop cr
+ loop
+ ;
+end-class
+
+\ : named-wid wordlist postpone c-wordlist metaclass => ref ;
+
+
+\ ** C - F I C L S T A C K
+object subclass c-ficlstack
+ c-4byte obj: .nCells
+ c-cellPtr obj: .link
+ c-cellPtr obj: .sp
+ c-4byte obj: .stackBase
+
+ : init 2drop ;
+ : ? 2drop
+ ." ficl stack " cr ;
+ : top
+ --> .sp --> .addr --> prev --> get ;
+end-class
+
+\ #endif
diff --git a/stand/ficl/softwords/ficllocal.fr b/stand/ficl/softwords/ficllocal.fr
new file mode 100644
index 0000000..c916089
--- /dev/null
+++ b/stand/ficl/softwords/ficllocal.fr
@@ -0,0 +1,49 @@
+\ ** ficl/softwords/ficllocal.fr
+\ ** stack comment style local syntax...
+\ {{ a b c -- d e }}
+\ variables before the "--" are initialized in reverse order
+\ from the stack. Those after the "--" are zero initialized
+\ Uses locals...
+\ locstate: 0 = looking for -- or }}
+\ 1 = found --
+\
+\ $FreeBSD$
+
+hide
+0 constant zero
+
+: ?-- s" --" compare 0= ;
+: ?}} s" }}" compare 0= ;
+
+set-current
+
+: {{
+ 0 dup locals| nLocs locstate |
+ begin
+ parse-word
+ ?dup 0= abort" Error: out of text without seeing }}"
+ 2dup 2dup ?-- -rot ?}} or 0=
+ while
+ nLocs 1+ to nLocs
+ repeat
+
+ ?-- if 1 to locstate endif
+
+ nLocs 0 do
+ (local)
+ loop
+
+ locstate 1 = if
+ begin
+ parse-word
+ 2dup ?}} 0=
+ while
+ postpone zero (local)
+ repeat
+ 2drop
+ endif
+
+ 0 0 (local)
+; immediate compile-only
+
+previous
diff --git a/stand/ficl/softwords/fileaccess.fr b/stand/ficl/softwords/fileaccess.fr
new file mode 100644
index 0000000..7297df6
--- /dev/null
+++ b/stand/ficl/softwords/fileaccess.fr
@@ -0,0 +1,25 @@
+\ #if FICL_WANT_FILE
+\ **
+\ ** File Access words for ficl
+\ ** submitted by Larry Hastings, larry@hastings.org
+\ **
+\
+\ $FreeBSD$
+
+: r/o 1 ;
+: r/w 3 ;
+: w/o 2 ;
+: bin 8 or ;
+
+: included
+ r/o bin open-file 0= if
+ locals| f | end-locals
+ f include-file
+ else
+ drop
+ endif
+ ;
+
+: include parse-word included ;
+
+\ #endif
diff --git a/stand/ficl/softwords/forml.fr b/stand/ficl/softwords/forml.fr
new file mode 100644
index 0000000..1144ef5
--- /dev/null
+++ b/stand/ficl/softwords/forml.fr
@@ -0,0 +1,75 @@
+\ examples from FORML conference paper Nov 98
+\ sadler
+\
+\ $FreeBSD$
+
+.( loading FORML examples ) cr
+object --> sub c-example
+ cell: .cell0
+ c-4byte obj: .nCells
+ 4 c-4byte array: .quad
+ c-byte obj: .length
+ 79 chars: .name
+
+ : init ( inst class -- )
+ 2dup object => init
+ s" aardvark" 2swap --> set-name
+ ;
+
+ : get-name ( inst class -- c-addr u )
+ 2dup
+ --> .name -rot ( c-addr inst class )
+ --> .length --> get
+ ;
+
+ : set-name { c-addr u 2:this -- }
+ u this --> .length --> set
+ c-addr this --> .name u move
+ ;
+
+ : ? ( inst class ) c-example => get-name type cr ;
+end-class
+
+
+: test ." this is a test" cr ;
+' test
+c-word --> ref testref
+
+\ add a method to c-word...
+c-word --> get-wid ficl-set-current
+\ list dictionary thread
+: list ( inst class )
+ begin
+ 2dup --> get-name type cr
+ --> next over
+ 0= until
+ 2drop
+;
+set-current
+
+object subclass c-led
+ c-byte obj: .state
+
+ : on { led# 2:this -- }
+ this --> .state --> get
+ 1 led# lshift or dup !oreg
+ this --> .state --> set
+ ;
+
+ : off { led# 2:this -- }
+ this --> .state --> get
+ 1 led# lshift invert and dup !oreg
+ this --> .state --> set
+ ;
+
+end-class
+
+
+object subclass c-switch
+
+ : ?on { bit# 2:this -- flag }
+
+ 1 bit# lshift
+ ;
+end-class
+
diff --git a/stand/ficl/softwords/freebsd.fr b/stand/ficl/softwords/freebsd.fr
new file mode 100644
index 0000000..96205c0
--- /dev/null
+++ b/stand/ficl/softwords/freebsd.fr
@@ -0,0 +1,36 @@
+\ ** Copyright (c) 1998 Daniel C. Sobral <dcs@freebsd.org>
+\ ** All rights reserved.
+\ **
+\ ** Redistribution and use in source and binary forms, with or without
+\ ** modification, are permitted provided that the following conditions
+\ ** are met:
+\ ** 1. Redistributions of source code must retain the above copyright
+\ ** notice, this list of conditions and the following disclaimer.
+\ ** 2. Redistributions in binary form must reproduce the above copyright
+\ ** notice, this list of conditions and the following disclaimer in the
+\ ** documentation and/or other materials provided with the distribution.
+\ **
+\ ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ ** SUCH DAMAGE.
+\ **
+\ ** $FreeBSD$
+
+\ Words for use in scripts:
+\ % ignore errors here
+\ $ echo this line
+
+: tib> source >in @ tuck over >in ! - >r + r> ;
+: % tib> ['] evaluate catch drop ;
+: $ tib> 2dup type cr evaluate ;
+
+\ ** E N D F R E E B S D . F R
+
diff --git a/stand/ficl/softwords/ifbrack.fr b/stand/ficl/softwords/ifbrack.fr
new file mode 100644
index 0000000..a8c6062
--- /dev/null
+++ b/stand/ficl/softwords/ifbrack.fr
@@ -0,0 +1,50 @@
+\ ** ficl/softwords/ifbrack.fr
+\ ** ANS conditional compile directives [if] [else] [then]
+\ ** Requires ficl 2.0 or greater...
+\
+\ $FreeBSD$
+
+hide
+
+: ?[if] ( c-addr u -- c-addr u flag )
+ 2dup s" [if]" compare-insensitive 0=
+;
+
+: ?[else] ( c-addr u -- c-addr u flag )
+ 2dup s" [else]" compare-insensitive 0=
+;
+
+: ?[then] ( c-addr u -- c-addr u flag )
+ 2dup s" [then]" compare-insensitive 0= >r
+ 2dup s" [endif]" compare-insensitive 0= r>
+ or
+;
+
+set-current
+
+: [else] ( -- )
+ 1 \ ( level )
+ begin
+ begin
+ parse-word dup while \ ( level addr len )
+ ?[if] if \ ( level addr len )
+ 2drop 1+ \ ( level )
+ else \ ( level addr len )
+ ?[else] if \ ( level addr len )
+ 2drop 1- dup if 1+ endif
+ else
+ ?[then] if 2drop 1- else 2drop endif
+ endif
+ endif ?dup 0= if exit endif \ level
+ repeat 2drop \ level
+ refill 0= until \ level
+ drop
+; immediate
+
+: [if] ( flag -- )
+0= if postpone [else] then ; immediate
+
+: [then] ( -- ) ; immediate
+: [endif] ( -- ) ; immediate
+
+previous
diff --git a/stand/ficl/softwords/jhlocal.fr b/stand/ficl/softwords/jhlocal.fr
new file mode 100644
index 0000000..12ccb9f
--- /dev/null
+++ b/stand/ficl/softwords/jhlocal.fr
@@ -0,0 +1,105 @@
+\ #if FICL_WANT_LOCALS
+\ ** ficl/softwords/jhlocal.fr
+\ ** stack comment style local syntax...
+\ { a b c | cleared -- d e }
+\ variables before the "|" are initialized in reverse order
+\ from the stack. Those after the "|" are zero initialized.
+\ Anything between "--" and "}" is treated as comment
+\ Uses locals...
+\ locstate: 0 = looking for | or -- or }}
+\ 1 = found |
+\ 2 = found --
+\ 3 = found }
+\ 4 = end of line
+\
+\ revised 2 June 2000 - { | a -- } now works correctly
+\
+\ $FreeBSD$
+
+hide
+
+0 constant zero
+
+
+: ?-- ( c-addr u -- c-addr u flag )
+ 2dup s" --" compare 0= ;
+: ?} ( c-addr u -- c-addr u flag )
+ 2dup s" }" compare 0= ;
+: ?| ( c-addr u -- c-addr u flag )
+ 2dup s" |" compare 0= ;
+
+\ examine name - if it's a 2local (starts with "2:"),
+\ nibble the prefix (the "2:") off the name and push true.
+\ Otherwise push false
+\ Problem if the local is named "2:" - we fall off the end...
+: ?2loc ( c-addr u -- c-addr u flag )
+ over dup c@ [char] 2 =
+ swap 1+ c@ [char] : = and
+ if
+ 2 - swap char+ char+ swap \ dcs/jws: nibble the '2:'
+ true
+ else
+ false
+ endif
+;
+
+: ?delim ( c-addr u -- state | c-addr u 0 )
+ ?| if 2drop 1 exit endif
+ ?-- if 2drop 2 exit endif
+ ?} if 2drop 3 exit endif
+ dup 0=
+ if 2drop 4 exit endif
+ 0
+;
+
+set-current
+
+: {
+ 0 dup locals| locstate |
+
+ \ stack locals until we hit a delimiter
+ begin
+ parse-word \ ( nLocals c-addr u )
+ ?delim dup to locstate
+ 0= while
+ rot 1+ \ ( c-addr u ... c-addr u nLocals )
+ repeat
+
+ \ now unstack the locals
+ 0 ?do
+ ?2loc if (2local) else (local) endif
+ loop \ ( )
+
+ \ zero locals until -- or }
+ locstate 1 = if
+ begin
+ parse-word
+ ?delim dup to locstate
+ 0= while
+ ?2loc if
+ postpone zero postpone zero (2local)
+ else
+ postpone zero (local)
+ endif
+ repeat
+ endif
+
+ 0 0 (local)
+
+ \ toss words until }
+ \ (explicitly allow | and -- in the comment)
+ locstate 2 = if
+ begin
+ parse-word
+ ?delim dup to locstate
+ 3 < while
+ locstate 0= if 2drop endif
+ repeat
+ endif
+
+ locstate 3 <> abort" syntax error in { } local line"
+; immediate compile-only
+
+previous
+\ #endif
+
diff --git a/stand/ficl/softwords/marker.fr b/stand/ficl/softwords/marker.fr
new file mode 100644
index 0000000..ee3c9bd
--- /dev/null
+++ b/stand/ficl/softwords/marker.fr
@@ -0,0 +1,27 @@
+\ ** ficl/softwords/marker.fr
+\ ** Ficl implementation of CORE EXT MARKER
+\ John Sadler, 4 Oct 98
+\ Requires ficl 2.02 FORGET-WID !!
+\
+\ $FreeBSD$
+
+: marker ( "name" -- )
+ create
+ get-current ,
+ get-order dup ,
+ 0 ?do , loop
+ does>
+ 0 set-order \ clear search order
+ dup body> >name drop
+ here - allot \ reset HERE to my xt-addr
+ dup @ ( pfa current-wid )
+ dup set-current forget-wid ( pfa )
+ cell+ dup @ swap ( count count-addr )
+ over cells + swap ( last-wid-addr count )
+ 0 ?do
+ dup @ dup ( wid-addr wid wid )
+ >search forget-wid ( wid-addr )
+ cell-
+ loop
+ drop
+;
diff --git a/stand/ficl/softwords/oo.fr b/stand/ficl/softwords/oo.fr
new file mode 100644
index 0000000..b1c8e21
--- /dev/null
+++ b/stand/ficl/softwords/oo.fr
@@ -0,0 +1,694 @@
+\ #if FICL_WANT_OOP
+\ ** ficl/softwords/oo.fr
+\ ** F I C L O - O E X T E N S I O N S
+\ ** john sadler aug 1998
+\
+\ $FreeBSD$
+
+17 ficl-vocabulary oop
+also oop definitions
+
+\ Design goals:
+\ 0. Traditional OOP: late binding by default for safety.
+\ Early binding if you ask for it.
+\ 1. Single inheritance
+\ 2. Object aggregation (has-a relationship)
+\ 3. Support objects in the dictionary and as proxies for
+\ existing structures (by reference):
+\ *** A ficl object can wrap a C struct ***
+\ 4. Separate name-spaces for methods - methods are
+\ only visible in the context of a class / object
+\ 5. Methods can be overridden, and subclasses can add methods.
+\ No limit on number of methods.
+
+\ General info:
+\ Classes are objects, too: all classes are instances of METACLASS
+\ All classes are derived (by convention) from OBJECT. This
+\ base class provides a default initializer and superclass
+\ access method
+
+\ A ficl object binds instance storage (payload) to a class.
+\ object ( -- instance class )
+\ All objects push their payload address and class address when
+\ executed.
+
+\ A ficl class consists of a parent class pointer, a wordlist
+\ ID for the methods of the class, and a size for the payload
+\ of objects created by the class. A class is an object.
+\ The NEW method creates and initializes an instance of a class.
+\ Classes have this footprint:
+\ cell 0: parent class address
+\ cell 1: wordlist ID
+\ cell 2: size of instance's payload
+
+\ Methods expect an object couple ( instance class )
+\ on the stack. This is by convention - ficl has no way to
+\ police your code to make sure this is always done, but it
+\ happens naturally if you use the facilities presented here.
+\
+\ Overridden methods must maintain the same stack signature as
+\ their predecessors. Ficl has no way of enforcing this, either.
+\
+\ Revised Apr 2001 - Added Guy Carver's vtable extensions. Class now
+\ has an extra field for the vtable method count. Hasvtable declares
+\ refs to vtable classes
+\
+\ Revised Nov 2001 - metaclass debug method now finds only metaclass methods
+\
+\ Planned: Ficl vtable support
+\ Each class has a vtable size parameter
+\ END-CLASS allocates and clears the vtable - then it walks class's method
+\ list and inserts all new methods into table. For each method, if the table
+\ slot is already nonzero, do nothing (overridden method). Otherwise fill
+\ vtable slot. Now do same check for parent class vtable, filling only
+\ empty slots in the new vtable.
+\ Methods are now structured as follows:
+\ - header
+\ - vtable index
+\ - xt
+\ :noname definition for code
+\
+\ : is redefined to check for override, fill in vtable index, increment method
+\ count if not an override, create header and fill in index. Allot code pointer
+\ and run :noname
+\ ; is overridden to fill in xt returned by :noname
+\ --> compiles code to fetch vtable address, offset by index, and execute
+\ => looks up xt in the vtable and compiles it directly
+
+
+
+user current-class
+0 current-class !
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** L A T E B I N D I N G
+\ Compile the method name, and code to find and
+\ execute it at run-time...
+\
+
+\ p a r s e - m e t h o d
+\ compiles a method name so that it pushes
+\ the string base address and count at run-time.
+
+: parse-method \ name run: ( -- c-addr u )
+ parse-word
+ postpone sliteral
+; compile-only
+
+
+
+: (lookup-method) { class 2:name -- class 0 | class xt 1 | class xt -1 }
+ class name class cell+ @ ( class c-addr u wid )
+ search-wordlist
+;
+
+\ l o o k u p - m e t h o d
+\ takes a counted string method name from the stack (as compiled
+\ by parse-method) and attempts to look this method up in the method list of
+\ the class that's on the stack. If successful, it leaves the class on the stack
+\ and pushes the xt of the method. If not, it aborts with an error message.
+
+: lookup-method { class 2:name -- class xt }
+ class name (lookup-method) ( 0 | xt 1 | xt -1 )
+ 0= if
+ name type ." not found in "
+ class body> >name type
+ cr abort
+ endif
+;
+
+: find-method-xt \ name ( class -- class xt )
+ parse-word lookup-method
+;
+
+: catch-method ( instance class c-addr u -- <method-signature> exc-flag )
+ lookup-method catch
+;
+
+: exec-method ( instance class c-addr u -- <method-signature> )
+ lookup-method execute
+;
+
+\ Method lookup operator takes a class-addr and instance-addr
+\ and executes the method from the class's wordlist if
+\ interpreting. If compiling, bind late.
+\
+: --> ( instance class -- ??? )
+ state @ 0= if
+ find-method-xt execute
+ else
+ parse-method postpone exec-method
+ endif
+; immediate
+
+\ Method lookup with CATCH in case of exceptions
+: c-> ( instance class -- ?? exc-flag )
+ state @ 0= if
+ find-method-xt catch
+ else
+ parse-method postpone catch-method
+ endif
+; immediate
+
+\ METHOD makes global words that do method invocations by late binding
+\ in case you prefer this style (no --> in your code)
+\ Example: everything has next and prev for array access, so...
+\ method next
+\ method prev
+\ my-instance next ( does whatever next does to my-instance by late binding )
+
+: method create does> body> >name lookup-method execute ;
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** E A R L Y B I N D I N G
+\ Early binding operator compiles code to execute a method
+\ given its class at compile time. Classes are immediate,
+\ so they leave their cell-pair on the stack when compiling.
+\ Example:
+\ : get-wid metaclass => .wid @ ;
+\ Usage
+\ my-class get-wid ( -- wid-of-my-class )
+\
+1 ficl-named-wordlist instance-vars
+instance-vars dup >search ficl-set-current
+
+: => \ c:( class meta -- ) run: ( -- ??? ) invokes compiled method
+ drop find-method-xt compile, drop
+; immediate compile-only
+
+: my=> \ c:( -- ) run: ( -- ??? ) late bind compiled method of current-class
+ current-class @ dup postpone =>
+; immediate compile-only
+
+\ Problem: my=[ assumes that each method except the last is am obj: member
+\ which contains its class as the first field of its parameter area. The code
+\ detects non-obect members and assumes the class does not change in this case.
+\ This handles methods like index, prev, and next correctly, but does not deal
+\ correctly with CLASS.
+: my=[ \ same as my=> , but binds a chain of methods
+ current-class @
+ begin
+ parse-word 2dup ( class c-addr u c-addr u )
+ s" ]" compare while ( class c-addr u )
+ lookup-method ( class xt )
+ dup compile, ( class xt )
+ dup ?object if \ If object member, get new class. Otherwise assume same class
+ nip >body cell+ @ ( new-class )
+ else
+ drop ( class )
+ endif
+ repeat 2drop drop
+; immediate compile-only
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** I N S T A N C E V A R I A B L E S
+\ Instance variables (IV) are represented by words in the class's
+\ private wordlist. Each IV word contains the offset
+\ of the IV it represents, and runs code to add that offset
+\ to the base address of an instance when executed.
+\ The metaclass SUB method, defined below, leaves the address
+\ of the new class's offset field and its initial size on the
+\ stack for these words to update. When a class definition is
+\ complete, END-CLASS saves the final size in the class's size
+\ field, and restores the search order and compile wordlist to
+\ prior state. Note that these words are hidden in their own
+\ wordlist to prevent accidental use outside a SUB END-CLASS pair.
+\
+: do-instance-var
+ does> ( instance class addr[offset] -- addr[field] )
+ nip @ +
+;
+
+: addr-units: ( offset size "name" -- offset' )
+ create over , +
+ do-instance-var
+;
+
+: chars: \ ( offset nCells "name" -- offset' ) Create n char member.
+ chars addr-units: ;
+
+: char: \ ( offset nCells "name" -- offset' ) Create 1 char member.
+ 1 chars: ;
+
+: cells: ( offset nCells "name" -- offset' )
+ cells >r aligned r> addr-units:
+;
+
+: cell: ( offset nCells "name" -- offset' )
+ 1 cells: ;
+
+\ Aggregate an object into the class...
+\ Needs the class of the instance to create
+\ Example: object obj: m_obj
+\
+: do-aggregate
+ objectify
+ does> ( instance class pfa -- a-instance a-class )
+ 2@ ( inst class a-class a-offset )
+ 2swap drop ( a-class a-offset inst )
+ + swap ( a-inst a-class )
+;
+
+: obj: { offset class meta -- offset' } \ "name"
+ create offset , class ,
+ class meta --> get-size offset +
+ do-aggregate
+;
+
+\ Aggregate an array of objects into a class
+\ Usage example:
+\ 3 my-class array: my-array
+\ Makes an instance variable array of 3 instances of my-class
+\ named my-array.
+\
+: array: ( offset n class meta "name" -- offset' )
+ locals| meta class nobjs offset |
+ create offset , class ,
+ class meta --> get-size nobjs * offset +
+ do-aggregate
+;
+
+\ Aggregate a pointer to an object: REF is a member variable
+\ whose class is set at compile time. This is useful for wrapping
+\ data structures in C, where there is only a pointer and the type
+\ it refers to is known. If you want polymorphism, see c_ref
+\ in classes.fr. REF is only useful for pre-initialized structures,
+\ since there's no supported way to set one.
+: ref: ( offset class meta "name" -- offset' )
+ locals| meta class offset |
+ create offset , class ,
+ offset cell+
+ does> ( inst class pfa -- ptr-inst ptr-class )
+ 2@ ( inst class ptr-class ptr-offset )
+ 2swap drop + @ swap
+;
+
+\ #if FICL_WANT_VCALL
+\ vcall extensions contributed by Guy Carver
+: vcall: ( paramcnt "name" -- )
+ current-class @ 8 + dup @ dup 1+ rot ! \ Kludge fix to get to .vtCount before it's defined.
+ create , , \ ( paramcnt index -- )
+ does> \ ( inst class pfa -- ptr-inst ptr-class )
+ nip 2@ vcall \ ( params offset inst class offset -- )
+;
+
+: vcallr: 0x80000000 or vcall: ; \ Call with return address desired.
+
+\ #if FICL_WANT_FLOAT
+: vcallf: \ ( paramcnt -<name>- f: r )
+ 0x80000000 or
+ current-class @ 8 + dup @ dup 1+ rot ! \ Kludge fix to get to .vtCount before it's defined.
+ create , , \ ( paramcnt index -- )
+ does> \ ( inst class pfa -- ptr-inst ptr-class )
+ nip 2@ vcall f> \ ( params offset inst class offset -- f: r )
+;
+\ #endif /* FLOAT */
+\ #endif /* VCALL */
+
+\ END-CLASS terminates construction of a class by storing
+\ the size of its instance variables in the class's size field
+\ ( -- old-wid addr[size] 0 )
+\
+: end-class ( old-wid addr[size] size -- )
+ swap ! set-current
+ search> drop \ pop struct builder wordlist
+;
+
+\ See resume-class (a metaclass method) below for usage
+\ This is equivalent to end-class for now, but that will change
+\ when we support vtable bindings.
+: suspend-class ( old-wid addr[size] size -- ) end-class ;
+
+set-current previous
+\ E N D I N S T A N C E V A R I A B L E S
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ D O - D O - I N S T A N C E
+\ Makes a class method that contains the code for an
+\ instance of the class. This word gets compiled into
+\ the wordlist of every class by the SUB method.
+\ PRECONDITION: current-class contains the class address
+\ why use a state variable instead of the stack?
+\ >> Stack state is not well-defined during compilation (there are
+\ >> control structure match codes on the stack, of undefined size
+\ >> easiest way around this is use of this thread-local variable
+\
+: do-do-instance ( -- )
+ s" : .do-instance does> [ current-class @ ] literal ;"
+ evaluate
+;
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** M E T A C L A S S
+\ Every class is an instance of metaclass. This lets
+\ classes have methods that are different from those
+\ of their instances.
+\ Classes are IMMEDIATE to make early binding simpler
+\ See above...
+\
+:noname
+ wordlist
+ create
+ immediate
+ 0 , \ NULL parent class
+ dup , \ wid
+\ #if FICL_WANT_VCALL
+ 4 cells , \ instance size
+\ #else
+ 3 cells , \ instance size
+\ #endif
+ ficl-set-current
+ does> dup
+; execute metaclass
+\ now brand OBJECT's wordlist (so that ORDER can display it by name)
+metaclass drop cell+ @ brand-wordlist
+
+metaclass drop current-class !
+do-do-instance
+
+\
+\ C L A S S M E T H O D S
+\
+instance-vars >search
+
+create .super ( class metaclass -- parent-class )
+ 0 cells , do-instance-var
+
+create .wid ( class metaclass -- wid ) \ return wid of class
+ 1 cells , do-instance-var
+
+\ #if FICL_WANT_VCALL
+create .vtCount \ Number of VTABLE methods, if any
+ 2 cells , do-instance-var
+
+create .size ( class metaclass -- size ) \ return class's payload size
+ 3 cells , do-instance-var
+\ #else
+create .size ( class metaclass -- size ) \ return class's payload size
+ 2 cells , do-instance-var
+\ #endif
+
+: get-size metaclass => .size @ ;
+: get-wid metaclass => .wid @ ;
+: get-super metaclass => .super @ ;
+\ #if FICL_WANT_VCALL
+: get-vtCount metaclass => .vtCount @ ;
+: get-vtAdd metaclass => .vtCount ;
+\ #endif
+
+\ create an uninitialized instance of a class, leaving
+\ the address of the new instance and its class
+\
+: instance ( class metaclass "name" -- instance class )
+ locals| meta parent |
+ create
+ here parent --> .do-instance \ ( inst class )
+ parent meta metaclass => get-size
+ allot \ allocate payload space
+;
+
+\ create an uninitialized array
+: array ( n class metaclass "name" -- n instance class )
+ locals| meta parent nobj |
+ create nobj
+ here parent --> .do-instance \ ( nobj inst class )
+ parent meta metaclass => get-size
+ nobj * allot \ allocate payload space
+;
+
+\ create an initialized instance
+\
+: new \ ( class metaclass "name" -- )
+ metaclass => instance --> init
+;
+
+\ create an initialized array of instances
+: new-array ( n class metaclass "name" -- )
+ metaclass => array
+ --> array-init
+;
+
+\ Create an anonymous initialized instance from the heap
+: alloc \ ( class metaclass -- instance class )
+ locals| meta class |
+ class meta metaclass => get-size allocate ( -- addr fail-flag )
+ abort" allocate failed " ( -- addr )
+ class 2dup --> init
+;
+
+\ Create an anonymous array of initialized instances from the heap
+: alloc-array \ ( n class metaclass -- instance class )
+ locals| meta class nobj |
+ class meta metaclass => get-size
+ nobj * allocate ( -- addr fail-flag )
+ abort" allocate failed " ( -- addr )
+ nobj over class --> array-init
+ class
+;
+
+\ Create an anonymous initialized instance from the dictionary
+: allot { 2:this -- 2:instance }
+ here ( instance-address )
+ this my=> get-size allot
+ this drop 2dup --> init
+;
+
+\ Create an anonymous array of initialized instances from the dictionary
+: allot-array { nobj 2:this -- 2:instance }
+ here ( instance-address )
+ this my=> get-size nobj * allot
+ this drop 2dup ( 2instance 2instance )
+ nobj -rot --> array-init
+;
+
+\ create a proxy object with initialized payload address given
+: ref ( instance-addr class metaclass "name" -- )
+ drop create , ,
+ does> 2@
+;
+
+\ suspend-class and resume-class help to build mutually referent classes.
+\ Example:
+\ object subclass c-akbar
+\ suspend-class ( put akbar on hold while we define jeff )
+\ object subclass c-jeff
+\ c-akbar ref: .akbar
+\ ( and whatever else comprises this class )
+\ end-class ( done with c-jeff )
+\ c-akbar --> resume-class
+\ c-jeff ref: .jeff
+\ ( and whatever else goes in c-akbar )
+\ end-class ( done with c-akbar )
+\
+: resume-class { 2:this -- old-wid addr[size] size }
+ this --> .wid @ ficl-set-current ( old-wid )
+ this --> .size dup @ ( old-wid addr[size] size )
+ instance-vars >search
+;
+
+\ create a subclass
+\ This method leaves the stack and search order ready for instance variable
+\ building. Pushes the instance-vars wordlist onto the search order,
+\ and sets the compilation wordlist to be the private wordlist of the
+\ new class. The class's wordlist is deliberately NOT in the search order -
+\ to prevent methods from getting used with wrong data.
+\ Postcondition: leaves the address of the new class in current-class
+: sub ( class metaclass "name" -- old-wid addr[size] size )
+ wordlist
+ locals| wid meta parent |
+ parent meta metaclass => get-wid
+ wid wid-set-super \ set superclass
+ create immediate \ get the subclass name
+ wid brand-wordlist \ label the subclass wordlist
+ here current-class ! \ prep for do-do-instance
+ parent , \ save parent class
+ wid , \ save wid
+\ #if FICL_WANT_VCALL
+ parent meta --> get-vtCount ,
+\ #endif
+ here parent meta --> get-size dup , ( addr[size] size )
+ metaclass => .do-instance
+ wid ficl-set-current -rot
+ do-do-instance
+ instance-vars >search \ push struct builder wordlist
+;
+
+\ OFFSET-OF returns the offset of an instance variable
+\ from the instance base address. If the next token is not
+\ the name of in instance variable method, you get garbage
+\ results -- there is no way at present to check for this error.
+: offset-of ( class metaclass "name" -- offset )
+ drop find-method-xt nip >body @ ;
+
+\ ID returns the string name cell-pair of its class
+: id ( class metaclass -- c-addr u )
+ drop body> >name ;
+
+\ list methods of the class
+: methods \ ( class meta -- )
+ locals| meta class |
+ begin
+ class body> >name type ." methods:" cr
+ class meta --> get-wid >search words cr previous
+ class meta metaclass => get-super
+ dup to class
+ 0= until cr
+;
+
+\ list class's ancestors
+: pedigree ( class meta -- )
+ locals| meta class |
+ begin
+ class body> >name type space
+ class meta metaclass => get-super
+ dup to class
+ 0= until cr
+;
+
+\ decompile an instance method
+: see ( class meta -- )
+ metaclass => get-wid >search see previous ;
+
+\ debug a method of metaclass
+\ Eg: my-class --> debug my-method
+: debug ( class meta -- )
+ find-method-xt debug-xt ;
+
+previous set-current
+\ E N D M E T A C L A S S
+
+\ ** META is a nickname for the address of METACLASS...
+metaclass drop
+constant meta
+
+\ ** SUBCLASS is a nickname for a class's SUB method...
+\ Subclass compilation ends when you invoke end-class
+\ This method is late bound for safety...
+: subclass --> sub ;
+
+\ #if FICL_WANT_VCALL
+\ VTABLE Support extensions (Guy Carver)
+\ object --> sub mine hasvtable
+: hasvtable 4 + ; immediate
+\ #endif
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** O B J E C T
+\ Root of all classes
+:noname
+ wordlist
+ create immediate
+ 0 , \ NULL parent class
+ dup , \ wid
+ 0 , \ instance size
+ ficl-set-current
+ does> meta
+; execute object
+\ now brand OBJECT's wordlist (so that ORDER can display it by name)
+object drop cell+ @ brand-wordlist
+
+object drop current-class !
+do-do-instance
+instance-vars >search
+
+\ O B J E C T M E T H O D S
+\ Convert instance cell-pair to class cell-pair
+\ Useful for binding class methods from an instance
+: class ( instance class -- class metaclass )
+ nip meta ;
+
+\ default INIT method zero fills an instance
+: init ( instance class -- )
+ meta
+ metaclass => get-size ( inst size )
+ erase ;
+
+\ Apply INIT to an array of NOBJ objects...
+\
+: array-init ( nobj inst class -- )
+ 0 dup locals| &init &next class inst |
+ \
+ \ bind methods outside the loop to save time
+ \
+ class s" init" lookup-method to &init
+ s" next" lookup-method to &next
+ drop
+ 0 ?do
+ inst class 2dup
+ &init execute
+ &next execute drop to inst
+ loop
+;
+
+\ free storage allocated to a heap instance by alloc or alloc-array
+\ NOTE: not protected against errors like FREEing something that's
+\ really in the dictionary.
+: free \ ( instance class -- )
+ drop free
+ abort" free failed "
+;
+
+\ Instance aliases for common class methods
+\ Upcast to parent class
+: super ( instance class -- instance parent-class )
+ meta metaclass => get-super ;
+
+: pedigree ( instance class -- )
+ object => class
+ metaclass => pedigree ;
+
+: size ( instance class -- sizeof-instance )
+ object => class
+ metaclass => get-size ;
+
+: methods ( instance class -- )
+ object => class
+ metaclass => methods ;
+
+\ Array indexing methods...
+\ Usage examples:
+\ 10 object-array --> index
+\ obj --> next
+\
+: index ( n instance class -- instance[n] class )
+ locals| class inst |
+ inst class
+ object => class
+ metaclass => get-size * ( n*size )
+ inst + class ;
+
+: next ( instance[n] class -- instance[n+1] class )
+ locals| class inst |
+ inst class
+ object => class
+ metaclass => get-size
+ inst +
+ class ;
+
+: prev ( instance[n] class -- instance[n-1] class )
+ locals| class inst |
+ inst class
+ object => class
+ metaclass => get-size
+ inst swap -
+ class ;
+
+: debug ( 2this -- ?? )
+ find-method-xt debug-xt ;
+
+previous set-current
+\ E N D O B J E C T
+
+\ reset to default search order
+only definitions
+
+\ redefine oop in default search order to put OOP words in the search order and make them
+\ the compiling wordlist...
+
+: oo only also oop definitions ;
+
+\ #endif
diff --git a/stand/ficl/softwords/prefix.fr b/stand/ficl/softwords/prefix.fr
new file mode 100644
index 0000000..ae1727f
--- /dev/null
+++ b/stand/ficl/softwords/prefix.fr
@@ -0,0 +1,59 @@
+\ **
+\ ** Prefix words for ficl
+\ ** submitted by Larry Hastings, larry@hastings.org
+\ **
+\ (jws) To make a prefix, simply create a new definition in the <prefixes>
+\ wordlist. start-prefixes and end-prefixes handle the bookkeeping
+\
+\ $FreeBSD$
+
+variable save-current
+
+: start-prefixes get-current save-current ! <prefixes> set-current ;
+: end-prefixes save-current @ set-current ;
+: show-prefixes <prefixes> >search words search> drop ;
+
+\ #if (FICL_EXTENDED_PREFIX)
+
+start-prefixes
+
+\ define " (double-quote) as an alias for s", and make it a prefix
+: " postpone s" ; immediate
+
+
+\ make .( a prefix (we just create an alias for it in the prefixes list)
+: .( postpone .( ; immediate
+
+
+\ make \ a prefix, and add // (same thing) as a prefix too
+\ (jws) "//" is precompiled to save aggravation with Perl
+\ : // postpone \ ; immediate
+
+
+\ ** add 0b, 0o, 0d, and 0x as prefixes
+\ ** these temporarily shift the base to 2, 8, 10, and 16 respectively
+\ ** and consume the next number in the input stream, pushing/compiling
+\ ** as normal
+
+\ (jws) __tempbase is precompiled, as are 0x and 0d - see prefix.c
+\
+\ : __tempbase { newbase | oldbase -- }
+\ base @ to oldbase
+\ newbase base !
+\ 0 0 parse-word >number 2drop drop
+\ oldbase base !
+\ ;
+
+: 0b 2 __tempbase ; immediate
+
+: 0o 8 __tempbase ; immediate
+
+\ : 0d 10 __tempbase ; immediate
+\ "0d" add-prefix
+
+\ : 0x 16 __tempbase ; immediate
+\ "0x" add-prefix
+
+end-prefixes
+
+\ #endif
diff --git a/stand/ficl/softwords/softcore.awk b/stand/ficl/softwords/softcore.awk
new file mode 100644
index 0000000..5a97999
--- /dev/null
+++ b/stand/ficl/softwords/softcore.awk
@@ -0,0 +1,183 @@
+#!/usr/bin/awk -f
+#
+# Convert forth source files to a giant C string
+#
+# Joe Abley <jabley@patho.gen.nz>, 12 January 1999
+#
+# 02-oct-1999: Cleaned up awk slightly; added some additional logic
+# suggested by dcs to compress the stored forth program.
+#
+# Note! This script uses strftime() which is a gawk-ism, and the
+# POSIX [[:space:]] character class.
+#
+# $FreeBSD$
+
+BEGIN \
+{
+ printf "/*******************************************************************\n";
+ printf "** s o f t c o r e . c\n";
+ printf "** Forth Inspired Command Language -\n";
+ printf "** Words from CORE set written in FICL\n";
+ printf "** Author: John Sadler (john_sadler@alum.mit.edu)\n";
+ printf "** Created: 27 December 1997\n";
+ printf "** Last update: %s\n", datestamp;
+ printf "*******************************************************************/\n";
+ printf "/*\n";
+ printf "** DO NOT EDIT THIS FILE -- it is generated by softwords/softcore.awk\n";
+ printf "** Make changes to the .fr files in ficl/softwords instead.\n";
+ printf "** This file contains definitions that are compiled into the\n";
+ printf "** system dictionary by the first virtual machine to be created.\n";
+ printf "** Created automagically by ficl/softwords/softcore.awk\n";
+ printf "*/\n";
+ printf "/*\n";
+ printf "** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)\n";
+ printf "** All rights reserved.\n";
+ printf "**\n";
+ printf "** Get the latest Ficl release at http://ficl.sourceforge.net\n";
+ printf "**\n";
+ printf "** I am interested in hearing from anyone who uses ficl. If you have\n";
+ printf "** a problem, a success story, a defect, an enhancement request, or\n";
+ printf "** if you would like to contribute to the ficl release, please send\n";
+ printf "** contact me by email at the address above.\n";
+ printf "**\n";
+ printf "** L I C E N S E and D I S C L A I M E R\n";
+ printf "** \n";
+ printf "** Redistribution and use in source and binary forms, with or without\n";
+ printf "** modification, are permitted provided that the following conditions\n";
+ printf "** are met:\n";
+ printf "** 1. Redistributions of source code must retain the above copyright\n";
+ printf "** notice, this list of conditions and the following disclaimer.\n";
+ printf "** 2. Redistributions in binary form must reproduce the above copyright\n";
+ printf "** notice, this list of conditions and the following disclaimer in the\n";
+ printf "** documentation and/or other materials provided with the distribution.\n";
+ printf "**\n";
+ printf "** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n";
+ printf "** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n";
+ printf "** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n";
+ printf "** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n";
+ printf "** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n";
+ printf "** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n";
+ printf "** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n";
+ printf "** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n";
+ printf "** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n";
+ printf "** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n";
+ printf "** SUCH DAMAGE.\n";
+ printf "*/\n";
+ printf "\n";
+ printf "\n#include \"ficl.h\"\n";
+ printf "\nstatic char softWords[] =\n";
+ printf "#if FICL_WANT_SOFTWORDS\n";
+
+ commenting = 0;
+}
+
+# some general early substitutions
+{
+ gsub(/\t/, " "); # replace each tab with 4 spaces
+ gsub(/\"/, "\\\""); # escape quotes
+ gsub(/\\[[:space:]]+$/, ""); # toss empty comments
+}
+
+# strip out empty lines
+/^ *$/ \
+{
+ next;
+}
+
+# emit / ** lines as multi-line C comments
+/^\\[[:space:]]\*\*/ \
+{
+ sub(/^\\[[:space:]]/, "");
+ if (commenting == 0) printf "/*\n";
+ printf "%s\n", $0;
+ commenting = 1;
+ next;
+}
+
+# strip blank lines
+/^[[:space:]]*$/ \
+{
+ next;
+}
+
+# function to close a comment, used later
+function end_comments()
+{
+ commenting = 0;
+ printf "*/\n";
+}
+
+# pass commented preprocessor directives
+/^\\[[:space:]]#/ \
+{
+ if (commenting) end_comments();
+ sub(/^\\[[:space:]]/, "");
+ printf "%s\n", $0;
+ next;
+}
+
+# toss all other full-line \ comments
+/^\\/ \
+{
+ if (commenting) end_comments();
+ next;
+}
+
+# lop off trailing \ comments
+/\\[[:space:]]+/ \
+{
+ sub(/\\[[:space:]]+.*$/, "");
+}
+
+# expunge ( ) comments
+/[[:space:]]+\([[:space:]][^)]*\)/ \
+{
+ sub(/[[:space:]]+\([[:space:]][^)]*\)/, "");
+}
+
+# remove leading spaces
+/^[[:space:]]+/ \
+{
+ sub(/^[[:space:]]+/, "");
+}
+
+# removing trailing spaces
+/[[:space:]]+$/ \
+{
+ sub(/[[:space:]]+$/, "");
+}
+
+# strip out empty lines again (preceding rules may have generated some)
+/^[[:space:]]*$/ \
+{
+ if (commenting) end_comments();
+ next;
+}
+
+# emit all other lines as quoted string fragments
+{
+ if (commenting) end_comments();
+
+ printf " \"%s \"\n", $0;
+ next;
+}
+
+END \
+{
+ if (commenting) end_comments();
+ printf "#endif /* WANT_SOFTWORDS */\n";
+ printf " \"quit \";\n";
+ printf "\n\nvoid ficlCompileSoftCore(FICL_SYSTEM *pSys)\n";
+ printf "{\n";
+ printf " FICL_VM *pVM = pSys->vmList;\n";
+ printf " CELL id = pVM->sourceID;\n";
+ printf " int ret = sizeof (softWords);\n";
+ printf " assert(pVM);\n";
+ printf " pVM->sourceID.i = -1;\n";
+ printf " ret = ficlExec(pVM, softWords);\n";
+ printf " pVM->sourceID = id;\n";
+ printf " if (ret == VM_ERREXIT)\n";
+ printf " assert(FALSE);\n";
+ printf " return;\n";
+ printf "}\n";
+}
diff --git a/stand/ficl/softwords/softcore.fr b/stand/ficl/softwords/softcore.fr
new file mode 100644
index 0000000..a70ebaa
--- /dev/null
+++ b/stand/ficl/softwords/softcore.fr
@@ -0,0 +1,206 @@
+\ ** ficl/softwords/softcore.fr
+\ ** FICL soft extensions
+\ ** John Sadler (john_sadler@alum.mit.edu)
+\ ** September, 1998
+\
+\ $FreeBSD$
+
+\ ** Ficl USER variables
+\ ** See words.c for primitive def'n of USER
+\ #if FICL_WANT_USER
+variable nUser 0 nUser !
+: user \ name ( -- )
+ nUser dup @ user 1 swap +! ;
+
+\ #endif
+
+\ ** ficl extras
+\ EMPTY cleans the parameter stack
+: empty ( xn..x1 -- ) depth 0 ?do drop loop ;
+\ CELL- undoes CELL+
+: cell- ( addr -- addr ) [ 1 cells ] literal - ;
+: -rot ( a b c -- c a b ) 2 -roll ;
+
+\ ** CORE
+: abs ( x -- x )
+ dup 0< if negate endif ;
+decimal 32 constant bl
+
+: space ( -- ) bl emit ;
+
+: spaces ( n -- ) 0 ?do space loop ;
+
+: abort"
+ state @ if
+ postpone if
+ postpone ."
+ postpone cr
+ -2
+ postpone literal
+ postpone throw
+ postpone endif
+ else
+ [char] " parse
+ rot if
+ type
+ cr
+ -2 throw
+ else
+ 2drop
+ endif
+ endif
+; immediate
+
+
+\ ** CORE EXT
+0 constant false
+false invert constant true
+: <> = 0= ;
+: 0<> 0= 0= ;
+: compile, , ;
+: convert char+ 65535 >number drop ; \ cribbed from DPANS A.6.2.0970
+: erase ( addr u -- ) 0 fill ;
+variable span
+: expect ( c-addr u1 -- ) accept span ! ;
+\ see marker.fr for MARKER implementation
+: nip ( y x -- x ) swap drop ;
+: tuck ( y x -- x y x) swap over ;
+: within ( test low high -- flag ) over - >r - r> u< ;
+
+
+\ ** LOCAL EXT word set
+\ #if FICL_WANT_LOCALS
+: locals| ( name...name | -- )
+ begin
+ bl word count
+ dup 0= abort" where's the delimiter??"
+ over c@
+ [char] | - over 1- or
+ while
+ (local)
+ repeat 2drop 0 0 (local)
+; immediate
+
+: local ( name -- ) bl word count (local) ; immediate
+
+: 2local ( name -- ) bl word count (2local) ; immediate
+
+: end-locals ( -- ) 0 0 (local) ; immediate
+
+\ #endif
+
+\ ** TOOLS word set...
+: ? ( addr -- ) @ . ;
+: dump ( addr u -- )
+ 0 ?do
+ dup c@ . 1+
+ i 7 and 7 = if cr endif
+ loop drop
+;
+
+\ ** SEARCH+EXT words and ficl helpers
+\ BRAND-WORDLIST is a helper for ficl-named-wordlist. Usage idiom:
+\ wordlist dup create , brand-wordlist
+\ gets the name of the word made by create and applies it to the wordlist...
+: brand-wordlist ( wid -- ) last-word >name drop wid-set-name ;
+
+: ficl-named-wordlist \ ( hash-size name -- ) run: ( -- wid )
+ ficl-wordlist dup create , brand-wordlist does> @ ;
+
+: wordlist ( -- )
+ 1 ficl-wordlist ;
+
+\ FICL-SET-CURRENT sets the compile wordlist and pushes the previous value
+: ficl-set-current ( wid -- old-wid )
+ get-current swap set-current ;
+
+\ DO_VOCABULARY handles the DOES> part of a VOCABULARY
+\ When executed, new voc replaces top of search stack
+: do-vocabulary ( -- )
+ does> @ search> drop >search ;
+
+: ficl-vocabulary ( nBuckets name -- )
+ ficl-named-wordlist do-vocabulary ;
+
+: vocabulary ( name -- )
+ 1 ficl-vocabulary ;
+
+\ PREVIOUS drops the search order stack
+: previous ( -- ) search> drop ;
+
+\ HIDDEN vocabulary is a place to keep helper words from cluttering the namespace
+\ USAGE:
+\ hide
+\ <definitions to hide>
+\ set-current
+\ <words that use hidden defs>
+\ previous ( pop HIDDEN off the search order )
+
+1 ficl-named-wordlist hidden
+: hide hidden dup >search ficl-set-current ;
+
+\ ALSO dups the search stack...
+: also ( -- )
+ search> dup >search >search ;
+
+\ FORTH drops the top of the search stack and pushes FORTH-WORDLIST
+: forth ( -- )
+ search> drop
+ forth-wordlist >search ;
+
+\ ONLY sets the search order to a default state
+: only ( -- )
+ -1 set-order ;
+
+\ ORDER displays the compile wid and the search order list
+hide
+: list-wid ( wid -- )
+ dup wid-get-name ( wid c-addr u )
+ ?dup if
+ type drop
+ else
+ drop ." (unnamed wid) " x.
+ endif cr
+;
+set-current \ stop hiding words
+
+: order ( -- )
+ ." Search:" cr
+ get-order 0 ?do 3 spaces list-wid loop cr
+ ." Compile: " get-current list-wid cr
+;
+
+: debug ' debug-xt ; immediate
+: on-step ." S: " .s cr ;
+
+
+\ Submitted by lch.
+: strdup ( c-addr length -- c-addr2 length2 ior )
+ 0 locals| addr2 length c-addr | end-locals
+ length 1 + allocate
+ 0= if
+ to addr2
+ c-addr addr2 length move
+ addr2 length 0
+ else
+ 0 -1
+ endif
+ ;
+
+: strcat ( 2:a 2:b -- 2:new-a )
+ 0 locals| b-length b-u b-addr a-u a-addr | end-locals
+ b-u to b-length
+ b-addr a-addr a-u + b-length move
+ a-addr a-u b-length +
+ ;
+
+: strcpy ( 2:a 2:b -- 2:new-a )
+ locals| b-u b-addr a-u a-addr | end-locals
+ a-addr 0 b-addr b-u strcat
+ ;
+
+
+previous \ lose hidden words from search order
+
+\ ** E N D S O F T C O R E . F R
+
diff --git a/stand/ficl/softwords/string.fr b/stand/ficl/softwords/string.fr
new file mode 100644
index 0000000..dabb390
--- /dev/null
+++ b/stand/ficl/softwords/string.fr
@@ -0,0 +1,148 @@
+\ #if (FICL_WANT_OOP)
+\ ** ficl/softwords/string.fr
+\ A useful dynamic string class
+\ John Sadler 14 Sep 1998
+\
+\ ** C - S T R I N G
+\ counted string, buffer sized dynamically
+\ Creation example:
+\ c-string --> new str
+\ s" arf arf!!" str --> set
+\ s" woof woof woof " str --> cat
+\ str --> type cr
+\
+\ $FreeBSD$
+
+also oop definitions
+
+object subclass c-string
+ c-cell obj: .count
+ c-cell obj: .buflen
+ c-ptr obj: .buf
+ 32 constant min-buf
+
+ : get-count ( 2:this -- count ) my=[ .count get ] ;
+ : set-count ( count 2:this -- ) my=[ .count set ] ;
+
+ : ?empty ( 2:this -- flag ) --> get-count 0= ;
+
+ : get-buflen ( 2:this -- len ) my=[ .buflen get ] ;
+ : set-buflen ( len 2:this -- ) my=[ .buflen set ] ;
+
+ : get-buf ( 2:this -- ptr ) my=[ .buf get-ptr ] ;
+ : set-buf { ptr len 2:this -- }
+ ptr this my=[ .buf set-ptr ]
+ len this my=> set-buflen
+ ;
+
+ \ set buffer to null and buflen to zero
+ : clr-buf ( 2:this -- )
+ 0 0 2over my=> set-buf
+ 0 -rot my=> set-count
+ ;
+
+ \ free the buffer if there is one, set buf pointer to null
+ : free-buf { 2:this -- }
+ this my=> get-buf
+ ?dup if
+ free
+ abort" c-string free failed"
+ this my=> clr-buf
+ endif
+ ;
+
+ \ guarantee buffer is large enough to hold size chars
+ : size-buf { size 2:this -- }
+ size 0< abort" need positive size for size-buf"
+ size 0= if
+ this --> free-buf exit
+ endif
+
+ \ force buflen to be a positive multiple of min-buf chars
+ my=> min-buf size over / 1+ * chars to size
+
+ \ if buffer is null, allocate one, else resize it
+ this --> get-buflen 0=
+ if
+ size allocate
+ abort" out of memory"
+ size this --> set-buf
+ size this --> set-buflen
+ exit
+ endif
+
+ size this --> get-buflen > if
+ this --> get-buf size resize
+ abort" out of memory"
+ size this --> set-buf
+ endif
+ ;
+
+ : set { c-addr u 2:this -- }
+ u this --> size-buf
+ u this --> set-count
+ c-addr this --> get-buf u move
+ ;
+
+ : get { 2:this -- c-addr u }
+ this --> get-buf
+ this --> get-count
+ ;
+
+ \ append string to existing one
+ : cat { c-addr u 2:this -- }
+ this --> get-count u + dup >r
+ this --> size-buf
+ c-addr this --> get-buf this --> get-count + u move
+ r> this --> set-count
+ ;
+
+ : type { 2:this -- }
+ this --> ?empty if ." (empty) " exit endif
+ this --> .buf --> get-ptr
+ this --> .count --> get
+ type
+ ;
+
+ : compare ( 2string 2:this -- n )
+ --> get
+ 2swap
+ --> get
+ 2swap compare
+ ;
+
+ : hashcode ( 2:this -- hashcode )
+ --> get hash
+ ;
+
+ \ destructor method (overrides object --> free)
+ : free ( 2:this -- ) 2dup --> free-buf object => free ;
+
+end-class
+
+c-string subclass c-hashstring
+ c-2byte obj: .hashcode
+
+ : set-hashcode { 2:this -- }
+ this --> super --> hashcode
+ this --> .hashcode --> set
+ ;
+
+ : get-hashcode ( 2:this -- hashcode )
+ --> .hashcode --> get
+ ;
+
+ : set ( c-addr u 2:this -- )
+ 2swap 2over --> super --> set
+ --> set-hashcode
+ ;
+
+ : cat ( c-addr u 2:this -- )
+ 2swap 2over --> super --> cat
+ --> set-hashcode
+ ;
+
+end-class
+
+previous definitions
+\ #endif
diff --git a/stand/ficl/sparc64/sysdep.c b/stand/ficl/sparc64/sysdep.c
new file mode 100644
index 0000000..ad38660
--- /dev/null
+++ b/stand/ficl/sparc64/sysdep.c
@@ -0,0 +1,99 @@
+/*******************************************************************
+** s y s d e p . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Implementations of FICL external interface functions...
+**
+*******************************************************************/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include <stand.h>
+#endif
+#include "ficl.h"
+
+/*
+******************* FreeBSD P O R T B E G I N S H E R E ******************** Michael Smith
+*/
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ u_int64_t qx;
+
+ qx = (u_int64_t)x * (u_int64_t) y;
+
+ q.hi = (u_int32_t)( qx >> 32 );
+ q.lo = (u_int32_t)( qx & 0xFFFFFFFFL);
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ u_int64_t qx, qh;
+
+ qh = q.hi;
+ qx = (qh << 32) | q.lo;
+
+ result.quot = qx / y;
+ result.rem = qx % y;
+
+ return result;
+}
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ while(*msg != 0)
+ putchar(*(msg++));
+ if (fNewline)
+ putchar('\n');
+
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+void *ficlRealloc (void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
diff --git a/stand/ficl/sparc64/sysdep.h b/stand/ficl/sparc64/sysdep.h
new file mode 100644
index 0000000..0a6ca33
--- /dev/null
+++ b/stand/ficl/sparc64/sysdep.h
@@ -0,0 +1,412 @@
+/*******************************************************************
+ s y s d e p . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Ficl system dependent types and prototypes...
+**
+** Note: Ficl also depends on the use of "assert" when
+** FICL_ROBUST is enabled. This may require some consideration
+** in firmware systems since assert often
+** assumes stderr/stdout.
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** 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.
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please send
+** contact me by email at the address above.
+**
+** $Id: sysdep.h,v 1.6 2001-04-26 21:41:55-07 jsadler Exp jsadler $
+*/
+
+/* $FreeBSD$ */
+
+#if !defined (__SYSDEP_H__)
+#define __SYSDEP_H__
+
+#include <sys/types.h>
+
+#include <stddef.h> /* size_t, NULL */
+#include <setjmp.h>
+#include <assert.h>
+
+#if !defined IGNORE /* Macro to silence unused param warnings */
+#define IGNORE(x) &x
+#endif
+
+/*
+** TRUE and FALSE for C boolean operations, and
+** portable 32 bit types for CELLs
+**
+*/
+#if !defined TRUE
+#define TRUE 1
+#endif
+#if !defined FALSE
+#define FALSE 0
+#endif
+
+
+/*
+** System dependent data type declarations...
+*/
+#if !defined INT32
+#define INT32 int
+#endif
+
+#if !defined UNS32
+#define UNS32 unsigned int
+#endif
+
+#if !defined UNS16
+#define UNS16 unsigned short
+#endif
+
+#if !defined UNS8
+#define UNS8 unsigned char
+#endif
+
+#if !defined NULL
+#define NULL ((void *)0)
+#endif
+
+/*
+** FICL_UNS and FICL_INT must have the same size as a void* on
+** the target system. A CELL is a union of void*, FICL_UNS, and
+** FICL_INT.
+** (11/2000: same for FICL_FLOAT)
+*/
+#if !defined FICL_INT
+#define FICL_INT long
+#endif
+
+#if !defined FICL_UNS
+#define FICL_UNS unsigned long
+#endif
+
+#if !defined FICL_FLOAT
+#define FICL_FLOAT float
+#endif
+
+/*
+** Ficl presently supports values of 32 and 64 for BITS_PER_CELL
+*/
+#if !defined BITS_PER_CELL
+#define BITS_PER_CELL 64
+#endif
+
+#if ((BITS_PER_CELL != 32) && (BITS_PER_CELL != 64))
+ Error!
+#endif
+
+typedef struct
+{
+ FICL_UNS hi;
+ FICL_UNS lo;
+} DPUNS;
+
+typedef struct
+{
+ FICL_UNS quot;
+ FICL_UNS rem;
+} UNSQR;
+
+typedef struct
+{
+ FICL_INT hi;
+ FICL_INT lo;
+} DPINT;
+
+typedef struct
+{
+ FICL_INT quot;
+ FICL_INT rem;
+} INTQR;
+
+
+/*
+** B U I L D C O N T R O L S
+*/
+
+#if !defined (FICL_MINIMAL)
+#define FICL_MINIMAL 0
+#endif
+#if (FICL_MINIMAL)
+#define FICL_WANT_SOFTWORDS 0
+#define FICL_WANT_FLOAT 0
+#define FICL_WANT_USER 0
+#define FICL_WANT_LOCALS 0
+#define FICL_WANT_DEBUGGER 0
+#define FICL_WANT_OOP 0
+#define FICL_PLATFORM_EXTEND 0
+#define FICL_MULTITHREAD 0
+#define FICL_ROBUST 0
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_PLATFORM_EXTEND
+** Includes words defined in ficlCompilePlatform
+*/
+#if !defined (FICL_PLATFORM_EXTEND)
+#define FICL_PLATFORM_EXTEND 1
+#endif
+
+/*
+** FICL_WANT_FLOAT
+** Includes a floating point stack for the VM, and words to do float operations.
+** Contributed by Guy Carver
+*/
+#if !defined (FICL_WANT_FLOAT)
+#define FICL_WANT_FLOAT 0
+#endif
+
+/*
+** FICL_WANT_DEBUGGER
+** Inludes a simple source level debugger
+*/
+#if !defined (FICL_WANT_DEBUGGER)
+#define FICL_WANT_DEBUGGER 1
+#endif
+
+/*
+** User variables: per-instance variables bound to the VM.
+** Kinda like thread-local storage. Could be implemented in a
+** VM private dictionary, but I've chosen the lower overhead
+** approach of an array of CELLs instead.
+*/
+#if !defined FICL_WANT_USER
+#define FICL_WANT_USER 1
+#endif
+
+#if !defined FICL_USER_CELLS
+#define FICL_USER_CELLS 16
+#endif
+
+/*
+** FICL_WANT_LOCALS controls the creation of the LOCALS wordset and
+** a private dictionary for local variable compilation.
+*/
+#if !defined FICL_WANT_LOCALS
+#define FICL_WANT_LOCALS 1
+#endif
+
+/* Max number of local variables per definition */
+#if !defined FICL_MAX_LOCALS
+#define FICL_MAX_LOCALS 16
+#endif
+
+/*
+** FICL_WANT_OOP
+** Inludes object oriented programming support (in softwords)
+** OOP support requires locals and user variables!
+*/
+#if !(FICL_WANT_LOCALS) || !(FICL_WANT_USER)
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 0
+#endif
+#endif
+
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 1
+#endif
+
+/*
+** FICL_WANT_SOFTWORDS
+** Controls inclusion of all softwords in softcore.c
+*/
+#if !defined (FICL_WANT_SOFTWORDS)
+#define FICL_WANT_SOFTWORDS 1
+#endif
+
+/*
+** FICL_MULTITHREAD enables dictionary mutual exclusion
+** wia the ficlLockDictionary system dependent function.
+** Note: this implementation is experimental and poorly
+** tested. Further, it's unnecessary unless you really
+** intend to have multiple SESSIONS (poor choice of name
+** on my part) - that is, threads that modify the dictionary
+** at the same time.
+*/
+#if !defined FICL_MULTITHREAD
+#define FICL_MULTITHREAD 0
+#endif
+
+/*
+** PORTABLE_LONGMULDIV causes ficlLongMul and ficlLongDiv to be
+** defined in C in sysdep.c. Use this if you cannot easily
+** generate an inline asm definition
+*/
+#if !defined (PORTABLE_LONGMULDIV)
+#define PORTABLE_LONGMULDIV 0
+#endif
+
+/*
+** INLINE_INNER_LOOP causes the inner interpreter to be inline code
+** instead of a function call. This is mainly because MS VC++ 5
+** chokes with an internal compiler error on the function version.
+** in release mode. Sheesh.
+*/
+#if !defined INLINE_INNER_LOOP
+#if defined _DEBUG
+#define INLINE_INNER_LOOP 0
+#else
+#define INLINE_INNER_LOOP 1
+#endif
+#endif
+
+/*
+** FICL_ROBUST enables bounds checking of stacks and the dictionary.
+** This will detect stack over and underflows and dictionary overflows.
+** Any exceptional condition will result in an assertion failure.
+** (As generated by the ANSI assert macro)
+** FICL_ROBUST == 1 --> stack checking in the outer interpreter
+** FICL_ROBUST == 2 also enables checking in many primitives
+*/
+
+#if !defined FICL_ROBUST
+#define FICL_ROBUST 2
+#endif
+
+/*
+** FICL_DEFAULT_STACK Specifies the default size (in CELLs) of
+** a new virtual machine's stacks, unless overridden at
+** create time.
+*/
+#if !defined FICL_DEFAULT_STACK
+#define FICL_DEFAULT_STACK 128
+#endif
+
+/*
+** FICL_DEFAULT_DICT specifies the number of CELLs to allocate
+** for the system dictionary by default. The value
+** can be overridden at startup time as well.
+** FICL_DEFAULT_ENV specifies the number of cells to allot
+** for the environment-query dictionary.
+*/
+#if !defined FICL_DEFAULT_DICT
+#define FICL_DEFAULT_DICT 12288
+#endif
+
+#if !defined FICL_DEFAULT_ENV
+#define FICL_DEFAULT_ENV 260
+#endif
+
+/*
+** FICL_DEFAULT_VOCS specifies the maximum number of wordlists in
+** the dictionary search order. See Forth DPANS sec 16.3.3
+** (file://dpans16.htm#16.3.3)
+*/
+#if !defined FICL_DEFAULT_VOCS
+#define FICL_DEFAULT_VOCS 16
+#endif
+
+/*
+** FICL_MAX_PARSE_STEPS controls the size of an array in the FICL_SYSTEM structure
+** that stores pointers to parser extension functions. I would never expect to have
+** more than 8 of these, so that's the default limit. Too many of these functions
+** will probably exact a nasty performance penalty.
+*/
+#if !defined FICL_MAX_PARSE_STEPS
+#define FICL_MAX_PARSE_STEPS 8
+#endif
+
+/*
+** FICL_EXTENDED_PREFIX enables a bunch of extra prefixes in prefix.c and prefix.fr (if
+** included as part of softcore.c)
+*/
+#if !defined FICL_EXTENDED_PREFIX
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_ALIGN is the power of two to which the dictionary
+** pointer address must be aligned. This value is usually
+** either 1 or 2, depending on the memory architecture
+** of the target system; 2 is safe on any 16 or 32 bit
+** machine. 3 would be appropriate for a 64 bit machine.
+*/
+#if !defined FICL_ALIGN
+#define FICL_ALIGN 3
+#define FICL_ALIGN_ADD ((1 << FICL_ALIGN) - 1)
+#endif
+
+/*
+** System dependent routines --
+** edit the implementations in sysdep.c to be compatible
+** with your runtime environment...
+** ficlTextOut sends a NULL terminated string to the
+** default output device - used for system error messages
+** ficlMalloc and ficlFree have the same semantics as malloc and free
+** in standard C
+** ficlLongMul multiplies two UNS32s and returns a 64 bit unsigned
+** product
+** ficlLongDiv divides an UNS64 by an UNS32 and returns UNS32 quotient
+** and remainder
+*/
+struct vm;
+void ficlTextOut(struct vm *pVM, char *msg, int fNewline);
+void *ficlMalloc (size_t size);
+void ficlFree (void *p);
+void *ficlRealloc(void *p, size_t size);
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** must be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** before timeout (optional - could also block forever)
+**
+** NOTE: this function must be implemented with lock counting
+** semantics: nested calls must behave properly.
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock);
+#else
+#define ficlLockDictionary(x) 0 /* ignore */
+#endif
+
+/*
+** 64 bit integer math support routines: multiply two UNS32s
+** to get a 64 bit product, & divide the product by an UNS32
+** to get an UNS32 quotient and remainder. Much easier in asm
+** on a 32 bit CPU than in C, which usually doesn't support
+** the double length result (but it should).
+*/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y);
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y);
+
+#endif /*__SYSDEP_H__*/
diff --git a/stand/ficl/stack.c b/stand/ficl/stack.c
new file mode 100644
index 0000000..f98a3b6
--- /dev/null
+++ b/stand/ficl/stack.c
@@ -0,0 +1,372 @@
+/*******************************************************************
+** s t a c k . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** $Id: stack.c,v 1.10 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdlib.h>
+#else
+#include <stand.h>
+#endif
+#include "ficl.h"
+
+#define STKDEPTH(s) ((s)->sp - (s)->base)
+
+/*
+** N O T E: Stack convention:
+**
+** sp points to the first available cell
+** push: store value at sp, increment sp
+** pop: decrement sp, fetch value at sp
+** Stack grows from low to high memory
+*/
+
+/*******************************************************************
+ v m C h e c k S t a c k
+** Check the parameter stack for underflow or overflow.
+** nCells controls the type of check: if nCells is zero,
+** the function checks the stack state for underflow and overflow.
+** If nCells > 0, checks to see that the stack has room to push
+** that many cells. If less than zero, checks to see that the
+** stack has room to pop that many cells. If any test fails,
+** the function throws (via vmThrow) a VM_ERREXIT exception.
+*******************************************************************/
+void vmCheckStack(FICL_VM *pVM, int popCells, int pushCells)
+{
+ FICL_STACK *pStack = pVM->pStack;
+ int nFree = pStack->base + pStack->nCells - pStack->sp;
+
+ if (popCells > STKDEPTH(pStack))
+ {
+ vmThrowErr(pVM, "Error: stack underflow");
+ }
+
+ if (nFree < pushCells - popCells)
+ {
+ vmThrowErr(pVM, "Error: stack overflow");
+ }
+
+ return;
+}
+
+#if FICL_WANT_FLOAT
+void vmCheckFStack(FICL_VM *pVM, int popCells, int pushCells)
+{
+ FICL_STACK *fStack = pVM->fStack;
+ int nFree = fStack->base + fStack->nCells - fStack->sp;
+
+ if (popCells > STKDEPTH(fStack))
+ {
+ vmThrowErr(pVM, "Error: float stack underflow");
+ }
+
+ if (nFree < pushCells - popCells)
+ {
+ vmThrowErr(pVM, "Error: float stack overflow");
+ }
+}
+#endif
+
+/*******************************************************************
+ s t a c k C r e a t e
+**
+*******************************************************************/
+
+FICL_STACK *stackCreate(unsigned nCells)
+{
+ size_t size = sizeof (FICL_STACK) + nCells * sizeof (CELL);
+ FICL_STACK *pStack = ficlMalloc(size);
+
+#if FICL_ROBUST
+ assert (nCells != 0);
+ assert (pStack != NULL);
+#endif
+
+ pStack->nCells = nCells;
+ pStack->sp = pStack->base;
+ pStack->pFrame = NULL;
+ return pStack;
+}
+
+
+/*******************************************************************
+ s t a c k D e l e t e
+**
+*******************************************************************/
+
+void stackDelete(FICL_STACK *pStack)
+{
+ if (pStack)
+ ficlFree(pStack);
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k D e p t h
+**
+*******************************************************************/
+
+int stackDepth(FICL_STACK *pStack)
+{
+ return STKDEPTH(pStack);
+}
+
+/*******************************************************************
+ s t a c k D r o p
+**
+*******************************************************************/
+
+void stackDrop(FICL_STACK *pStack, int n)
+{
+#if FICL_ROBUST
+ assert(n > 0);
+#endif
+ pStack->sp -= n;
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k F e t c h
+**
+*******************************************************************/
+
+CELL stackFetch(FICL_STACK *pStack, int n)
+{
+ return pStack->sp[-n-1];
+}
+
+void stackStore(FICL_STACK *pStack, int n, CELL c)
+{
+ pStack->sp[-n-1] = c;
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k G e t T o p
+**
+*******************************************************************/
+
+CELL stackGetTop(FICL_STACK *pStack)
+{
+ return pStack->sp[-1];
+}
+
+
+/*******************************************************************
+ s t a c k L i n k
+** Link a frame using the stack's frame pointer. Allot space for
+** nCells cells in the frame
+** 1) Push pFrame
+** 2) pFrame = sp
+** 3) sp += nCells
+*******************************************************************/
+
+void stackLink(FICL_STACK *pStack, int nCells)
+{
+ stackPushPtr(pStack, pStack->pFrame);
+ pStack->pFrame = pStack->sp;
+ pStack->sp += nCells;
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k U n l i n k
+** Unink a stack frame previously created by stackLink
+** 1) sp = pFrame
+** 2) pFrame = pop()
+*******************************************************************/
+
+void stackUnlink(FICL_STACK *pStack)
+{
+ pStack->sp = pStack->pFrame;
+ pStack->pFrame = stackPopPtr(pStack);
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k P i c k
+**
+*******************************************************************/
+
+void stackPick(FICL_STACK *pStack, int n)
+{
+ stackPush(pStack, stackFetch(pStack, n));
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k P o p
+**
+*******************************************************************/
+
+CELL stackPop(FICL_STACK *pStack)
+{
+ return *--pStack->sp;
+}
+
+void *stackPopPtr(FICL_STACK *pStack)
+{
+ return (*--pStack->sp).p;
+}
+
+FICL_UNS stackPopUNS(FICL_STACK *pStack)
+{
+ return (*--pStack->sp).u;
+}
+
+FICL_INT stackPopINT(FICL_STACK *pStack)
+{
+ return (*--pStack->sp).i;
+}
+
+#if (FICL_WANT_FLOAT)
+float stackPopFloat(FICL_STACK *pStack)
+{
+ return (*(--pStack->sp)).f;
+}
+#endif
+
+/*******************************************************************
+ s t a c k P u s h
+**
+*******************************************************************/
+
+void stackPush(FICL_STACK *pStack, CELL c)
+{
+ *pStack->sp++ = c;
+}
+
+void stackPushPtr(FICL_STACK *pStack, void *ptr)
+{
+ *pStack->sp++ = LVALUEtoCELL(ptr);
+}
+
+void stackPushUNS(FICL_STACK *pStack, FICL_UNS u)
+{
+ *pStack->sp++ = LVALUEtoCELL(u);
+}
+
+void stackPushINT(FICL_STACK *pStack, FICL_INT i)
+{
+ *pStack->sp++ = LVALUEtoCELL(i);
+}
+
+#if (FICL_WANT_FLOAT)
+void stackPushFloat(FICL_STACK *pStack, FICL_FLOAT f)
+{
+ *pStack->sp++ = LVALUEtoCELL(f);
+}
+#endif
+
+/*******************************************************************
+ s t a c k R e s e t
+**
+*******************************************************************/
+
+void stackReset(FICL_STACK *pStack)
+{
+ pStack->sp = pStack->base;
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k R o l l
+** Roll nth stack entry to the top (counting from zero), if n is
+** >= 0. Drop other entries as needed to fill the hole.
+** If n < 0, roll top-of-stack to nth entry, pushing others
+** upward as needed to fill the hole.
+*******************************************************************/
+
+void stackRoll(FICL_STACK *pStack, int n)
+{
+ CELL c;
+ CELL *pCell;
+
+ if (n == 0)
+ return;
+ else if (n > 0)
+ {
+ pCell = pStack->sp - n - 1;
+ c = *pCell;
+
+ for (;n > 0; --n, pCell++)
+ {
+ *pCell = pCell[1];
+ }
+
+ *pCell = c;
+ }
+ else
+ {
+ pCell = pStack->sp - 1;
+ c = *pCell;
+
+ for (; n < 0; ++n, pCell--)
+ {
+ *pCell = pCell[-1];
+ }
+
+ *pCell = c;
+ }
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k S e t T o p
+**
+*******************************************************************/
+
+void stackSetTop(FICL_STACK *pStack, CELL c)
+{
+ pStack->sp[-1] = c;
+ return;
+}
+
+
diff --git a/stand/ficl/testmain.c b/stand/ficl/testmain.c
new file mode 100644
index 0000000..7167f30
--- /dev/null
+++ b/stand/ficl/testmain.c
@@ -0,0 +1,345 @@
+/*
+** stub main for testing FICL under userland
+** $Id: testmain.c,v 1.13 2001/12/05 07:21:34 jsadler Exp $
+*/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "ficl.h"
+
+/*
+** Ficl interface to getcwd
+** Prints the current working directory using the VM's
+** textOut method...
+*/
+static void ficlGetCWD(FICL_VM *pVM)
+{
+ char *cp;
+
+ cp = getcwd(NULL, 80);
+ vmTextOut(pVM, cp, 1);
+ free(cp);
+ return;
+}
+
+/*
+** Ficl interface to chdir
+** Gets a newline (or NULL) delimited string from the input
+** and feeds it to chdir()
+** Example:
+** cd c:\tmp
+*/
+static void ficlChDir(FICL_VM *pVM)
+{
+ FICL_STRING *pFS = (FICL_STRING *)pVM->pad;
+ vmGetString(pVM, pFS, '\n');
+ if (pFS->count > 0)
+ {
+ int err = chdir(pFS->text);
+ if (err)
+ {
+ vmTextOut(pVM, "Error: path not found", 1);
+ vmThrow(pVM, VM_QUIT);
+ }
+ }
+ else
+ {
+ vmTextOut(pVM, "Warning (chdir): nothing happened", 1);
+ }
+ return;
+}
+
+/*
+** Ficl interface to system (ANSI)
+** Gets a newline (or NULL) delimited string from the input
+** and feeds it to system()
+** Example:
+** system rm -rf /
+** \ ouch!
+*/
+static void ficlSystem(FICL_VM *pVM)
+{
+ FICL_STRING *pFS = (FICL_STRING *)pVM->pad;
+
+ vmGetString(pVM, pFS, '\n');
+ if (pFS->count > 0)
+ {
+ int err = system(pFS->text);
+ if (err)
+ {
+ sprintf(pVM->pad, "System call returned %d", err);
+ vmTextOut(pVM, pVM->pad, 1);
+ vmThrow(pVM, VM_QUIT);
+ }
+ }
+ else
+ {
+ vmTextOut(pVM, "Warning (system): nothing happened", 1);
+ }
+ return;
+}
+
+/*
+** Ficl add-in to load a text file and execute it...
+** Cheesy, but illustrative.
+** Line oriented... filename is newline (or NULL) delimited.
+** Example:
+** load test.ficl
+*/
+#define nLINEBUF 256
+static void ficlLoad(FICL_VM *pVM)
+{
+ char cp[nLINEBUF];
+ char filename[nLINEBUF];
+ FICL_STRING *pFilename = (FICL_STRING *)filename;
+ int nLine = 0;
+ FILE *fp;
+ int result;
+ CELL id;
+ struct stat buf;
+
+
+ vmGetString(pVM, pFilename, '\n');
+
+ if (pFilename->count <= 0)
+ {
+ vmTextOut(pVM, "Warning (load): nothing happened", 1);
+ return;
+ }
+
+ /*
+ ** get the file's size and make sure it exists
+ */
+ result = stat( pFilename->text, &buf );
+
+ if (result != 0)
+ {
+ vmTextOut(pVM, "Unable to stat file: ", 0);
+ vmTextOut(pVM, pFilename->text, 1);
+ vmThrow(pVM, VM_QUIT);
+ }
+
+ fp = fopen(pFilename->text, "r");
+ if (!fp)
+ {
+ vmTextOut(pVM, "Unable to open file ", 0);
+ vmTextOut(pVM, pFilename->text, 1);
+ vmThrow(pVM, VM_QUIT);
+ }
+
+ id = pVM->sourceID;
+ pVM->sourceID.p = (void *)fp;
+
+ /* feed each line to ficlExec */
+ while (fgets(cp, nLINEBUF, fp))
+ {
+ int len = strlen(cp) - 1;
+
+ nLine++;
+ if (len <= 0)
+ continue;
+
+ result = ficlExecC(pVM, cp, len);
+ if (result != VM_QUIT && result != VM_USEREXIT && result != VM_OUTOFTEXT )
+ {
+ pVM->sourceID = id;
+ fclose(fp);
+ vmThrowErr(pVM, "Error loading file <%s> line %d", pFilename->text, nLine);
+ break;
+ }
+ }
+ /*
+ ** Pass an empty line with SOURCE-ID == -1 to flush
+ ** any pending REFILLs (as required by FILE wordset)
+ */
+ pVM->sourceID.i = -1;
+ ficlExec(pVM, "");
+
+ pVM->sourceID = id;
+ fclose(fp);
+
+ /* handle "bye" in loaded files. --lch */
+ if (result == VM_USEREXIT)
+ vmThrow(pVM, VM_USEREXIT);
+ return;
+}
+
+/*
+** Dump a tab delimited file that summarizes the contents of the
+** dictionary hash table by hashcode...
+*/
+static void spewHash(FICL_VM *pVM)
+{
+ FICL_HASH *pHash = vmGetDict(pVM)->pForthWords;
+ FICL_WORD *pFW;
+ FILE *pOut;
+ unsigned i;
+ unsigned nHash = pHash->size;
+
+ if (!vmGetWordToPad(pVM))
+ vmThrow(pVM, VM_OUTOFTEXT);
+
+ pOut = fopen(pVM->pad, "w");
+ if (!pOut)
+ {
+ vmTextOut(pVM, "unable to open file", 1);
+ return;
+ }
+
+ for (i=0; i < nHash; i++)
+ {
+ int n = 0;
+
+ pFW = pHash->table[i];
+ while (pFW)
+ {
+ n++;
+ pFW = pFW->link;
+ }
+
+ fprintf(pOut, "%d\t%d", i, n);
+
+ pFW = pHash->table[i];
+ while (pFW)
+ {
+ fprintf(pOut, "\t%s", pFW->name);
+ pFW = pFW->link;
+ }
+
+ fprintf(pOut, "\n");
+ }
+
+ fclose(pOut);
+ return;
+}
+
+static void ficlBreak(FICL_VM *pVM)
+{
+ pVM->state = pVM->state;
+ return;
+}
+
+static void ficlClock(FICL_VM *pVM)
+{
+ clock_t now = clock();
+ stackPushUNS(pVM->pStack, (FICL_UNS)now);
+ return;
+}
+
+static void clocksPerSec(FICL_VM *pVM)
+{
+ stackPushUNS(pVM->pStack, CLOCKS_PER_SEC);
+ return;
+}
+
+
+static void execxt(FICL_VM *pVM)
+{
+ FICL_WORD *pFW;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ pFW = stackPopPtr(pVM->pStack);
+ ficlExecXT(pVM, pFW);
+
+ return;
+}
+
+
+void buildTestInterface(FICL_SYSTEM *pSys)
+{
+ ficlBuild(pSys, "break", ficlBreak, FW_DEFAULT);
+ ficlBuild(pSys, "clock", ficlClock, FW_DEFAULT);
+ ficlBuild(pSys, "cd", ficlChDir, FW_DEFAULT);
+ ficlBuild(pSys, "execxt", execxt, FW_DEFAULT);
+ ficlBuild(pSys, "load", ficlLoad, FW_DEFAULT);
+ ficlBuild(pSys, "pwd", ficlGetCWD, FW_DEFAULT);
+ ficlBuild(pSys, "system", ficlSystem, FW_DEFAULT);
+ ficlBuild(pSys, "spewhash", spewHash, FW_DEFAULT);
+ ficlBuild(pSys, "clocks/sec",
+ clocksPerSec, FW_DEFAULT);
+
+ return;
+}
+
+
+int main(int argc, char **argv)
+{
+ char in[256];
+ FICL_VM *pVM;
+ FICL_SYSTEM *pSys;
+
+ pSys = ficlInitSystem(10000);
+ buildTestInterface(pSys);
+ pVM = ficlNewVM(pSys);
+
+ ficlEvaluate(pVM, ".ver .( " __DATE__ " ) cr quit");
+
+ /*
+ ** load file from cmd line...
+ */
+ if (argc > 1)
+ {
+ sprintf(in, ".( loading %s ) cr load %s\n cr", argv[1], argv[1]);
+ ficlEvaluate(pVM, in);
+ }
+
+ for (;;)
+ {
+ int ret;
+ if (fgets(in, sizeof(in) - 1, stdin) == NULL)
+ break;
+ ret = ficlExec(pVM, in);
+ if (ret == VM_USEREXIT)
+ {
+ ficlTermSystem(pSys);
+ break;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/stand/ficl/tools.c b/stand/ficl/tools.c
new file mode 100644
index 0000000..db1e948
--- /dev/null
+++ b/stand/ficl/tools.c
@@ -0,0 +1,918 @@
+/*******************************************************************
+** t o o l s . c
+** Forth Inspired Command Language - programming tools
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 20 June 2000
+** $Id: tools.c,v 1.11 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** 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.
+*/
+
+/*
+** NOTES:
+** SEE needs information about the addresses of functions that
+** are the CFAs of colon definitions, constants, variables, DOES>
+** words, and so on. It gets this information from a table and supporting
+** functions in words.c.
+** colonParen doDoes createParen variableParen userParen constantParen
+**
+** Step and break debugger for Ficl
+** debug ( xt -- ) Start debugging an xt
+** Set a breakpoint
+** Specify breakpoint default action
+*/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdlib.h>
+#include <stdio.h> /* sprintf */
+#include <ctype.h>
+#else
+#include <stand.h>
+#endif
+#include <string.h>
+#include "ficl.h"
+
+
+#if 0
+/*
+** nBREAKPOINTS sizes the breakpoint array. One breakpoint (bp 0) is reserved
+** for the STEP command. The rest are user programmable.
+*/
+#define nBREAKPOINTS 32
+
+#endif
+
+
+/**************************************************************************
+ v m S e t B r e a k
+** Set a breakpoint at the current value of IP by
+** storing that address in a BREAKPOINT record
+**************************************************************************/
+static void vmSetBreak(FICL_VM *pVM, FICL_BREAKPOINT *pBP)
+{
+ FICL_WORD *pStep = ficlLookup(pVM->pSys, "step-break");
+ assert(pStep);
+
+ pBP->address = pVM->ip;
+ pBP->origXT = *pVM->ip;
+ *pVM->ip = pStep;
+}
+
+
+/**************************************************************************
+** d e b u g P r o m p t
+**************************************************************************/
+static void debugPrompt(FICL_VM *pVM)
+{
+ vmTextOut(pVM, "dbg> ", 0);
+}
+
+
+/**************************************************************************
+** i s A F i c l W o r d
+** Vet a candidate pointer carefully to make sure
+** it's not some chunk o' inline data...
+** It has to have a name, and it has to look
+** like it's in the dictionary address range.
+** NOTE: this excludes :noname words!
+**************************************************************************/
+int isAFiclWord(FICL_DICT *pd, FICL_WORD *pFW)
+{
+
+ if (!dictIncludes(pd, pFW))
+ return 0;
+
+ if (!dictIncludes(pd, pFW->name))
+ return 0;
+
+ if ((pFW->link != NULL) && !dictIncludes(pd, pFW->link))
+ return 0;
+
+ if ((pFW->nName <= 0) || (pFW->name[pFW->nName] != '\0'))
+ return 0;
+
+ if (strlen(pFW->name) != pFW->nName)
+ return 0;
+
+ return 1;
+}
+
+
+#if 0
+static int isPrimitive(FICL_WORD *pFW)
+{
+ WORDKIND wk = ficlWordClassify(pFW);
+ return ((wk != COLON) && (wk != DOES));
+}
+#endif
+
+
+/**************************************************************************
+ f i n d E n c l o s i n g W o r d
+** Given a pointer to something, check to make sure it's an address in the
+** dictionary. If so, search backwards until we find something that looks
+** like a dictionary header. If successful, return the address of the
+** FICL_WORD found. Otherwise return NULL.
+** nSEARCH_CELLS sets the maximum neighborhood this func will search before giving up
+**************************************************************************/
+#define nSEARCH_CELLS 100
+
+static FICL_WORD *findEnclosingWord(FICL_VM *pVM, CELL *cp)
+{
+ FICL_WORD *pFW;
+ FICL_DICT *pd = vmGetDict(pVM);
+ int i;
+
+ if (!dictIncludes(pd, (void *)cp))
+ return NULL;
+
+ for (i = nSEARCH_CELLS; i > 0; --i, --cp)
+ {
+ pFW = (FICL_WORD *)(cp + 1 - (sizeof (FICL_WORD) / sizeof (CELL)));
+ if (isAFiclWord(pd, pFW))
+ return pFW;
+ }
+
+ return NULL;
+}
+
+
+/**************************************************************************
+ s e e
+** TOOLS ( "<spaces>name" -- )
+** Display a human-readable representation of the named word's definition.
+** The source of the representation (object-code decompilation, source
+** block, etc.) and the particular form of the display is implementation
+** defined.
+**************************************************************************/
+/*
+** seeColon (for proctologists only)
+** Walks a colon definition, decompiling
+** on the fly. Knows about primitive control structures.
+*/
+static void seeColon(FICL_VM *pVM, CELL *pc)
+{
+ char *cp;
+ CELL *param0 = pc;
+ FICL_DICT *pd = vmGetDict(pVM);
+ FICL_WORD *pSemiParen = ficlLookup(pVM->pSys, "(;)");
+ assert(pSemiParen);
+
+ for (; pc->p != pSemiParen; pc++)
+ {
+ FICL_WORD *pFW = (FICL_WORD *)(pc->p);
+
+ cp = pVM->pad;
+ if ((void *)pc == (void *)pVM->ip)
+ *cp++ = '>';
+ else
+ *cp++ = ' ';
+ cp += sprintf(cp, "%3d ", (int)(pc-param0));
+
+ if (isAFiclWord(pd, pFW))
+ {
+ WORDKIND kind = ficlWordClassify(pFW);
+ CELL c;
+
+ switch (kind)
+ {
+ case LITERAL:
+ c = *++pc;
+ if (isAFiclWord(pd, c.p))
+ {
+ FICL_WORD *pLit = (FICL_WORD *)c.p;
+ sprintf(cp, "%.*s ( %#lx literal )",
+ pLit->nName, pLit->name, (unsigned long)c.u);
+ }
+ else
+ sprintf(cp, "literal %ld (%#lx)",
+ (long)c.i, (unsigned long)c.u);
+ break;
+ case STRINGLIT:
+ {
+ FICL_STRING *sp = (FICL_STRING *)(void *)++pc;
+ pc = (CELL *)alignPtr(sp->text + sp->count + 1) - 1;
+ sprintf(cp, "s\" %.*s\"", sp->count, sp->text);
+ }
+ break;
+ case CSTRINGLIT:
+ {
+ FICL_STRING *sp = (FICL_STRING *)(void *)++pc;
+ pc = (CELL *)alignPtr(sp->text + sp->count + 1) - 1;
+ sprintf(cp, "c\" %.*s\"", sp->count, sp->text);
+ }
+ break;
+ case IF:
+ c = *++pc;
+ if (c.i > 0)
+ sprintf(cp, "if / while (branch %d)", (int)(pc+c.i-param0));
+ else
+ sprintf(cp, "until (branch %d)", (int)(pc+c.i-param0));
+ break;
+ case BRANCH:
+ c = *++pc;
+ if (c.i == 0)
+ sprintf(cp, "repeat (branch %d)", (int)(pc+c.i-param0));
+ else if (c.i == 1)
+ sprintf(cp, "else (branch %d)", (int)(pc+c.i-param0));
+ else
+ sprintf(cp, "endof (branch %d)", (int)(pc+c.i-param0));
+ break;
+
+ case OF:
+ c = *++pc;
+ sprintf(cp, "of (branch %d)", (int)(pc+c.i-param0));
+ break;
+
+ case QDO:
+ c = *++pc;
+ sprintf(cp, "?do (leave %d)", (int)((CELL *)c.p-param0));
+ break;
+ case DO:
+ c = *++pc;
+ sprintf(cp, "do (leave %d)", (int)((CELL *)c.p-param0));
+ break;
+ case LOOP:
+ c = *++pc;
+ sprintf(cp, "loop (branch %d)", (int)(pc+c.i-param0));
+ break;
+ case PLOOP:
+ c = *++pc;
+ sprintf(cp, "+loop (branch %d)", (int)(pc+c.i-param0));
+ break;
+ default:
+ sprintf(cp, "%.*s", pFW->nName, pFW->name);
+ break;
+ }
+
+ }
+ else /* probably not a word - punt and print value */
+ {
+ sprintf(cp, "%ld ( %#lx )", (long)pc->i, (unsigned long)pc->u);
+ }
+
+ vmTextOut(pVM, pVM->pad, 1);
+ }
+
+ vmTextOut(pVM, ";", 1);
+}
+
+/*
+** Here's the outer part of the decompiler. It's
+** just a big nested conditional that checks the
+** CFA of the word to decompile for each kind of
+** known word-builder code, and tries to do
+** something appropriate. If the CFA is not recognized,
+** just indicate that it is a primitive.
+*/
+static void seeXT(FICL_VM *pVM)
+{
+ FICL_WORD *pFW;
+ WORDKIND kind;
+
+ pFW = (FICL_WORD *)stackPopPtr(pVM->pStack);
+ kind = ficlWordClassify(pFW);
+
+ switch (kind)
+ {
+ case COLON:
+ sprintf(pVM->pad, ": %.*s", pFW->nName, pFW->name);
+ vmTextOut(pVM, pVM->pad, 1);
+ seeColon(pVM, pFW->param);
+ break;
+
+ case DOES:
+ vmTextOut(pVM, "does>", 1);
+ seeColon(pVM, (CELL *)pFW->param->p);
+ break;
+
+ case CREATE:
+ vmTextOut(pVM, "create", 1);
+ break;
+
+ case VARIABLE:
+ sprintf(pVM->pad, "variable = %ld (%#lx)",
+ (long)pFW->param->i, (unsigned long)pFW->param->u);
+ vmTextOut(pVM, pVM->pad, 1);
+ break;
+
+#if FICL_WANT_USER
+ case USER:
+ sprintf(pVM->pad, "user variable %ld (%#lx)",
+ (long)pFW->param->i, (unsigned long)pFW->param->u);
+ vmTextOut(pVM, pVM->pad, 1);
+ break;
+#endif
+
+ case CONSTANT:
+ sprintf(pVM->pad, "constant = %ld (%#lx)",
+ (long)pFW->param->i, (unsigned long)pFW->param->u);
+ vmTextOut(pVM, pVM->pad, 1);
+
+ default:
+ sprintf(pVM->pad, "%.*s is a primitive", pFW->nName, pFW->name);
+ vmTextOut(pVM, pVM->pad, 1);
+ break;
+ }
+
+ if (pFW->flags & FW_IMMEDIATE)
+ {
+ vmTextOut(pVM, "immediate", 1);
+ }
+
+ if (pFW->flags & FW_COMPILE)
+ {
+ vmTextOut(pVM, "compile-only", 1);
+ }
+
+ return;
+}
+
+
+static void see(FICL_VM *pVM)
+{
+ ficlTick(pVM);
+ seeXT(pVM);
+ return;
+}
+
+
+/**************************************************************************
+ f i c l D e b u g X T
+** debug ( xt -- )
+** Given an xt of a colon definition or a word defined by DOES>, set the
+** VM up to debug the word: push IP, set the xt as the next thing to execute,
+** set a breakpoint at its first instruction, and run to the breakpoint.
+** Note: the semantics of this word are equivalent to "step in"
+**************************************************************************/
+void ficlDebugXT(FICL_VM *pVM)
+{
+ FICL_WORD *xt = stackPopPtr(pVM->pStack);
+ WORDKIND wk = ficlWordClassify(xt);
+
+ stackPushPtr(pVM->pStack, xt);
+ seeXT(pVM);
+
+ switch (wk)
+ {
+ case COLON:
+ case DOES:
+ /*
+ ** Run the colon code and set a breakpoint at the next instruction
+ */
+ vmExecute(pVM, xt);
+ vmSetBreak(pVM, &(pVM->pSys->bpStep));
+ break;
+
+ default:
+ vmExecute(pVM, xt);
+ break;
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ s t e p I n
+** FICL
+** Execute the next instruction, stepping into it if it's a colon definition
+** or a does> word. This is the easy kind of step.
+**************************************************************************/
+void stepIn(FICL_VM *pVM)
+{
+ /*
+ ** Do one step of the inner loop
+ */
+ {
+ M_VM_STEP(pVM)
+ }
+
+ /*
+ ** Now set a breakpoint at the next instruction
+ */
+ vmSetBreak(pVM, &(pVM->pSys->bpStep));
+
+ return;
+}
+
+
+/**************************************************************************
+ s t e p O v e r
+** FICL
+** Execute the next instruction atomically. This requires some insight into
+** the memory layout of compiled code. Set a breakpoint at the next instruction
+** in this word, and run until we hit it
+**************************************************************************/
+void stepOver(FICL_VM *pVM)
+{
+ FICL_WORD *pFW;
+ WORDKIND kind;
+ FICL_WORD *pStep = ficlLookup(pVM->pSys, "step-break");
+ assert(pStep);
+
+ pFW = *pVM->ip;
+ kind = ficlWordClassify(pFW);
+
+ switch (kind)
+ {
+ case COLON:
+ case DOES:
+ /*
+ ** assume that the next cell holds an instruction
+ ** set a breakpoint there and return to the inner interp
+ */
+ pVM->pSys->bpStep.address = pVM->ip + 1;
+ pVM->pSys->bpStep.origXT = pVM->ip[1];
+ pVM->ip[1] = pStep;
+ break;
+
+ default:
+ stepIn(pVM);
+ break;
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ s t e p - b r e a k
+** FICL
+** Handles breakpoints for stepped execution.
+** Upon entry, bpStep contains the address and replaced instruction
+** of the current breakpoint.
+** Clear the breakpoint
+** Get a command from the console.
+** i (step in) - execute the current instruction and set a new breakpoint
+** at the IP
+** o (step over) - execute the current instruction to completion and set
+** a new breakpoint at the IP
+** g (go) - execute the current instruction and exit
+** q (quit) - abort current word
+** b (toggle breakpoint)
+**************************************************************************/
+void stepBreak(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ FICL_WORD *pFW;
+ FICL_WORD *pOnStep;
+
+ if (!pVM->fRestart)
+ {
+ assert(pVM->pSys->bpStep.address);
+ assert(pVM->pSys->bpStep.origXT);
+ /*
+ ** Clear the breakpoint that caused me to run
+ ** Restore the original instruction at the breakpoint,
+ ** and restore the IP
+ */
+ pVM->ip = (IPTYPE)(pVM->pSys->bpStep.address);
+ *pVM->ip = pVM->pSys->bpStep.origXT;
+
+ /*
+ ** If there's an onStep, do it
+ */
+ pOnStep = ficlLookup(pVM->pSys, "on-step");
+ if (pOnStep)
+ ficlExecXT(pVM, pOnStep);
+
+ /*
+ ** Print the name of the next instruction
+ */
+ pFW = pVM->pSys->bpStep.origXT;
+ sprintf(pVM->pad, "next: %.*s", pFW->nName, pFW->name);
+#if 0
+ if (isPrimitive(pFW))
+ {
+ strcat(pVM->pad, " ( primitive )");
+ }
+#endif
+
+ vmTextOut(pVM, pVM->pad, 1);
+ debugPrompt(pVM);
+ }
+ else
+ {
+ pVM->fRestart = 0;
+ }
+
+ si = vmGetWord(pVM);
+
+ if (!strincmp(si.cp, "i", si.count))
+ {
+ stepIn(pVM);
+ }
+ else if (!strincmp(si.cp, "g", si.count))
+ {
+ return;
+ }
+ else if (!strincmp(si.cp, "l", si.count))
+ {
+ FICL_WORD *xt;
+ xt = findEnclosingWord(pVM, (CELL *)(pVM->ip));
+ if (xt)
+ {
+ stackPushPtr(pVM->pStack, xt);
+ seeXT(pVM);
+ }
+ else
+ {
+ vmTextOut(pVM, "sorry - can't do that", 1);
+ }
+ vmThrow(pVM, VM_RESTART);
+ }
+ else if (!strincmp(si.cp, "o", si.count))
+ {
+ stepOver(pVM);
+ }
+ else if (!strincmp(si.cp, "q", si.count))
+ {
+ ficlTextOut(pVM, FICL_PROMPT, 0);
+ vmThrow(pVM, VM_ABORT);
+ }
+ else if (!strincmp(si.cp, "x", si.count))
+ {
+ /*
+ ** Take whatever's left in the TIB and feed it to a subordinate ficlExec
+ */
+ int ret;
+ char *cp = pVM->tib.cp + pVM->tib.index;
+ int count = pVM->tib.end - cp;
+ FICL_WORD *oldRun = pVM->runningWord;
+
+ ret = ficlExecC(pVM, cp, count);
+
+ if (ret == VM_OUTOFTEXT)
+ {
+ ret = VM_RESTART;
+ pVM->runningWord = oldRun;
+ vmTextOut(pVM, "", 1);
+ }
+
+ vmThrow(pVM, ret);
+ }
+ else
+ {
+ vmTextOut(pVM, "i -- step In", 1);
+ vmTextOut(pVM, "o -- step Over", 1);
+ vmTextOut(pVM, "g -- Go (execute to completion)", 1);
+ vmTextOut(pVM, "l -- List source code", 1);
+ vmTextOut(pVM, "q -- Quit (stop debugging and abort)", 1);
+ vmTextOut(pVM, "x -- eXecute the rest of the line as ficl words", 1);
+ debugPrompt(pVM);
+ vmThrow(pVM, VM_RESTART);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ b y e
+** TOOLS
+** Signal the system to shut down - this causes ficlExec to return
+** VM_USEREXIT. The rest is up to you.
+**************************************************************************/
+static void bye(FICL_VM *pVM)
+{
+ vmThrow(pVM, VM_USEREXIT);
+ return;
+}
+
+
+/**************************************************************************
+ d i s p l a y S t a c k
+** TOOLS
+** Display the parameter stack (code for ".s")
+**************************************************************************/
+static void displayPStack(FICL_VM *pVM)
+{
+ FICL_STACK *pStk = pVM->pStack;
+ int d = stackDepth(pStk);
+ int i;
+ CELL *pCell;
+
+ vmCheckStack(pVM, 0, 0);
+
+ if (d == 0)
+ vmTextOut(pVM, "(Stack Empty) ", 0);
+ else
+ {
+ pCell = pStk->base;
+ for (i = 0; i < d; i++)
+ {
+ vmTextOut(pVM, ltoa((*pCell++).i, pVM->pad, pVM->base), 0);
+ vmTextOut(pVM, " ", 0);
+ }
+ }
+ return;
+}
+
+
+static void displayRStack(FICL_VM *pVM)
+{
+ FICL_STACK *pStk = pVM->rStack;
+ int d = stackDepth(pStk);
+ int i;
+ CELL *pCell;
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ vmCheckStack(pVM, 0, 0);
+
+ if (d == 0)
+ vmTextOut(pVM, "(Stack Empty) ", 0);
+ else
+ {
+ pCell = pStk->base;
+ for (i = 0; i < d; i++)
+ {
+ CELL c = *pCell++;
+ /*
+ ** Attempt to find the word that contains the
+ ** stacked address (as if it is part of a colon definition).
+ ** If this works, print the name of the word. Otherwise print
+ ** the value as a number.
+ */
+ if (dictIncludes(dp, c.p))
+ {
+ FICL_WORD *pFW = findEnclosingWord(pVM, c.p);
+ if (pFW)
+ {
+ int offset = (CELL *)c.p - &pFW->param[0];
+ sprintf(pVM->pad, "%s+%d ", pFW->name, offset);
+ vmTextOut(pVM, pVM->pad, 0);
+ continue; /* no need to print the numeric value */
+ }
+ }
+ vmTextOut(pVM, ltoa(c.i, pVM->pad, pVM->base), 0);
+ vmTextOut(pVM, " ", 0);
+ }
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ f o r g e t - w i d
+**
+**************************************************************************/
+static void forgetWid(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ FICL_HASH *pHash;
+
+ pHash = (FICL_HASH *)stackPopPtr(pVM->pStack);
+ hashForget(pHash, pDict->here);
+
+ return;
+}
+
+
+/**************************************************************************
+ f o r g e t
+** TOOLS EXT ( "<spaces>name" -- )
+** Skip leading space delimiters. Parse name delimited by a space.
+** Find name, then delete name from the dictionary along with all
+** words added to the dictionary after name. An ambiguous
+** condition exists if name cannot be found.
+**
+** If the Search-Order word set is present, FORGET searches the
+** compilation word list. An ambiguous condition exists if the
+** compilation word list is deleted.
+**************************************************************************/
+static void forget(FICL_VM *pVM)
+{
+ void *where;
+ FICL_DICT *pDict = vmGetDict(pVM);
+ FICL_HASH *pHash = pDict->pCompile;
+
+ ficlTick(pVM);
+ where = ((FICL_WORD *)stackPopPtr(pVM->pStack))->name;
+ hashForget(pHash, where);
+ pDict->here = PTRtoCELL where;
+
+ return;
+}
+
+
+/**************************************************************************
+ l i s t W o r d s
+**
+**************************************************************************/
+#define nCOLWIDTH 8
+static void listWords(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_HASH *pHash = dp->pSearch[dp->nLists - 1];
+ FICL_WORD *wp;
+ int nChars = 0;
+ int len;
+ int y = 0;
+ unsigned i;
+ int nWords = 0;
+ char *cp;
+ char *pPad = pVM->pad;
+
+ for (i = 0; i < pHash->size; i++)
+ {
+ for (wp = pHash->table[i]; wp != NULL; wp = wp->link, nWords++)
+ {
+ if (wp->nName == 0) /* ignore :noname defs */
+ continue;
+
+ cp = wp->name;
+ nChars += sprintf(pPad + nChars, "%s", cp);
+
+ if (nChars > 70)
+ {
+ pPad[nChars] = '\0';
+ nChars = 0;
+ y++;
+ if(y>23) {
+ y=0;
+ vmTextOut(pVM, "--- Press Enter to continue ---",0);
+ getchar();
+ vmTextOut(pVM,"\r",0);
+ }
+ vmTextOut(pVM, pPad, 1);
+ }
+ else
+ {
+ len = nCOLWIDTH - nChars % nCOLWIDTH;
+ while (len-- > 0)
+ pPad[nChars++] = ' ';
+ }
+
+ if (nChars > 70)
+ {
+ pPad[nChars] = '\0';
+ nChars = 0;
+ y++;
+ if(y>23) {
+ y=0;
+ vmTextOut(pVM, "--- Press Enter to continue ---",0);
+ getchar();
+ vmTextOut(pVM,"\r",0);
+ }
+ vmTextOut(pVM, pPad, 1);
+ }
+ }
+ }
+
+ if (nChars > 0)
+ {
+ pPad[nChars] = '\0';
+ nChars = 0;
+ vmTextOut(pVM, pPad, 1);
+ }
+
+ sprintf(pVM->pad, "Dictionary: %d words, %ld cells used of %u total",
+ nWords, (long) (dp->here - dp->dict), dp->size);
+ vmTextOut(pVM, pVM->pad, 1);
+ return;
+}
+
+
+/**************************************************************************
+ l i s t E n v
+** Print symbols defined in the environment
+**************************************************************************/
+static void listEnv(FICL_VM *pVM)
+{
+ FICL_DICT *dp = pVM->pSys->envp;
+ FICL_HASH *pHash = dp->pForthWords;
+ FICL_WORD *wp;
+ unsigned i;
+ int nWords = 0;
+
+ for (i = 0; i < pHash->size; i++)
+ {
+ for (wp = pHash->table[i]; wp != NULL; wp = wp->link, nWords++)
+ {
+ vmTextOut(pVM, wp->name, 1);
+ }
+ }
+
+ sprintf(pVM->pad, "Environment: %d words, %ld cells used of %u total",
+ nWords, (long) (dp->here - dp->dict), dp->size);
+ vmTextOut(pVM, pVM->pad, 1);
+ return;
+}
+
+
+/**************************************************************************
+ e n v C o n s t a n t
+** Ficl interface to ficlSetEnv and ficlSetEnvD - allow ficl code to set
+** environment constants...
+**************************************************************************/
+static void envConstant(FICL_VM *pVM)
+{
+ unsigned value;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ vmGetWordToPad(pVM);
+ value = POPUNS();
+ ficlSetEnv(pVM->pSys, pVM->pad, (FICL_UNS)value);
+ return;
+}
+
+static void env2Constant(FICL_VM *pVM)
+{
+ unsigned v1, v2;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+
+ vmGetWordToPad(pVM);
+ v2 = POPUNS();
+ v1 = POPUNS();
+ ficlSetEnvD(pVM->pSys, pVM->pad, v1, v2);
+ return;
+}
+
+
+/**************************************************************************
+ f i c l C o m p i l e T o o l s
+** Builds wordset for debugger and TOOLS optional word set
+**************************************************************************/
+
+void ficlCompileTools(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+ /*
+ ** TOOLS and TOOLS EXT
+ */
+ dictAppendWord(dp, ".s", displayPStack, FW_DEFAULT);
+ dictAppendWord(dp, "bye", bye, FW_DEFAULT);
+ dictAppendWord(dp, "forget", forget, FW_DEFAULT);
+ dictAppendWord(dp, "see", see, FW_DEFAULT);
+ dictAppendWord(dp, "words", listWords, FW_DEFAULT);
+
+ /*
+ ** Set TOOLS environment query values
+ */
+ ficlSetEnv(pSys, "tools", FICL_TRUE);
+ ficlSetEnv(pSys, "tools-ext", FICL_FALSE);
+
+ /*
+ ** Ficl extras
+ */
+ dictAppendWord(dp, "r.s", displayRStack, FW_DEFAULT); /* guy carver */
+ dictAppendWord(dp, ".env", listEnv, FW_DEFAULT);
+ dictAppendWord(dp, "env-constant",
+ envConstant, FW_DEFAULT);
+ dictAppendWord(dp, "env-2constant",
+ env2Constant, FW_DEFAULT);
+ dictAppendWord(dp, "debug-xt", ficlDebugXT, FW_DEFAULT);
+ dictAppendWord(dp, "parse-order",
+ ficlListParseSteps,
+ FW_DEFAULT);
+ dictAppendWord(dp, "step-break",stepBreak, FW_DEFAULT);
+ dictAppendWord(dp, "forget-wid",forgetWid, FW_DEFAULT);
+ dictAppendWord(dp, "see-xt", seeXT, FW_DEFAULT);
+
+ return;
+}
+
diff --git a/stand/ficl/unix.c b/stand/ficl/unix.c
new file mode 100644
index 0000000..5b56440
--- /dev/null
+++ b/stand/ficl/unix.c
@@ -0,0 +1,23 @@
+/* $FreeBSD$ */
+
+#include <string.h>
+#include <netinet/in.h>
+
+#include "ficl.h"
+
+
+
+unsigned long ficlNtohl(unsigned long number)
+{
+ return ntohl(number);
+}
+
+
+
+
+void ficlCompilePlatform(FICL_DICT *dp)
+{
+ return;
+}
+
+
diff --git a/stand/ficl/vm.c b/stand/ficl/vm.c
new file mode 100644
index 0000000..97a4f04
--- /dev/null
+++ b/stand/ficl/vm.c
@@ -0,0 +1,805 @@
+/*******************************************************************
+** v m . c
+** Forth Inspired Command Language - virtual machine methods
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 19 July 1997
+** $Id: vm.c,v 1.13 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** This file implements the virtual machine of FICL. Each virtual
+** machine retains the state of an interpreter. A virtual machine
+** owns a pair of stacks for parameters and return addresses, as
+** well as a pile of state variables and the two dedicated registers
+** of the interp.
+*/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#else
+#include <stand.h>
+#endif
+#include <stdarg.h>
+#include <string.h>
+#include "ficl.h"
+
+static char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+
+/**************************************************************************
+ v m B r a n c h R e l a t i v e
+**
+**************************************************************************/
+void vmBranchRelative(FICL_VM *pVM, int offset)
+{
+ pVM->ip += offset;
+ return;
+}
+
+
+/**************************************************************************
+ v m C r e a t e
+** Creates a virtual machine either from scratch (if pVM is NULL on entry)
+** or by resizing and reinitializing an existing VM to the specified stack
+** sizes.
+**************************************************************************/
+FICL_VM *vmCreate(FICL_VM *pVM, unsigned nPStack, unsigned nRStack)
+{
+ if (pVM == NULL)
+ {
+ pVM = (FICL_VM *)ficlMalloc(sizeof (FICL_VM));
+ assert (pVM);
+ memset(pVM, 0, sizeof (FICL_VM));
+ }
+
+ if (pVM->pStack)
+ stackDelete(pVM->pStack);
+ pVM->pStack = stackCreate(nPStack);
+
+ if (pVM->rStack)
+ stackDelete(pVM->rStack);
+ pVM->rStack = stackCreate(nRStack);
+
+#if FICL_WANT_FLOAT
+ if (pVM->fStack)
+ stackDelete(pVM->fStack);
+ pVM->fStack = stackCreate(nPStack);
+#endif
+
+ pVM->textOut = ficlTextOut;
+
+ vmReset(pVM);
+ return pVM;
+}
+
+
+/**************************************************************************
+ v m D e l e t e
+** Free all memory allocated to the specified VM and its subordinate
+** structures.
+**************************************************************************/
+void vmDelete (FICL_VM *pVM)
+{
+ if (pVM)
+ {
+ ficlFree(pVM->pStack);
+ ficlFree(pVM->rStack);
+#if FICL_WANT_FLOAT
+ ficlFree(pVM->fStack);
+#endif
+ ficlFree(pVM);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ v m E x e c u t e
+** Sets up the specified word to be run by the inner interpreter.
+** Executes the word's code part immediately, but in the case of
+** colon definition, the definition itself needs the inner interp
+** to complete. This does not happen until control reaches ficlExec
+**************************************************************************/
+void vmExecute(FICL_VM *pVM, FICL_WORD *pWord)
+{
+ pVM->runningWord = pWord;
+ pWord->code(pVM);
+ return;
+}
+
+
+/**************************************************************************
+ v m I n n e r L o o p
+** the mysterious inner interpreter...
+** This loop is the address interpreter that makes colon definitions
+** work. Upon entry, it assumes that the IP points to an entry in
+** a definition (the body of a colon word). It runs one word at a time
+** until something does vmThrow. The catcher for this is expected to exist
+** in the calling code.
+** vmThrow gets you out of this loop with a longjmp()
+** Visual C++ 5 chokes on this loop in Release mode. Aargh.
+**************************************************************************/
+#if INLINE_INNER_LOOP == 0
+void vmInnerLoop(FICL_VM *pVM)
+{
+ M_INNER_LOOP(pVM);
+}
+#endif
+#if 0
+/*
+** Recast inner loop that inlines tokens for control structures, arithmetic and stack operations,
+** as well as create does> : ; and various literals
+*/
+typedef enum
+{
+ PATCH = 0,
+ L0,
+ L1,
+ L2,
+ LMINUS1,
+ LMINUS2,
+ DROP,
+ SWAP,
+ DUP,
+ PICK,
+ ROLL,
+ FETCH,
+ STORE,
+ BRANCH,
+ CBRANCH,
+ LEAVE,
+ TO_R,
+ R_FROM,
+ EXIT;
+} OPCODE;
+
+typedef CELL *IPTYPE;
+
+void vmInnerLoop(FICL_VM *pVM)
+{
+ IPTYPE ip = pVM->ip;
+ FICL_STACK *pStack = pVM->pStack;
+
+ for (;;)
+ {
+ OPCODE o = (*ip++).i;
+ CELL c;
+ switch (o)
+ {
+ case L0:
+ stackPushINT(pStack, 0);
+ break;
+ case L1:
+ stackPushINT(pStack, 1);
+ break;
+ case L2:
+ stackPushINT(pStack, 2);
+ break;
+ case LMINUS1:
+ stackPushINT(pStack, -1);
+ break;
+ case LMINUS2:
+ stackPushINT(pStack, -2);
+ break;
+ case DROP:
+ stackDrop(pStack, 1);
+ break;
+ case SWAP:
+ stackRoll(pStack, 1);
+ break;
+ case DUP:
+ stackPick(pStack, 0);
+ break;
+ case PICK:
+ c = *ip++;
+ stackPick(pStack, c.i);
+ break;
+ case ROLL:
+ c = *ip++;
+ stackRoll(pStack, c.i);
+ break;
+ case EXIT:
+ return;
+ }
+ }
+
+ return;
+}
+#endif
+
+
+
+/**************************************************************************
+ v m G e t D i c t
+** Returns the address dictionary for this VM's system
+**************************************************************************/
+FICL_DICT *vmGetDict(FICL_VM *pVM)
+{
+ assert(pVM);
+ return pVM->pSys->dp;
+}
+
+
+/**************************************************************************
+ v m G e t S t r i n g
+** Parses a string out of the VM input buffer and copies up to the first
+** FICL_STRING_MAX characters to the supplied destination buffer, a
+** FICL_STRING. The destination string is NULL terminated.
+**
+** Returns the address of the first unused character in the dest buffer.
+**************************************************************************/
+char *vmGetString(FICL_VM *pVM, FICL_STRING *spDest, char delimiter)
+{
+ STRINGINFO si = vmParseStringEx(pVM, delimiter, 0);
+
+ if (SI_COUNT(si) > FICL_STRING_MAX)
+ {
+ SI_SETLEN(si, FICL_STRING_MAX);
+ }
+
+ strncpy(spDest->text, SI_PTR(si), SI_COUNT(si));
+ spDest->text[SI_COUNT(si)] = '\0';
+ spDest->count = (FICL_COUNT)SI_COUNT(si);
+
+ return spDest->text + SI_COUNT(si) + 1;
+}
+
+
+/**************************************************************************
+ v m G e t W o r d
+** vmGetWord calls vmGetWord0 repeatedly until it gets a string with
+** non-zero length.
+**************************************************************************/
+STRINGINFO vmGetWord(FICL_VM *pVM)
+{
+ STRINGINFO si = vmGetWord0(pVM);
+
+ if (SI_COUNT(si) == 0)
+ {
+ vmThrow(pVM, VM_RESTART);
+ }
+
+ return si;
+}
+
+
+/**************************************************************************
+ v m G e t W o r d 0
+** Skip leading whitespace and parse a space delimited word from the tib.
+** Returns the start address and length of the word. Updates the tib
+** to reflect characters consumed, including the trailing delimiter.
+** If there's nothing of interest in the tib, returns zero. This function
+** does not use vmParseString because it uses isspace() rather than a
+** single delimiter character.
+**************************************************************************/
+STRINGINFO vmGetWord0(FICL_VM *pVM)
+{
+ char *pSrc = vmGetInBuf(pVM);
+ char *pEnd = vmGetInBufEnd(pVM);
+ STRINGINFO si;
+ FICL_UNS count = 0;
+ char ch = 0;
+
+ pSrc = skipSpace(pSrc, pEnd);
+ SI_SETPTR(si, pSrc);
+
+/*
+ for (ch = *pSrc; (pEnd != pSrc) && !isspace(ch); ch = *++pSrc)
+ {
+ count++;
+ }
+*/
+
+ /* Changed to make Purify happier. --lch */
+ for (;;)
+ {
+ if (pEnd == pSrc)
+ break;
+ ch = *pSrc;
+ if (isspace(ch))
+ break;
+ count++;
+ pSrc++;
+ }
+
+ SI_SETLEN(si, count);
+
+ if ((pEnd != pSrc) && isspace(ch)) /* skip one trailing delimiter */
+ pSrc++;
+
+ vmUpdateTib(pVM, pSrc);
+
+ return si;
+}
+
+
+/**************************************************************************
+ v m G e t W o r d T o P a d
+** Does vmGetWord and copies the result to the pad as a NULL terminated
+** string. Returns the length of the string. If the string is too long
+** to fit in the pad, it is truncated.
+**************************************************************************/
+int vmGetWordToPad(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ char *cp = (char *)pVM->pad;
+ si = vmGetWord(pVM);
+
+ if (SI_COUNT(si) > nPAD)
+ SI_SETLEN(si, nPAD);
+
+ strncpy(cp, SI_PTR(si), SI_COUNT(si));
+ cp[SI_COUNT(si)] = '\0';
+ return (int)(SI_COUNT(si));
+}
+
+
+/**************************************************************************
+ v m P a r s e S t r i n g
+** Parses a string out of the input buffer using the delimiter
+** specified. Skips leading delimiters, marks the start of the string,
+** and counts characters to the next delimiter it encounters. It then
+** updates the vm input buffer to consume all these chars, including the
+** trailing delimiter.
+** Returns the address and length of the parsed string, not including the
+** trailing delimiter.
+**************************************************************************/
+STRINGINFO vmParseString(FICL_VM *pVM, char delim)
+{
+ return vmParseStringEx(pVM, delim, 1);
+}
+
+STRINGINFO vmParseStringEx(FICL_VM *pVM, char delim, char fSkipLeading)
+{
+ STRINGINFO si;
+ char *pSrc = vmGetInBuf(pVM);
+ char *pEnd = vmGetInBufEnd(pVM);
+ char ch;
+
+ if (fSkipLeading)
+ { /* skip lead delimiters */
+ while ((pSrc != pEnd) && (*pSrc == delim))
+ pSrc++;
+ }
+
+ SI_SETPTR(si, pSrc); /* mark start of text */
+
+ for (ch = *pSrc; (pSrc != pEnd)
+ && (ch != delim)
+ && (ch != '\r')
+ && (ch != '\n'); ch = *++pSrc)
+ {
+ ; /* find next delimiter or end of line */
+ }
+
+ /* set length of result */
+ SI_SETLEN(si, pSrc - SI_PTR(si));
+
+ if ((pSrc != pEnd) && (*pSrc == delim)) /* gobble trailing delimiter */
+ pSrc++;
+
+ vmUpdateTib(pVM, pSrc);
+ return si;
+}
+
+
+/**************************************************************************
+ v m P o p
+**
+**************************************************************************/
+CELL vmPop(FICL_VM *pVM)
+{
+ return stackPop(pVM->pStack);
+}
+
+
+/**************************************************************************
+ v m P u s h
+**
+**************************************************************************/
+void vmPush(FICL_VM *pVM, CELL c)
+{
+ stackPush(pVM->pStack, c);
+ return;
+}
+
+
+/**************************************************************************
+ v m P o p I P
+**
+**************************************************************************/
+void vmPopIP(FICL_VM *pVM)
+{
+ pVM->ip = (IPTYPE)(stackPopPtr(pVM->rStack));
+ return;
+}
+
+
+/**************************************************************************
+ v m P u s h I P
+**
+**************************************************************************/
+void vmPushIP(FICL_VM *pVM, IPTYPE newIP)
+{
+ stackPushPtr(pVM->rStack, (void *)pVM->ip);
+ pVM->ip = newIP;
+ return;
+}
+
+
+/**************************************************************************
+ v m P u s h T i b
+** Binds the specified input string to the VM and clears >IN (the index)
+**************************************************************************/
+void vmPushTib(FICL_VM *pVM, char *text, FICL_INT nChars, TIB *pSaveTib)
+{
+ if (pSaveTib)
+ {
+ *pSaveTib = pVM->tib;
+ }
+
+ pVM->tib.cp = text;
+ pVM->tib.end = text + nChars;
+ pVM->tib.index = 0;
+}
+
+
+void vmPopTib(FICL_VM *pVM, TIB *pTib)
+{
+ if (pTib)
+ {
+ pVM->tib = *pTib;
+ }
+ return;
+}
+
+
+/**************************************************************************
+ v m Q u i t
+**
+**************************************************************************/
+void vmQuit(FICL_VM *pVM)
+{
+ stackReset(pVM->rStack);
+ pVM->fRestart = 0;
+ pVM->ip = NULL;
+ pVM->runningWord = NULL;
+ pVM->state = INTERPRET;
+ pVM->tib.cp = NULL;
+ pVM->tib.end = NULL;
+ pVM->tib.index = 0;
+ pVM->pad[0] = '\0';
+ pVM->sourceID.i = 0;
+ return;
+}
+
+
+/**************************************************************************
+ v m R e s e t
+**
+**************************************************************************/
+void vmReset(FICL_VM *pVM)
+{
+ vmQuit(pVM);
+ stackReset(pVM->pStack);
+#if FICL_WANT_FLOAT
+ stackReset(pVM->fStack);
+#endif
+ pVM->base = 10;
+ return;
+}
+
+
+/**************************************************************************
+ v m S e t T e x t O u t
+** Binds the specified output callback to the vm. If you pass NULL,
+** binds the default output function (ficlTextOut)
+**************************************************************************/
+void vmSetTextOut(FICL_VM *pVM, OUTFUNC textOut)
+{
+ if (textOut)
+ pVM->textOut = textOut;
+ else
+ pVM->textOut = ficlTextOut;
+
+ return;
+}
+
+
+/**************************************************************************
+ v m T e x t O u t
+** Feeds text to the vm's output callback
+**************************************************************************/
+void vmTextOut(FICL_VM *pVM, char *text, int fNewline)
+{
+ assert(pVM);
+ assert(pVM->textOut);
+ (pVM->textOut)(pVM, text, fNewline);
+
+ return;
+}
+
+
+/**************************************************************************
+ v m T h r o w
+**
+**************************************************************************/
+void vmThrow(FICL_VM *pVM, int except)
+{
+ if (pVM->pState)
+ longjmp(*(pVM->pState), except);
+}
+
+
+void vmThrowErr(FICL_VM *pVM, char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ vsprintf(pVM->pad, fmt, va);
+ vmTextOut(pVM, pVM->pad, 1);
+ va_end(va);
+ longjmp(*(pVM->pState), VM_ERREXIT);
+}
+
+
+/**************************************************************************
+ w o r d I s I m m e d i a t e
+**
+**************************************************************************/
+int wordIsImmediate(FICL_WORD *pFW)
+{
+ return ((pFW != NULL) && (pFW->flags & FW_IMMEDIATE));
+}
+
+
+/**************************************************************************
+ w o r d I s C o m p i l e O n l y
+**
+**************************************************************************/
+int wordIsCompileOnly(FICL_WORD *pFW)
+{
+ return ((pFW != NULL) && (pFW->flags & FW_COMPILE));
+}
+
+
+/**************************************************************************
+ s t r r e v
+**
+**************************************************************************/
+char *strrev( char *string )
+{ /* reverse a string in-place */
+ int i = strlen(string);
+ char *p1 = string; /* first char of string */
+ char *p2 = string + i - 1; /* last non-NULL char of string */
+ char c;
+
+ if (i > 1)
+ {
+ while (p1 < p2)
+ {
+ c = *p2;
+ *p2 = *p1;
+ *p1 = c;
+ p1++; p2--;
+ }
+ }
+
+ return string;
+}
+
+
+/**************************************************************************
+ d i g i t _ t o _ c h a r
+**
+**************************************************************************/
+char digit_to_char(int value)
+{
+ return digits[value];
+}
+
+
+/**************************************************************************
+ i s P o w e r O f T w o
+** Tests whether supplied argument is an integer power of 2 (2**n)
+** where 32 > n > 1, and returns n if so. Otherwise returns zero.
+**************************************************************************/
+int isPowerOfTwo(FICL_UNS u)
+{
+ int i = 1;
+ FICL_UNS t = 2;
+
+ for (; ((t <= u) && (t != 0)); i++, t <<= 1)
+ {
+ if (u == t)
+ return i;
+ }
+
+ return 0;
+}
+
+
+/**************************************************************************
+ l t o a
+**
+**************************************************************************/
+char *ltoa( FICL_INT value, char *string, int radix )
+{ /* convert long to string, any base */
+ char *cp = string;
+ int sign = ((radix == 10) && (value < 0));
+ int pwr;
+
+ assert(radix > 1);
+ assert(radix < 37);
+ assert(string);
+
+ pwr = isPowerOfTwo((FICL_UNS)radix);
+
+ if (sign)
+ value = -value;
+
+ if (value == 0)
+ *cp++ = '0';
+ else if (pwr != 0)
+ {
+ FICL_UNS v = (FICL_UNS) value;
+ FICL_UNS mask = (FICL_UNS) ~(-1 << pwr);
+ while (v)
+ {
+ *cp++ = digits[v & mask];
+ v >>= pwr;
+ }
+ }
+ else
+ {
+ UNSQR result;
+ DPUNS v;
+ v.hi = 0;
+ v.lo = (FICL_UNS)value;
+ while (v.lo)
+ {
+ result = ficlLongDiv(v, (FICL_UNS)radix);
+ *cp++ = digits[result.rem];
+ v.lo = result.quot;
+ }
+ }
+
+ if (sign)
+ *cp++ = '-';
+
+ *cp++ = '\0';
+
+ return strrev(string);
+}
+
+
+/**************************************************************************
+ u l t o a
+**
+**************************************************************************/
+char *ultoa(FICL_UNS value, char *string, int radix )
+{ /* convert long to string, any base */
+ char *cp = string;
+ DPUNS ud;
+ UNSQR result;
+
+ assert(radix > 1);
+ assert(radix < 37);
+ assert(string);
+
+ if (value == 0)
+ *cp++ = '0';
+ else
+ {
+ ud.hi = 0;
+ ud.lo = value;
+ result.quot = value;
+
+ while (ud.lo)
+ {
+ result = ficlLongDiv(ud, (FICL_UNS)radix);
+ ud.lo = result.quot;
+ *cp++ = digits[result.rem];
+ }
+ }
+
+ *cp++ = '\0';
+
+ return strrev(string);
+}
+
+
+/**************************************************************************
+ c a s e F o l d
+** Case folds a NULL terminated string in place. All characters
+** get converted to lower case.
+**************************************************************************/
+char *caseFold(char *cp)
+{
+ char *oldCp = cp;
+
+ while (*cp)
+ {
+ if (isupper(*cp))
+ *cp = (char)tolower(*cp);
+ cp++;
+ }
+
+ return oldCp;
+}
+
+
+/**************************************************************************
+ s t r i n c m p
+** (jws) simplified the code a bit in hopes of appeasing Purify
+**************************************************************************/
+int strincmp(char *cp1, char *cp2, FICL_UNS count)
+{
+ int i = 0;
+
+ for (; 0 < count; ++cp1, ++cp2, --count)
+ {
+ i = tolower(*cp1) - tolower(*cp2);
+ if (i != 0)
+ return i;
+ else if (*cp1 == '\0')
+ return 0;
+ }
+ return 0;
+}
+
+/**************************************************************************
+ s k i p S p a c e
+** Given a string pointer, returns a pointer to the first non-space
+** char of the string, or to the NULL terminator if no such char found.
+** If the pointer reaches "end" first, stop there. Pass NULL to
+** suppress this behavior.
+**************************************************************************/
+char *skipSpace(char *cp, char *end)
+{
+ assert(cp);
+
+ while ((cp != end) && isspace(*cp))
+ cp++;
+
+ return cp;
+}
+
+
diff --git a/stand/ficl/words.c b/stand/ficl/words.c
new file mode 100644
index 0000000..eee6fe9
--- /dev/null
+++ b/stand/ficl/words.c
@@ -0,0 +1,5208 @@
+/*******************************************************************
+** w o r d s . c
+** Forth Inspired Command Language
+** ANS Forth CORE word-set written in C
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 19 July 1997
+** $Id: words.c,v 1.17 2001/12/05 07:21:34 jsadler Exp $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* $FreeBSD$ */
+
+#ifdef TESTMAIN
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#else
+#include <stand.h>
+#endif
+#include <string.h>
+#include "ficl.h"
+#include "math64.h"
+
+static void colonParen(FICL_VM *pVM);
+static void literalIm(FICL_VM *pVM);
+static int ficlParseWord(FICL_VM *pVM, STRINGINFO si);
+
+/*
+** Control structure building words use these
+** strings' addresses as markers on the stack to
+** check for structure completion.
+*/
+static char doTag[] = "do";
+static char colonTag[] = "colon";
+static char leaveTag[] = "leave";
+
+static char destTag[] = "target";
+static char origTag[] = "origin";
+
+static char caseTag[] = "case";
+static char ofTag[] = "of";
+static char fallthroughTag[] = "fallthrough";
+
+#if FICL_WANT_LOCALS
+static void doLocalIm(FICL_VM *pVM);
+static void do2LocalIm(FICL_VM *pVM);
+#endif
+
+
+/*
+** C O N T R O L S T R U C T U R E B U I L D E R S
+**
+** Push current dict location for later branch resolution.
+** The location may be either a branch target or a patch address...
+*/
+static void markBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag)
+{
+ PUSHPTR(dp->here);
+ PUSHPTR(tag);
+ return;
+}
+
+static void markControlTag(FICL_VM *pVM, char *tag)
+{
+ PUSHPTR(tag);
+ return;
+}
+
+static void matchControlTag(FICL_VM *pVM, char *tag)
+{
+ char *cp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ cp = (char *)stackPopPtr(pVM->pStack);
+ /*
+ ** Changed the code below to compare the pointers first (by popular demand)
+ */
+ if ( (cp != tag) && strcmp(cp, tag) )
+ {
+ vmThrowErr(pVM, "Error -- unmatched control structure \"%s\"", tag);
+ }
+
+ return;
+}
+
+/*
+** Expect a branch target address on the param stack,
+** compile a literal offset from the current dict location
+** to the target address
+*/
+static void resolveBackBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag)
+{
+ FICL_INT offset;
+ CELL *patchAddr;
+
+ matchControlTag(pVM, tag);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ offset = patchAddr - dp->here;
+ dictAppendCell(dp, LVALUEtoCELL(offset));
+
+ return;
+}
+
+
+/*
+** Expect a branch patch address on the param stack,
+** compile a literal offset from the patch location
+** to the current dict location
+*/
+static void resolveForwardBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag)
+{
+ FICL_INT offset;
+ CELL *patchAddr;
+
+ matchControlTag(pVM, tag);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ offset = dp->here - patchAddr;
+ *patchAddr = LVALUEtoCELL(offset);
+
+ return;
+}
+
+/*
+** Match the tag to the top of the stack. If success,
+** sopy "here" address into the cell whose address is next
+** on the stack. Used by do..leave..loop.
+*/
+static void resolveAbsBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag)
+{
+ CELL *patchAddr;
+ char *cp;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ cp = stackPopPtr(pVM->pStack);
+ /*
+ ** Changed the comparison below to compare the pointers first (by popular demand)
+ */
+ if ((cp != tag) && strcmp(cp, tag))
+ {
+ vmTextOut(pVM, "Warning -- Unmatched control word: ", 0);
+ vmTextOut(pVM, tag, 1);
+ }
+
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ *patchAddr = LVALUEtoCELL(dp->here);
+
+ return;
+}
+
+
+/**************************************************************************
+ f i c l P a r s e N u m b e r
+** Attempts to convert the NULL terminated string in the VM's pad to
+** a number using the VM's current base. If successful, pushes the number
+** onto the param stack and returns TRUE. Otherwise, returns FALSE.
+** (jws 8/01) Trailing decimal point causes a zero cell to be pushed. (See
+** the standard for DOUBLE wordset.
+**************************************************************************/
+
+int ficlParseNumber(FICL_VM *pVM, STRINGINFO si)
+{
+ FICL_INT accum = 0;
+ char isNeg = FALSE;
+ char hasDP = FALSE;
+ unsigned base = pVM->base;
+ char *cp = SI_PTR(si);
+ FICL_COUNT count= (FICL_COUNT)SI_COUNT(si);
+ unsigned ch;
+ unsigned digit;
+
+ if (count > 1)
+ {
+ switch (*cp)
+ {
+ case '-':
+ cp++;
+ count--;
+ isNeg = TRUE;
+ break;
+ case '+':
+ cp++;
+ count--;
+ isNeg = FALSE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((count > 0) && (cp[count-1] == '.')) /* detect & remove trailing decimal */
+ {
+ hasDP = TRUE;
+ count--;
+ }
+
+ if (count == 0) /* detect "+", "-", ".", "+." etc */
+ return FALSE;
+
+ while ((count--) && ((ch = *cp++) != '\0'))
+ {
+ if (!isalnum(ch))
+ return FALSE;
+
+ digit = ch - '0';
+
+ if (digit > 9)
+ digit = tolower(ch) - 'a' + 10;
+
+ if (digit >= base)
+ return FALSE;
+
+ accum = accum * base + digit;
+ }
+
+ if (hasDP) /* simple (required) DOUBLE support */
+ PUSHINT(0);
+
+ if (isNeg)
+ accum = -accum;
+
+ PUSHINT(accum);
+ if (pVM->state == COMPILE)
+ literalIm(pVM);
+
+ return TRUE;
+}
+
+
+/**************************************************************************
+ a d d & f r i e n d s
+**
+**************************************************************************/
+
+static void add(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ i = stackPopINT(pVM->pStack);
+ i += stackGetTop(pVM->pStack).i;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void sub(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ i = stackPopINT(pVM->pStack);
+ i = stackGetTop(pVM->pStack).i - i;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void mul(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ i = stackPopINT(pVM->pStack);
+ i *= stackGetTop(pVM->pStack).i;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void negate(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ i = -stackPopINT(pVM->pStack);
+ PUSHINT(i);
+ return;
+}
+
+static void ficlDiv(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ i = stackPopINT(pVM->pStack);
+ i = stackGetTop(pVM->pStack).i / i;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+/*
+** slash-mod CORE ( n1 n2 -- n3 n4 )
+** Divide n1 by n2, giving the single-cell remainder n3 and the single-cell
+** quotient n4. An ambiguous condition exists if n2 is zero. If n1 and n2
+** differ in sign, the implementation-defined result returned will be the
+** same as that returned by either the phrase
+** >R S>D R> FM/MOD or the phrase >R S>D R> SM/REM .
+** NOTE: Ficl complies with the second phrase (symmetric division)
+*/
+static void slashMod(FICL_VM *pVM)
+{
+ DPINT n1;
+ FICL_INT n2;
+ INTQR qr;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+ n2 = stackPopINT(pVM->pStack);
+ n1.lo = stackPopINT(pVM->pStack);
+ i64Extend(n1);
+
+ qr = m64SymmetricDivI(n1, n2);
+ PUSHINT(qr.rem);
+ PUSHINT(qr.quot);
+ return;
+}
+
+static void onePlus(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ i = stackGetTop(pVM->pStack).i;
+ i += 1;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void oneMinus(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ i = stackGetTop(pVM->pStack).i;
+ i -= 1;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void twoMul(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ i = stackGetTop(pVM->pStack).i;
+ i *= 2;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void twoDiv(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ i = stackGetTop(pVM->pStack).i;
+ i >>= 1;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void mulDiv(FICL_VM *pVM)
+{
+ FICL_INT x, y, z;
+ DPINT prod;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 1);
+#endif
+ z = stackPopINT(pVM->pStack);
+ y = stackPopINT(pVM->pStack);
+ x = stackPopINT(pVM->pStack);
+
+ prod = m64MulI(x,y);
+ x = m64SymmetricDivI(prod, z).quot;
+
+ PUSHINT(x);
+ return;
+}
+
+
+static void mulDivRem(FICL_VM *pVM)
+{
+ FICL_INT x, y, z;
+ DPINT prod;
+ INTQR qr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 2);
+#endif
+ z = stackPopINT(pVM->pStack);
+ y = stackPopINT(pVM->pStack);
+ x = stackPopINT(pVM->pStack);
+
+ prod = m64MulI(x,y);
+ qr = m64SymmetricDivI(prod, z);
+
+ PUSHINT(qr.rem);
+ PUSHINT(qr.quot);
+ return;
+}
+
+
+/**************************************************************************
+ c o l o n d e f i n i t i o n s
+** Code to begin compiling a colon definition
+** This function sets the state to COMPILE, then creates a
+** new word whose name is the next word in the input stream
+** and whose code is colonParen.
+**************************************************************************/
+
+static void colon(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+ dictCheckThreshold(dp);
+
+ pVM->state = COMPILE;
+ markControlTag(pVM, colonTag);
+ dictAppendWord2(dp, si, colonParen, FW_DEFAULT | FW_SMUDGE);
+#if FICL_WANT_LOCALS
+ pVM->pSys->nLocals = 0;
+#endif
+ return;
+}
+
+
+/**************************************************************************
+ c o l o n P a r e n
+** This is the code that executes a colon definition. It assumes that the
+** virtual machine is running a "next" loop (See the vm.c
+** for its implementation of member function vmExecute()). The colon
+** code simply copies the address of the first word in the list of words
+** to interpret into IP after saving its old value. When we return to the
+** "next" loop, the virtual machine will call the code for each word in
+** turn.
+**
+**************************************************************************/
+
+static void colonParen(FICL_VM *pVM)
+{
+ IPTYPE tempIP = (IPTYPE) (pVM->runningWord->param);
+ vmPushIP(pVM, tempIP);
+
+ return;
+}
+
+
+/**************************************************************************
+ s e m i c o l o n C o I m
+**
+** IMMEDIATE code for ";". This function sets the state to INTERPRET and
+** terminates a word under compilation by appending code for "(;)" to
+** the definition. TO DO: checks for leftover branch target tags on the
+** return stack and complains if any are found.
+**************************************************************************/
+static void semiParen(FICL_VM *pVM)
+{
+ vmPopIP(pVM);
+ return;
+}
+
+
+static void semicolonCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pSemiParen);
+ matchControlTag(pVM, colonTag);
+
+#if FICL_WANT_LOCALS
+ assert(pVM->pSys->pUnLinkParen);
+ if (pVM->pSys->nLocals > 0)
+ {
+ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys);
+ dictEmpty(pLoc, pLoc->pForthWords->size);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pUnLinkParen));
+ }
+ pVM->pSys->nLocals = 0;
+#endif
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pSemiParen));
+ pVM->state = INTERPRET;
+ dictUnsmudge(dp);
+ return;
+}
+
+
+/**************************************************************************
+ e x i t
+** CORE
+** This function simply pops the previous instruction
+** pointer and returns to the "next" loop. Used for exiting from within
+** a definition. Note that exitParen is identical to semiParen - they
+** are in two different functions so that "see" can correctly identify
+** the end of a colon definition, even if it uses "exit".
+**************************************************************************/
+static void exitParen(FICL_VM *pVM)
+{
+ vmPopIP(pVM);
+ return;
+}
+
+static void exitCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ assert(pVM->pSys->pExitParen);
+ IGNORE(pVM);
+
+#if FICL_WANT_LOCALS
+ if (pVM->pSys->nLocals > 0)
+ {
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pUnLinkParen));
+ }
+#endif
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pExitParen));
+ return;
+}
+
+
+/**************************************************************************
+ c o n s t a n t P a r e n
+** This is the run-time code for "constant". It simply returns the
+** contents of its word's first data cell.
+**
+**************************************************************************/
+
+void constantParen(FICL_VM *pVM)
+{
+ FICL_WORD *pFW = pVM->runningWord;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+ stackPush(pVM->pStack, pFW->param[0]);
+ return;
+}
+
+void twoConstParen(FICL_VM *pVM)
+{
+ FICL_WORD *pFW = pVM->runningWord;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+ stackPush(pVM->pStack, pFW->param[0]); /* lo */
+ stackPush(pVM->pStack, pFW->param[1]); /* hi */
+ return;
+}
+
+
+/**************************************************************************
+ c o n s t a n t
+** IMMEDIATE
+** Compiles a constant into the dictionary. Constants return their
+** value when invoked. Expects a value on top of the parm stack.
+**************************************************************************/
+
+static void constant(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ dictAppendWord2(dp, si, constantParen, FW_DEFAULT);
+ dictAppendCell(dp, stackPop(pVM->pStack));
+ return;
+}
+
+
+static void twoConstant(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ c = stackPop(pVM->pStack);
+ dictAppendWord2(dp, si, twoConstParen, FW_DEFAULT);
+ dictAppendCell(dp, stackPop(pVM->pStack));
+ dictAppendCell(dp, c);
+ return;
+}
+
+
+/**************************************************************************
+ d i s p l a y C e l l
+** Drop and print the contents of the cell at the top of the param
+** stack
+**************************************************************************/
+
+static void displayCell(FICL_VM *pVM)
+{
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ c = stackPop(pVM->pStack);
+ ltoa((c).i, pVM->pad, pVM->base);
+ strcat(pVM->pad, " ");
+ vmTextOut(pVM, pVM->pad, 0);
+ return;
+}
+
+static void uDot(FICL_VM *pVM)
+{
+ FICL_UNS u;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ u = stackPopUNS(pVM->pStack);
+ ultoa(u, pVM->pad, pVM->base);
+ strcat(pVM->pad, " ");
+ vmTextOut(pVM, pVM->pad, 0);
+ return;
+}
+
+
+static void hexDot(FICL_VM *pVM)
+{
+ FICL_UNS u;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ u = stackPopUNS(pVM->pStack);
+ ultoa(u, pVM->pad, 16);
+ strcat(pVM->pad, " ");
+ vmTextOut(pVM, pVM->pad, 0);
+ return;
+}
+
+
+/**************************************************************************
+ s t r l e n
+** FICL ( c-string -- length )
+**
+** Returns the length of a C-style (zero-terminated) string.
+**
+** --lch
+**/
+static void ficlStrlen(FICL_VM *ficlVM)
+ {
+ char *address = (char *)stackPopPtr(ficlVM->pStack);
+ stackPushINT(ficlVM->pStack, strlen(address));
+ }
+
+
+/**************************************************************************
+ s p r i n t f
+** FICL ( i*x c-addr-fmt u-fmt c-addr-buffer u-buffer -- c-addr-buffer u-written success-flag )
+** Similar to the C sprintf() function. It formats into a buffer based on
+** a "format" string. Each character in the format string is copied verbatim
+** to the output buffer, until SPRINTF encounters a percent sign ("%").
+** SPRINTF then skips the percent sign, and examines the next character
+** (the "format character"). Here are the valid format characters:
+** s - read a C-ADDR U-LENGTH string from the stack and copy it to
+** the buffer
+** d - read a cell from the stack, format it as a string (base-10,
+** signed), and copy it to the buffer
+** x - same as d, except in base-16
+** u - same as d, but unsigned
+** % - output a literal percent-sign to the buffer
+** SPRINTF returns the c-addr-buffer argument unchanged, the number of bytes
+** written, and a flag indicating whether or not it ran out of space while
+** writing to the output buffer (TRUE if it ran out of space).
+**
+** If SPRINTF runs out of space in the buffer to store the formatted string,
+** it still continues parsing, in an effort to preserve your stack (otherwise
+** it might leave uneaten arguments behind).
+**
+** --lch
+**************************************************************************/
+static void ficlSprintf(FICL_VM *pVM) /* */
+{
+ int bufferLength = stackPopINT(pVM->pStack);
+ char *buffer = (char *)stackPopPtr(pVM->pStack);
+ char *bufferStart = buffer;
+
+ int formatLength = stackPopINT(pVM->pStack);
+ char *format = (char *)stackPopPtr(pVM->pStack);
+ char *formatStop = format + formatLength;
+
+ int base = 10;
+ int unsignedInteger = FALSE;
+
+ FICL_INT append = FICL_TRUE;
+
+ while (format < formatStop)
+ {
+ char scratch[64];
+ char *source;
+ int actualLength;
+ int desiredLength;
+ int leadingZeroes;
+
+
+ if (*format != '%')
+ {
+ source = format;
+ actualLength = desiredLength = 1;
+ leadingZeroes = 0;
+ }
+ else
+ {
+ format++;
+ if (format == formatStop)
+ break;
+
+ leadingZeroes = (*format == '0');
+ if (leadingZeroes)
+ {
+ format++;
+ if (format == formatStop)
+ break;
+ }
+
+ desiredLength = isdigit(*format);
+ if (desiredLength)
+ {
+ desiredLength = strtol(format, &format, 10);
+ if (format == formatStop)
+ break;
+ }
+ else if (*format == '*')
+ {
+ desiredLength = stackPopINT(pVM->pStack);
+ format++;
+ if (format == formatStop)
+ break;
+ }
+
+
+ switch (*format)
+ {
+ case 's':
+ case 'S':
+ {
+ actualLength = stackPopINT(pVM->pStack);
+ source = (char *)stackPopPtr(pVM->pStack);
+ break;
+ }
+ case 'x':
+ case 'X':
+ base = 16;
+ case 'u':
+ case 'U':
+ unsignedInteger = TRUE;
+ case 'd':
+ case 'D':
+ {
+ int integer = stackPopINT(pVM->pStack);
+ if (unsignedInteger)
+ ultoa(integer, scratch, base);
+ else
+ ltoa(integer, scratch, base);
+ base = 10;
+ unsignedInteger = FALSE;
+ source = scratch;
+ actualLength = strlen(scratch);
+ break;
+ }
+ case '%':
+ source = format;
+ actualLength = 1;
+ default:
+ continue;
+ }
+ }
+
+ if (append != FICL_FALSE)
+ {
+ if (!desiredLength)
+ desiredLength = actualLength;
+ if (desiredLength > bufferLength)
+ {
+ append = FICL_FALSE;
+ desiredLength = bufferLength;
+ }
+ while (desiredLength > actualLength)
+ {
+ *buffer++ = (char)((leadingZeroes) ? '0' : ' ');
+ bufferLength--;
+ desiredLength--;
+ }
+ memcpy(buffer, source, actualLength);
+ buffer += actualLength;
+ bufferLength -= actualLength;
+ }
+
+ format++;
+ }
+
+ stackPushPtr(pVM->pStack, bufferStart);
+ stackPushINT(pVM->pStack, buffer - bufferStart);
+ stackPushINT(pVM->pStack, append);
+}
+
+
+/**************************************************************************
+ d u p & f r i e n d s
+**
+**************************************************************************/
+
+static void depth(FICL_VM *pVM)
+{
+ int i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+ i = stackDepth(pVM->pStack);
+ PUSHINT(i);
+ return;
+}
+
+
+static void drop(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ stackDrop(pVM->pStack, 1);
+ return;
+}
+
+
+static void twoDrop(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ stackDrop(pVM->pStack, 2);
+ return;
+}
+
+
+static void dup(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 2);
+#endif
+ stackPick(pVM->pStack, 0);
+ return;
+}
+
+
+static void twoDup(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 4);
+#endif
+ stackPick(pVM->pStack, 1);
+ stackPick(pVM->pStack, 1);
+ return;
+}
+
+
+static void over(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 3);
+#endif
+ stackPick(pVM->pStack, 1);
+ return;
+}
+
+static void twoOver(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 6);
+#endif
+ stackPick(pVM->pStack, 3);
+ stackPick(pVM->pStack, 3);
+ return;
+}
+
+
+static void pick(FICL_VM *pVM)
+{
+ CELL c = stackPop(pVM->pStack);
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, c.i+1, c.i+2);
+#endif
+ stackPick(pVM->pStack, c.i);
+ return;
+}
+
+
+static void questionDup(FICL_VM *pVM)
+{
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 2);
+#endif
+ c = stackGetTop(pVM->pStack);
+
+ if (c.i != 0)
+ stackPick(pVM->pStack, 0);
+
+ return;
+}
+
+
+static void roll(FICL_VM *pVM)
+{
+ int i = stackPop(pVM->pStack).i;
+ i = (i > 0) ? i : 0;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, i+1, i+1);
+#endif
+ stackRoll(pVM->pStack, i);
+ return;
+}
+
+
+static void minusRoll(FICL_VM *pVM)
+{
+ int i = stackPop(pVM->pStack).i;
+ i = (i > 0) ? i : 0;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, i+1, i+1);
+#endif
+ stackRoll(pVM->pStack, -i);
+ return;
+}
+
+
+static void rot(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 3);
+#endif
+ stackRoll(pVM->pStack, 2);
+ return;
+}
+
+
+static void swap(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+ stackRoll(pVM->pStack, 1);
+ return;
+}
+
+
+static void twoSwap(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 4);
+#endif
+ stackRoll(pVM->pStack, 3);
+ stackRoll(pVM->pStack, 3);
+ return;
+}
+
+
+/**************************************************************************
+ e m i t & f r i e n d s
+**
+**************************************************************************/
+
+static void emit(FICL_VM *pVM)
+{
+ char *cp = pVM->pad;
+ int i;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ i = stackPopINT(pVM->pStack);
+ cp[0] = (char)i;
+ cp[1] = '\0';
+ vmTextOut(pVM, cp, 0);
+ return;
+}
+
+
+static void cr(FICL_VM *pVM)
+{
+ vmTextOut(pVM, "", 1);
+ return;
+}
+
+
+static void commentLine(FICL_VM *pVM)
+{
+ char *cp = vmGetInBuf(pVM);
+ char *pEnd = vmGetInBufEnd(pVM);
+ char ch = *cp;
+
+ while ((cp != pEnd) && (ch != '\r') && (ch != '\n'))
+ {
+ ch = *++cp;
+ }
+
+ /*
+ ** Cope with DOS or UNIX-style EOLs -
+ ** Check for /r, /n, /r/n, or /n/r end-of-line sequences,
+ ** and point cp to next char. If EOL is \0, we're done.
+ */
+ if (cp != pEnd)
+ {
+ cp++;
+
+ if ( (cp != pEnd) && (ch != *cp)
+ && ((*cp == '\r') || (*cp == '\n')) )
+ cp++;
+ }
+
+ vmUpdateTib(pVM, cp);
+ return;
+}
+
+
+/*
+** paren CORE
+** Compilation: Perform the execution semantics given below.
+** Execution: ( "ccc<paren>" -- )
+** Parse ccc delimited by ) (right parenthesis). ( is an immediate word.
+** The number of characters in ccc may be zero to the number of characters
+** in the parse area.
+**
+*/
+static void commentHang(FICL_VM *pVM)
+{
+ vmParseStringEx(pVM, ')', 0);
+ return;
+}
+
+
+/**************************************************************************
+ F E T C H & S T O R E
+**
+**************************************************************************/
+
+static void fetch(FICL_VM *pVM)
+{
+ CELL *pCell;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ pCell = (CELL *)stackPopPtr(pVM->pStack);
+ stackPush(pVM->pStack, *pCell);
+ return;
+}
+
+/*
+** two-fetch CORE ( a-addr -- x1 x2 )
+** Fetch the cell pair x1 x2 stored at a-addr. x2 is stored at a-addr and
+** x1 at the next consecutive cell. It is equivalent to the sequence
+** DUP CELL+ @ SWAP @ .
+*/
+static void twoFetch(FICL_VM *pVM)
+{
+ CELL *pCell;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 2);
+#endif
+ pCell = (CELL *)stackPopPtr(pVM->pStack);
+ stackPush(pVM->pStack, *pCell++);
+ stackPush(pVM->pStack, *pCell);
+ swap(pVM);
+ return;
+}
+
+/*
+** store CORE ( x a-addr -- )
+** Store x at a-addr.
+*/
+static void store(FICL_VM *pVM)
+{
+ CELL *pCell;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ pCell = (CELL *)stackPopPtr(pVM->pStack);
+ *pCell = stackPop(pVM->pStack);
+}
+
+/*
+** two-store CORE ( x1 x2 a-addr -- )
+** Store the cell pair x1 x2 at a-addr, with x2 at a-addr and x1 at the
+** next consecutive cell. It is equivalent to the sequence
+** SWAP OVER ! CELL+ ! .
+*/
+static void twoStore(FICL_VM *pVM)
+{
+ CELL *pCell;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 0);
+#endif
+ pCell = (CELL *)stackPopPtr(pVM->pStack);
+ *pCell++ = stackPop(pVM->pStack);
+ *pCell = stackPop(pVM->pStack);
+}
+
+static void plusStore(FICL_VM *pVM)
+{
+ CELL *pCell;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ pCell = (CELL *)stackPopPtr(pVM->pStack);
+ pCell->i += stackPop(pVM->pStack).i;
+}
+
+
+static void quadFetch(FICL_VM *pVM)
+{
+ UNS32 *pw;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ pw = (UNS32 *)stackPopPtr(pVM->pStack);
+ PUSHUNS((FICL_UNS)*pw);
+ return;
+}
+
+static void quadStore(FICL_VM *pVM)
+{
+ UNS32 *pw;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ pw = (UNS32 *)stackPopPtr(pVM->pStack);
+ *pw = (UNS32)(stackPop(pVM->pStack).u);
+}
+
+static void wFetch(FICL_VM *pVM)
+{
+ UNS16 *pw;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ pw = (UNS16 *)stackPopPtr(pVM->pStack);
+ PUSHUNS((FICL_UNS)*pw);
+ return;
+}
+
+static void wStore(FICL_VM *pVM)
+{
+ UNS16 *pw;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ pw = (UNS16 *)stackPopPtr(pVM->pStack);
+ *pw = (UNS16)(stackPop(pVM->pStack).u);
+}
+
+static void cFetch(FICL_VM *pVM)
+{
+ UNS8 *pc;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ pc = (UNS8 *)stackPopPtr(pVM->pStack);
+ PUSHUNS((FICL_UNS)*pc);
+ return;
+}
+
+static void cStore(FICL_VM *pVM)
+{
+ UNS8 *pc;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ pc = (UNS8 *)stackPopPtr(pVM->pStack);
+ *pc = (UNS8)(stackPop(pVM->pStack).u);
+}
+
+
+/**************************************************************************
+ b r a n c h P a r e n
+**
+** Runtime for "(branch)" -- expects a literal offset in the next
+** compilation address, and branches to that location.
+**************************************************************************/
+
+static void branchParen(FICL_VM *pVM)
+{
+ vmBranchRelative(pVM, (uintptr_t)*(pVM->ip));
+ return;
+}
+
+
+/**************************************************************************
+ b r a n c h 0
+** Runtime code for "(branch0)"; pop a flag from the stack,
+** branch if 0. fall through otherwise. The heart of "if" and "until".
+**************************************************************************/
+
+static void branch0(FICL_VM *pVM)
+{
+ FICL_UNS flag;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ flag = stackPopUNS(pVM->pStack);
+
+ if (flag)
+ { /* fall through */
+ vmBranchRelative(pVM, 1);
+ }
+ else
+ { /* take branch (to else/endif/begin) */
+ vmBranchRelative(pVM, (uintptr_t)*(pVM->ip));
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ i f C o I m
+** IMMEDIATE COMPILE-ONLY
+** Compiles code for a conditional branch into the dictionary
+** and pushes the branch patch address on the stack for later
+** patching by ELSE or THEN/ENDIF.
+**************************************************************************/
+
+static void ifCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranch0);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranch0));
+ markBranch(dp, pVM, origTag);
+ dictAppendUNS(dp, 1);
+ return;
+}
+
+
+/**************************************************************************
+ e l s e C o I m
+**
+** IMMEDIATE COMPILE-ONLY
+** compiles an "else"...
+** 1) Compile a branch and a patch address; the address gets patched
+** by "endif" to point past the "else" code.
+** 2) Pop the "if" patch address
+** 3) Patch the "if" branch to point to the current compile address.
+** 4) Push the "else" patch address. ("endif" patches this to jump past
+** the "else" code.
+**************************************************************************/
+
+static void elseCoIm(FICL_VM *pVM)
+{
+ CELL *patchAddr;
+ FICL_INT offset;
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranchParen);
+ /* (1) compile branch runtime */
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen));
+ matchControlTag(pVM, origTag);
+ patchAddr =
+ (CELL *)stackPopPtr(pVM->pStack); /* (2) pop "if" patch addr */
+ markBranch(dp, pVM, origTag); /* (4) push "else" patch addr */
+ dictAppendUNS(dp, 1); /* (1) compile patch placeholder */
+ offset = dp->here - patchAddr;
+ *patchAddr = LVALUEtoCELL(offset); /* (3) Patch "if" */
+
+ return;
+}
+
+
+/**************************************************************************
+ e n d i f C o I m
+** IMMEDIATE COMPILE-ONLY
+**************************************************************************/
+
+static void endifCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ resolveForwardBranch(dp, pVM, origTag);
+ return;
+}
+
+
+/**************************************************************************
+ c a s e C o I m
+** IMMEDIATE COMPILE-ONLY
+**
+**
+** At compile-time, a CASE-SYS (see DPANS94 6.2.0873) looks like this:
+** i*addr i caseTag
+** and an OF-SYS (see DPANS94 6.2.1950) looks like this:
+** i*addr i caseTag addr ofTag
+** The integer under caseTag is the count of fixup addresses that branch
+** to ENDCASE.
+**************************************************************************/
+
+static void caseCoIm(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+
+ PUSHUNS(0);
+ markControlTag(pVM, caseTag);
+ return;
+}
+
+
+/**************************************************************************
+ e n d c a s eC o I m
+** IMMEDIATE COMPILE-ONLY
+**************************************************************************/
+
+static void endcaseCoIm(FICL_VM *pVM)
+{
+ FICL_UNS fixupCount;
+ FICL_DICT *dp;
+ CELL *patchAddr;
+ FICL_INT offset;
+
+ assert(pVM->pSys->pDrop);
+
+ /*
+ ** if the last OF ended with FALLTHROUGH,
+ ** just add the FALLTHROUGH fixup to the
+ ** ENDOF fixups
+ */
+ if (stackGetTop(pVM->pStack).p == fallthroughTag)
+ {
+ matchControlTag(pVM, fallthroughTag);
+ patchAddr = POPPTR();
+ matchControlTag(pVM, caseTag);
+ fixupCount = POPUNS();
+ PUSHPTR(patchAddr);
+ PUSHUNS(fixupCount + 1);
+ markControlTag(pVM, caseTag);
+ }
+
+ matchControlTag(pVM, caseTag);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ fixupCount = POPUNS();
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, fixupCount, 0);
+#endif
+
+ dp = vmGetDict(pVM);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pDrop));
+
+ while (fixupCount--)
+ {
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ offset = dp->here - patchAddr;
+ *patchAddr = LVALUEtoCELL(offset);
+ }
+ return;
+}
+
+
+static void ofParen(FICL_VM *pVM)
+{
+ FICL_UNS a, b;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+
+ a = POPUNS();
+ b = stackGetTop(pVM->pStack).u;
+
+ if (a == b)
+ { /* fall through */
+ stackDrop(pVM->pStack, 1);
+ vmBranchRelative(pVM, 1);
+ }
+ else
+ { /* take branch to next of or endswitch */
+ vmBranchRelative(pVM, *(int *)(pVM->ip));
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ o f C o I m
+** IMMEDIATE COMPILE-ONLY
+**************************************************************************/
+
+static void ofCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ CELL *fallthroughFixup = NULL;
+
+ assert(pVM->pSys->pBranch0);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 3);
+#endif
+
+ if (stackGetTop(pVM->pStack).p == fallthroughTag)
+ {
+ matchControlTag(pVM, fallthroughTag);
+ fallthroughFixup = POPPTR();
+ }
+
+ matchControlTag(pVM, caseTag);
+
+ markControlTag(pVM, caseTag);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pOfParen));
+ markBranch(dp, pVM, ofTag);
+ dictAppendUNS(dp, 2);
+
+ if (fallthroughFixup != NULL)
+ {
+ FICL_INT offset = dp->here - fallthroughFixup;
+ *fallthroughFixup = LVALUEtoCELL(offset);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ e n d o f C o I m
+** IMMEDIATE COMPILE-ONLY
+**************************************************************************/
+
+static void endofCoIm(FICL_VM *pVM)
+{
+ CELL *patchAddr;
+ FICL_UNS fixupCount;
+ FICL_INT offset;
+ FICL_DICT *dp = vmGetDict(pVM);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 3);
+#endif
+
+ assert(pVM->pSys->pBranchParen);
+
+ /* ensure we're in an OF, */
+ matchControlTag(pVM, ofTag);
+ /* grab the address of the branch location after the OF */
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ /* ensure we're also in a "case" */
+ matchControlTag(pVM, caseTag);
+ /* grab the current number of ENDOF fixups */
+ fixupCount = POPUNS();
+
+ /* compile branch runtime */
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen));
+
+ /* push a new ENDOF fixup, the updated count of ENDOF fixups, and the caseTag */
+ PUSHPTR(dp->here);
+ PUSHUNS(fixupCount + 1);
+ markControlTag(pVM, caseTag);
+
+ /* reserve space for the ENDOF fixup */
+ dictAppendUNS(dp, 2);
+
+ /* and patch the original OF */
+ offset = dp->here - patchAddr;
+ *patchAddr = LVALUEtoCELL(offset);
+}
+
+
+/**************************************************************************
+ f a l l t h r o u g h C o I m
+** IMMEDIATE COMPILE-ONLY
+**************************************************************************/
+
+static void fallthroughCoIm(FICL_VM *pVM)
+{
+ CELL *patchAddr;
+ FICL_INT offset;
+ FICL_DICT *dp = vmGetDict(pVM);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 3);
+#endif
+
+ /* ensure we're in an OF, */
+ matchControlTag(pVM, ofTag);
+ /* grab the address of the branch location after the OF */
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ /* ensure we're also in a "case" */
+ matchControlTag(pVM, caseTag);
+
+ /* okay, here we go. put the case tag back. */
+ markControlTag(pVM, caseTag);
+
+ /* compile branch runtime */
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen));
+
+ /* push a new FALLTHROUGH fixup and the fallthroughTag */
+ PUSHPTR(dp->here);
+ markControlTag(pVM, fallthroughTag);
+
+ /* reserve space for the FALLTHROUGH fixup */
+ dictAppendUNS(dp, 2);
+
+ /* and patch the original OF */
+ offset = dp->here - patchAddr;
+ *patchAddr = LVALUEtoCELL(offset);
+}
+
+/**************************************************************************
+ h a s h
+** hash ( c-addr u -- code)
+** calculates hashcode of specified string and leaves it on the stack
+**************************************************************************/
+
+static void hash(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ SI_SETLEN(si, stackPopUNS(pVM->pStack));
+ SI_SETPTR(si, stackPopPtr(pVM->pStack));
+ PUSHUNS(hashHashCode(si));
+ return;
+}
+
+
+/**************************************************************************
+ i n t e r p r e t
+** This is the "user interface" of a Forth. It does the following:
+** while there are words in the VM's Text Input Buffer
+** Copy next word into the pad (vmGetWord)
+** Attempt to find the word in the dictionary (dictLookup)
+** If successful, execute the word.
+** Otherwise, attempt to convert the word to a number (isNumber)
+** If successful, push the number onto the parameter stack.
+** Otherwise, print an error message and exit loop...
+** End Loop
+**
+** From the standard, section 3.4
+** Text interpretation (see 6.1.1360 EVALUATE and 6.1.2050 QUIT) shall
+** repeat the following steps until either the parse area is empty or an
+** ambiguous condition exists:
+** a) Skip leading spaces and parse a name (see 3.4.1);
+**************************************************************************/
+
+static void interpret(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ int i;
+ FICL_SYSTEM *pSys;
+
+ assert(pVM);
+
+ pSys = pVM->pSys;
+ si = vmGetWord0(pVM);
+
+ /*
+ ** Get next word...if out of text, we're done.
+ */
+ if (si.count == 0)
+ {
+ vmThrow(pVM, VM_OUTOFTEXT);
+ }
+
+ /*
+ ** Attempt to find the incoming token in the dictionary. If that fails...
+ ** run the parse chain against the incoming token until somebody eats it.
+ ** Otherwise emit an error message and give up.
+ ** Although ficlParseWord could be part of the parse list, I've hard coded it
+ ** in for robustness. ficlInitSystem adds the other default steps to the list.
+ */
+ if (ficlParseWord(pVM, si))
+ return;
+
+ for (i=0; i < FICL_MAX_PARSE_STEPS; i++)
+ {
+ FICL_WORD *pFW = pSys->parseList[i];
+
+ if (pFW == NULL)
+ break;
+
+ if (pFW->code == parseStepParen)
+ {
+ FICL_PARSE_STEP pStep;
+ pStep = (FICL_PARSE_STEP)(pFW->param->fn);
+ if ((*pStep)(pVM, si))
+ return;
+ }
+ else
+ {
+ stackPushPtr(pVM->pStack, SI_PTR(si));
+ stackPushUNS(pVM->pStack, SI_COUNT(si));
+ ficlExecXT(pVM, pFW);
+ if (stackPopINT(pVM->pStack))
+ return;
+ }
+ }
+
+ i = SI_COUNT(si);
+ vmThrowErr(pVM, "%.*s not found", i, SI_PTR(si));
+
+ return; /* back to inner interpreter */
+}
+
+
+/**************************************************************************
+ f i c l P a r s e W o r d
+** From the standard, section 3.4
+** b) Search the dictionary name space (see 3.4.2). If a definition name
+** matching the string is found:
+** 1.if interpreting, perform the interpretation semantics of the definition
+** (see 3.4.3.2), and continue at a);
+** 2.if compiling, perform the compilation semantics of the definition
+** (see 3.4.3.3), and continue at a).
+**
+** c) If a definition name matching the string is not found, attempt to
+** convert the string to a number (see 3.4.1.3). If successful:
+** 1.if interpreting, place the number on the data stack, and continue at a);
+** 2.if compiling, compile code that when executed will place the number on
+** the stack (see 6.1.1780 LITERAL), and continue at a);
+**
+** d) If unsuccessful, an ambiguous condition exists (see 3.4.4).
+**
+** (jws 4/01) Modified to be a FICL_PARSE_STEP
+**************************************************************************/
+static int ficlParseWord(FICL_VM *pVM, STRINGINFO si)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *tempFW;
+
+#if FICL_ROBUST
+ dictCheck(dp, pVM, 0);
+ vmCheckStack(pVM, 0, 0);
+#endif
+
+#if FICL_WANT_LOCALS
+ if (pVM->pSys->nLocals > 0)
+ {
+ tempFW = ficlLookupLoc(pVM->pSys, si);
+ }
+ else
+#endif
+ tempFW = dictLookup(dp, si);
+
+ if (pVM->state == INTERPRET)
+ {
+ if (tempFW != NULL)
+ {
+ if (wordIsCompileOnly(tempFW))
+ {
+ vmThrowErr(pVM, "Error: Compile only!");
+ }
+
+ vmExecute(pVM, tempFW);
+ return (int)FICL_TRUE;
+ }
+ }
+
+ else /* (pVM->state == COMPILE) */
+ {
+ if (tempFW != NULL)
+ {
+ if (wordIsImmediate(tempFW))
+ {
+ vmExecute(pVM, tempFW);
+ }
+ else
+ {
+ dictAppendCell(dp, LVALUEtoCELL(tempFW));
+ }
+ return (int)FICL_TRUE;
+ }
+ }
+
+ return FICL_FALSE;
+}
+
+
+/*
+** Surrogate precompiled parse step for ficlParseWord (this step is hard coded in
+** INTERPRET)
+*/
+static void lookup(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ SI_SETLEN(si, stackPopUNS(pVM->pStack));
+ SI_SETPTR(si, stackPopPtr(pVM->pStack));
+ stackPushINT(pVM->pStack, ficlParseWord(pVM, si));
+ return;
+}
+
+
+/**************************************************************************
+ p a r e n P a r s e S t e p
+** (parse-step) ( c-addr u -- flag )
+** runtime for a precompiled parse step - pop a counted string off the
+** stack, run the parse step against it, and push the result flag (FICL_TRUE
+** if success, FICL_FALSE otherwise).
+**************************************************************************/
+
+void parseStepParen(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ FICL_WORD *pFW = pVM->runningWord;
+ FICL_PARSE_STEP pStep = (FICL_PARSE_STEP)(pFW->param->fn);
+
+ SI_SETLEN(si, stackPopINT(pVM->pStack));
+ SI_SETPTR(si, stackPopPtr(pVM->pStack));
+
+ PUSHINT((*pStep)(pVM, si));
+
+ return;
+}
+
+
+static void addParseStep(FICL_VM *pVM)
+{
+ FICL_WORD *pStep;
+ FICL_DICT *pd = vmGetDict(pVM);
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ pStep = (FICL_WORD *)(stackPop(pVM->pStack).p);
+ if ((pStep != NULL) && isAFiclWord(pd, pStep))
+ ficlAddParseStep(pVM->pSys, pStep);
+ return;
+}
+
+
+/**************************************************************************
+ l i t e r a l P a r e n
+**
+** This is the runtime for (literal). It assumes that it is part of a colon
+** definition, and that the next CELL contains a value to be pushed on the
+** parameter stack at runtime. This code is compiled by "literal".
+**
+**************************************************************************/
+
+static void literalParen(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+ PUSHINT(*(FICL_INT *)(pVM->ip));
+ vmBranchRelative(pVM, 1);
+ return;
+}
+
+static void twoLitParen(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+ PUSHINT(*((FICL_INT *)(pVM->ip)+1));
+ PUSHINT(*(FICL_INT *)(pVM->ip));
+ vmBranchRelative(pVM, 2);
+ return;
+}
+
+
+/**************************************************************************
+ l i t e r a l I m
+**
+** IMMEDIATE code for "literal". This function gets a value from the stack
+** and compiles it into the dictionary preceded by the code for "(literal)".
+** IMMEDIATE
+**************************************************************************/
+
+static void literalIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ assert(pVM->pSys->pLitParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pLitParen));
+ dictAppendCell(dp, stackPop(pVM->pStack));
+
+ return;
+}
+
+
+static void twoLiteralIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ assert(pVM->pSys->pTwoLitParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pTwoLitParen));
+ dictAppendCell(dp, stackPop(pVM->pStack));
+ dictAppendCell(dp, stackPop(pVM->pStack));
+
+ return;
+}
+
+/**************************************************************************
+ l o g i c a n d c o m p a r i s o n s
+**
+**************************************************************************/
+
+static void zeroEquals(FICL_VM *pVM)
+{
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ c.i = FICL_BOOL(stackPopINT(pVM->pStack) == 0);
+ stackPush(pVM->pStack, c);
+ return;
+}
+
+static void zeroLess(FICL_VM *pVM)
+{
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ c.i = FICL_BOOL(stackPopINT(pVM->pStack) < 0);
+ stackPush(pVM->pStack, c);
+ return;
+}
+
+static void zeroGreater(FICL_VM *pVM)
+{
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ c.i = FICL_BOOL(stackPopINT(pVM->pStack) > 0);
+ stackPush(pVM->pStack, c);
+ return;
+}
+
+static void isEqual(FICL_VM *pVM)
+{
+ CELL x, y;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ x = stackPop(pVM->pStack);
+ y = stackPop(pVM->pStack);
+ PUSHINT(FICL_BOOL(x.i == y.i));
+ return;
+}
+
+static void isLess(FICL_VM *pVM)
+{
+ CELL x, y;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ y = stackPop(pVM->pStack);
+ x = stackPop(pVM->pStack);
+ PUSHINT(FICL_BOOL(x.i < y.i));
+ return;
+}
+
+static void uIsLess(FICL_VM *pVM)
+{
+ FICL_UNS u1, u2;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ u2 = stackPopUNS(pVM->pStack);
+ u1 = stackPopUNS(pVM->pStack);
+ PUSHINT(FICL_BOOL(u1 < u2));
+ return;
+}
+
+static void isGreater(FICL_VM *pVM)
+{
+ CELL x, y;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ y = stackPop(pVM->pStack);
+ x = stackPop(pVM->pStack);
+ PUSHINT(FICL_BOOL(x.i > y.i));
+ return;
+}
+
+static void bitwiseAnd(FICL_VM *pVM)
+{
+ CELL x, y;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ x = stackPop(pVM->pStack);
+ y = stackPop(pVM->pStack);
+ PUSHINT(x.i & y.i);
+ return;
+}
+
+static void bitwiseOr(FICL_VM *pVM)
+{
+ CELL x, y;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ x = stackPop(pVM->pStack);
+ y = stackPop(pVM->pStack);
+ PUSHINT(x.i | y.i);
+ return;
+}
+
+static void bitwiseXor(FICL_VM *pVM)
+{
+ CELL x, y;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ x = stackPop(pVM->pStack);
+ y = stackPop(pVM->pStack);
+ PUSHINT(x.i ^ y.i);
+ return;
+}
+
+static void bitwiseNot(FICL_VM *pVM)
+{
+ CELL x;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ x = stackPop(pVM->pStack);
+ PUSHINT(~x.i);
+ return;
+}
+
+
+/**************************************************************************
+ D o / L o o p
+** do -- IMMEDIATE COMPILE ONLY
+** Compiles code to initialize a loop: compile (do),
+** allot space to hold the "leave" address, push a branch
+** target address for the loop.
+** (do) -- runtime for "do"
+** pops index and limit from the p stack and moves them
+** to the r stack, then skips to the loop body.
+** loop -- IMMEDIATE COMPILE ONLY
+** +loop
+** Compiles code for the test part of a loop:
+** compile (loop), resolve forward branch from "do", and
+** copy "here" address to the "leave" address allotted by "do"
+** i,j,k -- COMPILE ONLY
+** Runtime: Push loop indices on param stack (i is innermost loop...)
+** Note: each loop has three values on the return stack:
+** ( R: leave limit index )
+** "leave" is the absolute address of the next cell after the loop
+** limit and index are the loop control variables.
+** leave -- COMPILE ONLY
+** Runtime: pop the loop control variables, then pop the
+** "leave" address and jump (absolute) there.
+**************************************************************************/
+
+static void doCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pDoParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pDoParen));
+ /*
+ ** Allot space for a pointer to the end
+ ** of the loop - "leave" uses this...
+ */
+ markBranch(dp, pVM, leaveTag);
+ dictAppendUNS(dp, 0);
+ /*
+ ** Mark location of head of loop...
+ */
+ markBranch(dp, pVM, doTag);
+
+ return;
+}
+
+
+static void doParen(FICL_VM *pVM)
+{
+ CELL index, limit;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ index = stackPop(pVM->pStack);
+ limit = stackPop(pVM->pStack);
+
+ /* copy "leave" target addr to stack */
+ stackPushPtr(pVM->rStack, *(pVM->ip++));
+ stackPush(pVM->rStack, limit);
+ stackPush(pVM->rStack, index);
+
+ return;
+}
+
+
+static void qDoCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pQDoParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pQDoParen));
+ /*
+ ** Allot space for a pointer to the end
+ ** of the loop - "leave" uses this...
+ */
+ markBranch(dp, pVM, leaveTag);
+ dictAppendUNS(dp, 0);
+ /*
+ ** Mark location of head of loop...
+ */
+ markBranch(dp, pVM, doTag);
+
+ return;
+}
+
+
+static void qDoParen(FICL_VM *pVM)
+{
+ CELL index, limit;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ index = stackPop(pVM->pStack);
+ limit = stackPop(pVM->pStack);
+
+ /* copy "leave" target addr to stack */
+ stackPushPtr(pVM->rStack, *(pVM->ip++));
+
+ if (limit.u == index.u)
+ {
+ vmPopIP(pVM);
+ }
+ else
+ {
+ stackPush(pVM->rStack, limit);
+ stackPush(pVM->rStack, index);
+ }
+
+ return;
+}
+
+
+/*
+** Runtime code to break out of a do..loop construct
+** Drop the loop control variables; the branch address
+** past "loop" is next on the return stack.
+*/
+static void leaveCo(FICL_VM *pVM)
+{
+ /* almost unloop */
+ stackDrop(pVM->rStack, 2);
+ /* exit */
+ vmPopIP(pVM);
+ return;
+}
+
+
+static void unloopCo(FICL_VM *pVM)
+{
+ stackDrop(pVM->rStack, 3);
+ return;
+}
+
+
+static void loopCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pLoopParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pLoopParen));
+ resolveBackBranch(dp, pVM, doTag);
+ resolveAbsBranch(dp, pVM, leaveTag);
+ return;
+}
+
+
+static void plusLoopCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pPLoopParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pPLoopParen));
+ resolveBackBranch(dp, pVM, doTag);
+ resolveAbsBranch(dp, pVM, leaveTag);
+ return;
+}
+
+
+static void loopParen(FICL_VM *pVM)
+{
+ FICL_INT index = stackGetTop(pVM->rStack).i;
+ FICL_INT limit = stackFetch(pVM->rStack, 1).i;
+
+ index++;
+
+ if (index >= limit)
+ {
+ stackDrop(pVM->rStack, 3); /* nuke the loop indices & "leave" addr */
+ vmBranchRelative(pVM, 1); /* fall through the loop */
+ }
+ else
+ { /* update index, branch to loop head */
+ stackSetTop(pVM->rStack, LVALUEtoCELL(index));
+ vmBranchRelative(pVM, (uintptr_t)*(pVM->ip));
+ }
+
+ return;
+}
+
+
+static void plusLoopParen(FICL_VM *pVM)
+{
+ FICL_INT index,limit,increment;
+ int flag;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ index = stackGetTop(pVM->rStack).i;
+ limit = stackFetch(pVM->rStack, 1).i;
+ increment = POP().i;
+
+ index += increment;
+
+ if (increment < 0)
+ flag = (index < limit);
+ else
+ flag = (index >= limit);
+
+ if (flag)
+ {
+ stackDrop(pVM->rStack, 3); /* nuke the loop indices & "leave" addr */
+ vmBranchRelative(pVM, 1); /* fall through the loop */
+ }
+ else
+ { /* update index, branch to loop head */
+ stackSetTop(pVM->rStack, LVALUEtoCELL(index));
+ vmBranchRelative(pVM, (uintptr_t)*(pVM->ip));
+ }
+
+ return;
+}
+
+
+static void loopICo(FICL_VM *pVM)
+{
+ CELL index = stackGetTop(pVM->rStack);
+ stackPush(pVM->pStack, index);
+
+ return;
+}
+
+
+static void loopJCo(FICL_VM *pVM)
+{
+ CELL index = stackFetch(pVM->rStack, 3);
+ stackPush(pVM->pStack, index);
+
+ return;
+}
+
+
+static void loopKCo(FICL_VM *pVM)
+{
+ CELL index = stackFetch(pVM->rStack, 6);
+ stackPush(pVM->pStack, index);
+
+ return;
+}
+
+
+/**************************************************************************
+ r e t u r n s t a c k
+**
+**************************************************************************/
+static void toRStack(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ stackPush(pVM->rStack, POP());
+}
+
+static void fromRStack(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ PUSH(stackPop(pVM->rStack));
+}
+
+static void fetchRStack(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ PUSH(stackGetTop(pVM->rStack));
+}
+
+static void twoToR(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ stackRoll(pVM->pStack, 1);
+ stackPush(pVM->rStack, stackPop(pVM->pStack));
+ stackPush(pVM->rStack, stackPop(pVM->pStack));
+ return;
+}
+
+static void twoRFrom(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+ stackPush(pVM->pStack, stackPop(pVM->rStack));
+ stackPush(pVM->pStack, stackPop(pVM->rStack));
+ stackRoll(pVM->pStack, 1);
+ return;
+}
+
+static void twoRFetch(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+ stackPush(pVM->pStack, stackFetch(pVM->rStack, 1));
+ stackPush(pVM->pStack, stackFetch(pVM->rStack, 0));
+ return;
+}
+
+
+/**************************************************************************
+ v a r i a b l e
+**
+**************************************************************************/
+
+static void variableParen(FICL_VM *pVM)
+{
+ FICL_WORD *fw;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ fw = pVM->runningWord;
+ PUSHPTR(fw->param);
+}
+
+
+static void variable(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+ dictAppendWord2(dp, si, variableParen, FW_DEFAULT);
+ dictAllotCells(dp, 1);
+ return;
+}
+
+
+static void twoVariable(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+ dictAppendWord2(dp, si, variableParen, FW_DEFAULT);
+ dictAllotCells(dp, 2);
+ return;
+}
+
+
+/**************************************************************************
+ b a s e & f r i e n d s
+**
+**************************************************************************/
+
+static void base(FICL_VM *pVM)
+{
+ CELL *pBase;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ pBase = (CELL *)(&pVM->base);
+ stackPush(pVM->pStack, LVALUEtoCELL(pBase));
+ return;
+}
+
+
+static void decimal(FICL_VM *pVM)
+{
+ pVM->base = 10;
+ return;
+}
+
+
+static void hex(FICL_VM *pVM)
+{
+ pVM->base = 16;
+ return;
+}
+
+
+/**************************************************************************
+ a l l o t & f r i e n d s
+**
+**************************************************************************/
+
+static void allot(FICL_VM *pVM)
+{
+ FICL_DICT *dp;
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ dp = vmGetDict(pVM);
+ i = POPINT();
+
+#if FICL_ROBUST
+ dictCheck(dp, pVM, i);
+#endif
+
+ dictAllot(dp, i);
+ return;
+}
+
+
+static void here(FICL_VM *pVM)
+{
+ FICL_DICT *dp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ dp = vmGetDict(pVM);
+ PUSHPTR(dp->here);
+ return;
+}
+
+static void comma(FICL_VM *pVM)
+{
+ FICL_DICT *dp;
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ dp = vmGetDict(pVM);
+ c = POP();
+ dictAppendCell(dp, c);
+ return;
+}
+
+static void cComma(FICL_VM *pVM)
+{
+ FICL_DICT *dp;
+ char c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ dp = vmGetDict(pVM);
+ c = (char)POPINT();
+ dictAppendChar(dp, c);
+ return;
+}
+
+static void cells(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+
+ i = POPINT();
+ PUSHINT(i * (FICL_INT)sizeof (CELL));
+ return;
+}
+
+static void cellPlus(FICL_VM *pVM)
+{
+ char *cp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+
+ cp = POPPTR();
+ PUSHPTR(cp + sizeof (CELL));
+ return;
+}
+
+
+
+/**************************************************************************
+ t i c k
+** tick CORE ( "<spaces>name" -- xt )
+** Skip leading space delimiters. Parse name delimited by a space. Find
+** name and return xt, the execution token for name. An ambiguous condition
+** exists if name is not found.
+**************************************************************************/
+void ficlTick(FICL_VM *pVM)
+{
+ FICL_WORD *pFW = NULL;
+ STRINGINFO si = vmGetWord(pVM);
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ pFW = dictLookup(vmGetDict(pVM), si);
+ if (!pFW)
+ {
+ int i = SI_COUNT(si);
+ vmThrowErr(pVM, "%.*s not found", i, SI_PTR(si));
+ }
+ PUSHPTR(pFW);
+ return;
+}
+
+
+static void bracketTickCoIm(FICL_VM *pVM)
+{
+ ficlTick(pVM);
+ literalIm(pVM);
+
+ return;
+}
+
+
+/**************************************************************************
+ p o s t p o n e
+** Lookup the next word in the input stream and compile code to
+** insert it into definitions created by the resulting word
+** (defers compilation, even of immediate words)
+**************************************************************************/
+
+static void postponeCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *pFW;
+ FICL_WORD *pComma = ficlLookup(pVM->pSys, ",");
+ assert(pComma);
+
+ ficlTick(pVM);
+ pFW = stackGetTop(pVM->pStack).p;
+ if (wordIsImmediate(pFW))
+ {
+ dictAppendCell(dp, stackPop(pVM->pStack));
+ }
+ else
+ {
+ literalIm(pVM);
+ dictAppendCell(dp, LVALUEtoCELL(pComma));
+ }
+
+ return;
+}
+
+
+
+/**************************************************************************
+ e x e c u t e
+** Pop an execution token (pointer to a word) off the stack and
+** run it
+**************************************************************************/
+
+static void execute(FICL_VM *pVM)
+{
+ FICL_WORD *pFW;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ pFW = stackPopPtr(pVM->pStack);
+ vmExecute(pVM, pFW);
+
+ return;
+}
+
+
+/**************************************************************************
+ i m m e d i a t e
+** Make the most recently compiled word IMMEDIATE -- it executes even
+** in compile state (most often used for control compiling words
+** such as IF, THEN, etc)
+**************************************************************************/
+
+static void immediate(FICL_VM *pVM)
+{
+ IGNORE(pVM);
+ dictSetImmediate(vmGetDict(pVM));
+ return;
+}
+
+
+static void compileOnly(FICL_VM *pVM)
+{
+ IGNORE(pVM);
+ dictSetFlags(vmGetDict(pVM), FW_COMPILE, 0);
+ return;
+}
+
+
+static void setObjectFlag(FICL_VM *pVM)
+{
+ IGNORE(pVM);
+ dictSetFlags(vmGetDict(pVM), FW_ISOBJECT, 0);
+ return;
+}
+
+static void isObject(FICL_VM *pVM)
+{
+ FICL_INT flag;
+ FICL_WORD *pFW = (FICL_WORD *)stackPopPtr(pVM->pStack);
+
+ flag = ((pFW != NULL) && (pFW->flags & FW_ISOBJECT)) ? FICL_TRUE : FICL_FALSE;
+ stackPushINT(pVM->pStack, flag);
+ return;
+}
+
+static void cstringLit(FICL_VM *pVM)
+{
+ FICL_STRING *sp = (FICL_STRING *)(pVM->ip);
+
+ char *cp = sp->text;
+ cp += sp->count + 1;
+ cp = alignPtr(cp);
+ pVM->ip = (IPTYPE)(void *)cp;
+
+ stackPushPtr(pVM->pStack, sp);
+ return;
+}
+
+
+static void cstringQuoteIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ if (pVM->state == INTERPRET)
+ {
+ FICL_STRING *sp = (FICL_STRING *) dp->here;
+ vmGetString(pVM, sp, '\"');
+ stackPushPtr(pVM->pStack, sp);
+ /* move HERE past string so it doesn't get overwritten. --lch */
+ dictAllot(dp, sp->count + sizeof(FICL_COUNT));
+ }
+ else /* COMPILE state */
+ {
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pCStringLit));
+ dp->here = PTRtoCELL vmGetString(pVM, (FICL_STRING *)dp->here, '\"');
+ dictAlign(dp);
+ }
+
+ return;
+}
+
+/**************************************************************************
+ d o t Q u o t e
+** IMMEDIATE word that compiles a string literal for later display
+** Compile stringLit, then copy the bytes of the string from the TIB
+** to the dictionary. Backpatch the count byte and align the dictionary.
+**
+** stringlit: Fetch the count from the dictionary, then push the address
+** and count on the stack. Finally, update ip to point to the first
+** aligned address after the string text.
+**************************************************************************/
+
+static void stringLit(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ FICL_COUNT count;
+ char *cp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+
+ sp = (FICL_STRING *)(pVM->ip);
+ count = sp->count;
+ cp = sp->text;
+ PUSHPTR(cp);
+ PUSHUNS(count);
+ cp += count + 1;
+ cp = alignPtr(cp);
+ pVM->ip = (IPTYPE)(void *)cp;
+}
+
+static void dotQuoteCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *pType = ficlLookup(pVM->pSys, "type");
+ assert(pType);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStringLit));
+ dp->here = PTRtoCELL vmGetString(pVM, (FICL_STRING *)dp->here, '\"');
+ dictAlign(dp);
+ dictAppendCell(dp, LVALUEtoCELL(pType));
+ return;
+}
+
+
+static void dotParen(FICL_VM *pVM)
+{
+ char *pSrc = vmGetInBuf(pVM);
+ char *pEnd = vmGetInBufEnd(pVM);
+ char *pDest = pVM->pad;
+ char ch;
+
+ /*
+ ** Note: the standard does not want leading spaces skipped (apparently)
+ */
+ for (ch = *pSrc; (pEnd != pSrc) && (ch != ')'); ch = *++pSrc)
+ *pDest++ = ch;
+
+ *pDest = '\0';
+ if ((pEnd != pSrc) && (ch == ')'))
+ pSrc++;
+
+ vmTextOut(pVM, pVM->pad, 0);
+ vmUpdateTib(pVM, pSrc);
+
+ return;
+}
+
+
+/**************************************************************************
+ s l i t e r a l
+** STRING
+** Interpretation: Interpretation semantics for this word are undefined.
+** Compilation: ( c-addr1 u -- )
+** Append the run-time semantics given below to the current definition.
+** Run-time: ( -- c-addr2 u )
+** Return c-addr2 u describing a string consisting of the characters
+** specified by c-addr1 u during compilation. A program shall not alter
+** the returned string.
+**************************************************************************/
+static void sLiteralCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp;
+ char *cp, *cpDest;
+ FICL_UNS u;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+
+ dp = vmGetDict(pVM);
+ u = POPUNS();
+ cp = POPPTR();
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStringLit));
+ cpDest = (char *) dp->here;
+ *cpDest++ = (char) u;
+
+ for (; u > 0; --u)
+ {
+ *cpDest++ = *cp++;
+ }
+
+ *cpDest++ = 0;
+ dp->here = PTRtoCELL alignPtr(cpDest);
+ return;
+}
+
+
+/**************************************************************************
+ s t a t e
+** Return the address of the VM's state member (must be sized the
+** same as a CELL for this reason)
+**************************************************************************/
+static void state(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+ PUSHPTR(&pVM->state);
+ return;
+}
+
+
+/**************************************************************************
+ c r e a t e . . . d o e s >
+** Make a new word in the dictionary with the run-time effect of
+** a variable (push my address), but with extra space allotted
+** for use by does> .
+**************************************************************************/
+
+static void createParen(FICL_VM *pVM)
+{
+ CELL *pCell;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ pCell = pVM->runningWord->param;
+ PUSHPTR(pCell+1);
+ return;
+}
+
+
+static void create(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+ dictCheckThreshold(dp);
+
+ dictAppendWord2(dp, si, createParen, FW_DEFAULT);
+ dictAllotCells(dp, 1);
+ return;
+}
+
+
+static void doDoes(FICL_VM *pVM)
+{
+ CELL *pCell;
+ IPTYPE tempIP;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ pCell = pVM->runningWord->param;
+ tempIP = (IPTYPE)((*pCell).p);
+ PUSHPTR(pCell+1);
+ vmPushIP(pVM, tempIP);
+ return;
+}
+
+
+static void doesParen(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ dp->smudge->code = doDoes;
+ dp->smudge->param[0] = LVALUEtoCELL(pVM->ip);
+ vmPopIP(pVM);
+ return;
+}
+
+
+static void doesCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+#if FICL_WANT_LOCALS
+ assert(pVM->pSys->pUnLinkParen);
+ if (pVM->pSys->nLocals > 0)
+ {
+ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys);
+ dictEmpty(pLoc, pLoc->pForthWords->size);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pUnLinkParen));
+ }
+
+ pVM->pSys->nLocals = 0;
+#endif
+ IGNORE(pVM);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pDoesParen));
+ return;
+}
+
+
+/**************************************************************************
+ t o b o d y
+** to-body CORE ( xt -- a-addr )
+** a-addr is the data-field address corresponding to xt. An ambiguous
+** condition exists if xt is not for a word defined via CREATE.
+**************************************************************************/
+static void toBody(FICL_VM *pVM)
+{
+ FICL_WORD *pFW;
+/*#$-GUY CHANGE: Added robustness.-$#*/
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+
+ pFW = POPPTR();
+ PUSHPTR(pFW->param + 1);
+ return;
+}
+
+
+/*
+** from-body ficl ( a-addr -- xt )
+** Reverse effect of >body
+*/
+static void fromBody(FICL_VM *pVM)
+{
+ char *ptr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+
+ ptr = (char *)POPPTR() - sizeof (FICL_WORD);
+ PUSHPTR(ptr);
+ return;
+}
+
+
+/*
+** >name ficl ( xt -- c-addr u )
+** Push the address and length of a word's name given its address
+** xt.
+*/
+static void toName(FICL_VM *pVM)
+{
+ FICL_WORD *pFW;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 2);
+#endif
+
+ pFW = POPPTR();
+ PUSHPTR(pFW->name);
+ PUSHUNS(pFW->nName);
+ return;
+}
+
+
+static void getLastWord(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ FICL_WORD *wp = pDict->smudge;
+ assert(wp);
+ vmPush(pVM, LVALUEtoCELL(wp));
+ return;
+}
+
+
+/**************************************************************************
+ l b r a c k e t e t c
+**
+**************************************************************************/
+
+static void lbracketCoIm(FICL_VM *pVM)
+{
+ pVM->state = INTERPRET;
+ return;
+}
+
+
+static void rbracket(FICL_VM *pVM)
+{
+ pVM->state = COMPILE;
+ return;
+}
+
+
+/**************************************************************************
+ p i c t u r e d n u m e r i c w o r d s
+**
+** less-number-sign CORE ( -- )
+** Initialize the pictured numeric output conversion process.
+** (clear the pad)
+**************************************************************************/
+static void lessNumberSign(FICL_VM *pVM)
+{
+ FICL_STRING *sp = PTRtoSTRING pVM->pad;
+ sp->count = 0;
+ return;
+}
+
+/*
+** number-sign CORE ( ud1 -- ud2 )
+** Divide ud1 by the number in BASE giving the quotient ud2 and the remainder
+** n. (n is the least-significant digit of ud1.) Convert n to external form
+** and add the resulting character to the beginning of the pictured numeric
+** output string. An ambiguous condition exists if # executes outside of a
+** <# #> delimited number conversion.
+*/
+static void numberSign(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ DPUNS u;
+ UNS16 rem;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+
+ sp = PTRtoSTRING pVM->pad;
+ u = u64Pop(pVM->pStack);
+ rem = m64UMod(&u, (UNS16)(pVM->base));
+ sp->text[sp->count++] = digit_to_char(rem);
+ u64Push(pVM->pStack, u);
+ return;
+}
+
+/*
+** number-sign-greater CORE ( xd -- c-addr u )
+** Drop xd. Make the pictured numeric output string available as a character
+** string. c-addr and u specify the resulting character string. A program
+** may replace characters within the string.
+*/
+static void numberSignGreater(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+
+ sp = PTRtoSTRING pVM->pad;
+ sp->text[sp->count] = 0;
+ strrev(sp->text);
+ DROP(2);
+ PUSHPTR(sp->text);
+ PUSHUNS(sp->count);
+ return;
+}
+
+/*
+** number-sign-s CORE ( ud1 -- ud2 )
+** Convert one digit of ud1 according to the rule for #. Continue conversion
+** until the quotient is zero. ud2 is zero. An ambiguous condition exists if
+** #S executes outside of a <# #> delimited number conversion.
+** TO DO: presently does not use ud1 hi cell - use it!
+*/
+static void numberSignS(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ DPUNS u;
+ UNS16 rem;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+
+ sp = PTRtoSTRING pVM->pad;
+ u = u64Pop(pVM->pStack);
+
+ do
+ {
+ rem = m64UMod(&u, (UNS16)(pVM->base));
+ sp->text[sp->count++] = digit_to_char(rem);
+ }
+ while (u.hi || u.lo);
+
+ u64Push(pVM->pStack, u);
+ return;
+}
+
+/*
+** HOLD CORE ( char -- )
+** Add char to the beginning of the pictured numeric output string. An ambiguous
+** condition exists if HOLD executes outside of a <# #> delimited number conversion.
+*/
+static void hold(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ int i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ sp = PTRtoSTRING pVM->pad;
+ i = POPINT();
+ sp->text[sp->count++] = (char) i;
+ return;
+}
+
+/*
+** SIGN CORE ( n -- )
+** If n is negative, add a minus sign to the beginning of the pictured
+** numeric output string. An ambiguous condition exists if SIGN
+** executes outside of a <# #> delimited number conversion.
+*/
+static void sign(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ int i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ sp = PTRtoSTRING pVM->pad;
+ i = POPINT();
+ if (i < 0)
+ sp->text[sp->count++] = '-';
+ return;
+}
+
+
+/**************************************************************************
+ t o N u m b e r
+** to-number CORE ( ud1 c-addr1 u1 -- ud2 c-addr2 u2 )
+** ud2 is the unsigned result of converting the characters within the
+** string specified by c-addr1 u1 into digits, using the number in BASE,
+** and adding each into ud1 after multiplying ud1 by the number in BASE.
+** Conversion continues left-to-right until a character that is not
+** convertible, including any + or -, is encountered or the string is
+** entirely converted. c-addr2 is the location of the first unconverted
+** character or the first character past the end of the string if the string
+** was entirely converted. u2 is the number of unconverted characters in the
+** string. An ambiguous condition exists if ud2 overflows during the
+** conversion.
+**************************************************************************/
+static void toNumber(FICL_VM *pVM)
+{
+ FICL_UNS count;
+ char *cp;
+ DPUNS accum;
+ FICL_UNS base = pVM->base;
+ FICL_UNS ch;
+ FICL_UNS digit;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,4,4);
+#endif
+
+ count = POPUNS();
+ cp = (char *)POPPTR();
+ accum = u64Pop(pVM->pStack);
+
+ for (ch = *cp; count > 0; ch = *++cp, count--)
+ {
+ if (ch < '0')
+ break;
+
+ digit = ch - '0';
+
+ if (digit > 9)
+ digit = tolower(ch) - 'a' + 10;
+ /*
+ ** Note: following test also catches chars between 9 and a
+ ** because 'digit' is unsigned!
+ */
+ if (digit >= base)
+ break;
+
+ accum = m64Mac(accum, base, digit);
+ }
+
+ u64Push(pVM->pStack, accum);
+ PUSHPTR(cp);
+ PUSHUNS(count);
+
+ return;
+}
+
+
+
+/**************************************************************************
+ q u i t & a b o r t
+** quit CORE ( -- ) ( R: i*x -- )
+** Empty the return stack, store zero in SOURCE-ID if it is present, make
+** the user input device the input source, and enter interpretation state.
+** Do not display a message. Repeat the following:
+**
+** Accept a line from the input source into the input buffer, set >IN to
+** zero, and interpret.
+** Display the implementation-defined system prompt if in
+** interpretation state, all processing has been completed, and no
+** ambiguous condition exists.
+**************************************************************************/
+
+static void quit(FICL_VM *pVM)
+{
+ vmThrow(pVM, VM_QUIT);
+ return;
+}
+
+
+static void ficlAbort(FICL_VM *pVM)
+{
+ vmThrow(pVM, VM_ABORT);
+ return;
+}
+
+
+/**************************************************************************
+ a c c e p t
+** accept CORE ( c-addr +n1 -- +n2 )
+** Receive a string of at most +n1 characters. An ambiguous condition
+** exists if +n1 is zero or greater than 32,767. Display graphic characters
+** as they are received. A program that depends on the presence or absence
+** of non-graphic characters in the string has an environmental dependency.
+** The editing functions, if any, that the system performs in order to
+** construct the string are implementation-defined.
+**
+** (Although the standard text doesn't say so, I assume that the intent
+** of 'accept' is to store the string at the address specified on
+** the stack.)
+** Implementation: if there's more text in the TIB, use it. Otherwise
+** throw out for more text. Copy characters up to the max count into the
+** address given, and return the number of actual characters copied.
+**
+** Note (sobral) this may not be the behavior you'd expect if you're
+** trying to get user input at load time!
+**************************************************************************/
+static void accept(FICL_VM *pVM)
+{
+ FICL_UNS count, len;
+ char *cp;
+ char *pBuf, *pEnd;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ pBuf = vmGetInBuf(pVM);
+ pEnd = vmGetInBufEnd(pVM);
+ len = pEnd - pBuf;
+ if (len == 0)
+ vmThrow(pVM, VM_RESTART);
+
+ /*
+ ** Now we have something in the text buffer - use it
+ */
+ count = stackPopINT(pVM->pStack);
+ cp = stackPopPtr(pVM->pStack);
+
+ len = (count < len) ? count : len;
+ strncpy(cp, vmGetInBuf(pVM), len);
+ pBuf += len;
+ vmUpdateTib(pVM, pBuf);
+ PUSHINT(len);
+
+ return;
+}
+
+
+/**************************************************************************
+ a l i g n
+** 6.1.0705 ALIGN CORE ( -- )
+** If the data-space pointer is not aligned, reserve enough space to
+** align it.
+**************************************************************************/
+static void align(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ IGNORE(pVM);
+ dictAlign(dp);
+ return;
+}
+
+
+/**************************************************************************
+ a l i g n e d
+**
+**************************************************************************/
+static void aligned(FICL_VM *pVM)
+{
+ void *addr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,1);
+#endif
+
+ addr = POPPTR();
+ PUSHPTR(alignPtr(addr));
+ return;
+}
+
+
+/**************************************************************************
+ b e g i n & f r i e n d s
+** Indefinite loop control structures
+** A.6.1.0760 BEGIN
+** Typical use:
+** : X ... BEGIN ... test UNTIL ;
+** or
+** : X ... BEGIN ... test WHILE ... REPEAT ;
+**************************************************************************/
+static void beginCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ markBranch(dp, pVM, destTag);
+ return;
+}
+
+static void untilCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranch0);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranch0));
+ resolveBackBranch(dp, pVM, destTag);
+ return;
+}
+
+static void whileCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranch0);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranch0));
+ markBranch(dp, pVM, origTag);
+ twoSwap(pVM);
+ dictAppendUNS(dp, 1);
+ return;
+}
+
+static void repeatCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranchParen);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen));
+
+ /* expect "begin" branch marker */
+ resolveBackBranch(dp, pVM, destTag);
+ /* expect "while" branch marker */
+ resolveForwardBranch(dp, pVM, origTag);
+ return;
+}
+
+
+static void againCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranchParen);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen));
+
+ /* expect "begin" branch marker */
+ resolveBackBranch(dp, pVM, destTag);
+ return;
+}
+
+
+/**************************************************************************
+ c h a r & f r i e n d s
+** 6.1.0895 CHAR CORE ( "<spaces>name" -- char )
+** Skip leading space delimiters. Parse name delimited by a space.
+** Put the value of its first character onto the stack.
+**
+** bracket-char CORE
+** Interpretation: Interpretation semantics for this word are undefined.
+** Compilation: ( "<spaces>name" -- )
+** Skip leading space delimiters. Parse name delimited by a space.
+** Append the run-time semantics given below to the current definition.
+** Run-time: ( -- char )
+** Place char, the value of the first character of name, on the stack.
+**************************************************************************/
+static void ficlChar(FICL_VM *pVM)
+{
+ STRINGINFO si;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,0,1);
+#endif
+
+ si = vmGetWord(pVM);
+ PUSHUNS((FICL_UNS)(si.cp[0]));
+ return;
+}
+
+static void charCoIm(FICL_VM *pVM)
+{
+ ficlChar(pVM);
+ literalIm(pVM);
+ return;
+}
+
+/**************************************************************************
+ c h a r P l u s
+** char-plus CORE ( c-addr1 -- c-addr2 )
+** Add the size in address units of a character to c-addr1, giving c-addr2.
+**************************************************************************/
+static void charPlus(FICL_VM *pVM)
+{
+ char *cp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,1);
+#endif
+
+ cp = POPPTR();
+ PUSHPTR(cp + 1);
+ return;
+}
+
+/**************************************************************************
+ c h a r s
+** chars CORE ( n1 -- n2 )
+** n2 is the size in address units of n1 characters.
+** For most processors, this function can be a no-op. To guarantee
+** portability, we'll multiply by sizeof (char).
+**************************************************************************/
+#if defined (_M_IX86)
+#pragma warning(disable: 4127)
+#endif
+static void ficlChars(FICL_VM *pVM)
+{
+ if (sizeof (char) > 1)
+ {
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,1);
+#endif
+ i = POPINT();
+ PUSHINT(i * sizeof (char));
+ }
+ /* otherwise no-op! */
+ return;
+}
+#if defined (_M_IX86)
+#pragma warning(default: 4127)
+#endif
+
+
+/**************************************************************************
+ c o u n t
+** COUNT CORE ( c-addr1 -- c-addr2 u )
+** Return the character string specification for the counted string stored
+** at c-addr1. c-addr2 is the address of the first character after c-addr1.
+** u is the contents of the character at c-addr1, which is the length in
+** characters of the string at c-addr2.
+**************************************************************************/
+static void count(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,2);
+#endif
+
+ sp = POPPTR();
+ PUSHPTR(sp->text);
+ PUSHUNS(sp->count);
+ return;
+}
+
+/**************************************************************************
+ e n v i r o n m e n t ?
+** environment-query CORE ( c-addr u -- false | i*x true )
+** c-addr is the address of a character string and u is the string's
+** character count. u may have a value in the range from zero to an
+** implementation-defined maximum which shall not be less than 31. The
+** character string should contain a keyword from 3.2.6 Environmental
+** queries or the optional word sets to be checked for correspondence
+** with an attribute of the present environment. If the system treats the
+** attribute as unknown, the returned flag is false; otherwise, the flag
+** is true and the i*x returned is of the type specified in the table for
+** the attribute queried.
+**************************************************************************/
+static void environmentQ(FICL_VM *pVM)
+{
+ FICL_DICT *envp;
+ FICL_WORD *pFW;
+ STRINGINFO si;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ envp = pVM->pSys->envp;
+ si.count = (FICL_COUNT)stackPopUNS(pVM->pStack);
+ si.cp = stackPopPtr(pVM->pStack);
+
+ pFW = dictLookup(envp, si);
+
+ if (pFW != NULL)
+ {
+ vmExecute(pVM, pFW);
+ PUSHINT(FICL_TRUE);
+ }
+ else
+ {
+ PUSHINT(FICL_FALSE);
+ }
+ return;
+}
+
+/**************************************************************************
+ e v a l u a t e
+** EVALUATE CORE ( i*x c-addr u -- j*x )
+** Save the current input source specification. Store minus-one (-1) in
+** SOURCE-ID if it is present. Make the string described by c-addr and u
+** both the input source and input buffer, set >IN to zero, and interpret.
+** When the parse area is empty, restore the prior input source
+** specification. Other stack effects are due to the words EVALUATEd.
+**
+**************************************************************************/
+static void evaluate(FICL_VM *pVM)
+{
+ FICL_UNS count;
+ char *cp;
+ CELL id;
+ int result;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,0);
+#endif
+
+ count = POPUNS();
+ cp = POPPTR();
+
+ IGNORE(count);
+ id = pVM->sourceID;
+ pVM->sourceID.i = -1;
+ result = ficlExecC(pVM, cp, count);
+ pVM->sourceID = id;
+ if (result != VM_OUTOFTEXT)
+ vmThrow(pVM, result);
+
+ return;
+}
+
+
+/**************************************************************************
+ s t r i n g q u o t e
+** Interpreting: get string delimited by a quote from the input stream,
+** copy to a scratch area, and put its count and address on the stack.
+** Compiling: compile code to push the address and count of a string
+** literal, compile the string from the input stream, and align the dict
+** pointer.
+**************************************************************************/
+static void stringQuoteIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ if (pVM->state == INTERPRET)
+ {
+ FICL_STRING *sp = (FICL_STRING *) dp->here;
+ vmGetString(pVM, sp, '\"');
+ PUSHPTR(sp->text);
+ PUSHUNS(sp->count);
+ }
+ else /* COMPILE state */
+ {
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStringLit));
+ dp->here = PTRtoCELL vmGetString(pVM, (FICL_STRING *)dp->here, '\"');
+ dictAlign(dp);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ t y p e
+** Pop count and char address from stack and print the designated string.
+**************************************************************************/
+static void type(FICL_VM *pVM)
+{
+ FICL_UNS count = stackPopUNS(pVM->pStack);
+ char *cp = stackPopPtr(pVM->pStack);
+ char *pDest = (char *)ficlMalloc(count + 1);
+
+ /*
+ ** Since we don't have an output primitive for a counted string
+ ** (oops), make sure the string is null terminated. If not, copy
+ ** and terminate it.
+ */
+ if (!pDest)
+ vmThrowErr(pVM, "Error: out of memory");
+
+ strncpy(pDest, cp, count);
+ pDest[count] = '\0';
+
+ vmTextOut(pVM, pDest, 0);
+
+ ficlFree(pDest);
+ return;
+}
+
+/**************************************************************************
+ w o r d
+** word CORE ( char "<chars>ccc<char>" -- c-addr )
+** Skip leading delimiters. Parse characters ccc delimited by char. An
+** ambiguous condition exists if the length of the parsed string is greater
+** than the implementation-defined length of a counted string.
+**
+** c-addr is the address of a transient region containing the parsed word
+** as a counted string. If the parse area was empty or contained no
+** characters other than the delimiter, the resulting string has a zero
+** length. A space, not included in the length, follows the string. A
+** program may replace characters within the string.
+** NOTE! Ficl also NULL-terminates the dest string.
+**************************************************************************/
+static void ficlWord(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ char delim;
+ STRINGINFO si;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,1);
+#endif
+
+ sp = (FICL_STRING *)pVM->pad;
+ delim = (char)POPINT();
+ si = vmParseStringEx(pVM, delim, 1);
+
+ if (SI_COUNT(si) > nPAD-1)
+ SI_SETLEN(si, nPAD-1);
+
+ sp->count = (FICL_COUNT)SI_COUNT(si);
+ strncpy(sp->text, SI_PTR(si), SI_COUNT(si));
+ /*#$-GUY CHANGE: I added this.-$#*/
+ sp->text[sp->count] = 0;
+ strcat(sp->text, " ");
+
+ PUSHPTR(sp);
+ return;
+}
+
+
+/**************************************************************************
+ p a r s e - w o r d
+** ficl PARSE-WORD ( <spaces>name -- c-addr u )
+** Skip leading spaces and parse name delimited by a space. c-addr is the
+** address within the input buffer and u is the length of the selected
+** string. If the parse area is empty, the resulting string has a zero length.
+**************************************************************************/
+static void parseNoCopy(FICL_VM *pVM)
+{
+ STRINGINFO si;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,0,2);
+#endif
+
+ si = vmGetWord0(pVM);
+ PUSHPTR(SI_PTR(si));
+ PUSHUNS(SI_COUNT(si));
+ return;
+}
+
+
+/**************************************************************************
+ p a r s e
+** CORE EXT ( char "ccc<char>" -- c-addr u )
+** Parse ccc delimited by the delimiter char.
+** c-addr is the address (within the input buffer) and u is the length of
+** the parsed string. If the parse area was empty, the resulting string has
+** a zero length.
+** NOTE! PARSE differs from WORD: it does not skip leading delimiters.
+**************************************************************************/
+static void parse(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ char delim;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,2);
+#endif
+
+ delim = (char)POPINT();
+
+ si = vmParseStringEx(pVM, delim, 0);
+ PUSHPTR(SI_PTR(si));
+ PUSHUNS(SI_COUNT(si));
+ return;
+}
+
+
+/**************************************************************************
+ f i l l
+** CORE ( c-addr u char -- )
+** If u is greater than zero, store char in each of u consecutive
+** characters of memory beginning at c-addr.
+**************************************************************************/
+static void fill(FICL_VM *pVM)
+{
+ char ch;
+ FICL_UNS u;
+ char *cp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,3,0);
+#endif
+ ch = (char)POPINT();
+ u = POPUNS();
+ cp = (char *)POPPTR();
+
+ while (u > 0)
+ {
+ *cp++ = ch;
+ u--;
+ }
+ return;
+}
+
+
+/**************************************************************************
+ f i n d
+** FIND CORE ( c-addr -- c-addr 0 | xt 1 | xt -1 )
+** Find the definition named in the counted string at c-addr. If the
+** definition is not found, return c-addr and zero. If the definition is
+** found, return its execution token xt. If the definition is immediate,
+** also return one (1), otherwise also return minus-one (-1). For a given
+** string, the values returned by FIND while compiling may differ from
+** those returned while not compiling.
+**************************************************************************/
+static void do_find(FICL_VM *pVM, STRINGINFO si, void *returnForFailure)
+{
+ FICL_WORD *pFW;
+
+ pFW = dictLookup(vmGetDict(pVM), si);
+ if (pFW)
+ {
+ PUSHPTR(pFW);
+ PUSHINT((wordIsImmediate(pFW) ? 1 : -1));
+ }
+ else
+ {
+ PUSHPTR(returnForFailure);
+ PUSHUNS(0);
+ }
+ return;
+}
+
+
+
+/**************************************************************************
+ f i n d
+** FIND CORE ( c-addr -- c-addr 0 | xt 1 | xt -1 )
+** Find the definition named in the counted string at c-addr. If the
+** definition is not found, return c-addr and zero. If the definition is
+** found, return its execution token xt. If the definition is immediate,
+** also return one (1), otherwise also return minus-one (-1). For a given
+** string, the values returned by FIND while compiling may differ from
+** those returned while not compiling.
+**************************************************************************/
+static void cFind(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ STRINGINFO si;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,2);
+#endif
+ sp = POPPTR();
+ SI_PFS(si, sp);
+ do_find(pVM, si, sp);
+}
+
+
+
+/**************************************************************************
+ s f i n d
+** FICL ( c-addr u -- 0 0 | xt 1 | xt -1 )
+** Like FIND, but takes "c-addr u" for the string.
+**************************************************************************/
+static void sFind(FICL_VM *pVM)
+{
+ STRINGINFO si;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,2);
+#endif
+
+ si.count = stackPopINT(pVM->pStack);
+ si.cp = stackPopPtr(pVM->pStack);
+
+ do_find(pVM, si, NULL);
+}
+
+
+
+/**************************************************************************
+ f m S l a s h M o d
+** f-m-slash-mod CORE ( d1 n1 -- n2 n3 )
+** Divide d1 by n1, giving the floored quotient n3 and the remainder n2.
+** Input and output stack arguments are signed. An ambiguous condition
+** exists if n1 is zero or if the quotient lies outside the range of a
+** single-cell signed integer.
+**************************************************************************/
+static void fmSlashMod(FICL_VM *pVM)
+{
+ DPINT d1;
+ FICL_INT n1;
+ INTQR qr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,3,2);
+#endif
+
+ n1 = POPINT();
+ d1 = i64Pop(pVM->pStack);
+ qr = m64FlooredDivI(d1, n1);
+ PUSHINT(qr.rem);
+ PUSHINT(qr.quot);
+ return;
+}
+
+
+/**************************************************************************
+ s m S l a s h R e m
+** s-m-slash-rem CORE ( d1 n1 -- n2 n3 )
+** Divide d1 by n1, giving the symmetric quotient n3 and the remainder n2.
+** Input and output stack arguments are signed. An ambiguous condition
+** exists if n1 is zero or if the quotient lies outside the range of a
+** single-cell signed integer.
+**************************************************************************/
+static void smSlashRem(FICL_VM *pVM)
+{
+ DPINT d1;
+ FICL_INT n1;
+ INTQR qr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,3,2);
+#endif
+
+ n1 = POPINT();
+ d1 = i64Pop(pVM->pStack);
+ qr = m64SymmetricDivI(d1, n1);
+ PUSHINT(qr.rem);
+ PUSHINT(qr.quot);
+ return;
+}
+
+
+static void ficlMod(FICL_VM *pVM)
+{
+ DPINT d1;
+ FICL_INT n1;
+ INTQR qr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ n1 = POPINT();
+ d1.lo = POPINT();
+ i64Extend(d1);
+ qr = m64SymmetricDivI(d1, n1);
+ PUSHINT(qr.rem);
+ return;
+}
+
+
+/**************************************************************************
+ u m S l a s h M o d
+** u-m-slash-mod CORE ( ud u1 -- u2 u3 )
+** Divide ud by u1, giving the quotient u3 and the remainder u2.
+** All values and arithmetic are unsigned. An ambiguous condition
+** exists if u1 is zero or if the quotient lies outside the range of a
+** single-cell unsigned integer.
+*************************************************************************/
+static void umSlashMod(FICL_VM *pVM)
+{
+ DPUNS ud;
+ FICL_UNS u1;
+ UNSQR qr;
+
+ u1 = stackPopUNS(pVM->pStack);
+ ud = u64Pop(pVM->pStack);
+ qr = ficlLongDiv(ud, u1);
+ PUSHUNS(qr.rem);
+ PUSHUNS(qr.quot);
+ return;
+}
+
+
+/**************************************************************************
+ l s h i f t
+** l-shift CORE ( x1 u -- x2 )
+** Perform a logical left shift of u bit-places on x1, giving x2.
+** Put zeroes into the least significant bits vacated by the shift.
+** An ambiguous condition exists if u is greater than or equal to the
+** number of bits in a cell.
+**
+** r-shift CORE ( x1 u -- x2 )
+** Perform a logical right shift of u bit-places on x1, giving x2.
+** Put zeroes into the most significant bits vacated by the shift. An
+** ambiguous condition exists if u is greater than or equal to the
+** number of bits in a cell.
+**************************************************************************/
+static void lshift(FICL_VM *pVM)
+{
+ FICL_UNS nBits;
+ FICL_UNS x1;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ nBits = POPUNS();
+ x1 = POPUNS();
+ PUSHUNS(x1 << nBits);
+ return;
+}
+
+
+static void rshift(FICL_VM *pVM)
+{
+ FICL_UNS nBits;
+ FICL_UNS x1;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ nBits = POPUNS();
+ x1 = POPUNS();
+
+ PUSHUNS(x1 >> nBits);
+ return;
+}
+
+
+/**************************************************************************
+ m S t a r
+** m-star CORE ( n1 n2 -- d )
+** d is the signed product of n1 times n2.
+**************************************************************************/
+static void mStar(FICL_VM *pVM)
+{
+ FICL_INT n2;
+ FICL_INT n1;
+ DPINT d;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,2);
+#endif
+
+ n2 = POPINT();
+ n1 = POPINT();
+
+ d = m64MulI(n1, n2);
+ i64Push(pVM->pStack, d);
+ return;
+}
+
+
+static void umStar(FICL_VM *pVM)
+{
+ FICL_UNS u2;
+ FICL_UNS u1;
+ DPUNS ud;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,2);
+#endif
+
+ u2 = POPUNS();
+ u1 = POPUNS();
+
+ ud = ficlLongMul(u1, u2);
+ u64Push(pVM->pStack, ud);
+ return;
+}
+
+
+/**************************************************************************
+ m a x & m i n
+**
+**************************************************************************/
+static void ficlMax(FICL_VM *pVM)
+{
+ FICL_INT n2;
+ FICL_INT n1;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ n2 = POPINT();
+ n1 = POPINT();
+
+ PUSHINT((n1 > n2) ? n1 : n2);
+ return;
+}
+
+static void ficlMin(FICL_VM *pVM)
+{
+ FICL_INT n2;
+ FICL_INT n1;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ n2 = POPINT();
+ n1 = POPINT();
+
+ PUSHINT((n1 < n2) ? n1 : n2);
+ return;
+}
+
+
+/**************************************************************************
+ m o v e
+** CORE ( addr1 addr2 u -- )
+** If u is greater than zero, copy the contents of u consecutive address
+** units at addr1 to the u consecutive address units at addr2. After MOVE
+** completes, the u consecutive address units at addr2 contain exactly
+** what the u consecutive address units at addr1 contained before the move.
+** NOTE! This implementation assumes that a char is the same size as
+** an address unit.
+**************************************************************************/
+static void move(FICL_VM *pVM)
+{
+ FICL_UNS u;
+ char *addr2;
+ char *addr1;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,3,0);
+#endif
+
+ u = POPUNS();
+ addr2 = POPPTR();
+ addr1 = POPPTR();
+
+ if (u == 0)
+ return;
+ /*
+ ** Do the copy carefully, so as to be
+ ** correct even if the two ranges overlap
+ */
+ if (addr1 >= addr2)
+ {
+ for (; u > 0; u--)
+ *addr2++ = *addr1++;
+ }
+ else
+ {
+ addr2 += u-1;
+ addr1 += u-1;
+ for (; u > 0; u--)
+ *addr2-- = *addr1--;
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ r e c u r s e
+**
+**************************************************************************/
+static void recurseCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+
+ IGNORE(pVM);
+ dictAppendCell(pDict, LVALUEtoCELL(pDict->smudge));
+ return;
+}
+
+
+/**************************************************************************
+ s t o d
+** s-to-d CORE ( n -- d )
+** Convert the number n to the double-cell number d with the same
+** numerical value.
+**************************************************************************/
+static void sToD(FICL_VM *pVM)
+{
+ FICL_INT s;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,2);
+#endif
+
+ s = POPINT();
+
+ /* sign extend to 64 bits.. */
+ PUSHINT(s);
+ PUSHINT((s < 0) ? -1 : 0);
+ return;
+}
+
+
+/**************************************************************************
+ s o u r c e
+** CORE ( -- c-addr u )
+** c-addr is the address of, and u is the number of characters in, the
+** input buffer.
+**************************************************************************/
+static void source(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,0,2);
+#endif
+ PUSHPTR(pVM->tib.cp);
+ PUSHINT(vmGetInBufLen(pVM));
+ return;
+}
+
+
+/**************************************************************************
+ v e r s i o n
+** non-standard...
+**************************************************************************/
+static void ficlVersion(FICL_VM *pVM)
+{
+ vmTextOut(pVM, "ficl Version " FICL_VER, 1);
+ return;
+}
+
+
+/**************************************************************************
+ t o I n
+** to-in CORE
+**************************************************************************/
+static void toIn(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,0,1);
+#endif
+ PUSHPTR(&pVM->tib.index);
+ return;
+}
+
+
+/**************************************************************************
+ c o l o n N o N a m e
+** CORE EXT ( C: -- colon-sys ) ( S: -- xt )
+** Create an unnamed colon definition and push its address.
+** Change state to compile.
+**************************************************************************/
+static void colonNoName(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *pFW;
+ STRINGINFO si;
+
+ SI_SETLEN(si, 0);
+ SI_SETPTR(si, NULL);
+
+ pVM->state = COMPILE;
+ pFW = dictAppendWord2(dp, si, colonParen, FW_DEFAULT | FW_SMUDGE);
+ PUSHPTR(pFW);
+ markControlTag(pVM, colonTag);
+ return;
+}
+
+
+/**************************************************************************
+ u s e r V a r i a b l e
+** user ( u -- ) "<spaces>name"
+** Get a name from the input stream and create a user variable
+** with the name and the index supplied. The run-time effect
+** of a user variable is to push the address of the indexed cell
+** in the running vm's user array.
+**
+** User variables are vm local cells. Each vm has an array of
+** FICL_USER_CELLS of them when FICL_WANT_USER is nonzero.
+** Ficl's user facility is implemented with two primitives,
+** "user" and "(user)", a variable ("nUser") (in softcore.c) that
+** holds the index of the next free user cell, and a redefinition
+** (also in softcore) of "user" that defines a user word and increments
+** nUser.
+**************************************************************************/
+#if FICL_WANT_USER
+static void userParen(FICL_VM *pVM)
+{
+ FICL_INT i = pVM->runningWord->param[0].i;
+ PUSHPTR(&pVM->user[i]);
+ return;
+}
+
+
+static void userVariable(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+ CELL c;
+
+ c = stackPop(pVM->pStack);
+ if (c.i >= FICL_USER_CELLS)
+ {
+ vmThrowErr(pVM, "Error - out of user space");
+ }
+
+ dictAppendWord2(dp, si, userParen, FW_DEFAULT);
+ dictAppendCell(dp, c);
+ return;
+}
+#endif
+
+
+/**************************************************************************
+ t o V a l u e
+** CORE EXT
+** Interpretation: ( x "<spaces>name" -- )
+** Skip leading spaces and parse name delimited by a space. Store x in
+** name. An ambiguous condition exists if name was not defined by VALUE.
+** NOTE: In ficl, VALUE is an alias of CONSTANT
+**************************************************************************/
+static void toValue(FICL_VM *pVM)
+{
+ STRINGINFO si = vmGetWord(pVM);
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *pFW;
+
+#if FICL_WANT_LOCALS
+ if ((pVM->pSys->nLocals > 0) && (pVM->state == COMPILE))
+ {
+ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys);
+ pFW = dictLookup(pLoc, si);
+ if (pFW && (pFW->code == doLocalIm))
+ {
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pToLocalParen));
+ dictAppendCell(dp, LVALUEtoCELL(pFW->param[0]));
+ return;
+ }
+ else if (pFW && pFW->code == do2LocalIm)
+ {
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pTo2LocalParen));
+ dictAppendCell(dp, LVALUEtoCELL(pFW->param[0]));
+ return;
+ }
+ }
+#endif
+
+ assert(pVM->pSys->pStore);
+
+ pFW = dictLookup(dp, si);
+ if (!pFW)
+ {
+ int i = SI_COUNT(si);
+ vmThrowErr(pVM, "%.*s not found", i, SI_PTR(si));
+ }
+
+ if (pVM->state == INTERPRET)
+ pFW->param[0] = stackPop(pVM->pStack);
+ else /* compile code to store to word's param */
+ {
+ PUSHPTR(&pFW->param[0]);
+ literalIm(pVM);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStore));
+ }
+ return;
+}
+
+
+#if FICL_WANT_LOCALS
+/**************************************************************************
+ l i n k P a r e n
+** ( -- )
+** Link a frame on the return stack, reserving nCells of space for
+** locals - the value of nCells is the next cell in the instruction
+** stream.
+**************************************************************************/
+static void linkParen(FICL_VM *pVM)
+{
+ FICL_INT nLink = *(FICL_INT *)(pVM->ip);
+ vmBranchRelative(pVM, 1);
+ stackLink(pVM->rStack, nLink);
+ return;
+}
+
+
+static void unlinkParen(FICL_VM *pVM)
+{
+ stackUnlink(pVM->rStack);
+ return;
+}
+
+
+/**************************************************************************
+ d o L o c a l I m
+** Immediate - cfa of a local while compiling - when executed, compiles
+** code to fetch the value of a local given the local's index in the
+** word's pfa
+**************************************************************************/
+static void getLocalParen(FICL_VM *pVM)
+{
+ FICL_INT nLocal = *(FICL_INT *)(pVM->ip++);
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]);
+ return;
+}
+
+
+static void toLocalParen(FICL_VM *pVM)
+{
+ FICL_INT nLocal = *(FICL_INT *)(pVM->ip++);
+ pVM->rStack->pFrame[nLocal] = stackPop(pVM->pStack);
+ return;
+}
+
+
+static void getLocal0(FICL_VM *pVM)
+{
+ stackPush(pVM->pStack, pVM->rStack->pFrame[0]);
+ return;
+}
+
+
+static void toLocal0(FICL_VM *pVM)
+{
+ pVM->rStack->pFrame[0] = stackPop(pVM->pStack);
+ return;
+}
+
+
+static void getLocal1(FICL_VM *pVM)
+{
+ stackPush(pVM->pStack, pVM->rStack->pFrame[1]);
+ return;
+}
+
+
+static void toLocal1(FICL_VM *pVM)
+{
+ pVM->rStack->pFrame[1] = stackPop(pVM->pStack);
+ return;
+}
+
+
+/*
+** Each local is recorded in a private locals dictionary as a
+** word that does doLocalIm at runtime. DoLocalIm compiles code
+** into the client definition to fetch the value of the
+** corresponding local variable from the return stack.
+** The private dictionary gets initialized at the end of each block
+** that uses locals (in ; and does> for example).
+*/
+static void doLocalIm(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ FICL_INT nLocal = pVM->runningWord->param[0].i;
+
+ if (pVM->state == INTERPRET)
+ {
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]);
+ }
+ else
+ {
+
+ if (nLocal == 0)
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGetLocal0));
+ }
+ else if (nLocal == 1)
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGetLocal1));
+ }
+ else
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGetLocalParen));
+ dictAppendCell(pDict, LVALUEtoCELL(nLocal));
+ }
+ }
+ return;
+}
+
+
+/**************************************************************************
+ l o c a l P a r e n
+** paren-local-paren LOCAL
+** Interpretation: Interpretation semantics for this word are undefined.
+** Execution: ( c-addr u -- )
+** When executed during compilation, (LOCAL) passes a message to the
+** system that has one of two meanings. If u is non-zero,
+** the message identifies a new local whose definition name is given by
+** the string of characters identified by c-addr u. If u is zero,
+** the message is last local and c-addr has no significance.
+**
+** The result of executing (LOCAL) during compilation of a definition is
+** to create a set of named local identifiers, each of which is
+** a definition name, that only have execution semantics within the scope
+** of that definition's source.
+**
+** local Execution: ( -- x )
+**
+** Push the local's value, x, onto the stack. The local's value is
+** initialized as described in 13.3.3 Processing locals and may be
+** changed by preceding the local's name with TO. An ambiguous condition
+** exists when local is executed while in interpretation state.
+**************************************************************************/
+static void localParen(FICL_VM *pVM)
+{
+ FICL_DICT *pDict;
+ STRINGINFO si;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,0);
+#endif
+
+ pDict = vmGetDict(pVM);
+ SI_SETLEN(si, POPUNS());
+ SI_SETPTR(si, (char *)POPPTR());
+
+ if (SI_COUNT(si) > 0)
+ { /* add a local to the **locals** dict and update nLocals */
+ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys);
+ if (pVM->pSys->nLocals >= FICL_MAX_LOCALS)
+ {
+ vmThrowErr(pVM, "Error: out of local space");
+ }
+
+ dictAppendWord2(pLoc, si, doLocalIm, FW_COMPIMMED);
+ dictAppendCell(pLoc, LVALUEtoCELL(pVM->pSys->nLocals));
+
+ if (pVM->pSys->nLocals == 0)
+ { /* compile code to create a local stack frame */
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pLinkParen));
+ /* save location in dictionary for #locals */
+ pVM->pSys->pMarkLocals = pDict->here;
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals));
+ /* compile code to initialize first local */
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pToLocal0));
+ }
+ else if (pVM->pSys->nLocals == 1)
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pToLocal1));
+ }
+ else
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pToLocalParen));
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals));
+ }
+
+ (pVM->pSys->nLocals)++;
+ }
+ else if (pVM->pSys->nLocals > 0)
+ { /* write nLocals to (link) param area in dictionary */
+ *(FICL_INT *)(pVM->pSys->pMarkLocals) = pVM->pSys->nLocals;
+ }
+
+ return;
+}
+
+
+static void get2LocalParen(FICL_VM *pVM)
+{
+ FICL_INT nLocal = *(FICL_INT *)(pVM->ip++);
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]);
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal+1]);
+ return;
+}
+
+
+static void do2LocalIm(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ FICL_INT nLocal = pVM->runningWord->param[0].i;
+
+ if (pVM->state == INTERPRET)
+ {
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]);
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal+1]);
+ }
+ else
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGet2LocalParen));
+ dictAppendCell(pDict, LVALUEtoCELL(nLocal));
+ }
+ return;
+}
+
+
+static void to2LocalParen(FICL_VM *pVM)
+{
+ FICL_INT nLocal = *(FICL_INT *)(pVM->ip++);
+ pVM->rStack->pFrame[nLocal+1] = stackPop(pVM->pStack);
+ pVM->rStack->pFrame[nLocal] = stackPop(pVM->pStack);
+ return;
+}
+
+
+static void twoLocalParen(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ STRINGINFO si;
+ SI_SETLEN(si, stackPopUNS(pVM->pStack));
+ SI_SETPTR(si, (char *)stackPopPtr(pVM->pStack));
+
+ if (SI_COUNT(si) > 0)
+ { /* add a local to the **locals** dict and update nLocals */
+ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys);
+ if (pVM->pSys->nLocals >= FICL_MAX_LOCALS)
+ {
+ vmThrowErr(pVM, "Error: out of local space");
+ }
+
+ dictAppendWord2(pLoc, si, do2LocalIm, FW_COMPIMMED);
+ dictAppendCell(pLoc, LVALUEtoCELL(pVM->pSys->nLocals));
+
+ if (pVM->pSys->nLocals == 0)
+ { /* compile code to create a local stack frame */
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pLinkParen));
+ /* save location in dictionary for #locals */
+ pVM->pSys->pMarkLocals = pDict->here;
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals));
+ }
+
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pTo2LocalParen));
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals));
+
+ pVM->pSys->nLocals += 2;
+ }
+ else if (pVM->pSys->nLocals > 0)
+ { /* write nLocals to (link) param area in dictionary */
+ *(FICL_INT *)(pVM->pSys->pMarkLocals) = pVM->pSys->nLocals;
+ }
+
+ return;
+}
+
+
+#endif
+/**************************************************************************
+ c o m p a r e
+** STRING ( c-addr1 u1 c-addr2 u2 -- n )
+** Compare the string specified by c-addr1 u1 to the string specified by
+** c-addr2 u2. The strings are compared, beginning at the given addresses,
+** character by character, up to the length of the shorter string or until a
+** difference is found. If the two strings are identical, n is zero. If the two
+** strings are identical up to the length of the shorter string, n is minus-one
+** (-1) if u1 is less than u2 and one (1) otherwise. If the two strings are not
+** identical up to the length of the shorter string, n is minus-one (-1) if the
+** first non-matching character in the string specified by c-addr1 u1 has a
+** lesser numeric value than the corresponding character in the string specified
+** by c-addr2 u2 and one (1) otherwise.
+**************************************************************************/
+static void compareInternal(FICL_VM *pVM, int caseInsensitive)
+{
+ char *cp1, *cp2;
+ FICL_UNS u1, u2, uMin;
+ int n = 0;
+
+ vmCheckStack(pVM, 4, 1);
+ u2 = stackPopUNS(pVM->pStack);
+ cp2 = (char *)stackPopPtr(pVM->pStack);
+ u1 = stackPopUNS(pVM->pStack);
+ cp1 = (char *)stackPopPtr(pVM->pStack);
+
+ uMin = (u1 < u2)? u1 : u2;
+ for ( ; (uMin > 0) && (n == 0); uMin--)
+ {
+ char c1 = *cp1++;
+ char c2 = *cp2++;
+ if (caseInsensitive)
+ {
+ c1 = (char)tolower(c1);
+ c2 = (char)tolower(c2);
+ }
+ n = (int)(c1 - c2);
+ }
+
+ if (n == 0)
+ n = (int)(u1 - u2);
+
+ if (n < 0)
+ n = -1;
+ else if (n > 0)
+ n = 1;
+
+ PUSHINT(n);
+ return;
+}
+
+
+static void compareString(FICL_VM *pVM)
+{
+ compareInternal(pVM, FALSE);
+}
+
+
+static void compareStringInsensitive(FICL_VM *pVM)
+{
+ compareInternal(pVM, TRUE);
+}
+
+
+/**************************************************************************
+ p a d
+** CORE EXT ( -- c-addr )
+** c-addr is the address of a transient region that can be used to hold
+** data for intermediate processing.
+**************************************************************************/
+static void pad(FICL_VM *pVM)
+{
+ stackPushPtr(pVM->pStack, pVM->pad);
+}
+
+
+/**************************************************************************
+ s o u r c e - i d
+** CORE EXT, FILE ( -- 0 | -1 | fileid )
+** Identifies the input source as follows:
+**
+** SOURCE-ID Input source
+** --------- ------------
+** fileid Text file fileid
+** -1 String (via EVALUATE)
+** 0 User input device
+**************************************************************************/
+static void sourceid(FICL_VM *pVM)
+{
+ PUSHINT(pVM->sourceID.i);
+ return;
+}
+
+
+/**************************************************************************
+ r e f i l l
+** CORE EXT ( -- flag )
+** Attempt to fill the input buffer from the input source, returning a true
+** flag if successful.
+** When the input source is the user input device, attempt to receive input
+** into the terminal input buffer. If successful, make the result the input
+** buffer, set >IN to zero, and return true. Receipt of a line containing no
+** characters is considered successful. If there is no input available from
+** the current input source, return false.
+** When the input source is a string from EVALUATE, return false and
+** perform no other action.
+**************************************************************************/
+static void refill(FICL_VM *pVM)
+{
+ FICL_INT ret = (pVM->sourceID.i == -1) ? FICL_FALSE : FICL_TRUE;
+ if (ret && (pVM->fRestart == 0))
+ vmThrow(pVM, VM_RESTART);
+
+ PUSHINT(ret);
+ return;
+}
+
+
+/**************************************************************************
+ freebsd exception handling words
+** Catch, from ANS Forth standard. Installs a safety net, then EXECUTE
+** the word in ToS. If an exception happens, restore the state to what
+** it was before, and pushes the exception value on the stack. If not,
+** push zero.
+**
+** Notice that Catch implements an inner interpreter. This is ugly,
+** but given how ficl works, it cannot be helped. The problem is that
+** colon definitions will be executed *after* the function returns,
+** while "code" definitions will be executed immediately. I considered
+** other solutions to this problem, but all of them shared the same
+** basic problem (with added disadvantages): if ficl ever changes it's
+** inner thread modus operandi, one would have to fix this word.
+**
+** More comments can be found throughout catch's code.
+**
+** Daniel C. Sobral Jan 09/1999
+** sadler may 2000 -- revised to follow ficl.c:ficlExecXT.
+**************************************************************************/
+
+static void ficlCatch(FICL_VM *pVM)
+{
+ int except;
+ jmp_buf vmState;
+ FICL_VM VM;
+ FICL_STACK pStack;
+ FICL_STACK rStack;
+ FICL_WORD *pFW;
+
+ assert(pVM);
+ assert(pVM->pSys->pExitInner);
+
+
+ /*
+ ** Get xt.
+ ** We need this *before* we save the stack pointer, or
+ ** we'll have to pop one element out of the stack after
+ ** an exception. I prefer to get done with it up front. :-)
+ */
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ pFW = stackPopPtr(pVM->pStack);
+
+ /*
+ ** Save vm's state -- a catch will not back out environmental
+ ** changes.
+ **
+ ** We are *not* saving dictionary state, since it is
+ ** global instead of per vm, and we are not saving
+ ** stack contents, since we are not required to (and,
+ ** thus, it would be useless). We save pVM, and pVM
+ ** "stacks" (a structure containing general information
+ ** about it, including the current stack pointer).
+ */
+ memcpy((void*)&VM, (void*)pVM, sizeof(FICL_VM));
+ memcpy((void*)&pStack, (void*)pVM->pStack, sizeof(FICL_STACK));
+ memcpy((void*)&rStack, (void*)pVM->rStack, sizeof(FICL_STACK));
+
+ /*
+ ** Give pVM a jmp_buf
+ */
+ pVM->pState = &vmState;
+
+ /*
+ ** Safety net
+ */
+ except = setjmp(vmState);
+
+ switch (except)
+ {
+ /*
+ ** Setup condition - push poison pill so that the VM throws
+ ** VM_INNEREXIT if the XT terminates normally, then execute
+ ** the XT
+ */
+ case 0:
+ vmPushIP(pVM, &(pVM->pSys->pExitInner)); /* Open mouth, insert emetic */
+ vmExecute(pVM, pFW);
+ vmInnerLoop(pVM);
+ break;
+
+ /*
+ ** Normal exit from XT - lose the poison pill,
+ ** restore old setjmp vector and push a zero.
+ */
+ case VM_INNEREXIT:
+ vmPopIP(pVM); /* Gack - hurl poison pill */
+ pVM->pState = VM.pState; /* Restore just the setjmp vector */
+ PUSHINT(0); /* Push 0 -- everything is ok */
+ break;
+
+ /*
+ ** Some other exception got thrown - restore pre-existing VM state
+ ** and push the exception code
+ */
+ default:
+ /* Restore vm's state */
+ memcpy((void*)pVM, (void*)&VM, sizeof(FICL_VM));
+ memcpy((void*)pVM->pStack, (void*)&pStack, sizeof(FICL_STACK));
+ memcpy((void*)pVM->rStack, (void*)&rStack, sizeof(FICL_STACK));
+
+ PUSHINT(except);/* Push error */
+ break;
+ }
+}
+
+/**************************************************************************
+** t h r o w
+** EXCEPTION
+** Throw -- From ANS Forth standard.
+**
+** Throw takes the ToS and, if that's different from zero,
+** returns to the last executed catch context. Further throws will
+** unstack previously executed "catches", in LIFO mode.
+**
+** Daniel C. Sobral Jan 09/1999
+**************************************************************************/
+static void ficlThrow(FICL_VM *pVM)
+{
+ int except;
+
+ except = stackPopINT(pVM->pStack);
+
+ if (except)
+ vmThrow(pVM, except);
+}
+
+
+/**************************************************************************
+** a l l o c a t e
+** MEMORY
+**************************************************************************/
+static void ansAllocate(FICL_VM *pVM)
+{
+ size_t size;
+ void *p;
+
+ size = stackPopINT(pVM->pStack);
+ p = ficlMalloc(size);
+ PUSHPTR(p);
+ if (p)
+ PUSHINT(0);
+ else
+ PUSHINT(1);
+}
+
+
+/**************************************************************************
+** f r e e
+** MEMORY
+**************************************************************************/
+static void ansFree(FICL_VM *pVM)
+{
+ void *p;
+
+ p = stackPopPtr(pVM->pStack);
+ ficlFree(p);
+ PUSHINT(0);
+}
+
+
+/**************************************************************************
+** r e s i z e
+** MEMORY
+**************************************************************************/
+static void ansResize(FICL_VM *pVM)
+{
+ size_t size;
+ void *new, *old;
+
+ size = stackPopINT(pVM->pStack);
+ old = stackPopPtr(pVM->pStack);
+ new = ficlRealloc(old, size);
+ if (new)
+ {
+ PUSHPTR(new);
+ PUSHINT(0);
+ }
+ else
+ {
+ PUSHPTR(old);
+ PUSHINT(1);
+ }
+}
+
+
+/**************************************************************************
+** e x i t - i n n e r
+** Signals execXT that an inner loop has completed
+**************************************************************************/
+static void ficlExitInner(FICL_VM *pVM)
+{
+ vmThrow(pVM, VM_INNEREXIT);
+}
+
+
+/**************************************************************************
+ d n e g a t e
+** DOUBLE ( d1 -- d2 )
+** d2 is the negation of d1.
+**************************************************************************/
+static void dnegate(FICL_VM *pVM)
+{
+ DPINT i = i64Pop(pVM->pStack);
+ i = m64Negate(i);
+ i64Push(pVM->pStack, i);
+
+ return;
+}
+
+
+#if 0
+/**************************************************************************
+
+**
+**************************************************************************/
+static void funcname(FICL_VM *pVM)
+{
+ IGNORE(pVM);
+ return;
+}
+
+
+#endif
+/**************************************************************************
+ f i c l W o r d C l a s s i f y
+** This public function helps to classify word types for SEE
+** and the deugger in tools.c. Given a pointer to a word, it returns
+** a member of WOR
+**************************************************************************/
+WORDKIND ficlWordClassify(FICL_WORD *pFW)
+{
+ typedef struct
+ {
+ WORDKIND kind;
+ FICL_CODE code;
+ } CODEtoKIND;
+
+ static CODEtoKIND codeMap[] =
+ {
+ {BRANCH, branchParen},
+ {COLON, colonParen},
+ {CONSTANT, constantParen},
+ {CREATE, createParen},
+ {DO, doParen},
+ {DOES, doDoes},
+ {IF, branch0},
+ {LITERAL, literalParen},
+ {LOOP, loopParen},
+ {OF, ofParen},
+ {PLOOP, plusLoopParen},
+ {QDO, qDoParen},
+ {CSTRINGLIT, cstringLit},
+ {STRINGLIT, stringLit},
+#if FICL_WANT_USER
+ {USER, userParen},
+#endif
+ {VARIABLE, variableParen},
+ };
+
+#define nMAP (sizeof(codeMap) / sizeof(CODEtoKIND))
+
+ FICL_CODE code = pFW->code;
+ int i;
+
+ for (i=0; i < nMAP; i++)
+ {
+ if (codeMap[i].code == code)
+ return codeMap[i].kind;
+ }
+
+ return PRIMITIVE;
+}
+
+
+#ifdef TESTMAIN
+/**************************************************************************
+** r a n d o m
+** FICL-specific
+**************************************************************************/
+static void ficlRandom(FICL_VM *pVM)
+{
+ PUSHUNS(random());
+}
+
+
+/**************************************************************************
+** s e e d - r a n d o m
+** FICL-specific
+**************************************************************************/
+static void ficlSeedRandom(FICL_VM *pVM)
+{
+ srandom(POPUNS());
+}
+#endif
+
+
+/**************************************************************************
+ f i c l C o m p i l e C o r e
+** Builds the primitive wordset and the environment-query namespace.
+**************************************************************************/
+
+void ficlCompileCore(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+
+ /*
+ ** CORE word set
+ ** see softcore.c for definitions of: abs bl space spaces abort"
+ */
+ pSys->pStore =
+ dictAppendWord(dp, "!", store, FW_DEFAULT);
+ dictAppendWord(dp, "#", numberSign, FW_DEFAULT);
+ dictAppendWord(dp, "#>", numberSignGreater,FW_DEFAULT);
+ dictAppendWord(dp, "#s", numberSignS, FW_DEFAULT);
+ dictAppendWord(dp, "\'", ficlTick, FW_DEFAULT);
+ dictAppendWord(dp, "(", commentHang, FW_IMMEDIATE);
+ dictAppendWord(dp, "*", mul, FW_DEFAULT);
+ dictAppendWord(dp, "*/", mulDiv, FW_DEFAULT);
+ dictAppendWord(dp, "*/mod", mulDivRem, FW_DEFAULT);
+ dictAppendWord(dp, "+", add, FW_DEFAULT);
+ dictAppendWord(dp, "+!", plusStore, FW_DEFAULT);
+ dictAppendWord(dp, "+loop", plusLoopCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, ",", comma, FW_DEFAULT);
+ dictAppendWord(dp, "-", sub, FW_DEFAULT);
+ dictAppendWord(dp, ".", displayCell, FW_DEFAULT);
+ dictAppendWord(dp, ".\"", dotQuoteCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "/", ficlDiv, FW_DEFAULT);
+ dictAppendWord(dp, "/mod", slashMod, FW_DEFAULT);
+ dictAppendWord(dp, "0<", zeroLess, FW_DEFAULT);
+ dictAppendWord(dp, "0=", zeroEquals, FW_DEFAULT);
+ dictAppendWord(dp, "1+", onePlus, FW_DEFAULT);
+ dictAppendWord(dp, "1-", oneMinus, FW_DEFAULT);
+ dictAppendWord(dp, "2!", twoStore, FW_DEFAULT);
+ dictAppendWord(dp, "2*", twoMul, FW_DEFAULT);
+ dictAppendWord(dp, "2/", twoDiv, FW_DEFAULT);
+ dictAppendWord(dp, "2@", twoFetch, FW_DEFAULT);
+ dictAppendWord(dp, "2drop", twoDrop, FW_DEFAULT);
+ dictAppendWord(dp, "2dup", twoDup, FW_DEFAULT);
+ dictAppendWord(dp, "2over", twoOver, FW_DEFAULT);
+ dictAppendWord(dp, "2swap", twoSwap, FW_DEFAULT);
+ dictAppendWord(dp, ":", colon, FW_DEFAULT);
+ dictAppendWord(dp, ";", semicolonCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "<", isLess, FW_DEFAULT);
+ dictAppendWord(dp, "<#", lessNumberSign, FW_DEFAULT);
+ dictAppendWord(dp, "=", isEqual, FW_DEFAULT);
+ dictAppendWord(dp, ">", isGreater, FW_DEFAULT);
+ dictAppendWord(dp, ">body", toBody, FW_DEFAULT);
+ dictAppendWord(dp, ">in", toIn, FW_DEFAULT);
+ dictAppendWord(dp, ">number", toNumber, FW_DEFAULT);
+ dictAppendWord(dp, ">r", toRStack, FW_COMPILE);
+ dictAppendWord(dp, "?dup", questionDup, FW_DEFAULT);
+ dictAppendWord(dp, "@", fetch, FW_DEFAULT);
+ dictAppendWord(dp, "abort", ficlAbort, FW_DEFAULT);
+ dictAppendWord(dp, "accept", accept, FW_DEFAULT);
+ dictAppendWord(dp, "align", align, FW_DEFAULT);
+ dictAppendWord(dp, "aligned", aligned, FW_DEFAULT);
+ dictAppendWord(dp, "allot", allot, FW_DEFAULT);
+ dictAppendWord(dp, "and", bitwiseAnd, FW_DEFAULT);
+ dictAppendWord(dp, "base", base, FW_DEFAULT);
+ dictAppendWord(dp, "begin", beginCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "c!", cStore, FW_DEFAULT);
+ dictAppendWord(dp, "c,", cComma, FW_DEFAULT);
+ dictAppendWord(dp, "c@", cFetch, FW_DEFAULT);
+ dictAppendWord(dp, "case", caseCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "cell+", cellPlus, FW_DEFAULT);
+ dictAppendWord(dp, "cells", cells, FW_DEFAULT);
+ dictAppendWord(dp, "char", ficlChar, FW_DEFAULT);
+ dictAppendWord(dp, "char+", charPlus, FW_DEFAULT);
+ dictAppendWord(dp, "chars", ficlChars, FW_DEFAULT);
+ dictAppendWord(dp, "constant", constant, FW_DEFAULT);
+ dictAppendWord(dp, "count", count, FW_DEFAULT);
+ dictAppendWord(dp, "cr", cr, FW_DEFAULT);
+ dictAppendWord(dp, "create", create, FW_DEFAULT);
+ dictAppendWord(dp, "decimal", decimal, FW_DEFAULT);
+ dictAppendWord(dp, "depth", depth, FW_DEFAULT);
+ dictAppendWord(dp, "do", doCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "does>", doesCoIm, FW_COMPIMMED);
+ pSys->pDrop =
+ dictAppendWord(dp, "drop", drop, FW_DEFAULT);
+ dictAppendWord(dp, "dup", dup, FW_DEFAULT);
+ dictAppendWord(dp, "else", elseCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "emit", emit, FW_DEFAULT);
+ dictAppendWord(dp, "endcase", endcaseCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "endof", endofCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "environment?", environmentQ,FW_DEFAULT);
+ dictAppendWord(dp, "evaluate", evaluate, FW_DEFAULT);
+ dictAppendWord(dp, "execute", execute, FW_DEFAULT);
+ dictAppendWord(dp, "exit", exitCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "fallthrough",fallthroughCoIm,FW_COMPIMMED);
+ dictAppendWord(dp, "fill", fill, FW_DEFAULT);
+ dictAppendWord(dp, "find", cFind, FW_DEFAULT);
+ dictAppendWord(dp, "fm/mod", fmSlashMod, FW_DEFAULT);
+ dictAppendWord(dp, "here", here, FW_DEFAULT);
+ dictAppendWord(dp, "hold", hold, FW_DEFAULT);
+ dictAppendWord(dp, "i", loopICo, FW_COMPILE);
+ dictAppendWord(dp, "if", ifCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "immediate", immediate, FW_DEFAULT);
+ dictAppendWord(dp, "invert", bitwiseNot, FW_DEFAULT);
+ dictAppendWord(dp, "j", loopJCo, FW_COMPILE);
+ dictAppendWord(dp, "k", loopKCo, FW_COMPILE);
+ dictAppendWord(dp, "leave", leaveCo, FW_COMPILE);
+ dictAppendWord(dp, "literal", literalIm, FW_IMMEDIATE);
+ dictAppendWord(dp, "loop", loopCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "lshift", lshift, FW_DEFAULT);
+ dictAppendWord(dp, "m*", mStar, FW_DEFAULT);
+ dictAppendWord(dp, "max", ficlMax, FW_DEFAULT);
+ dictAppendWord(dp, "min", ficlMin, FW_DEFAULT);
+ dictAppendWord(dp, "mod", ficlMod, FW_DEFAULT);
+ dictAppendWord(dp, "move", move, FW_DEFAULT);
+ dictAppendWord(dp, "negate", negate, FW_DEFAULT);
+ dictAppendWord(dp, "of", ofCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "or", bitwiseOr, FW_DEFAULT);
+ dictAppendWord(dp, "over", over, FW_DEFAULT);
+ dictAppendWord(dp, "postpone", postponeCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "quit", quit, FW_DEFAULT);
+ dictAppendWord(dp, "r>", fromRStack, FW_COMPILE);
+ dictAppendWord(dp, "r@", fetchRStack, FW_COMPILE);
+ dictAppendWord(dp, "recurse", recurseCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "repeat", repeatCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "rot", rot, FW_DEFAULT);
+ dictAppendWord(dp, "rshift", rshift, FW_DEFAULT);
+ dictAppendWord(dp, "s\"", stringQuoteIm, FW_IMMEDIATE);
+ dictAppendWord(dp, "s>d", sToD, FW_DEFAULT);
+ dictAppendWord(dp, "sign", sign, FW_DEFAULT);
+ dictAppendWord(dp, "sm/rem", smSlashRem, FW_DEFAULT);
+ dictAppendWord(dp, "source", source, FW_DEFAULT);
+ dictAppendWord(dp, "state", state, FW_DEFAULT);
+ dictAppendWord(dp, "swap", swap, FW_DEFAULT);
+ dictAppendWord(dp, "then", endifCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "type", type, FW_DEFAULT);
+ dictAppendWord(dp, "u.", uDot, FW_DEFAULT);
+ dictAppendWord(dp, "u<", uIsLess, FW_DEFAULT);
+ dictAppendWord(dp, "um*", umStar, FW_DEFAULT);
+ dictAppendWord(dp, "um/mod", umSlashMod, FW_DEFAULT);
+ dictAppendWord(dp, "unloop", unloopCo, FW_COMPILE);
+ dictAppendWord(dp, "until", untilCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "variable", variable, FW_DEFAULT);
+ dictAppendWord(dp, "while", whileCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "word", ficlWord, FW_DEFAULT);
+ dictAppendWord(dp, "xor", bitwiseXor, FW_DEFAULT);
+ dictAppendWord(dp, "[", lbracketCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "[\']", bracketTickCoIm,FW_COMPIMMED);
+ dictAppendWord(dp, "[char]", charCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "]", rbracket, FW_DEFAULT);
+ /*
+ ** CORE EXT word set...
+ ** see softcore.fr for other definitions
+ */
+ /* "#tib" */
+ dictAppendWord(dp, ".(", dotParen, FW_IMMEDIATE);
+ /* ".r" */
+ dictAppendWord(dp, "0>", zeroGreater, FW_DEFAULT);
+ dictAppendWord(dp, "2>r", twoToR, FW_COMPILE);
+ dictAppendWord(dp, "2r>", twoRFrom, FW_COMPILE);
+ dictAppendWord(dp, "2r@", twoRFetch, FW_COMPILE);
+ dictAppendWord(dp, ":noname", colonNoName, FW_DEFAULT);
+ dictAppendWord(dp, "?do", qDoCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "again", againCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "c\"", cstringQuoteIm, FW_IMMEDIATE);
+ dictAppendWord(dp, "hex", hex, FW_DEFAULT);
+ dictAppendWord(dp, "pad", pad, FW_DEFAULT);
+ dictAppendWord(dp, "parse", parse, FW_DEFAULT);
+ dictAppendWord(dp, "pick", pick, FW_DEFAULT);
+ /* query restore-input save-input tib u.r u> unused [compile] */
+ dictAppendWord(dp, "roll", roll, FW_DEFAULT);
+ dictAppendWord(dp, "refill", refill, FW_DEFAULT);
+ dictAppendWord(dp, "source-id", sourceid, FW_DEFAULT);
+ dictAppendWord(dp, "to", toValue, FW_IMMEDIATE);
+ dictAppendWord(dp, "value", constant, FW_DEFAULT);
+ dictAppendWord(dp, "\\", commentLine, FW_IMMEDIATE);
+
+
+ /*
+ ** Set CORE environment query values
+ */
+ ficlSetEnv(pSys, "/counted-string", FICL_STRING_MAX);
+ ficlSetEnv(pSys, "/hold", nPAD);
+ ficlSetEnv(pSys, "/pad", nPAD);
+ ficlSetEnv(pSys, "address-unit-bits", 8);
+ ficlSetEnv(pSys, "core", FICL_TRUE);
+ ficlSetEnv(pSys, "core-ext", FICL_FALSE);
+ ficlSetEnv(pSys, "floored", FICL_FALSE);
+ ficlSetEnv(pSys, "max-char", UCHAR_MAX);
+ ficlSetEnvD(pSys,"max-d", 0x7fffffff, 0xffffffff);
+ ficlSetEnv(pSys, "max-n", 0x7fffffff);
+ ficlSetEnv(pSys, "max-u", 0xffffffff);
+ ficlSetEnvD(pSys,"max-ud", 0xffffffff, 0xffffffff);
+ ficlSetEnv(pSys, "return-stack-cells",FICL_DEFAULT_STACK);
+ ficlSetEnv(pSys, "stack-cells", FICL_DEFAULT_STACK);
+
+ /*
+ ** DOUBLE word set (partial)
+ */
+ dictAppendWord(dp, "2constant", twoConstant, FW_IMMEDIATE);
+ dictAppendWord(dp, "2literal", twoLiteralIm, FW_IMMEDIATE);
+ dictAppendWord(dp, "2variable", twoVariable, FW_IMMEDIATE);
+ dictAppendWord(dp, "dnegate", dnegate, FW_DEFAULT);
+
+
+ /*
+ ** EXCEPTION word set
+ */
+ dictAppendWord(dp, "catch", ficlCatch, FW_DEFAULT);
+ dictAppendWord(dp, "throw", ficlThrow, FW_DEFAULT);
+
+ ficlSetEnv(pSys, "exception", FICL_TRUE);
+ ficlSetEnv(pSys, "exception-ext", FICL_TRUE);
+
+ /*
+ ** LOCAL and LOCAL EXT
+ ** see softcore.c for implementation of locals|
+ */
+#if FICL_WANT_LOCALS
+ pSys->pLinkParen =
+ dictAppendWord(dp, "(link)", linkParen, FW_COMPILE);
+ pSys->pUnLinkParen =
+ dictAppendWord(dp, "(unlink)", unlinkParen, FW_COMPILE);
+ dictAppendWord(dp, "doLocal", doLocalIm, FW_COMPIMMED);
+ pSys->pGetLocalParen =
+ dictAppendWord(dp, "(@local)", getLocalParen, FW_COMPILE);
+ pSys->pToLocalParen =
+ dictAppendWord(dp, "(toLocal)", toLocalParen, FW_COMPILE);
+ pSys->pGetLocal0 =
+ dictAppendWord(dp, "(@local0)", getLocal0, FW_COMPILE);
+ pSys->pToLocal0 =
+ dictAppendWord(dp, "(toLocal0)",toLocal0, FW_COMPILE);
+ pSys->pGetLocal1 =
+ dictAppendWord(dp, "(@local1)", getLocal1, FW_COMPILE);
+ pSys->pToLocal1 =
+ dictAppendWord(dp, "(toLocal1)",toLocal1, FW_COMPILE);
+ dictAppendWord(dp, "(local)", localParen, FW_COMPILE);
+
+ pSys->pGet2LocalParen =
+ dictAppendWord(dp, "(@2local)", get2LocalParen, FW_COMPILE);
+ pSys->pTo2LocalParen =
+ dictAppendWord(dp, "(to2Local)",to2LocalParen, FW_COMPILE);
+ dictAppendWord(dp, "(2local)", twoLocalParen, FW_COMPILE);
+
+ ficlSetEnv(pSys, "locals", FICL_TRUE);
+ ficlSetEnv(pSys, "locals-ext", FICL_TRUE);
+ ficlSetEnv(pSys, "#locals", FICL_MAX_LOCALS);
+#endif
+
+ /*
+ ** Optional MEMORY-ALLOC word set
+ */
+
+ dictAppendWord(dp, "allocate", ansAllocate, FW_DEFAULT);
+ dictAppendWord(dp, "free", ansFree, FW_DEFAULT);
+ dictAppendWord(dp, "resize", ansResize, FW_DEFAULT);
+
+ ficlSetEnv(pSys, "memory-alloc", FICL_TRUE);
+
+ /*
+ ** optional SEARCH-ORDER word set
+ */
+ ficlCompileSearch(pSys);
+
+ /*
+ ** TOOLS and TOOLS EXT
+ */
+ ficlCompileTools(pSys);
+
+ /*
+ ** FILE and FILE EXT
+ */
+#if FICL_WANT_FILE
+ ficlCompileFile(pSys);
+#endif
+
+ /*
+ ** Ficl extras
+ */
+#if FICL_WANT_FLOAT
+ dictAppendWord(dp, ".hash", dictHashSummary,FW_DEFAULT);
+#endif
+ dictAppendWord(dp, ".ver", ficlVersion, FW_DEFAULT);
+ dictAppendWord(dp, "-roll", minusRoll, FW_DEFAULT);
+ dictAppendWord(dp, ">name", toName, FW_DEFAULT);
+ dictAppendWord(dp, "add-parse-step",
+ addParseStep, FW_DEFAULT);
+ dictAppendWord(dp, "body>", fromBody, FW_DEFAULT);
+ dictAppendWord(dp, "compare", compareString, FW_DEFAULT); /* STRING */
+ dictAppendWord(dp, "compare-insensitive", compareStringInsensitive, FW_DEFAULT); /* STRING */
+ dictAppendWord(dp, "compile-only",
+ compileOnly, FW_DEFAULT);
+ dictAppendWord(dp, "endif", endifCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "last-word", getLastWord, FW_DEFAULT);
+ dictAppendWord(dp, "hash", hash, FW_DEFAULT);
+ dictAppendWord(dp, "objectify", setObjectFlag, FW_DEFAULT);
+ dictAppendWord(dp, "?object", isObject, FW_DEFAULT);
+ dictAppendWord(dp, "parse-word",parseNoCopy, FW_DEFAULT);
+ dictAppendWord(dp, "sfind", sFind, FW_DEFAULT);
+ dictAppendWord(dp, "sliteral", sLiteralCoIm, FW_COMPIMMED); /* STRING */
+ dictAppendWord(dp, "sprintf", ficlSprintf, FW_DEFAULT);
+ dictAppendWord(dp, "strlen", ficlStrlen, FW_DEFAULT);
+ dictAppendWord(dp, "q@", quadFetch, FW_DEFAULT);
+ dictAppendWord(dp, "q!", quadStore, FW_DEFAULT);
+ dictAppendWord(dp, "w@", wFetch, FW_DEFAULT);
+ dictAppendWord(dp, "w!", wStore, FW_DEFAULT);
+ dictAppendWord(dp, "x.", hexDot, FW_DEFAULT);
+#if FICL_WANT_USER
+ dictAppendWord(dp, "(user)", userParen, FW_DEFAULT);
+ dictAppendWord(dp, "user", userVariable, FW_DEFAULT);
+#endif
+#ifdef TESTMAIN
+ dictAppendWord(dp, "random", ficlRandom, FW_DEFAULT);
+ dictAppendWord(dp, "seed-random",ficlSeedRandom,FW_DEFAULT);
+#endif
+
+ /*
+ ** internal support words
+ */
+ dictAppendWord(dp, "(create)", createParen, FW_COMPILE);
+ pSys->pExitParen =
+ dictAppendWord(dp, "(exit)", exitParen, FW_COMPILE);
+ pSys->pSemiParen =
+ dictAppendWord(dp, "(;)", semiParen, FW_COMPILE);
+ pSys->pLitParen =
+ dictAppendWord(dp, "(literal)", literalParen, FW_COMPILE);
+ pSys->pTwoLitParen =
+ dictAppendWord(dp, "(2literal)",twoLitParen, FW_COMPILE);
+ pSys->pStringLit =
+ dictAppendWord(dp, "(.\")", stringLit, FW_COMPILE);
+ pSys->pCStringLit =
+ dictAppendWord(dp, "(c\")", cstringLit, FW_COMPILE);
+ pSys->pBranch0 =
+ dictAppendWord(dp, "(branch0)", branch0, FW_COMPILE);
+ pSys->pBranchParen =
+ dictAppendWord(dp, "(branch)", branchParen, FW_COMPILE);
+ pSys->pDoParen =
+ dictAppendWord(dp, "(do)", doParen, FW_COMPILE);
+ pSys->pDoesParen =
+ dictAppendWord(dp, "(does>)", doesParen, FW_COMPILE);
+ pSys->pQDoParen =
+ dictAppendWord(dp, "(?do)", qDoParen, FW_COMPILE);
+ pSys->pLoopParen =
+ dictAppendWord(dp, "(loop)", loopParen, FW_COMPILE);
+ pSys->pPLoopParen =
+ dictAppendWord(dp, "(+loop)", plusLoopParen, FW_COMPILE);
+ pSys->pInterpret =
+ dictAppendWord(dp, "interpret", interpret, FW_DEFAULT);
+ dictAppendWord(dp, "lookup", lookup, FW_DEFAULT);
+ pSys->pOfParen =
+ dictAppendWord(dp, "(of)", ofParen, FW_DEFAULT);
+ dictAppendWord(dp, "(variable)",variableParen, FW_COMPILE);
+ dictAppendWord(dp, "(constant)",constantParen, FW_COMPILE);
+ dictAppendWord(dp, "(parse-step)",
+ parseStepParen, FW_DEFAULT);
+ pSys->pExitInner =
+ dictAppendWord(dp, "exit-inner",ficlExitInner, FW_DEFAULT);
+
+ /*
+ ** Set up system's outer interpreter loop - maybe this should be in initSystem?
+ */
+ pSys->pInterp[0] = pSys->pInterpret;
+ pSys->pInterp[1] = pSys->pBranchParen;
+ pSys->pInterp[2] = (FICL_WORD *)(void *)(-2);
+
+ assert(dictCellsAvail(dp) > 0);
+
+ return;
+}
diff --git a/stand/ficl32/Makefile b/stand/ficl32/Makefile
new file mode 100644
index 0000000..0949946
--- /dev/null
+++ b/stand/ficl32/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+DO32=1
+
+.include "${.CURDIR}/../ficl/Makefile"
diff --git a/stand/ficl32/Makefile.depend b/stand/ficl32/Makefile.depend
new file mode 100644
index 0000000..c04b7c3
--- /dev/null
+++ b/stand/ficl32/Makefile.depend
@@ -0,0 +1,15 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libstand \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/forth/Makefile b/stand/forth/Makefile
new file mode 100644
index 0000000..74d9ed7
--- /dev/null
+++ b/stand/forth/Makefile
@@ -0,0 +1,49 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+MAN+= beastie.4th.8 \
+ brand.4th.8 \
+ check-password.4th.8 \
+ color.4th.8 \
+ delay.4th.8 \
+ loader.conf.5 \
+ loader.4th.8 \
+ menu.4th.8 \
+ menusets.4th.8 \
+ version.4th.8
+
+FILES+= beastie.4th
+FILES+= brand.4th
+FILES+= brand-fbsd.4th
+FILES+= check-password.4th
+FILES+= color.4th
+FILES+= delay.4th
+FILES+= efi.4th
+FILES+= frames.4th
+FILES+= loader.4th
+FILES+= logo-beastie.4th
+FILES+= logo-beastiebw.4th
+FILES+= logo-fbsdbw.4th
+FILES+= logo-orb.4th
+FILES+= logo-orbbw.4th
+FILES+= menu.4th
+FILES+= menu-commands.4th
+FILES+= menusets.4th
+FILES+= pcibios.4th
+FILES+= screen.4th
+FILES+= shortcuts.4th
+FILES+= support.4th
+FILES+= version.4th
+FILESDIR_loader.conf= /boot/defaults
+
+# Allow machine specific loader.rc to be installed.
+.for f in loader.rc menu.rc loader.conf
+.if exists(${BOOTSRC}/${MACHINE:C/amd64/i386/}/loader/${f})
+FILES+= ${BOOTSRC}/${MACHINE:C/amd64/i386/}/loader/${f}
+.else
+FILES+= ${f}
+.endif
+.endfor
+
+.include <bsd.prog.mk>
diff --git a/stand/forth/Makefile.depend b/stand/forth/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/forth/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/forth/beastie.4th b/stand/forth/beastie.4th
new file mode 100644
index 0000000..752cce2
--- /dev/null
+++ b/stand/forth/beastie.4th
@@ -0,0 +1,110 @@
+\ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org>
+\ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com>
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+marker task-beastie.4th
+
+only forth definitions
+
+variable logoX
+variable logoY
+
+\ Initialize logo placement to defaults
+46 logoX !
+4 logoY !
+
+\ This function draws any number of beastie logos at (loader_logo_x,
+\ loader_logo_y) if defined, else (46,4) (to the right of the menu). To choose
+\ your beastie, set the variable `loader_logo' to the respective logo name.
+\
+\ NOTE: Each is defined as a logo function in /boot/logo-${loader_logo}.4th
+\ NOTE: If `/boot/logo-${loader_logo}.4th' does not exist or does not define
+\ a `logo' function, no beastie is drawn.
+\
+: draw-beastie ( -- ) \ at (loader_logo_x,loader_logo_y), else (46,4)
+
+ s" loader_logo_x" getenv dup -1 <> if
+ ?number 1 = if logoX ! then
+ else drop then
+ s" loader_logo_y" getenv dup -1 <> if
+ ?number 1 = if logoY ! then
+ else drop then
+
+
+ \ If `logo' is defined, execute it
+ s" logo" sfind ( -- xt|0 bool ) if
+ logoX @ logoY @ rot execute
+ else
+ \ Not defined; try-include desired logo file
+ drop ( xt = 0 ) \ cruft
+ s" loader_logo" getenv dup -1 = over 0= or if
+ dup 0= if 2drop else drop then \ getenv result unused
+ loader_color? if
+ s" try-include /boot/logo-orb.4th"
+ else
+ s" try-include /boot/logo-orbbw.4th"
+ then
+ else
+ 2drop ( c-addr/u -- ) \ getenv result unused
+ s" try-include /boot/logo-${loader_logo}.4th"
+ then
+ evaluate
+ 1 spaces
+
+ \ Execute `logo' if defined now
+ s" logo" sfind if
+ logoX @ logoY @ rot execute
+ else drop then
+ then
+;
+
+also support-functions
+
+: beastie-start ( -- ) \ starts the menu
+ s" beastie_disable" getenv dup -1 <> if
+ s" YES" compare-insensitive 0= if
+ any_conf_read? if
+ load_xen_throw
+ load_kernel
+ load_modules
+ then
+ exit \ to autoboot (default)
+ then
+ else drop then
+
+ s" loader_delay" getenv -1 = if
+ s" include /boot/menu.rc" evaluate
+ else
+ drop
+ ." Loading Menu (Ctrl-C to Abort)" cr
+ s" set delay_command='include /boot/menu.rc'" evaluate
+ s" set delay_showdots" evaluate
+ delay_execute
+ then
+;
+
+only forth definitions
diff --git a/stand/forth/beastie.4th.8 b/stand/forth/beastie.4th.8
new file mode 100644
index 0000000..e99f654
--- /dev/null
+++ b/stand/forth/beastie.4th.8
@@ -0,0 +1,173 @@
+.\" Copyright (c) 2011-2012 Devin Teske
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 6, 2016
+.Dt BEASTIE.4TH 8
+.Os
+.Sh NAME
+.Nm beastie.4th
+.Nd FreeBSD ASCII art boot module
+.Sh DESCRIPTION
+The file that goes by the name of
+.Nm
+is a set of commands designed to draw the ASCII art FreeBSD mascot
+\(en known simply as
+.Em beastie
+\(en to the right of the boot loader menu.
+The commands of
+.Nm
+by themselves are not enough for most uses.
+Please refer to the
+examples below for the most common situations, and to
+.Xr loader 8
+for additional commands.
+.Pp
+Before using any of the commands provided in
+.Nm ,
+it must be included
+through the command:
+.Pp
+.Dl include beastie.4th
+.Pp
+This line is present in the default
+.Pa /boot/loader.rc
+file, so it is not needed (and should not be re-issued) in a normal setup.
+.Pp
+The commands provided by it are:
+.Pp
+.Bl -tag -width disable-module_module -compact -offset indent
+.It Ic draw-beastie
+Draws the FreeBSD logo.
+.Pp
+The logo that is drawn is configured by setting the
+.Ic loader_logo
+variable in
+.Xr loader.conf 5
+to one of
+.Dq Li beastie ,
+.Dq Li beastiebw ,
+.Dq Li fbsdbw ,
+.Dq Li orb ,
+and
+.Dq Li orbbw
+(the default).
+.Pp
+The position of the logo can be configured by setting the
+.Ic loader_logo_x
+and
+.Ic loader_logo_y
+variables in
+.Xr loader.conf 5 .
+The default values are 46 (x) and 4 (y).
+.Pp
+.It Ic clear-beastie
+Clears the screen of beastie.
+.Pp
+.It Ic beastie-start
+Initializes the interactive boot loader menu.
+.Pp
+The
+.Ic loader_delay
+variable can be configured in
+.Xr loader.conf 5
+to the number of seconds you would like to delay loading the boot menu.
+During the delay the user can press Ctrl-C to fall back to
+.Ic autoboot
+or ENTER to proceed.
+The default behavior is to not delay.
+.El
+.Pp
+The environment variables that effect its behavior are:
+.Bl -tag -width bootfile -offset indent
+.It Va loader_logo
+Selects the desired logo in the beastie boot menu. Possible values are:
+.Dq Li fbsdbw ,
+.Dq Li beastie ,
+.Dq Li beastiebw ,
+.Dq Li orb ,
+.Dq Li orbbw
+(default), and
+.Dq Li none .
+.It Va loader_logo_x
+Sets the desired column position of the logo. Default is 46.
+.It Va loader_logo_y
+Sets the desired row position of the logo. Default is 4.
+.It Va beastie_disable
+If set to
+.Dq YES ,
+the beastie boot menu will be skipped.
+The beastie boot menu is always skipped if running non-x86 hardware.
+.It Va loader_delay
+If set to a number higher than zero, introduces a delay before starting the
+beastie boot menu. During the delay the user can press either Ctrl-C to skip
+the menu or ENTER to proceed to the menu. The default is to not delay when
+loading the menu.
+.El
+.Sh FILES
+.Bl -tag -width /boot/loader.4th -compact
+.It Pa /boot/loader
+The
+.Xr loader 8 .
+.It Pa /boot/beastie.4th
+.Nm
+itself.
+.It Pa /boot/loader.rc
+.Xr loader 8
+bootstrapping script.
+.El
+.Sh EXAMPLES
+Standard i386
+.Pa /boot/loader.rc :
+.Pp
+.Bd -literal -offset indent -compact
+include /boot/beastie.4th
+beastie-start
+.Ed
+.Pp
+Set a different logo in
+.Xr loader.conf 5 :
+.Pp
+.Bd -literal -offset indent -compact
+loader_logo="beastie"
+.Ed
+.Sh SEE ALSO
+.Xr loader.conf 5 ,
+.Xr loader 8 ,
+.Xr loader.4th 8
+.Sh HISTORY
+The
+.Nm
+set of commands first appeared in
+.Fx 5.1 .
+.Sh AUTHORS
+The
+.Nm
+set of commands was written by
+.An -nosplit
+.An Scott Long Aq scottl@FreeBSD.org ,
+.An Aleksander Fafula Aq alex@fafula.com
+and
+.An Devin Teske Aq dteske@FreeBSD.org .
diff --git a/stand/forth/brand-fbsd.4th b/stand/forth/brand-fbsd.4th
new file mode 100644
index 0000000..9cd017f
--- /dev/null
+++ b/stand/forth/brand-fbsd.4th
@@ -0,0 +1,46 @@
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+2 brandX ! 1 brandY ! \ Initialize brand placement defaults
+
+: brand+ ( x y c-addr/u -- x y' )
+ 2swap 2dup at-xy 2swap \ position the cursor
+ type \ print to the screen
+ 1+ \ increase y for next time we're called
+;
+
+: brand ( x y -- ) \ "FreeBSD" [wide] logo in B/W (7 rows x 42 columns)
+
+ s" ______ ____ _____ _____ " brand+
+ s" | ____| | _ \ / ____| __ \ " brand+
+ s" | |___ _ __ ___ ___ | |_) | (___ | | | |" brand+
+ s" | ___| '__/ _ \/ _ \| _ < \___ \| | | |" brand+
+ s" | | | | | __/ __/| |_) |____) | |__| |" brand+
+ s" | | | | | | || | | |" brand+
+ s" |_| |_| \___|\___||____/|_____/|_____/ " brand+
+
+ 2drop
+;
diff --git a/stand/forth/brand.4th b/stand/forth/brand.4th
new file mode 100644
index 0000000..39a9bfa
--- /dev/null
+++ b/stand/forth/brand.4th
@@ -0,0 +1,74 @@
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+marker task-brand.4th
+
+variable brandX
+variable brandY
+
+\ Initialize brand placement to defaults
+2 brandX !
+1 brandY !
+
+\ This function draws any number of company brands at (loader_brand_x,
+\ loader_brand_y) if defined, or (2,1) (top-left). To choose your brand, set
+\ the variable `loader_brand' to the respective brand name.
+\
+\ NOTE: Each is defined as a brand function in /boot/brand-${loader_brand}.4th
+\ NOTE: If `/boot/brand-${loader_brand}.4th' does not exist or does not define
+\ a `brand' function, no brand is drawn.
+\
+: draw-brand ( -- ) \ at (loader_brand_x,loader_brand_y), else (2,1)
+
+ s" loader_brand_x" getenv dup -1 <> if
+ ?number 1 = if brandX ! then
+ else drop then
+ s" loader_brand_y" getenv dup -1 <> if
+ ?number 1 = if brandY ! then
+ else drop then
+
+ \ If `brand' is defined, execute it
+ s" brand" sfind ( -- xt|0 bool ) if
+ brandX @ brandY @ rot execute
+ else
+ \ Not defined; try-include desired brand file
+ drop ( xt = 0 ) \ cruft
+ s" loader_brand" getenv dup -1 = over 0= or if
+ dup 0= if 2drop else drop then \ getenv result unused
+ s" try-include /boot/brand-fbsd.4th"
+ else
+ 2drop ( c-addr/u -- ) \ getenv result unused
+ s" try-include /boot/brand-${loader_brand}.4th"
+ then
+ evaluate
+ 1 spaces
+
+ \ Execute `brand' if defined now
+ s" brand" sfind if
+ brandX @ brandY @ rot execute
+ else drop then
+ then
+;
diff --git a/stand/forth/brand.4th.8 b/stand/forth/brand.4th.8
new file mode 100644
index 0000000..dfd188d
--- /dev/null
+++ b/stand/forth/brand.4th.8
@@ -0,0 +1,125 @@
+.\" Copyright (c) 2011 Devin Teske
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 18, 2011
+.Dt BRAND.4TH 8
+.Os
+.Sh NAME
+.Nm brand.4th
+.Nd FreeBSD ASCII art boot module
+.Sh DESCRIPTION
+The file that goes by the name of
+.Nm
+is a set of commands designed to draw the ASCII art BSD brand above the boot
+loader menu.
+The commands of
+.Nm
+by themselves are not enough for most uses.
+Please refer to the
+examples below for the most common situations, and to
+.Xr loader 8
+for additional commands.
+.Pp
+Before using any of the commands provided in
+.Nm ,
+it must be included
+through the command:
+.Pp
+.Dl include brand.4th
+.Pp
+This line is present in the default
+.Pa /boot/menu.rc
+file, so it is not needed (and should not be re-issued) in a normal setup.
+.Pp
+The commands provided by it are:
+.Pp
+.Bl -tag -width disable-module_module -compact -offset indent
+.It Ic draw-brand
+Draws the BSD brand.
+.Pp
+The brand that is drawn is configured by setting the
+.Ic loader_brand
+variable in
+.Xr loader.conf 5
+to one of
+.Dq Li fbsd
+(the default) or
+.Dq Li none .
+.Pp
+The position of the logo can be configured by setting the
+.Ic loader_brand_x
+and
+.Ic loader_brand_y
+variables in
+.Xr loader.conf 5 .
+The default values are 2 (x) and 1 (y).
+.El
+.Pp
+The environment variables that effect its behavior are:
+.Bl -tag -width bootfile -offset indent
+.It Va loader_brand
+Selects the desired brand in the beastie boot menu. Possible values are:
+.Dq Li fbsd
+(default) or
+.Dq Li none .
+.It Va loader_brand_x
+Sets the desired column position of the brand. Default is 2.
+.It Va loader_brand_y
+Sets the desired row position of the brand. Default is 1.
+.El
+.Sh FILES
+.Bl -tag -width /boot/loader.4th -compact
+.It Pa /boot/loader
+The
+.Xr loader 8 .
+.It Pa /boot/brand.4th
+.Nm
+itself.
+.It Pa /boot/loader.rc
+.Xr loader 8
+bootstrapping script.
+.El
+.Sh EXAMPLES
+Set FreeBSD brand in
+.Xr loader.conf 5 :
+.Pp
+.Bd -literal -offset indent -compact
+loader_brand="fbsd"
+.Ed
+.Sh SEE ALSO
+.Xr loader.conf 5 ,
+.Xr loader 8
+.Sh HISTORY
+The
+.Nm
+set of commands first appeared in
+.Fx 9.0 .
+.Sh AUTHORS
+The
+.Nm
+set of commands was written by
+.An -nosplit
+.An Devin Teske Aq dteske@FreeBSD.org .
diff --git a/stand/forth/check-password.4th b/stand/forth/check-password.4th
new file mode 100644
index 0000000..d41777c
--- /dev/null
+++ b/stand/forth/check-password.4th
@@ -0,0 +1,179 @@
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+marker task-check-password.4th
+
+include /boot/screen.4th
+
+vocabulary password-processing
+only forth also password-processing definitions
+
+13 constant enter_key \ The decimal ASCII value for Enter key
+8 constant bs_key \ The decimal ASCII value for Backspace key
+21 constant ctrl_u \ The decimal ASCII value for Ctrl-U sequence
+255 constant readmax \ Maximum number of characters for the password
+
+variable read-tick \ Twiddle position (used by read)
+variable read-start \ Starting X offset (column)(used by read)
+
+create readval readmax allot \ input obtained (up to readmax characters)
+variable readlen \ input length
+
+\ This function blocks program flow (loops forever) until a key is pressed.
+\ The key that was pressed is added to the top of the stack in the form of its
+\ decimal ASCII representation. Note: the stack cannot be empty when this
+\ function starts or an underflow exception will occur. Simplest way to prevent
+\ this is to pass 0 as a stack parameter (ie. `0 sgetkey'). This function is
+\ called by the read function. You need not call it directly. NOTE: arrow keys
+\ show as 0 on the stack
+\
+: sgetkey ( -- )
+
+ begin \ Loop forever
+ key? if \ Was a key pressed? (see loader(8))
+ drop \ Remove stack-cruft
+ key \ Get the key that was pressed
+
+ \ Check key pressed (see loader(8)) and input limit
+ dup 0<> if ( and ) readlen @ readmax < if
+ \ Spin the twiddle and then exit this function
+ read-tick @ dup 1+ 4 mod read-tick !
+ 2 spaces
+ dup 0 = if ( 1 ) ." /" else
+ dup 1 = if ( 2 ) ." -" else
+ dup 2 = if ( 3 ) ." \" else
+ dup 3 = if ( 4 ) ." |" else
+ 1 spaces
+ then then then then drop
+ read-start @ 25 at-xy
+ exit
+ then then
+
+ \ Always allow Backspace, Enter, and Ctrl-U
+ dup bs_key = if exit then
+ dup enter_key = if exit then
+ dup ctrl_u = if exit then
+ then
+ 50 ms \ Sleep for 50 milliseconds (see loader(8))
+ again
+;
+
+: cfill ( c c-addr/u -- )
+ begin dup 0> while
+ -rot 2dup c! 1+ rot 1-
+ repeat 2drop drop
+;
+
+: read-reset ( -- )
+ 0 readlen !
+ 0 readval readmax cfill
+;
+
+: read ( c-addr/u -- ) \ Expects string prompt as stack input
+
+ 0 25 at-xy \ Move the cursor to the bottom-left
+ dup 1+ read-start ! \ Store X offset after the prompt
+ 0 readlen ! \ Initialize the read length
+ type \ Print the prompt
+
+ begin \ Loop forever
+
+ 0 sgetkey \ Block here, waiting for a key to be pressed
+
+ \ We are not going to echo the password to the screen (for
+ \ security reasons). If Enter is pressed, we process the
+ \ password, otherwise augment the key to a string.
+
+ dup enter_key = if
+ drop \ Clean up stack cruft
+ 3 spaces \ Erase the twiddle
+ 10 emit \ Echo new line
+ exit
+ else dup ctrl_u = if
+ 3 spaces read-start @ 25 at-xy \ Erase the twiddle
+ 0 readlen ! \ Reset input to NULL
+ else dup bs_key = if
+ readlen @ 1 - dup readlen ! \ Decrement input length
+ dup 0< if drop 0 dup readlen ! then \ Don't go negative
+ 0= if 3 spaces read-start @ 25 at-xy then \ Twiddle
+ else dup \ Store the character
+ \ NB: sgetkey prevents overflow by way of blocking
+ \ at readmax except for Backspace or Enter
+ readlen @ 1+ dup readlen ! 1- readval + c!
+ then then then
+
+ drop \ last key pressed
+ again \ Enter was not pressed; repeat
+;
+
+only forth definitions also password-processing
+
+: check-password ( -- )
+
+ \ Do not allow the user to proceed beyond this point if a boot-lock
+ \ password has been set (preventing even boot from proceeding)
+ s" bootlock_password" getenv dup -1 <> if
+ dup readmax > if drop readmax then
+ begin
+ s" Boot Password: " read ( prompt -- )
+ 2dup readval readlen @ compare 0<>
+ while
+ 3000 ms ." loader: incorrect password" 10 emit
+ repeat
+ 2drop read-reset
+ else drop then
+
+ \ Prompt for GEOM ELI (geli(8)) passphrase if enabled
+ s" geom_eli_passphrase_prompt" getenv dup -1 <> if
+ s" YES" compare-insensitive 0= if
+ s" GELI Passphrase: " read ( prompt -- )
+ readval readlen @ s" kern.geom.eli.passphrase" setenv
+ read-reset
+ then
+ else drop then
+
+ \ Exit if a password was not set
+ s" password" getenv -1 = if exit else drop then
+
+ \ We should prevent the user from visiting the menu or dropping to the
+ \ interactive loader(8) prompt, but still allow the machine to boot...
+
+ 0 autoboot
+
+ \ Only reached if autoboot fails for any reason (including if/when
+ \ the user aborts/escapes the countdown sequence leading to boot).
+
+ s" password" getenv dup readmax > if drop readmax then
+ begin
+ s" Password: " read ( prompt -- )
+ 2dup readval readlen @ compare 0= if \ Correct password?
+ 2drop read-reset exit
+ then
+ 3000 ms ." loader: incorrect password" 10 emit
+ again
+;
+
+only forth definitions
diff --git a/stand/forth/check-password.4th.8 b/stand/forth/check-password.4th.8
new file mode 100644
index 0000000..db0aa4b
--- /dev/null
+++ b/stand/forth/check-password.4th.8
@@ -0,0 +1,167 @@
+.\" Copyright (c) 2011-2015 Devin Teske
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 20, 2015
+.Dt CHECK-PASSWORD.4TH 8
+.Os
+.Sh NAME
+.Nm check-password.4th
+.Nd FreeBSD password-checking boot module
+.Sh DESCRIPTION
+The file that goes by the name of
+.Nm
+is a set of commands designed to do one or more of the following:
+.Pp
+.Dl o Prevent booting without password
+.Dl o Prevent modification of boot options without password
+.Dl o Provide a password to mount geli(8) encrypted root disk(s)
+.Pp
+The commands of
+.Nm
+by themselves are not enough for most uses.
+Please refer to the
+examples below for the most common situations, and to
+.Xr loader 8
+for additional commands.
+.Pp
+Before using any of the commands provided in
+.Nm ,
+it must be included
+through the command:
+.Pp
+.Dl include check-password.4th
+.Pp
+This line is present in
+.Pa /boot/loader.4th
+file, so it is not needed (and should not be re-issued) in a normal setup.
+.Pp
+The commands provided by it are:
+.Pp
+.Bl -tag -width disable-module_module -compact -offset indent
+.It Ic check-password
+Multi-purpose function that can protect the interactive boot menu,
+prevent boot without password, or prompt for geli(8) passphrase
+.Pq depending on Xr loader.conf 5 settings .
+.Pp
+First checks
+.Va bootlock_password
+and if-set, the user cannot continue until the correct password is entered.
+.Pp
+Next, checks
+.Va geom_eli_passphrase_prompt
+and if set to
+.Li YES
+.Pq case-insensitive
+prompts the user to enter their GELI password for later mounting of the root
+device(s) during boot.
+.Pp
+Last, checks
+.Va password
+and if-set, tries to
+.Ic autoboot
+and only prompts for password on failure or user-interrupt.
+See
+.Xr loader.conf 5
+for additional information.
+.El
+.Pp
+The environment variables that effect its behavior are:
+.Bl -tag -width bootlock_password -offset indent
+.It Va bootlock_password
+Sets the bootlock password (up to 16 characters long) that is required by
+.Ic check-password
+to be entered before the system is allowed to boot.
+.It Va geom_eli_passphrase_prompt
+Selects whether loader(8) will prompt for GELI credentials, handing-off to the
+kernel for later mounting of
+.Xr geli 8
+encrypted root device(s).
+.It Va password
+Sets the password (up to 16 characters long) that is required by
+.Ic check-password
+before the user is allowed to visit the boot menu.
+.El
+.Sh FILES
+.Bl -tag -width /boot/check-password.4th -compact
+.It Pa /boot/loader
+The
+.Xr loader 8 .
+.It Pa /boot/check-password.4th
+.Nm
+itself.
+.It Pa /boot/loader.rc
+.Xr loader 8
+bootstrapping script.
+.El
+.Sh EXAMPLES
+Standard i386
+.Pa /boot/loader.rc :
+.Pp
+.Bd -literal -offset indent -compact
+include /boot/loader.4th
+check-password
+.Ed
+.Pp
+Set a password in
+.Xr loader.conf 5
+to prevent modification of boot options:
+.Pp
+.Bd -literal -offset indent -compact
+password="abc123"
+.Ed
+.Pp
+Set a password in
+.Xr loader.conf 5
+to prevent booting without password:
+.Pp
+.Bd -literal -offset indent -compact
+bootlock_password="boot"
+.Ed
+.Pp
+Add the following to
+.Xr loader.conf 5
+to generate a prompt at boot to collect GELI credentials for mounting
+.Xr geli 8
+encrypted root device(s):
+.Pp
+.Bd -literal -offset indent -compact
+geom_eli_passphrase_prompt="YES"
+.Ed
+.Sh SEE ALSO
+.Xr loader.conf 5 ,
+.Xr loader 8 ,
+.Xr loader.4th 8
+.Sh HISTORY
+The
+.Nm
+set of commands first appeared in
+.Fx 9.0 .
+.Sh AUTHORS
+The
+.Nm
+set of commands was written by
+.An -nosplit
+.An Devin Teske Aq dteske@FreeBSD.org .
diff --git a/stand/forth/color.4th b/stand/forth/color.4th
new file mode 100644
index 0000000..65e6de9
--- /dev/null
+++ b/stand/forth/color.4th
@@ -0,0 +1,49 @@
+\ Copyright (c) 2011-2013 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+marker task-color.4th
+
+\ This function returns FALSE if the `loader_color' environment variable is set
+\ to NO, no, or 0. Otherwise, TRUE is returned (unless booting serial).
+\
+: loader_color? ( -- N )
+
+ s" loader_color" getenv dup -1 <> if
+
+ 2dup s" NO" compare-insensitive 0= if
+ 2drop
+ FALSE exit
+ then
+ 2dup s" 0" compare 0= if
+ 2drop
+ FALSE exit
+ then
+ drop
+ then
+ drop
+
+ boot_serial? if FALSE else TRUE then
+;
diff --git a/stand/forth/color.4th.8 b/stand/forth/color.4th.8
new file mode 100644
index 0000000..9191da0
--- /dev/null
+++ b/stand/forth/color.4th.8
@@ -0,0 +1,116 @@
+.\" Copyright (c) 2011-2013 Devin Teske
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 6, 2013
+.Dt COLOR.4TH 8
+.Os
+.Sh NAME
+.Nm color.4th
+.Nd FreeBSD color-detection boot module
+.Sh DESCRIPTION
+The file that goes by the name of
+.Nm
+is a set of commands designed to simplify color logic.
+The commands of
+.Nm
+by themselves are not enough for most uses.
+Please refer to the
+examples below for the most common situations, and to
+.Xr loader 8
+for additional commands.
+.Pp
+Before using any of the commands provided in
+.Nm ,
+it must be included
+through the command:
+.Pp
+.Dl include color.4th
+.Pp
+This line is present in
+.Pa /boot/loader.4th
+file, so it is not needed (and should not be re-issued) in a normal setup.
+.Pp
+The commands provided by it are:
+.Pp
+.Bl -tag -width disable-module_module -compact -offset indent
+.It Ic loader_color?
+Returns FALSE if the
+.Ic loader_color
+environment variable is set to
+.Dq NO
+(case-insensitive) or
+.Dq 0 .
+Otherwise returns TRUE
+.Pq unless booting serial .
+.El
+.Pp
+The environment variables that effect its behavior are:
+.Bl -tag -width bootfile -offset indent
+.It Va loader_color
+If set to
+.Dq NO
+(case-insensitive) or
+.Dq 0 ,
+causes
+.Ic loader_color?
+to return FALSE, indicating to many modules that color should not be used.
+.El
+.Sh FILES
+.Bl -tag -width /boot/loader.4th -compact
+.It Pa /boot/loader
+The
+.Xr loader 8 .
+.It Pa /boot/color.4th
+.Nm
+itself.
+.It Pa /boot/loader.rc
+.Xr loader 8
+bootstrapping script.
+.El
+.Sh EXAMPLES
+Standard i386
+.Pa /boot/loader.rc :
+.Pp
+Use color where applicable:
+.Pp
+.Bd -literal -offset indent -compact
+loader_color="YES"
+.Ed
+.Sh SEE ALSO
+.Xr loader.conf 5 ,
+.Xr loader 8 ,
+.Xr loader.4th 8
+.Sh HISTORY
+The
+.Nm
+set of commands first appeared in
+.Fx 9.0 .
+.Sh AUTHORS
+The
+.Nm
+set of commands was written by
+.An -nosplit
+.An Devin Teske Aq dteske@FreeBSD.org .
diff --git a/stand/forth/delay.4th b/stand/forth/delay.4th
new file mode 100644
index 0000000..28cfa5c
--- /dev/null
+++ b/stand/forth/delay.4th
@@ -0,0 +1,119 @@
+\ Copyright (c) 2008-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+marker task-delay.4th
+
+vocabulary delay-processing
+only forth also delay-processing definitions
+
+2 constant delay_default \ Default delay (in seconds)
+3 constant etx_key \ End-of-Text character produced by Ctrl+C
+13 constant enter_key \ Carriage-Return character produce by ENTER
+27 constant esc_key \ Escape character produced by ESC or Ctrl+[
+
+variable delay_tstart \ state variable used for delay timing
+variable delay_delay \ determined configurable delay duration
+variable delay_cancelled \ state variable for user cancellation
+variable delay_showdots \ whether continually print dots while waiting
+
+only forth definitions also delay-processing
+
+: delay_execute ( -- )
+
+ \ make sure that we have a command to execute
+ s" delay_command" getenv dup -1 = if
+ drop exit
+ then
+
+ \ read custom time-duration (if set)
+ s" loader_delay" getenv dup -1 = if
+ drop \ no custom duration (remove dup'd bunk -1)
+ delay_default \ use default setting (replacing bunk -1)
+ else
+ \ make sure custom duration is a number
+ ?number 0= if
+ delay_default \ use default if otherwise
+ then
+ then
+
+ \ initialize state variables
+ delay_delay ! \ stored value is on the stack from above
+ seconds delay_tstart ! \ store the time we started
+ 0 delay_cancelled ! \ boolean flag indicating user-cancelled event
+
+ false delay_showdots ! \ reset to zero and read from environment
+ s" delay_showdots" getenv dup -1 <> if
+ 2drop \ don't need the value, just existence
+ true delay_showdots !
+ else
+ drop
+ then
+
+ \ Loop until we have exceeded the desired time duration
+ begin
+ 25 ms \ sleep for 25 milliseconds (40 iterations/sec)
+
+ \ throw some dots up on the screen if desired
+ delay_showdots @ if
+ ." ." \ dots visually aid in the perception of time
+ then
+
+ \ was a key depressed?
+ key? if
+ key \ obtain ASCII value for keystroke
+ dup enter_key = if
+ -1 delay_delay ! \ break loop
+ then
+ dup etx_key = swap esc_key = OR if
+ -1 delay_delay ! \ break loop
+ -1 delay_cancelled ! \ set cancelled flag
+ then
+ then
+
+ \ if the time duration is set to zero, loop forever
+ \ waiting for either ENTER or Ctrl-C/Escape to be pressed
+ delay_delay @ 0> if
+ \ calculate elapsed time
+ seconds delay_tstart @ - delay_delay @ >
+ else
+ -1 \ break loop
+ then
+ until
+
+ \ if we were throwing up dots, throw up a line-break
+ delay_showdots @ if
+ cr
+ then
+
+ \ did the user press either Ctrl-C or Escape?
+ delay_cancelled @ if
+ 2drop \ we don't need the command string anymore
+ else
+ evaluate \ evaluate/execute the command string
+ then
+;
+
+only forth definitions
diff --git a/stand/forth/delay.4th.8 b/stand/forth/delay.4th.8
new file mode 100644
index 0000000..af31fd0
--- /dev/null
+++ b/stand/forth/delay.4th.8
@@ -0,0 +1,126 @@
+.\" Copyright (c) 2011 Devin Teske
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 18, 2011
+.Dt DELAY.4TH 8
+.Os
+.Sh NAME
+.Nm delay.4th
+.Nd FreeBSD debugging boot module
+.Sh DESCRIPTION
+The file that goes by the name of
+.Nm
+is a set of commands designed to add debugging capabilities to
+.Xr loader 8 .
+The commands of
+.Nm
+by themselves are not enough for most uses.
+Please refer to the
+examples below for the most common situations, and to
+.Xr loader 8
+for additional commands.
+.Pp
+Before using any of the commands provided in
+.Nm ,
+it must be included
+through the command:
+.Pp
+.Dl include delay.4th
+.Pp
+This line is present in
+.Pa /boot/beastie.4th
+file, so it is not needed (and should not be re-issued) in a normal setup.
+.Pp
+The commands provided by it are:
+.Pp
+.Bl -tag -width disable-module_module -compact -offset indent
+.It Ic delay_execute
+Executes the [string] procedure stored in the
+.Ic delay_command
+environment variable after
+.Ic loader_delay
+seconds.
+.Pp
+If the optional
+.Ic delay_showdots
+environment variable is set, a continuous series of dots is printed.
+.Pp
+During the duration, the user can either press Ctrl-C (or Esc) to abort or
+ENTER to proceed immediately.
+.El
+.Pp
+The environment variables that effect its behavior are:
+.Bl -tag -width bootfile -offset indent
+.It Va delay_command
+The command to be executed by
+.Ic delay_execute .
+.It Va loader_delay
+The duration (in seconds) to delay before executing
+.Ic delay_command .
+.It Va delay_showdots
+If set, will cause
+.Ic delay_execute
+to print a continuous series of dots during the delay duration.
+.El
+.Sh FILES
+.Bl -tag -width /boot/loader.4th -compact
+.It Pa /boot/loader
+The
+.Xr loader 8 .
+.It Pa /boot/delay.4th
+.Nm
+itself.
+.It Pa /boot/loader.rc
+.Xr loader 8
+bootstrapping script.
+.El
+.Sh EXAMPLES
+Introducing a 5-second delay before including another file from
+.Pa /boot/loader.rc :
+.Pp
+.Bd -literal -offset indent -compact
+include /boot/delay.4th
+set delay_command="include /boot/other.4th"
+set delay_showdots
+set loader_delay=5
+delay_execute
+.Ed
+.Sh SEE ALSO
+.Xr loader.conf 5 ,
+.Xr beastie.4th 8 ,
+.Xr loader 8 ,
+.Xr loader.4th 8
+.Sh HISTORY
+The
+.Nm
+set of commands first appeared in
+.Fx 9.0 .
+.Sh AUTHORS
+The
+.Nm
+set of commands was written by
+.An -nosplit
+.An Devin Teske Aq dteske@FreeBSD.org .
diff --git a/stand/forth/efi.4th b/stand/forth/efi.4th
new file mode 100644
index 0000000..7c1bdf3
--- /dev/null
+++ b/stand/forth/efi.4th
@@ -0,0 +1,30 @@
+\ Copyright (c) 2016 Netflix, Inc
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+only forth definitions
+
+\ Place holder for more functions
+.( EFI boot environment) cr
diff --git a/stand/forth/frames.4th b/stand/forth/frames.4th
new file mode 100644
index 0000000..0f8d460
--- /dev/null
+++ b/stand/forth/frames.4th
@@ -0,0 +1,165 @@
+\ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org>
+\ Copyright (c) 2012-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+marker task-frames.4th
+
+vocabulary frame-drawing
+only forth also frame-drawing definitions
+
+\ XXX Filled boxes are left as an exercise for the reader... ;-/
+
+variable h_el
+variable v_el
+variable lt_el
+variable lb_el
+variable rt_el
+variable rb_el
+variable fill
+
+\ ASCII frames (used when serial console is detected)
+ 45 constant ascii_dash
+ 61 constant ascii_equal
+124 constant ascii_pipe
+ 43 constant ascii_plus
+
+s" arch-pc98" environment? [if]
+ \ Single frames
+ 149 constant sh_el
+ 150 constant sv_el
+ 152 constant slt_el
+ 154 constant slb_el
+ 153 constant srt_el
+ 155 constant srb_el
+ \ Double frames
+ 149 constant dh_el
+ 150 constant dv_el
+ 152 constant dlt_el
+ 154 constant dlb_el
+ 153 constant drt_el
+ 155 constant drb_el
+ \ Fillings
+ 0 constant fill_none
+ 32 constant fill_blank
+ 135 constant fill_dark
+ 135 constant fill_med
+ 135 constant fill_bright
+[else]
+ \ Single frames
+ 196 constant sh_el
+ 179 constant sv_el
+ 218 constant slt_el
+ 192 constant slb_el
+ 191 constant srt_el
+ 217 constant srb_el
+ \ Double frames
+ 205 constant dh_el
+ 186 constant dv_el
+ 201 constant dlt_el
+ 200 constant dlb_el
+ 187 constant drt_el
+ 188 constant drb_el
+ \ Fillings
+ 0 constant fill_none
+ 32 constant fill_blank
+ 176 constant fill_dark
+ 177 constant fill_med
+ 178 constant fill_bright
+[then]
+
+only forth definitions also frame-drawing
+
+: hline ( len x y -- ) \ Draw horizontal single line
+ at-xy \ move cursor
+ 0 do
+ h_el @ emit
+ loop
+;
+
+: f_ascii ( -- ) ( -- ) \ set frames to ascii
+ ascii_dash h_el !
+ ascii_pipe v_el !
+ ascii_plus lt_el !
+ ascii_plus lb_el !
+ ascii_plus rt_el !
+ ascii_plus rb_el !
+;
+
+: f_single ( -- ) \ set frames to single
+ boot_serial? if f_ascii exit then
+ sh_el h_el !
+ sv_el v_el !
+ slt_el lt_el !
+ slb_el lb_el !
+ srt_el rt_el !
+ srb_el rb_el !
+;
+
+: f_double ( -- ) \ set frames to double
+ boot_serial? if
+ f_ascii
+ ascii_equal h_el !
+ exit
+ then
+ dh_el h_el !
+ dv_el v_el !
+ dlt_el lt_el !
+ dlb_el lb_el !
+ drt_el rt_el !
+ drb_el rb_el !
+;
+
+: vline ( len x y -- ) \ Draw vertical single line
+ 2dup 4 pick
+ 0 do
+ at-xy
+ v_el @ emit
+ 1+
+ 2dup
+ loop
+ 2drop 2drop drop
+;
+
+: box ( w h x y -- ) \ Draw a box
+ 2dup 1+ 4 pick 1- -rot
+ vline \ Draw left vert line
+ 2dup 1+ swap 5 pick + swap 4 pick 1- -rot
+ vline \ Draw right vert line
+ 2dup swap 1+ swap 5 pick 1- -rot
+ hline \ Draw top horiz line
+ 2dup swap 1+ swap 4 pick + 5 pick 1- -rot
+ hline \ Draw bottom horiz line
+ 2dup at-xy lt_el @ emit \ Draw left-top corner
+ 2dup 4 pick + at-xy lb_el @ emit \ Draw left bottom corner
+ 2dup swap 5 pick + swap at-xy rt_el @ emit \ Draw right top corner
+ 2 pick + swap 3 pick + swap at-xy rb_el @ emit
+ 2drop
+;
+
+f_single
+fill_none fill !
+
+only forth definitions
diff --git a/stand/forth/loader.4th b/stand/forth/loader.4th
new file mode 100644
index 0000000..9486237
--- /dev/null
+++ b/stand/forth/loader.4th
@@ -0,0 +1,266 @@
+\ Copyright (c) 1999 Daniel C. Sobral <dcs@FreeBSD.org>
+\ Copyright (c) 2011-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+only forth definitions
+
+s" arch-i386" environment? [if] [if]
+ s" loader_version" environment? [if]
+ 11 < [if]
+ .( Loader version 1.1+ required) cr
+ abort
+ [then]
+ [else]
+ .( Could not get loader version!) cr
+ abort
+ [then]
+[then] [then]
+
+256 dictthreshold ! \ 256 cells minimum free space
+2048 dictincrease ! \ 2048 additional cells each time
+
+include /boot/support.4th
+include /boot/color.4th
+include /boot/delay.4th
+include /boot/check-password.4th
+s" efi-version" getenv? [if]
+ include /boot/efi.4th
+[then]
+
+only forth definitions
+
+: bootmsg ( -- )
+ loader_color? dup ( -- bool bool )
+ if 7 fg 4 bg then
+ ." Booting..."
+ if me then
+ cr
+;
+
+: try-menu-unset
+ \ menu-unset may not be present
+ s" beastie_disable" getenv
+ dup -1 <> if
+ s" YES" compare-insensitive 0= if
+ exit
+ then
+ else
+ drop
+ then
+ s" menu-unset"
+ sfind if
+ execute
+ else
+ drop
+ then
+ s" menusets-unset"
+ sfind if
+ execute
+ else
+ drop
+ then
+;
+
+only forth also support-functions also builtins definitions
+
+: boot
+ 0= if ( interpreted ) get_arguments then
+
+ \ Unload only if a path was passed
+ dup if
+ >r over r> swap
+ c@ [char] - <> if
+ 0 1 unload drop
+ else
+ s" kernelname" getenv? if ( a kernel has been loaded )
+ try-menu-unset
+ bootmsg 1 boot exit
+ then
+ load_kernel_and_modules
+ ?dup if exit then
+ try-menu-unset
+ bootmsg 0 1 boot exit
+ then
+ else
+ s" kernelname" getenv? if ( a kernel has been loaded )
+ try-menu-unset
+ bootmsg 1 boot exit
+ then
+ load_kernel_and_modules
+ ?dup if exit then
+ try-menu-unset
+ bootmsg 0 1 boot exit
+ then
+ load_kernel_and_modules
+ ?dup 0= if bootmsg 0 1 boot then
+;
+
+\ ***** boot-conf
+\
+\ Prepares to boot as specified by loaded configuration files.
+
+: boot-conf
+ 0= if ( interpreted ) get_arguments then
+ 0 1 unload drop
+ load_kernel_and_modules
+ ?dup 0= if 0 1 autoboot then
+;
+
+also forth definitions previous
+
+builtin: boot
+builtin: boot-conf
+
+only forth definitions also support-functions
+
+\ ***** start
+\
+\ Initializes support.4th global variables, sets loader_conf_files,
+\ processes conf files, and, if any one such file was successfully
+\ read to the end, loads kernel and modules.
+
+: start ( -- ) ( throws: abort & user-defined )
+ s" /boot/defaults/loader.conf" initialize
+ include_conf_files
+ include_nextboot_file
+ \ If the user defined a post-initialize hook, call it now
+ s" post-initialize" sfind if execute else drop then
+ \ Will *NOT* try to load kernel and modules if no configuration file
+ \ was successfully loaded!
+ any_conf_read? if
+ s" loader_delay" getenv -1 = if
+ load_xen_throw
+ load_kernel
+ load_modules
+ else
+ drop
+ ." Loading Kernel and Modules (Ctrl-C to Abort)" cr
+ s" also support-functions" evaluate
+ s" set delay_command='load_xen_throw load_kernel load_modules'" evaluate
+ s" set delay_showdots" evaluate
+ delay_execute
+ then
+ then
+;
+
+\ ***** initialize
+\
+\ Overrides support.4th initialization word with one that does
+\ everything start one does, short of loading the kernel and
+\ modules. Returns a flag.
+
+: initialize ( -- flag )
+ s" /boot/defaults/loader.conf" initialize
+ include_conf_files
+ include_nextboot_file
+ \ If the user defined a post-initialize hook, call it now
+ s" post-initialize" sfind if execute else drop then
+ any_conf_read?
+;
+
+\ ***** read-conf
+\
+\ Read a configuration file, whose name was specified on the command
+\ line, if interpreted, or given on the stack, if compiled in.
+
+: (read-conf) ( addr len -- )
+ conf_files string=
+ include_conf_files \ Will recurse on new loader_conf_files definitions
+;
+
+: read-conf ( <filename> | addr len -- ) ( throws: abort & user-defined )
+ state @ if
+ \ Compiling
+ postpone (read-conf)
+ else
+ \ Interpreting
+ bl parse (read-conf)
+ then
+; immediate
+
+\ show, enable, disable, toggle module loading. They all take module from
+\ the next word
+
+: set-module-flag ( module_addr val -- ) \ set and print flag
+ over module.flag !
+ dup module.name strtype
+ module.flag @ if ." will be loaded" else ." will not be loaded" then cr
+;
+
+: enable-module find-module ?dup if true set-module-flag then ;
+
+: disable-module find-module ?dup if false set-module-flag then ;
+
+: toggle-module find-module ?dup if dup module.flag @ 0= set-module-flag then ;
+
+\ ***** show-module
+\
+\ Show loading information about a module.
+
+: show-module ( <module> -- ) find-module ?dup if show-one-module then ;
+
+\ Words to be used inside configuration files
+
+: retry false ; \ For use in load error commands
+: ignore true ; \ For use in load error commands
+
+\ Return to strict forth vocabulary
+
+: #type
+ over - >r
+ type
+ r> spaces
+;
+
+: .? 2 spaces 2swap 15 #type 2 spaces type cr ;
+
+\ Execute the ? command to print all the commands defined in
+\ C, then list the ones we support here. Please note that this
+\ doesn't use pager_* routines that the C implementation of ?
+\ does, so these will always appear, even if you stop early
+\ there. And they may cause the commands to scroll off the
+\ screen if the number of commands modulus LINES is close
+\ to LINEs....
+: ?
+ ['] ? execute
+ s" boot-conf" s" load kernel and modules, then autoboot" .?
+ s" read-conf" s" read a configuration file" .?
+ s" enable-module" s" enable loading of a module" .?
+ s" disable-module" s" disable loading of a module" .?
+ s" toggle-module" s" toggle loading of a module" .?
+ s" show-module" s" show module load data" .?
+ s" try-include" s" try to load/interpret files" .?
+;
+
+: try-include ( -- ) \ see loader.4th(8)
+ ['] include ( -- xt ) \ get the execution token of `include'
+ catch ( xt -- exception# | 0 ) if \ failed
+ LF parse ( c -- s-addr/u ) 2drop \ advance >in to EOL (drop data)
+ \ ... prevents words unused by `include' from being interpreted
+ then
+; immediate \ interpret immediately for access to `source' (aka tib)
+
+only forth definitions
diff --git a/stand/forth/loader.4th.8 b/stand/forth/loader.4th.8
new file mode 100644
index 0000000..b689077
--- /dev/null
+++ b/stand/forth/loader.4th.8
@@ -0,0 +1,233 @@
+.\" Copyright (c) 1999 Daniel C. Sobral
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 13, 2013
+.Dt LOADER.4TH 8
+.Os
+.Sh NAME
+.Nm loader.4th
+.Nd loader.conf processing tools
+.Sh DESCRIPTION
+The file that goes by the name of
+.Nm
+is a set of commands designed to manipulate
+.Xr loader.conf 5
+files.
+The default
+.Pa /boot/loader.rc
+includes
+.Nm
+and uses one of its commands to automatically read and process
+the standard
+.Xr loader.conf 5
+files.
+Other commands exists to help the user specify alternate
+configurations.
+.Pp
+The commands of
+.Nm
+by themselves are not enough for most uses.
+Please refer to the
+examples below for the most common situations, and to
+.Xr loader 8
+for additional commands.
+.Pp
+Before using any of the commands provided in
+.Nm ,
+it must be included
+through the command:
+.Pp
+.Dl include loader.4th
+.Pp
+This line is present in the default
+.Pa /boot/loader.rc
+file, so it is not needed (and should not be re-issued) in a normal setup.
+.Pp
+The commands provided by it are:
+.Bl -tag -width disable-module_module -compact -offset indent
+.It Ic boot
+.It Ic boot Ar kernelname Op Cm ...
+.It Ic boot Ar directory Op Cm ...
+.It Ic boot Fl flag Cm ...
+Boot as specified by the
+.Xr loader.conf 5
+files read.
+.Pp
+Depending on the arguments passed, it can override boot flags and
+either the kernel name or the search path for kernel and modules.
+.Pp
+.It Ic boot-conf
+.It Ic boot-conf Ar kernelname Op Cm ...
+.It Ic boot-conf Ar directory Op Cm ...
+.It Ic boot-conf Fl flag Cm ...
+Works like
+.Ic boot
+described above, but instead of booting immediately, uses
+.Ic autoboot ,
+so it can be stopped.
+.Pp
+.It Ic start
+Reads
+.Pa /boot/defaults/loader.conf ,
+all other
+.Xr loader.conf 5
+files specified in it, then loads the desired kernel and modules
+.Pq if not already loaded .
+After which you can use the
+.Ic boot
+or
+.Ic autoboot
+commmands or simply exit (provided
+.Va autoboot_delay
+is not set to NO) to boot the system.
+.Ic start
+is the command used in the default
+.Pa /boot/loader.rc
+file
+.Pq see Xr loader 8 .
+.Pp
+.It Ic initialize
+Initialize the support library so commands can be used without executing
+.Ic start
+first.
+Like
+.Ic start ,
+it reads
+.Pa /boot/defaults/loader.conf
+and all other
+.Xr loader.conf 5
+files specified in it
+.Pq but does not load kernel or modules .
+Returns a flag on the stack to indicate
+if any configuration files were successfully loaded.
+.Pp
+.It Ic read-conf Ar filename
+Reads and processes a
+.Xr loader.conf 5
+file.
+Does not proceed to boot.
+.Pp
+.It Ic enable-module Ar module
+Enables the loading of
+.Ar module .
+.Pp
+.It Ic disable-module Ar module
+Disables the loading of
+.Ar module .
+.Pp
+.It Ic toggle-module Ar module
+Toggles the loading of
+.Ar module
+on and off.
+.Pp
+.It Ic show-module Ar module
+Shows the information gathered in the
+.Xr loader.conf 5
+files about the module
+.Ar module .
+.Pp
+.It Ic retry
+Used inside
+.Xr loader.conf 5
+files to specify the action after a module loading fails.
+.Pp
+.It Ic ignore
+Used inside
+.Xr loader.conf 5
+files to specify the action after a module loading fails.
+.It Ic try-include Ar file Op Ar
+Process script files if they exist.
+Each file, in turn, is completely read into memory,
+and then each of its lines is passed to the command line interpreter.
+If any error is returned by the interpreter, the try-include
+command aborts immediately, without reading any other files, and
+silently returns without error.
+.El
+.Sh FILES
+.Bl -tag -width /boot/loader.4th -compact
+.It Pa /boot/loader
+The
+.Xr loader 8 .
+.It Pa /boot/loader.4th
+.Nm
+itself.
+.It Pa /boot/loader.rc
+.Xr loader 8
+bootstrapping script.
+.It Pa /boot/defaults/loader.conf
+File loaded by the
+.Ic start
+command.
+.El
+.Sh EXAMPLES
+Standard
+.Pa /boot/loader.rc :
+.Pp
+.Bd -literal -offset indent -compact
+include /boot/loader.4th
+start
+.Ed
+.Pp
+Load a different kernel with the standard configuration:
+.Pp
+.Bd -literal -offset indent -compact
+set kernel="kernel.old"
+unload
+boot-conf
+.Ed
+.Pp
+Read an additional configuration file and then proceed to boot:
+.Pp
+.Bd -literal -offset indent -compact
+unload
+read-conf /boot/special.conf
+boot-conf
+.Ed
+.Pp
+Disable the loading of the splash screen module and bitmap and then
+proceed to boot:
+.Pp
+.Bd -literal -offset indent -compact
+unload
+disable-module splash_bmp
+disable-module bitmap
+boot-conf
+.Ed
+.Sh SEE ALSO
+.Xr loader.conf 5 ,
+.Xr loader 8
+.Sh HISTORY
+The
+.Nm
+set of commands first appeared in
+.Fx 3.2 .
+.Sh AUTHORS
+The
+.Nm
+set of commands was written by
+.An Daniel C. Sobral Aq dcs@FreeBSD.org .
+.Sh BUGS
+A British espionage series.
diff --git a/stand/forth/loader.conf b/stand/forth/loader.conf
new file mode 100644
index 0000000..9136659
--- /dev/null
+++ b/stand/forth/loader.conf
@@ -0,0 +1,575 @@
+# This is loader.conf - a file full of useful variables that you can
+# set to change the default load behavior of your system. You should
+# not edit this file! Put any overrides into one of the
+# loader_conf_files instead and you will be able to update these
+# defaults later without spamming your local configuration information.
+#
+# All arguments must be in double quotes.
+#
+# $FreeBSD$
+
+##############################################################
+### Basic configuration options ############################
+##############################################################
+
+exec="echo Loading /boot/defaults/loader.conf"
+
+kernel="kernel" # /boot sub-directory containing kernel and modules
+bootfile="kernel" # Kernel name (possibly absolute path)
+kernel_options="" # Flags to be passed to the kernel
+
+loader_conf_files="/boot/device.hints /boot/loader.conf /boot/loader.conf.local"
+nextboot_conf="/boot/nextboot.conf"
+nextboot_enable="NO"
+
+verbose_loading="NO" # Set to YES for verbose loader output
+
+
+##############################################################
+### Splash screen configuration ############################
+##############################################################
+
+splash_bmp_load="NO" # Set this to YES for bmp splash screen!
+splash_pcx_load="NO" # Set this to YES for pcx splash screen!
+splash_txt_load="NO" # Set this to YES for TheDraw splash screen!
+vesa_load="NO" # Set this to YES to load the vesa module
+bitmap_load="NO" # Set this to YES if you want splash screen!
+bitmap_name="splash.bmp" # Set this to the name of the file
+bitmap_type="splash_image_data" # and place it on the module_path
+
+
+##############################################################
+### Random number generator configuration ##################
+##############################################################
+
+# See rc.conf(5). The entropy_boot_file config variable must agree with the
+# settings below.
+
+entropy_cache_load="YES" # Set this to NO to disable loading
+ # entropy at boot time
+entropy_cache_name="/boot/entropy" # Set this to the name of the file
+entropy_cache_type="/boot/entropy" # Required for the kernel to find
+ # the boot-time entropy cache. This
+ # must not change value even if the
+ # _name above does change!
+
+
+##############################################################
+### RAM Blacklist configuration ############################
+##############################################################
+
+ram_blacklist_load="NO" # Set this to YES to load a file
+ # containing a list of addresses to
+ # exclude from the running system.
+ram_blacklist_name="/boot/blacklist.txt" # Set this to the name of the file
+ram_blacklist_type="ram_blacklist" # Required for the kernel to find
+ # the blacklist module
+
+
+##############################################################
+### Initial memory disk settings ###########################
+##############################################################
+
+#mdroot_load="YES" # The "mdroot" prefix is arbitrary.
+#mdroot_type="md_image" # Create md(4) disk at boot.
+#mdroot_name="/boot/root.img" # Path to a file containing the image.
+#rootdev="ufs:/dev/md0" # Set the root filesystem to md(4) device.
+
+
+##############################################################
+### Loader settings ########################################
+##############################################################
+
+#loader_delay="3" # Delay in seconds before loading anything.
+ # Default is unset and disabled (no delay).
+#autoboot_delay="10" # Delay in seconds before autobooting,
+ # set to -1 if you don't want user to be
+ # allowed to interrupt autoboot process and
+ # escape to the loader prompt, set to
+ # "NO" to disable autobooting
+#password="" # Prevent changes to boot options
+#bootlock_password="" # Prevent booting (see check-password.4th(8))
+#geom_eli_passphrase_prompt="NO" # Prompt for geli(8) passphrase to mount root
+bootenv_autolist="YES" # Auto populate the list of ZFS Boot Environments
+#beastie_disable="NO" # Turn the beastie boot menu on and off
+#kernels="kernel kernel.old" # Kernels to display in the boot menu
+#loader_logo="orbbw" # Desired logo: orbbw, orb, fbsdbw, beastiebw, beastie, none
+#comconsole_speed="9600" # Set the current serial console speed
+#console="vidconsole" # A comma separated list of console(s)
+#currdev="disk1s1a" # Set the current device
+module_path="/boot/modules;/boot/dtb" # Set the module search path
+#prompt="\\${interpret}" # Set the command prompt
+#root_disk_unit="0" # Force the root disk unit number
+#rootdev="disk1s1a" # Set the root filesystem
+#dumpdev="disk1s1b" # Set a dump device early in the boot process
+#tftp.blksize="1428" # Set the RFC 2348 TFTP block size.
+ # If the TFTP server does not support RFC 2348,
+ # the block size is set to 512. If the value
+ # is out of range ( < 8 || > 9008 ) an error is
+ # returned.
+#twiddle_divisor="1" # >1 means slow down the progress indicator.
+
+
+##############################################################
+### Kernel settings ########################################
+##############################################################
+
+# The following boot_ variables are enabled by setting them to any value.
+# Their presence in the kernel environment (see kenv(1)) has the same
+# effect as setting the given boot flag (see boot(8)).
+
+#boot_askname="" # -a: Prompt the user for the name of the root device
+#boot_cdrom="" # -C: Attempt to mount root file system from CD-ROM
+#boot_ddb="" # -d: Instructs the kernel to start in the DDB debugger
+#boot_dfltroot="" # -r: Use the statically configured root file system
+#boot_gdb="" # -g: Selects gdb-remote mode for the kernel debugger
+#boot_multicons="" # -D: Use multiple consoles
+#boot_mute="" # -m: Mute the console
+#boot_pause="" # -p: Pause after each line during device probing
+#boot_serial="" # -h: Use serial console
+#boot_single="" # -s: Start system in single-user mode
+#boot_verbose="" # -v: Causes extra debugging information to be printed
+#init_path="/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init"
+ # Sets the list of init candidates
+#init_shell="/bin/sh" # The shell binary used by init(8).
+#init_script="" # Initial script to run by init(8) before chrooting.
+#init_chroot="" # Directory for init(8) to chroot into.
+
+
+##############################################################
+### Kernel tunables ########################################
+##############################################################
+
+#hw.physmem="1G" # Limit physical memory. See loader(8)
+#kern.dfldsiz="" # Set the initial data size limit
+#kern.dflssiz="" # Set the initial stack size limit
+#kern.hz="100" # Set the kernel interval timer rate
+#kern.maxbcache="" # Set the max buffer cache KVA storage
+#kern.maxdsiz="" # Set the max data size
+#kern.maxfiles="" # Set the sys. wide open files limit
+#kern.maxproc="" # Set the maximum # of processes
+#kern.maxssiz="" # Set the max stack size
+#kern.maxswzone="" # Set the max swmeta KVA storage
+#kern.maxtsiz="" # Set the max text size
+#kern.maxusers="32" # Set size of various static tables
+#kern.msgbufsize="65536" # Set size of kernel message buffer
+#kern.nbuf="" # Set the number of buffer headers
+#kern.ncallout="" # Set the maximum # of timer events
+#kern.ngroups="1023" # Set the maximum # of supplemental groups
+#kern.sgrowsiz="" # Set the amount to grow stack
+#kern.cam.boot_delay="10000" # Delay (in ms) of root mount for CAM bus
+ # registration, useful for USB sticks as root
+#kern.cam.scsi_delay="2000" # Delay (in ms) before probing SCSI
+#kern.ipc.maxsockets="" # Set the maximum number of sockets available
+#kern.ipc.nmbclusters="" # Set the number of mbuf clusters
+#kern.ipc.nsfbufs="" # Set the number of sendfile(2) bufs
+#net.inet.tcp.tcbhashsize="" # Set the value of TCBHASHSIZE
+#vfs.root.mountfrom="" # Specify root partition in a way the
+ # kernel understands
+#vm.kmem_size="" # Sets the size of kernel memory (bytes)
+#debug.kdb.break_to_debugger="0" # Allow console to break into debugger.
+#debug.ktr.cpumask="0xf" # Bitmask of CPUs to enable KTR on
+#debug.ktr.mask="0x1200" # Bitmask of KTR events to enable
+#debug.ktr.verbose="1" # Enable console dump of KTR events
+#net.graph.maxalloc="128" # Maximum number of queue items to allocate
+
+
+##############################################################
+### ATA modules ############################################
+##############################################################
+
+ataacard_load="NO" # ACARD
+ataacerlabs_load="NO" # Acer Labs Inc. (ALI)
+ataamd_load="NO" # American Micro Devices (AMD)
+ataati_load="NO" # ATI
+atacenatek_load="NO" # Cenatek
+atacypress_load="NO" # Cypress
+atacyrix_load="NO" # Cyrix
+atahighpoint_load="NO" # HighPoint
+ataintel_load="NO" # Intel
+ataite_load="NO" # Integrated Technology Inc. (ITE)
+atajmicron_load="NO" # JMicron
+atamarvell_load="NO" # Marvell
+atamicron_load="NO" # Micron
+atanational_load="NO" # National
+atanetcell_load="NO" # NetCell
+atanvidia_load="NO" # nVidia
+atapromise_load="NO" # Promise
+ataserverworks_load="NO" # ServerWorks
+atasiliconimage_load="NO" # Silicon Image Inc. (SiI) (formerly CMD)
+atasis_load="NO" # Silicon Integrated Systems Corp.(SiS)
+atavia_load="NO" # VIA Technologies Inc.
+
+
+##############################################################
+### Filesystem and related modules #########################
+##############################################################
+
+# Filesystems
+
+cd9660_load="NO" # ISO 9660 filesystem
+fdescfs_load="NO" # Filedescriptors filesystem
+linprocfs_load="NO" # Linux compatibility process filesystem
+linsysfs_load="NO" # Linux compatibility system filesystem
+msdosfs_load="NO" # FAT-12/16/32
+nfsclient_load="NO" # NFS client
+nfsserver_load="NO" # NFS server
+nullfs_load="NO" # Null filesystem
+procfs_load="NO" # Process filesystem
+unionfs_load="NO" # Union filesystem
+zfs_load="NO" # ZFS
+
+# Related stuff
+
+geom_bde_load="NO" # Disk encryption driver (see gbde(4,8))
+geom_ccd_load="NO" # Concatenated disk driver (see ccd(4),
+ # ccdconfig(8))
+geom_concat_load="NO" # Concatenated disk driver (see gconcat(8))
+geom_eli_load="NO" # Disk encryption driver (see geli(8))
+geom_gate_load="NO" # Userland disk driver (see geom_gate(4),
+ # ggatec(8), ggated(8), ggatel(8))
+geom_journal_load="NO" # Journaled filesystem driver (see gjournal(8))
+geom_label_load="NO" # File system labels (see glabel(8))
+geom_md_load="NO" # Memory disk driver (vnode/swap/malloc) (see
+ # md(4), mdconfig(8))
+geom_mirror_load="NO" # RAID1 disk driver (see gmirror(8))
+geom_mountver_load="NO" # Mount verification disk driver
+geom_nop_load="NO" # Transparent disk driver (see gnop(8))
+geom_raid3_load="NO" # RAID3 disk driver (see graid3(8))
+geom_shsec_load="NO" # Shared secret disk driver (see gshsec(8))
+geom_stripe_load="NO" # RAID0 disk driver (see gstripe(8))
+geom_uzip_load="NO" # Compressed disk images driver (see mkuzip(8))
+geom_vinum_load="NO" # Concatenated/mirror/raid driver (see vinum(4))
+
+
+##############################################################
+### FireWire modules #######################################
+##############################################################
+
+firewire_load="NO" # IEEE1394 High-performance Serial Bus
+fwe_load="NO" # Ethernet emulation driver for FireWire
+fwip_load="NO" # IP over FireWire driver
+fwohci_load="NO" # OHCI FireWire chipset device driver
+sbp_load="NO" # SBP-2 Mass Storage Devices driver
+sbp_targ_load="NO" # SBP-2 Target mode
+
+
+##############################################################
+### Screen saver modules ###################################
+##############################################################
+
+# This is best done in rc.conf
+
+screensave_load="NO" # Set to YES to load a screensaver module
+screensave_name="green_saver" # Set to the name of the screensaver module
+
+
+##############################################################
+### Emulation modules ######################################
+##############################################################
+
+cloudabi_load="NO" # Platform independent CloudABI support
+cloudabi64_load="NO" # 64-bit CloudABI executables support
+ibcs2_load="NO" # IBCS2 (SCO) emulation
+ibcs2_coff_load="NO"
+linux_load="NO" # Linux emulation
+svr4_load="NO" # SystemV R4 emulation
+streams_load="NO" # System V streams module
+
+
+##############################################################
+### Networking modules #####################################
+##############################################################
+
+if_disc_load="NO" # Discard device
+if_ef_load="NO" # pseudo-device providing support for multiple
+ # ethernet frame types
+if_epair_load="NO" # Virtual b-t-b Ethernet-like interface pair
+if_gif_load="NO" # generic tunnel interface
+if_gre_load="NO" # encapsulating network device
+if_stf_load="NO" # 6to4 tunnel interface
+if_tap_load="NO" # Ethernet tunnel software network interface
+if_tun_load="NO" # Tunnel driver (user process ppp)
+if_vlan_load="NO" # IEEE 802.1Q VLAN network interface
+ipfw_load="NO" # Firewall
+pf_load="NO" # packet filter
+
+
+##############################################################
+### Networking drivers #####################################
+##############################################################
+
+bridgestp_load="NO" # if_bridge(4) support
+miibus_load="NO" # miibus support, needed for some drivers
+carp_load="NO" # carp(4) protocol
+if_ae_load="NO" # Attansic/Atheros L2 FastEthernet
+if_age_load="NO" # Attansic/Atheros L1 Gigabit Ethernet
+if_alc_load="NO" # Atheros AR8131/AR8132 Ethernet
+if_ale_load="NO" # Atheros AR8121/AR8113/AR8114 Ethernet
+if_an_load="NO" # Aironet 4500/4800 802.11 wireless NICs
+if_ath_load="NO" # Atheros IEEE 802.11 wireless NICs
+if_aue_load="NO" # ADMtek AN986 Pegasus USB Ethernet
+if_axe_load="NO" # ASIX Electronics AX88172 USB Ethernet
+if_bce_load="NO" # Broadcom NetXtreme II Gigabit Ethernet
+if_bfe_load="NO" # Broadcom BCM4401
+if_bge_load="NO" # Broadcom BCM570x PCI Gigabit Ethernet
+if_bnxt_load="NO" # Broadcom NetXtreme-C/NetXtreme-E
+if_bridge_load="NO" # if_bridge(4) devices
+if_bwi_load="NO" # Broadcom BCM53xx IEEE 802.11b/g wireness NICs
+if_bwn_load="NO" # Broadcom BCM43xx IEEE 802.11 wireless NICs
+if_bxe_load="NO" # Broadcom NetXtreme II 10Gb Ethernet
+if_cas_load="NO" # Sun Cassini/Cassini+ and NS DP83065 Saturn
+if_cm_load="NO" # SMC (90c26, 90c56, 90c66)
+if_cs_load="NO" # Crystal Semiconductor CS8920
+if_cue_load="NO" # CATC USB-EL1210A USB Ethernet
+if_cxgb_load="NO" # Chelsio T3 10 Gigabit Ethernet
+if_cxgbe_load="NO" # Chelsio T4/T5/T6 1/10/25/40/100 Gigabit Ethernet
+if_dc_load="NO" # DEC/Intel 21143 and various workalikes
+if_de_load="NO" # DEC DC21x4x Ethernet
+if_ed_load="NO" # National Semiconductor DS8390/WD83C690
+ # Ethernet
+if_em_load="NO" # Intel(R) PRO/1000 Gigabit Ethernet
+if_en_load="NO" # Midway-based ATM interfaces
+if_ep_load="NO" # 3Com Etherlink III (3c5x9)
+if_et_load="NO" # Agere ET1310 10/100/Gigabit Ethernet
+if_ex_load="NO" # Intel EtherExpress Pro/10 Ethernet
+if_fatm_load="NO" # Fore PCA200E ATM
+if_fe_load="NO" # Fujitsu MB86960A/MB86965A based Ethernet
+ # adapters
+if_fxp_load="NO" # Intel EtherExpress PRO/100B (82557, 82558)
+if_gem_load="NO" # Sun GEM/Sun ERI/Apple GMAC
+if_hatm_load="NO" # Fore/Marconi HE155 and HE622
+if_hme_load="NO" # Sun Microelectronics STP2002-STQ Ethernet
+if_ie_load="NO" # Intel 82586
+if_igb_load="NO" # Intel(R) PRO/1000 Gigabit Ethernet
+if_ipw_load="NO" # Intel PRO/Wireless 2100 wireless
+if_iwi_load="NO" # Intel PRO/Wireless 2200BG/2225BG/2915ABG
+ # wireless
+if_iwn_load="NO" # Intel Wireless WiFi Link 802.11n wireless
+if_ixgb_load="NO" # Intel PRO/10Gb Ethernet
+if_ixgbe_load="NO" # Intel PRO/10Gb Ethernet PCI Express
+if_ixl_load="NO" # Intel XL710 Ethernet 40Gb Base driver
+if_ixlv_load="NO" # Intel XL710 Ethernet 40Gb VF driver
+if_jme_load="NO" # JMicron JMC250 Gigabit/JMC260 Fast Ethernet
+if_lagg_load="NO" # lagg(4) devices
+if_le_load="NO" # AMD Am7900 LANCE and Am79C9xx PCnet
+if_lge_load="NO" # Level 1 LXT1001 NetCellerator PCI Gigabit
+ # Ethernet
+if_malo_load="NO" # Marvell Libertas 88W8335 802.11 wireless
+ # adapter
+if_msk_load="NO" # Marvell/SysKonnect Yukon II Gigabit Ethernet
+if_mxge_load="NO" # Myricom Myri10GE 10Gb Ethernet
+if_my_load="NO" # Myson PCI Fast Ethernet
+if_nfe_load="NO" # NVIDIA nForce MCP Networking Adapter
+if_nge_load="NO" # National Semiconductor PCI Gigabit Ethernet
+if_nxge_load="NO" # Neterion Xframe 10Gb Ethernet
+if_patm_load="NO" # IDT77252 ATM
+if_pcn_load="NO" # AMD PCnet PCI
+if_ral_load="NO" # Ralink Technology wireless
+if_re_load="NO" # RealTek 8139C+/8169/8169S/8110S
+if_rl_load="NO" # RealTek 8129/8139
+if_rue_load="NO" # RealTek RTL8150 USB to Fast Ethernet
+if_rum_load="NO" # Ralink Technology USB 802.11a/b/g wireless
+if_run_load="NO" # Ralink Technology USB 802.11a/g/n wireless
+if_sbni_load="NO" # Granch SBNI12 leased line adapters
+if_sf_load="NO" # Adaptec Duralink PCI (AIC-6915 "starfire")
+if_sge_load="NO" # Silicon Integrated Systems SiS 190/191
+if_sis_load="NO" # Silicon Integrated Systems SiS 900/7016
+if_sk_load="NO" # SysKonnect SK-984x series PCI Gigabit Ethernet
+if_sn_load="NO" # SMC 91Cxx
+if_ste_load="NO" # Sundance Technologies ST201 Fast Ethernet
+if_stge_load="NO" # Sundance/Tamarack TC9021 Gigabit Ethernet
+if_ti_load="NO" # Alteon Networks Tigon 1 and Tigon 2
+if_tl_load="NO" # Texas Instruments TNETE100 ("ThunderLAN")
+if_tx_load="NO" # SMC 83c17x Fast Ethernet
+if_txp_load="NO" # 3Com 3XP Typhoon/Sidewinder (3CR990)
+if_vge_load="NO" # VIA VT6122 PCI Gigabit Ethernet
+if_vte_load="NO" # DM&P Vortex86 RDC R6040 Fast Ethernet
+if_uath_load="NO" # Atheros USB wireless for AR5005UG & AR5005UX
+if_udav_load="NO" # Davicom DM9601 USB Ethernet
+if_upgt_load="NO" # Conexant/Intersil PrismGT USB wireless
+if_ural_load="NO" # Ralink Technology USB wireless
+if_urtw_load="NO" # Realtek 8187L USB wireless
+if_vr_load="NO" # VIA Rhine I and Rhine II
+if_vx_load="NO" # 3Com 3C590 family
+if_wb_load="NO" # Winbond W89C840F
+if_wi_load="NO" # WaveLAN/IEEE 802.11 wireless NICs
+if_wpi_load="NO" # Intel 3945ABG Wireless LAN IEEE 802.11
+if_xe_load="NO" # Xircom CreditCard PCMCIA
+if_xl_load="NO" # 3Com Etherlink XL (3c900, 3c905, 3c905B)
+sfxge_load="NO" # Solarflare 10Gb Ethernet adapter driver
+utopia_load="NO" # ATM PHY driver
+
+
+##############################################################
+### Netgraph modules #######################################
+##############################################################
+
+ng_UI_load="NO" # UI netgraph node type
+ng_async_load="NO" # asynchronous framing netgraph node type
+ng_bpf_load="NO" # Berkeley packet filter netgraph node type
+ng_bridge_load="NO" # Ethernet bridging netgraph node type
+ng_cisco_load="NO" # Cisco HDLC protocol netgraph node type
+ng_echo_load="NO" # Netgraph echo node type
+ng_eiface_load="NO" # generic Ethernet interface netgraph node type
+ng_etf_load="NO" # Ethertype filtering netgraph node type
+ng_ether_load="NO" # Ethernet netgraph node type
+ng_frame_relay_load="NO" # frame relay netgraph node type
+ng_gif_load="NO" # generic tunnel interface netgraph node type
+ng_gif_demux_load="NO" # demultiplexer for packets from ng_gif(4) nodes
+ng_hole_load="NO" # Netgraph discard node type
+ng_hub_load="NO" # packet distribution netgraph node type
+ng_iface_load="NO" # interface Netgraph node type
+ng_ip_input_load="NO" # netgraph IP input node type
+ng_ksocket_load="NO" # kernel socket netgraph node type
+ng_l2tp_load="NO" # L2TP protocol netgraph node type
+ng_lmi_load="NO" # frame relay LMI protocol netgraph node type
+ng_mppc_load="NO" # Microsoft MPPC/MPPE compression and
+ # encryption netgraph node type
+ng_netflow_load="NO" # Cisco's NetFlow netgraph node type
+ng_one2many_load="NO" # packet multiplexing netgraph node type
+ng_ppp_load="NO" # PPP protocol netgraph node type
+ng_pppoe_load="NO" # RFC 2516 PPPOE protocol netgraph node type
+ng_pptpgre_load="NO" # PPTP GRE protocol netgraph node type
+ng_rfc1490_load="NO" # RFC 1490 netgraph node type
+ng_socket_load="NO" # Netgraph socket node type
+ng_split_load="NO" # netgraph node to separate incoming and
+ # outgoing flows
+ng_sppp_load="NO" # sppp netgraph node type
+ng_tee_load="NO" # Netgraph ``tee'' node type
+ng_tty_load="NO" # Netgraph node type that is also a line
+ # discipline
+ng_vjc_load="NO" # Van Jacobsen compression netgraph node type
+ng_vlan_load="NO" # IEEE 802.1Q VLAN tagging netgraph node type
+
+
+##############################################################
+### Sound modules ##########################################
+##############################################################
+
+sound_load="NO" # Digital sound subsystem
+snd_ad1816_load="NO" # ad1816
+snd_als4000_load="NO" # als4000
+snd_atiixp_load="NO" # atiixp
+snd_cmi_load="NO" # cmi
+snd_cs4281_load="NO" # cs4281
+snd_csa_load="NO" # csa
+snd_ds1_load="NO" # ds1
+snd_emu10k1_load="NO" # Creative Sound Blaster Live
+snd_emu10kx_load="NO" # Creative SoundBlaster Live! and Audigy
+snd_envy24_load="NO" # VIA Envy24
+snd_envy24ht_load="NO" # VIA Envy24HT
+snd_es137x_load="NO" # es137x
+snd_ess_load="NO" # ess
+snd_fm801_load="NO" # fm801
+snd_hda_load="NO" # Intel High Definition Audio (Controller)
+snd_ich_load="NO" # Intel ICH
+snd_maestro_load="NO" # Maestro
+snd_maestro3_load="NO" # Maestro3
+snd_mss_load="NO" # Mss
+snd_neomagic_load="NO" # Neomagic
+snd_sb16_load="NO" # Sound Blaster 16
+snd_sb8_load="NO" # Sound Blaster Pro
+snd_sbc_load="NO" # Sbc
+snd_solo_load="NO" # Solo
+snd_spicds_load="NO" # SPI codecs
+snd_t4dwave_load="NO" # t4dwave
+snd_via8233_load="NO" # via8233
+snd_via82c686_load="NO" # via82c686
+snd_vibes_load="NO" # vibes
+snd_driver_load="NO" # All sound drivers
+
+
+##############################################################
+### USB modules ############################################
+##############################################################
+
+usb_load="NO" # USB subsystem
+udbp_load="NO" # USB double bulk pipe host 2 host cables
+ugen_load="NO" # USB generic device, if all else fails ...
+ucycom_load="NO" # Cyprus USB serial adapters
+ufm_load="NO" # Fm Radio
+uhid_load="NO" # Human Interface Devices
+ukbd_load="NO" # Keyboard
+ulpt_load="NO" # Printer
+ums_load="NO" # Mouse
+umass_load="NO" # Mass Storage Devices
+umct_load="NO" # Magic Control Technology USB-RS232
+umodem_load="NO" # Modems
+uplcom_load="NO" # Prolific USB serial adapters
+urio_load="NO" # Rio MP3 players
+uvisor_load="NO" # PalmOS based PDAs
+if_aue_load="NO" # ADMtek USB ethernet
+if_axe_load="NO" # ASIX Electronics AX88172 USB ethernet
+if_cdce_load="NO" # Ethernet over USB (CDC)
+if_cue_load="NO" # CATC USB ethernet
+if_kue_load="NO" # Kawasaki LSI USB ethernet
+if_rae_load="NO" # Realtek RTL8150 USB adapter.
+if_rum_load="NO" # Ralink USB 802.11 wireless adapter
+if_uath_load="NO" # Atheros AR5523 wireless adapter
+if_run_load="NO" # Ralink USB 802.11 wireless adapter
+if_ural_load="NO" # Ralink RT2500USB 802.11 wireless adapter
+if_zyd_load="NO" # ZyDAS ZD1211(B) USB 802.11 wireless adapter
+snd_uaudio_load="NO" # USB audio
+
+
+##############################################################
+### Other modules ##########################################
+##############################################################
+
+aio_load="NO" # Asynchronous I/O
+bktr_load="NO" # Brooktree Bt848/Bt878 TV/Video Capture Card
+ispfw_load="NO" # Qlogic ISP Firmware
+agp_load="NO" # agp module
+accf_data_load="NO" # Wait for data accept filter
+accf_dns_load="NO" # Wait for full DNS request accept filter
+accf_http_load="NO" # Wait for full HTTP request accept filter
+ppi_load="NO" # Interface to ppbus parallel 'geek' port
+pps_load="NO" # Pulse per second devices
+puc_load="NO" # PCI "Universal" Communications driver
+random_load="NO" # Random device
+speaker_load="NO" # AT speaker module
+coretemp_load="NO" # Intel Core CPU temperature monitor
+vkbd_load="NO" # Virtual AT keyboard interface
+vpd_load="NO" # Vital Product Data kernel interface
+vpo_load="NO" # Parallel to SCSI interface driver
+amdtemp_load="NO" # AMD K8/K10/K11 temperature monitor
+tpm_load="NO" # Trusted Platform Module
+wbwd_load="NO" # Winbond watchdog
+
+
+##############################################################
+### ACPI settings ##########################################
+##############################################################
+
+acpi_dsdt_load="NO" # DSDT Overriding
+acpi_dsdt_type="acpi_dsdt" # Don't change this
+acpi_dsdt_name="/boot/acpi_dsdt.aml"
+ # Override DSDT in BIOS by this file
+acpi_video_load="NO" # Load the ACPI video extension driver
+
+
+##############################################################
+### TrustedBSD MAC settings ################################
+##############################################################
+
+mac_biba_load="NO" # Biba MAC policy
+mac_bsdextended_load="NO" # BSD/extended MAC policy
+mac_ifoff="NO" # Interface silencing policy
+mac_mls_load="NO" # MLS MAC policy
+mac_none_load="NO" # Null MAC policy
+mac_partition_load="NO" # Partition MAC policy
+mac_seeotheruids_load="NO" # UID visbility MAC policy
+
+
+##############################################################
+### Module loading syntax example ##########################
+##############################################################
+
+#module_load="YES" # loads module "module"
+#module_name="realname" # uses "realname" instead of "module"
+#module_type="type" # passes "-t type" to load
+#module_flags="flags" # passes "flags" to the module
+#module_before="cmd" # executes "cmd" before loading the module
+#module_after="cmd" # executes "cmd" after loading the module
+#module_error="cmd" # executes "cmd" if load fails
diff --git a/stand/forth/loader.conf.5 b/stand/forth/loader.conf.5
new file mode 100644
index 0000000..631250c
--- /dev/null
+++ b/stand/forth/loader.conf.5
@@ -0,0 +1,330 @@
+.\" Copyright (c) 1999 Daniel C. Sobral
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd January 6, 2016
+.Dt LOADER.CONF 5
+.Os
+.Sh NAME
+.Nm loader.conf
+.Nd "system bootstrap configuration information"
+.Sh DESCRIPTION
+The file
+.Nm
+contains descriptive information on bootstrapping the system.
+Through
+it you can specify the kernel to be booted, parameters to be passed to
+it, and additional modules to be loaded; and generally set all variables
+described in
+.Xr loader 8 .
+.Pp
+The file
+.Pa /boot/loader.rc
+must contain the following two lines for
+.Nm
+to be automatically processed:
+.Pp
+.Dl include /boot/loader.4th
+.Dl start
+.Pp
+If no
+.Pa /boot/loader.rc
+exists at installworld time, one with the above lines will be installed.
+.Sh SYNTAX
+Though
+.Nm Ns 's
+format was defined explicitly to resemble
+.Xr rc.conf 5 ,
+and can be sourced by
+.Xr sh 1 ,
+some settings are treated in a special fashion.
+Also, the
+behavior of some settings is defined by the setting's suffix;
+the prefix identifies which module the setting controls.
+.Pp
+The general parsing rules are:
+.Bl -bullet
+.It
+Spaces and empty lines are ignored.
+.It
+A # sign will mark the remainder of the line as a comment.
+.It
+Only one setting can be present on each line.
+.El
+.Pp
+All settings have the following format:
+.Pp
+.Dl variable="value"
+.Pp
+Unless it belongs to one of the classes of settings that receive special
+treatment, a setting will set the value of a
+.Xr loader 8
+environment variable.
+The settings that receive special
+treatment are listed below.
+Settings beginning with
+.Qq *
+below define the modules to be loaded and
+may have any prefix; the prefix identifies a module.
+All such settings sharing a common
+prefix refer to the same module.
+.Bl -tag -width Ar
+.It Ar exec
+Immediately executes a
+.Xr loader 8
+command.
+This type of setting cannot be processed by programs other
+than
+.Xr loader 8 ,
+so its use should be avoided.
+Multiple instances of it will be processed
+independently.
+.It Ar loader_conf_files
+Defines additional configuration files to be processed right after the
+present file.
+.It Ar kernel
+Name of the kernel to be loaded.
+If no kernel name is set, no additional
+modules will be loaded.
+The name must be a subdirectory of
+.Pa /boot
+that contains a kernel.
+.It Ar kernel_options
+Flags to be passed to the kernel.
+.It Ar vfs.root.mountfrom
+Specify the root partition to mount.
+For example:
+.Pp
+.Dl vfs.root.mountfrom="ufs:/dev/da0s1a"
+.Pp
+.Xr loader 8
+automatically calculates the value of this tunable from
+.Pa /etc/fstab
+from the partition the kernel was loaded from.
+The calculated value might be calculated incorrectly when
+.Pa /etc/fstab
+is not available during
+.Xr loader 8
+startup (as during diskless booting from NFS), or if a different
+device is desired by the user.
+The preferred value can be set in
+.Pa /loader.conf .
+.Pp
+The value can also be overridden from the
+.Xr loader 8
+command line.
+This is useful for system recovery when
+.Pa /etc/fstab
+is damaged, lost, or read from the wrong partition.
+.It Ar password
+Protect boot menu with a password without interrupting
+.Ic autoboot
+process.
+The password should be in clear text format.
+If a password is set, boot menu will not appear until any key is pressed during
+countdown period specified by
+.Va autoboot_delay
+variable or
+.Ic autoboot
+process fails.
+In both cases user should provide specified password to be able to access boot
+menu.
+.It Ar bootlock_password
+Provides a password to be required by check-password before execution is
+allowed to continue.
+The password should be in clear text format.
+If a password is set, the user must provide specified password to boot.
+.It Ar verbose_loading
+If set to
+.Dq YES ,
+module names will be displayed as they are loaded.
+.It Ar *_load
+If set to
+.Dq YES ,
+that module will be loaded.
+If no name is defined (see below), the
+module's name is taken to be the same as the prefix.
+.It Ar *_name
+Defines the name of the module.
+.It Ar *_type
+Defines the module's type.
+If none is given, it defaults to a kld module.
+.It Ar *_flags
+Flags and parameters to be passed to the module.
+.It Ar *_before
+Commands to be executed before the module is loaded.
+Use of this setting
+should be avoided.
+.It Ar *_after
+Commands to be executed after the module is loaded.
+Use of this setting
+should be avoided.
+.It Ar *_error
+Commands to be executed if the loading of a module fails.
+Except for the
+special value
+.Dq abort ,
+which aborts the bootstrap process, use of this setting should be avoided.
+.El
+.Pp
+.Em WARNING:
+developers should never use these suffixes for any kernel environment
+variables (tunables) or conflicts will result.
+.Sh DEFAULT SETTINGS
+Most of
+.Nm Ns 's
+default settings can be ignored.
+The few of them which are important
+or useful are:
+.Bl -tag -width bootfile -offset indent
+.It Va bitmap_load
+.Pq Dq NO
+If set to
+.Dq YES ,
+a bitmap will be loaded to be displayed on screen while booting.
+.It Va bitmap_name
+.Pq Dq Pa /boot/splash.bmp
+Name of the bitmap to be loaded.
+Any other name can be used.
+.It Va comconsole_speed
+.Dq ( 9600
+or the value of the
+.Va BOOT_COMCONSOLE_SPEED
+variable when
+.Xr loader 8
+was compiled).
+Sets the speed of the serial console.
+If the previous boot loader stage specified that a serial console
+is in use then the default speed is determined from the current
+serial port speed setting.
+.It Va console
+.Pq Dq vidconsole
+.Dq comconsole
+selects serial console,
+.Dq vidconsole
+selects the video console,
+.Dq nullconsole
+selects a mute console
+(useful for systems with neither a video console nor a serial port), and
+.Dq spinconsole
+selects the video console which prevents any input and hides all output
+replacing it with
+.Dq spinning
+character (useful for embedded products and such).
+.It Va kernel
+.Pq Dq kernel
+.It Va kernels
+.Pq Dq kernel kernel.old
+Space or comma separated list of kernels to present in the boot menu.
+.It Va loader_conf_files
+.Pq Dq Pa /boot/loader.conf /boot/loader.conf.local
+.It Va splash_bmp_load
+.Pq Dq NO
+If set to
+.Dq YES ,
+will load the splash screen module, making it possible to display a bmp image
+on the screen while booting.
+.It Va splash_pcx_load
+.Pq Dq NO
+If set to
+.Dq YES ,
+will load the splash screen module, making it possible to display a pcx image
+on the screen while booting.
+.It Va vesa_load
+.Pq Dq NO
+If set to
+.Dq YES ,
+the vesa module will be loaded, enabling bitmaps above VGA resolution to
+be displayed.
+.It Va beastie_disable
+If set to
+.Dq YES ,
+the beastie boot menu will be skipped.
+The beastie boot menu is always skipped if running non-x86 hardware.
+.It Va loader_logo Pq Dq Li orbbw
+Selects a desired logo in the beastie boot menu.
+Possible values are:
+.Dq Li orbbw ,
+.Dq Li orb ,
+.Dq Li fbsdbw ,
+.Dq Li beastiebw ,
+.Dq Li beastie ,
+and
+.Dq Li none .
+.It Va loader_color
+If set to
+.Dq NO ,
+the beastie boot menu will be displayed without ANSI coloring.
+.It Va entropy_cache_load
+.Pq Dq YES
+If set to
+.Dq NO ,
+the very early
+boot-time entropy file
+will not be loaded.
+See the entropy entries in
+.Xr rc.conf 5 .
+.It Va entropy_cache_name
+.Pq Dq /boot/entropy
+The name of the very early
+boot-time entropy cache file.
+.El
+.Sh FILES
+.Bl -tag -width /boot/defaults/loader.conf -compact
+.It Pa /boot/defaults/loader.conf
+default settings -- do not change this file.
+.It Pa /boot/loader.4th
+defines the commands used by loader to read and process
+.Nm .
+.It Pa /boot/loader.conf
+user defined settings.
+.It Pa /boot/loader.conf.local
+machine-specific settings for sites with a common loader.conf.
+.It Pa /boot/loader.rc
+contains the instructions to automatically process
+.Nm .
+.El
+.Sh SEE ALSO
+.Xr rc.conf 5 ,
+.Xr boot 8 ,
+.Xr loader 8 ,
+.Xr loader.4th 8
+.Sh HISTORY
+The file
+.Nm
+first appeared in
+.Fx 3.2 .
+.Sh AUTHORS
+This manual page was written by
+.An Daniel C. Sobral Aq dcs@FreeBSD.org .
+.Sh BUGS
+The
+.Xr loader 8
+stops reading
+.Nm
+when it encounters a syntax error, so any options which are vital for
+booting a particular system (i.e.\&
+.Dq Va hw.ata.ata_dma Ns "=0" )
+should precede any experimental additions to
+.Nm .
diff --git a/stand/forth/loader.rc b/stand/forth/loader.rc
new file mode 100644
index 0000000..0bc6657
--- /dev/null
+++ b/stand/forth/loader.rc
@@ -0,0 +1,23 @@
+\ Loader.rc
+\ $FreeBSD$
+\
+\ You should not edit this file! Put any overrides in loader.rc.local
+\ instead as this file can be replaced during system updates.
+\
+\ Includes additional commands
+include /boot/loader.4th
+try-include /boot/loader.rc.local
+
+\ Reads and processes loader.conf variables
+\ NOTE: Change to `initialize' if you enable the below boot menu
+start
+
+\ Tests for password -- executes autoboot first if a password was defined
+check-password
+
+\ Uncomment to enable boot menu
+\ include /boot/beastie.4th
+\ beastie-start
+
+\ Unless set otherwise, autoboot is automatic at this point
+
diff --git a/stand/forth/logo-beastie.4th b/stand/forth/logo-beastie.4th
new file mode 100644
index 0000000..671eb5e
--- /dev/null
+++ b/stand/forth/logo-beastie.4th
@@ -0,0 +1,61 @@
+\ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org>
+\ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com>
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+46 logoX ! 4 logoY ! \ Initialize logo placement defaults
+
+: logo+ ( x y c-addr/u -- x y' )
+ 2swap 2dup at-xy 2swap \ position the cursor
+ [char] @ escc! \ replace @ with Esc
+ type \ print to the screen
+ 1+ \ increase y for next time we're called
+;
+
+: logo ( x y -- ) \ color BSD mascot (19 rows x 34 columns)
+
+ s" @[31m, ," logo+
+ s" /( )`" logo+
+ s" \ \___ / |" logo+
+ s" /- @[m_@[31m `-/ '" logo+
+ s" (@[m/\/ \@[31m \ /\" logo+
+ s" @[m/ / |@[31m ` \" logo+
+ s" @[34mO O @[m) @[31m/ |" logo+
+ s" @[m`-^--'@[31m`< '" logo+
+ s" (_.) _ ) /" logo+
+ s" `.___/` /" logo+
+ s" `-----' /" logo+
+ s" @[33m<----.@[31m __ / __ \" logo+
+ s" @[33m<----|====@[31mO)))@[33m==@[31m) \) /@[33m====|" logo+
+ s" @[33m<----'@[31m `--' `.__,' \" logo+
+ s" | |" logo+
+ s" \ / /\" logo+
+ s" @[36m______@[31m( (_ / \______/" logo+
+ s" @[36m,' ,-----' |" logo+
+ s" `--{__________)@[m" logo+
+
+ 2drop
+;
diff --git a/stand/forth/logo-beastiebw.4th b/stand/forth/logo-beastiebw.4th
new file mode 100644
index 0000000..197099c
--- /dev/null
+++ b/stand/forth/logo-beastiebw.4th
@@ -0,0 +1,59 @@
+\ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org>
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+46 logoX ! 4 logoY ! \ Initialize logo placement defaults
+
+: logo+ ( x y c-addr/u -- x y' )
+ 2swap 2dup at-xy 2swap \ position the cursor
+ type \ print to the screen
+ 1+ \ increase y for next time we're called
+;
+
+: logo ( x y -- ) \ B/W BSD mascot (19 rows x 34 columns)
+
+ s" , ," logo+
+ s" /( )`" logo+
+ s" \ \___ / |" logo+
+ s" /- _ `-/ '" logo+
+ s" (/\/ \ \ /\" logo+
+ s" / / | ` \" logo+
+ s" O O ) / |" logo+
+ s" `-^--'`< '" logo+
+ s" (_.) _ ) /" logo+
+ s" `.___/` /" logo+
+ s" `-----' /" logo+
+ s" <----. __ / __ \" logo+
+ s" <----|====O)))==) \) /====|" logo+
+ s" <----' `--' `.__,' \" logo+
+ s" | |" logo+
+ s" \ / /\" logo+
+ s" ______( (_ / \______/" logo+
+ s" ,' ,-----' |" logo+
+ s" `--{__________)" logo+
+
+ 2drop
+;
diff --git a/stand/forth/logo-fbsdbw.4th b/stand/forth/logo-fbsdbw.4th
new file mode 100644
index 0000000..d4a532b
--- /dev/null
+++ b/stand/forth/logo-fbsdbw.4th
@@ -0,0 +1,53 @@
+\ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org>
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+52 logoX ! 9 logoY ! \ Initialize logo placement defaults
+
+: logo+ ( x y c-addr/u -- x y' )
+ 2swap 2dup at-xy 2swap \ position the cursor
+ type \ print to the screen
+ 1+ \ increase y for next time we're called
+;
+
+: logo ( x y -- ) \ "FreeBSD" logo in B/W (13 rows x 21 columns)
+
+ s" ______" logo+
+ s" | ____| __ ___ ___ " logo+
+ s" | |__ | '__/ _ \/ _ \" logo+
+ s" | __|| | | __/ __/" logo+
+ s" | | | | | | |" logo+
+ s" |_| |_| \___|\___|" logo+
+ s" ____ _____ _____" logo+
+ s" | _ \ / ____| __ \" logo+
+ s" | |_) | (___ | | | |" logo+
+ s" | _ < \___ \| | | |" logo+
+ s" | |_) |____) | |__| |" logo+
+ s" | | | |" logo+
+ s" |____/|_____/|_____/" logo+
+
+ 2drop
+;
diff --git a/stand/forth/logo-orb.4th b/stand/forth/logo-orb.4th
new file mode 100644
index 0000000..c2a504d
--- /dev/null
+++ b/stand/forth/logo-orb.4th
@@ -0,0 +1,55 @@
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+46 logoX ! 7 logoY ! \ Initialize logo placement defaults
+
+: logo+ ( x y c-addr/u -- x y' )
+ 2swap 2dup at-xy 2swap \ position the cursor
+ [char] @ escc! \ replace @ with Esc
+ type \ print to the screen
+ 1+ \ increase y for next time we're called
+;
+
+: logo ( x y -- ) \ color Orb mascot (15 rows x 30 columns)
+
+ s" @[31m``` @[31;1m`@[31m" logo+
+ s" s` `.....---...@[31;1m....--.``` -/@[31m" logo+
+ s" +o .--` @[31;1m/y:` +.@[31m" logo+
+ s" yo`:. @[31;1m:o `+-@[31m" logo+
+ s" y/ @[31;1m-/` -o/@[31m" logo+
+ s" .- @[31;1m::/sy+:.@[31m" logo+
+ s" / @[31;1m`-- /@[31m" logo+
+ s" `: @[31;1m:`@[31m" logo+
+ s" `: @[31;1m:`@[31m" logo+
+ s" / @[31;1m/@[31m" logo+
+ s" .- @[31;1m-.@[31m" logo+
+ s" -- @[31;1m-.@[31m" logo+
+ s" `:` @[31;1m`:`" logo+
+ s" @[31;1m.-- `--." logo+
+ s" .---.....----.@[m" logo+
+
+ 2drop
+;
diff --git a/stand/forth/logo-orbbw.4th b/stand/forth/logo-orbbw.4th
new file mode 100644
index 0000000..11dc11c
--- /dev/null
+++ b/stand/forth/logo-orbbw.4th
@@ -0,0 +1,54 @@
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+46 logoX ! 7 logoY ! \ Initialize logo placement defaults
+
+: logo+ ( x y c-addr/u -- x y' )
+ 2swap 2dup at-xy 2swap \ position the cursor
+ type \ print to the screen
+ 1+ \ increase y for next time we're called
+;
+
+: logo ( x y -- ) \ B/W Orb mascot (15 rows x 32 columns)
+
+ s" ``` `" logo+
+ s" s` `.....---.......--.``` -/" logo+
+ s" +o .--` /y:` +." logo+
+ s" yo`:. :o `+-" logo+
+ s" y/ -/` -o/" logo+
+ s" .- ::/sy+:." logo+
+ s" / `-- /" logo+
+ s" `: :`" logo+
+ s" `: :`" logo+
+ s" / /" logo+
+ s" .- -." logo+
+ s" -- -." logo+
+ s" `:` `:`" logo+
+ s" .-- `--." logo+
+ s" .---.....----." logo+
+
+ 2drop
+;
diff --git a/stand/forth/menu-commands.4th b/stand/forth/menu-commands.4th
new file mode 100644
index 0000000..9adf30a
--- /dev/null
+++ b/stand/forth/menu-commands.4th
@@ -0,0 +1,418 @@
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+marker task-menu-commands.4th
+
+include /boot/menusets.4th
+
+only forth definitions
+
+variable kernel_state
+variable root_state
+0 kernel_state !
+0 root_state !
+
+also menu-namespace also menu-command-helpers
+
+\
+\ Boot
+\
+
+: init_boot ( N -- N )
+ dup
+ s" boot_single" getenv -1 <> if
+ drop ( n n c-addr -- n n ) \ unused
+ toggle_menuitem ( n n -- n n )
+ s" set menu_keycode[N]=115" \ base command to execute
+ else
+ s" set menu_keycode[N]=98" \ base command to execute
+ then
+ 17 +c! \ replace 'N' with ASCII numeral
+ evaluate
+;
+
+\
+\ Alternate Boot
+\
+
+: init_altboot ( N -- N )
+ dup
+ s" boot_single" getenv -1 <> if
+ drop ( n c-addr -- n ) \ unused
+ toggle_menuitem ( n -- n )
+ s" set menu_keycode[N]=109" \ base command to execute
+ else
+ s" set menu_keycode[N]=115" \ base command to execute
+ then
+ 17 +c! \ replace 'N' with ASCII numeral
+ evaluate
+;
+
+: altboot ( N -- NOTREACHED )
+ s" boot_single" 2dup getenv -1 <> if
+ drop ( c-addr/u c-addr -- c-addr/u ) \ unused
+ unsetenv ( c-addr/u -- )
+ else
+ 2drop ( c-addr/u -- ) \ unused
+ s" set boot_single=YES" evaluate
+ then
+ 0 boot ( state -- )
+;
+
+\
+\ ACPI
+\
+
+: acpi_enable ( -- )
+ s" set acpi_load=YES" evaluate \ XXX deprecated but harmless
+ s" set hint.acpi.0.disabled=0" evaluate
+ s" loader.acpi_disabled_by_user" unsetenv
+;
+
+: acpi_disable ( -- )
+ s" acpi_load" unsetenv \ XXX deprecated but harmless
+ s" set hint.acpi.0.disabled=1" evaluate
+ s" set loader.acpi_disabled_by_user=1" evaluate
+;
+
+: toggle_acpi ( N -- N TRUE )
+
+ \ Make changes effective _before_ calling menu-redraw
+
+ acpienabled? if
+ acpi_disable
+ else
+ acpi_enable
+ then
+
+ menu-redraw
+
+ TRUE \ loop menu again
+;
+
+\
+\ Safe Mode
+\
+
+: safemode_enabled? ( -- flag )
+ s" kern.smp.disabled" getenv -1 <> dup if
+ swap drop ( c-addr flag -- flag )
+ then
+;
+
+: safemode_enable ( -- )
+ s" set kern.smp.disabled=1" evaluate
+ s" set hw.ata.ata_dma=0" evaluate
+ s" set hw.ata.atapi_dma=0" evaluate
+ s" set hw.ata.wc=0" evaluate
+ s" set hw.eisa_slots=0" evaluate
+ s" set kern.eventtimer.periodic=1" evaluate
+ s" set kern.geom.part.check_integrity=0" evaluate
+;
+
+: safemode_disable ( -- )
+ s" kern.smp.disabled" unsetenv
+ s" hw.ata.ata_dma" unsetenv
+ s" hw.ata.atapi_dma" unsetenv
+ s" hw.ata.wc" unsetenv
+ s" hw.eisa_slots" unsetenv
+ s" kern.eventtimer.periodic" unsetenv
+ s" kern.geom.part.check_integrity" unsetenv
+;
+
+: init_safemode ( N -- N )
+ safemode_enabled? if
+ toggle_menuitem ( n -- n )
+ then
+;
+
+: toggle_safemode ( N -- N TRUE )
+ toggle_menuitem
+
+ \ Now we're going to make the change effective
+
+ dup toggle_stateN @ 0= if
+ safemode_disable
+ else
+ safemode_enable
+ then
+
+ menu-redraw
+
+ TRUE \ loop menu again
+;
+
+\
+\ Single User Mode
+\
+
+: singleuser_enabled? ( -- flag )
+ s" boot_single" getenv -1 <> dup if
+ swap drop ( c-addr flag -- flag )
+ then
+;
+
+: singleuser_enable ( -- )
+ s" set boot_single=YES" evaluate
+;
+
+: singleuser_disable ( -- )
+ s" boot_single" unsetenv
+;
+
+: init_singleuser ( N -- N )
+ singleuser_enabled? if
+ toggle_menuitem ( n -- n )
+ then
+;
+
+: toggle_singleuser ( N -- N TRUE )
+ toggle_menuitem
+ menu-redraw
+
+ \ Now we're going to make the change effective
+
+ dup toggle_stateN @ 0= if
+ singleuser_disable
+ else
+ singleuser_enable
+ then
+
+ TRUE \ loop menu again
+;
+
+\
+\ Verbose Boot
+\
+
+: verbose_enabled? ( -- flag )
+ s" boot_verbose" getenv -1 <> dup if
+ swap drop ( c-addr flag -- flag )
+ then
+;
+
+: verbose_enable ( -- )
+ s" set boot_verbose=YES" evaluate
+;
+
+: verbose_disable ( -- )
+ s" boot_verbose" unsetenv
+;
+
+: init_verbose ( N -- N )
+ verbose_enabled? if
+ toggle_menuitem ( n -- n )
+ then
+;
+
+: toggle_verbose ( N -- N TRUE )
+ toggle_menuitem
+ menu-redraw
+
+ \ Now we're going to make the change effective
+
+ dup toggle_stateN @ 0= if
+ verbose_disable
+ else
+ verbose_enable
+ then
+
+ TRUE \ loop menu again
+;
+
+\
+\ Escape to Prompt
+\
+
+: goto_prompt ( N -- N FALSE )
+
+ s" set autoboot_delay=NO" evaluate
+
+ cr
+ ." To get back to the menu, type `menu' and press ENTER" cr
+ ." or type `boot' and press ENTER to start FreeBSD." cr
+ cr
+
+ FALSE \ exit the menu
+;
+
+\
+\ Cyclestate (used by kernel/root below)
+\
+
+: init_cyclestate ( N K -- N )
+ over cycle_stateN ( n k -- n k addr )
+ begin
+ tuck @ ( n k addr -- n addr k c )
+ over <> ( n addr k c -- n addr k 0|-1 )
+ while
+ rot ( n addr k -- addr k n )
+ cycle_menuitem
+ swap rot ( addr k n -- n k addr )
+ repeat
+ 2drop ( n k addr -- n )
+;
+
+\
+\ Kernel
+\
+
+: init_kernel ( N -- N )
+ kernel_state @ ( n -- n k )
+ init_cyclestate ( n k -- n )
+;
+
+: activate_kernel ( N -- N )
+ dup cycle_stateN @ ( n -- n n2 )
+ dup kernel_state ! ( n n2 -- n n2 ) \ copy for re-initialization
+ 48 + ( n n2 -- n n2' ) \ kernel_state to ASCII num
+
+ s" set kernel=${kernel_prefix}${kernel[N]}${kernel_suffix}"
+ 36 +c! ( n n2 c-addr/u -- n c-addr/u ) \ 'N' to ASCII num
+ evaluate ( n c-addr/u -- n ) \ sets $kernel to full kernel-path
+;
+
+: cycle_kernel ( N -- N TRUE )
+ cycle_menuitem \ cycle cycle_stateN to next value
+ activate_kernel \ apply current cycle_stateN
+ menu-redraw \ redraw menu
+ TRUE \ loop menu again
+;
+
+\
+\ Root
+\
+
+: init_root ( N -- N )
+ root_state @ ( n -- n k )
+ init_cyclestate ( n k -- n )
+;
+
+: activate_root ( N -- N )
+ dup cycle_stateN @ ( n -- n n2 )
+ dup root_state ! ( n n2 -- n n2 ) \ copy for re-initialization
+ 48 + ( n n2 -- n n2' ) \ root_state to ASCII num
+
+ s" set root=${root_prefix}${root[N]}${root_suffix}"
+ 30 +c! ( n n2 c-addr/u -- n c-addr/u ) \ 'N' to ASCII num
+ evaluate ( n c-addr/u -- n ) \ sets $root to full kernel-path
+;
+
+: cycle_root ( N -- N TRUE )
+ cycle_menuitem \ cycle cycle_stateN to next value
+ activate_root \ apply current cycle_stateN
+ menu-redraw \ redraw menu
+ TRUE \ loop menu again
+;
+
+\
+\ Menusets
+\
+
+: goto_menu ( N M -- N TRUE )
+ menu-unset
+ menuset-loadsetnum ( n m -- n )
+ menu-redraw
+ TRUE \ Loop menu again
+;
+
+\
+\ Defaults
+\
+
+: set_default_boot_options ( N -- N TRUE )
+ acpi_enable
+ safemode_disable
+ singleuser_disable
+ verbose_disable
+ 2 goto_menu
+;
+
+\
+\ Set boot environment defaults
+\
+
+: init_bootenv ( -- )
+ s" set menu_caption[1]=${bemenu_current}${vfs.root.mountfrom}" evaluate
+ s" set ansi_caption[1]=${beansi_current}${vfs.root.mountfrom}" evaluate
+ s" set menu_caption[2]=${bemenu_bootfs}${zfs_be_active}" evaluate
+ s" set ansi_caption[2]=${beansi_bootfs}${zfs_be_active}" evaluate
+ s" set menu_caption[3]=${bemenu_page}${zfs_be_currpage}${bemenu_pageof}${zfs_be_pages}" evaluate
+ s" set ansi_caption[3]=${beansi_page}${zfs_be_currpage}${bemenu_pageof}${zfs_be_pages}" evaluate
+;
+
+\
+\ Redraw the entire screen. A long BE name can corrupt the menu
+\
+
+: be_draw_screen
+ clear \ Clear the screen (in screen.4th)
+ print_version \ print version string (bottom-right; see version.4th)
+ draw-beastie \ Draw FreeBSD logo at right (in beastie.4th)
+ draw-brand \ Draw brand.4th logo at top (in brand.4th)
+ menu-init \ Initialize menu and draw bounding box (in menu.4th)
+;
+
+\
+\ Select a boot environment
+\
+
+: set_bootenv ( N -- N TRUE )
+ dup s" set vfs.root.mountfrom=${bootenv_root[E]}" 38 +c! evaluate
+ s" set currdev=${vfs.root.mountfrom}:" evaluate
+ s" unload" evaluate
+ free-module-options
+ s" /boot/defaults/loader.conf" read-conf
+ s" /boot/loader.conf" read-conf
+ s" /boot/loader.conf.local" read-conf
+ init_bootenv
+ be_draw_screen
+ menu-redraw
+ TRUE
+;
+
+\
+\ Switch to the next page of boot environments
+\
+
+: set_be_page ( N -- N TRUE )
+ s" zfs_be_currpage" getenv dup -1 = if
+ drop s" 1"
+ else
+ 0 s>d 2swap
+ >number ( ud caddr/u -- ud' caddr'/u' ) \ convert string to numbers
+ 2drop \ drop the string
+ 1 um/mod ( ud u1 -- u2 u3 ) \ convert double ud' to single u3' and remainder u2
+ swap drop ( ud2 u3 -- u3 ) \ drop the remainder u2
+ 1+ \ increment the page number
+ s>d <# #s #> \ convert back to a string
+ then
+ s" zfs_be_currpage" setenv
+ s" reloadbe" evaluate
+ 3 goto_menu
+;
+
+only forth definitions
diff --git a/stand/forth/menu.4th b/stand/forth/menu.4th
new file mode 100644
index 0000000..e3fe0f7
--- /dev/null
+++ b/stand/forth/menu.4th
@@ -0,0 +1,1319 @@
+\ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org>
+\ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com>
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+marker task-menu.4th
+
+\ Frame drawing
+include /boot/frames.4th
+
+vocabulary menu-infrastructure
+vocabulary menu-namespace
+vocabulary menu-command-helpers
+
+only forth also menu-infrastructure definitions
+
+f_double \ Set frames to double (see frames.4th). Replace with
+ \ f_single if you want single frames.
+46 constant dot \ ASCII definition of a period (in decimal)
+
+ 5 constant menu_default_x \ default column position of timeout
+10 constant menu_default_y \ default row position of timeout msg
+ 4 constant menu_timeout_default_x \ default column position of timeout
+23 constant menu_timeout_default_y \ default row position of timeout msg
+10 constant menu_timeout_default \ default timeout (in seconds)
+
+\ Customize the following values with care
+
+ 1 constant menu_start \ Numerical prefix of first menu item
+dot constant bullet \ Menu bullet (appears after numerical prefix)
+ 5 constant menu_x \ Row position of the menu (from the top)
+ 10 constant menu_y \ Column position of the menu (from left side)
+
+\ Menu Appearance
+variable menuidx \ Menu item stack for number prefixes
+variable menurow \ Menu item stack for positioning
+variable menubllt \ Menu item bullet
+
+\ Menu Positioning
+variable menuX \ Menu X offset (columns)
+variable menuY \ Menu Y offset (rows)
+
+\ Menu-item elements
+variable menurebootadded
+
+\ Parsing of kernels into menu-items
+variable kernidx
+variable kernlen
+variable kernmenuidx
+
+\ Menu timer [count-down] variables
+variable menu_timeout_enabled \ timeout state (internal use only)
+variable menu_time \ variable for tracking the passage of time
+variable menu_timeout \ determined configurable delay duration
+variable menu_timeout_x \ column position of timeout message
+variable menu_timeout_y \ row position of timeout message
+
+\ Containers for parsing kernels into menu-items
+create kerncapbuf 64 allot
+create kerndefault 64 allot
+create kernelsbuf 256 allot
+
+only forth also menu-namespace definitions
+
+\ Menu-item key association/detection
+variable menukey1
+variable menukey2
+variable menukey3
+variable menukey4
+variable menukey5
+variable menukey6
+variable menukey7
+variable menukey8
+variable menureboot
+variable menuacpi
+variable menuoptions
+variable menukernel
+
+\ Menu initialization status variables
+variable init_state1
+variable init_state2
+variable init_state3
+variable init_state4
+variable init_state5
+variable init_state6
+variable init_state7
+variable init_state8
+
+\ Boolean option status variables
+variable toggle_state1
+variable toggle_state2
+variable toggle_state3
+variable toggle_state4
+variable toggle_state5
+variable toggle_state6
+variable toggle_state7
+variable toggle_state8
+
+\ Array option status variables
+variable cycle_state1
+variable cycle_state2
+variable cycle_state3
+variable cycle_state4
+variable cycle_state5
+variable cycle_state6
+variable cycle_state7
+variable cycle_state8
+
+\ Containers for storing the initial caption text
+create init_text1 64 allot
+create init_text2 64 allot
+create init_text3 64 allot
+create init_text4 64 allot
+create init_text5 64 allot
+create init_text6 64 allot
+create init_text7 64 allot
+create init_text8 64 allot
+
+only forth definitions
+
+: arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise.
+ s" arch-i386" environment? dup if
+ drop
+ then
+;
+
+: acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise
+ s" hint.acpi.0.rsdp" getenv
+ dup -1 = if
+ drop false exit
+ then
+ 2drop
+ true
+;
+
+: acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise
+ s" hint.acpi.0.disabled" getenv
+ dup -1 <> if
+ s" 0" compare 0<> if
+ false exit
+ then
+ else
+ drop
+ then
+ true
+;
+
+: +c! ( N C-ADDR/U K -- C-ADDR/U )
+ 3 pick 3 pick ( n c-addr/u k -- n c-addr/u k n c-addr )
+ rot + c! ( n c-addr/u k n c-addr -- n c-addr/u )
+ rot drop ( n c-addr/u -- c-addr/u )
+;
+
+only forth also menu-namespace definitions
+
+\ Forth variables
+: namespace ( C-ADDR/U N -- ) also menu-namespace +c! evaluate previous ;
+: menukeyN ( N -- ADDR ) s" menukeyN" 7 namespace ;
+: init_stateN ( N -- ADDR ) s" init_stateN" 10 namespace ;
+: toggle_stateN ( N -- ADDR ) s" toggle_stateN" 12 namespace ;
+: cycle_stateN ( N -- ADDR ) s" cycle_stateN" 11 namespace ;
+: init_textN ( N -- C-ADDR ) s" init_textN" 9 namespace ;
+
+\ Environment variables
+: kernel[x] ( N -- C-ADDR/U ) s" kernel[x]" 7 +c! ;
+: menu_init[x] ( N -- C-ADDR/U ) s" menu_init[x]" 10 +c! ;
+: menu_command[x] ( N -- C-ADDR/U ) s" menu_command[x]" 13 +c! ;
+: menu_caption[x] ( N -- C-ADDR/U ) s" menu_caption[x]" 13 +c! ;
+: ansi_caption[x] ( N -- C-ADDR/U ) s" ansi_caption[x]" 13 +c! ;
+: menu_keycode[x] ( N -- C-ADDR/U ) s" menu_keycode[x]" 13 +c! ;
+: toggled_text[x] ( N -- C-ADDR/U ) s" toggled_text[x]" 13 +c! ;
+: toggled_ansi[x] ( N -- C-ADDR/U ) s" toggled_ansi[x]" 13 +c! ;
+: menu_caption[x][y] ( N M -- C-ADDR/U ) s" menu_caption[x][y]" 16 +c! 13 +c! ;
+: ansi_caption[x][y] ( N M -- C-ADDR/U ) s" ansi_caption[x][y]" 16 +c! 13 +c! ;
+
+also menu-infrastructure definitions
+
+\ This function prints a menu item at menuX (row) and menuY (column), returns
+\ the incremental decimal ASCII value associated with the menu item, and
+\ increments the cursor position to the next row for the creation of the next
+\ menu item. This function is called by the menu-create function. You need not
+\ call it directly.
+\
+: printmenuitem ( menu_item_str -- ascii_keycode )
+
+ loader_color? if [char] ^ escc! then
+
+ menurow dup @ 1+ swap ! ( increment menurow )
+ menuidx dup @ 1+ swap ! ( increment menuidx )
+
+ \ Calculate the menuitem row position
+ menurow @ menuY @ +
+
+ \ Position the cursor at the menuitem position
+ dup menuX @ swap at-xy
+
+ \ Print the value of menuidx
+ loader_color? dup ( -- bool bool )
+ if b then
+ menuidx @ .
+ if me then
+
+ \ Move the cursor forward 1 column
+ dup menuX @ 1+ swap at-xy
+
+ menubllt @ emit \ Print the menu bullet using the emit function
+
+ \ Move the cursor to the 3rd column from the current position
+ \ to allow for a space between the numerical prefix and the
+ \ text caption
+ menuX @ 3 + swap at-xy
+
+ \ Print the menu caption (we expect a string to be on the stack
+ \ prior to invoking this function)
+ type
+
+ \ Here we will add the ASCII decimal of the numerical prefix
+ \ to the stack (decimal ASCII for `1' is 49) as a "return value"
+ menuidx @ 48 +
+;
+
+\ This function prints the appropriate menuitem basename to the stack if an
+\ ACPI option is to be presented to the user, otherwise returns -1. Used
+\ internally by menu-create, you need not (nor should you) call this directly.
+\
+: acpimenuitem ( -- C-Addr/U | -1 )
+
+ arch-i386? if
+ acpipresent? if
+ acpienabled? if
+ loader_color? if
+ s" toggled_ansi[x]"
+ else
+ s" toggled_text[x]"
+ then
+ else
+ loader_color? if
+ s" ansi_caption[x]"
+ else
+ s" menu_caption[x]"
+ then
+ then
+ else
+ menuidx dup @ 1+ swap ! ( increment menuidx )
+ -1
+ then
+ else
+ -1
+ then
+;
+
+: delim? ( C -- BOOL )
+ dup 32 = ( c -- c bool ) \ [sp] space
+ over 9 = or ( c bool -- c bool ) \ [ht] horizontal tab
+ over 10 = or ( c bool -- c bool ) \ [nl] newline
+ over 13 = or ( c bool -- c bool ) \ [cr] carriage return
+ over [char] , = or ( c bool -- c bool ) \ comma
+ swap drop ( c bool -- bool ) \ return boolean
+;
+
+\ This function parses $kernels into variables that are used by the menu to
+\ display which kernel to boot when the [overloaded] `boot' word is interpreted.
+\ Used internally by menu-create, you need not (nor should you) call this
+\ directly.
+\
+: parse-kernels ( N -- ) \ kernidx
+ kernidx ! ( n -- ) \ store provided `x' value
+ [char] 0 kernmenuidx ! \ initialize `y' value for menu_caption[x][y]
+
+ \ Attempt to get a list of kernels, fall back to sensible default
+ s" kernels" getenv dup -1 = if
+ drop ( cruft )
+ s" kernel kernel.old"
+ then ( -- c-addr/u )
+
+ \ Check to see if the user has altered $kernel by comparing it against
+ \ $kernel[N] where N is kernel_state (the actively displayed kernel).
+ s" kernel_state" evaluate @ 48 + s" kernel[N]" 7 +c! getenv
+ dup -1 <> if
+ s" kernel" getenv dup -1 = if
+ drop ( cruft ) s" "
+ then
+ 2swap 2over compare 0= if
+ 2drop FALSE ( skip below conditional )
+ else \ User has changed $kernel
+ TRUE ( slurp in new value )
+ then
+ else \ We haven't yet parsed $kernels into $kernel[N]
+ drop ( getenv cruft )
+ s" kernel" getenv dup -1 = if
+ drop ( cruft ) s" "
+ then
+ TRUE ( slurp in initial value )
+ then ( c-addr/u -- c-addr/u c-addr/u,-1 | 0 )
+ if \ slurp new value into kerndefault
+ kerndefault 1+ 0 2swap strcat swap 1- c!
+ then
+
+ \ Clear out existing parsed-kernels
+ kernidx @ [char] 0
+ begin
+ dup kernel[x] unsetenv
+ 2dup menu_caption[x][y] unsetenv
+ 2dup ansi_caption[x][y] unsetenv
+ 1+ dup [char] 8 >
+ until
+ 2drop
+
+ \ Step through the string until we find the end
+ begin
+ 0 kernlen ! \ initialize length of value
+
+ \ Skip leading whitespace and/or comma delimiters
+ begin
+ dup 0<> if
+ over c@ delim? ( c-addr/u -- c-addr/u bool )
+ else
+ false ( c-addr/u -- c-addr/u bool )
+ then
+ while
+ 1- swap 1+ swap ( c-addr/u -- c-addr'/u' )
+ repeat
+ ( c-addr/u -- c-addr'/u' )
+
+ dup 0= if \ end of string while eating whitespace
+ 2drop ( c-addr/u -- )
+ kernmenuidx @ [char] 0 <> if \ found at least one
+ exit \ all done
+ then
+
+ \ No entries in $kernels; use $kernel instead
+ s" kernel" getenv dup -1 = if
+ drop ( cruft ) s" "
+ then ( -- c-addr/u )
+ dup kernlen ! \ store entire value length as kernlen
+ else
+ \ We're still within $kernels parsing toward the end;
+ \ find delimiter/end to determine kernlen
+ 2dup ( c-addr/u -- c-addr/u c-addr/u )
+ begin dup 0<> while
+ over c@ delim? if
+ drop 0 ( break ) \ found delimiter
+ else
+ kernlen @ 1+ kernlen ! \ incrememnt
+ 1- swap 1+ swap \ c-addr++ u--
+ then
+ repeat
+ 2drop ( c-addr/u c-addr'/u' -- c-addr/u )
+
+ \ If this is the first entry, compare it to $kernel
+ \ If different, then insert $kernel beforehand
+ kernmenuidx @ [char] 0 = if
+ over kernlen @ kerndefault count compare if
+ kernelsbuf 0 kerndefault count strcat
+ s" ," strcat 2swap strcat
+ kerndefault count swap drop kernlen !
+ then
+ then
+ then
+ ( c-addr/u -- c-addr'/u' )
+
+ \ At this point, we should have something on the stack to store
+ \ as the next kernel menu option; start assembling variables
+
+ over kernlen @ ( c-addr/u -- c-addr/u c-addr/u2 )
+
+ \ Assign first to kernel[x]
+ 2dup kernmenuidx @ kernel[x] setenv
+
+ \ Assign second to menu_caption[x][y]
+ kerncapbuf 0 s" [K]ernel: " strcat
+ 2over strcat
+ kernidx @ kernmenuidx @ menu_caption[x][y]
+ setenv
+
+ \ Assign third to ansi_caption[x][y]
+ kerncapbuf 0 s" @[1mK@[37mernel: " [char] @ escc! strcat
+ kernmenuidx @ [char] 0 = if
+ s" default/@[32m"
+ else
+ s" @[34;1m"
+ then
+ [char] @ escc! strcat
+ 2over strcat
+ s" @[37m" [char] @ escc! strcat
+ kernidx @ kernmenuidx @ ansi_caption[x][y]
+ setenv
+
+ 2drop ( c-addr/u c-addr/u2 -- c-addr/u )
+
+ kernmenuidx @ 1+ dup kernmenuidx ! [char] 8 > if
+ 2drop ( c-addr/u -- ) exit
+ then
+
+ kernlen @ - swap kernlen @ + swap ( c-addr/u -- c-addr'/u' )
+ again
+;
+
+\ This function goes through the kernels that were discovered by the
+\ parse-kernels function [above], adding " (# of #)" text to the end of each
+\ caption.
+\
+: tag-kernels ( -- )
+ kernidx @ ( -- x ) dup 0= if exit then
+ [char] 0 s" (Y of Z)" ( x -- x y c-addr/u )
+ kernmenuidx @ -rot 7 +c! \ Replace 'Z' with number of kernels parsed
+ begin
+ 2 pick 1+ -rot 2 +c! \ Replace 'Y' with current ASCII num
+
+ 2over menu_caption[x][y] getenv dup -1 <> if
+ 2dup + 1- c@ [char] ) = if
+ 2drop \ Already tagged
+ else
+ kerncapbuf 0 2swap strcat
+ 2over strcat
+ 5 pick 5 pick menu_caption[x][y] setenv
+ then
+ else
+ drop ( getenv cruft )
+ then
+
+ 2over ansi_caption[x][y] getenv dup -1 <> if
+ 2dup + 1- c@ [char] ) = if
+ 2drop \ Already tagged
+ else
+ kerncapbuf 0 2swap strcat
+ 2over strcat
+ 5 pick 5 pick ansi_caption[x][y] setenv
+ then
+ else
+ drop ( getenv cruft )
+ then
+
+ rot 1+ dup [char] 8 > if
+ -rot 2drop TRUE ( break )
+ else
+ -rot FALSE
+ then
+ until
+ 2drop ( x y -- )
+;
+
+\ This function creates the list of menu items. This function is called by the
+\ menu-display function. You need not call it directly.
+\
+: menu-create ( -- )
+
+ \ Print the frame caption at (x,y)
+ s" loader_menu_title" getenv dup -1 = if
+ drop s" Welcome to FreeBSD"
+ then
+ TRUE ( use default alignment )
+ s" loader_menu_title_align" getenv dup -1 <> if
+ 2dup s" left" compare-insensitive 0= if ( 1 )
+ 2drop ( c-addr/u ) drop ( bool )
+ menuX @ menuY @ 1-
+ FALSE ( don't use default alignment )
+ else ( 1 ) 2dup s" right" compare-insensitive 0= if ( 2 )
+ 2drop ( c-addr/u ) drop ( bool )
+ menuX @ 42 + 4 - over - menuY @ 1-
+ FALSE ( don't use default alignment )
+ else ( 2 ) 2drop ( c-addr/u ) then ( 1 ) then
+ else
+ drop ( getenv cruft )
+ then
+ if ( use default center alignement? )
+ menuX @ 19 + over 2 / - menuY @ 1-
+ then
+ at-xy type
+
+ \ If $menu_init is set, evaluate it (allowing for whole menus to be
+ \ constructed dynamically -- as this function could conceivably set
+ \ the remaining environment variables to construct the menu entirely).
+ \
+ s" menu_init" getenv dup -1 <> if
+ evaluate
+ else
+ drop
+ then
+
+ \ Print our menu options with respective key/variable associations.
+ \ `printmenuitem' ends by adding the decimal ASCII value for the
+ \ numerical prefix to the stack. We store the value left on the stack
+ \ to the key binding variable for later testing against a character
+ \ captured by the `getkey' function.
+
+ \ Note that any menu item beyond 9 will have a numerical prefix on the
+ \ screen consisting of the first digit (ie. 1 for the tenth menu item)
+ \ and the key required to activate that menu item will be the decimal
+ \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:')
+ \ which is misleading and not desirable.
+ \
+ \ Thus, we do not allow more than 8 configurable items on the menu
+ \ (with "Reboot" as the optional ninth and highest numbered item).
+
+ \
+ \ Initialize the ACPI option status.
+ \
+ 0 menuacpi !
+ s" menu_acpi" getenv -1 <> if
+ c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
+ menuacpi !
+ arch-i386? if acpipresent? if
+ \
+ \ Set menu toggle state to active state
+ \ (required by generic toggle_menuitem)
+ \
+ acpienabled? menuacpi @ toggle_stateN !
+ then then
+ else
+ drop
+ then
+ then
+
+ \
+ \ Initialize kernel captions after parsing $kernels
+ \
+ 0 menukernel !
+ s" menu_kernel" getenv -1 <> if
+ c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
+ dup menukernel !
+ dup parse-kernels tag-kernels
+
+ \ Get the current cycle state (entry to use)
+ s" kernel_state" evaluate @ 48 + ( n -- n y )
+
+ \ If state is invalid, reset
+ dup kernmenuidx @ 1- > if
+ drop [char] 0 ( n y -- n 48 )
+ 0 s" kernel_state" evaluate !
+ over s" init_kernel" evaluate drop
+ then
+
+ \ Set the current non-ANSI caption
+ 2dup swap dup ( n y -- n y y n n )
+ s" set menu_caption[x]=$menu_caption[x][y]"
+ 17 +c! 34 +c! 37 +c! evaluate
+ ( n y y n n c-addr/u -- n y )
+
+ \ Set the current ANSI caption
+ 2dup swap dup ( n y -- n y y n n )
+ s" set ansi_caption[x]=$ansi_caption[x][y]"
+ 17 +c! 34 +c! 37 +c! evaluate
+ ( n y y n n c-addr/u -- n y )
+
+ \ Initialize cycle state from stored value
+ 48 - ( n y -- n k )
+ s" init_cyclestate" evaluate ( n k -- n )
+
+ \ Set $kernel to $kernel[y]
+ s" activate_kernel" evaluate ( n -- n )
+ then
+ drop
+ then
+
+ \
+ \ Initialize the menu_options visual separator.
+ \
+ 0 menuoptions !
+ s" menu_options" getenv -1 <> if
+ c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
+ menuoptions !
+ else
+ drop
+ then
+ then
+
+ \ Initialize "Reboot" menu state variable (prevents double-entry)
+ false menurebootadded !
+
+ menu_start
+ 1- menuidx ! \ Initialize the starting index for the menu
+ 0 menurow ! \ Initialize the starting position for the menu
+
+ 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
+ begin
+ \ If the "Options:" separator, print it.
+ dup menuoptions @ = if
+ \ Optionally add a reboot option to the menu
+ s" menu_reboot" getenv -1 <> if
+ drop
+ s" Reboot" printmenuitem menureboot !
+ true menurebootadded !
+ then
+
+ menuX @
+ menurow @ 2 + menurow !
+ menurow @ menuY @ +
+ at-xy
+ s" menu_optionstext" getenv dup -1 <> if
+ type
+ else
+ drop ." Options:"
+ then
+ then
+
+ \ If this is the ACPI menu option, act accordingly.
+ dup menuacpi @ = if
+ dup acpimenuitem ( n -- n n c-addr/u | n n -1 )
+ dup -1 <> if
+ 13 +c! ( n n c-addr/u -- n c-addr/u )
+ \ replace 'x' with n
+ else
+ swap drop ( n n -1 -- n -1 )
+ over menu_command[x] unsetenv
+ then
+ else
+ \ make sure we have not already initialized this item
+ dup init_stateN dup @ 0= if
+ 1 swap !
+
+ \ If this menuitem has an initializer, run it
+ dup menu_init[x]
+ getenv dup -1 <> if
+ evaluate
+ else
+ drop
+ then
+ else
+ drop
+ then
+
+ dup
+ loader_color? if
+ ansi_caption[x]
+ else
+ menu_caption[x]
+ then
+ then
+
+ dup -1 <> if
+ \ test for environment variable
+ getenv dup -1 <> if
+ printmenuitem ( c-addr/u -- n )
+ dup menukeyN !
+ else
+ drop
+ then
+ else
+ drop
+ then
+
+ 1+ dup 56 > \ add 1 to iterator, continue if less than 57
+ until
+ drop \ iterator
+
+ \ Optionally add a reboot option to the menu
+ menurebootadded @ true <> if
+ s" menu_reboot" getenv -1 <> if
+ drop \ no need for the value
+ s" Reboot" \ menu caption (required by printmenuitem)
+
+ printmenuitem
+ menureboot !
+ else
+ 0 menureboot !
+ then
+ then
+;
+
+\ Takes a single integer on the stack and updates the timeout display. The
+\ integer must be between 0 and 9 (we will only update a single digit in the
+\ source message).
+\
+: menu-timeout-update ( N -- )
+
+ \ Enforce minimum/maximum
+ dup 9 > if drop 9 then
+ dup 0 < if drop 0 then
+
+ s" Autoboot in N seconds. [Space] to pause" ( n -- n c-addr/u )
+
+ 2 pick 0> if
+ rot 48 + -rot ( n c-addr/u -- n' c-addr/u ) \ convert to ASCII
+ 12 +c! ( n' c-addr/u -- c-addr/u ) \ replace 'N' above
+
+ menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor
+ type ( c-addr/u -- ) \ print message
+ else
+ menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor
+ spaces ( n c-addr/u -- n c-addr ) \ erase message
+ 2drop ( n c-addr -- )
+ then
+
+ 0 25 at-xy ( position cursor back at bottom-left )
+;
+
+\ This function blocks program flow (loops forever) until a key is pressed.
+\ The key that was pressed is added to the top of the stack in the form of its
+\ decimal ASCII representation. This function is called by the menu-display
+\ function. You need not call it directly.
+\
+: getkey ( -- ascii_keycode )
+
+ begin \ loop forever
+
+ menu_timeout_enabled @ 1 = if
+ ( -- )
+ seconds ( get current time: -- N )
+ dup menu_time @ <> if ( has time elapsed?: N N N -- N )
+
+ \ At least 1 second has elapsed since last loop
+ \ so we will decrement our "timeout" (really a
+ \ counter, insuring that we do not proceed too
+ \ fast) and update our timeout display.
+
+ menu_time ! ( update time record: N -- )
+ menu_timeout @ ( "time" remaining: -- N )
+ dup 0> if ( greater than 0?: N N 0 -- N )
+ 1- ( decrement counter: N -- N )
+ dup menu_timeout !
+ ( re-assign: N N Addr -- N )
+ then
+ ( -- N )
+
+ dup 0= swap 0< or if ( N <= 0?: N N -- )
+ \ halt the timer
+ 0 menu_timeout ! ( 0 Addr -- )
+ 0 menu_timeout_enabled ! ( 0 Addr -- )
+ then
+
+ \ update the timer display ( N -- )
+ menu_timeout @ menu-timeout-update
+
+ menu_timeout @ 0= if
+ \ We've reached the end of the timeout
+ \ (user did not cancel by pressing ANY
+ \ key)
+
+ s" menu_timeout_command" getenv dup
+ -1 = if
+ drop \ clean-up
+ else
+ evaluate
+ then
+ then
+
+ else ( -- N )
+ \ No [detectable] time has elapsed (in seconds)
+ drop ( N -- )
+ then
+ ( -- )
+ then
+
+ key? if \ Was a key pressed? (see loader(8))
+
+ \ An actual key was pressed (if the timeout is running,
+ \ kill it regardless of which key was pressed)
+ menu_timeout @ 0<> if
+ 0 menu_timeout !
+ 0 menu_timeout_enabled !
+
+ \ clear screen of timeout message
+ 0 menu-timeout-update
+ then
+
+ \ get the key that was pressed and exit (if we
+ \ get a non-zero ASCII code)
+ key dup 0<> if
+ exit
+ else
+ drop
+ then
+ then
+ 50 ms \ sleep for 50 milliseconds (see loader(8))
+
+ again
+;
+
+: menu-erase ( -- ) \ Erases menu and resets positioning variable to position 1.
+
+ \ Clear the screen area associated with the interactive menu
+ menuX @ menuY @
+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
+ 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces
+ 2drop
+
+ \ Reset the starting index and position for the menu
+ menu_start 1- menuidx !
+ 0 menurow !
+;
+
+only forth
+also menu-infrastructure
+also menu-namespace
+also menu-command-helpers definitions
+
+: toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state
+
+ \ ASCII numeral equal to user-selected menu item must be on the stack.
+ \ We do not modify the stack, so the ASCII numeral is left on top.
+
+ dup init_textN c@ 0= if
+ \ NOTE: no need to check toggle_stateN since the first time we
+ \ are called, we will populate init_textN. Further, we don't
+ \ need to test whether menu_caption[x] (ansi_caption[x] when
+ \ loader_color?=1) is available since we would not have been
+ \ called if the caption was NULL.
+
+ \ base name of environment variable
+ dup ( n -- n n ) \ key pressed
+ loader_color? if
+ ansi_caption[x]
+ else
+ menu_caption[x]
+ then
+ getenv dup -1 <> if
+
+ 2 pick ( n c-addr/u -- n c-addr/u n )
+ init_textN ( n c-addr/u n -- n c-addr/u c-addr )
+
+ \ now we have the buffer c-addr on top
+ \ ( followed by c-addr/u of current caption )
+
+ \ Copy the current caption into our buffer
+ 2dup c! -rot \ store strlen at first byte
+ begin
+ rot 1+ \ bring alt addr to top and increment
+ -rot -rot \ bring buffer addr to top
+ 2dup c@ swap c! \ copy current character
+ 1+ \ increment buffer addr
+ rot 1- \ bring buffer len to top and decrement
+ dup 0= \ exit loop if buffer len is zero
+ until
+ 2drop \ buffer len/addr
+ drop \ alt addr
+
+ else
+ drop
+ then
+ then
+
+ \ Now we are certain to have init_textN populated with the initial
+ \ value of menu_caption[x] (ansi_caption[x] with loader_color enabled).
+ \ We can now use init_textN as the untoggled caption and
+ \ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the
+ \ toggled caption and store the appropriate value into menu_caption[x]
+ \ (again, ansi_caption[x] with loader_color enabled). Last, we'll
+ \ negate the toggled state so that we reverse the flow on subsequent
+ \ calls.
+
+ dup toggle_stateN @ 0= if
+ \ state is OFF, toggle to ON
+
+ dup ( n -- n n ) \ key pressed
+ loader_color? if
+ toggled_ansi[x]
+ else
+ toggled_text[x]
+ then
+ getenv dup -1 <> if
+ \ Assign toggled text to menu caption
+ 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed
+ loader_color? if
+ ansi_caption[x]
+ else
+ menu_caption[x]
+ then
+ setenv
+ else
+ \ No toggled text, keep the same caption
+ drop ( n -1 -- n ) \ getenv cruft
+ then
+
+ true \ new value of toggle state var (to be stored later)
+ else
+ \ state is ON, toggle to OFF
+
+ dup init_textN count ( n -- n c-addr/u )
+
+ \ Assign init_textN text to menu caption
+ 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed
+ loader_color? if
+ ansi_caption[x]
+ else
+ menu_caption[x]
+ then
+ setenv
+
+ false \ new value of toggle state var (to be stored below)
+ then
+
+ \ now we'll store the new toggle state (on top of stack)
+ over toggle_stateN !
+;
+
+: cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem
+
+ \ ASCII numeral equal to user-selected menu item must be on the stack.
+ \ We do not modify the stack, so the ASCII numeral is left on top.
+
+ dup cycle_stateN dup @ 1+ \ get value and increment
+
+ \ Before assigning the (incremented) value back to the pointer,
+ \ let's test for the existence of this particular array element.
+ \ If the element exists, we'll store index value and move on.
+ \ Otherwise, we'll loop around to zero and store that.
+
+ dup 48 + ( n addr k -- n addr k k' )
+ \ duplicate array index and convert to ASCII numeral
+
+ 3 pick swap ( n addr k k' -- n addr k n k' ) \ (n,k') as (x,y)
+ loader_color? if
+ ansi_caption[x][y]
+ else
+ menu_caption[x][y]
+ then
+ ( n addr k n k' -- n addr k c-addr/u )
+
+ \ Now test for the existence of our incremented array index in the
+ \ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color
+ \ enabled) as set in loader.rc(5), et. al.
+
+ getenv dup -1 = if
+ \ No caption set for this array index. Loop back to zero.
+
+ drop ( n addr k -1 -- n addr k ) \ getenv cruft
+ drop 0 ( n addr k -- n addr 0 ) \ new value to store later
+
+ 2 pick [char] 0 ( n addr 0 -- n addr 0 n 48 ) \ (n,48) as (x,y)
+ loader_color? if
+ ansi_caption[x][y]
+ else
+ menu_caption[x][y]
+ then
+ ( n addr 0 n 48 -- n addr 0 c-addr/u )
+ getenv dup -1 = if
+ \ Highly unlikely to occur, but to ensure things move
+ \ along smoothly, allocate a temporary NULL string
+ drop ( cruft ) s" "
+ then
+ then
+
+ \ At this point, we should have the following on the stack (in order,
+ \ from bottom to top):
+ \
+ \ n - Ascii numeral representing the menu choice (inherited)
+ \ addr - address of our internal cycle_stateN variable
+ \ k - zero-based number we intend to store to the above
+ \ c-addr/u - string value we intend to store to menu_caption[x]
+ \ (or ansi_caption[x] with loader_color enabled)
+ \
+ \ Let's perform what we need to with the above.
+
+ \ Assign array value text to menu caption
+ 4 pick ( n addr k c-addr/u -- n addr k c-addr/u n )
+ loader_color? if
+ ansi_caption[x]
+ else
+ menu_caption[x]
+ then
+ setenv
+
+ swap ! ( n addr k -- n ) \ update array state variable
+;
+
+only forth definitions also menu-infrastructure
+
+\ Erase and redraw the menu. Useful if you change a caption and want to
+\ update the menu to reflect the new value.
+\
+: menu-redraw ( -- )
+ menu-erase
+ menu-create
+;
+
+\ This function initializes the menu. Call this from your `loader.rc' file
+\ before calling any other menu-related functions.
+\
+: menu-init ( -- )
+ menu_start
+ 1- menuidx ! \ Initialize the starting index for the menu
+ 0 menurow ! \ Initialize the starting position for the menu
+
+ \ Assign configuration values
+ s" loader_menu_y" getenv dup -1 = if
+ drop \ no custom row position
+ menu_default_y
+ else
+ \ make sure custom position is a number
+ ?number 0= if
+ menu_default_y \ or use default
+ then
+ then
+ menuY !
+ s" loader_menu_x" getenv dup -1 = if
+ drop \ no custom column position
+ menu_default_x
+ else
+ \ make sure custom position is a number
+ ?number 0= if
+ menu_default_x \ or use default
+ then
+ then
+ menuX !
+
+ \ Interpret a custom frame type for the menu
+ TRUE ( draw a box? default yes, but might be altered below )
+ s" loader_menu_frame" getenv dup -1 = if ( 1 )
+ drop \ no custom frame type
+ else ( 1 ) 2dup s" single" compare-insensitive 0= if ( 2 )
+ f_single ( see frames.4th )
+ else ( 2 ) 2dup s" double" compare-insensitive 0= if ( 3 )
+ f_double ( see frames.4th )
+ else ( 3 ) s" none" compare-insensitive 0= if ( 4 )
+ drop FALSE \ don't draw a box
+ ( 4 ) then ( 3 ) then ( 2 ) then ( 1 ) then
+ if
+ 42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y)
+ then
+
+ 0 25 at-xy \ Move cursor to the bottom for output
+;
+
+also menu-namespace
+
+\ Main function. Call this from your `loader.rc' file.
+\
+: menu-display ( -- )
+
+ 0 menu_timeout_enabled ! \ start with automatic timeout disabled
+
+ \ check indication that automatic execution after delay is requested
+ s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr )
+ drop ( just testing existence right now: Addr -- )
+
+ \ initialize state variables
+ seconds menu_time ! ( store the time we started )
+ 1 menu_timeout_enabled ! ( enable automatic timeout )
+
+ \ read custom time-duration (if set)
+ s" autoboot_delay" getenv dup -1 = if
+ drop \ no custom duration (remove dup'd bunk -1)
+ menu_timeout_default \ use default setting
+ else
+ 2dup ?number 0= if ( if not a number )
+ \ disable timeout if "NO", else use default
+ s" NO" compare-insensitive 0= if
+ 0 menu_timeout_enabled !
+ 0 ( assigned to menu_timeout below )
+ else
+ menu_timeout_default
+ then
+ else
+ -rot 2drop
+
+ \ boot immediately if less than zero
+ dup 0< if
+ drop
+ menu-create
+ 0 25 at-xy
+ 0 boot
+ then
+ then
+ then
+ menu_timeout ! ( store value on stack from above )
+
+ menu_timeout_enabled @ 1 = if
+ \ read custom column position (if set)
+ s" loader_menu_timeout_x" getenv dup -1 = if
+ drop \ no custom column position
+ menu_timeout_default_x \ use default setting
+ else
+ \ make sure custom position is a number
+ ?number 0= if
+ menu_timeout_default_x \ or use default
+ then
+ then
+ menu_timeout_x ! ( store value on stack from above )
+
+ \ read custom row position (if set)
+ s" loader_menu_timeout_y" getenv dup -1 = if
+ drop \ no custom row position
+ menu_timeout_default_y \ use default setting
+ else
+ \ make sure custom position is a number
+ ?number 0= if
+ menu_timeout_default_y \ or use default
+ then
+ then
+ menu_timeout_y ! ( store value on stack from above )
+ then
+ then
+
+ menu-create
+
+ begin \ Loop forever
+
+ 0 25 at-xy \ Move cursor to the bottom for output
+ getkey \ Block here, waiting for a key to be pressed
+
+ dup -1 = if
+ drop exit \ Caught abort (abnormal return)
+ then
+
+ \ Boot if the user pressed Enter/Ctrl-M (13) or
+ \ Ctrl-Enter/Ctrl-J (10)
+ dup over 13 = swap 10 = or if
+ drop ( no longer needed )
+ s" boot" evaluate
+ exit ( pedantic; never reached )
+ then
+
+ dup menureboot @ = if 0 reboot then
+
+ \ Evaluate the decimal ASCII value against known menu item
+ \ key associations and act accordingly
+
+ 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
+ begin
+ dup menukeyN @
+ rot tuck = if
+
+ \ Adjust for missing ACPI menuitem on non-i386
+ arch-i386? true <> menuacpi @ 0<> and if
+ menuacpi @ over 2dup < -rot = or
+ over 58 < and if
+ ( key >= menuacpi && key < 58: N -- N )
+ 1+
+ then
+ then
+
+ \ Test for the environment variable
+ dup menu_command[x]
+ getenv dup -1 <> if
+ \ Execute the stored procedure
+ evaluate
+
+ \ We expect there to be a non-zero
+ \ value left on the stack after
+ \ executing the stored procedure.
+ \ If so, continue to run, else exit.
+
+ 0= if
+ drop \ key pressed
+ drop \ loop iterator
+ exit
+ else
+ swap \ need iterator on top
+ then
+ then
+
+ \ Re-adjust for missing ACPI menuitem
+ arch-i386? true <> menuacpi @ 0<> and if
+ swap
+ menuacpi @ 1+ over 2dup < -rot = or
+ over 59 < and if
+ 1-
+ then
+ swap
+ then
+ else
+ swap \ need iterator on top
+ then
+
+ \
+ \ Check for menu keycode shortcut(s)
+ \
+ dup menu_keycode[x]
+ getenv dup -1 = if
+ drop
+ else
+ ?number 0<> if
+ rot tuck = if
+ swap
+ dup menu_command[x]
+ getenv dup -1 <> if
+ evaluate
+ 0= if
+ 2drop
+ exit
+ then
+ else
+ drop
+ then
+ else
+ swap
+ then
+ then
+ then
+
+ 1+ dup 56 > \ increment iterator
+ \ continue if less than 57
+ until
+ drop \ loop iterator
+ drop \ key pressed
+
+ again \ Non-operational key was pressed; repeat
+;
+
+\ This function unsets all the possible environment variables associated with
+\ creating the interactive menu.
+\
+: menu-unset ( -- )
+
+ 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
+ begin
+ dup menu_init[x] unsetenv \ menu initializer
+ dup menu_command[x] unsetenv \ menu command
+ dup menu_caption[x] unsetenv \ menu caption
+ dup ansi_caption[x] unsetenv \ ANSI caption
+ dup menu_keycode[x] unsetenv \ menu keycode
+ dup toggled_text[x] unsetenv \ toggle_menuitem caption
+ dup toggled_ansi[x] unsetenv \ toggle_menuitem ANSI caption
+
+ 48 \ Iterator start (inner range 48 to 57; ASCII '0' to '9')
+ begin
+ \ cycle_menuitem caption and ANSI caption
+ 2dup menu_caption[x][y] unsetenv
+ 2dup ansi_caption[x][y] unsetenv
+ 1+ dup 57 >
+ until
+ drop \ inner iterator
+
+ 0 over menukeyN ! \ used by menu-create, menu-display
+ 0 over init_stateN ! \ used by menu-create
+ 0 over toggle_stateN ! \ used by toggle_menuitem
+ 0 over init_textN c! \ used by toggle_menuitem
+ 0 over cycle_stateN ! \ used by cycle_menuitem
+
+ 1+ dup 56 > \ increment, continue if less than 57
+ until
+ drop \ iterator
+
+ s" menu_timeout_command" unsetenv \ menu timeout command
+ s" menu_reboot" unsetenv \ Reboot menu option flag
+ s" menu_acpi" unsetenv \ ACPI menu option flag
+ s" menu_kernel" unsetenv \ Kernel menu option flag
+ s" menu_options" unsetenv \ Options separator flag
+ s" menu_optionstext" unsetenv \ separator display text
+ s" menu_init" unsetenv \ menu initializer
+
+ 0 menureboot !
+ 0 menuacpi !
+ 0 menuoptions !
+;
+
+only forth definitions also menu-infrastructure
+
+\ This function both unsets menu variables and visually erases the menu area
+\ in-preparation for another menu.
+\
+: menu-clear ( -- )
+ menu-unset
+ menu-erase
+;
+
+bullet menubllt !
+
+also menu-namespace
+
+\ Initialize our menu initialization state variables
+0 init_state1 !
+0 init_state2 !
+0 init_state3 !
+0 init_state4 !
+0 init_state5 !
+0 init_state6 !
+0 init_state7 !
+0 init_state8 !
+
+\ Initialize our boolean state variables
+0 toggle_state1 !
+0 toggle_state2 !
+0 toggle_state3 !
+0 toggle_state4 !
+0 toggle_state5 !
+0 toggle_state6 !
+0 toggle_state7 !
+0 toggle_state8 !
+
+\ Initialize our array state variables
+0 cycle_state1 !
+0 cycle_state2 !
+0 cycle_state3 !
+0 cycle_state4 !
+0 cycle_state5 !
+0 cycle_state6 !
+0 cycle_state7 !
+0 cycle_state8 !
+
+\ Initialize string containers
+0 init_text1 c!
+0 init_text2 c!
+0 init_text3 c!
+0 init_text4 c!
+0 init_text5 c!
+0 init_text6 c!
+0 init_text7 c!
+0 init_text8 c!
+
+only forth definitions
diff --git a/stand/forth/menu.4th.8 b/stand/forth/menu.4th.8
new file mode 100644
index 0000000..3673eec
--- /dev/null
+++ b/stand/forth/menu.4th.8
@@ -0,0 +1,352 @@
+.\" Copyright (c) 2011-2013 Devin Teske
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 6, 2013
+.Dt MENU.4TH 8
+.Os
+.Sh NAME
+.Nm menu.4th
+.Nd FreeBSD dynamic menu boot module
+.Sh DESCRIPTION
+The file that goes by the name of
+.Nm
+is a set of commands designed to display a dynamic menu system managed through
+a system of carefully named environment variables.
+The commands of
+.Nm
+by themselves are not enough for most uses.
+Please refer to the
+examples below for the most common situations, and to
+.Xr loader 8
+for additional commands.
+.Pp
+Before using any of the commands provided in
+.Nm ,
+it must be included
+through the command:
+.Pp
+.Dl include menu.4th
+.Pp
+This line is present in the default
+.Pa /boot/menu.rc
+file, so it is not needed (and should not be re-issued) in a normal setup.
+.Pp
+The commands provided by it are:
+.Pp
+.Bl -tag -width disable-module_module -compact -offset indent
+.It Ic menu-init
+Draws the menu bounding box and initializes some internal state variables.
+This should be called before any other menu-related functions.
+.It Ic menu-display
+Displays the menu (configured via the below documented environment variables)
+and blocks on keyboard input, awaiting user action.
+.It Ic menu-erase
+Clears the screen area within the menu bounding box.
+.It Ic menu-redraw
+Calls
+.Ic menu-erase
+and then redraws the menu.
+.It Ic menu-unset
+Unsets the environment variables associated with individual menu items,
+clearing the way for a new menu.
+.It Ic menu-clear
+Calls
+.Ic menu-unset
+and then
+.Ic menu-erase .
+.El
+.Pp
+The environment variables that effect its behavior are:
+.Bl -tag -width bootfile -offset indent
+.It Va loader_color
+If set to
+.Dq Li NO
+(case-insensitive) or
+.Dq Li 0 ,
+causes the menu to be displayed without color.
+The default is to use ANSI coloring whenever possible.
+If serial boot is enabled, color is disabled by default.
+Color features include the use of ANSI bold for numbers appearing to the left
+of menuitems and the use of special
+.Dq Li ansi
+variables described below.
+.It Va autoboot_delay
+Number of seconds
+.Ic menu-display
+will wait before executing
+.Va menu_timeout_command
+.Ic ( boot
+by default) unless a key is pressed.
+If set to
+.Dq Li NO
+(case-insensitive)
+.Ic menu-display
+will wait for user input and never execute
+.Ic menu_timeout_command .
+If set to
+.Dq Li -1 ,
+.Ic menu-display
+will boot immediately, preventing both interruption of the
+.Ic autoboot
+process and escaping to the loader prompt.
+Default is
+.Dq Li 10 .
+See
+.Xr loader 8
+for additional information.
+.It Va menu_timeout_command
+The command to be executed after
+.Va autoboot_delay
+seconds if a key is not pressed.
+The default is
+.Ic boot .
+.It Va loader_menu_frame
+Sets the desired box style to draw around the boot menu.
+Possible values are:
+.Dq Li single
+.Pq the default ,
+.Dq Li double ,
+and
+.Dq Li none .
+.It Va loader_menu_timeout_x
+Sets the desired column position of the timeout countdown text.
+Default is 4.
+.It Va loader_menu_timeout_y
+Sets the desired row position of the timeout countdown text.
+Default is 23.
+.It Va loader_menu_title
+The text to display above the menu.
+Default is
+.Dq Li "Welcome to FreeBSD" .
+.It Va loader_menu_title_align
+Default is to align
+.Ic loader_menu_title
+centered above the menu. This can be set to
+.Dq Li left
+or
+.Dq Li right
+to instead display the title left-or-right justified
+.Pq respectively .
+.It Va loader_menu_x
+Sets the desired column position of the boot menu.
+Default is 5.
+.It Va loader_menu_y
+Sets the desired row position of the boot menu.
+Default is 10.
+.It Va menu_caption[x]
+The text to be displayed for the numbered menuitem
+.Dq Li x .
+.It Va menu_command[x]
+The command to be executed when the number associated with menuitem
+.Dq Li x
+is pressed.
+See the list of included FICL words below for some ideas.
+.It Va menu_keycode[x]
+An optional decimal ASCII keycode to be associated with menuitem
+.Dq Li x .
+When pressed, will cause the execution of
+.Va menu_command[x] .
+.It Va ansi_caption[x]
+If
+.Va loader_color
+is set
+.Pq enabled by default ,
+use this caption for menuitem
+.Dq Li x
+instead of
+.Va menu_caption[x] .
+.It Va toggled_text[x]
+For menuitems where
+.Va menu_command[x]
+is set to
+.Dq Li toggle_menuitem
+(or a derivative thereof), the text displayed
+will toggle between this and
+.Va menu_caption[x] .
+.It Va toggled_ansi[x]
+Like
+.Va toggled_text[x]
+except used when
+.Va loader_color
+is enabled
+.Pq default .
+.It Va menu_caption[x][y]
+For menuitems where
+.Va menu_command[x]
+is set to
+.Dq Li cycle_menuitem
+(or a derivative thereof), the text displayed will cycle between this and other
+.Va menu_caption[x][y]
+entries.
+.It Va ansi_caption[x][y]
+Like
+.Va menu_caption[x][y]
+except used when
+.Va loader_color
+is enabled
+.Pq default .
+.It Va menu_acpi
+When set to a number
+.Dq Li x
+associated with a given menuitem, that menuitem will only appear when
+running on i386-compatible hardware,
+.Va hint.acpi.0.rsdp
+is set (indicating the presence of hardware ACPI support as detected by
+.Xr loader 8 ) ,
+and
+.Va hint.acpi.0.disabled
+is not set.
+On non-i386 hardware, menuitems configured after the
+.Dq Li menu_acpi
+menuitem will use a lower number (to compensate for the missing ACPI menuitem)
+but continue to function as expected.
+On i386-compatible hardware lacking ACPI support (as detected by
+.Xr loader 8 ) ,
+subsequent menuitems will retain their associated numbers.
+.It Va hint.acpi.0.rsdp
+Set automatically by
+.Xr loader 8
+on i386-compatible hardware when ACPI support is detected at boot time.
+Effects the display of the
+.Dq Li menu_acpi
+menuitem (if configured).
+.It Va hint.acpi.0.disabled
+Effects the display of the
+.Va menu_acpi
+menuitem.
+If set, the menuitem will display
+.Va toggled_text[x]
+.Va ( toggled_ansi[x]
+if
+.Va loader_color
+is set), otherwise
+.Va menu_caption[x]
+.Va ( ansi_caption[x]
+if
+.Va loader_color
+is set).
+.It Va menu_options
+When set to a number
+.Dq Li x ,
+a single blank-line and an
+.Dq Li Options
+header are inserted between
+.Va menu_caption[x-1]
+and
+.Va menu_caption[x]
+(if configured).
+.It Va menu_reboot
+If set, adds a built-in
+.Dq Li Reboot
+menuitem to the end of the last configured menuitem.
+If
+.Va menu_options
+is configured, the
+.Dq Li Reboot
+menuitem will be inserted before the
+.Dq Options
+separator.
+.El
+.Pp
+In addition, it provides the following FICL words:
+.Pp
+.Bl -tag -width disable-module_module -compact -offset indent
+.It Ic arch-i386? ( -- BOOL )
+Returns true (-1) on i386 and false (0) otherwise.
+.It Ic acpipresent? ( -- BOOL )
+Returns true (-1) if ACPI is present and false (0) otherwise.
+.It Ic acpienabled? ( -- BOOL )
+Returns true (-1) if ACPI is enabled and false (0) otherwise.
+.It Ic toggle_menuitem ( N -- N )
+Toggles menuitem
+.Dq Li N
+between
+.Va menu_caption[x]
+and
+.Va toggled_text[x]
+(where
+.Dq Li N
+represents the ASCII decimal value for
+.Dq Li x ) .
+.It Ic cycle_menuitem ( N -- N )
+Cycles menuitem
+.Dq Li N
+between
+.Va menu_caption[x][y]
+entries (where
+.Va N
+represents the ASCII decimal value for
+.Va x ) .
+.El
+.Pp
+For all values of
+.Dq Li x
+above, use any number between 1 through 9. Sorry, double-digits are not
+currently supported.
+.Sh FILES
+.Bl -tag -width /boot/loader.4th -compact
+.It Pa /boot/loader
+The
+.Xr loader 8 .
+.It Pa /boot/menu.4th
+.Nm
+itself.
+.It Pa /boot/loader.rc
+.Xr loader 8
+bootstrapping script.
+.El
+.Sh EXAMPLES
+A simple boot menu:
+.Pp
+.Bd -literal -offset indent -compact
+include /boot/menu.4th
+menu-init
+set menu_caption[1]="Boot"
+set menu_command[1]="boot"
+set menu_options=2
+set menu_caption[2]="Option: NO"
+set toggled_text[2]="Option: YES"
+set menu_command[2]="toggle_menuitem"
+set menu_timeout_command="boot"
+set menu_reboot
+menu-display
+.Ed
+.Sh SEE ALSO
+.Xr loader.conf 5 ,
+.Xr beastie.4th 8 ,
+.Xr loader 8 ,
+.Xr loader.4th 8
+.Sh HISTORY
+The
+.Nm
+set of commands first appeared in
+.Fx 9.0 .
+.Sh AUTHORS
+The
+.Nm
+set of commands was written by
+.An -nosplit
+.An Devin Teske Aq dteske@FreeBSD.org .
diff --git a/stand/forth/menu.rc b/stand/forth/menu.rc
new file mode 100644
index 0000000..6fe3dfe
--- /dev/null
+++ b/stand/forth/menu.rc
@@ -0,0 +1,202 @@
+\ Menu.rc
+\ $FreeBSD$
+\
+\ You should not edit this file! Put any overrides in menu.rc.local
+\ instead as this file can be replaced during system updates.
+\
+\ Load required Forth modules
+include /boot/version.4th
+include /boot/brand.4th
+include /boot/menu.4th
+include /boot/menu-commands.4th
+include /boot/shortcuts.4th
+
+\ Screen prep
+clear \ clear the screen (see `screen.4th')
+print_version \ print version string (bottom-right; see `version.4th')
+draw-beastie \ draw freebsd mascot (on right; see `beastie.4th')
+draw-brand \ draw the FreeBSD title (top-left; see `brand.4th')
+menu-init \ initialize the menu area (see `menu.4th')
+
+\ Initialize main menu constructs (see `menu.4th')
+\ NOTE: To use `non-ansi' variants, add `loader_color=0' to loader.conf(5)
+\ NOTE: ANSI variants can use `^' in place of literal `Esc' (ASCII 27)
+
+\
+\ MAIN MENU
+\
+
+set menuset_name1="main"
+
+set mainmenu_init[1]="init_boot"
+set mainmenu_caption[1]="Boot Multi User [Enter]"
+set maintoggled_text[1]="Boot [S]ingle User [Enter]"
+set mainmenu_command[1]="boot"
+set mainansi_caption[1]="^[1mB^[moot Multi User ^[1m[Enter]^[m"
+set maintoggled_ansi[1]="Boot ^[1mS^[mingle User ^[1m[Enter]^[m"
+\ keycode set by init_boot
+
+set mainmenu_init[2]="init_altboot"
+set mainmenu_caption[2]="Boot [S]ingle User"
+set maintoggled_text[2]="Boot [M]ulti User"
+set mainmenu_command[2]="altboot"
+set mainansi_caption[2]="Boot ^[1mS^[mingle User"
+set maintoggled_ansi[2]="Boot ^[1mM^[multi User"
+\ keycode set by init_altboot
+
+set mainmenu_caption[3]="[Esc]ape to loader prompt"
+set mainmenu_command[3]="goto_prompt"
+set mainmenu_keycode[3]=27
+set mainansi_caption[3]="^[1mEsc^[mape to loader prompt"
+
+\ Enable built-in "Reboot" trailing menuitem
+\ NOTE: appears before menu_options if configured
+\
+set mainmenu_reboot
+
+\ Enable "Options:" separator. When set to a numerical value (1-8), a visual
+\ separator is inserted before that menuitem number.
+\
+set mainmenu_options=5
+
+set mainmenu_kernel=5
+set mainmenu_command[5]="cycle_kernel"
+set mainmenu_keycode[5]=107
+
+set mainmenu_caption[6]="Configure Boot [O]ptions..."
+set mainmenu_command[6]="2 goto_menu"
+set mainmenu_keycode[6]=111
+set mainansi_caption[6]="Configure Boot ^[1mO^[mptions..."
+
+s" currdev" getenv dup 0> [if] drop 4 s" zfs:" compare 0= [if]
+ set mainmenu_caption[7]="Select Boot [E]nvironment..."
+ set mainmenu_command[7]="3 goto_menu"
+ set mainmenu_keycode[7]=101
+ set mainansi_caption[7]="Select Boot ^[1mE^[37mnvironment..."
+
+ s" chain_disk" getenv? [if]
+ set mainmenu_caption[8]="Chain[L]oad ${chain_disk}"
+ set mainmenu_command[8]="chain ${chain_disk}"
+ set mainmenu_keycode[8]=108
+ set mainansi_caption[8]="Chain^[1mL^[moad ${chain_disk}"
+ [then]
+[else]
+ s" chain_disk" getenv? [if]
+ set mainmenu_caption[7]="Chain[L]oad ${chain_disk}"
+ set mainmenu_command[7]="chain ${chain_disk}"
+ set mainmenu_keycode[7]=108
+ set mainansi_caption[7]="Chain^[1mL^[moad ${chain_disk}"
+ [then]
+[then] [else] drop [then]
+
+
+\
+\ BOOT OPTIONS MENU
+\
+
+set menuset_name2="options"
+
+set optionsmenu_caption[1]="Back to Main Menu [Backspace]"
+set optionsmenu_command[1]="1 goto_menu"
+set optionsmenu_keycode[1]=8
+set optionsansi_caption[1]="Back to Main Menu ^[1m[Backspace]^[m"
+
+set optionsmenu_caption[2]="Load System [D]efaults"
+set optionsmenu_command[2]="set_default_boot_options"
+set optionsmenu_keycode[2]=100
+set optionsansi_caption[2]="Load System ^[1mD^[mefaults"
+
+set optionsmenu_options=3
+set optionsmenu_optionstext="Boot Options:"
+
+set optionsmenu_acpi=3
+set optionsmenu_caption[3]="[A]CPI Support off"
+set optionstoggled_text[3]="[A]CPI Support On"
+set optionsmenu_command[3]="toggle_acpi"
+set optionsmenu_keycode[3]=97
+set optionsansi_caption[3]="^[1mA^[mCPI Support ^[34;1mOff^[m"
+set optionstoggled_ansi[3]="^[1mA^[mCPI Support ^[32;7mOn^[m"
+
+set optionsmenu_init[4]="init_safemode"
+set optionsmenu_caption[4]="Safe [M]ode... off"
+set optionstoggled_text[4]="Safe [M]ode... On"
+set optionsmenu_command[4]="toggle_safemode"
+set optionsmenu_keycode[4]=109
+set optionsansi_caption[4]="Safe ^[1mM^[mode... ^[34;1mOff^[m"
+set optionstoggled_ansi[4]="Safe ^[1mM^[mode... ^[32;7mOn^[m"
+
+set optionsmenu_init[5]="init_singleuser"
+set optionsmenu_caption[5]="[S]ingle User. off"
+set optionstoggled_text[5]="[S]ingle User. On"
+set optionsmenu_command[5]="toggle_singleuser"
+set optionsmenu_keycode[5]=115
+set optionsansi_caption[5]="^[1mS^[mingle User. ^[34;1mOff^[m"
+set optionstoggled_ansi[5]="^[1mS^[mingle User. ^[32;7mOn^[m"
+
+set optionsmenu_init[6]="init_verbose"
+set optionsmenu_caption[6]="[V]erbose..... off"
+set optionstoggled_text[6]="[V]erbose..... On"
+set optionsmenu_command[6]="toggle_verbose"
+set optionsmenu_keycode[6]=118
+set optionsansi_caption[6]="^[1mV^[merbose..... ^[34;1mOff^[m"
+set optionstoggled_ansi[6]="^[1mV^[merbose..... ^[32;7mOn^[m"
+
+\
+\ BOOT ENVIRONMENT MENU
+\
+
+set menuset_name3="bootenv"
+
+set bemenu_current="Active: "
+set beansi_current="^[1m${bemenu_current}^[m"
+set bemenu_bootfs="bootfs: "
+set beansi_bootfs="^[1m${bemenu_bootfs}^[m"
+set bemenu_page="[P]age: "
+set beansi_page="^[1mP^[mage: "
+set bemenu_pageof=" of "
+set beansi_pageof="${bemenu_pageof}"
+set zfs_be_currpage=1
+
+set bootenvmenu_init="init_bootenv"
+
+set bootenvmenu_command[1]="be_draw_screen 1 goto_menu"
+set bootenvmenu_keycode[1]=8
+
+set bootenvmenu_command[2]="set_bootenv"
+set bootenvmenu_keycode[2]=97
+set bootenv_root[2]="${zfs_be_active}"
+
+set bootenvmenu_command[3]="set_be_page"
+set bootenvmenu_keycode[3]=112
+
+set bootenvmenu_options=4
+set bootenvmenu_optionstext="Boot Environments:"
+
+\ Enable automatic booting (add ``autoboot_delay=N'' to loader.conf(5) to
+\ customize the timeout; default is 10-seconds)
+\
+set menu_timeout_command="boot"
+
+\ Include optional elements defined in a local file
+\
+try-include /boot/menu.rc.local
+
+\ Initialize boot environment variables
+\
+s" reloadbe" sfind ( xt|0 bool ) [if]
+ s" bootenv_autolist" getenv dup -1 = [if]
+ drop s" execute" evaluate \ Use evaluate to avoid passing
+ \ reloadbe an optional parameter
+ [else]
+ s" YES" compare-insensitive 0= [if]
+ s" execute" evaluate
+ [then]
+ [then]
+[else]
+ drop ( xt=0 )
+[then]
+
+\ Display the main menu (see `menu.4th')
+set menuset_initial=1
+menuset-loadinitial
+menu-display
diff --git a/stand/forth/menusets.4th b/stand/forth/menusets.4th
new file mode 100644
index 0000000..9335b80
--- /dev/null
+++ b/stand/forth/menusets.4th
@@ -0,0 +1,624 @@
+\ Copyright (c) 2012 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+marker task-menusets.4th
+
+vocabulary menusets-infrastructure
+only forth also menusets-infrastructure definitions
+
+variable menuset_use_name
+
+create menuset_affixbuf 255 allot
+create menuset_x 1 allot
+create menuset_y 1 allot
+
+: menuset-loadvar ( -- )
+
+ \ menuset_use_name is true or false
+ \ $type should be set to one of:
+ \ menu toggled ansi
+ \ $var should be set to one of:
+ \ caption command keycode text ...
+ \ $affix is either prefix (menuset_use_name is true)
+ \ or infix (menuset_use_name is false)
+
+ s" set cmdbuf='set ${type}_${var}=\$'" evaluate
+ s" cmdbuf" getenv swap drop ( -- u1 ) \ get string length
+ menuset_use_name @ true = if
+ s" set cmdbuf=${cmdbuf}${affix}${type}_${var}"
+ ( u1 -- u1 c-addr2 u2 )
+ else
+ s" set cmdbuf=${cmdbuf}${type}set${affix}_${var}"
+ ( u1 -- u1 c-addr2 u2 )
+ then
+ evaluate ( u1 c-addr2 u2 -- u1 )
+ s" cmdbuf" getenv ( u1 -- u1 c-addr2 u2 )
+ rot 2 pick 2 pick over + -rot + tuck -
+ ( u1 c-addr2 u2 -- c-addr2 u2 c-addr1 u1 )
+ \ Generate a string representing rvalue inheritance var
+ getenv dup -1 = if
+ ( c-addr2 u2 c-addr1 u1 -- c-addr2 u2 -1 )
+ \ NOT set -- clean up the stack
+ drop ( c-addr2 u2 -1 -- c-addr2 u2 )
+ 2drop ( c-addr2 u2 -- )
+ else
+ ( c-addr2 u2 c-addr1 u1 -- c-addr2 u2 c-addr1 u1 )
+ \ SET -- execute cmdbuf (c-addr2/u2) to inherit value
+ 2drop ( c-addr2 u2 c-addr1 u1 -- c-addr2 u2 )
+ evaluate ( c-addr2 u2 -- )
+ then
+
+ s" cmdbuf" unsetenv
+;
+
+: menuset-unloadvar ( -- )
+
+ \ menuset_use_name is true or false
+ \ $type should be set to one of:
+ \ menu toggled ansi
+ \ $var should be set to one of:
+ \ caption command keycode text ...
+ \ $affix is either prefix (menuset_use_name is true)
+ \ or infix (menuset_use_name is false)
+
+ menuset_use_name @ true = if
+ s" set buf=${affix}${type}_${var}"
+ else
+ s" set buf=${type}set${affix}_${var}"
+ then
+ evaluate
+ s" buf" getenv unsetenv
+ s" buf" unsetenv
+;
+
+: menuset-loadmenuvar ( -- )
+ s" set type=menu" evaluate
+ menuset-loadvar
+;
+
+: menuset-unloadmenuvar ( -- )
+ s" set type=menu" evaluate
+ menuset-unloadvar
+;
+
+: menuset-loadxvar ( -- )
+
+ \ menuset_use_name is true or false
+ \ $type should be set to one of:
+ \ menu toggled ansi
+ \ $var should be set to one of:
+ \ caption command keycode text ...
+ \ $x is "1" through "8"
+ \ $affix is either prefix (menuset_use_name is true)
+ \ or infix (menuset_use_name is false)
+
+ s" set cmdbuf='set ${type}_${var}[${x}]=\$'" evaluate
+ s" cmdbuf" getenv swap drop ( -- u1 ) \ get string length
+ menuset_use_name @ true = if
+ s" set cmdbuf=${cmdbuf}${affix}${type}_${var}[${x}]"
+ ( u1 -- u1 c-addr2 u2 )
+ else
+ s" set cmdbuf=${cmdbuf}${type}set${affix}_${var}[${x}]"
+ ( u1 -- u1 c-addr2 u2 )
+ then
+ evaluate ( u1 c-addr2 u2 -- u1 )
+ s" cmdbuf" getenv ( u1 -- u1 c-addr2 u2 )
+ rot 2 pick 2 pick over + -rot + tuck -
+ ( u1 c-addr2 u2 -- c-addr2 u2 c-addr1 u1 )
+ \ Generate a string representing rvalue inheritance var
+ getenv dup -1 = if
+ ( c-addr2 u2 c-addr1 u1 -- c-addr2 u2 -1 )
+ \ NOT set -- clean up the stack
+ drop ( c-addr2 u2 -1 -- c-addr2 u2 )
+ 2drop ( c-addr2 u2 -- )
+ else
+ ( c-addr2 u2 c-addr1 u1 -- c-addr2 u2 c-addr1 u1 )
+ \ SET -- execute cmdbuf (c-addr2/u2) to inherit value
+ 2drop ( c-addr2 u2 c-addr1 u1 -- c-addr2 u2 )
+ evaluate ( c-addr2 u2 -- )
+ then
+
+ s" cmdbuf" unsetenv
+;
+
+: menuset-unloadxvar ( -- )
+
+ \ menuset_use_name is true or false
+ \ $type should be set to one of:
+ \ menu toggled ansi
+ \ $var should be set to one of:
+ \ caption command keycode text ...
+ \ $x is "1" through "8"
+ \ $affix is either prefix (menuset_use_name is true)
+ \ or infix (menuset_use_name is false)
+
+ menuset_use_name @ true = if
+ s" set buf=${affix}${type}_${var}[${x}]"
+ else
+ s" set buf=${type}set${affix}_${var}[${x}]"
+ then
+ evaluate
+ s" buf" getenv unsetenv
+ s" buf" unsetenv
+;
+
+: menuset-loadansixvar ( -- )
+ s" set type=ansi" evaluate
+ menuset-loadxvar
+;
+
+: menuset-unloadansixvar ( -- )
+ s" set type=ansi" evaluate
+ menuset-unloadxvar
+;
+
+: menuset-loadmenuxvar ( -- )
+ s" set type=menu" evaluate
+ menuset-loadxvar
+;
+
+: menuset-unloadmenuxvar ( -- )
+ s" set type=menu" evaluate
+ menuset-unloadxvar
+;
+
+: menuset-loadtoggledxvar ( -- )
+ s" set type=toggled" evaluate
+ menuset-loadxvar
+;
+
+: menuset-unloadtoggledxvar ( -- )
+ s" set type=toggled" evaluate
+ menuset-unloadxvar
+;
+
+: menuset-loadxyvar ( -- )
+
+ \ menuset_use_name is true or false
+ \ $type should be set to one of:
+ \ menu toggled ansi
+ \ $var should be set to one of:
+ \ caption command keycode text ...
+ \ $x is "1" through "8"
+ \ $y is "0" through "9"
+ \ $affix is either prefix (menuset_use_name is true)
+ \ or infix (menuset_use_name is false)
+
+ s" set cmdbuf='set ${type}_${var}[${x}][${y}]=\$'" evaluate
+ s" cmdbuf" getenv swap drop ( -- u1 ) \ get string length
+ menuset_use_name @ true = if
+ s" set cmdbuf=${cmdbuf}${affix}${type}_${var}[${x}][${y}]"
+ ( u1 -- u1 c-addr2 u2 )
+ else
+ s" set cmdbuf=${cmdbuf}${type}set${affix}_${var}[${x}][${y}]"
+ ( u1 -- u1 c-addr2 u2 )
+ then
+ evaluate ( u1 c-addr2 u2 -- u1 )
+ s" cmdbuf" getenv ( u1 -- u1 c-addr2 u2 )
+ rot 2 pick 2 pick over + -rot + tuck -
+ ( u1 c-addr2 u2 -- c-addr2 u2 c-addr1 u1 )
+ \ Generate a string representing rvalue inheritance var
+ getenv dup -1 = if
+ ( c-addr2 u2 c-addr1 u1 -- c-addr2 u2 -1 )
+ \ NOT set -- clean up the stack
+ drop ( c-addr2 u2 -1 -- c-addr2 u2 )
+ 2drop ( c-addr2 u2 -- )
+ else
+ ( c-addr2 u2 c-addr1 u1 -- c-addr2 u2 c-addr1 u1 )
+ \ SET -- execute cmdbuf (c-addr2/u2) to inherit value
+ 2drop ( c-addr2 u2 c-addr1 u1 -- c-addr2 u2 )
+ evaluate ( c-addr2 u2 -- )
+ then
+
+ s" cmdbuf" unsetenv
+;
+
+: menuset-unloadxyvar ( -- )
+
+ \ menuset_use_name is true or false
+ \ $type should be set to one of:
+ \ menu toggled ansi
+ \ $var should be set to one of:
+ \ caption command keycode text ...
+ \ $x is "1" through "8"
+ \ $y is "0" through "9"
+ \ $affix is either prefix (menuset_use_name is true)
+ \ or infix (menuset_use_name is false)
+
+ menuset_use_name @ true = if
+ s" set buf=${affix}${type}_${var}[${x}][${y}]"
+ else
+ s" set buf=${type}set${affix}_${var}[${x}][${y}]"
+ then
+ evaluate
+ s" buf" getenv unsetenv
+ s" buf" unsetenv
+;
+
+: menuset-loadansixyvar ( -- )
+ s" set type=ansi" evaluate
+ menuset-loadxyvar
+;
+
+: menuset-unloadansixyvar ( -- )
+ s" set type=ansi" evaluate
+ menuset-unloadxyvar
+;
+
+: menuset-loadmenuxyvar ( -- )
+ s" set type=menu" evaluate
+ menuset-loadxyvar
+;
+
+: menuset-unloadmenuxyvar ( -- )
+ s" set type=menu" evaluate
+ menuset-unloadxyvar
+;
+
+: menuset-setnum-namevar ( N -- C-Addr/U )
+
+ s" menuset_nameNNNNN" ( n -- n c-addr1 u1 ) \ variable basename
+ drop 12 ( n c-addr1 u1 -- n c-addr1 12 ) \ remove "NNNNN"
+ rot ( n c-addr1 12 -- c-addr1 12 n ) \ move number on top
+
+ \ convert to string
+ s>d <# #s #> ( c-addr1 12 n -- c-addr1 12 c-addr2 u2 )
+
+ \ Combine strings
+ begin ( using u2 in c-addr2/u2 pair as countdown to zero )
+ over ( c-addr1 u1 c-addr2 u2 -- continued below )
+ ( c-addr1 u1 c-addr2 u2 c-addr2 ) \ copy src-addr
+ c@ ( c-addr1 u1 c-addr2 u2 c-addr2 -- continued below )
+ ( c-addr1 u1 c-addr2 u2 c ) \ get next src-addr byte
+ 4 pick 4 pick
+ ( c-addr1 u1 c-addr2 u2 c -- continued below )
+ ( c-addr1 u1 c-addr2 u2 c c-addr1 u1 )
+ \ get destination c-addr1/u1 pair
+ + ( c-addr1 u1 c-addr2 u2 c c-addr1 u1 -- cont. below )
+ ( c-addr1 u1 c-addr2 u2 c c-addr3 )
+ \ combine dest-c-addr to get dest-addr for byte
+ c! ( c-addr1 u1 c-addr2 u2 c c-addr3 -- continued below )
+ ( c-addr1 u1 c-addr2 u2 )
+ \ store the current src-addr byte into dest-addr
+
+ 2swap 1+ 2swap \ increment u1 in destination c-addr1/u1 pair
+ swap 1+ swap \ increment c-addr2 in source c-addr2/u2 pair
+ 1- \ decrement u2 in the source c-addr2/u2 pair
+
+ dup 0= \ time to break?
+ until
+
+ 2drop ( c-addr1 u1 c-addr2 u2 -- c-addr1 u1 )
+ \ drop temporary number-format conversion c-addr2/u2
+;
+
+: menuset-checksetnum ( N -- )
+
+ \
+ \ adjust input to be both positive and no-higher than 65535
+ \
+ abs dup 65535 > if drop 65535 then ( n -- n )
+
+ \
+ \ The next few blocks will determine if we should use the default
+ \ methodology (referencing the original numeric stack-input), or if-
+ \ instead $menuset_name{N} has been defined wherein we would then
+ \ use the value thereof as the prefix to every menu variable.
+ \
+
+ false menuset_use_name ! \ assume name is not set
+
+ menuset-setnum-namevar
+ \
+ \ We now have a string that is the assembled variable name to check
+ \ for... $menuset_name{N}. Let's check for it.
+ \
+ 2dup ( c-addr1 u1 -- c-addr1 u1 c-addr1 u1 ) \ save a copy
+ getenv dup -1 <> if ( c-addr1 u1 c-addr1 u1 -- c-addr1 u1 c-addr2 u2 )
+ \ The variable is set. Let's clean up the stack leaving only
+ \ its value for later use.
+
+ true menuset_use_name !
+ 2swap 2drop ( c-addr1 u1 c-addr2 u2 -- c-addr2 u2 )
+ \ drop assembled variable name, leave the value
+ else ( c-addr1 u1 c-addr1 u1 -- c-addr1 u1 -1 ) \ no such variable
+ \ The variable is not set. Let's clean up the stack leaving the
+ \ string [portion] representing the original numeric input.
+
+ drop ( c-addr1 u1 -1 -- c-addr1 u1 ) \ drop -1 result
+ 12 - swap 12 + swap ( c-addr1 u1 -- c-addr2 u2 )
+ \ truncate to original numeric stack-input
+ then
+
+ \
+ \ Now, depending on whether $menuset_name{N} has been set, we have
+ \ either the value thereof to be used as a prefix to all menu_*
+ \ variables or we have a string representing the numeric stack-input
+ \ to be used as a "set{N}" infix to the same menu_* variables.
+ \
+ \ For example, if the stack-input is 1 and menuset_name1 is NOT set
+ \ the following variables will be referenced:
+ \ ansiset1_caption[x] -> ansi_caption[x]
+ \ ansiset1_caption[x][y] -> ansi_caption[x][y]
+ \ menuset1_acpi -> menu_acpi
+ \ menuset1_caption[x] -> menu_caption[x]
+ \ menuset1_caption[x][y] -> menu_caption[x][y]
+ \ menuset1_command[x] -> menu_command[x]
+ \ menuset1_init -> ``evaluated''
+ \ menuset1_init[x] -> menu_init[x]
+ \ menuset1_kernel -> menu_kernel
+ \ menuset1_keycode[x] -> menu_keycode[x]
+ \ menuset1_options -> menu_options
+ \ menuset1_optionstext -> menu_optionstext
+ \ menuset1_reboot -> menu_reboot
+ \ toggledset1_ansi[x] -> toggled_ansi[x]
+ \ toggledset1_text[x] -> toggled_text[x]
+ \ otherwise, the following variables are referenced (where {name}
+ \ represents the value of $menuset_name1 (given 1 as stack-input):
+ \ {name}ansi_caption[x] -> ansi_caption[x]
+ \ {name}ansi_caption[x][y] -> ansi_caption[x][y]
+ \ {name}menu_acpi -> menu_acpi
+ \ {name}menu_caption[x] -> menu_caption[x]
+ \ {name}menu_caption[x][y] -> menu_caption[x][y]
+ \ {name}menu_command[x] -> menu_command[x]
+ \ {name}menu_init -> ``evaluated''
+ \ {name}menu_init[x] -> menu_init[x]
+ \ {name}menu_kernel -> menu_kernel
+ \ {name}menu_keycode[x] -> menu_keycode[x]
+ \ {name}menu_options -> menu_options
+ \ {name}menu_optionstext -> menu_optionstext
+ \ {name}menu_reboot -> menu_reboot
+ \ {name}toggled_ansi[x] -> toggled_ansi[x]
+ \ {name}toggled_text[x] -> toggled_text[x]
+ \
+ \ Note that menuset{N}_init and {name}menu_init are the initializers
+ \ for the entire menu (for wholly dynamic menus) opposed to the per-
+ \ menuitem initializers (with [x] afterward). The whole-menu init
+ \ routine is evaluated and not passed down to $menu_init (which
+ \ would result in double evaluation). By doing this, the initializer
+ \ can initialize the menuset before we transfer it to active-duty.
+ \
+
+ \
+ \ Copy our affixation (prefix or infix depending on menuset_use_name)
+ \ to our buffer so that we can safely use the s-quote (s") buf again.
+ \
+ menuset_affixbuf 0 2swap ( c-addr2 u2 -- c-addr1 0 c-addr2 u2 )
+ begin ( using u2 in c-addr2/u2 pair as countdown to zero )
+ over ( c-addr1 u1 c-addr2 u2 -- c-addr1 u1 c-addr2 u2 c-addr2 )
+ c@ ( c-addr1 u1 c-addr2 u2 -- c-addr1 u1 c-addr2 u2 c )
+ 4 pick 4 pick
+ ( c-addr1 u1 c-addr2 u2 c -- continued below )
+ ( c-addr1 u1 c-addr2 u2 c c-addr1 u1 )
+ + ( c-addr1 u1 c-addr2 u2 c c-addr1 u1 -- continued below )
+ ( c-addr1 u1 c-addr2 u2 c c-addr3 )
+ c! ( c-addr1 u1 c-addr2 u2 c c-addr3 -- continued below )
+ ( c-addr1 u1 c-addr2 u2 )
+ 2swap 1+ 2swap \ increment affixbuf byte position/count
+ swap 1+ swap \ increment strbuf pointer (source c-addr2)
+ 1- \ decrement strbuf byte count (source u2)
+ dup 0= \ time to break?
+ until
+ 2drop ( c-addr1 u1 c-addr2 u2 -- c-addr1 u1 ) \ drop strbuf c-addr2/u2
+
+ \
+ \ Create a variable for referencing our affix data (prefix or infix
+ \ depending on menuset_use_name as described above). This variable will
+ \ be temporary and only used to simplify cmdbuf assembly.
+ \
+ s" affix" setenv ( c-addr1 u1 -- )
+;
+
+: menuset-cleanup ( -- )
+ s" type" unsetenv
+ s" var" unsetenv
+ s" x" unsetenv
+ s" y" unsetenv
+ s" affix" unsetenv
+;
+
+only forth definitions also menusets-infrastructure
+
+: menuset-loadsetnum ( N -- )
+
+ menuset-checksetnum ( n -- )
+
+ \
+ \ From here out, we use temporary environment variables to make
+ \ dealing with variable-length strings easier.
+ \
+ \ menuset_use_name is true or false
+ \ $affix should be used appropriately w/respect to menuset_use_name
+ \
+
+ \ ... menu_init ...
+ s" set var=init" evaluate
+ menuset-loadmenuvar
+
+ \ If menu_init was set by the above, evaluate it here-and-now
+ \ so that the remaining variables are influenced by its actions
+ s" menu_init" 2dup getenv dup -1 <> if
+ 2swap unsetenv \ don't want later menu-create to re-call this
+ evaluate
+ else
+ drop 2drop ( n c-addr u -1 -- n )
+ then
+
+ [char] 1 ( -- x ) \ Loop range ASCII '1' (49) to '8' (56)
+ begin
+ dup menuset_x tuck c! 1 s" x" setenv \ set loop iterator and $x
+
+ s" set var=caption" evaluate
+
+ \ ... menu_caption[x] ...
+ menuset-loadmenuxvar
+
+ \ ... ansi_caption[x] ...
+ menuset-loadansixvar
+
+ [char] 0 ( x -- x y ) \ Inner Loop ASCII '1' (48) to '9' (57)
+ begin
+ dup menuset_y tuck c! 1 s" y" setenv
+ \ set inner loop iterator and $y
+
+ \ ... menu_caption[x][y] ...
+ menuset-loadmenuxyvar
+
+ \ ... ansi_caption[x][y] ...
+ menuset-loadansixyvar
+
+ 1+ dup 57 > ( x y -- y' 0|-1 ) \ increment and test
+ until
+ drop ( x y -- x )
+
+ \ ... menu_command[x] ...
+ s" set var=command" evaluate
+ menuset-loadmenuxvar
+
+ \ ... menu_init[x] ...
+ s" set var=init" evaluate
+ menuset-loadmenuxvar
+
+ \ ... menu_keycode[x] ...
+ s" set var=keycode" evaluate
+ menuset-loadmenuxvar
+
+ \ ... toggled_text[x] ...
+ s" set var=text" evaluate
+ menuset-loadtoggledxvar
+
+ \ ... toggled_ansi[x] ...
+ s" set var=ansi" evaluate
+ menuset-loadtoggledxvar
+
+ 1+ dup 56 > ( x -- x' 0|-1 ) \ increment iterator
+ \ continue if less than 57
+ until
+ drop ( x -- ) \ loop iterator
+
+ \ ... menu_reboot ...
+ s" set var=reboot" evaluate
+ menuset-loadmenuvar
+
+ \ ... menu_acpi ...
+ s" set var=acpi" evaluate
+ menuset-loadmenuvar
+
+ \ ... menu_kernel ...
+ s" set var=kernel" evaluate
+ menuset-loadmenuvar
+
+ \ ... menu_options ...
+ s" set var=options" evaluate
+ menuset-loadmenuvar
+
+ \ ... menu_optionstext ...
+ s" set var=optionstext" evaluate
+ menuset-loadmenuvar
+
+ menuset-cleanup
+;
+
+: menusets-unset ( -- )
+
+ s" menuset_initial" unsetenv
+
+ 1 begin
+ dup menuset-checksetnum ( n n -- n )
+
+ dup menuset-setnum-namevar ( n n -- n )
+ unsetenv
+
+ \ If the current menuset does not populate the first menuitem,
+ \ we stop completely.
+
+ menuset_use_name @ true = if
+ s" set buf=${affix}menu_caption[1]"
+ else
+ s" set buf=menuset${affix}_caption[1]"
+ then
+ evaluate s" buf" getenv getenv -1 = if
+ drop ( n -- )
+ s" buf" unsetenv
+ menuset-cleanup
+ exit
+ else
+ drop ( n c-addr2 -- n ) \ unused
+ then
+
+ [char] 1 ( n -- n x ) \ Loop range ASCII '1' (49) to '8' (56)
+ begin
+ dup menuset_x tuck c! 1 s" x" setenv \ set $x to x
+
+ s" set var=caption" evaluate
+ menuset-unloadmenuxvar
+ menuset-unloadmenuxvar
+ menuset-unloadansixvar
+ [char] 0 ( n x -- n x y ) \ Inner loop '0' to '9'
+ begin
+ dup menuset_y tuck c! 1 s" y" setenv
+ \ sets $y to y
+ menuset-unloadmenuxyvar
+ menuset-unloadansixyvar
+ 1+ dup 57 > ( n x y -- n x y' 0|-1 )
+ until
+ drop ( n x y -- n x )
+ s" set var=command" evaluate menuset-unloadmenuxvar
+ s" set var=init" evaluate menuset-unloadmenuxvar
+ s" set var=keycode" evaluate menuset-unloadmenuxvar
+ s" set var=text" evaluate menuset-unloadtoggledxvar
+ s" set var=ansi" evaluate menuset-unloadtoggledxvar
+
+ 1+ dup 56 > ( x -- x' 0|-1 ) \ increment and test
+ until
+ drop ( n x -- n ) \ loop iterator
+
+ s" set var=acpi" evaluate menuset-unloadmenuvar
+ s" set var=init" evaluate menuset-unloadmenuvar
+ s" set var=kernel" evaluate menuset-unloadmenuvar
+ s" set var=options" evaluate menuset-unloadmenuvar
+ s" set var=optionstext" evaluate menuset-unloadmenuvar
+ s" set var=reboot" evaluate menuset-unloadmenuvar
+
+ 1+ dup 65535 > ( n -- n' 0|-1 ) \ increment and test
+ until
+ drop ( n' -- ) \ loop iterator
+
+ s" buf" unsetenv
+ menuset-cleanup
+;
+
+only forth definitions
+
+: menuset-loadinitial ( -- )
+ s" menuset_initial" getenv dup -1 <> if
+ ?number 0<> if
+ menuset-loadsetnum
+ then
+ else
+ drop \ cruft
+ then
+;
diff --git a/stand/forth/menusets.4th.8 b/stand/forth/menusets.4th.8
new file mode 100644
index 0000000..f785ae1
--- /dev/null
+++ b/stand/forth/menusets.4th.8
@@ -0,0 +1,372 @@
+.\" Copyright (c) 2012 Devin Teske
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 5, 2012
+.Dt MENUSETS.4TH 8
+.Os
+.Sh NAME
+.Nm menusets.4th
+.Nd FreeBSD dynamic submenu boot module
+.Sh DESCRIPTION
+The file that goes by the name of
+.Nm
+is a set of commands designed to add submenu functionality to the dynamic menu
+system provided by
+.Xr menu.4th 8 .
+Submenus are managed through a system of carefully named environment variables.
+The commands of
+.Nm
+by themselves are not enough for most uses.
+Please refer to the examples below for the most common situations, and to
+.Xr menu.4th 8
+for additional commands.
+.Pp
+Before using any of the commands provided in
+.Nm ,
+it must be included
+through the command:
+.Pp
+.Dl include menusets.4th
+.Pp
+This line is present in the default
+.Pa /boot/menu-commands.4th
+file, so it is not needed (and should not be re-issued) in a normal setup.
+.Pp
+The commands provided by it are:
+.Pp
+.Bl -tag -width menuset-loadinitial -compact -offset indent
+.It Ic menuset-loadsetnum
+Takes a single integer on the stack to identify the menuset environment
+variables to be activated (see environment variables below).
+.It Ic menuset-loadinitial
+If $menuset_initial is set, passes the value to menuset-loadsetnum.
+The value must be a number.
+.It Ic menusets-unset
+Unsets the environment variables associated with all menusets.
+Increments starting at 1 and stops at the first unconfigured menuset.
+A menuset is considered configured if the caption for item 1 is set.
+.El
+.Pp
+The environment variables that effect its behavior are:
+.Bl -tag -width bootfile -offset indent
+.It Va menuset_initial
+Number to pass to menuset-loadsetnum when menuset-loadinitial is called.
+.It Va menuset_nameN
+Used to give a name to a menuset.
+.El
+.Pp
+When a menuset is NOT given a name (the default),
+menuset N is comprised of the following environment variables:
+.Pp
+.Bl -tag -width menusetN_caption[x][y] -compact -offset indent
+.It Va ansisetN_caption[x]
+-> ansi_caption[x]
+.It Va ansisetN_caption[x][y]
+-> ansi_caption[x][y]
+.It Va menusetN_acpi
+-> menu_acpi
+.It Va menusetN_caption[x]
+-> menu_caption[x]
+.It Va menusetN_caption[x][y]
+-> menu_caption[x][y]
+.It Va menusetN_command[x]
+-> menu_command[x]
+.It Va menusetN_init
+->
+.Dq Li evaluated
+.It Va menusetN_init[x]
+-> menu_init[x]
+.It Va menusetN_keycode[x]
+-> menu_keycode[x]
+.It Va menusetN_options
+-> menu_options
+.It Va menusetN_optionstext
+-> menu_optionstext
+.It Va menusetN_reboot
+-> menu_reboot
+.It Va toggledsetN_ansi[x]
+-> toggled_ansi[x]
+.It Va toggledsetN_text[x]
+-> toggled_text[x]
+.El
+.Pp
+When you choose to give a menuset a name (by setting $menuset_nameN),
+menuset N is instead comprised of the following environment variables:
+.Pp
+.Bl -tag -width NAMEmenu_caption[x][y] -compact -offset indent
+.It Va NAMEansi_caption[x]
+-> ansi_caption[x]
+.It Va NAMEansi_caption[x][y]
+-> ansi_caption[x][y]
+.It Va NAMEmenu_acpi
+-> menu_acpi
+.It Va NAMEmenu_caption[x]
+-> menu_caption[x]
+.It Va NAMEmenu_caption[x][y]
+-> menu_caption[x][y]
+.It Va NAMEmenu_command[x]
+-> menu_command[x]
+.It Va NAMEmenu_init
+->
+.Dq Li evaluated
+.It Va NAMEmenu_init[x]
+-> menu_init[x]
+.It Va NAMEmenu_keycode[x]
+-> menu_keycode[x]
+.It Va NAMEmenu_options
+-> menu_options
+.It Va NAMEmenu_optionstext
+-> menu_optionstext
+.It Va NAMEmenu_reboot
+-> menu_reboot
+.It Va NAMEtoggled_ansi[x]
+-> toggled_ansi[x]
+.It Va NAMEtoggled_text[x]
+-> toggled_text[x]
+.El
+.Pp
+where
+.Dq Li NAME
+is the value of $menuset_nameN.
+In the case of $NAMEmenu_init ($menusetN_init when $menuset_nameN is unset),
+the value is evaluated as an FICL statement.
+This can be used to dynamically adjust the menuset variables right before the
+menu is activated.
+.Pp
+In addition,
+.Nm
+provides the following FICL words:
+.Pp
+.Bl -tag -width menuset -compact -offset indent
+.It Ic menuset-checksetnum ( N -- )
+Given a single integer on the stack, sets a global variable
+.Va menuset_use_name
+to a boolean based on whether $menuset_nameN is set (true) or not (false).
+Also sets $affix temporary variable (prefix or infix depending on
+menuset_use_name).
+Automatically called by menuset-loadsetnum and menusets-unset.
+.It Ic menuset-loadvar ( -- )
+Used indirectly to shorten syntax and mitigate dictionary size.
+Requires the following temporary environment variables:
+.Pp
+.Bl -tag -width affix -compact -offset indent
+.It Va type
+should be set to one of: menu toggled ansi
+.It Va var
+should be set to one of: caption command keycode text ...
+.It Va affix
+either a prefix (menuset_use_name is true) or infix (menuset_use_name is false)
+.El
+.Pp
+If the global
+.Va menuset_use_name
+is true, the variable ${type}_${var} is made to
+equal the value of the variable ${affix}${type}_${var}
+(note: in this case menuset-checksetnum has set $affix to $menuset_nameN).
+Otherwise (when
+.Va menuset_use_name
+is false), the variable ${type}_${var} is made to
+equal the value of the variable ${type}set${affix}_${var}
+(note: in this case menuset-checksetnum has set $affix to N).
+.Pp
+Both the global variable
+.Va menuset_use_name
+and the environment variable $affix are automatically handled by
+menuset-checksetnum above (which is automatically called by
+menuset-loadsetnum).
+.It Ic menuset-unloadvar ( -- )
+Used indirectly to shorten syntax and mitigate dictionary size.
+Like menuset-loadvar except it unsets the menuset variable.
+If global
+.Va menuset_use_name
+is true ($affix is $menuset_nameN),
+variable ${affix}${type}_${var} is unset.
+Otherwise, $affix is N and variable ${type}set${affix}_${var} is unset.
+.It Ic menuset-loadmenuvar ( -- )
+Sets $type to
+.Dq menu
+and calls menuset-loadvar.
+.It Ic menuset-unloadmenuvar ( -- )
+Sets $type to
+.Dq menu
+and calls menuset-unloadvar.
+.It Ic menuset-loadxvar ( -- )
+Like menuset-loadvar except it takes an additional temporary variable $x.
+If the global
+.Va menuset_use_name
+is true (making $affix equal $menuset_nameN),
+sets variable ${type}_${var}[${x}] to variable ${affix}${type}_${var}[${x}].
+Otherwise ($affix being N), sets the same variable to instead
+${type}set{affix}_${var}[${x}].
+.It Ic menuset-unloadxvar ( -- )
+Like menuset-loadxvar except it unsets the menuset variable.
+If global
+.Va menuset_use_name
+is true, unsets ${affix}${type}_${var}[${x}].
+Otherwise, unsets ${type}set${affix}_${var}[${x}].
+.It Ic menuset-loadansixvar ( -- )
+Sets $type to
+.Dq ansi
+and calls menuset-loadxvar
+.It Ic menuset-unloadansixvar ( -- )
+Sets $type to
+.Dq ansi
+and calls menuset-unloadxvar
+.It Ic menuset-loadmenuxvar ( -- )
+Sets $type to
+.Dq ansi
+and calls menuset-loadxvar
+.It Ic menuset-unloadmenuxvar ( -- )
+Sets $type to
+.Dq ansi
+and calls menuset-unloadxvar
+.It Ic menuset-loadtoggledxvar ( -- )
+Sets $type to
+.Dq toggled
+and calls menuset-loadxvar
+.It Ic menuset-unloadtoggledxvar ( -- )
+Sets $type to
+.Dq toggled
+and calls menuset-unloadxvar
+.It Ic menuset-loadxyvar ( -- )
+Like menuset-loadxvar except it takes an additional temporary variable $y.
+If the global
+.Va menuset_use_name
+is true ($affix is $menuset_nameN),
+sets variable ${type}_${var}[${x}][${y}] to ${affix}${type}_${var}[${x}][${y}].
+Otherwise ($affix is N) sets the same variable to instead
+${type}set${affix}_${var}[${x}][${y}].
+.It Ic menuset-unloadxyvar ( -- )
+Like menuset-loadxyvar except it unsets the menuset variable.
+If the global
+.Va menuset_use_name
+is true, unsets ${affix}${type}_${var}[${x}][${y}].
+Otherwise, unsets ${type}set${affix}_${var}[${x}][${y}].
+.It Ic menuset-loadansixyvar ( -- )
+Sets $type to
+.Dq ansi
+and calls menuset-loadxyvar.
+.It Ic menuset-unloadansixyvar ( -- )
+Sets $type to
+.Dq ansi
+and calls menuset-unloadxyvar.
+.It Ic menuset-loadmenuxyvar ( -- )
+Sets $type to
+.Dq menu
+and calls menuset-loadxyvar.
+.It Ic menuset-unloadmenuxyvar ( -- )
+Sets $type to
+.Dq menu
+and calls menuset-unloadxyvar.
+.It Ic menuset-setnum-namevar ( N -- C-Addr/U )
+Takes a single integer on the stack and replaces it with a string (in c-addr/u
+format) whose value is
+.Dq menuset_nameN .
+For example, if given 1 returns
+.Dq menuset_name1 .
+.It Ic menuset-cleanup ( N -- )
+Unsets all the various temporary variables, currently
+.Va type ,
+.Va var ,
+.Va x ,
+.Va y ,
+and
+.Va affix .
+.El
+.Pp
+For all values of
+.Dq Li x
+above, use any number between 1 through 9. Sorry, double-digits are not
+currently supported.
+For all values of
+.Dq Li N
+above, use any number between 1 and 65535.
+.Sh FILES
+.Bl -tag -width /boot/menu-commands.4th -compact
+.It Pa /boot/loader
+The
+.Xr loader 8 .
+.It Pa /boot/menu.4th
+Dynamic menu module.
+.It Pa /boot/menu-commands.4th
+Contains the goto_menu command.
+.It Pa /boot/menusets.4th
+.Nm
+itself.
+.It Pa /boot/loader.rc
+.Xr loader 8
+bootstrapping script.
+.El
+.Sh EXAMPLES
+A simple boot menu with a submenu:
+.Pp
+.Bd -literal -offset indent -compact
+include /boot/menu.4th
+include /boot/menu-commands.4th
+menu-init
+set menuset1_caption[1]="Boot"
+set menuset1_command[1]="boot"
+set menuset1_caption[2]="Submenu..."
+set menuset1_command[2]="2 goto_menu"
+set menuset2_caption[1]="Back"
+set menuset2_command[1]="1 goto_menu"
+set menuset_initial=2
+menuset-loadinitial
+menu-display
+.Ed
+.Pp
+The same boot menu with named menusets:
+.Pp
+.Bd -literal -offset indent -compact
+include /boot/menu.4th
+include /boot/menu-commands.4th
+menu-init
+set menuset_name1=main
+set mainmenu_caption[1]="Boot"
+set mainmenu_command[1]="boot"
+set mainmenu_caption[2]="Submenu..."
+set mainmenu_command[2]="2 goto_menu"
+set menuset_name2=sub
+set submenu_caption[1]="Back"
+set submenu_command[1]="1 goto_menu"
+.Ed
+.Sh SEE ALSO
+.Xr loader.conf 5 ,
+.Xr beastie.4th 8 ,
+.Xr loader 8 ,
+.Xr loader.4th 8 ,
+.Xr menu.4th 8
+.Sh HISTORY
+The
+.Nm
+set of commands first appeared in
+.Fx 10.0 .
+.Sh AUTHORS
+The
+.Nm
+set of commands was written by
+.An -nosplit
+.An Devin Teske Aq dteske@FreeBSD.org .
diff --git a/stand/forth/pcibios.4th b/stand/forth/pcibios.4th
new file mode 100644
index 0000000..71702da
--- /dev/null
+++ b/stand/forth/pcibios.4th
@@ -0,0 +1,47 @@
+\ Copyright (c) 2014 M. Warner Losh <imp@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+only forth also support-functions also builtins definitions
+
+\ pci-device-count pci-id
+\
+\ Counts the number of instances of pci-id in the system and reports
+\ it to the user.
+: pci-device-count
+ 0= if ( interpreted ) get_arguments then
+
+ 0= if ." Need an argument" cr abort then
+ \ First argument is 0 when we're interprated. See support.4th
+ \ for get_arguments reading the rest of the line and parsing it
+ \ stack: argN lenN ... arg1 len1 N
+ hex ?number decimal
+ 0= if ." Bad pci-id given (must be legal hex value)" cr abort then
+ dup pcibios-device-count ." Found " . ." instances of " hex . decimal cr
+;
+
+also forth definitions also builtins
+
+builtin: pci-device-count
diff --git a/stand/forth/pnp.4th b/stand/forth/pnp.4th
new file mode 100644
index 0000000..5ce2355
--- /dev/null
+++ b/stand/forth/pnp.4th
@@ -0,0 +1,205 @@
+\ Copyright (c) 2000 Daniel C. Sobral <dcs@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+
+\ The following pnp code is used in pnp.4th and pnp.c
+structure: STAILQ_HEAD
+ ptr stqh_first \ type*
+ ptr stqh_last \ type**
+;structure
+
+structure: STAILQ_ENTRY
+ ptr stqe_next \ type*
+;structure
+
+structure: pnphandler
+ ptr pnph.name
+ ptr pnph.enumerate
+;structure
+
+structure: pnpident
+ ptr pnpid.ident \ char*
+ sizeof STAILQ_ENTRY cells member: pnpid.link \ pnpident
+;structure
+
+structure: pnpinfo \ sync with stand/config/bootstrap.h
+ ptr pnpi.desc
+ int pnpi.revision
+ ptr pnpi.module \ (char*) module args
+ int pnpi.argc
+ ptr pnpi.argv
+ ptr pnpi.handler \ pnphandler
+ sizeof STAILQ_HEAD member: pnpi.ident \ pnpident
+ sizeof STAILQ_ENTRY member: pnpi.link \ pnpinfo
+;structure
+\ end of pnp support
+
+pnpdevices drop
+
+: enumerate
+ pnphandlers begin
+ dup @
+ while
+ ." Probing " dup @ pnph.name @ dup strlen type ." ..." cr
+ 0 over @ pnph.enumerate @ ccall drop
+ cell+
+ repeat
+;
+
+: summary
+ ." PNP scan summary:" cr
+ pnpdevices stqh_first @
+ begin
+ dup
+ while
+ dup pnpi.ident stqh_first @ pnpid.ident @ dup strlen type
+ dup pnpi.desc @ ?dup if
+ ." : "
+ dup strlen type
+ then
+ cr
+ pnpi.link stqe_next @
+ repeat
+ drop
+;
+
+: compare-pnpid ( addr addr' -- flag )
+ begin
+ over c@ over c@ <> if drop drop false exit then
+ over c@ over c@ and
+ while
+ char+ swap char+ swap
+ repeat
+ c@ swap c@ or 0=
+;
+
+: search-pnpid ( id -- flag )
+ >r
+ pnpdevices stqh_first @
+ begin ( pnpinfo )
+ dup
+ while
+ dup pnpi.ident stqh_first @
+ begin ( pnpinfo pnpident )
+ dup pnpid.ident @ r@ compare-pnpid
+ if
+ r> drop
+ \ XXX Temporary debugging message
+ ." Found " pnpid.ident @ dup strlen type
+ pnpi.desc @ ?dup if
+ ." : " dup strlen type
+ then cr
+ \ drop drop
+ true
+ exit
+ then
+ pnpid.link stqe_next @
+ ?dup 0=
+ until
+ pnpi.link stqe_next @
+ repeat
+ r> drop
+ drop
+ false
+;
+
+: skip-space ( addr -- addr' )
+ begin
+ dup c@ bl =
+ over c@ 9 = or
+ while
+ char+
+ repeat
+;
+
+: skip-to-space ( addr -- addr' )
+ begin
+ dup c@ bl <>
+ over c@ 9 <> and
+ over c@ and
+ while
+ char+
+ repeat
+;
+
+: premature-end? ( addr -- addr flag )
+ postpone dup postpone c@ postpone 0=
+ postpone if postpone exit postpone then
+; immediate
+
+0 value filename
+0 value timestamp
+0 value id
+
+only forth also support-functions
+
+: (load) load ;
+
+: check-pnpid ( -- )
+ line_buffer .addr @
+ \ Search for filename
+ skip-space premature-end?
+ dup to filename
+ \ Search for end of filename
+ skip-to-space premature-end?
+ 0 over c! char+
+ \ Search for timestamp
+ skip-space premature-end?
+ dup to timestamp
+ skip-to-space premature-end?
+ 0 over c! char+
+ \ Search for ids
+ begin
+ skip-space premature-end?
+ dup to id
+ skip-to-space dup c@ >r
+ 0 over c! char+
+ id search-pnpid if
+ filename dup strlen 1 ['] (load) catch if
+ drop drop drop
+ ." Error loading " filename dup strlen type cr
+ then
+ r> drop exit
+ then
+ r> 0=
+ until
+;
+
+: load-pnp
+ 0 to end_of_file?
+ reset_line_reading
+ s" /boot/pnpid.conf" O_RDONLY fopen fd !
+ fd @ -1 <> if
+ begin
+ end_of_file? 0=
+ while
+ read_line
+ check-pnpid
+ repeat
+ fd @ fclose
+ then
+;
+
diff --git a/stand/forth/screen.4th b/stand/forth/screen.4th
new file mode 100644
index 0000000..e0af82b
--- /dev/null
+++ b/stand/forth/screen.4th
@@ -0,0 +1,74 @@
+\ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org>
+\ Copyright (c) 2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+marker task-screen.4th
+
+\ emit Esc-[
+: escc ( -- ) 27 emit [char] [ emit ;
+
+\ Home cursor ( Esc-[H )
+: ho ( -- ) escc [char] H emit ;
+
+\ Clear from current position to end of display ( Esc-[J )
+: cld ( -- ) escc [char] J emit ;
+
+\ clear screen
+: clear ( -- ) ho cld ;
+
+\ move cursor to x rows, y cols (1-based coords) ( Esc-[%d;%dH )
+: at-xy ( x y -- ) escc .# [char] ; emit .# [char] H emit ;
+
+\ Set foreground color ( Esc-[3%dm )
+: fg ( x -- ) escc 3 .# .# [char] m emit ;
+
+\ Set background color ( Esc-[4%dm )
+: bg ( x -- ) escc 4 .# .# [char] m emit ;
+
+\ Mode end (clear attributes)
+: me ( -- ) escc [char] m emit ;
+
+\ Enable bold mode ( Esc-[1m )
+: b ( -- ) escc 1 .# [char] m emit ;
+
+\ Disable bold mode ( Esc-[22m )
+: -b ( -- ) escc 22 .# [char] m emit ;
+
+\ Enable inverse foreground/background mode ( Esc-[7m )
+: inv ( -- ) escc 7 .# [char] m emit ;
+
+\ Disable inverse foreground/background mode ( Esc-[27m )
+: -inv ( -- ) escc 27 .# [char] m emit ;
+
+\ Convert all occurrences of given character (c) in string (c-addr/u) to Esc
+: escc! ( c-addr/u c -- c-addr/u )
+ 2 pick 2 pick
+ begin dup 0> while
+ over c@ 3 pick = if over 27 swap c! then
+ 1- swap 1+ swap
+ repeat
+ 2drop drop
+;
diff --git a/stand/forth/shortcuts.4th b/stand/forth/shortcuts.4th
new file mode 100644
index 0000000..33a1cf6
--- /dev/null
+++ b/stand/forth/shortcuts.4th
@@ -0,0 +1,50 @@
+\ Copyright (c) 2008-2011 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+\ FICL words intended to be used as shortcuts for carrying out common tasks or
+\ producing common results. Generally, words defined here are simply groupings
+\ of other custom words that pull from multiple libraries (for example, if you
+\ want to define a custom word that uses words defined in three different
+\ libraries, this is a good place to define such a word).
+\
+\ This script should be included after you have included any/all other
+\ libraries. This will prevent calling a word defined here before any required
+\ words have been defined.
+
+marker task-shortcuts.4th
+
+\ This "shortcut" word will not be used directly, but is defined here to
+\ offer the user a quick way to get back into the interactive PXE menu
+\ after they have escaped to the shell (perhaps by accident).
+\
+: menu ( -- )
+ clear \ Clear the screen (in screen.4th)
+ print_version \ print version string (bottom-right; see version.4th)
+ draw-beastie \ Draw FreeBSD logo at right (in beastie.4th)
+ draw-brand \ Draw FIS logo at top (in brand.4th)
+ menu-init \ Initialize menu and draw bounding box (in menu.4th)
+ menu-display \ Launch interactive menu (in menu.4th)
+;
diff --git a/stand/forth/support.4th b/stand/forth/support.4th
new file mode 100644
index 0000000..aa50b3b
--- /dev/null
+++ b/stand/forth/support.4th
@@ -0,0 +1,1606 @@
+\ Copyright (c) 1999 Daniel C. Sobral <dcs@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+\ Loader.rc support functions:
+\
+\ initialize ( addr len -- ) as above, plus load_conf_files
+\ load_conf ( addr len -- ) load conf file given
+\ include_conf_files ( -- ) load all conf files in load_conf_files
+\ print_syntax_error ( -- ) print line and marker of where a syntax
+\ error was detected
+\ print_line ( -- ) print last line processed
+\ load_kernel ( -- ) load kernel
+\ load_modules ( -- ) load modules flagged
+\
+\ Exported structures:
+\
+\ string counted string structure
+\ cell .addr string address
+\ cell .len string length
+\ module module loading information structure
+\ cell module.flag should we load it?
+\ string module.name module's name
+\ string module.loadname name to be used in loading the module
+\ string module.type module's type
+\ string module.args flags to be passed during load
+\ string module.beforeload command to be executed before load
+\ string module.afterload command to be executed after load
+\ string module.loaderror command to be executed if load fails
+\ cell module.next list chain
+\
+\ Exported global variables;
+\
+\ string conf_files configuration files to be loaded
+\ cell modules_options pointer to first module information
+\ value verbose? indicates if user wants a verbose loading
+\ value any_conf_read? indicates if a conf file was successfully read
+\
+\ Other exported words:
+\ note, strlen is internal
+\ strdup ( addr len -- addr' len) similar to strdup(3)
+\ strcat ( addr len addr' len' -- addr len+len' ) similar to strcat(3)
+\ s' ( | string' -- addr len | ) similar to s"
+\ rudimentary structure support
+
+\ Exception values
+
+1 constant ESYNTAX
+2 constant ENOMEM
+3 constant EFREE
+4 constant ESETERROR \ error setting environment variable
+5 constant EREAD \ error reading
+6 constant EOPEN
+7 constant EEXEC \ XXX never catched
+8 constant EBEFORELOAD
+9 constant EAFTERLOAD
+
+\ I/O constants
+
+0 constant SEEK_SET
+1 constant SEEK_CUR
+2 constant SEEK_END
+
+0 constant O_RDONLY
+1 constant O_WRONLY
+2 constant O_RDWR
+
+\ Crude structure support
+
+: structure:
+ create here 0 , ['] drop , 0
+ does> create here swap dup @ allot cell+ @ execute
+;
+: member: create dup , over , + does> cell+ @ + ;
+: ;structure swap ! ;
+: constructor! >body cell+ ! ;
+: constructor: over :noname ;
+: ;constructor postpone ; swap cell+ ! ; immediate
+: sizeof ' >body @ state @ if postpone literal then ; immediate
+: offsetof ' >body cell+ @ state @ if postpone literal then ; immediate
+: ptr 1 cells member: ;
+: int 1 cells member: ;
+
+\ String structure
+
+structure: string
+ ptr .addr
+ int .len
+ constructor:
+ 0 over .addr !
+ 0 swap .len !
+ ;constructor
+;structure
+
+
+\ Module options linked list
+
+structure: module
+ int module.flag
+ sizeof string member: module.name
+ sizeof string member: module.loadname
+ sizeof string member: module.type
+ sizeof string member: module.args
+ sizeof string member: module.beforeload
+ sizeof string member: module.afterload
+ sizeof string member: module.loaderror
+ ptr module.next
+;structure
+
+\ Internal loader structures (preloaded_file, kernel_module, file_metadata)
+\ must be in sync with the C struct in stand/common/bootstrap.h
+structure: preloaded_file
+ ptr pf.name
+ ptr pf.type
+ ptr pf.args
+ ptr pf.metadata \ file_metadata
+ int pf.loader
+ int pf.addr
+ int pf.size
+ ptr pf.modules \ kernel_module
+ ptr pf.next \ preloaded_file
+;structure
+
+structure: kernel_module
+ ptr km.name
+ \ ptr km.args
+ ptr km.fp \ preloaded_file
+ ptr km.next \ kernel_module
+;structure
+
+structure: file_metadata
+ int md.size
+ 2 member: md.type \ this is not ANS Forth compatible (XXX)
+ ptr md.next \ file_metadata
+ 0 member: md.data \ variable size
+;structure
+
+\ end of structures
+
+\ Global variables
+
+string conf_files
+string nextboot_conf_file
+create module_options sizeof module.next allot 0 module_options !
+create last_module_option sizeof module.next allot 0 last_module_option !
+0 value verbose?
+0 value nextboot?
+
+\ Support string functions
+: strdup { addr len -- addr' len' }
+ len allocate if ENOMEM throw then
+ addr over len move len
+;
+
+: strcat { addr len addr' len' -- addr len+len' }
+ addr' addr len + len' move
+ addr len len' +
+;
+
+: strchr { addr len c -- addr' len' }
+ begin
+ len
+ while
+ addr c@ c = if addr len exit then
+ addr 1 + to addr
+ len 1 - to len
+ repeat
+ 0 0
+;
+
+: s' \ same as s", allows " in the string
+ [char] ' parse
+ state @ if postpone sliteral then
+; immediate
+
+: 2>r postpone >r postpone >r ; immediate
+: 2r> postpone r> postpone r> ; immediate
+: 2r@ postpone 2r> postpone 2dup postpone 2>r ; immediate
+
+: getenv? getenv -1 = if false else drop true then ;
+
+\ determine if a word appears in a string, case-insensitive
+: contains? ( addr1 len1 addr2 len2 -- 0 | -1 )
+ 2 pick 0= if 2drop 2drop true exit then
+ dup 0= if 2drop 2drop false exit then
+ begin
+ begin
+ swap dup c@ dup 32 = over 9 = or over 10 = or
+ over 13 = or over 44 = or swap drop
+ while 1+ swap 1- repeat
+ swap 2 pick 1- over <
+ while
+ 2over 2over drop over compare-insensitive 0= if
+ 2 pick over = if 2drop 2drop true exit then
+ 2 pick tuck - -rot + swap over c@ dup 32 =
+ over 9 = or over 10 = or over 13 = or over 44 = or
+ swap drop if 2drop 2drop true exit then
+ then begin
+ swap dup c@ dup 32 = over 9 = or over 10 = or
+ over 13 = or over 44 = or swap drop
+ if false else true then 2 pick 0> and
+ while 1+ swap 1- repeat
+ swap
+ repeat
+ 2drop 2drop false
+;
+
+: boot_serial? ( -- 0 | -1 )
+ s" console" getenv dup -1 <> if
+ s" comconsole" 2swap contains?
+ else drop false then
+ s" boot_serial" getenv dup -1 <> if
+ swap drop 0>
+ else drop false then
+ or \ console contains comconsole ( or ) boot_serial
+ s" boot_multicons" getenv dup -1 <> if
+ swap drop 0>
+ else drop false then
+ or \ previous boolean ( or ) boot_multicons
+;
+
+\ Private definitions
+
+vocabulary support-functions
+only forth also support-functions definitions
+
+\ Some control characters constants
+
+7 constant bell
+8 constant backspace
+9 constant tab
+10 constant lf
+13 constant <cr>
+
+\ Read buffer size
+
+80 constant read_buffer_size
+
+\ Standard suffixes
+
+: load_module_suffix s" _load" ;
+: module_loadname_suffix s" _name" ;
+: module_type_suffix s" _type" ;
+: module_args_suffix s" _flags" ;
+: module_beforeload_suffix s" _before" ;
+: module_afterload_suffix s" _after" ;
+: module_loaderror_suffix s" _error" ;
+
+\ Support operators
+
+: >= < 0= ;
+: <= > 0= ;
+
+\ Assorted support functions
+
+: free-memory free if EFREE throw then ;
+
+: strget { var -- addr len } var .addr @ var .len @ ;
+
+\ assign addr len to variable.
+: strset { addr len var -- } addr var .addr ! len var .len ! ;
+
+\ free memory and reset fields
+: strfree { var -- } var .addr @ ?dup if free-memory 0 0 var strset then ;
+
+\ free old content, make a copy of the string and assign to variable
+: string= { addr len var -- } var strfree addr len strdup var strset ;
+
+: strtype ( str -- ) strget type ;
+
+\ assign a reference to what is on the stack
+: strref { addr len var -- addr len }
+ addr var .addr ! len var .len ! addr len
+;
+
+\ unquote a string
+: unquote ( addr len -- addr len )
+ over c@ [char] " = if 2 chars - swap char+ swap then
+;
+
+\ Assignment data temporary storage
+
+string name_buffer
+string value_buffer
+
+\ Line by line file reading functions
+\
+\ exported:
+\ line_buffer
+\ end_of_file?
+\ fd
+\ read_line
+\ reset_line_reading
+
+vocabulary line-reading
+also line-reading definitions
+
+\ File data temporary storage
+
+string read_buffer
+0 value read_buffer_ptr
+
+\ File's line reading function
+
+get-current ( -- wid ) previous definitions
+
+string line_buffer
+0 value end_of_file?
+variable fd
+
+>search ( wid -- ) definitions
+
+: skip_newlines
+ begin
+ read_buffer .len @ read_buffer_ptr >
+ while
+ read_buffer .addr @ read_buffer_ptr + c@ lf = if
+ read_buffer_ptr char+ to read_buffer_ptr
+ else
+ exit
+ then
+ repeat
+;
+
+: scan_buffer ( -- addr len )
+ read_buffer_ptr >r
+ begin
+ read_buffer .len @ r@ >
+ while
+ read_buffer .addr @ r@ + c@ lf = if
+ read_buffer .addr @ read_buffer_ptr + ( -- addr )
+ r@ read_buffer_ptr - ( -- len )
+ r> to read_buffer_ptr
+ exit
+ then
+ r> char+ >r
+ repeat
+ read_buffer .addr @ read_buffer_ptr + ( -- addr )
+ r@ read_buffer_ptr - ( -- len )
+ r> to read_buffer_ptr
+;
+
+: line_buffer_resize ( len -- len )
+ >r
+ line_buffer .len @ if
+ line_buffer .addr @
+ line_buffer .len @ r@ +
+ resize if ENOMEM throw then
+ else
+ r@ allocate if ENOMEM throw then
+ then
+ line_buffer .addr !
+ r>
+;
+
+: append_to_line_buffer ( addr len -- )
+ line_buffer strget
+ 2swap strcat
+ line_buffer .len !
+ drop
+;
+
+: read_from_buffer
+ scan_buffer ( -- addr len )
+ line_buffer_resize ( len -- len )
+ append_to_line_buffer ( addr len -- )
+;
+
+: refill_required?
+ read_buffer .len @ read_buffer_ptr =
+ end_of_file? 0= and
+;
+
+: refill_buffer
+ 0 to read_buffer_ptr
+ read_buffer .addr @ 0= if
+ read_buffer_size allocate if ENOMEM throw then
+ read_buffer .addr !
+ then
+ fd @ read_buffer .addr @ read_buffer_size fread
+ dup -1 = if EREAD throw then
+ dup 0= if true to end_of_file? then
+ read_buffer .len !
+;
+
+get-current ( -- wid ) previous definitions >search ( wid -- )
+
+: reset_line_reading
+ 0 to read_buffer_ptr
+;
+
+: read_line
+ line_buffer strfree
+ skip_newlines
+ begin
+ read_from_buffer
+ refill_required?
+ while
+ refill_buffer
+ repeat
+;
+
+only forth also support-functions definitions
+
+\ Conf file line parser:
+\ <line> ::= <spaces><name><spaces>'='<spaces><value><spaces>[<comment>] |
+\ <spaces>[<comment>]
+\ <name> ::= <letter>{<letter>|<digit>|'_'}
+\ <value> ::= '"'{<character_set>|'\'<anything>}'"' | <name>
+\ <character_set> ::= ASCII 32 to 126, except '\' and '"'
+\ <comment> ::= '#'{<anything>}
+\
+\ exported:
+\ line_pointer
+\ process_conf
+
+0 value line_pointer
+
+vocabulary file-processing
+also file-processing definitions
+
+\ parser functions
+\
+\ exported:
+\ get_assignment
+
+vocabulary parser
+also parser definitions
+
+0 value parsing_function
+0 value end_of_line
+
+: end_of_line? line_pointer end_of_line = ;
+
+\ classifiers for various character classes in the input line
+
+: letter?
+ line_pointer c@ >r
+ r@ [char] A >=
+ r@ [char] Z <= and
+ r@ [char] a >=
+ r> [char] z <= and
+ or
+;
+
+: digit?
+ line_pointer c@ >r
+ r@ [char] - =
+ r@ [char] 0 >=
+ r> [char] 9 <= and
+ or
+;
+
+: quote? line_pointer c@ [char] " = ;
+
+: assignment_sign? line_pointer c@ [char] = = ;
+
+: comment? line_pointer c@ [char] # = ;
+
+: space? line_pointer c@ bl = line_pointer c@ tab = or ;
+
+: backslash? line_pointer c@ [char] \ = ;
+
+: underscore? line_pointer c@ [char] _ = ;
+
+: dot? line_pointer c@ [char] . = ;
+
+\ manipulation of input line
+: skip_character line_pointer char+ to line_pointer ;
+
+: skip_to_end_of_line end_of_line to line_pointer ;
+
+: eat_space
+ begin
+ end_of_line? if 0 else space? then
+ while
+ skip_character
+ repeat
+;
+
+: parse_name ( -- addr len )
+ line_pointer
+ begin
+ end_of_line? if 0 else letter? digit? underscore? dot? or or or then
+ while
+ skip_character
+ repeat
+ line_pointer over -
+ strdup
+;
+
+: remove_backslashes { addr len | addr' len' -- addr' len' }
+ len allocate if ENOMEM throw then
+ to addr'
+ addr >r
+ begin
+ addr c@ [char] \ <> if
+ addr c@ addr' len' + c!
+ len' char+ to len'
+ then
+ addr char+ to addr
+ r@ len + addr =
+ until
+ r> drop
+ addr' len'
+;
+
+: parse_quote ( -- addr len )
+ line_pointer
+ skip_character
+ end_of_line? if ESYNTAX throw then
+ begin
+ quote? 0=
+ while
+ backslash? if
+ skip_character
+ end_of_line? if ESYNTAX throw then
+ then
+ skip_character
+ end_of_line? if ESYNTAX throw then
+ repeat
+ skip_character
+ line_pointer over -
+ remove_backslashes
+;
+
+: read_name
+ parse_name ( -- addr len )
+ name_buffer strset
+;
+
+: read_value
+ quote? if
+ parse_quote ( -- addr len )
+ else
+ parse_name ( -- addr len )
+ then
+ value_buffer strset
+;
+
+: comment
+ skip_to_end_of_line
+;
+
+: white_space_4
+ eat_space
+ comment? if ['] comment to parsing_function exit then
+ end_of_line? 0= if ESYNTAX throw then
+;
+
+: variable_value
+ read_value
+ ['] white_space_4 to parsing_function
+;
+
+: white_space_3
+ eat_space
+ letter? digit? quote? or or if
+ ['] variable_value to parsing_function exit
+ then
+ ESYNTAX throw
+;
+
+: assignment_sign
+ skip_character
+ ['] white_space_3 to parsing_function
+;
+
+: white_space_2
+ eat_space
+ assignment_sign? if ['] assignment_sign to parsing_function exit then
+ ESYNTAX throw
+;
+
+: variable_name
+ read_name
+ ['] white_space_2 to parsing_function
+;
+
+: white_space_1
+ eat_space
+ letter? if ['] variable_name to parsing_function exit then
+ comment? if ['] comment to parsing_function exit then
+ end_of_line? 0= if ESYNTAX throw then
+;
+
+get-current ( -- wid ) previous definitions >search ( wid -- )
+
+: get_assignment
+ line_buffer strget + to end_of_line
+ line_buffer .addr @ to line_pointer
+ ['] white_space_1 to parsing_function
+ begin
+ end_of_line? 0=
+ while
+ parsing_function execute
+ repeat
+ parsing_function ['] comment =
+ parsing_function ['] white_space_1 =
+ parsing_function ['] white_space_4 =
+ or or 0= if ESYNTAX throw then
+;
+
+only forth also support-functions also file-processing definitions
+
+\ Process line
+
+: assignment_type? ( addr len -- flag )
+ name_buffer strget
+ compare 0=
+;
+
+: suffix_type? ( addr len -- flag )
+ name_buffer .len @ over <= if 2drop false exit then
+ name_buffer .len @ over - name_buffer .addr @ +
+ over compare 0=
+;
+
+: loader_conf_files? s" loader_conf_files" assignment_type? ;
+
+: nextboot_flag? s" nextboot_enable" assignment_type? ;
+
+: nextboot_conf? s" nextboot_conf" assignment_type? ;
+
+: verbose_flag? s" verbose_loading" assignment_type? ;
+
+: execute? s" exec" assignment_type? ;
+
+: module_load? load_module_suffix suffix_type? ;
+
+: module_loadname? module_loadname_suffix suffix_type? ;
+
+: module_type? module_type_suffix suffix_type? ;
+
+: module_args? module_args_suffix suffix_type? ;
+
+: module_beforeload? module_beforeload_suffix suffix_type? ;
+
+: module_afterload? module_afterload_suffix suffix_type? ;
+
+: module_loaderror? module_loaderror_suffix suffix_type? ;
+
+\ build a 'set' statement and execute it
+: set_environment_variable
+ name_buffer .len @ value_buffer .len @ + 5 chars + \ size of result string
+ allocate if ENOMEM throw then
+ dup 0 \ start with an empty string and append the pieces
+ s" set " strcat
+ name_buffer strget strcat
+ s" =" strcat
+ value_buffer strget strcat
+ ['] evaluate catch if
+ 2drop free drop
+ ESETERROR throw
+ else
+ free-memory
+ then
+;
+
+: set_conf_files
+ set_environment_variable
+ s" loader_conf_files" getenv conf_files string=
+;
+
+: set_nextboot_conf
+ value_buffer strget unquote nextboot_conf_file string=
+;
+
+: append_to_module_options_list ( addr -- )
+ module_options @ 0= if
+ dup module_options !
+ last_module_option !
+ else
+ dup last_module_option @ module.next !
+ last_module_option !
+ then
+;
+
+: set_module_name { addr -- } \ check leaks
+ name_buffer strget addr module.name string=
+;
+
+: yes_value?
+ value_buffer strget \ XXX could use unquote
+ 2dup s' "YES"' compare >r
+ 2dup s' "yes"' compare >r
+ 2dup s" YES" compare >r
+ s" yes" compare r> r> r> and and and 0=
+;
+
+: find_module_option ( -- addr | 0 ) \ return ptr to entry matching name_buffer
+ module_options @
+ begin
+ dup
+ while
+ dup module.name strget
+ name_buffer strget
+ compare 0= if exit then
+ module.next @
+ repeat
+;
+
+: new_module_option ( -- addr )
+ sizeof module allocate if ENOMEM throw then
+ dup sizeof module erase
+ dup append_to_module_options_list
+ dup set_module_name
+;
+
+: get_module_option ( -- addr )
+ find_module_option
+ ?dup 0= if new_module_option then
+;
+
+: set_module_flag
+ name_buffer .len @ load_module_suffix nip - name_buffer .len !
+ yes_value? get_module_option module.flag !
+;
+
+: set_module_args
+ name_buffer .len @ module_args_suffix nip - name_buffer .len !
+ value_buffer strget unquote
+ get_module_option module.args string=
+;
+
+: set_module_loadname
+ name_buffer .len @ module_loadname_suffix nip - name_buffer .len !
+ value_buffer strget unquote
+ get_module_option module.loadname string=
+;
+
+: set_module_type
+ name_buffer .len @ module_type_suffix nip - name_buffer .len !
+ value_buffer strget unquote
+ get_module_option module.type string=
+;
+
+: set_module_beforeload
+ name_buffer .len @ module_beforeload_suffix nip - name_buffer .len !
+ value_buffer strget unquote
+ get_module_option module.beforeload string=
+;
+
+: set_module_afterload
+ name_buffer .len @ module_afterload_suffix nip - name_buffer .len !
+ value_buffer strget unquote
+ get_module_option module.afterload string=
+;
+
+: set_module_loaderror
+ name_buffer .len @ module_loaderror_suffix nip - name_buffer .len !
+ value_buffer strget unquote
+ get_module_option module.loaderror string=
+;
+
+: set_nextboot_flag
+ yes_value? to nextboot?
+;
+
+: set_verbose
+ yes_value? to verbose?
+;
+
+: execute_command
+ value_buffer strget unquote
+ ['] evaluate catch if EEXEC throw then
+;
+
+: process_assignment
+ name_buffer .len @ 0= if exit then
+ loader_conf_files? if set_conf_files exit then
+ nextboot_flag? if set_nextboot_flag exit then
+ nextboot_conf? if set_nextboot_conf exit then
+ verbose_flag? if set_verbose exit then
+ execute? if execute_command exit then
+ module_load? if set_module_flag exit then
+ module_loadname? if set_module_loadname exit then
+ module_type? if set_module_type exit then
+ module_args? if set_module_args exit then
+ module_beforeload? if set_module_beforeload exit then
+ module_afterload? if set_module_afterload exit then
+ module_loaderror? if set_module_loaderror exit then
+ set_environment_variable
+;
+
+\ free_buffer ( -- )
+\
+\ Free some pointers if needed. The code then tests for errors
+\ in freeing, and throws an exception if needed. If a pointer is
+\ not allocated, it's value (0) is used as flag.
+
+: free_buffers
+ name_buffer strfree
+ value_buffer strfree
+;
+
+\ Higher level file processing
+
+get-current ( -- wid ) previous definitions >search ( wid -- )
+
+: process_conf
+ begin
+ end_of_file? 0=
+ while
+ free_buffers
+ read_line
+ get_assignment
+ ['] process_assignment catch
+ ['] free_buffers catch
+ swap throw throw
+ repeat
+;
+
+: peek_file ( addr len -- )
+ 0 to end_of_file?
+ reset_line_reading
+ O_RDONLY fopen fd !
+ fd @ -1 = if EOPEN throw then
+ free_buffers
+ read_line
+ get_assignment
+ ['] process_assignment catch
+ ['] free_buffers catch
+ fd @ fclose
+ swap throw throw
+;
+
+only forth also support-functions definitions
+
+\ Interface to loading conf files
+
+: load_conf ( addr len -- )
+ 0 to end_of_file?
+ reset_line_reading
+ O_RDONLY fopen fd !
+ fd @ -1 = if EOPEN throw then
+ ['] process_conf catch
+ fd @ fclose
+ throw
+;
+
+: print_line line_buffer strtype cr ;
+
+: print_syntax_error
+ line_buffer strtype cr
+ line_buffer .addr @
+ begin
+ line_pointer over <>
+ while
+ bl emit char+
+ repeat
+ drop
+ ." ^" cr
+;
+
+
+\ Debugging support functions
+
+only forth definitions also support-functions
+
+: test-file
+ ['] load_conf catch dup .
+ ESYNTAX = if cr print_syntax_error then
+;
+
+\ find a module name, leave addr on the stack (0 if not found)
+: find-module ( <module> -- ptr | 0 )
+ bl parse ( addr len )
+ module_options @ >r ( store current pointer )
+ begin
+ r@
+ while
+ 2dup ( addr len addr len )
+ r@ module.name strget
+ compare 0= if drop drop r> exit then ( found it )
+ r> module.next @ >r
+ repeat
+ type ." was not found" cr r>
+;
+
+: show-nonempty ( addr len mod -- )
+ strget dup verbose? or if
+ 2swap type type cr
+ else
+ drop drop drop drop
+ then ;
+
+: show-one-module { addr -- addr }
+ ." Name: " addr module.name strtype cr
+ s" Path: " addr module.loadname show-nonempty
+ s" Type: " addr module.type show-nonempty
+ s" Flags: " addr module.args show-nonempty
+ s" Before load: " addr module.beforeload show-nonempty
+ s" After load: " addr module.afterload show-nonempty
+ s" Error: " addr module.loaderror show-nonempty
+ ." Status: " addr module.flag @ if ." Load" else ." Don't load" then cr
+ cr
+ addr
+;
+
+: show-module-options
+ module_options @
+ begin
+ ?dup
+ while
+ show-one-module
+ module.next @
+ repeat
+;
+
+: free-one-module { addr -- addr }
+ addr module.name strfree
+ addr module.loadname strfree
+ addr module.type strfree
+ addr module.args strfree
+ addr module.beforeload strfree
+ addr module.afterload strfree
+ addr module.loaderror strfree
+ addr
+;
+
+: free-module-options
+ module_options @
+ begin
+ ?dup
+ while
+ free-one-module
+ dup module.next @
+ swap free-memory
+ repeat
+ 0 module_options !
+ 0 last_module_option !
+;
+
+only forth also support-functions definitions
+
+\ Variables used for processing multiple conf files
+
+string current_file_name_ref \ used to print the file name
+
+\ Indicates if any conf file was successfully read
+
+0 value any_conf_read?
+
+\ loader_conf_files processing support functions
+
+: get_conf_files ( -- addr len ) \ put addr/len on stack, reset var
+ conf_files strget 0 0 conf_files strset
+;
+
+: skip_leading_spaces { addr len pos -- addr len pos' }
+ begin
+ pos len = if 0 else addr pos + c@ bl = then
+ while
+ pos char+ to pos
+ repeat
+ addr len pos
+;
+
+\ return the file name at pos, or free the string if nothing left
+: get_file_name { addr len pos -- addr len pos' addr' len' || 0 }
+ pos len = if
+ addr free abort" Fatal error freeing memory"
+ 0 exit
+ then
+ pos >r
+ begin
+ \ stay in the loop until have chars and they are not blank
+ pos len = if 0 else addr pos + c@ bl <> then
+ while
+ pos char+ to pos
+ repeat
+ addr len pos addr r@ + pos r> -
+;
+
+: get_next_file ( addr len ptr -- addr len ptr' addr' len' | 0 )
+ skip_leading_spaces
+ get_file_name
+;
+
+: print_current_file
+ current_file_name_ref strtype
+;
+
+: process_conf_errors
+ dup 0= if true to any_conf_read? drop exit then
+ >r 2drop r>
+ dup ESYNTAX = if
+ ." Warning: syntax error on file " print_current_file cr
+ print_syntax_error drop exit
+ then
+ dup ESETERROR = if
+ ." Warning: bad definition on file " print_current_file cr
+ print_line drop exit
+ then
+ dup EREAD = if
+ ." Warning: error reading file " print_current_file cr drop exit
+ then
+ dup EOPEN = if
+ verbose? if ." Warning: unable to open file " print_current_file cr then
+ drop exit
+ then
+ dup EFREE = abort" Fatal error freeing memory"
+ dup ENOMEM = abort" Out of memory"
+ throw \ Unknown error -- pass ahead
+;
+
+\ Process loader_conf_files recursively
+\ Interface to loader_conf_files processing
+
+: include_conf_files
+ get_conf_files 0 ( addr len offset )
+ begin
+ get_next_file ?dup ( addr len 1 | 0 )
+ while
+ current_file_name_ref strref
+ ['] load_conf catch
+ process_conf_errors
+ conf_files .addr @ if recurse then
+ repeat
+;
+
+: get_nextboot_conf_file ( -- addr len )
+ nextboot_conf_file strget
+;
+
+: rewrite_nextboot_file ( -- )
+ get_nextboot_conf_file
+ O_WRONLY fopen fd !
+ fd @ -1 = if EOPEN throw then
+ fd @ s' nextboot_enable="NO" ' fwrite ( fd buf len -- nwritten ) drop
+ fd @ fclose
+;
+
+: include_nextboot_file ( -- )
+ get_nextboot_conf_file
+ ['] peek_file catch if 2drop then
+ nextboot? if
+ get_nextboot_conf_file
+ current_file_name_ref strref
+ ['] load_conf catch
+ process_conf_errors
+ ['] rewrite_nextboot_file catch if 2drop then
+ then
+;
+
+\ Module loading functions
+
+: load_parameters { addr -- addr addrN lenN ... addr1 len1 N }
+ addr
+ addr module.args strget
+ addr module.loadname .len @ if
+ addr module.loadname strget
+ else
+ addr module.name strget
+ then
+ addr module.type .len @ if
+ addr module.type strget
+ s" -t "
+ 4 ( -t type name flags )
+ else
+ 2 ( name flags )
+ then
+;
+
+: before_load ( addr -- addr )
+ dup module.beforeload .len @ if
+ dup module.beforeload strget
+ ['] evaluate catch if EBEFORELOAD throw then
+ then
+;
+
+: after_load ( addr -- addr )
+ dup module.afterload .len @ if
+ dup module.afterload strget
+ ['] evaluate catch if EAFTERLOAD throw then
+ then
+;
+
+: load_error ( addr -- addr )
+ dup module.loaderror .len @ if
+ dup module.loaderror strget
+ evaluate \ This we do not intercept so it can throw errors
+ then
+;
+
+: pre_load_message ( addr -- addr )
+ verbose? if
+ dup module.name strtype
+ ." ..."
+ then
+;
+
+: load_error_message verbose? if ." failed!" cr then ;
+
+: load_successful_message verbose? if ." ok" cr then ;
+
+: load_module
+ load_parameters load
+;
+
+: process_module ( addr -- addr )
+ pre_load_message
+ before_load
+ begin
+ ['] load_module catch if
+ dup module.loaderror .len @ if
+ load_error \ Command should return a flag!
+ else
+ load_error_message true \ Do not retry
+ then
+ else
+ after_load
+ load_successful_message true \ Successful, do not retry
+ then
+ until
+;
+
+: process_module_errors ( addr ior -- )
+ dup EBEFORELOAD = if
+ drop
+ ." Module "
+ dup module.name strtype
+ dup module.loadname .len @ if
+ ." (" dup module.loadname strtype ." )"
+ then
+ cr
+ ." Error executing "
+ dup module.beforeload strtype cr \ XXX there was a typo here
+ abort
+ then
+
+ dup EAFTERLOAD = if
+ drop
+ ." Module "
+ dup module.name .addr @ over module.name .len @ type
+ dup module.loadname .len @ if
+ ." (" dup module.loadname strtype ." )"
+ then
+ cr
+ ." Error executing "
+ dup module.afterload strtype cr
+ abort
+ then
+
+ throw \ Don't know what it is all about -- pass ahead
+;
+
+\ Module loading interface
+
+\ scan the list of modules, load enabled ones.
+: load_modules ( -- ) ( throws: abort & user-defined )
+ module_options @ ( list_head )
+ begin
+ ?dup
+ while
+ dup module.flag @ if
+ ['] process_module catch
+ process_module_errors
+ then
+ module.next @
+ repeat
+;
+
+\ h00h00 magic used to try loading either a kernel with a given name,
+\ or a kernel with the default name in a directory of a given name
+\ (the pain!)
+
+: bootpath s" /boot/" ;
+: modulepath s" module_path" ;
+
+\ Functions used to save and restore module_path's value.
+: saveenv ( addr len | -1 -- addr' len | 0 -1 )
+ dup -1 = if 0 swap exit then
+ strdup
+;
+: freeenv ( addr len | 0 -1 )
+ -1 = if drop else free abort" Freeing error" then
+;
+: restoreenv ( addr len | 0 -1 -- )
+ dup -1 = if ( it wasn't set )
+ 2drop
+ modulepath unsetenv
+ else
+ over >r
+ modulepath setenv
+ r> free abort" Freeing error"
+ then
+;
+
+: clip_args \ Drop second string if only one argument is passed
+ 1 = if
+ 2swap 2drop
+ 1
+ else
+ 2
+ then
+;
+
+also builtins
+
+\ Parse filename from a semicolon-separated list
+
+\ replacement, not working yet
+: newparse-; { addr len | a1 -- a' len-x addr x }
+ addr len [char] ; strchr dup if ( a1 len1 )
+ swap to a1 ( store address )
+ 1 - a1 @ 1 + swap ( remove match )
+ addr a1 addr -
+ else
+ 0 0 addr len
+ then
+;
+
+: parse-; ( addr len -- addr' len-x addr x )
+ over 0 2swap ( addr 0 addr len )
+ begin
+ dup 0 <> ( addr 0 addr len )
+ while
+ over c@ [char] ; <> ( addr 0 addr len flag )
+ while
+ 1- swap 1+ swap
+ 2swap 1+ 2swap
+ repeat then
+ dup 0 <> if
+ 1- swap 1+ swap
+ then
+ 2swap
+;
+
+\ Try loading one of multiple kernels specified
+
+: try_multiple_kernels ( addr len addr' len' args -- flag )
+ >r
+ begin
+ parse-; 2>r
+ 2over 2r>
+ r@ clip_args
+ s" DEBUG" getenv? if
+ s" echo Module_path: ${module_path}" evaluate
+ ." Kernel : " >r 2dup type r> cr
+ dup 2 = if ." Flags : " >r 2over type r> cr then
+ then
+ 1 load
+ while
+ dup 0=
+ until
+ 1 >r \ Failure
+ else
+ 0 >r \ Success
+ then
+ 2drop 2drop
+ r>
+ r> drop
+;
+
+\ Try to load a kernel; the kernel name is taken from one of
+\ the following lists, as ordered:
+\
+\ 1. The "bootfile" environment variable
+\ 2. The "kernel" environment variable
+\
+\ Flags are passed, if available. If not, dummy values must be given.
+\
+\ The kernel gets loaded from the current module_path.
+
+: load_a_kernel ( flags len 1 | x x 0 -- flag )
+ local args
+ 2local flags
+ 0 0 2local kernel
+ end-locals
+
+ \ Check if a default kernel name exists at all, exits if not
+ s" bootfile" getenv dup -1 <> if
+ to kernel
+ flags kernel args 1+ try_multiple_kernels
+ dup 0= if exit then
+ then
+ drop
+
+ s" kernel" getenv dup -1 <> if
+ to kernel
+ else
+ drop
+ 1 exit \ Failure
+ then
+
+ \ Try all default kernel names
+ flags kernel args 1+ try_multiple_kernels
+;
+
+\ Try to load a kernel; the kernel name is taken from one of
+\ the following lists, as ordered:
+\
+\ 1. The "bootfile" environment variable
+\ 2. The "kernel" environment variable
+\
+\ Flags are passed, if provided.
+\
+\ The kernel will be loaded from a directory computed from the
+\ path given. Two directories will be tried in the following order:
+\
+\ 1. /boot/path
+\ 2. path
+\
+\ The module_path variable is overridden if load is successful, by
+\ prepending the successful path.
+
+: load_from_directory ( path len 1 | flags len' path len 2 -- flag )
+ local args
+ 2local path
+ args 1 = if 0 0 then
+ 2local flags
+ 0 0 2local oldmodulepath \ like a string
+ 0 0 2local newmodulepath \ like a string
+ end-locals
+
+ \ Set the environment variable module_path, and try loading
+ \ the kernel again.
+ modulepath getenv saveenv to oldmodulepath
+
+ \ Try prepending /boot/ first
+ bootpath nip path nip + \ total length
+ oldmodulepath nip dup -1 = if
+ drop
+ else
+ 1+ + \ add oldpath -- XXX why the 1+ ?
+ then
+ allocate if ( out of memory ) 1 exit then \ XXX throw ?
+
+ 0
+ bootpath strcat
+ path strcat
+ 2dup to newmodulepath
+ modulepath setenv
+
+ \ Try all default kernel names
+ flags args 1- load_a_kernel
+ 0= if ( success )
+ oldmodulepath nip -1 <> if
+ newmodulepath s" ;" strcat
+ oldmodulepath strcat
+ modulepath setenv
+ newmodulepath drop free-memory
+ oldmodulepath drop free-memory
+ then
+ 0 exit
+ then
+
+ \ Well, try without the prepended /boot/
+ path newmodulepath drop swap move
+ newmodulepath drop path nip
+ 2dup to newmodulepath
+ modulepath setenv
+
+ \ Try all default kernel names
+ flags args 1- load_a_kernel
+ if ( failed once more )
+ oldmodulepath restoreenv
+ newmodulepath drop free-memory
+ 1
+ else
+ oldmodulepath nip -1 <> if
+ newmodulepath s" ;" strcat
+ oldmodulepath strcat
+ modulepath setenv
+ newmodulepath drop free-memory
+ oldmodulepath drop free-memory
+ then
+ 0
+ then
+;
+
+\ Try to load a kernel; the kernel name is taken from one of
+\ the following lists, as ordered:
+\
+\ 1. The "bootfile" environment variable
+\ 2. The "kernel" environment variable
+\ 3. The "path" argument
+\
+\ Flags are passed, if provided.
+\
+\ The kernel will be loaded from a directory computed from the
+\ path given. Two directories will be tried in the following order:
+\
+\ 1. /boot/path
+\ 2. path
+\
+\ Unless "path" is meant to be kernel name itself. In that case, it
+\ will first be tried as a full path, and, next, search on the
+\ directories pointed by module_path.
+\
+\ The module_path variable is overridden if load is successful, by
+\ prepending the successful path.
+
+: load_directory_or_file ( path len 1 | flags len' path len 2 -- flag )
+ local args
+ 2local path
+ args 1 = if 0 0 then
+ 2local flags
+ end-locals
+
+ \ First, assume path is an absolute path to a directory
+ flags path args clip_args load_from_directory
+ dup 0= if exit else drop then
+
+ \ Next, assume path points to the kernel
+ flags path args try_multiple_kernels
+;
+
+: initialize ( addr len -- )
+ strdup conf_files strset
+;
+
+: kernel_options ( -- addr len 1 | 0 )
+ s" kernel_options" getenv
+ dup -1 = if drop 0 else 1 then
+;
+
+: standard_kernel_search ( flags 1 | 0 -- flag )
+ local args
+ args 0= if 0 0 then
+ 2local flags
+ s" kernel" getenv
+ dup -1 = if 0 swap then
+ 2local path
+ end-locals
+
+ path nip -1 = if ( there isn't a "kernel" environment variable )
+ flags args load_a_kernel
+ else
+ flags path args 1+ clip_args load_directory_or_file
+ then
+;
+
+: load_kernel ( -- ) ( throws: abort )
+ kernel_options standard_kernel_search
+ abort" Unable to load a kernel!"
+;
+
+: load_xen ( -- flag )
+ s" xen_kernel" getenv dup -1 <> if
+ 1 1 load ( c-addr/u flag N -- flag )
+ else
+ drop
+ 0 ( -1 -- flag )
+ then
+;
+
+: load_xen_throw ( -- ) ( throws: abort )
+ load_xen
+ abort" Unable to load Xen!"
+;
+
+: set_defaultoptions ( -- )
+ s" kernel_options" getenv dup -1 = if
+ drop
+ else
+ s" temp_options" setenv
+ then
+;
+
+\ pick the i-th argument, i starts at 0
+: argv[] ( aN uN ... a1 u1 N i -- aN uN ... a1 u1 N ai+1 ui+1 )
+ 2dup = if 0 0 exit then \ out of range
+ dup >r
+ 1+ 2* ( skip N and ui )
+ pick
+ r>
+ 1+ 2* ( skip N and ai )
+ pick
+;
+
+: drop_args ( aN uN ... a1 u1 N -- )
+ 0 ?do 2drop loop
+;
+
+: argc
+ dup
+;
+
+: queue_argv ( aN uN ... a1 u1 N a u -- a u aN uN ... a1 u1 N+1 )
+ >r
+ over 2* 1+ -roll
+ r>
+ over 2* 1+ -roll
+ 1+
+;
+
+: unqueue_argv ( aN uN ... a1 u1 N -- aN uN ... a2 u2 N-1 a1 u1 )
+ 1- -rot
+;
+
+\ compute the length of the buffer including the spaces between words
+: strlen(argv) ( aN uN .. a1 u1 N -- aN uN .. a1 u1 N len )
+ dup 0= if 0 exit then
+ 0 >r \ Size
+ 0 >r \ Index
+ begin
+ argc r@ <>
+ while
+ r@ argv[]
+ nip
+ r> r> rot + 1+
+ >r 1+ >r
+ repeat
+ r> drop
+ r>
+;
+
+: concat_argv ( aN uN ... a1 u1 N -- a u )
+ strlen(argv) allocate if ENOMEM throw then
+ 0 2>r ( save addr 0 on return stack )
+
+ begin
+ dup
+ while
+ unqueue_argv ( ... N a1 u1 )
+ 2r> 2swap ( old a1 u1 )
+ strcat
+ s" " strcat ( append one space ) \ XXX this gives a trailing space
+ 2>r ( store string on the result stack )
+ repeat
+ drop_args
+ 2r>
+;
+
+: set_tempoptions ( addrN lenN ... addr1 len1 N -- addr len 1 | 0 )
+ \ Save the first argument, if it exists and is not a flag
+ argc if
+ 0 argv[] drop c@ [char] - <> if
+ unqueue_argv 2>r \ Filename
+ 1 >r \ Filename present
+ else
+ 0 >r \ Filename not present
+ then
+ else
+ 0 >r \ Filename not present
+ then
+
+ \ If there are other arguments, assume they are flags
+ ?dup if
+ concat_argv
+ 2dup s" temp_options" setenv
+ drop free if EFREE throw then
+ else
+ set_defaultoptions
+ then
+
+ \ Bring back the filename, if one was provided
+ r> if 2r> 1 else 0 then
+;
+
+: get_arguments ( -- addrN lenN ... addr1 len1 N )
+ 0
+ begin
+ \ Get next word on the command line
+ parse-word
+ ?dup while
+ queue_argv
+ repeat
+ drop ( empty string )
+;
+
+: load_kernel_and_modules ( args -- flag )
+ set_tempoptions
+ argc >r
+ s" temp_options" getenv dup -1 <> if
+ queue_argv
+ else
+ drop
+ then
+ load_xen
+ ?dup 0= if ( success )
+ r> if ( a path was passed )
+ load_directory_or_file
+ else
+ standard_kernel_search
+ then
+ ?dup 0= if ['] load_modules catch then
+ then
+;
+
+only forth definitions
diff --git a/stand/forth/version.4th b/stand/forth/version.4th
new file mode 100644
index 0000000..a5311b4
--- /dev/null
+++ b/stand/forth/version.4th
@@ -0,0 +1,96 @@
+\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
+\ All rights reserved.
+\
+\ Redistribution and use in source and binary forms, with or without
+\ modification, are permitted provided that the following conditions
+\ are met:
+\ 1. Redistributions of source code must retain the above copyright
+\ notice, this list of conditions and the following disclaimer.
+\ 2. Redistributions in binary form must reproduce the above copyright
+\ notice, this list of conditions and the following disclaimer in the
+\ documentation and/or other materials provided with the distribution.
+\
+\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+\ SUCH DAMAGE.
+\
+\ $FreeBSD$
+
+marker task-version.4th
+
+vocabulary version-processing
+only forth also version-processing definitions
+
+variable versionX
+variable versionY
+
+\ Default $loader_version value if not overridden or using tribute screen
+: str_loader_version ( -- C-ADDR/U|-1 ) -1 ;
+
+\ Initialize text placement to defaults
+80 versionX ! \ NOTE: this is the ending column (text is right-justified)
+24 versionY !
+
+only forth definitions also version-processing
+
+: print_version ( -- )
+
+ \ Get the text placement position (if set)
+ s" loader_version_x" getenv dup -1 <> if
+ ?number drop versionX ! -1
+ then drop
+ s" loader_version_y" getenv dup -1 <> if
+ ?number drop versionY ! -1
+ then drop
+
+ \ Default version if none was set
+ s" loader_version" getenv dup -1 = if
+ drop
+ \ Use above default if no logo is requested
+ s" loader_logo" getenv dup -1 = if
+ drop str_loader_version
+ else
+ \ For tributes, do nothing (defer to logo-*.4th)
+ 2dup s" tribute" compare-insensitive 0= if
+ 2drop
+ s" logo" sfind if
+ drop exit \ see logo-tribute.4th
+ else
+ drop str_loader_version
+ then
+ else 2dup s" tributebw" compare-insensitive 0= if
+ 2drop
+ s" logo" sfind if
+ drop exit \ see logo-tributebw.4th
+ else
+ drop str_loader_version
+ then
+ else
+ 2drop str_loader_version
+ then then
+ then
+ then dup -1 = if
+ drop exit \ default version (above) is disabled
+ then
+
+ \ Right justify the text
+ dup versionX @ swap - versionY @ at-xy
+
+ \ Print the version (optionally in cyan)
+ loader_color? dup ( c-addr/u -- c-addr/u bool bool )
+ if 6 fg then
+ -rot type
+ if me then
+
+ 0 25 at-xy
+;
+
+only forth definitions
diff --git a/stand/forth/version.4th.8 b/stand/forth/version.4th.8
new file mode 100644
index 0000000..256df1e
--- /dev/null
+++ b/stand/forth/version.4th.8
@@ -0,0 +1,128 @@
+.\" Copyright (c) 2011-2013 Devin Teske
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 6, 2013
+.Dt VERSION.4TH 8
+.Os
+.Sh NAME
+.Nm version.4th
+.Nd FreeBSD version string boot module
+.Sh DESCRIPTION
+The file that goes by the name of
+.Nm
+is a set of commands designed to draw the boot loader
+version at the bottom-right of the screen.
+The commands of
+.Nm
+by themselves are not enough for most uses.
+Please refer to the
+examples below for the most common situations, and to
+.Xr loader 8
+for additional commands.
+.Pp
+Before using any of the commands provided in
+.Nm ,
+it must be included
+through the command:
+.Pp
+.Dl include version.4th
+.Pp
+This line is present in the default
+.Pa /boot/menu.rc
+file, so it is not needed (and should not be re-issued) in a normal setup.
+.Pp
+The commands provided by it are:
+.Pp
+.Bl -tag -width disable-module_module -compact -offset indent
+.It Ic print_version
+Prints the contents of the
+.Va loader_version
+environment variable right-justified at the column
+.Va loader_version_x
+and row
+.Va loader_version_y .
+.El
+.Pp
+The environment variables that effect its behavior are:
+.Bl -tag -width bootfile -offset indent
+.It Va loader_version
+Set automatically by
+.Xr loader 8 ,
+but you can override it by setting in
+.Xr loader.conf 5 .
+This should be the version of boot loader used.
+.It Va loader_version_x
+Sets the desired ending column position of
+.Va loader_version .
+Default is 80.
+.It Va loader_version_y
+Sets the desired ending row position of
+.Va loader_version .
+Default is 24.
+.It Va loader_color
+If set to
+.Dq Li NO
+(case-insensitive) or
+.Dq Li 0 ,
+causes the version to be printed without color
+.Pq default is ANSI Cyan .
+.El
+.Sh FILES
+.Bl -tag -width /boot/version.4th -compact
+.It Pa /boot/loader
+The
+.Xr loader 8 .
+.It Pa /boot/version.4th
+.Nm
+itself.
+.It Pa /boot/loader.rc
+.Xr loader 8
+bootstrapping script.
+.El
+.Sh EXAMPLES
+Override
+.Xr loader 8
+version in
+.Xr loader.conf 5 :
+.Pp
+.Bd -literal -offset indent -compact
+loader_version="loader 1.1"
+.Ed
+.Sh SEE ALSO
+.Xr loader.conf 5 ,
+.Xr color.4th 8 ,
+.Xr loader 8
+.Sh HISTORY
+The
+.Nm
+set of commands first appeared in
+.Fx 9.0 .
+.Sh AUTHORS
+The
+.Nm
+set of commands was written by
+.An -nosplit
+.An Devin Teske Aq dteske@FreeBSD.org .
diff --git a/stand/geli/Makefile b/stand/geli/Makefile
new file mode 100644
index 0000000..c790431
--- /dev/null
+++ b/stand/geli/Makefile
@@ -0,0 +1,56 @@
+# $FreeBSD$
+# libgeliboot
+
+MAN=
+
+.include <bsd.init.mk>
+MK_SSP= no
+
+LIB= geliboot
+INTERNALLIB=
+MK_PROFILE= no
+NO_PIC=
+
+.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64"
+CFLAGS+= -march=i386
+.endif
+.if ${MACHINE_ARCH} == "amd64"
+CFLAGS+= -m32
+.endif
+
+WARNS?= 0
+
+# string functions from libc
+.PATH: ${SRCTOP}/lib/libc/string
+SRCS+= bcmp.c bcopy.c bzero.c
+
+# Our password input method
+SRCS+= pwgets.c
+
+# sha256 and sha512 from sys/crypto
+.PATH: ${SYSDIR}/crypto/sha2
+CFLAGS+= -DWEAK_REFS
+SRCS+= sha256c.c sha512c.c
+
+# md5 from libmd
+.PATH: ${SRCTOP}/lib/libmd
+SRCS+= md5c.c
+
+# AES implementation from sys/crypto
+.PATH: ${SYSDIR}/crypto/rijndael
+CFLAGS+= -I${LDRSRC}
+# Remove asserts
+CFLAGS+= -DNDEBUG
+SRCS+= rijndael-alg-fst.c rijndael-api-fst.c rijndael-api.c
+
+# local GELI Implementation
+.PATH: ${SYSDIR}/geom/eli
+CFLAGS+= -D_STAND
+SRCS+= geliboot_crypto.c g_eli_hmac.c g_eli_key.c g_eli_key_cache.c pkcs5v2.c
+
+# aes
+.PATH: ${SYSDIR}/opencrypto
+SRCS+= xform_aes_xts.c
+
+.include <bsd.stand.mk>
+.include <bsd.lib.mk>
diff --git a/stand/geli/Makefile.depend b/stand/geli/Makefile.depend
new file mode 100644
index 0000000..7b57224
--- /dev/null
+++ b/stand/geli/Makefile.depend
@@ -0,0 +1,16 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libmd \
+ lib/libstand \
+ secure/lib/libcrypto \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/geli/geliboot.c b/stand/geli/geliboot.c
new file mode 100644
index 0000000..2fca7ba
--- /dev/null
+++ b/stand/geli/geliboot.c
@@ -0,0 +1,437 @@
+/*-
+ * Copyright (c) 2015 Allan Jude <allanjude@FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net>
+ * 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "geliboot_internal.h"
+#include "geliboot.h"
+
+SLIST_HEAD(geli_list, geli_entry) geli_head = SLIST_HEAD_INITIALIZER(geli_head);
+struct geli_list *geli_headp;
+
+typedef u_char geli_ukey[G_ELI_USERKEYLEN];
+
+static geli_ukey saved_keys[GELI_MAX_KEYS];
+static unsigned int nsaved_keys = 0;
+
+/*
+ * Copy keys from local storage to the keybuf struct.
+ * Destroy the local storage when finished.
+ */
+void
+geli_fill_keybuf(struct keybuf *fkeybuf)
+{
+ unsigned int i;
+
+ for (i = 0; i < nsaved_keys; i++) {
+ fkeybuf->kb_ents[i].ke_type = KEYBUF_TYPE_GELI;
+ memcpy(fkeybuf->kb_ents[i].ke_data, saved_keys[i],
+ G_ELI_USERKEYLEN);
+ }
+ fkeybuf->kb_nents = nsaved_keys;
+ explicit_bzero(saved_keys, sizeof(saved_keys));
+}
+
+/*
+ * Copy keys from a keybuf struct into local storage.
+ * Zero out the keybuf.
+ */
+void
+geli_save_keybuf(struct keybuf *skeybuf)
+{
+ unsigned int i;
+
+ for (i = 0; i < skeybuf->kb_nents && i < GELI_MAX_KEYS; i++) {
+ memcpy(saved_keys[i], skeybuf->kb_ents[i].ke_data,
+ G_ELI_USERKEYLEN);
+ explicit_bzero(skeybuf->kb_ents[i].ke_data,
+ G_ELI_USERKEYLEN);
+ skeybuf->kb_ents[i].ke_type = KEYBUF_TYPE_NONE;
+ }
+ nsaved_keys = skeybuf->kb_nents;
+ skeybuf->kb_nents = 0;
+}
+
+static void
+save_key(geli_ukey key)
+{
+
+ /*
+ * If we run out of key space, the worst that will happen is
+ * it will ask the user for the password again.
+ */
+ if (nsaved_keys < GELI_MAX_KEYS) {
+ memcpy(saved_keys[nsaved_keys], key, G_ELI_USERKEYLEN);
+ nsaved_keys++;
+ }
+}
+
+static int
+geli_same_device(struct geli_entry *ge, struct dsk *dskp)
+{
+
+ if (ge->dsk->drive == dskp->drive &&
+ dskp->part == 255 && ge->dsk->part == dskp->slice) {
+ /*
+ * Sometimes slice = slice, and sometimes part = slice
+ * If the incoming struct dsk has part=255, it means look at
+ * the slice instead of the part number
+ */
+ return (0);
+ }
+
+ /* Is this the same device? */
+ if (ge->dsk->drive != dskp->drive ||
+ ge->dsk->slice != dskp->slice ||
+ ge->dsk->part != dskp->part) {
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+geli_findkey(struct geli_entry *ge, struct dsk *dskp, u_char *mkey)
+{
+ u_int keynum;
+ int i;
+
+ if (ge->keybuf_slot >= 0) {
+ if (g_eli_mkey_decrypt(&ge->md, saved_keys[ge->keybuf_slot],
+ mkey, &keynum) == 0) {
+ return (0);
+ }
+ }
+
+ for (i = 0; i < nsaved_keys; i++) {
+ if (g_eli_mkey_decrypt(&ge->md, saved_keys[i], mkey,
+ &keynum) == 0) {
+ ge->keybuf_slot = i;
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+void
+geli_init(void)
+{
+
+ geli_count = 0;
+ SLIST_INIT(&geli_head);
+}
+
+/*
+ * Read the last sector of the drive or partition pointed to by dsk and see
+ * if it is GELI encrypted
+ */
+int
+geli_taste(int read_func(void *vdev, void *priv, off_t off, void *buf,
+ size_t bytes), struct dsk *dskp, daddr_t lastsector)
+{
+ struct g_eli_metadata md;
+ u_char buf[DEV_GELIBOOT_BSIZE];
+ int error;
+ off_t alignsector;
+
+ alignsector = rounddown2(lastsector * DEV_BSIZE, DEV_GELIBOOT_BSIZE);
+ if (alignsector + DEV_GELIBOOT_BSIZE > ((lastsector + 1) * DEV_BSIZE)) {
+ /* Don't read past the end of the disk */
+ alignsector = (lastsector * DEV_BSIZE) + DEV_BSIZE
+ - DEV_GELIBOOT_BSIZE;
+ }
+ error = read_func(NULL, dskp, alignsector, &buf, DEV_GELIBOOT_BSIZE);
+ if (error != 0) {
+ return (error);
+ }
+ /* Extract the last 4k sector of the disk. */
+ error = eli_metadata_decode(buf, &md);
+ if (error != 0) {
+ /* Try the last 512 byte sector instead. */
+ error = eli_metadata_decode(buf +
+ (DEV_GELIBOOT_BSIZE - DEV_BSIZE), &md);
+ if (error != 0) {
+ return (error);
+ }
+ }
+
+ if (!(md.md_flags & G_ELI_FLAG_GELIBOOT)) {
+ /* The GELIBOOT feature is not activated */
+ return (1);
+ }
+ if ((md.md_flags & G_ELI_FLAG_ONETIME)) {
+ /* Swap device, skip it. */
+ return (1);
+ }
+ if (md.md_iterations < 0) {
+ /* XXX TODO: Support loading key files. */
+ /* Disk does not have a passphrase, skip it. */
+ return (1);
+ }
+ geli_e = malloc(sizeof(struct geli_entry));
+ if (geli_e == NULL)
+ return (2);
+
+ geli_e->dsk = malloc(sizeof(struct dsk));
+ if (geli_e->dsk == NULL)
+ return (2);
+ memcpy(geli_e->dsk, dskp, sizeof(struct dsk));
+ geli_e->part_end = lastsector;
+ if (dskp->part == 255) {
+ geli_e->dsk->part = dskp->slice;
+ }
+ geli_e->keybuf_slot = -1;
+
+ geli_e->md = md;
+ eli_metadata_softc(&geli_e->sc, &md, DEV_BSIZE,
+ (lastsector + DEV_BSIZE) * DEV_BSIZE);
+
+ SLIST_INSERT_HEAD(&geli_head, geli_e, entries);
+ geli_count++;
+
+ return (0);
+}
+
+/*
+ * Attempt to decrypt the device
+ */
+static int
+geli_attach(struct geli_entry *ge, struct dsk *dskp, const char *passphrase,
+ const u_char *mkeyp)
+{
+ u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN], *mkp;
+ u_int keynum;
+ struct hmac_ctx ctx;
+ int error;
+
+ if (mkeyp != NULL) {
+ memcpy(&mkey, mkeyp, G_ELI_DATAIVKEYLEN);
+ explicit_bzero(mkeyp, G_ELI_DATAIVKEYLEN);
+ }
+
+ if (mkeyp != NULL || geli_findkey(ge, dskp, mkey) == 0) {
+ goto found_key;
+ }
+
+ g_eli_crypto_hmac_init(&ctx, NULL, 0);
+ /*
+ * Prepare Derived-Key from the user passphrase.
+ */
+ if (geli_e->md.md_iterations < 0) {
+ /* XXX TODO: Support loading key files. */
+ return (1);
+ } else if (geli_e->md.md_iterations == 0) {
+ g_eli_crypto_hmac_update(&ctx, geli_e->md.md_salt,
+ sizeof(geli_e->md.md_salt));
+ g_eli_crypto_hmac_update(&ctx, passphrase,
+ strlen(passphrase));
+ } else if (geli_e->md.md_iterations > 0) {
+ printf("Calculating GELI Decryption Key disk%dp%d @ %d"
+ " iterations...\n", dskp->unit,
+ (dskp->slice > 0 ? dskp->slice : dskp->part),
+ geli_e->md.md_iterations);
+ u_char dkey[G_ELI_USERKEYLEN];
+
+ pkcs5v2_genkey(dkey, sizeof(dkey), geli_e->md.md_salt,
+ sizeof(geli_e->md.md_salt), passphrase,
+ geli_e->md.md_iterations);
+ g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey));
+ explicit_bzero(dkey, sizeof(dkey));
+ }
+
+ g_eli_crypto_hmac_final(&ctx, key, 0);
+
+ error = g_eli_mkey_decrypt(&geli_e->md, key, mkey, &keynum);
+ if (error == -1) {
+ explicit_bzero(mkey, sizeof(mkey));
+ explicit_bzero(key, sizeof(key));
+ printf("Bad GELI key: bad password?\n");
+ return (error);
+ } else if (error != 0) {
+ explicit_bzero(mkey, sizeof(mkey));
+ explicit_bzero(key, sizeof(key));
+ printf("Failed to decrypt GELI master key: %d\n", error);
+ return (error);
+ } else {
+ /* Add key to keychain */
+ save_key(key);
+ explicit_bzero(&key, sizeof(key));
+ }
+
+found_key:
+ /* Store the keys */
+ bcopy(mkey, geli_e->sc.sc_mkey, sizeof(geli_e->sc.sc_mkey));
+ bcopy(mkey, geli_e->sc.sc_ivkey, sizeof(geli_e->sc.sc_ivkey));
+ mkp = mkey + sizeof(geli_e->sc.sc_ivkey);
+ if ((geli_e->sc.sc_flags & G_ELI_FLAG_AUTH) == 0) {
+ bcopy(mkp, geli_e->sc.sc_ekey, G_ELI_DATAKEYLEN);
+ } else {
+ /*
+ * The encryption key is: ekey = HMAC_SHA512(Data-Key, 0x10)
+ */
+ g_eli_crypto_hmac(mkp, G_ELI_MAXKEYLEN, "\x10", 1,
+ geli_e->sc.sc_ekey, 0);
+ }
+ explicit_bzero(mkey, sizeof(mkey));
+
+ /* Initialize the per-sector IV. */
+ switch (geli_e->sc.sc_ealgo) {
+ case CRYPTO_AES_XTS:
+ break;
+ default:
+ SHA256_Init(&geli_e->sc.sc_ivctx);
+ SHA256_Update(&geli_e->sc.sc_ivctx, geli_e->sc.sc_ivkey,
+ sizeof(geli_e->sc.sc_ivkey));
+ break;
+ }
+
+ return (0);
+}
+
+int
+is_geli(struct dsk *dskp)
+{
+ SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) {
+ if (geli_same_device(geli_e, dskp) == 0) {
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+int
+geli_read(struct dsk *dskp, off_t offset, u_char *buf, size_t bytes)
+{
+ u_char iv[G_ELI_IVKEYLEN];
+ u_char *pbuf;
+ int error;
+ off_t dstoff;
+ uint64_t keyno;
+ size_t n, nsec, secsize;
+ struct g_eli_key gkey;
+
+ pbuf = buf;
+ SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) {
+ if (geli_same_device(geli_e, dskp) != 0) {
+ continue;
+ }
+
+ secsize = geli_e->sc.sc_sectorsize;
+ nsec = bytes / secsize;
+ if (nsec == 0) {
+ /*
+ * A read of less than the GELI sector size has been
+ * requested. The caller provided destination buffer may
+ * not be big enough to boost the read to a full sector,
+ * so just attempt to decrypt the truncated sector.
+ */
+ secsize = bytes;
+ nsec = 1;
+ }
+
+ for (n = 0, dstoff = offset; n < nsec; n++, dstoff += secsize) {
+
+ g_eli_crypto_ivgen(&geli_e->sc, dstoff, iv,
+ G_ELI_IVKEYLEN);
+
+ /* Get the key that corresponds to this offset. */
+ keyno = (dstoff >> G_ELI_KEY_SHIFT) / secsize;
+ g_eli_key_fill(&geli_e->sc, &gkey, keyno);
+
+ error = geliboot_crypt(geli_e->sc.sc_ealgo, 0, pbuf,
+ secsize, gkey.gek_key,
+ geli_e->sc.sc_ekeylen, iv);
+
+ if (error != 0) {
+ explicit_bzero(&gkey, sizeof(gkey));
+ printf("Failed to decrypt in geli_read()!");
+ return (error);
+ }
+ pbuf += secsize;
+ }
+ explicit_bzero(&gkey, sizeof(gkey));
+ return (0);
+ }
+
+ printf("GELI provider not found\n");
+ return (1);
+}
+
+int
+geli_havekey(struct dsk *dskp)
+{
+ u_char mkey[G_ELI_DATAIVKEYLEN];
+
+ SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) {
+ if (geli_same_device(geli_e, dskp) != 0) {
+ continue;
+ }
+
+ if (geli_findkey(geli_e, dskp, mkey) == 0) {
+ if (geli_attach(geli_e, dskp, NULL, mkey) == 0) {
+ return (0);
+ }
+ }
+ }
+ explicit_bzero(mkey, sizeof(mkey));
+
+ return (1);
+}
+
+int
+geli_passphrase(char *pw, int disk, int parttype, int part, struct dsk *dskp)
+{
+ int i;
+
+ SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) {
+ if (geli_same_device(geli_e, dskp) != 0) {
+ continue;
+ }
+
+ /* TODO: Implement GELI keyfile(s) support */
+ for (i = 0; i < 3; i++) {
+ /* Try cached passphrase */
+ if (i == 0 && pw[0] != '\0') {
+ if (geli_attach(geli_e, dskp, pw, NULL) == 0) {
+ return (0);
+ }
+ }
+ printf("GELI Passphrase for disk%d%c%d: ", disk,
+ parttype, part);
+ pwgets(pw, GELI_PW_MAXLEN,
+ (geli_e->md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS) == 0);
+ printf("\n");
+ if (geli_attach(geli_e, dskp, pw, NULL) == 0) {
+ return (0);
+ }
+ }
+ }
+
+ return (1);
+}
diff --git a/stand/geli/geliboot.h b/stand/geli/geliboot.h
new file mode 100644
index 0000000..788b82c
--- /dev/null
+++ b/stand/geli/geliboot.h
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2015 Allan Jude <allanjude@FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net>
+ * 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <crypto/intake.h>
+
+#ifndef _GELIBOOT_H_
+#define _GELIBOOT_H_
+
+#ifndef DEV_BSIZE
+#define DEV_BSIZE 512
+#endif
+#ifndef DEV_GELIBOOT_BSIZE
+#define DEV_GELIBOOT_BSIZE 4096
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define GELI_MAX_KEYS 64
+#define GELI_PW_MAXLEN 256
+
+extern void pwgets(char *buf, int n, int hide);
+
+struct dsk;
+
+void geli_init(void);
+int geli_taste(int read_func(void *vdev, void *priv, off_t off,
+ void *buf, size_t bytes), struct dsk *dsk, daddr_t lastsector);
+int is_geli(struct dsk *dsk);
+int geli_read(struct dsk *dsk, off_t offset, u_char *buf, size_t bytes);
+int geli_decrypt(u_int algo, u_char *data, size_t datasize,
+ const u_char *key, size_t keysize, const uint8_t* iv);
+int geli_havekey(struct dsk *dskp);
+int geli_passphrase(char *pw, int disk, int parttype, int part, struct dsk *dskp);
+
+int geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize,
+ const u_char *key, size_t keysize, u_char *iv);
+
+void geli_fill_keybuf(struct keybuf *keybuf);
+void geli_save_keybuf(struct keybuf *keybuf);
+
+#endif /* _GELIBOOT_H_ */
diff --git a/stand/geli/geliboot_crypto.c b/stand/geli/geliboot_crypto.c
new file mode 100644
index 0000000..4187e97
--- /dev/null
+++ b/stand/geli/geliboot_crypto.c
@@ -0,0 +1,140 @@
+/*-
+ * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * Copyright (c) 2015 Allan Jude <allanjude@FreeBSD.org>
+ * 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include "geliboot_internal.h"
+#include "geliboot.h"
+
+int
+geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize,
+ const u_char *key, size_t keysize, u_char *iv)
+{
+ keyInstance aeskey;
+ cipherInstance cipher;
+ struct aes_xts_ctx xtsctx, *ctxp;
+ size_t xts_len;
+ int err, blks, i;
+
+ switch (algo) {
+ case CRYPTO_AES_CBC:
+ err = rijndael_makeKey(&aeskey, !enc, keysize,
+ (const char *)key);
+ if (err < 0) {
+ printf("Failed to setup decryption keys: %d\n", err);
+ return (err);
+ }
+
+ err = rijndael_cipherInit(&cipher, MODE_CBC, iv);
+ if (err < 0) {
+ printf("Failed to setup IV: %d\n", err);
+ return (err);
+ }
+
+ switch (enc) {
+ case 0: /* decrypt */
+ blks = rijndael_blockDecrypt(&cipher, &aeskey, data,
+ datasize * 8, data);
+ break;
+ case 1: /* encrypt */
+ blks = rijndael_blockEncrypt(&cipher, &aeskey, data,
+ datasize * 8, data);
+ break;
+ }
+ if (datasize != (blks / 8)) {
+ printf("Failed to decrypt the entire input: "
+ "%u != %u\n", blks, datasize);
+ return (1);
+ }
+ break;
+ case CRYPTO_AES_XTS:
+ xts_len = keysize << 1;
+ ctxp = &xtsctx;
+
+ rijndael_set_key(&ctxp->key1, key, xts_len / 2);
+ rijndael_set_key(&ctxp->key2, key + (xts_len / 16), xts_len / 2);
+
+ enc_xform_aes_xts.reinit((caddr_t)ctxp, iv);
+
+ switch (enc) {
+ case 0: /* decrypt */
+ for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) {
+ enc_xform_aes_xts.decrypt((caddr_t)ctxp, data + i);
+ }
+ break;
+ case 1: /* encrypt */
+ for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) {
+ enc_xform_aes_xts.encrypt((caddr_t)ctxp, data + i);
+ }
+ break;
+ }
+ break;
+ default:
+ printf("Unsupported crypto algorithm #%d\n", algo);
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+g_eli_crypto_cipher(u_int algo, int enc, u_char *data, size_t datasize,
+ const u_char *key, size_t keysize)
+{
+ u_char iv[keysize];
+
+ explicit_bzero(iv, sizeof(iv));
+ return (geliboot_crypt(algo, enc, data, datasize, key, keysize, iv));
+}
+
+int
+g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize,
+ const u_char *key, size_t keysize)
+{
+
+ /* We prefer AES-CBC for metadata protection. */
+ if (algo == CRYPTO_AES_XTS)
+ algo = CRYPTO_AES_CBC;
+
+ return (g_eli_crypto_cipher(algo, 1, data, datasize, key, keysize));
+}
+
+int
+g_eli_crypto_decrypt(u_int algo, u_char *data, size_t datasize,
+ const u_char *key, size_t keysize)
+{
+
+ /* We prefer AES-CBC for metadata protection. */
+ if (algo == CRYPTO_AES_XTS)
+ algo = CRYPTO_AES_CBC;
+
+ return (g_eli_crypto_cipher(algo, 0, data, datasize, key, keysize));
+}
diff --git a/stand/geli/geliboot_internal.h b/stand/geli/geliboot_internal.h
new file mode 100644
index 0000000..1fad3c5
--- /dev/null
+++ b/stand/geli/geliboot_internal.h
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2015 Allan Jude <allanjude@FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net>
+ * 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _GELIBOOT_INTERNAL_H_
+#define _GELIBOOT_INTERNAL_H_
+
+#define _STRING_H_
+#define _STRINGS_H_
+#define _STDIO_H_
+
+#include <sys/endian.h>
+#include <sys/queue.h>
+
+#include <geom/eli/g_eli.h>
+#include <geom/eli/pkcs5v2.h>
+
+#include <bootstrap.h>
+
+/* Pull in the md5, sha256, and sha512 implementations */
+#include <md5.h>
+#include <crypto/sha2/sha256.h>
+#include <crypto/sha2/sha512.h>
+
+/* Pull in AES implementation */
+#include <crypto/rijndael/rijndael-api-fst.h>
+
+/* AES-XTS implementation */
+#define _STAND 1
+#define STAND_H /* We don't want stand.h in {gpt,zfs,gptzfs}boot */
+#include <opencrypto/xform_enc.h>
+
+struct geli_entry {
+ struct dsk *dsk;
+ off_t part_end;
+ struct g_eli_softc sc;
+ struct g_eli_metadata md;
+ int keybuf_slot;
+ SLIST_ENTRY(geli_entry) entries;
+} *geli_e, *geli_e_tmp;
+
+static int geli_count;
+
+#endif /* _GELIBOOT_INTERNAL_H_ */
diff --git a/stand/geli/pwgets.c b/stand/geli/pwgets.c
new file mode 100644
index 0000000..36f9391
--- /dev/null
+++ b/stand/geli/pwgets.c
@@ -0,0 +1,79 @@
+/* $NetBSD: gets.c,v 1.6 1995/10/11 21:16:57 pk Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)gets.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+/* gets() with constrained input length, for passwords */
+
+void
+pwgets(char *buf, int n, int hide)
+{
+ int c;
+ char *lp;
+
+ for (lp = buf;;)
+ switch (c = getchar() & 0177) {
+ case '\n':
+ case '\r':
+ *lp = '\0';
+ putchar('\n');
+ return;
+ case '\b':
+ case '\177':
+ if (lp > buf) {
+ lp--;
+ if (hide == 0) {
+ putchar('\b');
+ putchar(' ');
+ putchar('\b');
+ }
+ }
+ break;
+ case 'u'&037:
+ case 'w'&037:
+ lp = buf;
+ putchar('\n');
+ break;
+ default:
+ if ((n < 1) || ((lp - buf) < n - 1)) {
+ *lp++ = c;
+ if (hide == 0) {
+ putchar('*');
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
diff --git a/stand/i386/Makefile b/stand/i386/Makefile
new file mode 100644
index 0000000..9a1663a
--- /dev/null
+++ b/stand/i386/Makefile
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+SUBDIR= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot \
+ libi386
+
+.if ${MK_LOADER_FIREWIRE} == "yes"
+SUBDIR+= libfirewire
+.endif
+
+SUBDIR+= loader
+
+# special boot programs, 'self-extracting boot2+loader'
+SUBDIR+= pxeldr
+
+.if ${MACHINE_CPUARCH} == "i386"
+SUBDIR+= kgzldr
+.endif
+
+.if ${MK_ZFS} != "no"
+SUBDIR+= zfsboot gptzfsboot zfsloader
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/stand/i386/Makefile.inc b/stand/i386/Makefile.inc
new file mode 100644
index 0000000..6a4bea7
--- /dev/null
+++ b/stand/i386/Makefile.inc
@@ -0,0 +1,36 @@
+# Common defines for all of /stand/i386/
+#
+# $FreeBSD$
+
+LOADER_ADDRESS?=0x200000
+CFLAGS+= -march=i386 -ffreestanding
+CFLAGS.gcc+= -mpreferred-stack-boundary=2
+CFLAGS+= ${CFLAGS_NO_SIMD} -msoft-float
+LDFLAGS+= -nostdlib
+
+# BTX components
+BTXDIR= ${BOOTOBJ}/i386/btx
+BTXLDR= ${BTXDIR}/btxldr/btxldr
+BTXKERN= ${BTXDIR}/btx/btx
+BTXCRT= ${BTXDIR}/lib/crt0.o
+
+BTXSRC= ${BOOTSRC}/i386/btx
+BTXLIB= ${BTXSRC}/lib
+
+# compact binary with no padding between text, data, bss
+LDSCRIPT= ${BOOTSRC}/i386/boot.ldscript
+# LDFLAGS_BIN=-e start -Ttext ${ORG} -Wl,-T,${LDSCRIPT},-S,--oformat,binary
+# LD_FLAGS_BIN=-static -T ${LDSCRIPT} --gc-sections
+LDFLAGS_BIN=-e start -Ttext ${ORG} -Wl,-N,-S,--oformat,binary
+LD_FLAGS_BIN=-static -N --gc-sections
+
+.if ${MACHINE_CPUARCH} == "amd64"
+DO32=1
+.endif
+
+.if defined(LOADER_FIREWIRE_SUPPORT)
+MK_LOADER_FIREWIRE=yes
+.warning "LOADER_FIREWIRE_SUPPORT deprecated, please move to WITH_LOADER_FIREWIRE"
+.endif
+
+.include "../Makefile.inc"
diff --git a/stand/i386/boot.ldscript b/stand/i386/boot.ldscript
new file mode 100644
index 0000000..04ea39d
--- /dev/null
+++ b/stand/i386/boot.ldscript
@@ -0,0 +1,11 @@
+/* $FreeBSD$ */
+/* Merge text, data and bss together almost no padding */
+OUTPUT_FORMAT("elf32-i386-freebsd")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SECTIONS {
+ . = 0x08048000 + SIZEOF_HEADERS;
+ .text : { *(.text) } =0x90909090 /* Pad with nops, if needed */
+ .data : { *(.data) } _edata = .;
+ .bss : { *(.bss) } _end = .;
+}
diff --git a/stand/i386/boot0/Makefile b/stand/i386/boot0/Makefile
new file mode 100644
index 0000000..39c0072
--- /dev/null
+++ b/stand/i386/boot0/Makefile
@@ -0,0 +1,80 @@
+# $FreeBSD$
+
+PROG?= boot0
+STRIP=
+BINMODE=${NOBINMODE}
+MAN=
+SRCS= ${PROG}.S
+
+# Additional options that you can specify with make OPTS="..."
+# (these only apply to boot0.S)
+#
+# -DVOLUME_SERIAL support volume serial number (NT, XP, Vista)
+# -DSIO do I/O using COM1:
+# -DPXE fallback to INT18/PXE with F6
+# -DCHECK_DRIVE enable checking drive number
+# -DONLY_F_KEYS accept only Fx keys in console
+# -DTEST print drive number on entry
+#
+OPTS ?= -DVOLUME_SERIAL -DPXE
+CFLAGS += ${OPTS}
+
+# Flags used in the boot0.S code:
+# 0x0f all valid partitions enabled.
+# 0x80 'packet', use BIOS EDD (LBA) extensions instead of CHS
+# to read from disk. boot0.S does not check that the extensions
+# are supported, but all modern BIOSes should have them.
+# 0x40 'noupdate', disable writing boot0 back to disk so that
+# the current selection is not preserved across reboots.
+# 0x20 'setdrv', override the drive number supplied by the bios
+# with the one in the boot sector.
+
+# Default boot flags:
+BOOT_BOOT0_FLAGS?= 0x8f
+
+# The number of timer ticks to wait for a keypress before assuming the default
+# selection. Since there are 18.2 ticks per second, the default value of
+# 0xb6 (182d) corresponds to 10 seconds.
+BOOT_BOOT0_TICKS?= 0xb6
+
+# The base address that we the boot0 code to to run it. Don't change this
+# unless you are glutton for punishment.
+BOOT_BOOT0_ORG?= 0x600
+ORG=${BOOT_BOOT0_ORG}
+
+# Comm settings for boot0sio.
+# Bit(s) Description
+# 7-5 data rate (110,150,300,600,1200,2400,4800,9600 bps)
+# 4-3 parity (00 or 10 = none, 01 = odd, 11 = even)
+# 2 stop bits (set = 2, clear = 1)
+# 1-0 data bits (00 = 5, 01 = 6, 10 = 7, 11 = 8)
+.if !defined(BOOT_BOOT0_COMCONSOLE_SPEED)
+BOOT_COMCONSOLE_SPEED?= 9600
+.if ${BOOT_COMCONSOLE_SPEED} == 9600
+BOOT_BOOT0_COMCONSOLE_SPEED= "7 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 4800
+BOOT_BOOT0_COMCONSOLE_SPEED= "6 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 2400
+BOOT_BOOT0_COMCONSOLE_SPEED= "5 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 1200
+BOOT_BOOT0_COMCONSOLE_SPEED= "4 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 600
+BOOT_BOOT0_COMCONSOLE_SPEED= "3 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 300
+BOOT_BOOT0_COMCONSOLE_SPEED= "2 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 150
+BOOT_BOOT0_COMCONSOLE_SPEED= "1 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 110
+BOOT_BOOT0_COMCONSOLE_SPEED= "0 << 5 + 3"
+.else
+BOOT_BOOT0_COMCONSOLE_SPEED= "7 << 5 + 3"
+.endif
+.endif
+
+CFLAGS+=-DFLAGS=${BOOT_BOOT0_FLAGS} \
+ -DTICKS=${BOOT_BOOT0_TICKS} \
+ -DCOMSPEED=${BOOT_BOOT0_COMCONSOLE_SPEED}
+
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/boot0/Makefile.depend b/stand/i386/boot0/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/boot0/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/boot0/boot0.S b/stand/i386/boot0/boot0.S
new file mode 100644
index 0000000..708f093
--- /dev/null
+++ b/stand/i386/boot0/boot0.S
@@ -0,0 +1,682 @@
+/*
+ * Copyright (c) 2008 Luigi Rizzo (mostly documentation)
+ * Copyright (c) 2002 Bruce M. Simpson
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+/* build options: */
+#ifdef SIO /* use serial console on COM1. */
+#endif
+
+#ifdef PXE /* enable PXE/INT18 booting with F6 */
+#define SAVE_MORE_MEMORY
+#endif
+
+#ifdef CHECK_DRIVE /* make sure we boot from a HD. */
+#endif
+
+#ifdef ONLY_F_KEYS /* Only F1..F6, no digits on console */
+#endif
+
+#ifdef VOLUME_SERIAL /* support Volume serial number */
+#define B0_BASE 0x1ae /* move the internal data area */
+#define SAVE_MEMORY
+#else
+#define B0_BASE 0x1b2
+#endif
+
+#ifdef TEST /* enable some test code */
+#define SAVE_MEMORY
+#define SAVE_MORE_MEMORY
+#endif
+
+/*
+ * Note - this code uses many tricks to save space and fit in one sector.
+ * This includes using side effects of certain instructions, reusing
+ * register values from previous operations, etc.
+ * Be extremely careful when changing the code, even for simple things.
+ */
+
+/*
+ * BOOT BLOCK STRUCTURE
+ *
+ * This code implements a Master Boot Record (MBR) for an Intel/PC disk.
+ * It is 512 bytes long and it is normally loaded by the BIOS (or another
+ * bootloader) at 0:0x7c00. This code depends on %cs:%ip being 0:0x7c00
+ *
+ * The initial chunk of instructions is used as a signature by external
+ * tools (e.g. boot0cfg) which can manipulate the block itself.
+ *
+ * The area at offset 0x1b2 contains a magic string ('Drive '), also
+ * used as a signature to detect the block, and some variables that can
+ * be updated by boot0cfg (and optionally written back to the disk).
+ * These variables control the operation of the bootloader itself,
+ * e.g. which partitions to enable, the timeout, the use of LBA
+ * (called 'packet') or CHS mode, whether to force a drive number,
+ * and whether to write back the user's selection back to disk.
+ *
+ * As in every Master Boot Record, the partition table is at 0x1be,
+ * made of four 16-byte entries each containing:
+ *
+ * OFF SIZE DESCRIPTION
+ * 0 1 status (0x80: bootable, 0: non bootable)
+ * 1 3 start sector CHS
+ * 8:head, 6:sector, 2:cyl bit 9..8, 8:cyl bit 7..0
+ * 4 1 partition type
+ * 5 3 end sector CHS
+ * 8 4 LBA of first sector
+ * 12 4 partition size in sectors
+ *
+ * and followed by the two bytes 0x55, 0xAA (MBR signature).
+ */
+
+
+/*
+ * BOOT BLOCK OPERATION
+ *
+ * On entry, the registers contain the following values:
+ *
+ * %cs:%ip 0:0x7c00
+ * %dl drive number (0x80, 0x81, ... )
+ * %si pointer to the partition table from which we were loaded.
+ * Some boot code (e.g. syslinux) use this info to relocate
+ * themselves, so we want to pass a valid one to the next stage.
+ * NOTE: the use of %si is not a standard.
+ *
+ * This boot block first relocates itself at a different address (0:0x600),
+ * to free the space at 0:0x7c00 for the next stage boot block.
+ *
+ * It then initializes some memory at 0:0x800 and above (pointed by %bp)
+ * to store the original drive number (%dl) passed to us, and to construct a
+ * fake partition entry. The latter is used by the disk I/O routine and,
+ * in some cases, passed in %si to the next stage boot code.
+ *
+ * The variables at 0x1b2 are accessed as negative offsets from %bp.
+ *
+ * After the relocation, the code scans the partition table printing
+ * out enabled partition or disks, and waits for user input.
+ *
+ * When a partition is selected, or a timeout expires, the currently
+ * selected partition is used to load the next stage boot code,
+ * %dl and %si are set appropriately as when we were called, and
+ * control is transferred to the newly loaded code at 0:0x7c00.
+ */
+
+/*
+ * CONSTANTS
+ *
+ * NHRDRV is the address in segment 0 where the BIOS writes the
+ * total number of hard disks in the system.
+ * LOAD is the original load address and cannot be changed.
+ * ORIGIN is the relocation address. If you change it, you also need
+ * to change the value passed to the linker in the Makefile
+ * PRT_OFF is the location of the partition table (from the MBR standard).
+ * B0_OFF is the location of the data area, known to boot0cfg so
+ * it cannot be changed. Computed as a negative offset from 0x200
+ * MAGIC is the signature of a boot block.
+ */
+
+ .set NHRDRV,0x475 # Number of hard drives
+ .set ORIGIN,0x600 # Execution address
+ .set LOAD,0x7c00 # Load address
+
+ .set PRT_OFF,0x1be # Partition table
+ .set B0_OFF,(B0_BASE-0x200) # Offset of boot0 data
+
+ .set MAGIC,0xaa55 # Magic: bootable
+
+ .set KEY_ENTER,0x1c # Enter key scan code
+ .set KEY_F1,0x3b # F1 key scan code
+ .set KEY_1,0x02 # #1 key scan code
+
+ .set ASCII_BEL,'#' # ASCII code for <BEL>
+ .set ASCII_CR,0x0D # ASCII code for <CR>
+
+/*
+ * Offsets of variables in the block at B0_OFF, and in the volatile
+ * data area, computed as displacement from %bp.
+ * We need to define them as constant as the assembler cannot
+ * compute them in its single pass.
+ */
+ .set _NXTDRV, B0_OFF+6 # Next drive
+ .set _OPT, B0_OFF+7 # Default option
+ .set _SETDRV, B0_OFF+8 # Drive to force
+ .set _FLAGS, B0_OFF+9 # Flags
+ .set SETDRV, 0x20 # the 'setdrv' flag
+ .set NOUPDATE, 0x40 # the 'noupdate' flag
+ .set USEPACKET, 0x80 # the 'packet' flag
+
+ /* ticks is at a fixed position */
+ .set _TICKS, (PRT_OFF - 0x200 - 2) # Timeout ticks
+ .set _MNUOPT, 0x10 # Saved menu entries
+
+ .set TLEN, (desc_ofs - bootable_ids) # size of bootable ids
+ .globl start # Entry point
+ .code16 # This runs in real mode
+
+/*
+ * MAIN ENTRY POINT
+ * Initialise segments and registers to known values.
+ * segments start at 0.
+ * The stack is immediately below the address we were loaded to.
+ * NOTE: the initial section of the code (up to movw $LOAD,%sp)
+ * is used by boot0cfg, together with the 'Drive ' string and
+ * the 0x55, 0xaa at the end, as an identifier for version 1.0
+ * of the boot code. Do not change it.
+ * In version 1.0 the parameter table (_NEXTDRV etc) is at 0x1b9
+ */
+start: cld # String ops inc
+ xorw %ax,%ax # Zero
+ movw %ax,%es # Address
+ movw %ax,%ds # data
+ movw %ax,%ss # Set up
+ movw $LOAD,%sp # stack
+
+ /*
+ * Copy this code to the address it was linked for, 0x600 by default.
+ */
+ movw %sp,%si # Source
+ movw $start,%di # Destination
+ movw $0x100,%cx # Word count
+ rep # Relocate
+ movsw # code
+ /*
+ * After the code, (i.e. at %di+0, 0x800) create a partition entry,
+ * initialized to LBA 0 / CHS 0:0:1.
+ * Set %bp to point to the partition and also, with negative offsets,
+ * to the variables embedded in the bootblock (nextdrv and so on).
+ */
+ movw %di,%bp # Address variables
+ movb $0x8,%cl # Words to clear
+ rep # Zero
+ stosw # them
+ incb -0xe(%di) # Set the S field to 1
+
+ jmp main-LOAD+ORIGIN # Jump to relocated code
+
+main:
+#if defined(SIO) && COMSPEED != 0
+ /*
+ * Init the serial port. bioscom preserves the driver number in DX.
+ */
+ movw $COMSPEED,%ax # defined by Makefile
+ callw bioscom
+#endif
+
+ /*
+ * If the 'setdrv' flag is set in the boot sector, use the drive
+ * number from the boot sector at 'setdrv_num'.
+ * Optionally, do the same if the BIOS gives us an invalid number
+ * (note though that the override prevents booting from a floppy
+ * or a ZIP/flash drive in floppy emulation).
+ * The test costs 4 bytes of code so it is disabled by default.
+ */
+ testb $SETDRV,_FLAGS(%bp) # Set drive number?
+#ifndef CHECK_DRIVE /* disable drive checks */
+ jz save_curdrive # no, use the default
+#else
+ jnz disable_update # Yes
+ testb %dl,%dl # Drive number valid?
+ js save_curdrive # Possibly (0x80 set)
+#endif
+ /*
+ * Disable updates if the drive number is forced.
+ */
+disable_update: orb $NOUPDATE,_FLAGS(%bp) # Disable updates
+ movb _SETDRV(%bp),%dl # Use stored drive number
+
+ /*
+ * Whatever drive we decided to use, store it at (%bp). The byte
+ * is normally used for the state of the partition (0x80 or 0x00),
+ * but we abuse it as it is very convenient to access at offset 0.
+ * The value is read back after 'check_selection'
+ */
+save_curdrive: movb %dl, (%bp) # Save drive number
+ pushw %dx # Also in the stack
+#ifdef TEST /* test code, print internal bios drive */
+ rolb $1, %dl
+ movw $drive, %si
+ call putkey
+#endif
+ callw putn # Print a newline
+ /*
+ * Start out with a pointer to the 4th byte of the first table entry
+ * so that after 4 iterations it's beyond the end of the sector
+ * and beyond a 256 byte boundary. We use the latter trick to check for
+ * end of the loop without using an extra register (see start.5).
+ */
+ movw $(partbl+0x4),%bx # Partition table (+4)
+ xorw %dx,%dx # Item number
+
+ /*
+ * Loop around on the partition table, printing values until we
+ * pass a 256 byte boundary.
+ */
+read_entry: movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
+ btw %dx,_FLAGS(%bp) # Entry enabled?
+ jnc next_entry # No
+ movb (%bx),%al # Load type
+ test %al, %al # skip empty partition
+ jz next_entry
+ /*
+ * Scan the table of bootable ids, which starts at %di and has
+ * length TLEN. On a match, %di points to the element following the
+ * match; the corresponding offset to the description is $(TLEN-1)
+ * bytes ahead. We use a count of TLEN+1 so if we don't find a match
+ * within the first TLEN entries, we hit the 'unknown' entry.
+ */
+ movw $bootable_ids,%di # Lookup tables
+ movb $(TLEN+1),%cl # Number of entries
+ repne # Locate
+ scasb # type
+ /*
+ * Get the matching element in the next array.
+ * The byte at $(TLEN-1)(%di) contains the offset of the description
+ * string from %di, so we add the number and print the string.
+ */
+ addw $(TLEN-1), %di # Adjust
+ movb (%di),%cl # Partition
+ addw %cx,%di # description
+ callw putx # Display it
+
+next_entry: incw %dx # Next item
+ addb $0x10,%bl # Next entry
+ jnc read_entry # Till done
+ /*
+ * We are past a 256 byte boundary: the partition table is finished.
+ * Add one to the drive number and check it is valid.
+ * Note that if we started from a floppy, %dl was 0 so we still
+ * get an entry for the next drive, which is the first Hard Disk.
+ */
+ popw %ax # Drive number
+ subb $0x80-0x1,%al # Does next
+ cmpb NHRDRV,%al # drive exist? (from BIOS?)
+ jb print_drive # Yes
+ /*
+ * If this is the only drive, don't display it as an option.
+ */
+ decw %ax # Already drive 0?
+ jz print_prompt # Yes
+ /*
+ * If it was illegal or we cycled through them, go back to drive 0.
+ */
+ xorb %al,%al # Drive 0
+ /*
+ * Whatever drive we selected, make it an ascii digit and save it
+ * back to the "nxtdrv" location in case we want to save it to disk.
+ * This digit is also part of the printed drive string, so add 0x80
+ * to indicate end of string.
+ */
+print_drive: addb $'0'|0x80,%al # Save next
+ movb %al,_NXTDRV(%bp) # drive number
+ movw $drive,%di # Display
+ callw putx # item
+ /*
+ * Menu is complete, display a prompt followed by current selection.
+ * 'decw %si' makes the register point to the space after 'Boot: '
+ * so we do not see an extra CRLF on the screen.
+ */
+print_prompt: movw $prompt,%si # Display
+ callw putstr # prompt
+ movb _OPT(%bp),%dl # Display
+ decw %si # default
+ callw putkey # key
+ jmp start_input # Skip beep
+
+/*
+ * Here we have the code waiting for user input or a timeout.
+ */
+beep: movb $ASCII_BEL,%al # Input error, print or beep
+ callw putchr
+
+start_input:
+ /*
+ * Actual Start of input loop. Take note of time
+ */
+ xorb %ah,%ah # BIOS: Get
+ int $0x1a # system time
+ movw %dx,%di # Ticks when
+ addw _TICKS(%bp),%di # timeout
+read_key:
+ /*
+ * Busy loop, looking for keystrokes but keeping one eye on the time.
+ */
+#ifndef SIO
+ movb $0x1,%ah # BIOS: Check
+ int $0x16 # for keypress
+#else /* SIO */
+ movb $0x03,%ah # BIOS: Read COM
+ call bioscom
+ testb $0x01,%ah # Check line status
+ # (bit 1 indicates input)
+#endif /* SIO */
+ jnz got_key # Have input
+ xorb %ah,%ah # BIOS: int 0x1a, 00
+ int $0x1a # get system time
+ cmpw %di,%dx # Timeout?
+ jb read_key # No
+
+ /*
+ * Timed out or default selection
+ */
+use_default: movb _OPT(%bp),%al # Load default
+ orb $NOUPDATE,_FLAGS(%bp) # Disable updates
+ jmp check_selection # Join common code
+
+ /*
+ * Get the keystroke.
+ * ENTER or CR confirm the current selection (same as a timeout).
+ * Otherwise convert F1..F6 (or '1'..'6') to 0..5 and check if the
+ * selection is valid.
+ * The SIO code uses ascii chars, the console code uses scancodes.
+ */
+got_key:
+#ifndef SIO
+ xorb %ah,%ah # BIOS: int 0x16, 00
+ int $0x16 # get keypress
+ movb %ah,%al # move scan code to %al
+ cmpb $KEY_ENTER,%al
+#else
+ movb $0x02,%ah # BIOS: Receive
+ call bioscom
+ cmpb $ASCII_CR,%al
+#endif
+ je use_default # enter -> default
+ /*
+ * Check if the key is acceptable, and loop back if not.
+ * The console (non-SIO) code looks at scancodes and accepts
+ * both F1..F6 and 1..6 (the latter costs 6 bytes of code),
+ * relying on the fact that F1..F6 have higher scancodes than 1..6
+ * The SIO code only takes 1..6
+ */
+#ifdef SIO /* SIO mode, use ascii values */
+ subb $'1',%al # Subtract '1' ascii code
+#else /* console mode -- use scancodes */
+ subb $KEY_F1,%al /* Subtract F1 scan code */
+#if !defined(ONLY_F_KEYS)
+ cmpb $0x5,%al # F1..F6
+ jna 3f # Yes
+ subb $(KEY_1 - KEY_F1),%al # Less #1 scan code
+ 3:
+#endif /* ONLY_F_KEYS */
+#endif /* SIO */
+check_selection:
+ cmpb $0x5,%al # F1..F6 or 1..6 ?
+#ifdef PXE /* enable PXE/INT18 using F6 */
+ jne 1f;
+ int $0x18 # found F6, try INT18
+ 1:
+#endif /* PXE */
+ jae beep # Not in F1..F5, beep
+
+ /*
+ * We have a selection. If it's a bad selection go back to complain.
+ * The bits in MNUOPT were set when the options were printed.
+ * Anything not printed is not an option.
+ */
+ cbtw # Extend (%ah=0 used later)
+ btw %ax,_MNUOPT(%bp) # Option enabled?
+ jnc beep # No
+ /*
+ * Save the info in the original tables
+ * for rewriting to the disk.
+ */
+ movb %al,_OPT(%bp) # Save option
+
+ /*
+ * Make %si and %bx point to the fake partition at LBA 0 (CHS 0:0:1).
+ * Because the correct address is already in %bp, just use it.
+ * Set %dl with the drive number saved in byte 0.
+ * If we have pressed F5 or 5, then this is a good, fake value
+ * to present to the next stage boot code.
+ */
+ movw %bp,%si # Partition for write
+ movb (%si),%dl # Drive number, saved above
+ movw %si,%bx # Partition for read
+ cmpb $0x4,%al # F5/#5 pressed?
+ pushf # Save results for later
+ je 1f # Yes, F5
+
+ /*
+ * F1..F4 was pressed, so make %bx point to the currently
+ * selected partition, and leave the drive number unchanged.
+ */
+ shlb $0x4,%al # Point to
+ addw $partbl,%ax # selected
+ xchgw %bx,%ax # partition
+ movb $0x80,(%bx) # Flag active
+ /*
+ * If not asked to do a write-back (flags 0x40) don't do one.
+ * Around the call, save the partition pointer to %bx and
+ * restore to %si which is where the next stage expects it.
+ */
+ 1: pushw %bx # Save
+ testb $NOUPDATE,_FLAGS(%bp) # No updates?
+ jnz 2f # skip update
+ movw $start,%bx # Data to write
+ movb $0x3,%ah # Write sector
+ callw intx13 # to disk
+ 2: popw %si # Restore
+
+ /*
+ * If going to next drive, replace drive with selected one.
+ * Remember to un-ascii it. Hey 0x80 is already set, cool!
+ */
+ popf # Restore %al test results
+ jne 3f # If not F5/#5
+ movb _NXTDRV(%bp),%dl # Next drive
+ subb $'0',%dl # number
+ /*
+ * Load selected bootsector to the LOAD location in RAM. If read
+ * fails or there is no 0x55aa marker, treat it as a bad selection.
+ */
+ 3: movw $LOAD,%bx # Address for read
+ movb $0x2,%ah # Read sector
+ callw intx13 # from disk
+ jc beep # If error
+ cmpw $MAGIC,0x1fe(%bx) # Bootable?
+ jne beep # No
+ pushw %si # Save ptr to selected part.
+ callw putn # Leave some space
+ popw %si # Restore, next stage uses it
+ jmp *%bx # Invoke bootstrap
+
+/*
+ * Display routines
+ * putkey prints the option selected in %dl (F1..F5 or 1..5) followed by
+ * the string at %si
+ * putx: print the option in %dl followed by the string at %di
+ * also record the drive as valid.
+ * putn: print a crlf
+ * putstr: print the string at %si
+ * putchr: print the char in al
+ */
+
+/*
+ * Display the option and record the drive as valid in the options.
+ * That last point is done using the btsw instruction which does
+ * a test and set. We don't care for the test part.
+ */
+putx: btsw %dx,_MNUOPT(%bp) # Enable menu option
+ movw $item,%si # Display
+ callw putkey # key
+ movw %di,%si # Display the rest
+ callw putstr # Display string
+
+putn: movw $crlf,%si # To next line
+ jmp putstr
+
+putkey:
+#ifndef SIO
+ movb $'F',%al # Display
+ callw putchr # 'F'
+#endif
+ movb $'1',%al # Prepare
+ addb %dl,%al # digit
+
+putstr.1: callw putchr # Display char
+putstr: lodsb # Get byte
+ testb $0x80,%al # End of string?
+ jz putstr.1 # No
+ andb $~0x80,%al # Clear MSB then print last
+
+putchr:
+#ifndef SIO
+ pushw %bx # Save
+ movw $0x7,%bx # Page:attribute
+ movb $0xe,%ah # BIOS: Display
+ int $0x10 # character
+ popw %bx # Restore
+#else /* SIO */
+ movb $0x01,%ah # BIOS: Send character
+bioscom:
+ pushw %dx # Save
+ xorw %dx,%dx # Use COM1
+ int $0x14 # BIOS: Serial I/O
+ popw %dx # Restore
+#endif /* SIO */
+ retw # To caller
+
+/* One-sector disk I/O routine */
+
+/*
+ * %dl: drive, %si partition entry, %es:%bx transfer buffer.
+ * Load the CHS values and possibly the LBA address from the block
+ * at %si, and use the appropriate method to load the sector.
+ * Don't use packet mode for a floppy.
+ */
+intx13: # Prepare CHS parameters
+ movb 0x1(%si),%dh # Load head
+ movw 0x2(%si),%cx # Load cylinder:sector
+ movb $0x1,%al # Sector count
+ pushw %si # Save
+ movw %sp,%di # Save
+#ifndef CHECK_DRIVE /* floppy support */
+ testb %dl, %dl # is this a floppy ?
+ jz 1f # Yes, use CHS mode
+#endif
+ testb $USEPACKET,_FLAGS(%bp) # Use packet interface?
+ jz 1f # No
+ pushl $0x0 # Set the
+ pushl 0x8(%si) # LBA address
+ pushw %es # Set the transfer
+ pushw %bx # buffer address
+ push $0x1 # Block count
+ push $0x10 # Packet size
+ movw %sp,%si # Packet pointer
+ decw %ax # Verify off
+ orb $0x40,%ah # Use disk packet
+ 1: int $0x13 # BIOS: Disk I/O
+ movw %di,%sp # Restore
+ popw %si # Restore
+ retw # To caller
+
+/*
+ * Various menu strings. 'item' goes after 'prompt' to save space.
+ * Also use shorter versions to make room for the PXE/INT18 code.
+ */
+prompt:
+#ifdef PXE
+ .ascii "\nF6 PXE\r"
+#endif
+ .ascii "\nBoot:"
+item: .ascii " "; .byte ' '|0x80
+crlf: .ascii "\r"; .byte '\n'|0x80
+
+/* Partition type tables */
+
+bootable_ids:
+ /*
+ * These values indicate bootable types we know about.
+ * Corresponding descriptions are at desc_ofs:
+ * Entries don't need to be sorted.
+ */
+ .byte 0x83, 0xa5, 0xa6, 0xa9, 0x06, 0x07, 0x0b
+#ifndef SAVE_MORE_MEMORY
+ .byte 0x05 # extended partition
+#endif
+#ifndef SAVE_MEMORY /* other DOS partitions */
+ .byte 0x01 # FAT12
+ .byte 0x04 # FAT16 < 32M
+#endif
+
+desc_ofs:
+ /*
+ * Offsets that match the known types above, used to point to the
+ * actual partition name. The last entry must point to os_misc,
+ * which is used for non-matching names.
+ */
+ .byte os_linux-. # 131, Linux
+ .byte os_freebsd-. # 165, FreeBSD
+ .byte os_bsd-. # 166, OpenBSD
+ .byte os_bsd-. # 169, NetBSD
+ .byte os_dos-. # 6, FAT16 >= 32M
+ .byte os_win-. # 7, NTFS
+ .byte os_win-. # 11, FAT32
+
+#ifndef SAVE_MORE_MEMORY
+ .byte os_ext-. # 5, DOS Ext
+#endif
+#ifndef SAVE_MEMORY
+ .byte os_dos-. # 1, FAT12 DOS
+ .byte os_dos-. # 4, FAT16 <32M
+#endif
+ .byte os_misc-. # Unknown
+
+ /*
+ * And here are the strings themselves. The last byte of
+ * the string has bit 7 set.
+ */
+os_misc: .byte '?'|0x80
+os_dos:
+#ifndef SAVE_MORE_MEMORY /* 'DOS' remapped to 'WIN' if no room */
+ .ascii "DO"; .byte 'S'|0x80
+#endif
+os_win: .ascii "Wi"; .byte 'n'|0x80
+os_linux: .ascii "Linu"; .byte 'x'|0x80
+os_freebsd: .ascii "Free"
+os_bsd: .ascii "BS"; .byte 'D'|0x80
+#ifndef SAVE_MORE_MEMORY
+os_ext: .ascii "EX"; .byte 'T'|0x80
+#endif
+
+ .org (0x200 + B0_OFF),0x90
+/*
+ * The boot0 version 1.0 parameter table.
+ * Do not move it nor change the "Drive " string, boot0cfg
+ * uses its offset and content to identify the boot sector.
+ * The other fields are sometimes changed before writing back to the drive
+ * Be especially careful that nxtdrv: must come after drive:, as it
+ * is part of the same string.
+ */
+drive: .ascii "Drive "
+nxtdrv: .byte 0x0 # Next drive number
+opt: .byte 0x0 # Option
+setdrv_num: .byte 0x80 # Drive to force
+flags: .byte FLAGS # Flags
+#ifdef VOLUME_SERIAL
+ .byte 0xa8,0xa8,0xa8,0xa8 # Volume Serial Number
+#endif
+ticks: .word TICKS # Delay
+
+ .org PRT_OFF
+/*
+ * Here is the 64 byte partition table that fdisk would fiddle with.
+ */
+partbl: .fill 0x40,0x1,0x0 # Partition table
+ .word MAGIC # Magic number
+ .org 0x200 # again, safety check
+endblock:
diff --git a/stand/i386/boot0sio/Makefile b/stand/i386/boot0sio/Makefile
new file mode 100644
index 0000000..1321dd3
--- /dev/null
+++ b/stand/i386/boot0sio/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../boot0
+
+PROGNAME= boot0sio
+CFLAGS+= -DSIO
+
+.include "${.CURDIR}/../boot0/Makefile"
diff --git a/stand/i386/boot0sio/Makefile.depend b/stand/i386/boot0sio/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/boot0sio/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/boot2/Makefile b/stand/i386/boot2/Makefile
new file mode 100644
index 0000000..c3146b3
--- /dev/null
+++ b/stand/i386/boot2/Makefile
@@ -0,0 +1,99 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+FILES= boot boot1 boot2
+
+NM?= nm
+
+# A value of 0x80 enables LBA support.
+BOOT_BOOT1_FLAGS?= 0x80
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+REL1= 0x700
+ORG1= 0x7c00
+ORG2= 0x2000
+
+# Decide level of UFS support.
+BOOT2_UFS?= UFS1_AND_UFS2
+#BOOT2_UFS?= UFS2_ONLY
+#BOOT2_UFS?= UFS1_ONLY
+
+CFLAGS+=-fomit-frame-pointer \
+ -mrtd \
+ -mregparm=3 \
+ -D${BOOT2_UFS} \
+ -DFLAGS=${BOOT_BOOT1_FLAGS} \
+ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} \
+ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ -I${LDRSRC} \
+ -I${BTXLIB} \
+ -Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+ -Winline
+
+CFLAGS.gcc+= -Os \
+ -fno-asynchronous-unwind-tables \
+ --param max-inline-insns-single=100
+.if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} <= 40201
+CFLAGS.gcc+= -mno-align-long-strings
+.endif
+
+CFLAGS.clang+= -Oz ${CLANG_OPT_SMALL}
+
+LD_FLAGS+=${LD_FLAGS_BIN}
+
+CLEANFILES+= boot
+
+boot: boot1 boot2
+ cat boot1 boot2 > boot
+
+CLEANFILES+= boot1 boot1.out boot1.o
+
+boot1: boot1.out
+ ${OBJCOPY} -S -O binary boot1.out ${.TARGET}
+
+boot1.out: boot1.o
+ ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} boot1.o
+
+CLEANFILES+= boot2 boot2.ld boot2.ldr boot2.bin boot2.out boot2.o \
+ boot2.h sio.o
+
+BOOT2SIZE= 7680
+
+boot2: boot2.ld
+ @set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \
+ echo "$$x bytes available"; test $$x -ge 0
+ ${DD} if=${.ALLSRC} of=${.TARGET} obs=${BOOT2SIZE} conv=osync
+
+boot2.ld: boot2.ldr boot2.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \
+ -o ${.TARGET} -P 1 boot2.bin
+
+boot2.ldr:
+ ${DD} if=/dev/zero of=${.TARGET} bs=512 count=1
+
+boot2.bin: boot2.out
+ ${OBJCOPY} -S -O binary boot2.out ${.TARGET}
+
+boot2.out: ${BTXCRT} boot2.o sio.o
+ ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC}
+
+SRCS= boot2.c boot2.h
+
+boot2.h: boot1.out
+ ${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \
+ { x = $$1 - ORG1; \
+ printf("#define XREADORG %#x\n", REL1 + x) }' \
+ ORG1=`printf "%d" ${ORG1}` \
+ REL1=`printf "%d" ${REL1}` > ${.TARGET}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.boot1.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/boot2/Makefile.depend b/stand/i386/boot2/Makefile.depend
new file mode 100644
index 0000000..d5a67a6
--- /dev/null
+++ b/stand/i386/boot2/Makefile.depend
@@ -0,0 +1,14 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/lib \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/boot2/boot1.S b/stand/i386/boot2/boot1.S
new file mode 100644
index 0000000..984ab52
--- /dev/null
+++ b/stand/i386/boot2/boot1.S
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+/* Memory Locations */
+ .set MEM_REL,0x700 # Relocation address
+ .set MEM_ARG,0x900 # Arguments
+ .set MEM_ORG,0x7c00 # Origin
+ .set MEM_BUF,0x8c00 # Load area
+ .set MEM_BTX,0x9000 # BTX start
+ .set MEM_JMP,0x9010 # BTX entry point
+ .set MEM_USR,0xa000 # Client start
+ .set BDA_BOOT,0x472 # Boot howto flag
+
+/* Partition Constants */
+ .set PRT_OFF,0x1be # Partition offset
+ .set PRT_NUM,0x4 # Partitions
+ .set PRT_BSD,0xa5 # Partition type
+
+/* Flag Bits */
+ .set FL_PACKET,0x80 # Packet mode
+
+/* Misc. Constants */
+ .set SIZ_PAG,0x1000 # Page size
+ .set SIZ_SEC,0x200 # Sector size
+
+ .set NSECT,0x10
+ .globl start
+ .globl xread
+ .code16
+
+start: jmp main # Start recognizably
+
+/*
+ * This is the start of a standard BIOS Parameter Block (BPB). Most bootable
+ * FAT disks have this at the start of their MBR. While normal BIOS's will
+ * work fine without this section, IBM's El Torito emulation "fixes" up the
+ * BPB by writing into the memory copy of the MBR. Rather than have data
+ * written into our xread routine, we'll define a BPB to work around it.
+ * The data marked with (T) indicates a field required for a ThinkPad to
+ * recognize the disk and (W) indicates fields written from IBM BIOS code.
+ * The use of the BPB is based on what OpenBSD and NetBSD implemented in
+ * their boot code but the required fields were determined by trial and error.
+ *
+ * Note: If additional space is needed in boot1, one solution would be to
+ * move the "prompt" message data (below) to replace the OEM ID.
+ */
+ .org 0x03, 0x00
+oemid: .space 0x08, 0x00 # OEM ID
+
+ .org 0x0b, 0x00
+bpb: .word 512 # sector size (T)
+ .byte 0 # sectors/clustor
+ .word 0 # reserved sectors
+ .byte 0 # number of FATs
+ .word 0 # root entries
+ .word 0 # small sectors
+ .byte 0 # media type (W)
+ .word 0 # sectors/fat
+ .word 18 # sectors per track (T)
+ .word 2 # number of heads (T)
+ .long 0 # hidden sectors (W)
+ .long 0 # large sectors
+
+ .org 0x24, 0x00
+ebpb: .byte 0 # BIOS physical drive number (W)
+
+ .org 0x25,0x90
+/*
+ * Trampoline used by boot2 to call read to read data from the disk via
+ * the BIOS. Call with:
+ *
+ * %cx:%ax - long - LBA to read in
+ * %es:(%bx) - caddr_t - buffer to read data into
+ * %dl - byte - drive to read from
+ * %dh - byte - num sectors to read
+ */
+
+xread: push %ss # Address
+ pop %ds # data
+/*
+ * Setup an EDD disk packet and pass it to read
+ */
+xread.1: # Starting
+ pushl $0x0 # absolute
+ push %cx # block
+ push %ax # number
+ push %es # Address of
+ push %bx # transfer buffer
+ xor %ax,%ax # Number of
+ movb %dh,%al # blocks to
+ push %ax # transfer
+ push $0x10 # Size of packet
+ mov %sp,%bp # Packet pointer
+ callw read # Read from disk
+ lea 0x10(%bp),%sp # Clear stack
+ lret # To far caller
+/*
+ * Load the rest of boot2 and BTX up, copy the parts to the right locations,
+ * and start it all up.
+ */
+
+/*
+ * Setup the segment registers to flat addressing (segment 0) and setup the
+ * stack to end just below the start of our code.
+ */
+main: cld # String ops inc
+ xor %cx,%cx # Zero
+ mov %cx,%es # Address
+ mov %cx,%ds # data
+ mov %cx,%ss # Set up
+ mov $start,%sp # stack
+/*
+ * Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets
+ * %cx == 0x100. Note that boot1 does not use this relocated copy
+ * of itself while loading boot2; however, BTX reclaims the memory
+ * used by boot1 during its initialization. As a result, boot2 uses
+ * xread from the relocated copy.
+ */
+ mov %sp,%si # Source
+ mov $MEM_REL,%di # Destination
+ incb %ch # Word count
+ rep # Copy
+ movsw # code
+/*
+ * If we are on a hard drive, then load the MBR and look for the first
+ * FreeBSD slice. We use the fake partition entry below that points to
+ * the MBR when we call nread. The first pass looks for the first active
+ * FreeBSD slice. The second pass looks for the first non-active FreeBSD
+ * slice if the first one fails.
+ */
+ mov $part4,%si # Partition
+ cmpb $0x80,%dl # Hard drive?
+ jb main.4 # No
+ movb $0x1,%dh # Block count
+ callw nread # Read MBR
+ mov $0x1,%cx # Two passes
+main.1: mov $MEM_BUF+PRT_OFF,%si # Partition table
+ movb $0x1,%dh # Partition
+main.2: cmpb $PRT_BSD,0x4(%si) # Our partition type?
+ jne main.3 # No
+ jcxz main.5 # If second pass
+ testb $0x80,(%si) # Active?
+ jnz main.5 # Yes
+main.3: add $0x10,%si # Next entry
+ incb %dh # Partition
+ cmpb $0x1+PRT_NUM,%dh # In table?
+ jb main.2 # Yes
+ dec %cx # Do two
+ jcxz main.1 # passes
+/*
+ * If we get here, we didn't find any FreeBSD slices at all, so print an
+ * error message and die.
+ */
+ mov $msg_part,%si # Message
+ jmp error # Error
+/*
+ * Floppies use partition 0 of drive 0.
+ */
+main.4: xor %dx,%dx # Partition:drive
+/*
+ * Ok, we have a slice and drive in %dx now, so use that to locate and load
+ * boot2. %si references the start of the slice we are looking for, so go
+ * ahead and load up the first 16 sectors (boot1 + boot2) from that. When
+ * we read it in, we conveniently use 0x8c00 as our transfer buffer. Thus,
+ * boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00.
+ * The first part of boot2 is the disklabel, which is 0x200 bytes long.
+ * The second part is BTX, which is thus loaded into 0x9000, which is where
+ * it also runs from. The boot2.bin binary starts right after the end of
+ * BTX, so we have to figure out where the start of it is and then move the
+ * binary to 0xc000. Normally, BTX clients start at MEM_USR, or 0xa000, but
+ * when we use btxld to create boot2, we use an entry point of 0x2000. That
+ * entry point is relative to MEM_USR; thus boot2.bin starts at 0xc000.
+ */
+main.5: mov %dx,MEM_ARG # Save args
+ movb $NSECT,%dh # Sector count
+ callw nread # Read disk
+ mov $MEM_BTX,%bx # BTX
+ mov 0xa(%bx),%si # Get BTX length and set
+ add %bx,%si # %si to start of boot2.bin
+ mov $MEM_USR+SIZ_PAG*2,%di # Client page 2
+ mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx # Byte
+ sub %si,%cx # count
+ rep # Relocate
+ movsb # client
+
+/*
+ * Enable A20 so we can access memory above 1 meg.
+ * Use the zero-valued %cx as a timeout for embedded hardware which do not
+ * have a keyboard controller.
+ */
+seta20: cli # Disable interrupts
+seta20.1: dec %cx # Timeout?
+ jz seta20.3 # Yes
+ inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.1 # Yes
+ movb $0xd1,%al # Command: Write
+ outb %al,$0x64 # output port
+seta20.2: inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.2 # Yes
+ movb $0xdf,%al # Enable
+ outb %al,$0x60 # A20
+seta20.3: sti # Enable interrupts
+
+ jmp start+MEM_JMP-MEM_ORG # Start BTX
+
+
+/*
+ * Trampoline used to call read from within boot1.
+ */
+nread: mov $MEM_BUF,%bx # Transfer buffer
+ mov 0x8(%si),%ax # Get
+ mov 0xa(%si),%cx # LBA
+ push %cs # Read from
+ callw xread.1 # disk
+ jnc return # If success, return
+ mov $msg_read,%si # Otherwise, set the error
+ # message and fall through to
+ # the error routine
+/*
+ * Print out the error message pointed to by %ds:(%si) followed
+ * by a prompt, wait for a keypress, and then reboot the machine.
+ */
+error: callw putstr # Display message
+ mov $prompt,%si # Display
+ callw putstr # prompt
+ xorb %ah,%ah # BIOS: Get
+ int $0x16 # keypress
+ movw $0x1234, BDA_BOOT # Do a warm boot
+ ljmp $0xf000,$0xfff0 # reboot the machine
+/*
+ * Display a null-terminated string using the BIOS output.
+ */
+putstr.0: mov $0x7,%bx # Page:attribute
+ movb $0xe,%ah # BIOS: Display
+ int $0x10 # character
+putstr: lodsb # Get char
+ testb %al,%al # End of string?
+ jne putstr.0 # No
+
+/*
+ * Overused return code. ereturn is used to return an error from the
+ * read function. Since we assume putstr succeeds, we (ab)use the
+ * same code when we return from putstr.
+ */
+ereturn: movb $0x1,%ah # Invalid
+ stc # argument
+return: retw # To caller
+/*
+ * Reads sectors from the disk. If EDD is enabled, then check if it is
+ * installed and use it if it is. If it is not installed or not enabled, then
+ * fall back to using CHS. Since we use a LBA, if we are using CHS, we have to
+ * fetch the drive parameters from the BIOS and divide it out ourselves.
+ * Call with:
+ *
+ * %dl - byte - drive number
+ * stack - 10 bytes - EDD Packet
+ */
+read: testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled?
+ jz read.1 # No, use CHS
+ cmpb $0x80,%dl # Hard drive?
+ jb read.1 # No, use CHS
+ mov $0x55aa,%bx # Magic
+ push %dx # Save
+ movb $0x41,%ah # BIOS: Check
+ int $0x13 # extensions present
+ pop %dx # Restore
+ jc read.1 # If error, use CHS
+ cmp $0xaa55,%bx # Magic?
+ jne read.1 # No, so use CHS
+ testb $0x1,%cl # Packet interface?
+ jz read.1 # No, so use CHS
+ mov %bp,%si # Disk packet
+ movb $0x42,%ah # BIOS: Extended
+ int $0x13 # read
+ retw # To caller
+read.1: push %dx # Save
+ movb $0x8,%ah # BIOS: Get drive
+ int $0x13 # parameters
+ movb %dh,%ch # Max head number
+ pop %dx # Restore
+ jc return # If error
+ andb $0x3f,%cl # Sectors per track
+ jz ereturn # If zero
+ cli # Disable interrupts
+ mov 0x8(%bp),%eax # Get LBA
+ push %dx # Save
+ movzbl %cl,%ebx # Divide by
+ xor %edx,%edx # sectors
+ div %ebx # per track
+ movb %ch,%bl # Max head number
+ movb %dl,%ch # Sector number
+ inc %bx # Divide by
+ xorb %dl,%dl # number
+ div %ebx # of heads
+ movb %dl,%bh # Head number
+ pop %dx # Restore
+ cmpl $0x3ff,%eax # Cylinder number supportable?
+ sti # Enable interrupts
+ ja ereturn # No, return an error
+ xchgb %al,%ah # Set up cylinder
+ rorb $0x2,%al # number
+ orb %ch,%al # Merge
+ inc %ax # sector
+ xchg %ax,%cx # number
+ movb %bh,%dh # Head number
+ subb %ah,%al # Sectors this track
+ mov 0x2(%bp),%ah # Blocks to read
+ cmpb %ah,%al # To read
+ jb read.2 # this
+#ifdef TRACK_AT_A_TIME
+ movb %ah,%al # track
+#else
+ movb $1,%al # one sector
+#endif
+read.2: mov $0x5,%di # Try count
+read.3: les 0x4(%bp),%bx # Transfer buffer
+ push %ax # Save
+ movb $0x2,%ah # BIOS: Read
+ int $0x13 # from disk
+ pop %bx # Restore
+ jnc read.4 # If success
+ dec %di # Retry?
+ jz read.6 # No
+ xorb %ah,%ah # BIOS: Reset
+ int $0x13 # disk system
+ xchg %bx,%ax # Block count
+ jmp read.3 # Continue
+read.4: movzbw %bl,%ax # Sectors read
+ add %ax,0x8(%bp) # Adjust
+ jnc read.5 # LBA,
+ incw 0xa(%bp) # transfer
+read.5: shlb %bl # buffer
+ add %bl,0x5(%bp) # pointer,
+ sub %al,0x2(%bp) # block count
+ ja read.1 # If not done
+read.6: retw # To caller
+
+/* Messages */
+
+msg_read: .asciz "Read"
+msg_part: .asciz "Boot"
+
+prompt: .asciz " error\r\n"
+
+flags: .byte FLAGS # Flags
+
+ .org PRT_OFF,0x90
+
+/* Partition table */
+
+ .fill 0x30,0x1,0x0
+part4: .byte 0x80, 0x00, 0x01, 0x00
+ .byte 0xa5, 0xfe, 0xff, 0xff
+ .byte 0x00, 0x00, 0x00, 0x00
+ .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh
+
+ .word 0xaa55 # Magic number
diff --git a/stand/i386/boot2/boot2.c b/stand/i386/boot2/boot2.c
new file mode 100644
index 0000000..cc5d76f
--- /dev/null
+++ b/stand/i386/boot2/boot2.c
@@ -0,0 +1,646 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/diskmbr.h>
+#include <sys/dirent.h>
+#include <sys/reboot.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+
+#include <stdarg.h>
+
+#include <a.out.h>
+
+#include <btxv86.h>
+
+#include "boot2.h"
+#include "lib.h"
+#include "paths.h"
+#include "rbx.h"
+
+/* Define to 0 to omit serial support */
+#ifndef SERIAL
+#define SERIAL 1
+#endif
+
+#define IO_KEYBOARD 1
+#define IO_SERIAL 2
+
+#if SERIAL
+#define DO_KBD (ioctrl & IO_KEYBOARD)
+#define DO_SIO (ioctrl & IO_SERIAL)
+#else
+#define DO_KBD (1)
+#define DO_SIO (0)
+#endif
+
+#define SECOND 18 /* Circa that many ticks in a second. */
+
+#define ARGS 0x900
+#define NOPT 14
+#define NDEV 3
+#define MEM_BASE 0x12
+#define MEM_EXT 0x15
+
+#define DRV_HARD 0x80
+#define DRV_MASK 0x7f
+
+#define TYPE_AD 0
+#define TYPE_DA 1
+#define TYPE_MAXHARD TYPE_DA
+#define TYPE_FD 2
+
+extern uint32_t _end;
+
+static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
+static const unsigned char flags[NOPT] = {
+ RBX_DUAL,
+ RBX_SERIAL,
+ RBX_ASKNAME,
+ RBX_CDROM,
+ RBX_CONFIG,
+ RBX_KDB,
+ RBX_GDB,
+ RBX_MUTE,
+ RBX_NOINTR,
+ RBX_PAUSE,
+ RBX_QUIET,
+ RBX_DFLTROOT,
+ RBX_SINGLE,
+ RBX_VERBOSE
+};
+
+static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
+static const unsigned char dev_maj[NDEV] = {30, 4, 2};
+
+static struct dsk {
+ unsigned drive;
+ unsigned type;
+ unsigned unit;
+ uint8_t slice;
+ uint8_t part;
+ unsigned start;
+ int init;
+} dsk;
+static char cmd[512], cmddup[512], knamebuf[1024];
+static const char *kname;
+uint32_t opts;
+static struct bootinfo bootinfo;
+#if SERIAL
+static int comspeed = SIOSPD;
+static uint8_t ioctrl = IO_KEYBOARD;
+#endif
+
+int main(void);
+void exit(int);
+static void load(void);
+static int parse(void);
+static int dskread(void *, unsigned, unsigned);
+static void printf(const char *,...);
+static void putchar(int);
+static int drvread(void *, unsigned, unsigned);
+static int keyhit(unsigned);
+static int xputc(int);
+static int xgetc(int);
+static inline int getc(int);
+
+static void memcpy(void *, const void *, int);
+static void
+memcpy(void *dst, const void *src, int len)
+{
+ const char *s = src;
+ char *d = dst;
+
+ while (len--)
+ *d++ = *s++;
+}
+
+static inline int
+strcmp(const char *s1, const char *s2)
+{
+ for (; *s1 == *s2 && *s1; s1++, s2++);
+ return (unsigned char)*s1 - (unsigned char)*s2;
+}
+
+#define UFS_SMALL_CGBASE
+#include "ufsread.c"
+
+static int
+xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
+{
+ if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
+ printf("Invalid %s\n", "format");
+ return -1;
+ }
+ return 0;
+}
+
+static inline void
+getstr(void)
+{
+ char *s;
+ int c;
+
+ s = cmd;
+ for (;;) {
+ switch (c = xgetc(0)) {
+ case 0:
+ break;
+ case '\177':
+ case '\b':
+ if (s > cmd) {
+ s--;
+ printf("\b \b");
+ }
+ break;
+ case '\n':
+ case '\r':
+ *s = 0;
+ return;
+ default:
+ if (s - cmd < sizeof(cmd) - 1)
+ *s++ = c;
+ putchar(c);
+ }
+ }
+}
+
+static inline void
+putc(int c)
+{
+ v86.addr = 0x10;
+ v86.eax = 0xe00 | (c & 0xff);
+ v86.ebx = 0x7;
+ v86int();
+}
+
+int
+main(void)
+{
+ uint8_t autoboot;
+ ufs_ino_t ino;
+ size_t nbyte;
+
+ dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+ v86.ctl = V86_FLAGS;
+ v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
+ dsk.drive = *(uint8_t *)PTOV(ARGS);
+ dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
+ dsk.unit = dsk.drive & DRV_MASK;
+ dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+
+ /* Process configuration file */
+
+ autoboot = 1;
+
+ if ((ino = lookup(PATH_CONFIG)) ||
+ (ino = lookup(PATH_DOTCONFIG))) {
+ nbyte = fsread(ino, cmd, sizeof(cmd) - 1);
+ cmd[nbyte] = '\0';
+ }
+
+ if (*cmd) {
+ memcpy(cmddup, cmd, sizeof(cmd));
+ if (parse())
+ autoboot = 0;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%s: %s", PATH_CONFIG, cmddup);
+ /* Do not process this command twice */
+ *cmd = 0;
+ }
+
+ /*
+ * Try to exec stage 3 boot loader. If interrupted by a keypress,
+ * or in case of failure, try to load a kernel directly instead.
+ */
+
+ if (!kname) {
+ kname = PATH_LOADER;
+ if (autoboot && !keyhit(3*SECOND)) {
+ load();
+ kname = PATH_KERNEL;
+ }
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ printf("\nFreeBSD/x86 boot\n"
+ "Default: %u:%s(%u,%c)%s\n"
+ "boot: ",
+ dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
+ 'a' + dsk.part, kname);
+ if (DO_SIO)
+ sio_flush();
+ if (!autoboot || keyhit(3*SECOND))
+ getstr();
+ else if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ putchar('\n');
+ autoboot = 0;
+ if (parse())
+ putchar('\a');
+ else
+ load();
+ }
+}
+
+/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
+void
+exit(int x)
+{
+}
+
+static void
+load(void)
+{
+ union {
+ struct exec ex;
+ Elf32_Ehdr eh;
+ } hdr;
+ static Elf32_Phdr ep[2];
+ static Elf32_Shdr es[2];
+ caddr_t p;
+ ufs_ino_t ino;
+ uint32_t addr;
+ int k;
+ uint8_t i, j;
+
+ if (!(ino = lookup(kname))) {
+ if (!ls)
+ printf("No %s\n", kname);
+ return;
+ }
+ if (xfsread(ino, &hdr, sizeof(hdr)))
+ return;
+
+ if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
+ addr = hdr.ex.a_entry & 0xffffff;
+ p = PTOV(addr);
+ fs_off = PAGE_SIZE;
+ if (xfsread(ino, p, hdr.ex.a_text))
+ return;
+ p += roundup2(hdr.ex.a_text, PAGE_SIZE);
+ if (xfsread(ino, p, hdr.ex.a_data))
+ return;
+ } else if (IS_ELF(hdr.eh)) {
+ fs_off = hdr.eh.e_phoff;
+ for (j = k = 0; k < hdr.eh.e_phnum && j < 2; k++) {
+ if (xfsread(ino, ep + j, sizeof(ep[0])))
+ return;
+ if (ep[j].p_type == PT_LOAD)
+ j++;
+ }
+ for (i = 0; i < 2; i++) {
+ p = PTOV(ep[i].p_paddr & 0xffffff);
+ fs_off = ep[i].p_offset;
+ if (xfsread(ino, p, ep[i].p_filesz))
+ return;
+ }
+ p += roundup2(ep[1].p_memsz, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
+ fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
+ (hdr.eh.e_shstrndx + 1);
+ if (xfsread(ino, &es, sizeof(es)))
+ return;
+ for (i = 0; i < 2; i++) {
+ *(Elf32_Word *)p = es[i].sh_size;
+ p += sizeof(es[i].sh_size);
+ fs_off = es[i].sh_offset;
+ if (xfsread(ino, p, es[i].sh_size))
+ return;
+ p += es[i].sh_size;
+ }
+ }
+ addr = hdr.eh.e_entry & 0xffffff;
+ bootinfo.bi_esymtab = VTOP(p);
+ } else {
+ printf("Invalid %s\n", "format");
+ return;
+ }
+
+ bootinfo.bi_kernelname = VTOP(kname);
+ bootinfo.bi_bios_dev = dsk.drive;
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
+ MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),
+ 0, 0, 0, VTOP(&bootinfo));
+}
+
+static int
+parse()
+{
+ char *arg = cmd;
+ char *ep, *p, *q;
+ const char *cp;
+ unsigned int drv;
+ int c, i, j;
+ size_t k;
+
+ while ((c = *arg++)) {
+ if (c == ' ' || c == '\t' || c == '\n')
+ continue;
+ for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+ ep = p;
+ if (*p)
+ *p++ = 0;
+ if (c == '-') {
+ while ((c = *arg++)) {
+ if (c == 'P') {
+ if (*(uint8_t *)PTOV(0x496) & 0x10) {
+ cp = "yes";
+ } else {
+ opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
+ cp = "no";
+ }
+ printf("Keyboard: %s\n", cp);
+ continue;
+#if SERIAL
+ } else if (c == 'S') {
+ j = 0;
+ while ((unsigned int)(i = *arg++ - '0') <= 9)
+ j = j * 10 + i;
+ if (j > 0 && i == -'0') {
+ comspeed = j;
+ break;
+ }
+ /* Fall through to error below ('S' not in optstr[]). */
+#endif
+ }
+ for (i = 0; c != optstr[i]; i++)
+ if (i == NOPT - 1)
+ return -1;
+ opts ^= OPT_SET(flags[i]);
+ }
+#if SERIAL
+ ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
+ OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
+ if (DO_SIO) {
+ if (sio_init(115200 / comspeed) != 0)
+ ioctrl &= ~IO_SERIAL;
+ }
+#endif
+ } else {
+ for (q = arg--; *q && *q != '('; q++);
+ if (*q) {
+ drv = -1;
+ if (arg[1] == ':') {
+ drv = *arg - '0';
+ if (drv > 9)
+ return (-1);
+ arg += 2;
+ }
+ if (q - arg != 2)
+ return -1;
+ for (i = 0; arg[0] != dev_nm[i][0] ||
+ arg[1] != dev_nm[i][1]; i++)
+ if (i == NDEV - 1)
+ return -1;
+ dsk.type = i;
+ arg += 3;
+ dsk.unit = *arg - '0';
+ if (arg[1] != ',' || dsk.unit > 9)
+ return -1;
+ arg += 2;
+ dsk.slice = WHOLE_DISK_SLICE;
+ if (arg[1] == ',') {
+ dsk.slice = *arg - '0' + 1;
+ if (dsk.slice > NDOSPART + 1)
+ return -1;
+ arg += 2;
+ }
+ if (arg[1] != ')')
+ return -1;
+ dsk.part = *arg - 'a';
+ if (dsk.part > 7)
+ return (-1);
+ arg += 2;
+ if (drv == -1)
+ drv = dsk.unit;
+ dsk.drive = (dsk.type <= TYPE_MAXHARD
+ ? DRV_HARD : 0) + drv;
+ dsk_meta = 0;
+ }
+ k = ep - arg;
+ if (k > 0) {
+ if (k >= sizeof(knamebuf))
+ return -1;
+ memcpy(knamebuf, arg, k + 1);
+ kname = knamebuf;
+ }
+ }
+ arg = p;
+ }
+ return 0;
+}
+
+static int
+dskread(void *buf, unsigned lba, unsigned nblk)
+{
+ struct dos_partition *dp;
+ struct disklabel *d;
+ char *sec;
+ unsigned i;
+ uint8_t sl;
+ const char *reason;
+
+ if (!dsk_meta) {
+ sec = dmadat->secbuf;
+ dsk.start = 0;
+ if (drvread(sec, DOSBBSECTOR, 1))
+ return -1;
+ dp = (void *)(sec + DOSPARTOFF);
+ sl = dsk.slice;
+ if (sl < BASE_SLICE) {
+ for (i = 0; i < NDOSPART; i++)
+ if (dp[i].dp_typ == DOSPTYP_386BSD &&
+ (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
+ sl = BASE_SLICE + i;
+ if (dp[i].dp_flag & 0x80 ||
+ dsk.slice == COMPATIBILITY_SLICE)
+ break;
+ }
+ if (dsk.slice == WHOLE_DISK_SLICE)
+ dsk.slice = sl;
+ }
+ if (sl != WHOLE_DISK_SLICE) {
+ if (sl != COMPATIBILITY_SLICE)
+ dp += sl - BASE_SLICE;
+ if (dp->dp_typ != DOSPTYP_386BSD) {
+ reason = "slice";
+ goto error;
+ }
+ dsk.start = dp->dp_start;
+ }
+ if (drvread(sec, dsk.start + LABELSECTOR, 1))
+ return -1;
+ d = (void *)(sec + LABELOFFSET);
+ if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
+ if (dsk.part != RAW_PART) {
+ reason = "label";
+ goto error;
+ }
+ } else {
+ if (!dsk.init) {
+ if (d->d_type == DTYPE_SCSI)
+ dsk.type = TYPE_DA;
+ dsk.init++;
+ }
+ if (dsk.part >= d->d_npartitions ||
+ !d->d_partitions[dsk.part].p_size) {
+ reason = "partition";
+ goto error;
+ }
+ dsk.start += d->d_partitions[dsk.part].p_offset;
+ dsk.start -= d->d_partitions[RAW_PART].p_offset;
+ }
+ }
+ return drvread(buf, dsk.start + lba, nblk);
+error:
+ printf("Invalid %s\n", reason);
+ return -1;
+}
+
+static void
+printf(const char *fmt,...)
+{
+ va_list ap;
+ static char buf[10];
+ char *s;
+ unsigned u;
+ int c;
+
+ va_start(ap, fmt);
+ while ((c = *fmt++)) {
+ if (c == '%') {
+ c = *fmt++;
+ switch (c) {
+ case 'c':
+ putchar(va_arg(ap, int));
+ continue;
+ case 's':
+ for (s = va_arg(ap, char *); *s; s++)
+ putchar(*s);
+ continue;
+ case 'u':
+ u = va_arg(ap, unsigned);
+ s = buf;
+ do
+ *s++ = '0' + u % 10U;
+ while (u /= 10U);
+ while (--s >= buf)
+ putchar(*s);
+ continue;
+ }
+ }
+ putchar(c);
+ }
+ va_end(ap);
+ return;
+}
+
+static void
+putchar(int c)
+{
+ if (c == '\n')
+ xputc('\r');
+ xputc(c);
+}
+
+static int
+drvread(void *buf, unsigned lba, unsigned nblk)
+{
+ static unsigned c = 0x2d5c7c2f;
+
+ if (!OPT_CHECK(RBX_QUIET)) {
+ xputc(c = c << 8 | c >> 24);
+ xputc('\b');
+ }
+ v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
+ v86.addr = XREADORG; /* call to xread in boot1 */
+ v86.es = VTOPSEG(buf);
+ v86.eax = lba;
+ v86.ebx = VTOPOFF(buf);
+ v86.ecx = lba >> 16;
+ v86.edx = nblk << 8 | dsk.drive;
+ v86int();
+ v86.ctl = V86_FLAGS;
+ if (V86_CY(v86.efl)) {
+ printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+keyhit(unsigned ticks)
+{
+ uint32_t t0, t1;
+
+ if (OPT_CHECK(RBX_NOINTR))
+ return 0;
+ t0 = 0;
+ for (;;) {
+ if (xgetc(1))
+ return 1;
+ t1 = *(uint32_t *)PTOV(0x46c);
+ if (!t0)
+ t0 = t1;
+ if ((uint32_t)(t1 - t0) >= ticks)
+ return 0;
+ }
+}
+
+static int
+xputc(int c)
+{
+ if (DO_KBD)
+ putc(c);
+ if (DO_SIO)
+ sio_putc(c);
+ return c;
+}
+
+static int
+getc(int fn)
+{
+ v86.addr = 0x16;
+ v86.eax = fn << 8;
+ v86int();
+ return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
+}
+
+static int
+xgetc(int fn)
+{
+ if (OPT_CHECK(RBX_NOINTR))
+ return 0;
+ for (;;) {
+ if (DO_KBD && getc(1))
+ return fn ? 1 : getc(0);
+ if (DO_SIO && sio_ischar())
+ return fn ? 1 : sio_getc();
+ if (fn)
+ return 0;
+ }
+}
diff --git a/stand/i386/boot2/lib.h b/stand/i386/boot2/lib.h
new file mode 100644
index 0000000..d8d3317
--- /dev/null
+++ b/stand/i386/boot2/lib.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+int sio_init(int) __attribute__((regparm (3)));
+int sio_flush(void);
+void sio_putc(int) __attribute__((regparm (3)));
+int sio_getc(void);
+int sio_ischar(void);
diff --git a/stand/i386/boot2/sio.S b/stand/i386/boot2/sio.S
new file mode 100644
index 0000000..ca9d0a2
--- /dev/null
+++ b/stand/i386/boot2/sio.S
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+ .set SIO_PRT,SIOPRT # Base port
+ .set SIO_FMT,SIOFMT # 8N1
+
+ .globl sio_init
+ .globl sio_flush
+ .globl sio_putc
+ .globl sio_getc
+ .globl sio_ischar
+
+/* int sio_init(int div) */
+
+sio_init: pushl %eax
+ movw $SIO_PRT+0x3,%dx # Data format reg
+ movb $SIO_FMT|0x80,%al # Set format
+ outb %al,(%dx) # and DLAB
+ subb $0x3,%dl # Divisor latch reg
+ popl %eax
+ outw %ax,(%dx) # BPS
+ movw $SIO_PRT+0x3,%dx # Data format reg
+ movb $SIO_FMT,%al # Clear
+ outb %al,(%dx) # DLAB
+ incl %edx # Modem control reg
+ movb $0x3,%al # Set RTS,
+ outb %al,(%dx) # DTR
+ incl %edx # Line status reg
+ # Fallthrough
+
+/* int sio_flush(void) */
+
+sio_flush: xorl %ecx,%ecx # Timeout
+ movb $0x80,%ch # counter
+sio_flush.1: call sio_ischar # Check for character
+ jz sio_flush.2 # Till none
+ loop sio_flush.1 # or counter is zero
+ movb $1, %al # Exhausted all tries
+sio_flush.2: ret # To caller
+
+/* void sio_putc(int c) */
+
+sio_putc: pushl %eax
+ movw $SIO_PRT+0x5,%dx # Line status reg
+ xor %ecx,%ecx # Timeout
+ movb $0x40,%ch # counter
+sio_putc.1: inb (%dx),%al # Transmitter
+ testb $0x20,%al # buffer empty?
+ loopz sio_putc.1 # No
+ jz sio_putc.2 # If timeout
+ popl %eax # Get the character
+ subb $0x5,%dl # Transmitter hold reg
+ outb %al,(%dx) # Write character
+sio_putc.2: ret # To caller
+
+/* int sio_getc(void) */
+
+sio_getc: call sio_ischar # Character available?
+ jz sio_getc # No
+sio_getc.1: subb $0x5,%dl # Receiver buffer reg
+ inb (%dx),%al # Read character
+ ret # To caller
+
+/* int sio_ischar(void) */
+
+sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register
+ xorl %eax,%eax # Zero
+ inb (%dx),%al # Received data
+ andb $0x1,%al # ready?
+ ret # To caller
diff --git a/stand/i386/btx/Makefile b/stand/i386/btx/Makefile
new file mode 100644
index 0000000..39f78ed
--- /dev/null
+++ b/stand/i386/btx/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= btx btxldr lib
+
+.include <bsd.subdir.mk>
diff --git a/stand/i386/btx/Makefile.inc b/stand/i386/btx/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/stand/i386/btx/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/stand/i386/btx/btx/Makefile b/stand/i386/btx/btx/Makefile
new file mode 100644
index 0000000..f42e20d
--- /dev/null
+++ b/stand/i386/btx/btx/Makefile
@@ -0,0 +1,35 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= btx
+INTERNALPROG=
+MAN=
+SRCS= btx.S
+
+.if defined(BOOT_BTX_NOHANG)
+BOOT_BTX_FLAGS=0x1
+.else
+BOOT_BTX_FLAGS=0x0
+.endif
+
+CFLAGS+=-DBTX_FLAGS=${BOOT_BTX_FLAGS}
+CFLAGS+=-I${BOOTSRC}/i386/common
+
+.if defined(BTX_SERIAL)
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+CFLAGS+=-DBTX_SERIAL -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} -DSIOSPD=${BOOT_COMCONSOLE_SPEED}
+.endif
+
+ORG= 0x9000
+
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.btx.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/btx/btx/Makefile.depend b/stand/i386/btx/btx/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/btx/btx/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/btx/btx/btx.S b/stand/i386/btx/btx/btx.S
new file mode 100644
index 0000000..87d09a5
--- /dev/null
+++ b/stand/i386/btx/btx/btx.S
@@ -0,0 +1,1082 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+#include <bootargs.h>
+
+/*
+ * Memory layout.
+ */
+ .set MEM_BTX,0x1000 # Start of BTX memory
+ .set MEM_ESP0,0x1800 # Supervisor stack
+ .set MEM_BUF,0x1800 # Scratch buffer
+ .set MEM_ESPR,0x5e00 # Real mode stack
+ .set MEM_IDT,0x5e00 # IDT
+ .set MEM_TSS,0x5f98 # TSS
+ .set MEM_MAP,0x6000 # I/O bit map
+ .set MEM_TSS_END,0x7fff # End of TSS
+ .set MEM_ORG,0x9000 # BTX code
+ .set MEM_USR,0xa000 # Start of user memory
+/*
+ * Paging control.
+ */
+ .set PAG_SIZ,0x1000 # Page size
+ .set PAG_CNT,0x1000 # Pages to map
+/*
+ * Fields in %eflags.
+ */
+ .set PSL_RESERVED_DEFAULT,0x00000002
+ .set PSL_T,0x00000100 # Trap flag
+ .set PSL_I,0x00000200 # Interrupt enable flag
+ .set PSL_D,0x00000400 # String instruction direction
+ .set PSL_NT,0x00004000 # Nested task flag
+ .set PSL_VM,0x00020000 # Virtual 8086 mode flag
+ .set PSL_AC,0x00040000 # Alignment check flag
+/*
+ * Segment selectors.
+ */
+ .set SEL_SCODE,0x8 # Supervisor code
+ .set SEL_SDATA,0x10 # Supervisor data
+ .set SEL_RCODE,0x18 # Real mode code
+ .set SEL_RDATA,0x20 # Real mode data
+ .set SEL_UCODE,0x28|3 # User code
+ .set SEL_UDATA,0x30|3 # User data
+ .set SEL_TSS,0x38 # TSS
+/*
+ * Task state segment fields.
+ */
+ .set TSS_ESP0,0x4 # PL 0 ESP
+ .set TSS_SS0,0x8 # PL 0 SS
+ .set TSS_MAP,0x66 # I/O bit map base
+/*
+ * System calls.
+ */
+ .set SYS_EXIT,0x0 # Exit
+ .set SYS_EXEC,0x1 # Exec
+/*
+ * Fields in V86 interface structure.
+ */
+ .set V86_CTL,0x0 # Control flags
+ .set V86_ADDR,0x4 # Int number/address
+ .set V86_ES,0x8 # V86 ES
+ .set V86_DS,0xc # V86 DS
+ .set V86_FS,0x10 # V86 FS
+ .set V86_GS,0x14 # V86 GS
+/*
+ * V86 control flags.
+ */
+ .set V86F_ADDR,0x10000 # Segment:offset address
+ .set V86F_CALLF,0x20000 # Emulate far call
+ .set V86F_FLAGS,0x40000 # Return flags
+/*
+ * Dump format control bytes.
+ */
+ .set DMP_X16,0x1 # Word
+ .set DMP_X32,0x2 # Long
+ .set DMP_MEM,0x4 # Memory
+ .set DMP_EOL,0x8 # End of line
+/*
+ * Screen defaults and assumptions.
+ */
+ .set SCR_MAT,0x7 # Mode/attribute
+ .set SCR_COL,0x50 # Columns per row
+ .set SCR_ROW,0x19 # Rows per screen
+/*
+ * BIOS Data Area locations.
+ */
+ .set BDA_MEM,0x413 # Free memory
+ .set BDA_SCR,0x449 # Video mode
+ .set BDA_POS,0x450 # Cursor position
+ .set BDA_BOOT,0x472 # Boot howto flag
+/*
+ * Derivations, for brevity.
+ */
+ .set _ESP0H,MEM_ESP0>>0x8 # Byte 1 of ESP0
+ .set _TSSIO,MEM_MAP-MEM_TSS # TSS I/O base
+ .set _TSSLM,MEM_TSS_END-MEM_TSS # TSS limit
+ .set _IDTLM,MEM_TSS-MEM_IDT-1 # IDT limit
+/*
+ * Code segment.
+ */
+ .globl start
+ .code16
+start: # Start of code
+/*
+ * BTX header.
+ */
+btx_hdr: .byte 0xeb # Machine ID
+ .byte 0xe # Header size
+ .ascii "BTX" # Magic
+ .byte 0x1 # Major version
+ .byte 0x2 # Minor version
+ .byte BTX_FLAGS # Flags
+ .word PAG_CNT-MEM_ORG>>0xc # Paging control
+ .word break-start # Text size
+ .long 0x0 # Entry address
+/*
+ * Initialization routine.
+ */
+init: cli # Disable interrupts
+ xor %ax,%ax # Zero/segment
+ mov %ax,%ss # Set up
+ mov $MEM_ESP0,%sp # stack
+ mov %ax,%es # Address
+ mov %ax,%ds # data
+ pushl $0x2 # Clear
+ popfl # flags
+/*
+ * Initialize memory.
+ */
+ mov $MEM_IDT,%di # Memory to initialize
+ mov $(MEM_ORG-MEM_IDT)/2,%cx # Words to zero
+ rep # Zero-fill
+ stosw # memory
+/*
+ * Update real mode IDT for reflecting hardware interrupts.
+ */
+ mov $intr20,%bx # Address first handler
+ mov $0x10,%cx # Number of handlers
+ mov $0x20*4,%di # First real mode IDT entry
+init.0: mov %bx,(%di) # Store IP
+ inc %di # Address next
+ inc %di # entry
+ stosw # Store CS
+ add $4,%bx # Next handler
+ loop init.0 # Next IRQ
+/*
+ * Create IDT.
+ */
+ mov $MEM_IDT,%di
+ mov $idtctl,%si # Control string
+init.1: lodsb # Get entry
+ cbw # count
+ xchg %ax,%cx # as word
+ jcxz init.4 # If done
+ lodsb # Get segment
+ xchg %ax,%dx # P:DPL:type
+ lodsw # Get control
+ xchg %ax,%bx # set
+ lodsw # Get handler offset
+ mov $SEL_SCODE,%dh # Segment selector
+init.2: shr %bx # Handle this int?
+ jnc init.3 # No
+ mov %ax,(%di) # Set handler offset
+ mov %dh,0x2(%di) # and selector
+ mov %dl,0x5(%di) # Set P:DPL:type
+ add $0x4,%ax # Next handler
+init.3: lea 0x8(%di),%di # Next entry
+ loop init.2 # Till set done
+ jmp init.1 # Continue
+/*
+ * Initialize TSS.
+ */
+init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0
+ movb $SEL_SDATA,TSS_SS0(%di) # Set SS0
+ movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base
+/*
+ * Bring up the system.
+ */
+ mov $0x2820,%bx # Set protected mode
+ callw setpic # IRQ offsets
+ lidt idtdesc # Set IDT
+ lgdt gdtdesc # Set GDT
+ mov %cr0,%eax # Switch to protected
+ inc %ax # mode
+ mov %eax,%cr0 #
+ ljmp $SEL_SCODE,$init.8 # To 32-bit code
+ .code32
+init.8: xorl %ecx,%ecx # Zero
+ movb $SEL_SDATA,%cl # To 32-bit
+ movw %cx,%ss # stack
+/*
+ * Launch user task.
+ */
+ movb $SEL_TSS,%cl # Set task
+ ltr %cx # register
+ movl $MEM_USR,%edx # User base address
+ movzwl %ss:BDA_MEM,%eax # Get free memory
+ shll $0xa,%eax # To bytes
+ subl $ARGSPACE,%eax # Less arg space
+ subl %edx,%eax # Less base
+ movb $SEL_UDATA,%cl # User data selector
+ pushl %ecx # Set SS
+ pushl %eax # Set ESP
+ push $0x202 # Set flags (IF set)
+ push $SEL_UCODE # Set CS
+ pushl btx_hdr+0xc # Set EIP
+ pushl %ecx # Set GS
+ pushl %ecx # Set FS
+ pushl %ecx # Set DS
+ pushl %ecx # Set ES
+ pushl %edx # Set EAX
+ movb $0x7,%cl # Set remaining
+init.9: push $0x0 # general
+ loop init.9 # registers
+#ifdef BTX_SERIAL
+ call sio_init # setup the serial console
+#endif
+ popa # and initialize
+ popl %es # Initialize
+ popl %ds # user
+ popl %fs # segment
+ popl %gs # registers
+ iret # To user mode
+/*
+ * Exit routine.
+ */
+exit: cli # Disable interrupts
+ movl $MEM_ESP0,%esp # Clear stack
+/*
+ * Turn off paging.
+ */
+ movl %cr0,%eax # Get CR0
+ andl $~0x80000000,%eax # Disable
+ movl %eax,%cr0 # paging
+ xorl %ecx,%ecx # Zero
+ movl %ecx,%cr3 # Flush TLB
+/*
+ * Restore the GDT in case we caught a kernel trap.
+ */
+ lgdt %cs:gdtdesc # Set GDT
+/*
+ * To 16 bits.
+ */
+ ljmpw $SEL_RCODE,$exit.1 # Reload CS
+ .code16
+exit.1: mov $SEL_RDATA,%cl # 16-bit selector
+ mov %cx,%ss # Reload SS
+ mov %cx,%ds # Load
+ mov %cx,%es # remaining
+ mov %cx,%fs # segment
+ mov %cx,%gs # registers
+/*
+ * To real-address mode.
+ */
+ dec %ax # Switch to
+ mov %eax,%cr0 # real mode
+ ljmp $0x0,$exit.2 # Reload CS
+exit.2: xor %ax,%ax # Real mode segment
+ mov %ax,%ss # Reload SS
+ mov %ax,%ds # Address data
+ mov $0x7008,%bx # Set real mode
+ callw setpic # IRQ offsets
+ lidt ivtdesc # Set IVT
+/*
+ * Reboot or await reset.
+ */
+ sti # Enable interrupts
+ testb $0x1,btx_hdr+0x7 # Reboot?
+exit.3: jz exit.3 # No
+ movw $0x1234, BDA_BOOT # Do a warm boot
+ ljmp $0xf000,$0xfff0 # reboot the machine
+/*
+ * Set IRQ offsets by reprogramming 8259A PICs.
+ */
+setpic: in $0x21,%al # Save master
+ push %ax # IMR
+ in $0xa1,%al # Save slave
+ push %ax # IMR
+ movb $0x11,%al # ICW1 to
+ outb %al,$0x20 # master,
+ outb %al,$0xa0 # slave
+ movb %bl,%al # ICW2 to
+ outb %al,$0x21 # master
+ movb %bh,%al # ICW2 to
+ outb %al,$0xa1 # slave
+ movb $0x4,%al # ICW3 to
+ outb %al,$0x21 # master
+ movb $0x2,%al # ICW3 to
+ outb %al,$0xa1 # slave
+ movb $0x1,%al # ICW4 to
+ outb %al,$0x21 # master,
+ outb %al,$0xa1 # slave
+ pop %ax # Restore slave
+ outb %al,$0xa1 # IMR
+ pop %ax # Restore master
+ outb %al,$0x21 # IMR
+ retw # To caller
+ .code32
+/*
+ * Exception jump table.
+ */
+intx00: push $0x0 # Int 0x0: #DE
+ jmp ex_noc # Divide error
+ push $0x1 # Int 0x1: #DB
+ jmp ex_noc # Debug
+ push $0x3 # Int 0x3: #BP
+ jmp ex_noc # Breakpoint
+ push $0x4 # Int 0x4: #OF
+ jmp ex_noc # Overflow
+ push $0x5 # Int 0x5: #BR
+ jmp ex_noc # BOUND range exceeded
+ push $0x6 # Int 0x6: #UD
+ jmp ex_noc # Invalid opcode
+ push $0x7 # Int 0x7: #NM
+ jmp ex_noc # Device not available
+ push $0x8 # Int 0x8: #DF
+ jmp except # Double fault
+ push $0xa # Int 0xa: #TS
+ jmp except # Invalid TSS
+ push $0xb # Int 0xb: #NP
+ jmp except # Segment not present
+ push $0xc # Int 0xc: #SS
+ jmp except # Stack segment fault
+ push $0xd # Int 0xd: #GP
+ jmp except # General protection
+ push $0xe # Int 0xe: #PF
+ jmp except # Page fault
+intx10: push $0x10 # Int 0x10: #MF
+ jmp ex_noc # Floating-point error
+/*
+ * Save a zero error code.
+ */
+ex_noc: pushl (%esp,1) # Duplicate int no
+ movb $0x0,0x4(%esp,1) # Fake error code
+/*
+ * Handle exception.
+ */
+except: cld # String ops inc
+ pushl %ds # Save
+ pushl %es # most
+ pusha # registers
+ pushl %gs # Set GS
+ pushl %fs # Set FS
+ pushl %ds # Set DS
+ pushl %es # Set ES
+ cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode?
+ jne except.1 # No
+ pushl %ss # Set SS
+ jmp except.2 # Join common code
+except.1: pushl 0x50(%esp,1) # Set SS
+except.2: pushl 0x50(%esp,1) # Set ESP
+ push $SEL_SDATA # Set up
+ popl %ds # to
+ pushl %ds # address
+ popl %es # data
+ movl %esp,%ebx # Stack frame
+ movl $dmpfmt,%esi # Dump format string
+ movl $MEM_BUF,%edi # Buffer
+ pushl %edi # Dump to
+ call dump # buffer
+ popl %esi # and
+ call putstr # display
+ leal 0x18(%esp,1),%esp # Discard frame
+ popa # Restore
+ popl %es # registers
+ popl %ds # saved
+ cmpb $0x3,(%esp,1) # Breakpoint?
+ je except.3 # Yes
+ cmpb $0x1,(%esp,1) # Debug?
+ jne except.2a # No
+ testl $PSL_T,0x10(%esp,1) # Trap flag set?
+ jnz except.3 # Yes
+except.2a: jmp exit # Exit
+except.3: leal 0x8(%esp,1),%esp # Discard err, int no
+ iret # From interrupt
+
+/*
+ * Reboot the machine by setting the reboot flag and exiting
+ */
+reboot: orb $0x1,btx_hdr+0x7 # Set the reboot flag
+ jmp exit # Terminate BTX and reboot
+
+/*
+ * Protected Mode Hardware interrupt jump table.
+ */
+intx20: push $0x8 # Int 0x20: IRQ0
+ jmp int_hw # V86 int 0x8
+ push $0x9 # Int 0x21: IRQ1
+ jmp int_hw # V86 int 0x9
+ push $0xa # Int 0x22: IRQ2
+ jmp int_hw # V86 int 0xa
+ push $0xb # Int 0x23: IRQ3
+ jmp int_hw # V86 int 0xb
+ push $0xc # Int 0x24: IRQ4
+ jmp int_hw # V86 int 0xc
+ push $0xd # Int 0x25: IRQ5
+ jmp int_hw # V86 int 0xd
+ push $0xe # Int 0x26: IRQ6
+ jmp int_hw # V86 int 0xe
+ push $0xf # Int 0x27: IRQ7
+ jmp int_hw # V86 int 0xf
+ push $0x70 # Int 0x28: IRQ8
+ jmp int_hw # V86 int 0x70
+ push $0x71 # Int 0x29: IRQ9
+ jmp int_hw # V86 int 0x71
+ push $0x72 # Int 0x2a: IRQ10
+ jmp int_hw # V86 int 0x72
+ push $0x73 # Int 0x2b: IRQ11
+ jmp int_hw # V86 int 0x73
+ push $0x74 # Int 0x2c: IRQ12
+ jmp int_hw # V86 int 0x74
+ push $0x75 # Int 0x2d: IRQ13
+ jmp int_hw # V86 int 0x75
+ push $0x76 # Int 0x2e: IRQ14
+ jmp int_hw # V86 int 0x76
+ push $0x77 # Int 0x2f: IRQ15
+ jmp int_hw # V86 int 0x77
+
+/*
+ * Invoke real mode interrupt/function call from user mode with arguments.
+ */
+intx31: pushl $-1 # Dummy int no for btx_v86
+/*
+ * Invoke real mode interrupt/function call from protected mode.
+ *
+ * We place a trampoline on the user stack that will return to rret_tramp
+ * which will reenter protected mode and then finally return to the user
+ * client.
+ *
+ * Kernel frame %esi points to: Real mode stack frame at MEM_ESPR:
+ *
+ * -0x00 user %ss -0x04 kernel %esp (with full frame)
+ * -0x04 user %esp -0x08 btx_v86 pointer
+ * -0x08 user %eflags -0x0c flags (only used if interrupt)
+ * -0x0c user %cs -0x10 real mode CS:IP return trampoline
+ * -0x10 user %eip -0x12 real mode flags
+ * -0x14 int no -0x16 real mode CS:IP (target)
+ * -0x18 %eax
+ * -0x1c %ecx
+ * -0x20 %edx
+ * -0x24 %ebx
+ * -0x28 %esp
+ * -0x2c %ebp
+ * -0x30 %esi
+ * -0x34 %edi
+ * -0x38 %gs
+ * -0x3c %fs
+ * -0x40 %ds
+ * -0x44 %es
+ * -0x48 zero %eax (hardware int only)
+ * -0x4c zero %ecx (hardware int only)
+ * -0x50 zero %edx (hardware int only)
+ * -0x54 zero %ebx (hardware int only)
+ * -0x58 zero %esp (hardware int only)
+ * -0x5c zero %ebp (hardware int only)
+ * -0x60 zero %esi (hardware int only)
+ * -0x64 zero %edi (hardware int only)
+ * -0x68 zero %gs (hardware int only)
+ * -0x6c zero %fs (hardware int only)
+ * -0x70 zero %ds (hardware int only)
+ * -0x74 zero %es (hardware int only)
+ */
+int_hw: cld # String ops inc
+ pusha # Save gp regs
+ pushl %gs # Save
+ pushl %fs # seg
+ pushl %ds # regs
+ pushl %es
+ push $SEL_SDATA # Set up
+ popl %ds # to
+ pushl %ds # address
+ popl %es # data
+ leal 0x44(%esp,1),%esi # Base of frame
+ movl %esp,MEM_ESPR-0x04 # Save kernel stack pointer
+ movl -0x14(%esi),%eax # Get Int no
+ cmpl $-1,%eax # Hardware interrupt?
+ jne intusr.1 # Yes
+/*
+ * v86 calls save the btx_v86 pointer on the real mode stack and read
+ * the address and flags from the btx_v86 structure. For interrupt
+ * handler invocations (VM86 INTx requests), disable interrupts,
+ * tracing, and alignment checking while the handler runs.
+ */
+ movl $MEM_USR,%ebx # User base
+ movl %ebx,%edx # address
+ addl -0x4(%esi),%ebx # User ESP
+ movl (%ebx),%ebp # btx_v86 pointer
+ addl %ebp,%edx # Flatten btx_v86 ptr
+ movl %edx,MEM_ESPR-0x08 # Save btx_v86 ptr
+ movl V86_ADDR(%edx),%eax # Get int no/address
+ movl V86_CTL(%edx),%edx # Get control flags
+ movl -0x08(%esi),%ebx # Save user flags in %ebx
+ testl $V86F_ADDR,%edx # Segment:offset?
+ jnz intusr.4 # Yes
+ andl $~(PSL_I|PSL_T|PSL_AC),%ebx # Disable interrupts, tracing,
+ # and alignment checking for
+ # interrupt handler
+ jmp intusr.3 # Skip hardware interrupt
+/*
+ * Hardware interrupts store a NULL btx_v86 pointer and use the
+ * address (interrupt number) from the stack with empty flags. Also,
+ * push a dummy frame of zeros onto the stack for all the general
+ * purpose and segment registers and clear %eflags. This gives the
+ * hardware interrupt handler a clean slate.
+ */
+intusr.1: xorl %edx,%edx # Control flags
+ movl %edx,MEM_ESPR-0x08 # NULL btx_v86 ptr
+ movl $12,%ecx # Frame is 12 dwords
+intusr.2: pushl $0x0 # Fill frame
+ loop intusr.2 # with zeros
+ movl $PSL_RESERVED_DEFAULT,%ebx # Set clean %eflags
+/*
+ * Look up real mode IDT entry for hardware interrupts and VM86 INTx
+ * requests.
+ */
+intusr.3: shll $0x2,%eax # Scale
+ movl (%eax),%eax # Load int vector
+ jmp intusr.5 # Skip CALLF test
+/*
+ * Panic if V86F_CALLF isn't set with V86F_ADDR.
+ */
+intusr.4: testl $V86F_CALLF,%edx # Far call?
+ jnz intusr.5 # Ok
+ movl %edx,0x30(%esp,1) # Place VM86 flags in int no
+ movl $badvm86,%esi # Display bad
+ call putstr # VM86 call
+ popl %es # Restore
+ popl %ds # seg
+ popl %fs # regs
+ popl %gs
+ popal # Restore gp regs
+ jmp ex_noc # Panic
+/*
+ * %eax now holds the segment:offset of the function.
+ * %ebx now holds the %eflags to pass to real mode.
+ * %edx now holds the V86F_* flags.
+ */
+intusr.5: movw %bx,MEM_ESPR-0x12 # Pass user flags to real mode
+ # target
+/*
+ * If this is a v86 call, copy the seg regs out of the btx_v86 structure.
+ */
+ movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr
+ jecxz intusr.6 # Skip for hardware ints
+ leal -0x44(%esi),%edi # %edi => kernel stack seg regs
+ pushl %esi # Save
+ leal V86_ES(%ecx),%esi # %esi => btx_v86 seg regs
+ movl $4,%ecx # Copy seg regs
+ rep # from btx_v86
+ movsl # to kernel stack
+ popl %esi # Restore
+intusr.6: movl -0x08(%esi),%ebx # Copy user flags to real
+ movl %ebx,MEM_ESPR-0x0c # mode return trampoline
+ movl $rret_tramp,%ebx # Set return trampoline
+ movl %ebx,MEM_ESPR-0x10 # CS:IP
+ movl %eax,MEM_ESPR-0x16 # Real mode target CS:IP
+ ljmpw $SEL_RCODE,$intusr.7 # Change to 16-bit segment
+ .code16
+intusr.7: movl %cr0,%eax # Leave
+ dec %al # protected
+ movl %eax,%cr0 # mode
+ ljmpw $0x0,$intusr.8
+intusr.8: xorw %ax,%ax # Reset %ds
+ movw %ax,%ds # and
+ movw %ax,%ss # %ss
+ lidt ivtdesc # Set IVT
+ popl %es # Restore
+ popl %ds # seg
+ popl %fs # regs
+ popl %gs
+ popal # Restore gp regs
+ movw $MEM_ESPR-0x16,%sp # Switch to real mode stack
+ iret # Call target routine
+/*
+ * For the return to real mode we setup a stack frame like this on the real
+ * mode stack. Note that callf calls won't pop off the flags, but we just
+ * ignore that by repositioning %sp to be just above the btx_v86 pointer
+ * so it is aligned. The stack is relative to MEM_ESPR.
+ *
+ * -0x04 kernel %esp
+ * -0x08 btx_v86
+ * -0x0c %eax
+ * -0x10 %ecx
+ * -0x14 %edx
+ * -0x18 %ebx
+ * -0x1c %esp
+ * -0x20 %ebp
+ * -0x24 %esi
+ * -0x28 %edi
+ * -0x2c %gs
+ * -0x30 %fs
+ * -0x34 %ds
+ * -0x38 %es
+ * -0x3c %eflags
+ */
+rret_tramp: movw $MEM_ESPR-0x08,%sp # Reset stack pointer
+ pushal # Save gp regs
+ pushl %gs # Save
+ pushl %fs # seg
+ pushl %ds # regs
+ pushl %es
+ pushfl # Save %eflags
+ pushl $PSL_RESERVED_DEFAULT|PSL_D # Use clean %eflags with
+ popfl # string ops dec
+ xorw %ax,%ax # Reset seg
+ movw %ax,%ds # regs
+ movw %ax,%es # (%ss is already 0)
+ lidt idtdesc # Set IDT
+ lgdt gdtdesc # Set GDT
+ mov %cr0,%eax # Switch to protected
+ inc %ax # mode
+ mov %eax,%cr0 #
+ ljmp $SEL_SCODE,$rret_tramp.1 # To 32-bit code
+ .code32
+rret_tramp.1: xorl %ecx,%ecx # Zero
+ movb $SEL_SDATA,%cl # Setup
+ movw %cx,%ss # 32-bit
+ movw %cx,%ds # seg
+ movw %cx,%es # regs
+ movl MEM_ESPR-0x04,%esp # Switch to kernel stack
+ leal 0x44(%esp,1),%esi # Base of frame
+ andb $~0x2,tss_desc+0x5 # Clear TSS busy
+ movb $SEL_TSS,%cl # Set task
+ ltr %cx # register
+/*
+ * Now we are back in protected mode. The kernel stack frame set up
+ * before entering real mode is still intact. For hardware interrupts,
+ * leave the frame unchanged.
+ */
+ cmpl $0,MEM_ESPR-0x08 # Leave saved regs unchanged
+ jz rret_tramp.3 # for hardware ints
+/*
+ * For V86 calls, copy the registers off of the real mode stack onto
+ * the kernel stack as we want their updated values. Also, initialize
+ * the segment registers on the kernel stack.
+ *
+ * Note that the %esp in the kernel stack after this is garbage, but popa
+ * ignores it, so we don't have to fix it up.
+ */
+ leal -0x18(%esi),%edi # Kernel stack GP regs
+ pushl %esi # Save
+ movl $MEM_ESPR-0x0c,%esi # Real mode stack GP regs
+ movl $8,%ecx # Copy GP regs from
+ rep # real mode stack
+ movsl # to kernel stack
+ movl $SEL_UDATA,%eax # Selector for data seg regs
+ movl $4,%ecx # Initialize %ds,
+ rep # %es, %fs, and
+ stosl # %gs
+/*
+ * For V86 calls, copy the saved seg regs on the real mode stack back
+ * over to the btx_v86 structure. Also, conditionally update the
+ * saved eflags on the kernel stack based on the flags from the user.
+ */
+ movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr
+ leal V86_GS(%ecx),%edi # %edi => btx_v86 seg regs
+ leal MEM_ESPR-0x2c,%esi # %esi => real mode seg regs
+ xchgl %ecx,%edx # Save btx_v86 ptr
+ movl $4,%ecx # Copy seg regs
+ rep # from real mode stack
+ movsl # to btx_v86
+ popl %esi # Restore
+ movl V86_CTL(%edx),%edx # Read V86 control flags
+ testl $V86F_FLAGS,%edx # User wants flags?
+ jz rret_tramp.3 # No
+ movl MEM_ESPR-0x3c,%eax # Read real mode flags
+ andl $~(PSL_T|PSL_NT),%eax # Clear unsafe flags
+ movw %ax,-0x08(%esi) # Update user flags (low 16)
+/*
+ * Return to the user task
+ */
+rret_tramp.3: popl %es # Restore
+ popl %ds # seg
+ popl %fs # regs
+ popl %gs
+ popal # Restore gp regs
+ addl $4,%esp # Discard int no
+ iret # Return to user mode
+
+/*
+ * System Call.
+ */
+intx30: cmpl $SYS_EXEC,%eax # Exec system call?
+ jne intx30.1 # No
+ pushl %ss # Set up
+ popl %es # all
+ pushl %es # segment
+ popl %ds # registers
+ pushl %ds # for the
+ popl %fs # program
+ pushl %fs # we're
+ popl %gs # invoking
+ movl $MEM_USR,%eax # User base address
+ addl 0xc(%esp,1),%eax # Change to user
+ leal 0x4(%eax),%esp # stack
+ popl %eax # Call
+ call *%eax # program
+intx30.1: orb $0x1,%ss:btx_hdr+0x7 # Flag reboot
+ jmp exit # Exit
+/*
+ * Dump structure [EBX] to [EDI], using format string [ESI].
+ */
+dump.0: stosb # Save char
+dump: lodsb # Load char
+ testb %al,%al # End of string?
+ jz dump.10 # Yes
+ testb $0x80,%al # Control?
+ jz dump.0 # No
+ movb %al,%ch # Save control
+ movb $'=',%al # Append
+ stosb # '='
+ lodsb # Get offset
+ pushl %esi # Save
+ movsbl %al,%esi # To
+ addl %ebx,%esi # pointer
+ testb $DMP_X16,%ch # Dump word?
+ jz dump.1 # No
+ lodsw # Get and
+ call hex16 # dump it
+dump.1: testb $DMP_X32,%ch # Dump long?
+ jz dump.2 # No
+ lodsl # Get and
+ call hex32 # dump it
+dump.2: testb $DMP_MEM,%ch # Dump memory?
+ jz dump.8 # No
+ pushl %ds # Save
+ testl $PSL_VM,0x50(%ebx) # V86 mode?
+ jnz dump.3 # Yes
+ verr 0x4(%esi) # Readable selector?
+ jnz dump.3 # No
+ ldsl (%esi),%esi # Load pointer
+ jmp dump.4 # Join common code
+dump.3: lodsl # Set offset
+ xchgl %eax,%edx # Save
+ lodsl # Get segment
+ shll $0x4,%eax # * 0x10
+ addl %edx,%eax # + offset
+ xchgl %eax,%esi # Set pointer
+dump.4: movb $2,%dl # Num lines
+dump.4a: movb $0x10,%cl # Bytes to dump
+dump.5: lodsb # Get byte and
+ call hex8 # dump it
+ decb %cl # Keep count
+ jz dump.6a # If done
+ movb $'-',%al # Separator
+ cmpb $0x8,%cl # Half way?
+ je dump.6 # Yes
+ movb $' ',%al # Use space
+dump.6: stosb # Save separator
+ jmp dump.5 # Continue
+dump.6a: decb %dl # Keep count
+ jz dump.7 # If done
+ movb $0xa,%al # Line feed
+ stosb # Save one
+ movb $7,%cl # Leading
+ movb $' ',%al # spaces
+dump.6b: stosb # Dump
+ decb %cl # spaces
+ jnz dump.6b
+ jmp dump.4a # Next line
+dump.7: popl %ds # Restore
+dump.8: popl %esi # Restore
+ movb $0xa,%al # Line feed
+ testb $DMP_EOL,%ch # End of line?
+ jnz dump.9 # Yes
+ movb $' ',%al # Use spaces
+ stosb # Save one
+dump.9: jmp dump.0 # Continue
+dump.10: stosb # Terminate string
+ ret # To caller
+/*
+ * Convert EAX, AX, or AL to hex, saving the result to [EDI].
+ */
+hex32: pushl %eax # Save
+ shrl $0x10,%eax # Do upper
+ call hex16 # 16
+ popl %eax # Restore
+hex16: call hex16.1 # Do upper 8
+hex16.1: xchgb %ah,%al # Save/restore
+hex8: pushl %eax # Save
+ shrb $0x4,%al # Do upper
+ call hex8.1 # 4
+ popl %eax # Restore
+hex8.1: andb $0xf,%al # Get lower 4
+ cmpb $0xa,%al # Convert
+ sbbb $0x69,%al # to hex
+ das # digit
+ orb $0x20,%al # To lower case
+ stosb # Save char
+ ret # (Recursive)
+/*
+ * Output zero-terminated string [ESI] to the console.
+ */
+putstr.0: call putchr # Output char
+putstr: lodsb # Load char
+ testb %al,%al # End of string?
+ jnz putstr.0 # No
+ ret # To caller
+#ifdef BTX_SERIAL
+ .set SIO_PRT,SIOPRT # Base port
+ .set SIO_FMT,SIOFMT # 8N1
+ .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD
+
+/*
+ * int sio_init(void)
+ */
+sio_init: movw $SIO_PRT+0x3,%dx # Data format reg
+ movb $SIO_FMT|0x80,%al # Set format
+ outb %al,(%dx) # and DLAB
+ pushl %edx # Save
+ subb $0x3,%dl # Divisor latch reg
+ movw $SIO_DIV,%ax # Set
+ outw %ax,(%dx) # BPS
+ popl %edx # Restore
+ movb $SIO_FMT,%al # Clear
+ outb %al,(%dx) # DLAB
+ incl %edx # Modem control reg
+ movb $0x3,%al # Set RTS,
+ outb %al,(%dx) # DTR
+ incl %edx # Line status reg
+ call sio_getc.1 # Get character
+
+/*
+ * int sio_flush(void)
+ */
+sio_flush: xorl %eax,%eax # Return value
+ xorl %ecx,%ecx # Timeout
+ movb $0x80,%ch # counter
+sio_flush.1: call sio_ischar # Check for character
+ jz sio_flush.2 # Till none
+ loop sio_flush.1 # or counter is zero
+ movb $1, %al # Exhausted all tries
+sio_flush.2: ret # To caller
+
+/*
+ * void sio_putc(int c)
+ */
+sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg
+ xor %ecx,%ecx # Timeout
+ movb $0x40,%ch # counter
+sio_putc.1: inb (%dx),%al # Transmitter
+ testb $0x20,%al # buffer empty?
+ loopz sio_putc.1 # No
+ jz sio_putc.2 # If timeout
+ movb 0x4(%esp,1),%al # Get character
+ subb $0x5,%dl # Transmitter hold reg
+ outb %al,(%dx) # Write character
+sio_putc.2: ret $0x4 # To caller
+
+/*
+ * int sio_getc(void)
+ */
+sio_getc: call sio_ischar # Character available?
+ jz sio_getc # No
+sio_getc.1: subb $0x5,%dl # Receiver buffer reg
+ inb (%dx),%al # Read character
+ ret # To caller
+
+/*
+ * int sio_ischar(void)
+ */
+sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register
+ xorl %eax,%eax # Zero
+ inb (%dx),%al # Received data
+ andb $0x1,%al # ready?
+ ret # To caller
+
+/*
+ * Output character AL to the serial console.
+ */
+putchr: pusha # Save
+ cmpb $10, %al # is it a newline?
+ jne putchr.1 # no?, then leave
+ push $13 # output a carriage
+ call sio_putc # return first
+ movb $10, %al # restore %al
+putchr.1: pushl %eax # Push the character
+ # onto the stack
+ call sio_putc # Output the character
+ popa # Restore
+ ret # To caller
+#else
+/*
+ * Output character AL to the console.
+ */
+putchr: pusha # Save
+ xorl %ecx,%ecx # Zero for loops
+ movb $SCR_MAT,%ah # Mode/attribute
+ movl $BDA_POS,%ebx # BDA pointer
+ movw (%ebx),%dx # Cursor position
+ movl $0xb8000,%edi # Regen buffer (color)
+ cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode?
+ jne putchr.1 # No
+ xorw %di,%di # Regen buffer (mono)
+putchr.1: cmpb $0xa,%al # New line?
+ je putchr.2 # Yes
+ xchgl %eax,%ecx # Save char
+ movb $SCR_COL,%al # Columns per row
+ mulb %dh # * row position
+ addb %dl,%al # + column
+ adcb $0x0,%ah # position
+ shll %eax # * 2
+ xchgl %eax,%ecx # Swap char, offset
+ movw %ax,(%edi,%ecx,1) # Write attr:char
+ incl %edx # Bump cursor
+ cmpb $SCR_COL,%dl # Beyond row?
+ jb putchr.3 # No
+putchr.2: xorb %dl,%dl # Zero column
+ incb %dh # Bump row
+putchr.3: cmpb $SCR_ROW,%dh # Beyond screen?
+ jb putchr.4 # No
+ leal 2*SCR_COL(%edi),%esi # New top line
+ movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
+ rep # Scroll
+ movsl # screen
+ movb $0x20,%al # Space
+ movb $SCR_COL,%cl # Columns to clear
+ rep # Clear
+ stosw # line
+ movb $SCR_ROW-1,%dh # Bottom line
+putchr.4: movw %dx,(%ebx) # Update position
+ popa # Restore
+ ret # To caller
+#endif
+
+ .code16
+/*
+ * Real Mode Hardware interrupt jump table.
+ */
+intr20: push $0x8 # Int 0x20: IRQ0
+ jmp int_hwr # V86 int 0x8
+ push $0x9 # Int 0x21: IRQ1
+ jmp int_hwr # V86 int 0x9
+ push $0xa # Int 0x22: IRQ2
+ jmp int_hwr # V86 int 0xa
+ push $0xb # Int 0x23: IRQ3
+ jmp int_hwr # V86 int 0xb
+ push $0xc # Int 0x24: IRQ4
+ jmp int_hwr # V86 int 0xc
+ push $0xd # Int 0x25: IRQ5
+ jmp int_hwr # V86 int 0xd
+ push $0xe # Int 0x26: IRQ6
+ jmp int_hwr # V86 int 0xe
+ push $0xf # Int 0x27: IRQ7
+ jmp int_hwr # V86 int 0xf
+ push $0x70 # Int 0x28: IRQ8
+ jmp int_hwr # V86 int 0x70
+ push $0x71 # Int 0x29: IRQ9
+ jmp int_hwr # V86 int 0x71
+ push $0x72 # Int 0x2a: IRQ10
+ jmp int_hwr # V86 int 0x72
+ push $0x73 # Int 0x2b: IRQ11
+ jmp int_hwr # V86 int 0x73
+ push $0x74 # Int 0x2c: IRQ12
+ jmp int_hwr # V86 int 0x74
+ push $0x75 # Int 0x2d: IRQ13
+ jmp int_hwr # V86 int 0x75
+ push $0x76 # Int 0x2e: IRQ14
+ jmp int_hwr # V86 int 0x76
+ push $0x77 # Int 0x2f: IRQ15
+ jmp int_hwr # V86 int 0x77
+/*
+ * Reflect hardware interrupts in real mode.
+ */
+int_hwr: push %ax # Save
+ push %ds # Save
+ push %bp # Save
+ mov %sp,%bp # Address stack frame
+ xchg %bx,6(%bp) # Swap BX, int no
+ xor %ax,%ax # Set %ds:%bx to
+ shl $2,%bx # point to
+ mov %ax,%ds # IDT entry
+ mov (%bx),%ax # Load IP
+ mov 2(%bx),%bx # Load CS
+ xchg %ax,4(%bp) # Swap saved %ax,%bx with
+ xchg %bx,6(%bp) # CS:IP of handler
+ pop %bp # Restore
+ pop %ds # Restore
+ lret # Jump to handler
+
+ .p2align 4
+/*
+ * Global descriptor table.
+ */
+gdt: .word 0x0,0x0,0x0,0x0 # Null entry
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
+ .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+ .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE
+ .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA
+tss_desc: .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS
+gdt.1:
+/*
+ * Pseudo-descriptors.
+ */
+gdtdesc: .word gdt.1-gdt-1,gdt,0x0 # GDT
+idtdesc: .word _IDTLM,MEM_IDT,0x0 # IDT
+ivtdesc: .word 0x400-0x0-1,0x0,0x0 # IVT
+/*
+ * IDT construction control string.
+ */
+idtctl: .byte 0x10, 0x8e # Int 0x0-0xf
+ .word 0x7dfb,intx00 # (exceptions)
+ .byte 0x10, 0x8e # Int 0x10
+ .word 0x1, intx10 # (exception)
+ .byte 0x10, 0x8e # Int 0x20-0x2f
+ .word 0xffff,intx20 # (hardware)
+ .byte 0x1, 0xee # int 0x30
+ .word 0x1, intx30 # (system call)
+ .byte 0x2, 0xee # Int 0x31-0x32
+ .word 0x1, intx31 # (V86, null)
+ .byte 0x0 # End of string
+/*
+ * Dump format string.
+ */
+dmpfmt: .byte '\n' # "\n"
+ .ascii "int" # "int="
+ .byte 0x80|DMP_X32, 0x40 # "00000000 "
+ .ascii "err" # "err="
+ .byte 0x80|DMP_X32, 0x44 # "00000000 "
+ .ascii "efl" # "efl="
+ .byte 0x80|DMP_X32, 0x50 # "00000000 "
+ .ascii "eip" # "eip="
+ .byte 0x80|DMP_X32|DMP_EOL,0x48 # "00000000\n"
+ .ascii "eax" # "eax="
+ .byte 0x80|DMP_X32, 0x34 # "00000000 "
+ .ascii "ebx" # "ebx="
+ .byte 0x80|DMP_X32, 0x28 # "00000000 "
+ .ascii "ecx" # "ecx="
+ .byte 0x80|DMP_X32, 0x30 # "00000000 "
+ .ascii "edx" # "edx="
+ .byte 0x80|DMP_X32|DMP_EOL,0x2c # "00000000\n"
+ .ascii "esi" # "esi="
+ .byte 0x80|DMP_X32, 0x1c # "00000000 "
+ .ascii "edi" # "edi="
+ .byte 0x80|DMP_X32, 0x18 # "00000000 "
+ .ascii "ebp" # "ebp="
+ .byte 0x80|DMP_X32, 0x20 # "00000000 "
+ .ascii "esp" # "esp="
+ .byte 0x80|DMP_X32|DMP_EOL,0x0 # "00000000\n"
+ .ascii "cs" # "cs="
+ .byte 0x80|DMP_X16, 0x4c # "0000 "
+ .ascii "ds" # "ds="
+ .byte 0x80|DMP_X16, 0xc # "0000 "
+ .ascii "es" # "es="
+ .byte 0x80|DMP_X16, 0x8 # "0000 "
+ .ascii " " # " "
+ .ascii "fs" # "fs="
+ .byte 0x80|DMP_X16, 0x10 # "0000 "
+ .ascii "gs" # "gs="
+ .byte 0x80|DMP_X16, 0x14 # "0000 "
+ .ascii "ss" # "ss="
+ .byte 0x80|DMP_X16|DMP_EOL,0x4 # "0000\n"
+ .ascii "cs:eip" # "cs:eip="
+ .byte 0x80|DMP_MEM|DMP_EOL,0x48 # "00 00 ... 00 00\n"
+ .ascii "ss:esp" # "ss:esp="
+ .byte 0x80|DMP_MEM|DMP_EOL,0x0 # "00 00 ... 00 00\n"
+ .asciz "BTX halted\n" # End
+/*
+ * Bad VM86 call panic
+ */
+badvm86: .asciz "Invalid VM86 Request\n"
+
+/*
+ * End of BTX memory.
+ */
+ .p2align 4
+break:
diff --git a/stand/i386/btx/btxldr/Makefile b/stand/i386/btx/btxldr/Makefile
new file mode 100644
index 0000000..3279597
--- /dev/null
+++ b/stand/i386/btx/btxldr/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= btxldr
+INTERNALPROG=
+MAN=
+SRCS= btxldr.S
+
+CFLAGS+=-DLOADER_ADDRESS=${LOADER_ADDRESS}
+CFLAGS+=-I${BOOTSRC}/i386/common
+
+.if defined(BTXLDR_VERBOSE)
+CFLAGS+=-DBTXLDR_VERBOSE
+.endif
+
+ORG=${LOADER_ADDRESS}
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.btxldr.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/btx/btxldr/Makefile.depend b/stand/i386/btx/btxldr/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/btx/btxldr/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/btx/btxldr/btxldr.S b/stand/i386/btx/btxldr/btxldr.S
new file mode 100644
index 0000000..1a0f5f4
--- /dev/null
+++ b/stand/i386/btx/btxldr/btxldr.S
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+#include <bootargs.h>
+
+#define RBX_MUTE 0x10 /* -m */
+#define OPT_SET(opt) (1 << (opt))
+
+/*
+ * Prototype BTX loader program, written in a couple of hours. The
+ * real thing should probably be more flexible, and in C.
+ */
+
+/*
+ * Memory locations.
+ */
+ .set MEM_STUB,0x600 # Real mode stub
+ .set MEM_ESP,0x1000 # New stack pointer
+ .set MEM_TBL,0x5000 # BTX page tables
+ .set MEM_ENTRY,0x9010 # BTX entry point
+ .set MEM_DATA,start+0x1000 # Data segment
+/*
+ * Segment selectors.
+ */
+ .set SEL_SCODE,0x8 # 4GB code
+ .set SEL_SDATA,0x10 # 4GB data
+ .set SEL_RCODE,0x18 # 64K code
+ .set SEL_RDATA,0x20 # 64K data
+/*
+ * Paging constants.
+ */
+ .set PAG_SIZ,0x1000 # Page size
+ .set PAG_ENT,0x4 # Page entry size
+/*
+ * Screen constants.
+ */
+ .set SCR_MAT,0x7 # Mode/attribute
+ .set SCR_COL,0x50 # Columns per row
+ .set SCR_ROW,0x19 # Rows per screen
+/*
+ * BIOS Data Area locations.
+ */
+ .set BDA_MEM,0x413 # Free memory
+ .set BDA_SCR,0x449 # Video mode
+ .set BDA_POS,0x450 # Cursor position
+/*
+ * Required by aout gas inadequacy.
+ */
+ .set SIZ_STUB,0x1a # Size of stub
+/*
+ * We expect to be loaded by boot2 at the origin defined in ./Makefile.
+ */
+ .globl start
+/*
+ * BTX program loader for ELF clients.
+ */
+start: cld # String ops inc
+ testl $OPT_SET(RBX_MUTE), 4(%esp) # Check first argument
+ setnz muted # for RBX_MUTE, set flag
+ movl $m_logo,%esi # Identify
+ call putstr # ourselves
+ movzwl BDA_MEM,%eax # Get base memory
+ shll $0xa,%eax # in bytes
+ movl %eax,%ebp # Base of user stack
+#ifdef BTXLDR_VERBOSE
+ movl $m_mem,%esi # Display
+ call hexout # amount of
+ call putstr # base memory
+#endif
+ lgdt gdtdesc # Load new GDT
+/*
+ * Relocate caller's arguments.
+ */
+#ifdef BTXLDR_VERBOSE
+ movl $m_esp,%esi # Display
+ movl %esp,%eax # caller
+ call hexout # stack
+ call putstr # pointer
+ movl $m_args,%esi # Format string
+ leal 0x4(%esp),%ebx # First argument
+ movl $0x6,%ecx # Count
+start.1: movl (%ebx),%eax # Get argument and
+ addl $0x4,%ebx # bump pointer
+ call hexout # Display it
+ loop start.1 # Till done
+ call putstr # End message
+#endif
+ movl BA_BOOTINFO+4(%esp),%esi # Source: bootinfo
+ cmpl $0x0, %esi # If the bootinfo pointer
+ je start_null_bi # is null, don't copy it
+ movl BI_SIZE(%esi),%ecx # Allocate space
+ subl %ecx,%ebp # for bootinfo
+ movl %ebp,%edi # Destination
+ rep # Copy
+ movsb # it
+ movl %ebp,BA_BOOTINFO+4(%esp) # Update pointer
+ movl %edi,%ebp # Restore base pointer
+#ifdef BTXLDR_VERBOSE
+ movl $m_rel_bi,%esi # Display
+ movl %ebp,%eax # bootinfo
+ call hexout # relocation
+ call putstr # message
+#endif
+start_null_bi: movl $BOOTARGS_SIZE,%ecx # Fixed size of arguments
+ testl $KARGS_FLAGS_EXTARG, BA_BOOTFLAGS+4(%esp) # Check for extra data
+ jz start_fixed # Skip if the flag is not set
+ addl BOOTARGS_SIZE+4(%esp),%ecx # Add size of variable args
+start_fixed: subl $ARGOFF,%ebp # Place args at fixed offset
+ leal 0x4(%esp),%esi # Source
+ movl %ebp,%edi # Destination
+ rep # Copy
+ movsb # them
+#ifdef BTXLDR_VERBOSE
+ movl $m_rel_args,%esi # Display
+ movl %ebp,%eax # argument
+ call hexout # relocation
+ call putstr # message
+#endif
+/*
+ * Set up BTX kernel.
+ */
+ movl $MEM_ESP,%esp # Set up new stack
+ movl $MEM_DATA,%ebx # Data segment
+ movl $m_vers,%esi # Display BTX
+ call putstr # version message
+ movb 0x5(%ebx),%al # Get major version
+ addb $'0',%al # Display
+ call putchr # it
+ movb $'.',%al # And a
+ call putchr # dot
+ movb 0x6(%ebx),%al # Get minor
+ xorb %ah,%ah # version
+ movb $0xa,%dl # Divide
+ divb %dl,%al # by 10
+ addb $'0',%al # Display
+ call putchr # tens
+ movb %ah,%al # Get units
+ addb $'0',%al # Display
+ call putchr # units
+ call putstr # End message
+ movl %ebx,%esi # BTX image
+ movzwl 0x8(%ebx),%edi # Compute
+ orl $PAG_SIZ/PAG_ENT-1,%edi # the
+ incl %edi # BTX
+ shll $0x2,%edi # load
+ addl $MEM_TBL,%edi # address
+ pushl %edi # Save load address
+ movzwl 0xa(%ebx),%ecx # Image size
+#ifdef BTXLDR_VERBOSE
+ pushl %ecx # Save image size
+#endif
+ rep # Relocate
+ movsb # BTX
+ movl %esi,%ebx # Keep place
+#ifdef BTXLDR_VERBOSE
+ movl $m_rel_btx,%esi # Restore
+ popl %eax # parameters
+ call hexout # and
+#endif
+ popl %ebp # display
+#ifdef BTXLDR_VERBOSE
+ movl %ebp,%eax # the
+ call hexout # relocation
+ call putstr # message
+#endif
+ addl $PAG_SIZ,%ebp # Display
+#ifdef BTXLDR_VERBOSE
+ movl $m_base,%esi # the
+ movl %ebp,%eax # user
+ call hexout # base
+ call putstr # address
+#endif
+/*
+ * Set up ELF-format client program.
+ */
+ cmpl $0x464c457f,(%ebx) # ELF magic number?
+ je start.3 # Yes
+ movl $e_fmt,%esi # Display error
+ call putstr # message
+start.2: jmp start.2 # Hang
+start.3:
+#ifdef BTXLDR_VERBOSE
+ movl $m_elf,%esi # Display ELF
+ call putstr # message
+ movl $m_segs,%esi # Format string
+#endif
+ movl 0x1c(%ebx),%edx # Get e_phoff
+ addl %ebx,%edx # To pointer
+ movzwl 0x2c(%ebx),%ecx # Get e_phnum
+start.4: cmpl $0x1,(%edx) # Is p_type PT_LOAD?
+ jne start.6 # No
+#ifdef BTXLDR_VERBOSE
+ movl 0x4(%edx),%eax # Display
+ call hexout # p_offset
+ movl 0x8(%edx),%eax # Display
+ call hexout # p_vaddr
+ movl 0x10(%edx),%eax # Display
+ call hexout # p_filesz
+ movl 0x14(%edx),%eax # Display
+ call hexout # p_memsz
+ call putstr # End message
+#endif
+ pushl %esi # Save
+ pushl %ecx # working registers
+ movl 0x4(%edx),%esi # Get p_offset
+ addl %ebx,%esi # as pointer
+ movl 0x8(%edx),%edi # Get p_vaddr
+ addl %ebp,%edi # as pointer
+ movl 0x10(%edx),%ecx # Get p_filesz
+ rep # Set up
+ movsb # segment
+ movl 0x14(%edx),%ecx # Any bytes
+ subl 0x10(%edx),%ecx # to zero?
+ jz start.5 # No
+ xorb %al,%al # Then
+ rep # zero
+ stosb # them
+start.5: popl %ecx # Restore
+ popl %esi # registers
+start.6: addl $0x20,%edx # To next entry
+ loop start.4 # Till done
+#ifdef BTXLDR_VERBOSE
+ movl $m_done,%esi # Display done
+ call putstr # message
+#endif
+ movl $start.8,%esi # Real mode stub
+ movl $MEM_STUB,%edi # Destination
+ movl $start.9-start.8,%ecx # Size
+ rep # Relocate
+ movsb # it
+ ljmp $SEL_RCODE,$MEM_STUB # To 16-bit code
+ .code16
+start.8: xorw %ax,%ax # Data
+ movb $SEL_RDATA,%al # selector
+ movw %ax,%ss # Reload SS
+ movw %ax,%ds # Reset
+ movw %ax,%es # other
+ movw %ax,%fs # segment
+ movw %ax,%gs # limits
+ movl %cr0,%eax # Switch to
+ decw %ax # real
+ movl %eax,%cr0 # mode
+ ljmp $0,$MEM_ENTRY # Jump to BTX entry point
+start.9:
+ .code32
+/*
+ * Output message [ESI] followed by EAX in hex.
+ */
+hexout: pushl %eax # Save
+ call putstr # Display message
+ popl %eax # Restore
+ pushl %esi # Save
+ pushl %edi # caller's
+ movl $buf,%edi # Buffer
+ pushl %edi # Save
+ call hex32 # To hex
+ xorb %al,%al # Terminate
+ stosb # string
+ popl %esi # Restore
+hexout.1: lodsb # Get a char
+ cmpb $'0',%al # Leading zero?
+ je hexout.1 # Yes
+ testb %al,%al # End of string?
+ jne hexout.2 # No
+ decl %esi # Undo
+hexout.2: decl %esi # Adjust for inc
+ call putstr # Display hex
+ popl %edi # Restore
+ popl %esi # caller's
+ ret # To caller
+/*
+ * Output zero-terminated string [ESI] to the console.
+ */
+putstr.0: call putchr # Output char
+putstr: lodsb # Load char
+ testb %al,%al # End of string?
+ jne putstr.0 # No
+ ret # To caller
+/*
+ * Output character AL to the console.
+ */
+putchr: testb $1,muted # Check muted
+ jnz putchr.5 # do a nop
+ pusha # Save
+ xorl %ecx,%ecx # Zero for loops
+ movb $SCR_MAT,%ah # Mode/attribute
+ movl $BDA_POS,%ebx # BDA pointer
+ movw (%ebx),%dx # Cursor position
+ movl $0xb8000,%edi # Regen buffer (color)
+ cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode?
+ jne putchr.1 # No
+ xorw %di,%di # Regen buffer (mono)
+putchr.1: cmpb $0xa,%al # New line?
+ je putchr.2 # Yes
+ xchgl %eax,%ecx # Save char
+ movb $SCR_COL,%al # Columns per row
+ mulb %dh # * row position
+ addb %dl,%al # + column
+ adcb $0x0,%ah # position
+ shll %eax # * 2
+ xchgl %eax,%ecx # Swap char, offset
+ movw %ax,(%edi,%ecx,1) # Write attr:char
+ incl %edx # Bump cursor
+ cmpb $SCR_COL,%dl # Beyond row?
+ jb putchr.3 # No
+putchr.2: xorb %dl,%dl # Zero column
+ incb %dh # Bump row
+putchr.3: cmpb $SCR_ROW,%dh # Beyond screen?
+ jb putchr.4 # No
+ leal 2*SCR_COL(%edi),%esi # New top line
+ movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
+ rep # Scroll
+ movsl # screen
+ movb $' ',%al # Space
+ movb $SCR_COL,%cl # Columns to clear
+ rep # Clear
+ stosw # line
+ movb $SCR_ROW-1,%dh # Bottom line
+putchr.4: movw %dx,(%ebx) # Update position
+ popa # Restore
+putchr.5: ret # To caller
+/*
+ * Convert EAX, AX, or AL to hex, saving the result to [EDI].
+ */
+hex32: pushl %eax # Save
+ shrl $0x10,%eax # Do upper
+ call hex16 # 16
+ popl %eax # Restore
+hex16: call hex16.1 # Do upper 8
+hex16.1: xchgb %ah,%al # Save/restore
+hex8: pushl %eax # Save
+ shrb $0x4,%al # Do upper
+ call hex8.1 # 4
+ popl %eax # Restore
+hex8.1: andb $0xf,%al # Get lower 4
+ cmpb $0xa,%al # Convert
+ sbbb $0x69,%al # to hex
+ das # digit
+ orb $0x20,%al # To lower case
+ stosb # Save char
+ ret # (Recursive)
+
+ .data
+ .p2align 4
+/*
+ * Global descriptor table.
+ */
+gdt: .word 0x0,0x0,0x0,0x0 # Null entry
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
+ .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+gdt.1:
+gdtdesc: .word gdt.1-gdt-1 # Limit
+ .long gdt # Base
+/*
+ * Messages.
+ */
+m_logo: .asciz " \nBTX loader 1.00 "
+m_vers: .asciz "BTX version is \0\n"
+e_fmt: .asciz "Error: Client format not supported\n"
+#ifdef BTXLDR_VERBOSE
+m_mem: .asciz "Starting in protected mode (base mem=\0)\n"
+m_esp: .asciz "Arguments passed (esp=\0):\n"
+m_args: .asciz "<howto="
+ .asciz " bootdev="
+ .asciz " junk="
+ .asciz " "
+ .asciz " "
+ .asciz " bootinfo=\0>\n"
+m_rel_bi: .asciz "Relocated bootinfo (size=48) to \0\n"
+m_rel_args: .asciz "Relocated arguments (size=18) to \0\n"
+m_rel_btx: .asciz "Relocated kernel (size=\0) to \0\n"
+m_base: .asciz "Client base address is \0\n"
+m_elf: .asciz "Client format is ELF\n"
+m_segs: .asciz "text segment: offset="
+ .asciz " vaddr="
+ .asciz " filesz="
+ .asciz " memsz=\0\n"
+ .asciz "data segment: offset="
+ .asciz " vaddr="
+ .asciz " filesz="
+ .asciz " memsz=\0\n"
+m_done: .asciz "Loading complete\n"
+#endif
+
+/*
+ * Flags
+ */
+muted: .byte 0x0
+
+/*
+ * Uninitialized data area.
+ */
+buf: # Scratch buffer
diff --git a/stand/i386/btx/lib/Makefile b/stand/i386/btx/lib/Makefile
new file mode 100644
index 0000000..8de6255
--- /dev/null
+++ b/stand/i386/btx/lib/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= crt0.o
+INTERNALPROG=
+MAN=
+SRCS= btxcsu.S btxsys.s btxv86.s
+CFLAGS+=-I${BOOTSRC}/i386/common
+LDFLAGS+=-Wl,-r
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/btx/lib/Makefile.depend b/stand/i386/btx/lib/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/btx/lib/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/btx/lib/btxcsu.S b/stand/i386/btx/lib/btxcsu.S
new file mode 100644
index 0000000..c46f809
--- /dev/null
+++ b/stand/i386/btx/lib/btxcsu.S
@@ -0,0 +1,49 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#include <bootargs.h>
+
+#
+# BTX C startup code (ELF).
+#
+
+#
+# Globals.
+#
+ .global _start
+#
+# Client entry point.
+#
+_start: cld
+ pushl %eax
+ movl $_edata,%edi
+ movl $_end,%ecx
+ subl %edi, %ecx
+ xorb %al, %al
+ rep
+ stosb
+ popl __base
+ movl %esp,%eax # Set
+ addl $ARGADJ,%eax # argument
+ movl %eax,__args # pointer
+ call main # Invoke client main()
+ call exit # Invoke client exit()
+#
+# Data.
+#
+ .comm __base,4 # Client base address
+ .comm __args,4 # Client arguments
diff --git a/stand/i386/btx/lib/btxsys.s b/stand/i386/btx/lib/btxsys.s
new file mode 100644
index 0000000..9c77b42
--- /dev/null
+++ b/stand/i386/btx/lib/btxsys.s
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# BTX system calls.
+#
+
+#
+# Globals.
+#
+ .global __exit
+ .global __exec
+#
+# Constants.
+#
+ .set INT_SYS,0x30 # Interrupt number
+#
+# System call: exit
+#
+__exit: xorl %eax,%eax # BTX system
+ int $INT_SYS # call 0x0
+#
+# System call: exec
+#
+__exec: movl $0x1,%eax # BTX system
+ int $INT_SYS # call 0x1
diff --git a/stand/i386/btx/lib/btxv86.h b/stand/i386/btx/lib/btxv86.h
new file mode 100644
index 0000000..f04ce5e
--- /dev/null
+++ b/stand/i386/btx/lib/btxv86.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+#ifndef _BTXV86_H_
+#define _BTXV86_H_
+
+#include <sys/types.h>
+#include <machine/psl.h>
+
+/*
+ * Memory buffer space for real mode IO.
+ * Just one page is not much, but the space is rather limited.
+ * See ../btx/btx.S for details.
+ */
+#define V86_IO_BUFFER 0x8000
+#define V86_IO_BUFFER_SIZE 0x1000
+
+#define V86_ADDR 0x10000 /* Segment:offset address */
+#define V86_CALLF 0x20000 /* Emulate far call */
+#define V86_FLAGS 0x40000 /* Return flags */
+
+struct __v86 {
+ uint32_t ctl; /* Control flags */
+ uint32_t addr; /* Interrupt number or address */
+ uint32_t es; /* V86 ES register */
+ uint32_t ds; /* V86 DS register */
+ uint32_t fs; /* V86 FS register */
+ uint32_t gs; /* V86 GS register */
+ uint32_t eax; /* V86 EAX register */
+ uint32_t ecx; /* V86 ECX register */
+ uint32_t edx; /* V86 EDX register */
+ uint32_t ebx; /* V86 EBX register */
+ uint32_t efl; /* V86 eflags register */
+ uint32_t ebp; /* V86 EBP register */
+ uint32_t esi; /* V86 ESI register */
+ uint32_t edi; /* V86 EDI register */
+};
+
+extern struct __v86 __v86; /* V86 interface structure */
+void __v86int(void);
+
+#define v86 __v86
+#define v86int __v86int
+
+extern u_int32_t __base;
+extern u_int32_t __args;
+
+#define PTOV(pa) ((caddr_t)(pa) - __base)
+#define VTOP(va) ((vm_offset_t)(va) + __base)
+#define VTOPSEG(va) (u_int16_t)(VTOP((caddr_t)va) >> 4)
+#define VTOPOFF(va) (u_int16_t)(VTOP((caddr_t)va) & 0xf)
+
+#define V86_CY(x) ((x) & PSL_C)
+#define V86_ZR(x) ((x) & PSL_Z)
+
+void __exit(int) __attribute__((__noreturn__));
+void __exec(caddr_t, ...);
+
+#endif /* !_BTXV86_H_ */
diff --git a/stand/i386/btx/lib/btxv86.s b/stand/i386/btx/lib/btxv86.s
new file mode 100644
index 0000000..0d7d111
--- /dev/null
+++ b/stand/i386/btx/lib/btxv86.s
@@ -0,0 +1,85 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# BTX V86 interface.
+#
+
+#
+# Globals.
+#
+ .global __v86int
+#
+# Fields in V86 interface structure.
+#
+ .set V86_CTL,0x0 # Control flags
+ .set V86_ADDR,0x4 # Int number/address
+ .set V86_ES,0x8 # V86 ES
+ .set V86_DS,0xc # V86 DS
+ .set V86_FS,0x10 # V86 FS
+ .set V86_GS,0x14 # V86 GS
+ .set V86_EAX,0x18 # V86 EAX
+ .set V86_ECX,0x1c # V86 ECX
+ .set V86_EDX,0x20 # V86 EDX
+ .set V86_EBX,0x24 # V86 EBX
+ .set V86_EFL,0x28 # V86 eflags
+ .set V86_EBP,0x2c # V86 EBP
+ .set V86_ESI,0x30 # V86 ESI
+ .set V86_EDI,0x34 # V86 EDI
+#
+# Other constants.
+#
+ .set INT_V86,0x31 # Interrupt number
+ .set SIZ_V86,0x38 # Size of V86 structure
+#
+# V86 interface function.
+#
+__v86int: popl __v86ret # Save return address
+ pushl $__v86 # Push pointer
+ call __v86_swap # Load V86 registers
+ int $INT_V86 # To BTX
+ call __v86_swap # Load user registers
+ addl $0x4,%esp # Discard pointer
+ pushl __v86ret # Restore return address
+ ret # To user
+#
+# Swap V86 and user registers.
+#
+__v86_swap: xchgl %ebp,0x4(%esp,1) # Swap pointer, EBP
+ xchgl %eax,V86_EAX(%ebp) # Swap EAX
+ xchgl %ecx,V86_ECX(%ebp) # Swap ECX
+ xchgl %edx,V86_EDX(%ebp) # Swap EDX
+ xchgl %ebx,V86_EBX(%ebp) # Swap EBX
+ pushl %eax # Save
+ pushf # Put eflags
+ popl %eax # in EAX
+ xchgl %eax,V86_EFL(%ebp) # Swap
+ pushl %eax # Put EAX
+ popf # in eflags
+ movl 0x8(%esp,1),%eax # Load EBP
+ xchgl %eax,V86_EBP(%ebp) # Swap
+ movl %eax,0x8(%esp,1) # Save EBP
+ popl %eax # Restore
+ xchgl %esi,V86_ESI(%ebp) # Swap ESI
+ xchgl %edi,V86_EDI(%ebp) # Swap EDI
+ xchgl %ebp,0x4(%esp,1) # Swap pointer, EBP
+ ret # To caller
+#
+# V86 interface structure.
+#
+ .comm __v86,SIZ_V86
+ .comm __v86ret,4
diff --git a/stand/i386/cdboot/Makefile b/stand/i386/cdboot/Makefile
new file mode 100644
index 0000000..7acd404
--- /dev/null
+++ b/stand/i386/cdboot/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= cdboot
+STRIP=
+BINMODE=${NOBINMODE}
+MAN=
+SRCS= ${PROG}.S
+
+CFLAGS+=-I${BOOTSRC}/i386/common
+
+ORG= 0x7c00
+
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.cdboot.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/cdboot/Makefile.depend b/stand/i386/cdboot/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/cdboot/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/cdboot/cdboot.S b/stand/i386/cdboot/cdboot.S
new file mode 100644
index 0000000..9c3fb15
--- /dev/null
+++ b/stand/i386/cdboot/cdboot.S
@@ -0,0 +1,594 @@
+#
+# Copyright (c) 2001 John Baldwin <jhb@FreeBSD.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+# $FreeBSD$
+
+#
+# This program is a freestanding boot program to load an a.out binary
+# from a CD-ROM booted with no emulation mode as described by the El
+# Torito standard. Due to broken BIOSen that do not load the desired
+# number of sectors, we try to fit this in as small a space as possible.
+#
+# Basically, we first create a set of boot arguments to pass to the loaded
+# binary. Then we attempt to load /boot/loader from the CD we were booted
+# off of.
+#
+
+#include <bootargs.h>
+
+#
+# Memory locations.
+#
+ .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k
+ .set MEM_ARG,0x900 # Arguments at start
+ .set MEM_ARG_BTX,0xa100 # Where we move them to so the
+ # BTX client can see them
+ .set MEM_ARG_SIZE,0x18 # Size of the arguments
+ .set MEM_BTX_ADDRESS,0x9000 # where BTX lives
+ .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute
+ .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader
+ .set MEM_BTX_CLIENT,0xa000 # where BTX clients live
+#
+# a.out header fields
+#
+ .set AOUT_TEXT,0x04 # text segment size
+ .set AOUT_DATA,0x08 # data segment size
+ .set AOUT_BSS,0x0c # zero'd BSS size
+ .set AOUT_SYMBOLS,0x10 # symbol table
+ .set AOUT_ENTRY,0x14 # entry point
+ .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header
+#
+# Segment selectors.
+#
+ .set SEL_SDATA,0x8 # Supervisor data
+ .set SEL_RDATA,0x10 # Real mode data
+ .set SEL_SCODE,0x18 # PM-32 code
+ .set SEL_SCODE16,0x20 # PM-16 code
+#
+# BTX constants
+#
+ .set INT_SYS,0x30 # BTX syscall interrupt
+#
+# Constants for reading from the CD.
+#
+ .set ERROR_TIMEOUT,0x80 # BIOS timeout on read
+ .set NUM_RETRIES,3 # Num times to retry
+ .set SECTOR_SIZE,0x800 # size of a sector
+ .set SECTOR_SHIFT,11 # number of place to shift
+ .set BUFFER_LEN,0x100 # number of sectors in buffer
+ .set MAX_READ,0x10000 # max we can read at a time
+ .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT
+ .set MEM_READ_BUFFER,0x9000 # buffer to read from CD
+ .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor
+ .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer
+ .set VOLDESC_LBA,0x10 # LBA of vol descriptor
+ .set VD_PRIMARY,1 # Primary VD
+ .set VD_END,255 # VD Terminator
+ .set VD_ROOTDIR,156 # Offset of Root Dir Record
+ .set DIR_LEN,0 # Offset of Dir Record length
+ .set DIR_EA_LEN,1 # Offset of EA length
+ .set DIR_EXTENT,2 # Offset of 64-bit LBA
+ .set DIR_SIZE,10 # Offset of 64-bit length
+ .set DIR_NAMELEN,32 # Offset of 8-bit name len
+ .set DIR_NAME,33 # Offset of dir name
+#
+# We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry
+# point)
+#
+ .code16
+ .globl start
+ .org 0x0, 0x0
+#
+# Program start.
+#
+start: cld # string ops inc
+ xor %ax,%ax # zero %ax
+ mov %ax,%ss # setup the
+ mov $start,%sp # stack
+ mov %ax,%ds # setup the
+ mov %ax,%es # data segments
+ mov %dl,drive # Save BIOS boot device
+ mov $msg_welcome,%si # %ds:(%si) -> welcome message
+ call putstr # display the welcome message
+#
+# Setup the arguments that the loader is expecting from boot[12]
+#
+ mov $msg_bootinfo,%si # %ds:(%si) -> boot args message
+ call putstr # display the message
+ mov $MEM_ARG,%bx # %ds:(%bx) -> boot args
+ mov %bx,%di # %es:(%di) -> boot args
+ xor %eax,%eax # zero %eax
+ mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit
+ # dwords
+ rep # Clear the arguments
+ stosl # to zero
+ mov drive,%dl # Store BIOS boot device
+ mov %dl,0x4(%bx) # in kargs->bootdev
+ orb $KARGS_FLAGS_CD,0x8(%bx) # kargs->bootflags |=
+ # KARGS_FLAGS_CD
+#
+# Load Volume Descriptor
+#
+ mov $VOLDESC_LBA,%eax # Set LBA of first VD
+load_vd: push %eax # Save %eax
+ mov $1,%dh # One sector
+ mov $MEM_VOLDESC,%ebx # Destination
+ call read # Read it in
+ cmpb $VD_PRIMARY,(%bx) # Primary VD?
+ je have_vd # Yes
+ pop %eax # Prepare to
+ inc %eax # try next
+ cmpb $VD_END,(%bx) # Last VD?
+ jne load_vd # No, read next
+ mov $msg_novd,%si # No VD
+ jmp error # Halt
+have_vd: # Have Primary VD
+#
+# Try to look up the loader binary using the paths in the loader_paths
+# array.
+#
+ mov $loader_paths,%si # Point to start of array
+lookup_path: push %si # Save file name pointer
+ call lookup # Try to find file
+ pop %di # Restore file name pointer
+ jnc lookup_found # Found this file
+ xor %al,%al # Look for next
+ mov $0xffff,%cx # path name by
+ repnz # scanning for
+ scasb # nul char
+ mov %di,%si # Point %si at next path
+ mov (%si),%al # Get first char of next path
+ or %al,%al # Is it double nul?
+ jnz lookup_path # No, try it.
+ mov $msg_failed,%si # Failed message
+ jmp error # Halt
+lookup_found: # Found a loader file
+#
+# Load the binary into the buffer. Due to real mode addressing limitations
+# we have to read it in 64k chunks.
+#
+ mov DIR_SIZE(%bx),%eax # Read file length
+ add $SECTOR_SIZE-1,%eax # Convert length to sectors
+ shr $SECTOR_SHIFT,%eax
+ cmp $BUFFER_LEN,%eax
+ jbe load_sizeok
+ mov $msg_load2big,%si # Error message
+ call error
+load_sizeok: movzbw %al,%cx # Num sectors to read
+ mov DIR_EXTENT(%bx),%eax # Load extent
+ xor %edx,%edx
+ mov DIR_EA_LEN(%bx),%dl
+ add %edx,%eax # Skip extended
+ mov $MEM_READ_BUFFER,%ebx # Read into the buffer
+load_loop: mov %cl,%dh
+ cmp $MAX_READ_SEC,%cl # Truncate to max read size
+ jbe load_notrunc
+ mov $MAX_READ_SEC,%dh
+load_notrunc: sub %dh,%cl # Update count
+ push %eax # Save
+ call read # Read it in
+ pop %eax # Restore
+ add $MAX_READ_SEC,%eax # Update LBA
+ add $MAX_READ,%ebx # Update dest addr
+ jcxz load_done # Done?
+ jmp load_loop # Keep going
+load_done:
+#
+# Turn on the A20 address line
+#
+ call seta20 # Turn A20 on
+#
+# Relocate the loader and BTX using a very lazy protected mode
+#
+ mov $msg_relocate,%si # Display the
+ call putstr # relocation message
+ mov MEM_READ_BUFFER+AOUT_ENTRY,%edi # %edi is the destination
+ mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is
+ # the start of the text
+ # segment
+ mov MEM_READ_BUFFER+AOUT_TEXT,%ecx # %ecx = length of the text
+ # segment
+ push %edi # Save entry point for later
+ lgdt gdtdesc # setup our own gdt
+ cli # turn off interrupts
+ mov %cr0,%eax # Turn on
+ or $0x1,%al # protected
+ mov %eax,%cr0 # mode
+ ljmp $SEL_SCODE,$pm_start # long jump to clear the
+ # instruction pre-fetch queue
+ .code32
+pm_start: mov $SEL_SDATA,%ax # Initialize
+ mov %ax,%ds # %ds and
+ mov %ax,%es # %es to a flat selector
+ rep # Relocate the
+ movsb # text segment
+ add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page
+ and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment
+ mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment
+ rep # Relocate the
+ movsb # data segment
+ mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss
+ xor %eax,%eax # zero %eax
+ add $3,%cl # round %ecx up to
+ shr $2,%ecx # a multiple of 4
+ rep # zero the
+ stosl # bss
+ mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader
+ add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader
+ mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go
+ movzwl 0xa(%esi),%ecx # %ecx -> length of BTX
+ rep # Relocate
+ movsb # BTX
+ ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM
+ .code16
+pm_16: mov $SEL_RDATA,%ax # Initialize
+ mov %ax,%ds # %ds and
+ mov %ax,%es # %es to a real mode selector
+ mov %cr0,%eax # Turn off
+ and $~0x1,%al # protected
+ mov %eax,%cr0 # mode
+ ljmp $0,$pm_end # Long jump to clear the
+ # instruction pre-fetch queue
+pm_end: sti # Turn interrupts back on now
+#
+# Copy the BTX client to MEM_BTX_CLIENT
+#
+ xor %ax,%ax # zero %ax and set
+ mov %ax,%ds # %ds and %es
+ mov %ax,%es # to segment 0
+ mov $MEM_BTX_CLIENT,%di # Prepare to relocate
+ mov $btx_client,%si # the simple btx client
+ mov $(btx_client_end-btx_client),%cx # length of btx client
+ rep # Relocate the
+ movsb # simple BTX client
+#
+# Copy the boot[12] args to where the BTX client can see them
+#
+ mov $MEM_ARG,%si # where the args are at now
+ mov $MEM_ARG_BTX,%di # where the args are moving to
+ mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs
+ rep # Relocate
+ movsl # the words
+#
+# Save the entry point so the client can get to it later on
+#
+ pop %eax # Restore saved entry point
+ stosl # and add it to the end of
+ # the arguments
+#
+# Now we just start up BTX and let it do the rest
+#
+ mov $msg_jump,%si # Display the
+ call putstr # jump message
+ ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point
+
+#
+# Lookup the file in the path at [SI] from the root directory.
+#
+# Trashes: All but BX
+# Returns: CF = 0 (success), BX = pointer to record
+# CF = 1 (not found)
+#
+lookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record
+ push %si
+ mov $msg_lookup,%si # Display lookup message
+ call putstr
+ pop %si
+ push %si
+ call putstr
+ mov $msg_lookup2,%si
+ call putstr
+ pop %si
+lookup_dir: lodsb # Get first char of path
+ cmp $0,%al # Are we done?
+ je lookup_done # Yes
+ cmp $'/',%al # Skip path separator.
+ je lookup_dir
+ dec %si # Undo lodsb side effect
+ call find_file # Lookup first path item
+ jnc lookup_dir # Try next component
+ mov $msg_lookupfail,%si # Not found message
+ call putstr
+ stc # Set carry
+ ret
+ jmp error
+lookup_done: mov $msg_lookupok,%si # Success message
+ call putstr
+ clc # Clear carry
+ ret
+
+#
+# Lookup file at [SI] in directory whose record is at [BX].
+#
+# Trashes: All but returns
+# Returns: CF = 0 (success), BX = pointer to record, SI = next path item
+# CF = 1 (not found), SI = preserved
+#
+find_file: mov DIR_EXTENT(%bx),%eax # Load extent
+ xor %edx,%edx
+ mov DIR_EA_LEN(%bx),%dl
+ add %edx,%eax # Skip extended attributes
+ mov %eax,rec_lba # Save LBA
+ mov DIR_SIZE(%bx),%eax # Save size
+ mov %eax,rec_size
+ xor %cl,%cl # Zero length
+ push %si # Save
+ff.namelen: inc %cl # Update length
+ lodsb # Read char
+ cmp $0,%al # Nul?
+ je ff.namedone # Yes
+ cmp $'/',%al # Path separator?
+ jnz ff.namelen # No, keep going
+ff.namedone: dec %cl # Adjust length and save
+ mov %cl,name_len
+ pop %si # Restore
+ff.load: mov rec_lba,%eax # Load LBA
+ mov $MEM_DIR,%ebx # Address buffer
+ mov $1,%dh # One sector
+ call read # Read directory block
+ incl rec_lba # Update LBA to next block
+ff.scan: mov %ebx,%edx # Check for EOF
+ sub $MEM_DIR,%edx
+ cmp %edx,rec_size
+ ja ff.scan.1
+ stc # EOF reached
+ ret
+ff.scan.1: cmpb $0,DIR_LEN(%bx) # Last record in block?
+ je ff.nextblock
+ push %si # Save
+ movzbw DIR_NAMELEN(%bx),%si # Find end of string
+ff.checkver: cmpb $'0',DIR_NAME-1(%bx,%si) # Less than '0'?
+ jb ff.checkver.1
+ cmpb $'9',DIR_NAME-1(%bx,%si) # Greater than '9'?
+ ja ff.checkver.1
+ dec %si # Next char
+ jnz ff.checkver
+ jmp ff.checklen # All numbers in name, so
+ # no version
+ff.checkver.1: movzbw DIR_NAMELEN(%bx),%cx
+ cmp %cx,%si # Did we find any digits?
+ je ff.checkdot # No
+ cmpb $';',DIR_NAME-1(%bx,%si) # Check for semicolon
+ jne ff.checkver.2
+ dec %si # Skip semicolon
+ mov %si,%cx
+ mov %cl,DIR_NAMELEN(%bx) # Adjust length
+ jmp ff.checkdot
+ff.checkver.2: mov %cx,%si # Restore %si to end of string
+ff.checkdot: cmpb $'.',DIR_NAME-1(%bx,%si) # Trailing dot?
+ jne ff.checklen # No
+ decb DIR_NAMELEN(%bx) # Adjust length
+ff.checklen: pop %si # Restore
+ movzbw name_len,%cx # Load length of name
+ cmp %cl,DIR_NAMELEN(%bx) # Does length match?
+ je ff.checkname # Yes, check name
+ff.nextrec: add DIR_LEN(%bx),%bl # Next record
+ adc $0,%bh
+ jmp ff.scan
+ff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size
+ jnc ff.load # If subtract ok, keep going
+ ret # End of file, so not found
+ff.checkname: lea DIR_NAME(%bx),%di # Address name in record
+ push %si # Save
+ repe cmpsb # Compare name
+ je ff.match # We have a winner!
+ pop %si # Restore
+ jmp ff.nextrec # Keep looking.
+ff.match: add $2,%sp # Discard saved %si
+ clc # Clear carry
+ ret
+
+#
+# Load DH sectors starting at LBA EAX into [EBX].
+#
+# Trashes: EAX
+#
+read: push %si # Save
+ push %cx # Save since some BIOSs trash
+ mov %eax,edd_lba # LBA to read from
+ mov %ebx,%eax # Convert address
+ shr $4,%eax # to segment
+ mov %ax,edd_addr+0x2 # and store
+read.retry: call twiddle # Entertain the user
+ push %dx # Save
+ mov $edd_packet,%si # Address Packet
+ mov %dh,edd_len # Set length
+ mov drive,%dl # BIOS Device
+ mov $0x42,%ah # BIOS: Extended Read
+ int $0x13 # Call BIOS
+ pop %dx # Restore
+ jc read.fail # Worked?
+ pop %cx # Restore
+ pop %si
+ ret # Return
+read.fail: cmp $ERROR_TIMEOUT,%ah # Timeout?
+ je read.retry # Yes, Retry.
+read.error: mov %ah,%al # Save error
+ mov $hex_error,%di # Format it
+ call hex8 # as hex
+ mov $msg_badread,%si # Display Read error message
+
+#
+# Display error message at [SI] and halt.
+#
+error: call putstr # Display message
+halt: hlt
+ jmp halt # Spin
+
+#
+# Display a null-terminated string.
+#
+# Trashes: AX, SI
+#
+putstr: push %bx # Save
+putstr.load: lodsb # load %al from %ds:(%si)
+ test %al,%al # stop at null
+ jnz putstr.putc # if the char != null, output it
+ pop %bx # Restore
+ ret # return when null is hit
+putstr.putc: call putc # output char
+ jmp putstr.load # next char
+
+#
+# Display a single char.
+#
+putc: mov $0x7,%bx # attribute for output
+ mov $0xe,%ah # BIOS: put_char
+ int $0x10 # call BIOS, print char in %al
+ ret # Return to caller
+
+#
+# Output the "twiddle"
+#
+twiddle: push %ax # Save
+ push %bx # Save
+ mov twiddle_index,%al # Load index
+ mov $twiddle_chars,%bx # Address table
+ inc %al # Next
+ and $3,%al # char
+ mov %al,twiddle_index # Save index for next call
+ xlat # Get char
+ call putc # Output it
+ mov $8,%al # Backspace
+ call putc # Output it
+ pop %bx # Restore
+ pop %ax # Restore
+ ret
+
+#
+# Enable A20. Put an upper limit on the amount of time we wait for the
+# keyboard controller to get ready (65K x ISA access time). If
+# we wait more than that amount, the hardware is probably
+# legacy-free and simply doesn't have a keyboard controller.
+# Thus, the A20 line is already enabled.
+#
+seta20: cli # Disable interrupts
+ xor %cx,%cx # Clear
+seta20.1: inc %cx # Increment, overflow?
+ jz seta20.3 # Yes
+ in $0x64,%al # Get status
+ test $0x2,%al # Busy?
+ jnz seta20.1 # Yes
+ mov $0xd1,%al # Command: Write
+ out %al,$0x64 # output port
+seta20.2: in $0x64,%al # Get status
+ test $0x2,%al # Busy?
+ jnz seta20.2 # Yes
+ mov $0xdf,%al # Enable
+ out %al,$0x60 # A20
+seta20.3: sti # Enable interrupts
+ ret # To caller
+
+#
+# Convert AL to hex, saving the result to [EDI].
+#
+hex8: pushl %eax # Save
+ shrb $0x4,%al # Do upper
+ call hex8.1 # 4
+ popl %eax # Restore
+hex8.1: andb $0xf,%al # Get lower 4
+ cmpb $0xa,%al # Convert
+ sbbb $0x69,%al # to hex
+ das # digit
+ orb $0x20,%al # To lower case
+ stosb # Save char
+ ret # (Recursive)
+
+#
+# BTX client to start btxldr
+#
+ .code32
+btx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi
+ # %ds:(%esi) -> end
+ # of boot[12] args
+ mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push
+ std # Go backwards
+push_arg: lodsl # Read argument
+ push %eax # Push it onto the stack
+ loop push_arg # Push all of the arguments
+ cld # In case anyone depends on this
+ pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of
+ # the loader
+ push %eax # Emulate a near call
+ mov $0x1,%eax # 'exec' system call
+ int $INT_SYS # BTX system call
+btx_client_end:
+ .code16
+
+ .p2align 4
+#
+# Global descriptor table.
+#
+gdt: .word 0x0,0x0,0x0,0x0 # Null entry
+ .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit)
+ .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit)
+gdt.1:
+#
+# Pseudo-descriptors.
+#
+gdtdesc: .word gdt.1-gdt-1 # Limit
+ .long gdt # Base
+#
+# EDD Packet
+#
+edd_packet: .byte 0x10 # Length
+ .byte 0 # Reserved
+edd_len: .byte 0x0 # Num to read
+ .byte 0 # Reserved
+edd_addr: .word 0x0,0x0 # Seg:Off
+edd_lba: .quad 0x0 # LBA
+
+drive: .byte 0
+
+#
+# State for searching dir
+#
+rec_lba: .long 0x0 # LBA (adjusted for EA)
+rec_size: .long 0x0 # File size
+name_len: .byte 0x0 # Length of current name
+
+twiddle_index: .byte 0x0
+
+msg_welcome: .asciz "CD Loader 1.2\r\n\n"
+msg_bootinfo: .asciz "Building the boot loader arguments\r\n"
+msg_relocate: .asciz "Relocating the loader and the BTX\r\n"
+msg_jump: .asciz "Starting the BTX loader\r\n"
+msg_badread: .ascii "Read Error: 0x"
+hex_error: .asciz "00\r\n"
+msg_novd: .asciz "Could not find Primary Volume Descriptor\r\n"
+msg_lookup: .asciz "Looking up "
+msg_lookup2: .asciz "... "
+msg_lookupok: .asciz "Found\r\n"
+msg_lookupfail: .asciz "File not found\r\n"
+msg_load2big: .asciz "File too big\r\n"
+msg_failed: .asciz "Boot failed\r\n"
+twiddle_chars: .ascii "|/-\\"
+loader_paths: .asciz "/BOOT/LOADER"
+ .asciz "/boot/loader"
+ .byte 0
+
diff --git a/stand/i386/common/bootargs.h b/stand/i386/common/bootargs.h
new file mode 100644
index 0000000..df55807
--- /dev/null
+++ b/stand/i386/common/bootargs.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOT_I386_ARGS_H_
+#define _BOOT_I386_ARGS_H_
+
+#define KARGS_FLAGS_CD 0x1
+#define KARGS_FLAGS_PXE 0x2
+#define KARGS_FLAGS_ZFS 0x4
+#define KARGS_FLAGS_EXTARG 0x8 /* variably sized extended argument */
+
+#define BOOTARGS_SIZE 24 /* sizeof(struct bootargs) */
+#define BA_BOOTFLAGS 8 /* offsetof(struct bootargs, bootflags) */
+#define BA_BOOTINFO 20 /* offsetof(struct bootargs, bootinfo) */
+#define BI_SIZE 48 /* offsetof(struct bootinfo, bi_size) */
+
+/*
+ * We reserve some space above BTX allocated stack for the arguments
+ * and certain data that could hang off them. Currently only struct bootinfo
+ * is supported in that category. The bootinfo is placed at the top
+ * of the arguments area and the actual arguments are placed at ARGOFF offset
+ * from the top and grow towards the top. Hopefully we have enough space
+ * for bootinfo and the arguments to not run into each other.
+ * Arguments area below ARGOFF is reserved for future use.
+ */
+#define ARGSPACE 0x1000 /* total size of the BTX args area */
+#define ARGOFF 0x800 /* actual args offset within the args area */
+#define ARGADJ (ARGSPACE - ARGOFF)
+
+#ifndef __ASSEMBLER__
+
+struct bootargs
+{
+ uint32_t howto;
+ uint32_t bootdev;
+ uint32_t bootflags;
+ union {
+ struct {
+ uint32_t pxeinfo;
+ uint32_t reserved;
+ };
+ uint64_t zfspool;
+ };
+ uint32_t bootinfo;
+
+ /*
+ * If KARGS_FLAGS_EXTARG is set in bootflags, then the above fields
+ * are followed by a uint32_t field that specifies a size of the
+ * extended arguments (including the size field).
+ */
+};
+
+#ifdef LOADER_GELI_SUPPORT
+#include <crypto/intake.h>
+#endif
+
+struct geli_boot_args
+{
+ uint32_t size;
+ union {
+ char gelipw[256];
+ struct {
+ char notapw; /*
+ * single null byte to stop keybuf
+ * being interpreted as a password
+ */
+ uint32_t keybuf_sentinel;
+#ifdef LOADER_GELI_SUPPORT
+ struct keybuf *keybuf;
+#else
+ void *keybuf;
+#endif
+ };
+ };
+};
+
+#endif /*__ASSEMBLER__*/
+
+#endif /* !_BOOT_I386_ARGS_H_ */
diff --git a/stand/i386/common/cons.c b/stand/i386/common/cons.c
new file mode 100644
index 0000000..5fb1a93
--- /dev/null
+++ b/stand/i386/common/cons.c
@@ -0,0 +1,177 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <machine/psl.h>
+
+#include <btxv86.h>
+
+#include "lib.h"
+#include "rbx.h"
+#include "util.h"
+#include "cons.h"
+
+#define SECOND 18 /* Circa that many ticks in a second. */
+
+uint8_t ioctrl = IO_KEYBOARD;
+
+void
+putc(int c)
+{
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0xe00 | (c & 0xff);
+ v86.ebx = 0x7;
+ v86int();
+}
+
+void
+xputc(int c)
+{
+
+ if (ioctrl & IO_KEYBOARD)
+ putc(c);
+ if (ioctrl & IO_SERIAL)
+ sio_putc(c);
+}
+
+void
+putchar(int c)
+{
+
+ if (c == '\n')
+ xputc('\r');
+ xputc(c);
+}
+
+int
+getc(int fn)
+{
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x16;
+ v86.eax = fn << 8;
+ v86int();
+
+ if (fn == 0)
+ return (v86.eax);
+
+ if (V86_ZR(v86.efl))
+ return (0);
+ return (v86.eax);
+}
+
+int
+xgetc(int fn)
+{
+
+ if (OPT_CHECK(RBX_NOINTR))
+ return (0);
+ for (;;) {
+ if (ioctrl & IO_KEYBOARD && getc(1))
+ return (fn ? 1 : getc(0));
+ if (ioctrl & IO_SERIAL && sio_ischar())
+ return (fn ? 1 : sio_getc());
+ if (fn)
+ return (0);
+ }
+ /* NOTREACHED */
+}
+
+int
+getchar(void)
+{
+
+ return (xgetc(0));
+}
+
+int
+keyhit(unsigned int secs)
+{
+ uint32_t t0, t1, c;
+
+ if (OPT_CHECK(RBX_NOINTR))
+ return (0);
+ secs *= SECOND;
+ t0 = 0;
+ for (;;) {
+ /*
+ * The extra comparison is an attempt to work around
+ * what appears to be a bug in QEMU and Bochs. Both emulators
+ * sometimes report a key-press with scancode one and ascii zero
+ * when no such key is pressed in reality. As far as I can tell,
+ * this only happens shortly after a reboot.
+ */
+ c = xgetc(1);
+ if (c != 0 && c != 0x0100)
+ return (1);
+ if (secs > 0) {
+ t1 = *(uint32_t *)PTOV(0x46c);
+ if (!t0)
+ t0 = t1;
+ if (t1 < t0 || t1 >= t0 + secs)
+ return (0);
+ }
+ }
+ /* NOTREACHED */
+}
+
+void
+getstr(char *cmdstr, size_t cmdstrsize)
+{
+ char *s;
+ int c;
+
+ s = cmdstr;
+ for (;;) {
+ c = xgetc(0);
+
+ /* Translate some extended codes. */
+ switch (c) {
+ case 0x5300: /* delete */
+ c = '\177';
+ break;
+ default:
+ c &= 0xff;
+ break;
+ }
+
+ switch (c) {
+ case '\177':
+ case '\b':
+ if (s > cmdstr) {
+ s--;
+ printf("\b \b");
+ }
+ break;
+ case '\n':
+ case '\r':
+ *s = 0;
+ return;
+ default:
+ if (c >= 0x20 && c <= 0x7e) {
+ if (s - cmdstr < cmdstrsize - 1)
+ *s++ = c;
+ putchar(c);
+ }
+ break;
+ }
+ }
+}
diff --git a/stand/i386/common/cons.h b/stand/i386/common/cons.h
new file mode 100644
index 0000000..73474fb
--- /dev/null
+++ b/stand/i386/common/cons.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CONS_H_
+#define _CONS_H_
+
+#define IO_KEYBOARD 1
+#define IO_SERIAL 2
+
+extern uint8_t ioctrl;
+
+void putc(int c);
+void xputc(int c);
+void putchar(int c);
+int getc(int fn);
+int xgetc(int fn);
+int getchar(void);
+int keyhit(unsigned int secs);
+void getstr(char *cmdstr, size_t cmdstrsize);
+
+#endif /* !_CONS_H_ */
diff --git a/stand/i386/common/drv.c b/stand/i386/common/drv.c
new file mode 100644
index 0000000..a805c41
--- /dev/null
+++ b/stand/i386/common/drv.c
@@ -0,0 +1,102 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <btxv86.h>
+
+#include "rbx.h"
+#include "util.h"
+#include "drv.h"
+#include "edd.h"
+
+static struct edd_params params;
+
+uint64_t
+drvsize(struct dsk *dskp)
+{
+
+ params.len = sizeof(struct edd_params);
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4800;
+ v86.edx = dskp->drive;
+ v86.ds = VTOPSEG(&params);
+ v86.esi = VTOPOFF(&params);
+ v86int();
+ if (V86_CY(v86.efl)) {
+ printf("error %u\n", v86.eax >> 8 & 0xff);
+ return (0);
+ }
+ return (params.sectors);
+}
+
+static struct edd_packet packet;
+
+int
+drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk)
+{
+ static unsigned c = 0x2d5c7c2f;
+
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%c\b", c = c << 8 | c >> 24);
+ packet.len = sizeof(struct edd_packet);
+ packet.count = nblk;
+ packet.off = VTOPOFF(buf);
+ packet.seg = VTOPSEG(buf);
+ packet.lba = lba;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4200;
+ v86.edx = dskp->drive;
+ v86.ds = VTOPSEG(&packet);
+ v86.esi = VTOPOFF(&packet);
+ v86int();
+ if (V86_CY(v86.efl)) {
+ printf("%s: error %u lba %u\n",
+ BOOTPROG, v86.eax >> 8 & 0xff, lba);
+ return (-1);
+ }
+ return (0);
+}
+
+#if defined(GPT) || defined(ZFS)
+int
+drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk)
+{
+
+ packet.len = sizeof(struct edd_packet);
+ packet.count = nblk;
+ packet.off = VTOPOFF(buf);
+ packet.seg = VTOPSEG(buf);
+ packet.lba = lba;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4300;
+ v86.edx = dskp->drive;
+ v86.ds = VTOPSEG(&packet);
+ v86.esi = VTOPOFF(&packet);
+ v86int();
+ if (V86_CY(v86.efl)) {
+ printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
+ return (-1);
+ }
+ return (0);
+}
+#endif /* GPT || ZFS */
diff --git a/stand/i386/common/drv.h b/stand/i386/common/drv.h
new file mode 100644
index 0000000..c0995df
--- /dev/null
+++ b/stand/i386/common/drv.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DRV_H_
+#define _DRV_H_
+
+struct dsk {
+ unsigned int drive;
+ unsigned int type;
+ unsigned int unit;
+ unsigned int slice;
+ int part;
+ daddr_t start;
+ uint64_t size;
+};
+
+int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk);
+#if defined(GPT) || defined(ZFS)
+int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk);
+#endif /* GPT || ZFS */
+uint64_t drvsize(struct dsk *dskp);
+
+#endif /* !_DRV_H_ */
diff --git a/stand/i386/common/edd.h b/stand/i386/common/edd.h
new file mode 100644
index 0000000..7d1f450
--- /dev/null
+++ b/stand/i386/common/edd.h
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 2011 Hudson River Trading LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _EDD_H_
+#define _EDD_H_
+
+/* Supported interfaces for "Check Extensions Present". */
+#define EDD_INTERFACE_FIXED_DISK 0x01
+#define EDD_INTERFACE_EJECT 0x02
+#define EDD_INTERFACE_EDD 0x04
+
+struct edd_packet {
+ uint16_t len;
+ uint16_t count;
+ uint16_t off;
+ uint16_t seg;
+ uint64_t lba;
+};
+
+struct edd_packet_v3 {
+ uint16_t len;
+ uint16_t count;
+ uint16_t off;
+ uint16_t seg;
+ uint64_t lba;
+ uint64_t phys_addr;
+};
+
+struct edd_params {
+ uint16_t len;
+ uint16_t flags;
+ uint32_t cylinders;
+ uint32_t heads;
+ uint32_t sectors_per_track;
+ uint64_t sectors;
+ uint16_t sector_size;
+ uint16_t edd_params_seg;
+ uint16_t edd_params_off;
+} __packed;
+
+struct edd_device_path_v3 {
+ uint16_t key;
+ uint8_t len;
+ uint8_t reserved[3];
+ char host_bus[4];
+ char interface[8];
+ uint64_t interface_path;
+ uint64_t device_path;
+ uint8_t reserved2[1];
+ uint8_t checksum;
+} __packed;
+
+struct edd_params_v3 {
+ struct edd_params params;
+ struct edd_device_path_v3 device_path;
+} __packed;
+
+struct edd_device_path_v4 {
+ uint16_t key;
+ uint8_t len;
+ uint8_t reserved[3];
+ char host_bus[4];
+ char interface[8];
+ uint64_t interface_path;
+ uint64_t device_path[2];
+ uint8_t reserved2[1];
+ uint8_t checksum;
+} __packed;
+
+struct edd_params_v4 {
+ struct edd_params params;
+ struct edd_device_path_v4 device_path;
+} __packed;
+
+#define EDD_FLAGS_DMA_BOUNDARY_HANDLING 0x0001
+#define EDD_FLAGS_REMOVABLE_MEDIA 0x0002
+#define EDD_FLAGS_WRITE_VERIFY 0x0004
+#define EDD_FLAGS_MEDIA_CHANGE_NOTIFICATION 0x0008
+#define EDD_FLAGS_LOCKABLE_MEDIA 0x0010
+#define EDD_FLAGS_NO_MEDIA_PRESENT 0x0020
+
+#define EDD_DEVICE_PATH_KEY 0xbedd
+
+#endif /* !_EDD_H_ */
diff --git a/stand/i386/gptboot/Makefile b/stand/i386/gptboot/Makefile
new file mode 100644
index 0000000..9e62007
--- /dev/null
+++ b/stand/i386/gptboot/Makefile
@@ -0,0 +1,75 @@
+# $FreeBSD$
+
+HAVE_GELI= yes
+
+.include <bsd.init.mk>
+
+.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/common ${SASRC}
+
+FILES= gptboot
+MAN= gptboot.8
+
+NM?= nm
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+REL1= 0x700
+ORG1= 0x7c00
+ORG2= 0x0
+
+# Decide level of UFS support.
+GPTBOOT_UFS?= UFS1_AND_UFS2
+#GPTBOOT_UFS?= UFS2_ONLY
+#GPTBOOT_UFS?= UFS1_ONLY
+
+CFLAGS+=-DBOOTPROG=\"gptboot\" \
+ -O1 \
+ -DGPT \
+ -D${GPTBOOT_UFS} \
+ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} \
+ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ -I${LDRSRC} \
+ -I${BOOTSRC}/i386/common \
+ -I${BTXLIB} \
+ -I${BOOTSRC}/i386/boot2 \
+ -Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+ -Winline -Wno-pointer-sign
+
+CFLAGS.gcc+= --param max-inline-insns-single=100
+
+LD_FLAGS+=${LD_FLAGS_BIN}
+
+CLEANFILES+= gptboot
+
+gptboot: gptldr.bin gptboot.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \
+ -o ${.TARGET} gptboot.bin
+
+CLEANFILES+= gptldr.bin gptldr.out gptldr.o
+
+gptldr.bin: gptldr.out
+ ${OBJCOPY} -S -O binary gptldr.out ${.TARGET}
+
+gptldr.out: gptldr.o
+ ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
+
+CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o crc32.o drv.o \
+ cons.o ${OPENCRYPTO_XTS}
+
+gptboot.bin: gptboot.out
+ ${OBJCOPY} -S -O binary gptboot.out ${.TARGET}
+
+gptboot.out: ${BTXCRT} gptboot.o sio.o crc32.o drv.o cons.o ${OPENCRYPTO_XTS}
+ ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBGELIBOOT} ${LIBSA32}
+
+gptboot.o: ${SASRC}/ufsread.c
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.gptldr.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/gptboot/Makefile.depend b/stand/i386/gptboot/Makefile.depend
new file mode 100644
index 0000000..295be1a
--- /dev/null
+++ b/stand/i386/gptboot/Makefile.depend
@@ -0,0 +1,19 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libmd \
+ lib/libstand \
+ sys/boot/geli \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/lib \
+ sys/boot/libstand32 \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/gptboot/gptboot.8 b/stand/i386/gptboot/gptboot.8
new file mode 100644
index 0000000..a7afd81
--- /dev/null
+++ b/stand/i386/gptboot/gptboot.8
@@ -0,0 +1,245 @@
+.\" Copyright (c) 2013 Warren Block
+.\" 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 5, 2014
+.Dt GPTBOOT 8
+.Os
+.Sh NAME
+.Nm gptboot
+.Nd GPT bootcode for UFS on BIOS-based computers
+.Sh DESCRIPTION
+.Nm
+is used on BIOS-based computers to boot from a UFS partition on a
+GPT-partitioned disk.
+.Nm
+is installed in a
+.Cm freebsd-boot
+partition with
+.Xr gpart 8 .
+.Sh IMPLEMENTATION NOTES
+The GPT standard allows a variable number of partitions, but
+.Nm
+only boots from tables with 128 partitions or less.
+.Sh PARTITION ATTRIBUTES
+.Nm
+checks and manages several attributes of GPT UFS partitions.
+.Bl -tag -width ".Cm bootfailed"
+.It Cm bootme
+Attempt to boot from this partition.
+If more than one partition has the
+.Cm bootme
+attribute set,
+.Nm
+will attempt to boot each one until successful.
+.It Cm bootonce
+Attempt to boot from this partition only one time.
+Setting this attribute with
+.Xr gpart 8
+automatically also sets the
+.Cm bootme
+attribute.
+Multiple partitions may have the
+.Cm bootonce
+and
+.Cm bootme
+attributes set.
+.It Cm bootfailed
+The
+.Cm bootfailed
+attribute marks partitions that had the
+.Cm bootonce
+attribute set, but failed to boot.
+This attribute is managed by the system.
+See
+.Sx "BOOTING"
+and
+.Sx "POST-BOOT ACTIONS"
+below for details.
+.El
+.Sh USAGE
+For normal usage, the user does not have to set or manage any of the
+partition attributes.
+.Nm
+will boot from the first UFS partition found.
+.Pp
+The
+.Cm bootonce
+attribute can be used for testing an upgraded operating system on
+an already-working computer.
+The existing system partition is left untouched, and the new version
+of the operating system to be tested is installed on another partition.
+The
+.Cm bootonce
+attribute is set on that new test partition.
+The next boot is attempted from the test partition.
+Success or failure will be shown in the system log files.
+After a successful boot of the test partition, a user script can check
+the logs and change the
+.Cm bootme
+attributes so the test partition becomes the new system partition.
+Because the
+.Cm bootonce
+attribute is cleared after an attempted boot, a failed boot will not
+leave the system attempting to boot from a partition that will never
+succeed.
+Instead, the system will boot from the older, known-working operating
+system that has not been modified.
+If the
+.Cm bootme
+attribute is set on any partitions, booting will be attempted from them
+first.
+If no partitions with
+.Cm bootme
+attributes are found, booting will be attempted from the first UFS
+partition found.
+.Sh BOOTING
+.Nm
+first reads the partition table.
+All
+.Cm freebsd-ufs
+partitions with only the
+.Cm bootonce
+attribute set, indicating a failed boot, are set to
+.Cm bootfailed .
+.Nm
+then scans through all of the
+.Cm freebsd-ufs
+partitions.
+Boot behavior depends on the combination of
+.Cm bootme
+and
+.Cm bootonce
+attributes set on those partitions.
+.Bl -tag -width ".Cm bootonce + .Cm bootme"
+.It Cm bootonce + Cm bootme
+Highest priority: booting is attempted from each of the
+.Cm freebsd-ufs
+partitions with both of these attributes.
+On each partition, the
+.Cm bootme
+attribute is removed and the boot attempted.
+.It Cm bootme
+Middle priority: booting is attempted from each of the
+.Cm freebsd-ufs
+partitions with the
+.Cm bootme
+attribute.
+.El
+.Pp
+If neither
+.Cm bootonce
+nor
+.Cm bootme
+attributes are found on any partitions, booting is attempted from the
+first
+.Cm freebsd-ufs
+partition on the disk.
+.Sh POST-BOOT ACTIONS
+The startup script
+.Pa /etc/rc.d/gptboot
+checks the attributes of
+.Cm freebsd-ufs
+partitions on all GPT disks.
+Partitions with the
+.Cm bootfailed
+attribute generate a
+.Dq boot from X failed
+system log message.
+Partitions with only the
+.Cm bootonce
+attribute, indicating a partition that successfully booted, generate a
+.Dq boot from X succeeded
+system log message.
+The
+.Cm bootfailed
+attributes are cleared from all the partitions.
+The
+.Cm bootonce
+attribute is cleared from the partition that successfully booted.
+There is normally only one of these.
+.Sh FILES
+.Bl -tag -width /boot/gptboot -compact
+.It Pa /boot/gptboot
+bootcode binary
+.It Pa /boot.config
+parameters for the boot blocks
+.Pq optional
+.El
+.Sh EXAMPLES
+.Nm
+is installed in a
+.Cm freebsd-boot
+partition, usually the first partition on the disk.
+A
+.Dq protective MBR
+.Po
+see
+.Xr gpart 8
+.Pc
+is typically installed in combination with
+.Nm .
+.Pp
+Install
+.Nm
+on the
+.Pa ada0
+drive:
+.Bd -literal -offset indent
+gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 ada0
+.Ed
+.Pp
+.Nm
+can also be installed without the PMBR:
+.Bd -literal -offset indent
+gpart bootcode -p /boot/gptboot -i 1 ada0
+.Ed
+.Pp
+Set the
+.Cm bootme
+attribute for partition 2:
+.Bd -literal -offset indent
+gpart set -a bootme -i 2 ada0
+.Ed
+.Pp
+Set the
+.Cm bootonce
+attribute for partition 2, automatically also setting the
+.Cm bootme
+attribute:
+.Bd -literal -offset indent
+gpart set -a bootonce -i 2 ada0
+.Ed
+.Sh SEE ALSO
+.Xr boot.config 5 ,
+.Xr rc.conf 5 ,
+.Xr boot 8 ,
+.Xr gpart 8
+.Sh HISTORY
+.Nm
+appeared in FreeBSD 7.1.
+.Sh AUTHORS
+This manual page written by
+.An Warren Block Aq wblock@FreeBSD.org .
diff --git a/stand/i386/gptboot/gptboot.c b/stand/i386/gptboot/gptboot.c
new file mode 100644
index 0000000..dd06f0c
--- /dev/null
+++ b/stand/i386/gptboot/gptboot.c
@@ -0,0 +1,648 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/gpt.h>
+#include <sys/dirent.h>
+#include <sys/reboot.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <machine/pc/bios.h>
+#include <machine/psl.h>
+
+#include <stdarg.h>
+
+#include <a.out.h>
+
+#include <btxv86.h>
+
+#include "bootargs.h"
+#include "lib.h"
+#include "rbx.h"
+#include "drv.h"
+#include "util.h"
+#include "cons.h"
+#include "gpt.h"
+#include "paths.h"
+
+#define ARGS 0x900
+#define NOPT 14
+#define NDEV 3
+#define MEM_BASE 0x12
+#define MEM_EXT 0x15
+
+#define DRV_HARD 0x80
+#define DRV_MASK 0x7f
+
+#define TYPE_AD 0
+#define TYPE_DA 1
+#define TYPE_MAXHARD TYPE_DA
+#define TYPE_FD 2
+
+extern uint32_t _end;
+
+static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS;
+static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
+static const unsigned char flags[NOPT] = {
+ RBX_DUAL,
+ RBX_SERIAL,
+ RBX_ASKNAME,
+ RBX_CDROM,
+ RBX_CONFIG,
+ RBX_KDB,
+ RBX_GDB,
+ RBX_MUTE,
+ RBX_NOINTR,
+ RBX_PAUSE,
+ RBX_QUIET,
+ RBX_DFLTROOT,
+ RBX_SINGLE,
+ RBX_VERBOSE
+};
+uint32_t opts;
+
+static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
+static const unsigned char dev_maj[NDEV] = {30, 4, 2};
+
+static struct dsk dsk;
+static char kname[1024];
+static int comspeed = SIOSPD;
+static struct bootinfo bootinfo;
+#ifdef LOADER_GELI_SUPPORT
+static struct geli_boot_args geliargs;
+#endif
+
+static vm_offset_t high_heap_base;
+static uint32_t bios_basemem, bios_extmem, high_heap_size;
+
+static struct bios_smap smap;
+
+/*
+ * The minimum amount of memory to reserve in bios_extmem for the heap.
+ */
+#define HEAP_MIN (3 * 1024 * 1024)
+
+static char *heap_next;
+static char *heap_end;
+
+void exit(int);
+static void load(void);
+static int parse_cmds(char *, int *);
+static int dskread(void *, daddr_t, unsigned);
+void *malloc(size_t n);
+void free(void *ptr);
+#ifdef LOADER_GELI_SUPPORT
+static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf,
+ size_t bytes);
+#endif
+
+void *
+malloc(size_t n)
+{
+ char *p = heap_next;
+ if (p + n > heap_end) {
+ printf("malloc failure\n");
+ for (;;)
+ ;
+ /* NOTREACHED */
+ return (0);
+ }
+ heap_next += n;
+ return (p);
+}
+
+void
+free(void *ptr)
+{
+
+ return;
+}
+
+#include "ufsread.c"
+#include "gpt.c"
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.c"
+static char gelipw[GELI_PW_MAXLEN];
+static struct keybuf *gelibuf;
+#endif
+
+static inline int
+xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
+{
+
+ if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
+ printf("Invalid %s\n", "format");
+ return (-1);
+ }
+ return (0);
+}
+
+static void
+bios_getmem(void)
+{
+ uint64_t size;
+
+ /* Parse system memory map */
+ v86.ebx = 0;
+ do {
+ v86.ctl = V86_FLAGS;
+ v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/
+ v86.eax = 0xe820;
+ v86.ecx = sizeof(struct bios_smap);
+ v86.edx = SMAP_SIG;
+ v86.es = VTOPSEG(&smap);
+ v86.edi = VTOPOFF(&smap);
+ v86int();
+ if ((v86.efl & 1) || (v86.eax != SMAP_SIG))
+ break;
+ /* look for a low-memory segment that's large enough */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
+ (smap.length >= (512 * 1024)))
+ bios_basemem = smap.length;
+ /* look for the first segment in 'extended' memory */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) {
+ bios_extmem = smap.length;
+ }
+
+ /*
+ * Look for the largest segment in 'extended' memory beyond
+ * 1MB but below 4GB.
+ */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) &&
+ (smap.base < 0x100000000ull)) {
+ size = smap.length;
+
+ /*
+ * If this segment crosses the 4GB boundary, truncate it.
+ */
+ if (smap.base + size > 0x100000000ull)
+ size = 0x100000000ull - smap.base;
+
+ if (size > high_heap_size) {
+ high_heap_size = size;
+ high_heap_base = smap.base;
+ }
+ }
+ } while (v86.ebx != 0);
+
+ /* Fall back to the old compatibility function for base memory */
+ if (bios_basemem == 0) {
+ v86.ctl = 0;
+ v86.addr = 0x12; /* int 0x12 */
+ v86int();
+
+ bios_basemem = (v86.eax & 0xffff) * 1024;
+ }
+
+ /* Fall back through several compatibility functions for extended memory */
+ if (bios_extmem == 0) {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x15; /* int 0x15 function 0xe801*/
+ v86.eax = 0xe801;
+ v86int();
+ if (!(v86.efl & 1)) {
+ bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024;
+ }
+ }
+ if (bios_extmem == 0) {
+ v86.ctl = 0;
+ v86.addr = 0x15; /* int 0x15 function 0x88*/
+ v86.eax = 0x8800;
+ v86int();
+ bios_extmem = (v86.eax & 0xffff) * 1024;
+ }
+
+ /*
+ * If we have extended memory and did not find a suitable heap
+ * region in the SMAP, use the last 3MB of 'extended' memory as a
+ * high heap candidate.
+ */
+ if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
+ high_heap_size = HEAP_MIN;
+ high_heap_base = bios_extmem + 0x100000 - HEAP_MIN;
+ }
+}
+
+static int
+gptinit(void)
+{
+
+ if (gptread(&freebsd_ufs_uuid, &dsk, dmadat->secbuf) == -1) {
+ printf("%s: unable to load GPT\n", BOOTPROG);
+ return (-1);
+ }
+ if (gptfind(&freebsd_ufs_uuid, &dsk, dsk.part) == -1) {
+ printf("%s: no UFS partition was found\n", BOOTPROG);
+ return (-1);
+ }
+#ifdef LOADER_GELI_SUPPORT
+ if (geli_taste(vdev_read, &dsk, (gpttable[curent].ent_lba_end -
+ gpttable[curent].ent_lba_start)) == 0) {
+ if (geli_havekey(&dsk) != 0 && geli_passphrase(&gelipw,
+ dsk.unit, 'p', curent + 1, &dsk) != 0) {
+ printf("%s: unable to decrypt GELI key\n", BOOTPROG);
+ return (-1);
+ }
+ }
+#endif
+
+ dsk_meta = 0;
+ return (0);
+}
+
+int
+main(void)
+{
+ char cmd[512], cmdtmp[512];
+ ssize_t sz;
+ int autoboot, dskupdated;
+ ufs_ino_t ino;
+
+ dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+
+ bios_getmem();
+
+ if (high_heap_size > 0) {
+ heap_end = PTOV(high_heap_base + high_heap_size);
+ heap_next = PTOV(high_heap_base);
+ } else {
+ heap_next = (char *)dmadat + sizeof(*dmadat);
+ heap_end = (char *)PTOV(bios_basemem);
+ }
+
+ v86.ctl = V86_FLAGS;
+ v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
+ dsk.drive = *(uint8_t *)PTOV(ARGS);
+ dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
+ dsk.unit = dsk.drive & DRV_MASK;
+ dsk.part = -1;
+ dsk.start = 0;
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+ bootinfo.bi_basemem = bios_basemem / 1024;
+ bootinfo.bi_extmem = bios_extmem / 1024;
+ bootinfo.bi_memsizes_valid++;
+ bootinfo.bi_bios_dev = dsk.drive;
+
+#ifdef LOADER_GELI_SUPPORT
+ geli_init();
+#endif
+ /* Process configuration file */
+
+ if (gptinit() != 0)
+ return (-1);
+
+ autoboot = 1;
+ *cmd = '\0';
+
+ for (;;) {
+ *kname = '\0';
+ if ((ino = lookup(PATH_CONFIG)) ||
+ (ino = lookup(PATH_DOTCONFIG))) {
+ sz = fsread(ino, cmd, sizeof(cmd) - 1);
+ cmd[(sz < 0) ? 0 : sz] = '\0';
+ }
+ if (*cmd != '\0') {
+ memcpy(cmdtmp, cmd, sizeof(cmdtmp));
+ if (parse_cmds(cmdtmp, &dskupdated))
+ break;
+ if (dskupdated && gptinit() != 0)
+ break;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%s: %s", PATH_CONFIG, cmd);
+ *cmd = '\0';
+ }
+
+ if (autoboot && keyhit(3)) {
+ if (*kname == '\0')
+ memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
+ break;
+ }
+ autoboot = 0;
+
+ /*
+ * Try to exec stage 3 boot loader. If interrupted by a
+ * keypress, or in case of failure, try to load a kernel
+ * directly instead.
+ */
+ if (*kname != '\0')
+ load();
+ memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
+ load();
+ memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
+ load();
+ gptbootfailed(&dsk);
+ if (gptfind(&freebsd_ufs_uuid, &dsk, -1) == -1)
+ break;
+ dsk_meta = 0;
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ if (!OPT_CHECK(RBX_QUIET)) {
+ printf("\nFreeBSD/x86 boot\n"
+ "Default: %u:%s(%up%u)%s\n"
+ "boot: ",
+ dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
+ dsk.part, kname);
+ }
+ if (ioctrl & IO_SERIAL)
+ sio_flush();
+ *cmd = '\0';
+ if (keyhit(0))
+ getstr(cmd, sizeof(cmd));
+ else if (!OPT_CHECK(RBX_QUIET))
+ putchar('\n');
+ if (parse_cmds(cmd, &dskupdated)) {
+ putchar('\a');
+ continue;
+ }
+ if (dskupdated && gptinit() != 0)
+ continue;
+ load();
+ }
+ /* NOTREACHED */
+}
+
+/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
+void
+exit(int x)
+{
+}
+
+static void
+load(void)
+{
+ union {
+ struct exec ex;
+ Elf32_Ehdr eh;
+ } hdr;
+ static Elf32_Phdr ep[2];
+ static Elf32_Shdr es[2];
+ caddr_t p;
+ ufs_ino_t ino;
+ uint32_t addr, x;
+ int fmt, i, j;
+
+ if (!(ino = lookup(kname))) {
+ if (!ls) {
+ printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG,
+ kname, dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
+ dsk.part);
+ }
+ return;
+ }
+ if (xfsread(ino, &hdr, sizeof(hdr)))
+ return;
+ if (N_GETMAGIC(hdr.ex) == ZMAGIC)
+ fmt = 0;
+ else if (IS_ELF(hdr.eh))
+ fmt = 1;
+ else {
+ printf("Invalid %s\n", "format");
+ return;
+ }
+ if (fmt == 0) {
+ addr = hdr.ex.a_entry & 0xffffff;
+ p = PTOV(addr);
+ fs_off = PAGE_SIZE;
+ if (xfsread(ino, p, hdr.ex.a_text))
+ return;
+ p += roundup2(hdr.ex.a_text, PAGE_SIZE);
+ if (xfsread(ino, p, hdr.ex.a_data))
+ return;
+ p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
+ p += sizeof(hdr.ex.a_syms);
+ if (hdr.ex.a_syms) {
+ if (xfsread(ino, p, hdr.ex.a_syms))
+ return;
+ p += hdr.ex.a_syms;
+ if (xfsread(ino, p, sizeof(int)))
+ return;
+ x = *(uint32_t *)p;
+ p += sizeof(int);
+ x -= sizeof(int);
+ if (xfsread(ino, p, x))
+ return;
+ p += x;
+ }
+ } else {
+ fs_off = hdr.eh.e_phoff;
+ for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
+ if (xfsread(ino, ep + j, sizeof(ep[0])))
+ return;
+ if (ep[j].p_type == PT_LOAD)
+ j++;
+ }
+ for (i = 0; i < 2; i++) {
+ p = PTOV(ep[i].p_paddr & 0xffffff);
+ fs_off = ep[i].p_offset;
+ if (xfsread(ino, p, ep[i].p_filesz))
+ return;
+ }
+ p += roundup2(ep[1].p_memsz, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
+ fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
+ (hdr.eh.e_shstrndx + 1);
+ if (xfsread(ino, &es, sizeof(es)))
+ return;
+ for (i = 0; i < 2; i++) {
+ memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
+ p += sizeof(es[i].sh_size);
+ fs_off = es[i].sh_offset;
+ if (xfsread(ino, p, es[i].sh_size))
+ return;
+ p += es[i].sh_size;
+ }
+ }
+ addr = hdr.eh.e_entry & 0xffffff;
+ }
+ bootinfo.bi_esymtab = VTOP(p);
+ bootinfo.bi_kernelname = VTOP(kname);
+ bootinfo.bi_bios_dev = dsk.drive;
+#ifdef LOADER_GELI_SUPPORT
+ geliargs.size = sizeof(geliargs);
+ explicit_bzero(gelipw, sizeof(gelipw));
+ gelibuf = malloc(sizeof(struct keybuf) + (GELI_MAX_KEYS * sizeof(struct keybuf_ent)));
+ geli_fill_keybuf(gelibuf);
+ geliargs.notapw = '\0';
+ geliargs.keybuf_sentinel = KEYBUF_SENTINEL;
+ geliargs.keybuf = gelibuf;
+#endif
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
+ MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff),
+ KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo)
+#ifdef LOADER_GELI_SUPPORT
+ , geliargs
+#endif
+ );
+}
+
+static int
+parse_cmds(char *cmdstr, int *dskupdated)
+{
+ char *arg = cmdstr;
+ char *ep, *p, *q;
+ const char *cp;
+ unsigned int drv;
+ int c, i, j;
+
+ *dskupdated = 0;
+ while ((c = *arg++)) {
+ if (c == ' ' || c == '\t' || c == '\n')
+ continue;
+ for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+ ep = p;
+ if (*p)
+ *p++ = 0;
+ if (c == '-') {
+ while ((c = *arg++)) {
+ if (c == 'P') {
+ if (*(uint8_t *)PTOV(0x496) & 0x10) {
+ cp = "yes";
+ } else {
+ opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
+ cp = "no";
+ }
+ printf("Keyboard: %s\n", cp);
+ continue;
+ } else if (c == 'S') {
+ j = 0;
+ while ((unsigned int)(i = *arg++ - '0') <= 9)
+ j = j * 10 + i;
+ if (j > 0 && i == -'0') {
+ comspeed = j;
+ break;
+ }
+ /* Fall through to error below ('S' not in optstr[]). */
+ }
+ for (i = 0; c != optstr[i]; i++)
+ if (i == NOPT - 1)
+ return -1;
+ opts ^= OPT_SET(flags[i]);
+ }
+ ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
+ OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
+ if (ioctrl & IO_SERIAL) {
+ if (sio_init(115200 / comspeed) != 0)
+ ioctrl &= ~IO_SERIAL;
+ }
+ } else {
+ for (q = arg--; *q && *q != '('; q++);
+ if (*q) {
+ drv = -1;
+ if (arg[1] == ':') {
+ drv = *arg - '0';
+ if (drv > 9)
+ return (-1);
+ arg += 2;
+ }
+ if (q - arg != 2)
+ return -1;
+ for (i = 0; arg[0] != dev_nm[i][0] ||
+ arg[1] != dev_nm[i][1]; i++)
+ if (i == NDEV - 1)
+ return -1;
+ dsk.type = i;
+ arg += 3;
+ dsk.unit = *arg - '0';
+ if (arg[1] != 'p' || dsk.unit > 9)
+ return -1;
+ arg += 2;
+ dsk.part = *arg - '0';
+ if (dsk.part < 1 || dsk.part > 9)
+ return -1;
+ arg++;
+ if (arg[0] != ')')
+ return -1;
+ arg++;
+ if (drv == -1)
+ drv = dsk.unit;
+ dsk.drive = (dsk.type <= TYPE_MAXHARD
+ ? DRV_HARD : 0) + drv;
+ *dskupdated = 1;
+ }
+ if ((i = ep - arg)) {
+ if ((size_t)i >= sizeof(kname))
+ return -1;
+ memcpy(kname, arg, i + 1);
+ }
+ }
+ arg = p;
+ }
+ return 0;
+}
+
+static int
+dskread(void *buf, daddr_t lba, unsigned nblk)
+{
+ int err;
+
+ err = drvread(&dsk, buf, lba + dsk.start, nblk);
+
+#ifdef LOADER_GELI_SUPPORT
+ if (err == 0 && is_geli(&dsk) == 0) {
+ /* Decrypt */
+ if (geli_read(&dsk, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE))
+ return (err);
+ }
+#endif
+
+ return (err);
+}
+
+#ifdef LOADER_GELI_SUPPORT
+/*
+ * Read function compartible with the ZFS callback, required to keep the GELI
+ * Implementation the same for both UFS and ZFS
+ */
+static int
+vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes)
+{
+ char *p;
+ daddr_t lba;
+ unsigned int nb;
+ struct dsk *dskp = (struct dsk *) priv;
+
+ if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
+ return (-1);
+
+ p = buf;
+ lba = off / DEV_BSIZE;
+ lba += dskp->start;
+
+ while (bytes > 0) {
+ nb = bytes / DEV_BSIZE;
+ if (nb > VBLKSIZE / DEV_BSIZE)
+ nb = VBLKSIZE / DEV_BSIZE;
+ if (drvread(dskp, dmadat->blkbuf, lba, nb))
+ return (-1);
+ memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE);
+ p += nb * DEV_BSIZE;
+ lba += nb;
+ bytes -= nb * DEV_BSIZE;
+ }
+
+ return (0);
+}
+#endif /* LOADER_GELI_SUPPORT */
diff --git a/stand/i386/gptboot/gptldr.S b/stand/i386/gptboot/gptldr.S
new file mode 100644
index 0000000..088122f
--- /dev/null
+++ b/stand/i386/gptboot/gptldr.S
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2007 Yahoo!, Inc.
+ * All rights reserved.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ *
+ * 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 author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * Partly from: src/sys/boot/i386/boot2/boot1.S 1.31
+ */
+
+/* Memory Locations */
+ .set MEM_REL,0x700 # Relocation address
+ .set MEM_ARG,0x900 # Arguments
+ .set MEM_ORG,0x7c00 # Origin
+ .set MEM_BUF,0x8cec # Load area
+ .set MEM_BTX,0x9000 # BTX start
+ .set MEM_JMP,0x9010 # BTX entry point
+ .set MEM_USR,0xa000 # Client start
+ .set BDA_BOOT,0x472 # Boot howto flag
+
+/* Misc. Constants */
+ .set SIZ_PAG,0x1000 # Page size
+ .set SIZ_SEC,0x200 # Sector size
+ .set COPY_BLKS,0x8 # Number of blocks
+ # to copy for boot2
+ .set COPY_BLK_SZ,0x8000 # Copy in 32k blocks; must be
+ # a multiple of 16 bytes
+
+ .globl start
+ .code16
+
+/*
+ * Copy BTX and boot2 to the right locations and start it all up.
+ */
+
+/*
+ * Setup the segment registers to flat addressing (segment 0) and setup the
+ * stack to end just below the start of our code.
+ */
+start: xor %cx,%cx # Zero
+ mov %cx,%es # Address
+ mov %cx,%ds # data
+ mov %cx,%ss # Set up
+ mov $start,%sp # stack
+
+/*
+ * BTX is right after us at 'end'. We read the length of BTX out of
+ * its header to find boot2. We need to copy boot2 to MEM_USR and BTX
+ * to MEM_BTX. Since those might overlap, we have to copy boot2
+ * backwards first and then copy BTX. We aren't sure exactly how long
+ * boot2 is, but it's currently under 128kB so we'll copy 4 blocks of 32kB
+ * each; this can be adjusted via COPY_BLK and COPY_BLK_SZ above.
+ */
+ mov $end,%bx # BTX
+ mov 0xa(%bx),%si # Get BTX length and set
+ add %bx,%si # %si to start of boot2
+ dec %si # Set %ds:%si to point at the
+ mov %si,%ax # last byte we want to copy
+ shr $4,%ax # from boot2, with %si made as
+ add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # small as possible.
+ and $0xf,%si #
+ mov %ax,%ds #
+ mov $MEM_USR/16,%ax # Set %es:(-1) to point at
+ add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # the last byte we
+ mov %ax,%es # want to copy boot2 into.
+ mov $COPY_BLKS,%bx # Copy COPY_BLKS 32k blocks
+copyloop:
+ add $COPY_BLK_SZ,%si # Adjust %ds:%si to point at
+ mov %ds,%ax # the end of the next 32k to
+ sub $COPY_BLK_SZ/16,%ax # copy from boot2
+ mov %ax,%ds
+ mov $COPY_BLK_SZ-1,%di # Adjust %es:%di to point at
+ mov %es,%ax # the end of the next 32k into
+ sub $COPY_BLK_SZ/16,%ax # which we want boot2 copied
+ mov %ax,%es
+ mov $COPY_BLK_SZ,%cx # Copy 32k
+ std
+ rep movsb
+ dec %bx
+ jnz copyloop
+ mov %cx,%ds # Reset %ds and %es
+ mov %cx,%es
+ mov $end,%bx # BTX
+ mov 0xa(%bx),%cx # Get BTX length and set
+ mov %bx,%si # %si to end of BTX
+ mov $MEM_BTX,%di # %di -> end of BTX at
+ add %cx,%si # MEM_BTX
+ add %cx,%di
+ dec %si
+ dec %di
+ rep movsb # Move BTX
+ cld # String ops inc
+/*
+ * Enable A20 so we can access memory above 1 meg.
+ * Use the zero-valued %cx as a timeout for embedded hardware which do not
+ * have a keyboard controller.
+ */
+seta20: cli # Disable interrupts
+seta20.1: dec %cx # Timeout?
+ jz seta20.3 # Yes
+ inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.1 # Yes
+ movb $0xd1,%al # Command: Write
+ outb %al,$0x64 # output port
+seta20.2: inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.2 # Yes
+ movb $0xdf,%al # Enable
+ outb %al,$0x60 # A20
+seta20.3: sti # Enable interrupts
+
+/*
+ * Save drive number from BIOS so boot2 can see it and start BTX.
+ */
+ movb %dl,MEM_ARG
+ jmp MEM_JMP # Start BTX
+end:
diff --git a/stand/i386/gptzfsboot/Makefile b/stand/i386/gptzfsboot/Makefile
new file mode 100644
index 0000000..97f1954
--- /dev/null
+++ b/stand/i386/gptzfsboot/Makefile
@@ -0,0 +1,87 @@
+# $FreeBSD$
+
+HAVE_GPT= yes
+
+.include <bsd.init.mk>
+
+.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/gptboot \
+ ${BOOTSRC}/i386/zfsboot ${BOOTSRC}/i386/common \
+ ${SASRC}
+
+FILES= gptzfsboot
+MAN= gptzfsboot.8
+
+NM?= nm
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+REL1= 0x700
+ORG1= 0x7c00
+ORG2= 0x0
+
+CFLAGS+=-DBOOTPROG=\"gptzfsboot\" \
+ -O1 \
+ -DGPT -DZFS -DBOOT2 \
+ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} \
+ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ -I${LDRSRC} \
+ -I${BOOTSRC}/i386/common \
+ -I${ZFSSRC} \
+ -I${SYSDIR}/cddl/boot/zfs \
+ -I${BOOTSRC}/i386/btx/lib \
+ -I${BOOTSRC}/i386/boot2 \
+ -Wall -Waggregate-return -Wbad-function-cast \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+ -Winline -Wno-pointer-sign
+
+NO_WCAST_ALIGN=
+
+.if ${COMPILER_TYPE} == "clang" || \
+ (${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} > 40201)
+CFLAGS+= -Wno-tentative-definition-incomplete-type
+.endif
+
+.if ${MACHINE} == "amd64"
+LIBZFSBOOT=${BOOTOBJ}/zfs32/libzfsboot.a
+.else
+LIBZFSBOOT=${BOOTOBJ}/zfs/libzfsboot.a
+.endif
+
+CFLAGS.gcc+= --param max-inline-insns-single=100
+
+LD_FLAGS+=${LD_FLAGS_BIN}
+
+CLEANFILES+= gptzfsboot
+
+gptzfsboot: gptldr.bin gptzfsboot.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \
+ -o ${.TARGET} gptzfsboot.bin
+
+CLEANFILES+= gptldr.bin gptldr.out gptldr.o
+
+gptldr.bin: gptldr.out
+ ${OBJCOPY} -S -O binary gptldr.out ${.TARGET}
+
+gptldr.out: gptldr.o
+ ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
+
+CLEANFILES+= gptzfsboot.bin gptzfsboot.out zfsboot.o sio.o cons.o \
+ drv.o gpt.o util.o ${OPENCRYPTO_XTS}
+
+gptzfsboot.bin: gptzfsboot.out
+ ${OBJCOPY} -S -O binary gptzfsboot.out ${.TARGET}
+
+gptzfsboot.out: ${BTXCRT} zfsboot.o sio.o gpt.o drv.o cons.o util.o \
+ ${OPENCRYPTO_XTS}
+ ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBGELIBOOT} ${LIBZFSBOOT} ${LIBSA32}
+
+zfsboot.o: ${ZFSSRC}/zfsimpl.c
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.gptldr.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/gptzfsboot/Makefile.depend b/stand/i386/gptzfsboot/Makefile.depend
new file mode 100644
index 0000000..295be1a
--- /dev/null
+++ b/stand/i386/gptzfsboot/Makefile.depend
@@ -0,0 +1,19 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libmd \
+ lib/libstand \
+ sys/boot/geli \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/lib \
+ sys/boot/libstand32 \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/gptzfsboot/gptzfsboot.8 b/stand/i386/gptzfsboot/gptzfsboot.8
new file mode 100644
index 0000000..66feb6d
--- /dev/null
+++ b/stand/i386/gptzfsboot/gptzfsboot.8
@@ -0,0 +1,193 @@
+.\" Copyright (c) 2014 Andriy Gapon <avg@FreeBSD.org>
+.\" 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 15, 2014
+.Dt GPTZFSBOOT 8
+.Os
+.Sh NAME
+.Nm gptzfsboot
+.Nd GPT bootcode for ZFS on BIOS-based computers
+.Sh DESCRIPTION
+.Nm
+is used on BIOS-based computers to boot from a filesystem in
+a ZFS pool.
+.Nm
+is installed in a
+.Cm freebsd-boot
+partition of a GPT-partitioned disk with
+.Xr gpart 8 .
+.Sh IMPLEMENTATION NOTES
+The GPT standard allows a variable number of partitions, but
+.Nm
+only boots from tables with 128 partitions or less.
+.Sh BOOTING
+.Nm
+tries to find all ZFS pools that are composed of BIOS-visible
+hard disks or partitions on them.
+.Nm
+looks for ZFS device labels on all visible disks and in discovered
+supported partitions for all supported partition scheme types.
+The search starts with the disk from which
+.Nm
+itself was loaded.
+Other disks are probed in BIOS defined order.
+After a disk is probed and
+.Nm
+determines that the whole disk is not a ZFS pool member, the
+individual partitions are probed in their partition table order.
+Currently GPT and MBR partition schemes are supported.
+With the GPT scheme, only partitions of type
+.Cm freebsd-zfs
+are probed.
+The first pool seen during probing is used as a default boot pool.
+.Pp
+The filesystem specified by the
+.Cm bootfs
+property of the pool is used as a default boot filesystem.
+If the
+.Cm bootfs
+property is not set, then the root filesystem of the pool is used as
+the default.
+.Xr zfsloader 8
+is loaded from the boot filesystem.
+If
+.Pa /boot.config
+or
+.Pa /boot/config
+is present in the boot filesystem, boot options are read from it
+in the same way as
+.Xr boot 8 .
+.Pp
+The ZFS GUIDs of the first successfully probed device and the first
+detected pool are made available to
+.Xr zfsloader 8
+in the
+.Cm vfs.zfs.boot.primary_vdev
+and
+.Cm vfs.zfs.boot.primary_pool
+variables.
+.Sh USAGE
+Normally
+.Nm
+will boot in fully automatic mode.
+However, like
+.Xr boot 8 ,
+it is possible to interrupt the automatic boot process and interact with
+.Nm
+through a prompt.
+.Nm
+accepts all the options that
+.Xr boot 8
+supports.
+.Pp
+The filesystem specification and the path to
+.Xr zfsloader 8
+are different from
+.Xr boot 8 .
+The format is
+.Pp
+.Sm off
+.Oo zfs:pool/filesystem: Oc Oo /path/to/loader Oc
+.Sm on
+.Pp
+Both the filesystem and the path can be specified.
+If only a path is specified, then the default filesystem is used.
+If only a pool and filesystem are specified, then
+.Pa /boot/zfsloader
+is used as a path.
+.Pp
+Additionally, the
+.Ic status
+command can be used to query information about discovered pools.
+The output format is similar to that of
+.Cm zpool status
+.Pq see Xr zpool 8 .
+.Pp
+The configured or automatically determined ZFS boot filesystem is
+stored in the
+.Xr zfsloader 8
+.Cm loaddev
+variable, and also set as the initial value of the
+.Cm currdev
+variable.
+.Sh FILES
+.Bl -tag -width /boot/gptzfsboot -compact
+.It Pa /boot/gptzfsboot
+boot code binary
+.It Pa /boot.config
+parameters for the boot block
+.Pq optional
+.It Pa /boot/config
+alternative parameters for the boot block
+.Pq optional
+.El
+.Sh EXAMPLES
+.Nm
+is typically installed in combination with a
+.Dq protective MBR
+.Po
+see
+.Xr gpart 8
+.Pc .
+To install
+.Nm
+on the
+.Pa ada0
+drive:
+.Bd -literal -offset indent
+gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada0
+.Ed
+.Pp
+.Nm
+can also be installed without the PMBR:
+.Bd -literal -offset indent
+gpart bootcode -p /boot/gptzfsboot -i 1 ada0
+.Ed
+.Sh SEE ALSO
+.Xr boot.config 5 ,
+.Xr boot 8 ,
+.Xr gpart 8 ,
+.Xr loader 8 ,
+.Xr zfsloader 8 ,
+.Xr zpool 8
+.Sh HISTORY
+.Nm
+appeared in FreeBSD 7.3.
+.Sh AUTHORS
+This manual page was written by
+.An Andriy Gapon Aq avg@FreeBSD.org .
+.Sh BUGS
+.Nm
+looks for ZFS meta-data only in MBR partitions
+.Pq known on FreeBSD as slices .
+It does not look into BSD
+.Xr disklabel 8
+partitions that are traditionally called partitions.
+If a disklabel partition happens to be placed so that ZFS meta-data can be
+found at the fixed offsets relative to a slice, then
+.Nm
+will recognize the partition as a part of a ZFS pool,
+but this is not guaranteed to happen.
diff --git a/stand/i386/kgzldr/Makefile b/stand/i386/kgzldr/Makefile
new file mode 100644
index 0000000..5a03b83
--- /dev/null
+++ b/stand/i386/kgzldr/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= kgzldr.o
+STRIP=
+BINMODE=${LIBMODE}
+BINDIR= ${LIBDIR}
+MAN=
+
+SRCS= start.s boot.c inflate.c lib.c crt.s sio.s
+CFLAGS= -Os
+CFLAGS+=-DKZIP
+NO_SHARED=
+LDFLAGS+=-Wl,-r
+.PATH: ${SYSDIR}/kern
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+AFLAGS+=--defsym SIO_PRT=${BOOT_COMCONSOLE_PORT}
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/kgzldr/Makefile.depend b/stand/i386/kgzldr/Makefile.depend
new file mode 100644
index 0000000..79506ce
--- /dev/null
+++ b/stand/i386/kgzldr/Makefile.depend
@@ -0,0 +1,12 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/kgzldr/boot.c b/stand/i386/kgzldr/boot.c
new file mode 100644
index 0000000..45ed2ee
--- /dev/null
+++ b/stand/i386/kgzldr/boot.c
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <sys/inflate.h>
+
+#include "kgzldr.h"
+
+#define KGZ_HEAD 0xa /* leading bytes to ignore */
+#define KGZ_TAIL 0x8 /* trailing bytes to ignore */
+
+#define E_FMT 1 /* Error: Invalid format */
+#define E_MEM 2 /* Error: Out of memory */
+
+struct kgz_hdr {
+ char ident[4]; /* identification */
+ uint32_t dload; /* decoded image load address */
+ uint32_t dsize; /* decoded image size */
+ uint32_t isize; /* image size in memory */
+ uint32_t entry; /* program entry point */
+ uint32_t nsize; /* encoded image size */
+};
+extern struct kgz_hdr kgz; /* header */
+extern uint8_t kgz_ndata[]; /* encoded image */
+
+static const char *const msg[] = {
+ "done",
+ "invalid format",
+ "out of memory"
+};
+
+static const u_char *ip; /* input pointer */
+static u_char *op; /* output pointer */
+
+static struct inflate infl; /* inflate() parameters */
+
+static int decode(void);
+static int input(void *);
+static int output(void *, u_char *, u_long);
+
+/*
+ * Uncompress and boot a kernel.
+ */
+int
+boot(int howto)
+{
+ int err;
+
+ kgz_con = howto & RB_SERIAL ? KGZ_SIO : KGZ_CRT;
+ putstr("Uncompressing ... ");
+ err = decode();
+ putstr(msg[err]);
+ putstr("\n");
+ if (err) {
+ putstr("System halted");
+ for (;;)
+ ;
+ }
+ return err;
+}
+
+/*
+ * Interface with inflate() to uncompress the data.
+ */
+static int
+decode(void)
+{
+ static u_char slide[GZ_WSIZE];
+ int err;
+
+ ip = kgz_ndata + KGZ_HEAD;
+ op = (u_char *)kgz.dload;
+ infl.gz_input = input;
+ infl.gz_output = output;
+ infl.gz_slide = slide;
+ err = inflate(&infl);
+ return err ? err == 3 ? E_MEM : E_FMT : 0;
+}
+
+/*
+ * Read a byte.
+ */
+static int
+input(void *dummy)
+{
+ if ((size_t)(ip - kgz_ndata) + KGZ_TAIL > kgz.nsize)
+ return GZ_EOF;
+ return *ip++;
+}
+
+/*
+ * Write some bytes.
+ */
+static int
+output(void *dummy, u_char * ptr, u_long len)
+{
+ if (op - (u_char *)kgz.dload + len > kgz.dsize)
+ return -1;
+ while (len--)
+ *op++ = *ptr++;
+ return 0;
+}
diff --git a/stand/i386/kgzldr/crt.s b/stand/i386/kgzldr/crt.s
new file mode 100644
index 0000000..cfb479f
--- /dev/null
+++ b/stand/i386/kgzldr/crt.s
@@ -0,0 +1,83 @@
+#
+# Copyright (c) 1999 Global Technology Associates, Inc.
+# 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.
+#
+# From: btx.s 1.10 1999/02/25 16:27:41 rnordier
+# $FreeBSD$
+#
+
+# Screen defaults and assumptions.
+
+ .set SCR_MAT,0x7 # Mode/attribute
+ .set SCR_COL,0x50 # Columns per row
+ .set SCR_ROW,0x19 # Rows per screen
+
+# BIOS Data Area locations.
+
+ .set BDA_SCR,0x449 # Video mode
+ .set BDA_POS,0x450 # Cursor position
+
+ .globl crt_putchr
+
+# void crt_putchr(int c)
+
+crt_putchr: movb 0x4(%esp,1),%al # Get character
+ pusha # Save
+ xorl %ecx,%ecx # Zero for loops
+ movb $SCR_MAT,%ah # Mode/attribute
+ movl $BDA_POS,%ebx # BDA pointer
+ movw (%ebx),%dx # Cursor position
+ movl $0xb8000,%edi # Regen buffer (color)
+ cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode?
+ jne crt_putchr.1 # No
+ xorw %di,%di # Regen buffer (mono)
+crt_putchr.1: cmpb $0xa,%al # New line?
+ je crt_putchr.2 # Yes
+ xchgl %eax,%ecx # Save char
+ movb $SCR_COL,%al # Columns per row
+ mulb %dh # * row position
+ addb %dl,%al # + column
+ adcb $0x0,%ah # position
+ shll %eax # * 2
+ xchgl %eax,%ecx # Swap char, offset
+ movw %ax,(%edi,%ecx,1) # Write attr:char
+ incl %edx # Bump cursor
+ cmpb $SCR_COL,%dl # Beyond row?
+ jb crt_putchr.3 # No
+crt_putchr.2: xorb %dl,%dl # Zero column
+ incb %dh # Bump row
+crt_putchr.3: cmpb $SCR_ROW,%dh # Beyond screen?
+ jb crt_putchr.4 # No
+ leal 2*SCR_COL(%edi),%esi # New top line
+ movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
+ rep # Scroll
+ movsl # screen
+ movb $' ',%al # Space
+ movb $SCR_COL,%cl # Columns to clear
+ rep # Clear
+ stosw # line
+ movb $SCR_ROW-1,%dh # Bottom line
+crt_putchr.4: movw %dx,(%ebx) # Update position
+ popa # Restore
+ ret # To caller
diff --git a/stand/i386/kgzldr/kgzldr.h b/stand/i386/kgzldr/kgzldr.h
new file mode 100644
index 0000000..5cd5b44
--- /dev/null
+++ b/stand/i386/kgzldr/kgzldr.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define KGZ_CRT 0x1 /* Video console */
+#define KGZ_SIO 0x2 /* Serial console */
+
+extern int kgz_con;
+
+int boot(int);
+
+unsigned char *kzipmalloc(int);
+void kzipfree(void *);
+void putstr(const char *);
+
+void crt_putchr(int);
+void sio_putchr(int);
diff --git a/stand/i386/kgzldr/lib.c b/stand/i386/kgzldr/lib.c
new file mode 100644
index 0000000..538875b
--- /dev/null
+++ b/stand/i386/kgzldr/lib.c
@@ -0,0 +1,88 @@
+/*-
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <stddef.h>
+
+#include "kgzldr.h"
+
+#define MEMSIZ 0x8000 /* Memory pool size */
+
+int kgz_con; /* Console control */
+
+static size_t memtot; /* Memory allocated: bytes */
+static u_int memcnt; /* Memory allocated: blocks */
+
+/*
+ * Library functions required by inflate().
+ */
+
+/*
+ * Allocate memory block.
+ */
+unsigned char *
+kzipmalloc(int size)
+{
+ static u_char mem[MEMSIZ];
+ void *ptr;
+
+ if (memtot + size > MEMSIZ)
+ return NULL;
+ ptr = mem + memtot;
+ memtot += size;
+ memcnt++;
+ return ptr;
+}
+
+/*
+ * Free allocated memory block.
+ */
+void
+kzipfree(void *ptr)
+{
+ memcnt--;
+ if (!memcnt)
+ memtot = 0;
+}
+
+/*
+ * Write a string to the console.
+ */
+void
+putstr(const char *str)
+{
+ int c;
+
+ while ((c = *str++)) {
+ if (kgz_con & KGZ_CRT)
+ crt_putchr(c);
+ if (kgz_con & KGZ_SIO)
+ sio_putchr(c);
+ }
+}
diff --git a/stand/i386/kgzldr/sio.s b/stand/i386/kgzldr/sio.s
new file mode 100644
index 0000000..ff174eb
--- /dev/null
+++ b/stand/i386/kgzldr/sio.s
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 1999 Global Technology Associates, Inc.
+# 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.
+#
+# From: sio.s 1.3 1999/01/10 14:48:03 rnordier
+# $FreeBSD$
+#
+
+ .globl sio_putchr
+
+# void sio_putchr(int c)
+
+sio_putchr: movw $SIO_PRT+0x5,%dx # Line status reg
+ xor %ecx,%ecx # Timeout
+ movb $0x40,%ch # counter
+sio_putchr.1: inb %dx,%al # Transmitter
+ testb $0x20,%al # buffer empty?
+ loopz sio_putchr.1 # No
+ jz sio_putchr.2 # If timeout
+ movb 0x4(%esp,1),%al # Get character
+ subb $0x5,%dl # Transmitter hold reg
+ outb %al,%dx # Write character
+sio_putchr.2: ret # To caller
diff --git a/stand/i386/kgzldr/start.s b/stand/i386/kgzldr/start.s
new file mode 100644
index 0000000..550fa52
--- /dev/null
+++ b/stand/i386/kgzldr/start.s
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 1999 Global Technology Associates, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+ .set entry,0x10 # kgz.entry
+
+ .globl _start
+
+# C startup code for kgzldr.
+
+_start: cld # String ops inc
+ movl $_edata,%edi # Start of bss
+ movl $_end,%ecx # Compute
+ subl %edi,%ecx # size
+ xorl %eax,%eax # Zero
+ rep # Clear
+ stosb # bss
+ pushl 0x4(%esp) # Pass howto flags
+ call boot # Call C code
+ popl %ecx # Clear stack
+ jmp *kgz+entry # To loaded code
diff --git a/stand/i386/libfirewire/Makefile b/stand/i386/libfirewire/Makefile
new file mode 100644
index 0000000..d3f88c4
--- /dev/null
+++ b/stand/i386/libfirewire/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+LIB= firewire
+INTERNALLIB=
+
+.PATH: ${SYSDIR}/dev/dcons ${SYSDIR}/dev/firewire
+SRCS+= firewire.c fwohci.c dconsole.c
+SRCS+= dcons.c fwcrom.c
+
+CFLAGS+= -D_BOOT
+
+CFLAGS+= -I${LDRSRC}
+CFLAGS+= -I${BTXLIB}
+CFLAGS+= -I${BOOTSRC}/i386/libi386
+
+CFLAGS+= -Wformat -Wall
+
+.include <bsd.lib.mk>
diff --git a/stand/i386/libfirewire/Makefile.depend b/stand/i386/libfirewire/Makefile.depend
new file mode 100644
index 0000000..18be76b
--- /dev/null
+++ b/stand/i386/libfirewire/Makefile.depend
@@ -0,0 +1,13 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/libfirewire/dconsole.c b/stand/i386/libfirewire/dconsole.c
new file mode 100644
index 0000000..1528faf
--- /dev/null
+++ b/stand/i386/libfirewire/dconsole.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2004 Hidetoshi Shimokawa
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include <sys/param.h>
+#include <btxv86.h>
+#include <dev/dcons/dcons.h>
+
+void fw_enable(void);
+void fw_poll(void);
+
+static void dconsole_probe(struct console *cp);
+static int dconsole_init(int arg);
+static void dconsole_putchar(int c);
+static int dconsole_getchar(void);
+static int dconsole_ischar(void);
+
+static int dcons_started = 0;
+
+#define DCONS_BUF_SIZE (64*1024)
+static struct dcons_softc sc[DCONS_NPORT];
+uint32_t dcons_paddr;
+
+/* The buffer must be allocated in BSS becase:
+ * - The dcons driver in the kernel is initialized before VM/pmap is
+ * initialized, so that the buffer must be allocate in the region
+ * that is mapped at the very early boot state.
+ * - We expect identiy map only for regions before KERNLOAD
+ * (i386:4MB amd64:1MB).
+ * - It seems that heap in conventional memory(640KB) is not sufficient
+ * and we move it to high address as LOADER_SUPPORT_BZIP2.
+ * - BSS is placed in conventional memory.
+ */
+static char dcons_buffer[DCONS_BUF_SIZE + PAGE_SIZE];
+
+struct console dconsole = {
+ "dcons",
+ "dumb console port",
+ 0,
+ dconsole_probe,
+ dconsole_init,
+ dconsole_putchar,
+ dconsole_getchar,
+ dconsole_ischar
+};
+
+#define DCONSOLE_AS_MULTI_CONSOLE 1
+
+static void
+dconsole_probe(struct console *cp)
+{
+ /* XXX check the BIOS equipment list? */
+ cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+#if DCONSOLE_AS_MULTI_CONSOLE
+ dconsole_init(0);
+ cp->c_flags |= (C_ACTIVEIN | C_ACTIVEOUT);
+#endif
+}
+
+static int
+dconsole_init(int arg)
+{
+ char buf[16], *dbuf;
+ int size;
+
+ if (dcons_started && arg == 0)
+ return 0;
+ dcons_started = 1;
+
+ size = DCONS_BUF_SIZE;
+ dbuf = (char *)round_page((vm_offset_t)&dcons_buffer[0]);
+ dcons_paddr = VTOP(dbuf);
+ sprintf(buf, "0x%08x", dcons_paddr);
+ setenv("dcons.addr", buf, 1);
+
+ dcons_init((struct dcons_buf *)dbuf, size, sc);
+ sprintf(buf, "%d", size);
+ setenv("dcons.size", buf, 1);
+ fw_enable();
+ return(0);
+}
+
+static void
+dconsole_putchar(int c)
+{
+ dcons_putc(&sc[0], c);
+}
+
+static int
+dconsole_getchar(void)
+{
+ fw_poll();
+ return (dcons_checkc(&sc[0]));
+}
+
+static int
+dconsole_ischar(void)
+{
+ fw_poll();
+ return (dcons_ischar(&sc[0]));
+}
diff --git a/stand/i386/libfirewire/firewire.c b/stand/i386/libfirewire/firewire.c
new file mode 100644
index 0000000..4840325
--- /dev/null
+++ b/stand/i386/libfirewire/firewire.c
@@ -0,0 +1,484 @@
+/*-
+ * Copyright (c) 2004 Hidetoshi Shimokawa <simokawa@FreeBSD.ORG>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * FireWire disk device handling.
+ *
+ */
+
+#include <stand.h>
+
+#include <machine/bootinfo.h>
+
+#include <stdarg.h>
+
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <libi386.h>
+#include "fwohci.h"
+#include <dev/dcons/dcons.h>
+
+/* XXX */
+#define BIT4x2(x,y) uint8_t y:4, x:4
+#define BIT16x2(x,y) uint32_t y:16, x:16
+#define _KERNEL
+#include <dev/firewire/iec13213.h>
+
+extern uint32_t dcons_paddr;
+extern struct console dconsole;
+
+struct crom_src_buf {
+ struct crom_src src;
+ struct crom_chunk root;
+ struct crom_chunk vendor;
+ struct crom_chunk hw;
+ /* for dcons */
+ struct crom_chunk unit;
+ struct crom_chunk spec;
+ struct crom_chunk ver;
+};
+
+static int fw_init(void);
+static int fw_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int fw_open(struct open_file *f, ...);
+static int fw_close(struct open_file *f);
+static int fw_print(int verbose);
+static void fw_cleanup(void);
+
+void fw_enable(void);
+
+struct devsw fwohci = {
+ "FW1394", /* 7 chars at most */
+ DEVT_NET,
+ fw_init,
+ fw_strategy,
+ fw_open,
+ fw_close,
+ noioctl,
+ fw_print,
+ fw_cleanup
+};
+
+static struct fwohci_softc fwinfo[MAX_OHCI];
+static int fw_initialized = 0;
+
+static void
+fw_probe(int index, struct fwohci_softc *sc)
+{
+ int err;
+
+ sc->state = FWOHCI_STATE_INIT;
+ err = biospci_find_devclass(
+ 0x0c0010 /* Serial:FireWire:OHCI */,
+ index /* index */,
+ &sc->locator);
+
+ if (err != 0) {
+ sc->state = FWOHCI_STATE_DEAD;
+ return;
+ }
+
+ biospci_write_config(sc->locator,
+ 0x4 /* command */,
+ 0x6 /* enable bus master and memory mapped I/O */,
+ 1 /* word */);
+
+ biospci_read_config(sc->locator, 0x00 /*devid*/, 2 /*dword*/,
+ &sc->devid);
+ biospci_read_config(sc->locator, 0x10 /*base_addr*/, 2 /*dword*/,
+ &sc->base_addr);
+
+ sc->handle = (uint32_t)PTOV(sc->base_addr);
+ sc->bus_id = OREAD(sc, OHCI_BUS_ID);
+
+ return;
+}
+
+static int
+fw_init(void)
+{
+ int i, avail;
+ struct fwohci_softc *sc;
+
+ if (fw_initialized)
+ return (0);
+
+ avail = 0;
+ for (i = 0; i < MAX_OHCI; i ++) {
+ sc = &fwinfo[i];
+ fw_probe(i, sc);
+ if (sc->state == FWOHCI_STATE_DEAD)
+ break;
+ avail ++;
+ break;
+ }
+ fw_initialized = 1;
+
+ return (0);
+}
+
+
+/*
+ * Print information about OHCI chips
+ */
+static int
+fw_print(int verbose)
+{
+ char line[80];
+ int i, ret = 0;
+ struct fwohci_softc *sc;
+
+ printf("%s devices:", fwohci.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ for (i = 0; i < MAX_OHCI; i ++) {
+ sc = &fwinfo[i];
+ if (sc->state == FWOHCI_STATE_DEAD)
+ break;
+ snprintf(line, sizeof(line), "%d: locator=0x%04x devid=0x%08x"
+ " base_addr=0x%08x handle=0x%08x bus_id=0x%08x\n",
+ i, sc->locator, sc->devid,
+ sc->base_addr, sc->handle, sc->bus_id);
+ ret = pager_output(line);
+ if (ret != 0)
+ break;
+ }
+ return (ret);
+}
+
+static int
+fw_open(struct open_file *f, ...)
+{
+#if 0
+ va_list ap;
+ struct i386_devdesc *dev;
+ struct open_disk *od;
+ int error;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct i386_devdesc *);
+ va_end(ap);
+#endif
+
+ return (ENXIO);
+}
+
+static int
+fw_close(struct open_file *f)
+{
+ return (0);
+}
+
+static void
+fw_cleanup()
+{
+ struct dcons_buf *db;
+
+ /* invalidate dcons buffer */
+ if (dcons_paddr) {
+ db = (struct dcons_buf *)PTOV(dcons_paddr);
+ db->magic = 0;
+ }
+}
+
+static int
+fw_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ return (EIO);
+}
+
+static void
+fw_init_crom(struct fwohci_softc *sc)
+{
+ struct crom_src *src;
+
+ printf("fw_init_crom\n");
+ sc->crom_src_buf = (struct crom_src_buf *)
+ malloc(sizeof(struct crom_src_buf));
+ if (sc->crom_src_buf == NULL)
+ return;
+
+ src = &sc->crom_src_buf->src;
+ bzero(src, sizeof(struct crom_src));
+
+ /* BUS info sample */
+ src->hdr.info_len = 4;
+
+ src->businfo.bus_name = CSR_BUS_NAME_IEEE1394;
+
+ src->businfo.irmc = 1;
+ src->businfo.cmc = 1;
+ src->businfo.isc = 1;
+ src->businfo.bmc = 1;
+ src->businfo.pmc = 0;
+ src->businfo.cyc_clk_acc = 100;
+ src->businfo.max_rec = sc->maxrec;
+ src->businfo.max_rom = MAXROM_4;
+#define FW_GENERATION_CHANGEABLE 2
+ src->businfo.generation = FW_GENERATION_CHANGEABLE;
+ src->businfo.link_spd = sc->speed;
+
+ src->businfo.eui64.hi = sc->eui.hi;
+ src->businfo.eui64.lo = sc->eui.lo;
+
+ STAILQ_INIT(&src->chunk_list);
+
+ sc->crom_src = src;
+ sc->crom_root = &sc->crom_src_buf->root;
+}
+
+static void
+fw_reset_crom(struct fwohci_softc *sc)
+{
+ struct crom_src_buf *buf;
+ struct crom_src *src;
+ struct crom_chunk *root;
+
+ printf("fw_reset\n");
+ if (sc->crom_src_buf == NULL)
+ fw_init_crom(sc);
+
+ buf = sc->crom_src_buf;
+ src = sc->crom_src;
+ root = sc->crom_root;
+
+ STAILQ_INIT(&src->chunk_list);
+
+ bzero(root, sizeof(struct crom_chunk));
+ crom_add_chunk(src, NULL, root, 0);
+ crom_add_entry(root, CSRKEY_NCAP, 0x0083c0); /* XXX */
+ /* private company_id */
+ crom_add_entry(root, CSRKEY_VENDOR, CSRVAL_VENDOR_PRIVATE);
+#ifdef __DragonFly__
+ crom_add_simple_text(src, root, &buf->vendor, "DragonFly Project");
+#else
+ crom_add_simple_text(src, root, &buf->vendor, "FreeBSD Project");
+#endif
+}
+
+
+#define ADDR_HI(x) (((x) >> 24) & 0xffffff)
+#define ADDR_LO(x) ((x) & 0xffffff)
+
+static void
+dcons_crom(struct fwohci_softc *sc)
+{
+ struct crom_src_buf *buf;
+ struct crom_src *src;
+ struct crom_chunk *root;
+
+ buf = sc->crom_src_buf;
+ src = sc->crom_src;
+ root = sc->crom_root;
+
+ bzero(&buf->unit, sizeof(struct crom_chunk));
+
+ crom_add_chunk(src, root, &buf->unit, CROM_UDIR);
+ crom_add_entry(&buf->unit, CSRKEY_SPEC, CSRVAL_VENDOR_PRIVATE);
+ crom_add_simple_text(src, &buf->unit, &buf->spec, "FreeBSD");
+ crom_add_entry(&buf->unit, CSRKEY_VER, DCONS_CSR_VAL_VER);
+ crom_add_simple_text(src, &buf->unit, &buf->ver, "dcons");
+ crom_add_entry(&buf->unit, DCONS_CSR_KEY_HI, ADDR_HI(dcons_paddr));
+ crom_add_entry(&buf->unit, DCONS_CSR_KEY_LO, ADDR_LO(dcons_paddr));
+}
+
+void
+fw_crom(struct fwohci_softc *sc)
+{
+ struct crom_src *src;
+ void *newrom;
+
+ fw_reset_crom(sc);
+ dcons_crom(sc);
+
+ newrom = malloc(CROMSIZE);
+ src = &sc->crom_src_buf->src;
+ crom_load(src, (uint32_t *)newrom, CROMSIZE);
+ if (bcmp(newrom, sc->config_rom, CROMSIZE) != 0) {
+ /* Bump generation and reload. */
+ src->businfo.generation++;
+
+ /* Handle generation count wraps. */
+ if (src->businfo.generation < 2)
+ src->businfo.generation = 2;
+
+ /* Recalculate CRC to account for generation change. */
+ crom_load(src, (uint32_t *)newrom, CROMSIZE);
+ bcopy(newrom, (void *)sc->config_rom, CROMSIZE);
+ }
+ free(newrom);
+}
+
+static int
+fw_busreset(struct fwohci_softc *sc)
+{
+ int count;
+
+ if (sc->state < FWOHCI_STATE_ENABLED) {
+ printf("fwohci not enabled\n");
+ return(CMD_OK);
+ }
+ fw_crom(sc);
+ fwohci_ibr(sc);
+ count = 0;
+ while (sc->state< FWOHCI_STATE_NORMAL) {
+ fwohci_poll(sc);
+ count ++;
+ if (count > 1000) {
+ printf("give up to wait bus initialize\n");
+ return (-1);
+ }
+ }
+ printf("poll count = %d\n", count);
+ return (0);
+}
+
+void
+fw_enable(void)
+{
+ struct fwohci_softc *sc;
+ int i;
+
+ if (fw_initialized == 0)
+ fw_init();
+
+ for (i = 0; i < MAX_OHCI; i ++) {
+ sc = &fwinfo[i];
+ if (sc->state != FWOHCI_STATE_INIT)
+ break;
+
+ sc->config_rom = (uint32_t *)
+ (((uint32_t)sc->config_rom_buf
+ + (CROMSIZE - 1)) & ~(CROMSIZE - 1));
+#if 0
+ printf("configrom: %08p %08p\n",
+ sc->config_rom_buf, sc->config_rom);
+#endif
+ if (fwohci_init(sc, 0) == 0) {
+ sc->state = FWOHCI_STATE_ENABLED;
+ fw_busreset(sc);
+ } else
+ sc->state = FWOHCI_STATE_DEAD;
+ }
+}
+
+void
+fw_poll(void)
+{
+ struct fwohci_softc *sc;
+ int i;
+
+ if (fw_initialized == 0)
+ return;
+
+ for (i = 0; i < MAX_OHCI; i ++) {
+ sc = &fwinfo[i];
+ if (sc->state < FWOHCI_STATE_ENABLED)
+ break;
+ fwohci_poll(sc);
+ }
+}
+
+#if 0 /* for debug */
+static int
+fw_busreset_cmd(int argc, char *argv[])
+{
+ struct fwohci_softc *sc;
+ int i;
+
+ for (i = 0; i < MAX_OHCI; i ++) {
+ sc = &fwinfo[i];
+ if (sc->state < FWOHCI_STATE_INIT)
+ break;
+ fw_busreset(sc);
+ }
+ return(CMD_OK);
+}
+
+static int
+fw_poll_cmd(int argc, char *argv[])
+{
+ fw_poll();
+ return(CMD_OK);
+}
+
+static int
+fw_enable_cmd(int argc, char *argv[])
+{
+ fw_print(0);
+ fw_enable();
+ return(CMD_OK);
+}
+
+
+static int
+dcons_enable(int argc, char *argv[])
+{
+ dconsole.c_init(0);
+ fw_enable();
+ dconsole.c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
+ return(CMD_OK);
+}
+
+static int
+dcons_read(int argc, char *argv[])
+{
+ char c;
+ while (dconsole.c_ready()) {
+ c = dconsole.c_in();
+ printf("%c", c);
+ }
+ printf("\r\n");
+ return(CMD_OK);
+}
+
+static int
+dcons_write(int argc, char *argv[])
+{
+ int len, i;
+ if (argc < 2)
+ return(CMD_OK);
+
+ len = strlen(argv[1]);
+ for (i = 0; i < len; i ++)
+ dconsole.c_out(argv[1][i]);
+ dconsole.c_out('\r');
+ dconsole.c_out('\n');
+ return(CMD_OK);
+}
+COMMAND_SET(firewire, "firewire", "enable firewire", fw_enable_cmd);
+COMMAND_SET(fwbusreset, "fwbusreset", "firewire busreset", fw_busreset_cmd);
+COMMAND_SET(fwpoll, "fwpoll", "firewire poll", fw_poll_cmd);
+COMMAND_SET(dcons, "dcons", "enable dcons", dcons_enable);
+COMMAND_SET(dread, "dread", "read from dcons", dcons_read);
+COMMAND_SET(dwrite, "dwrite", "write to dcons", dcons_write);
+#endif
diff --git a/stand/i386/libfirewire/fwohci.c b/stand/i386/libfirewire/fwohci.c
new file mode 100644
index 0000000..567abcf
--- /dev/null
+++ b/stand/i386/libfirewire/fwohci.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2003 Hidetoshi Shimokawa
+ * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the acknowledgement as bellow:
+ *
+ * This product includes software developed by K. Kobayashi and H. Shimokawa
+ *
+ * 4. 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <stand.h>
+#include <btxv86.h>
+#include <bootstrap.h>
+
+#include "fwohci.h"
+#include "fwohcireg.h"
+#include <dev/firewire/firewire_phy.h>
+
+static uint32_t fwphy_wrdata ( struct fwohci_softc *, uint32_t, uint32_t);
+static uint32_t fwphy_rddata ( struct fwohci_softc *, uint32_t);
+int firewire_debug=0;
+
+#if 0
+#define device_printf(a, x, ...) printf("FW1394: " x, ## __VA_ARGS__)
+#else
+#define device_printf(a, x, ...)
+#endif
+
+#define device_t int
+#define DELAY(x) delay(x)
+
+#define MAX_SPEED 3
+#define MAXREC(x) (2 << (x))
+char *linkspeed[] = {
+ "S100", "S200", "S400", "S800",
+ "S1600", "S3200", "undef", "undef"
+};
+
+#define FW_EUI64_BYTE(eui, x) \
+ ((((x)<4)? \
+ ((eui)->hi >> (8*(3-(x)))): \
+ ((eui)->lo >> (8*(7-(x)))) \
+ ) & 0xff)
+
+/*
+ * Communication with PHY device
+ */
+static uint32_t
+fwphy_wrdata( struct fwohci_softc *sc, uint32_t addr, uint32_t data)
+{
+ uint32_t fun;
+
+ addr &= 0xf;
+ data &= 0xff;
+
+ fun = (PHYDEV_WRCMD | (addr << PHYDEV_REGADDR) | (data << PHYDEV_WRDATA));
+ OWRITE(sc, OHCI_PHYACCESS, fun);
+ DELAY(100);
+
+ return(fwphy_rddata( sc, addr));
+}
+
+static uint32_t
+fwphy_rddata(struct fwohci_softc *sc, u_int addr)
+{
+ uint32_t fun, stat;
+ u_int i, retry = 0;
+
+ addr &= 0xf;
+#define MAX_RETRY 100
+again:
+ OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_REG_FAIL);
+ fun = PHYDEV_RDCMD | (addr << PHYDEV_REGADDR);
+ OWRITE(sc, OHCI_PHYACCESS, fun);
+ for ( i = 0 ; i < MAX_RETRY ; i ++ ){
+ fun = OREAD(sc, OHCI_PHYACCESS);
+ if ((fun & PHYDEV_RDCMD) == 0 && (fun & PHYDEV_RDDONE) != 0)
+ break;
+ DELAY(100);
+ }
+ if(i >= MAX_RETRY) {
+ if (firewire_debug)
+ device_printf(sc->fc.dev, "phy read failed(1).\n");
+ if (++retry < MAX_RETRY) {
+ DELAY(100);
+ goto again;
+ }
+ }
+ /* Make sure that SCLK is started */
+ stat = OREAD(sc, FWOHCI_INTSTAT);
+ if ((stat & OHCI_INT_REG_FAIL) != 0 ||
+ ((fun >> PHYDEV_REGADDR) & 0xf) != addr) {
+ if (firewire_debug)
+ device_printf(sc->fc.dev, "phy read failed(2).\n");
+ if (++retry < MAX_RETRY) {
+ DELAY(100);
+ goto again;
+ }
+ }
+ if (firewire_debug || retry >= MAX_RETRY)
+ device_printf(sc->fc.dev,
+ "fwphy_rddata: 0x%x loop=%d, retry=%d\n", addr, i, retry);
+#undef MAX_RETRY
+ return((fun >> PHYDEV_RDDATA )& 0xff);
+}
+
+
+static int
+fwohci_probe_phy(struct fwohci_softc *sc, device_t dev)
+{
+ uint32_t reg, reg2;
+ int e1394a = 1;
+ int nport, speed;
+/*
+ * probe PHY parameters
+ * 0. to prove PHY version, whether compliance of 1394a.
+ * 1. to probe maximum speed supported by the PHY and
+ * number of port supported by core-logic.
+ * It is not actually available port on your PC .
+ */
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS);
+ DELAY(500);
+
+ reg = fwphy_rddata(sc, FW_PHY_SPD_REG);
+
+ if((reg >> 5) != 7 ){
+ nport = reg & FW_PHY_NP;
+ speed = reg & FW_PHY_SPD >> 6;
+ if (speed > MAX_SPEED) {
+ device_printf(dev, "invalid speed %d (fixed to %d).\n",
+ speed, MAX_SPEED);
+ speed = MAX_SPEED;
+ }
+ device_printf(dev,
+ "Phy 1394 only %s, %d ports.\n",
+ linkspeed[speed], nport);
+ }else{
+ reg2 = fwphy_rddata(sc, FW_PHY_ESPD_REG);
+ nport = reg & FW_PHY_NP;
+ speed = (reg2 & FW_PHY_ESPD) >> 5;
+ if (speed > MAX_SPEED) {
+ device_printf(dev, "invalid speed %d (fixed to %d).\n",
+ speed, MAX_SPEED);
+ speed = MAX_SPEED;
+ }
+ device_printf(dev,
+ "Phy 1394a available %s, %d ports.\n",
+ linkspeed[speed], nport);
+
+ /* check programPhyEnable */
+ reg2 = fwphy_rddata(sc, 5);
+#if 0
+ if (e1394a && (OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_PRPHY)) {
+#else /* XXX force to enable 1394a */
+ if (e1394a) {
+#endif
+ if (firewire_debug)
+ device_printf(dev,
+ "Enable 1394a Enhancements\n");
+ /* enable EAA EMC */
+ reg2 |= 0x03;
+ /* set aPhyEnhanceEnable */
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_PHYEN);
+ OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_PRPHY);
+ } else {
+ /* for safe */
+ reg2 &= ~0x83;
+ }
+ reg2 = fwphy_wrdata(sc, 5, reg2);
+ }
+ sc->speed = speed;
+
+ reg = fwphy_rddata(sc, FW_PHY_SPD_REG);
+ if((reg >> 5) == 7 ){
+ reg = fwphy_rddata(sc, 4);
+ reg |= 1 << 6;
+ fwphy_wrdata(sc, 4, reg);
+ reg = fwphy_rddata(sc, 4);
+ }
+ return 0;
+}
+
+
+void
+fwohci_reset(struct fwohci_softc *sc, device_t dev)
+{
+ int i, max_rec, speed;
+ uint32_t reg, reg2;
+
+ /* Disable interrupts */
+ OWRITE(sc, FWOHCI_INTMASKCLR, ~0);
+
+ /* FLUSH FIFO and reset Transmitter/Receiver */
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_RESET);
+ if (firewire_debug)
+ device_printf(dev, "resetting OHCI...");
+ i = 0;
+ while(OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_RESET) {
+ if (i++ > 100) break;
+ DELAY(1000);
+ }
+ if (firewire_debug)
+ printf("done (loop=%d)\n", i);
+
+ /* Probe phy */
+ fwohci_probe_phy(sc, dev);
+
+ /* Probe link */
+ reg = OREAD(sc, OHCI_BUS_OPT);
+ reg2 = reg | OHCI_BUSFNC;
+ max_rec = (reg & 0x0000f000) >> 12;
+ speed = (reg & 0x00000007);
+ device_printf(dev, "Link %s, max_rec %d bytes.\n",
+ linkspeed[speed], MAXREC(max_rec));
+ /* XXX fix max_rec */
+ sc->maxrec = sc->speed + 8;
+ if (max_rec != sc->maxrec) {
+ reg2 = (reg2 & 0xffff0fff) | (sc->maxrec << 12);
+ device_printf(dev, "max_rec %d -> %d\n",
+ MAXREC(max_rec), MAXREC(sc->maxrec));
+ }
+ if (firewire_debug)
+ device_printf(dev, "BUS_OPT 0x%x -> 0x%x\n", reg, reg2);
+ OWRITE(sc, OHCI_BUS_OPT, reg2);
+
+ /* Initialize registers */
+ OWRITE(sc, OHCI_CROMHDR, sc->config_rom[0]);
+ OWRITE(sc, OHCI_CROMPTR, VTOP(sc->config_rom));
+#if 0
+ OWRITE(sc, OHCI_SID_BUF, sc->sid_dma.bus_addr);
+#endif
+ OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_BIGEND);
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_POSTWR);
+#if 0
+ OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_SID);
+#endif
+
+ /* Enable link */
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LINKEN);
+}
+
+int
+fwohci_init(struct fwohci_softc *sc, device_t dev)
+{
+ int i, mver;
+ uint32_t reg;
+ uint8_t ui[8];
+
+/* OHCI version */
+ reg = OREAD(sc, OHCI_VERSION);
+ mver = (reg >> 16) & 0xff;
+ device_printf(dev, "OHCI version %x.%x (ROM=%d)\n",
+ mver, reg & 0xff, (reg>>24) & 1);
+ if (mver < 1 || mver > 9) {
+ device_printf(dev, "invalid OHCI version\n");
+ return (ENXIO);
+ }
+
+/* Available Isochronous DMA channel probe */
+ OWRITE(sc, OHCI_IT_MASK, 0xffffffff);
+ OWRITE(sc, OHCI_IR_MASK, 0xffffffff);
+ reg = OREAD(sc, OHCI_IT_MASK) & OREAD(sc, OHCI_IR_MASK);
+ OWRITE(sc, OHCI_IT_MASKCLR, 0xffffffff);
+ OWRITE(sc, OHCI_IR_MASKCLR, 0xffffffff);
+ for (i = 0; i < 0x20; i++)
+ if ((reg & (1 << i)) == 0)
+ break;
+ device_printf(dev, "No. of Isochronous channels is %d.\n", i);
+ if (i == 0)
+ return (ENXIO);
+
+#if 0
+/* SID receive buffer must align 2^11 */
+#define OHCI_SIDSIZE (1 << 11)
+ sc->sid_buf = fwdma_malloc(&sc->fc, OHCI_SIDSIZE, OHCI_SIDSIZE,
+ &sc->sid_dma, BUS_DMA_WAITOK);
+ if (sc->sid_buf == NULL) {
+ device_printf(dev, "sid_buf alloc failed.");
+ return ENOMEM;
+ }
+#endif
+
+ sc->eui.hi = OREAD(sc, FWOHCIGUID_H);
+ sc->eui.lo = OREAD(sc, FWOHCIGUID_L);
+ for( i = 0 ; i < 8 ; i ++)
+ ui[i] = FW_EUI64_BYTE(&sc->eui,i);
+ device_printf(dev, "EUI64 %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ ui[0], ui[1], ui[2], ui[3], ui[4], ui[5], ui[6], ui[7]);
+ fwohci_reset(sc, dev);
+
+ return 0;
+}
+
+void
+fwohci_ibr(struct fwohci_softc *sc)
+{
+ uint32_t fun;
+
+ device_printf(sc->dev, "Initiate bus reset\n");
+
+ /*
+ * Make sure our cached values from the config rom are
+ * initialised.
+ */
+ OWRITE(sc, OHCI_CROMHDR, ntohl(sc->config_rom[0]));
+ OWRITE(sc, OHCI_BUS_OPT, ntohl(sc->config_rom[2]));
+
+ /*
+ * Set root hold-off bit so that non cyclemaster capable node
+ * shouldn't became the root node.
+ */
+#if 1
+ fun = fwphy_rddata(sc, FW_PHY_IBR_REG);
+ fun |= FW_PHY_IBR;
+ fun = fwphy_wrdata(sc, FW_PHY_IBR_REG, fun);
+#else /* Short bus reset */
+ fun = fwphy_rddata(sc, FW_PHY_ISBR_REG);
+ fun |= FW_PHY_ISBR;
+ fun = fwphy_wrdata(sc, FW_PHY_ISBR_REG, fun);
+#endif
+}
+
+
+void
+fwohci_sid(struct fwohci_softc *sc)
+{
+ uint32_t node_id;
+ int plen;
+
+ node_id = OREAD(sc, FWOHCI_NODEID);
+ if (!(node_id & OHCI_NODE_VALID)) {
+#if 0
+ printf("Bus reset failure\n");
+#endif
+ return;
+ }
+
+ /* Enable bus reset interrupt */
+ OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_PHY_BUS_R);
+ /* Allow async. request to us */
+ OWRITE(sc, OHCI_AREQHI, 1 << 31);
+ /* XXX insecure ?? */
+ OWRITE(sc, OHCI_PREQHI, 0x7fffffff);
+ OWRITE(sc, OHCI_PREQLO, 0xffffffff);
+ OWRITE(sc, OHCI_PREQUPPER, 0x10000);
+ /* Set ATRetries register */
+ OWRITE(sc, OHCI_ATRETRY, 1<<(13+16) | 0xfff);
+/*
+** Checking whether the node is root or not. If root, turn on
+** cycle master.
+*/
+ plen = OREAD(sc, OHCI_SID_CNT);
+ device_printf(fc->dev, "node_id=0x%08x, gen=%d, ",
+ node_id, (plen >> 16) & 0xff);
+ if (node_id & OHCI_NODE_ROOT) {
+ device_printf(sc->dev, "CYCLEMASTER mode\n");
+ OWRITE(sc, OHCI_LNKCTL,
+ OHCI_CNTL_CYCMTR | OHCI_CNTL_CYCTIMER);
+ } else {
+ device_printf(sc->dev, "non CYCLEMASTER mode\n");
+ OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCMTR);
+ OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_CYCTIMER);
+ }
+ if (plen & OHCI_SID_ERR) {
+ device_printf(fc->dev, "SID Error\n");
+ return;
+ }
+ device_printf(sc->dev, "bus reset phase done\n");
+ sc->state = FWOHCI_STATE_NORMAL;
+}
+
+static void
+fwohci_intr_body(struct fwohci_softc *sc, uint32_t stat, int count)
+{
+#undef OHCI_DEBUG
+#ifdef OHCI_DEBUG
+#if 0
+ if(stat & OREAD(sc, FWOHCI_INTMASK))
+#else
+ if (1)
+#endif
+ device_printf(fc->dev, "INTERRUPT < %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s> 0x%08x, 0x%08x\n",
+ stat & OHCI_INT_EN ? "DMA_EN ":"",
+ stat & OHCI_INT_PHY_REG ? "PHY_REG ":"",
+ stat & OHCI_INT_CYC_LONG ? "CYC_LONG ":"",
+ stat & OHCI_INT_ERR ? "INT_ERR ":"",
+ stat & OHCI_INT_CYC_ERR ? "CYC_ERR ":"",
+ stat & OHCI_INT_CYC_LOST ? "CYC_LOST ":"",
+ stat & OHCI_INT_CYC_64SECOND ? "CYC_64SECOND ":"",
+ stat & OHCI_INT_CYC_START ? "CYC_START ":"",
+ stat & OHCI_INT_PHY_INT ? "PHY_INT ":"",
+ stat & OHCI_INT_PHY_BUS_R ? "BUS_RESET ":"",
+ stat & OHCI_INT_PHY_SID ? "SID ":"",
+ stat & OHCI_INT_LR_ERR ? "DMA_LR_ERR ":"",
+ stat & OHCI_INT_PW_ERR ? "DMA_PW_ERR ":"",
+ stat & OHCI_INT_DMA_IR ? "DMA_IR ":"",
+ stat & OHCI_INT_DMA_IT ? "DMA_IT " :"",
+ stat & OHCI_INT_DMA_PRRS ? "DMA_PRRS " :"",
+ stat & OHCI_INT_DMA_PRRQ ? "DMA_PRRQ " :"",
+ stat & OHCI_INT_DMA_ARRS ? "DMA_ARRS " :"",
+ stat & OHCI_INT_DMA_ARRQ ? "DMA_ARRQ " :"",
+ stat & OHCI_INT_DMA_ATRS ? "DMA_ATRS " :"",
+ stat & OHCI_INT_DMA_ATRQ ? "DMA_ATRQ " :"",
+ stat, OREAD(sc, FWOHCI_INTMASK)
+ );
+#endif
+/* Bus reset */
+ if(stat & OHCI_INT_PHY_BUS_R ){
+ device_printf(fc->dev, "BUS reset\n");
+ if (sc->state == FWOHCI_STATE_BUSRESET)
+ goto busresetout;
+ sc->state = FWOHCI_STATE_BUSRESET;
+ /* Disable bus reset interrupt until sid recv. */
+ OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_PHY_BUS_R);
+
+ OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_CYC_LOST);
+ OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCSRC);
+
+ OWRITE(sc, OHCI_CROMHDR, ntohl(sc->config_rom[0]));
+ OWRITE(sc, OHCI_BUS_OPT, ntohl(sc->config_rom[2]));
+ } else if (sc->state == FWOHCI_STATE_BUSRESET) {
+ fwohci_sid(sc);
+ }
+busresetout:
+ return;
+}
+
+static uint32_t
+fwochi_check_stat(struct fwohci_softc *sc)
+{
+ uint32_t stat;
+
+ stat = OREAD(sc, FWOHCI_INTSTAT);
+ if (stat == 0xffffffff) {
+ device_printf(sc->fc.dev,
+ "device physically ejected?\n");
+ return(stat);
+ }
+ if (stat)
+ OWRITE(sc, FWOHCI_INTSTATCLR, stat);
+ return(stat);
+}
+
+void
+fwohci_poll(struct fwohci_softc *sc)
+{
+ uint32_t stat;
+
+ stat = fwochi_check_stat(sc);
+ if (stat != 0xffffffff)
+ fwohci_intr_body(sc, stat, 1);
+}
diff --git a/stand/i386/libfirewire/fwohci.h b/stand/i386/libfirewire/fwohci.h
new file mode 100644
index 0000000..4a93220
--- /dev/null
+++ b/stand/i386/libfirewire/fwohci.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2007 Hidetoshi Shimokawa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the acknowledgement as bellow:
+ *
+ * This product includes software developed by K. Kobayashi and H. Shimokawa
+ *
+ * 4. 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#define MAX_OHCI 5
+#define CROMSIZE 0x400
+
+struct fw_eui64 {
+ uint32_t hi, lo;
+};
+
+struct fwohci_softc {
+ uint32_t locator;
+ uint32_t devid;
+ uint32_t base_addr;
+ uint32_t bus_id;
+ uint32_t handle;
+ int32_t state;
+ struct crom_src_buf *crom_src_buf;
+ struct crom_src *crom_src;
+ struct crom_chunk *crom_root;
+ struct fw_eui64 eui;
+ int speed;
+ int maxrec;
+ uint32_t *config_rom;
+ char config_rom_buf[CROMSIZE*2]; /* double size for alignment */
+};
+
+int fwohci_init(struct fwohci_softc *, int);
+void fwohci_ibr(struct fwohci_softc *);
+void fwohci_poll(struct fwohci_softc *);
+
+#define FWOHCI_STATE_DEAD (-1)
+#define FWOHCI_STATE_INIT 0
+#define FWOHCI_STATE_ENABLED 1
+#define FWOHCI_STATE_BUSRESET 2
+#define FWOHCI_STATE_NORMAL 3
+
+#define OREAD(f, o) (*(volatile uint32_t *)((f)->handle + (o)))
+#define OWRITE(f, o, v) (*(volatile uint32_t *)((f)->handle + (o)) = (v))
+
+#define OHCI_VERSION 0x00
+#define OHCI_ATRETRY 0x08
+#define OHCI_CROMHDR 0x18
+#define OHCI_BUS_ID 0x1c
+#define OHCI_BUS_OPT 0x20
+#define OHCI_BUSIRMC (1U << 31)
+#define OHCI_BUSCMC (1 << 30)
+#define OHCI_BUSISC (1 << 29)
+#define OHCI_BUSBMC (1 << 28)
+#define OHCI_BUSPMC (1 << 27)
+#define OHCI_BUSFNC OHCI_BUSIRMC | OHCI_BUSCMC | OHCI_BUSISC |\
+ OHCI_BUSBMC | OHCI_BUSPMC
+
+#define OHCI_EUID_HI 0x24
+#define OHCI_EUID_LO 0x28
+
+#define OHCI_CROMPTR 0x34
+#define OHCI_HCCCTL 0x50
+#define OHCI_HCCCTLCLR 0x54
+#define OHCI_AREQHI 0x100
+#define OHCI_AREQHICLR 0x104
+#define OHCI_AREQLO 0x108
+#define OHCI_AREQLOCLR 0x10c
+#define OHCI_PREQHI 0x110
+#define OHCI_PREQHICLR 0x114
+#define OHCI_PREQLO 0x118
+#define OHCI_PREQLOCLR 0x11c
+#define OHCI_PREQUPPER 0x120
+
+#define OHCI_SID_BUF 0x64
+#define OHCI_SID_CNT 0x68
+#define OHCI_SID_ERR (1U << 31)
+#define OHCI_SID_CNT_MASK 0xffc
+
+#define OHCI_IT_STAT 0x90
+#define OHCI_IT_STATCLR 0x94
+#define OHCI_IT_MASK 0x98
+#define OHCI_IT_MASKCLR 0x9c
+
+#define OHCI_IR_STAT 0xa0
+#define OHCI_IR_STATCLR 0xa4
+#define OHCI_IR_MASK 0xa8
+#define OHCI_IR_MASKCLR 0xac
+
+#define OHCI_LNKCTL 0xe0
+#define OHCI_LNKCTLCLR 0xe4
+
+#define OHCI_PHYACCESS 0xec
+#define OHCI_CYCLETIMER 0xf0
+
+#define OHCI_DMACTL(off) (off)
+#define OHCI_DMACTLCLR(off) (off + 4)
+#define OHCI_DMACMD(off) (off + 0xc)
+#define OHCI_DMAMATCH(off) (off + 0x10)
+
+#define OHCI_ATQOFF 0x180
+#define OHCI_ATQCTL OHCI_ATQOFF
+#define OHCI_ATQCTLCLR (OHCI_ATQOFF + 4)
+#define OHCI_ATQCMD (OHCI_ATQOFF + 0xc)
+#define OHCI_ATQMATCH (OHCI_ATQOFF + 0x10)
+
+#define OHCI_ATSOFF 0x1a0
+#define OHCI_ATSCTL OHCI_ATSOFF
+#define OHCI_ATSCTLCLR (OHCI_ATSOFF + 4)
+#define OHCI_ATSCMD (OHCI_ATSOFF + 0xc)
+#define OHCI_ATSMATCH (OHCI_ATSOFF + 0x10)
+
+#define OHCI_ARQOFF 0x1c0
+#define OHCI_ARQCTL OHCI_ARQOFF
+#define OHCI_ARQCTLCLR (OHCI_ARQOFF + 4)
+#define OHCI_ARQCMD (OHCI_ARQOFF + 0xc)
+#define OHCI_ARQMATCH (OHCI_ARQOFF + 0x10)
+
+#define OHCI_ARSOFF 0x1e0
+#define OHCI_ARSCTL OHCI_ARSOFF
+#define OHCI_ARSCTLCLR (OHCI_ARSOFF + 4)
+#define OHCI_ARSCMD (OHCI_ARSOFF + 0xc)
+#define OHCI_ARSMATCH (OHCI_ARSOFF + 0x10)
+
+#define OHCI_ITOFF(CH) (0x200 + 0x10 * (CH))
+#define OHCI_ITCTL(CH) (OHCI_ITOFF(CH))
+#define OHCI_ITCTLCLR(CH) (OHCI_ITOFF(CH) + 4)
+#define OHCI_ITCMD(CH) (OHCI_ITOFF(CH) + 0xc)
+
+#define OHCI_IROFF(CH) (0x400 + 0x20 * (CH))
+#define OHCI_IRCTL(CH) (OHCI_IROFF(CH))
+#define OHCI_IRCTLCLR(CH) (OHCI_IROFF(CH) + 4)
+#define OHCI_IRCMD(CH) (OHCI_IROFF(CH) + 0xc)
+#define OHCI_IRMATCH(CH) (OHCI_IROFF(CH) + 0x10)
diff --git a/stand/i386/libfirewire/fwohcireg.h b/stand/i386/libfirewire/fwohcireg.h
new file mode 100644
index 0000000..d57870c
--- /dev/null
+++ b/stand/i386/libfirewire/fwohcireg.h
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2003 Hidetoshi Shimokawa
+ * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the acknowledgement as bellow:
+ *
+ * This product includes software developed by K. Kobayashi and H. Shimokawa
+ *
+ * 4. 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.
+ *
+ * $FreeBSD$
+ *
+ */
+#define PCI_CBMEM PCIR_BAR(0)
+
+#define FW_VENDORID_NATSEMI 0x100B
+#define FW_VENDORID_NEC 0x1033
+#define FW_VENDORID_SIS 0x1039
+#define FW_VENDORID_TI 0x104c
+#define FW_VENDORID_SONY 0x104d
+#define FW_VENDORID_VIA 0x1106
+#define FW_VENDORID_RICOH 0x1180
+#define FW_VENDORID_APPLE 0x106b
+#define FW_VENDORID_LUCENT 0x11c1
+#define FW_VENDORID_INTEL 0x8086
+#define FW_VENDORID_ADAPTEC 0x9004
+
+#define FW_DEVICE_CS4210 (0x000f << 16)
+#define FW_DEVICE_UPD861 (0x0063 << 16)
+#define FW_DEVICE_UPD871 (0x00ce << 16)
+#define FW_DEVICE_UPD72870 (0x00cd << 16)
+#define FW_DEVICE_UPD72873 (0x00e7 << 16)
+#define FW_DEVICE_UPD72874 (0x00f2 << 16)
+#define FW_DEVICE_TITSB22 (0x8009 << 16)
+#define FW_DEVICE_TITSB23 (0x8019 << 16)
+#define FW_DEVICE_TITSB26 (0x8020 << 16)
+#define FW_DEVICE_TITSB43 (0x8021 << 16)
+#define FW_DEVICE_TITSB43A (0x8023 << 16)
+#define FW_DEVICE_TITSB43AB23 (0x8024 << 16)
+#define FW_DEVICE_TITSB82AA2 (0x8025 << 16)
+#define FW_DEVICE_TITSB43AB21 (0x8026 << 16)
+#define FW_DEVICE_TIPCI4410A (0x8017 << 16)
+#define FW_DEVICE_TIPCI4450 (0x8011 << 16)
+#define FW_DEVICE_TIPCI4451 (0x8027 << 16)
+#define FW_DEVICE_CXD1947 (0x8009 << 16)
+#define FW_DEVICE_CXD3222 (0x8039 << 16)
+#define FW_DEVICE_VT6306 (0x3044 << 16)
+#define FW_DEVICE_R5C551 (0x0551 << 16)
+#define FW_DEVICE_R5C552 (0x0552 << 16)
+#define FW_DEVICE_PANGEA (0x0030 << 16)
+#define FW_DEVICE_UNINORTH (0x0031 << 16)
+#define FW_DEVICE_AIC5800 (0x5800 << 16)
+#define FW_DEVICE_FW322 (0x5811 << 16)
+#define FW_DEVICE_7007 (0x7007 << 16)
+#define FW_DEVICE_82372FB (0x7605 << 16)
+
+#define PCI_INTERFACE_OHCI 0x10
+
+#define FW_OHCI_BASE_REG 0x10
+
+#define OHCI_DMA_ITCH 0x20
+#define OHCI_DMA_IRCH 0x20
+
+#define OHCI_MAX_DMA_CH (0x4 + OHCI_DMA_ITCH + OHCI_DMA_IRCH)
+
+
+typedef uint32_t fwohcireg_t;
+
+/* for PCI */
+#if BYTE_ORDER == BIG_ENDIAN
+#define FWOHCI_DMA_WRITE(x, y) ((x) = htole32(y))
+#define FWOHCI_DMA_READ(x) le32toh(x)
+#define FWOHCI_DMA_SET(x, y) ((x) |= htole32(y))
+#define FWOHCI_DMA_CLEAR(x, y) ((x) &= htole32(~(y)))
+#else
+#define FWOHCI_DMA_WRITE(x, y) ((x) = (y))
+#define FWOHCI_DMA_READ(x) (x)
+#define FWOHCI_DMA_SET(x, y) ((x) |= (y))
+#define FWOHCI_DMA_CLEAR(x, y) ((x) &= ~(y))
+#endif
+
+struct fwohcidb {
+ union {
+ struct {
+ uint32_t cmd;
+ uint32_t addr;
+ uint32_t depend;
+ uint32_t res;
+ } desc;
+ uint32_t immed[4];
+ } db;
+#define OHCI_STATUS_SHIFT 16
+#define OHCI_COUNT_MASK 0xffff
+#define OHCI_OUTPUT_MORE (0 << 28)
+#define OHCI_OUTPUT_LAST (1 << 28)
+#define OHCI_INPUT_MORE (2 << 28)
+#define OHCI_INPUT_LAST (3 << 28)
+#define OHCI_STORE_QUAD (4 << 28)
+#define OHCI_LOAD_QUAD (5 << 28)
+#define OHCI_NOP (6 << 28)
+#define OHCI_STOP (7 << 28)
+#define OHCI_STORE (8 << 28)
+#define OHCI_CMD_MASK (0xf << 28)
+
+#define OHCI_UPDATE (1 << 27)
+
+#define OHCI_KEY_ST0 (0 << 24)
+#define OHCI_KEY_ST1 (1 << 24)
+#define OHCI_KEY_ST2 (2 << 24)
+#define OHCI_KEY_ST3 (3 << 24)
+#define OHCI_KEY_REGS (5 << 24)
+#define OHCI_KEY_SYS (6 << 24)
+#define OHCI_KEY_DEVICE (7 << 24)
+#define OHCI_KEY_MASK (7 << 24)
+
+#define OHCI_INTERRUPT_NEVER (0 << 20)
+#define OHCI_INTERRUPT_TRUE (1 << 20)
+#define OHCI_INTERRUPT_FALSE (2 << 20)
+#define OHCI_INTERRUPT_ALWAYS (3 << 20)
+
+#define OHCI_BRANCH_NEVER (0 << 18)
+#define OHCI_BRANCH_TRUE (1 << 18)
+#define OHCI_BRANCH_FALSE (2 << 18)
+#define OHCI_BRANCH_ALWAYS (3 << 18)
+#define OHCI_BRANCH_MASK (3 << 18)
+
+#define OHCI_WAIT_NEVER (0 << 16)
+#define OHCI_WAIT_TRUE (1 << 16)
+#define OHCI_WAIT_FALSE (2 << 16)
+#define OHCI_WAIT_ALWAYS (3 << 16)
+};
+
+#define OHCI_SPD_S100 0x4
+#define OHCI_SPD_S200 0x1
+#define OHCI_SPD_S400 0x2
+
+
+#define FWOHCIEV_NOSTAT 0
+#define FWOHCIEV_LONGP 2
+#define FWOHCIEV_MISSACK 3
+#define FWOHCIEV_UNDRRUN 4
+#define FWOHCIEV_OVRRUN 5
+#define FWOHCIEV_DESCERR 6
+#define FWOHCIEV_DTRDERR 7
+#define FWOHCIEV_DTWRERR 8
+#define FWOHCIEV_BUSRST 9
+#define FWOHCIEV_TIMEOUT 0xa
+#define FWOHCIEV_TCODERR 0xb
+#define FWOHCIEV_UNKNOWN 0xe
+#define FWOHCIEV_FLUSHED 0xf
+#define FWOHCIEV_ACKCOMPL 0x11
+#define FWOHCIEV_ACKPEND 0x12
+#define FWOHCIEV_ACKBSX 0x14
+#define FWOHCIEV_ACKBSA 0x15
+#define FWOHCIEV_ACKBSB 0x16
+#define FWOHCIEV_ACKTARD 0x1b
+#define FWOHCIEV_ACKDERR 0x1d
+#define FWOHCIEV_ACKTERR 0x1e
+
+#define FWOHCIEV_MASK 0x1f
+
+struct ohci_dma{
+ fwohcireg_t cntl;
+
+#define OHCI_CNTL_CYCMATCH_S (0x1 << 31)
+
+#define OHCI_CNTL_BUFFIL (0x1 << 31)
+#define OHCI_CNTL_ISOHDR (0x1 << 30)
+#define OHCI_CNTL_CYCMATCH_R (0x1 << 29)
+#define OHCI_CNTL_MULTICH (0x1 << 28)
+
+#define OHCI_CNTL_DMA_RUN (0x1 << 15)
+#define OHCI_CNTL_DMA_WAKE (0x1 << 12)
+#define OHCI_CNTL_DMA_DEAD (0x1 << 11)
+#define OHCI_CNTL_DMA_ACTIVE (0x1 << 10)
+#define OHCI_CNTL_DMA_BT (0x1 << 8)
+#define OHCI_CNTL_DMA_BAD (0x1 << 7)
+#define OHCI_CNTL_DMA_STAT (0xff)
+
+ fwohcireg_t cntl_clr;
+ fwohcireg_t dummy0;
+ fwohcireg_t cmd;
+ fwohcireg_t match;
+ fwohcireg_t dummy1;
+ fwohcireg_t dummy2;
+ fwohcireg_t dummy3;
+};
+
+struct ohci_itdma{
+ fwohcireg_t cntl;
+ fwohcireg_t cntl_clr;
+ fwohcireg_t dummy0;
+ fwohcireg_t cmd;
+};
+
+struct ohci_registers {
+ fwohcireg_t ver; /* Version No. 0x0 */
+ fwohcireg_t guid; /* GUID_ROM No. 0x4 */
+ fwohcireg_t retry; /* AT retries 0x8 */
+#define FWOHCI_RETRY 0x8
+ fwohcireg_t csr_data; /* CSR data 0xc */
+ fwohcireg_t csr_cmp; /* CSR compare 0x10 */
+ fwohcireg_t csr_cntl; /* CSR compare 0x14 */
+ fwohcireg_t rom_hdr; /* config ROM ptr. 0x18 */
+ fwohcireg_t bus_id; /* BUS_ID 0x1c */
+ fwohcireg_t bus_opt; /* BUS option 0x20 */
+#define FWOHCIGUID_H 0x24
+#define FWOHCIGUID_L 0x28
+ fwohcireg_t guid_hi; /* GUID hi 0x24 */
+ fwohcireg_t guid_lo; /* GUID lo 0x28 */
+ fwohcireg_t dummy0[2]; /* dummy 0x2c-0x30 */
+ fwohcireg_t config_rom; /* config ROM map 0x34 */
+ fwohcireg_t post_wr_lo; /* post write addr lo 0x38 */
+ fwohcireg_t post_wr_hi; /* post write addr hi 0x3c */
+ fwohcireg_t vendor; /* vendor ID 0x40 */
+ fwohcireg_t dummy1[3]; /* dummy 0x44-0x4c */
+ fwohcireg_t hcc_cntl_set; /* HCC control set 0x50 */
+ fwohcireg_t hcc_cntl_clr; /* HCC control clr 0x54 */
+#define OHCI_HCC_BIBIV (1U << 31) /* BIBimage Valid */
+#define OHCI_HCC_BIGEND (1 << 30) /* noByteSwapData */
+#define OHCI_HCC_PRPHY (1 << 23) /* programPhyEnable */
+#define OHCI_HCC_PHYEN (1 << 22) /* aPhyEnhanceEnable */
+#define OHCI_HCC_LPS (1 << 19) /* LPS */
+#define OHCI_HCC_POSTWR (1 << 18) /* postedWriteEnable */
+#define OHCI_HCC_LINKEN (1 << 17) /* linkEnable */
+#define OHCI_HCC_RESET (1 << 16) /* softReset */
+ fwohcireg_t dummy2[2]; /* dummy 0x58-0x5c */
+ fwohcireg_t dummy3[1]; /* dummy 0x60 */
+ fwohcireg_t sid_buf; /* self id buffer 0x64 */
+ fwohcireg_t sid_cnt; /* self id count 0x68 */
+ fwohcireg_t dummy4[1]; /* dummy 0x6c */
+ fwohcireg_t ir_mask_hi_set; /* ir mask hi set 0x70 */
+ fwohcireg_t ir_mask_hi_clr; /* ir mask hi set 0x74 */
+ fwohcireg_t ir_mask_lo_set; /* ir mask hi set 0x78 */
+ fwohcireg_t ir_mask_lo_clr; /* ir mask hi set 0x7c */
+#define FWOHCI_INTSTAT 0x80
+#define FWOHCI_INTSTATCLR 0x84
+#define FWOHCI_INTMASK 0x88
+#define FWOHCI_INTMASKCLR 0x8c
+ fwohcireg_t int_stat; /* 0x80 */
+ fwohcireg_t int_clear; /* 0x84 */
+ fwohcireg_t int_mask; /* 0x88 */
+ fwohcireg_t int_mask_clear; /* 0x8c */
+ fwohcireg_t it_int_stat; /* 0x90 */
+ fwohcireg_t it_int_clear; /* 0x94 */
+ fwohcireg_t it_int_mask; /* 0x98 */
+ fwohcireg_t it_mask_clear; /* 0x9c */
+ fwohcireg_t ir_int_stat; /* 0xa0 */
+ fwohcireg_t ir_int_clear; /* 0xa4 */
+ fwohcireg_t ir_int_mask; /* 0xa8 */
+ fwohcireg_t ir_mask_clear; /* 0xac */
+ fwohcireg_t dummy5[11]; /* dummy 0xb0-d8 */
+ fwohcireg_t fairness; /* fairness control 0xdc */
+ fwohcireg_t link_cntl; /* Chip control 0xe0*/
+ fwohcireg_t link_cntl_clr; /* Chip control clear 0xe4*/
+#define FWOHCI_NODEID 0xe8
+ fwohcireg_t node; /* Node ID 0xe8 */
+#define OHCI_NODE_VALID (1U << 31)
+#define OHCI_NODE_ROOT (1 << 30)
+
+#define OHCI_ASYSRCBUS 1
+
+ fwohcireg_t phy_access; /* PHY cntl 0xec */
+#define PHYDEV_RDDONE (1<<31)
+#define PHYDEV_RDCMD (1<<15)
+#define PHYDEV_WRCMD (1<<14)
+#define PHYDEV_REGADDR 8
+#define PHYDEV_WRDATA 0
+#define PHYDEV_RDADDR 24
+#define PHYDEV_RDDATA 16
+
+ fwohcireg_t cycle_timer; /* Cycle Timer 0xf0 */
+ fwohcireg_t dummy6[3]; /* dummy 0xf4-fc */
+ fwohcireg_t areq_hi; /* Async req. filter hi 0x100 */
+ fwohcireg_t areq_hi_clr; /* Async req. filter hi 0x104 */
+ fwohcireg_t areq_lo; /* Async req. filter lo 0x108 */
+ fwohcireg_t areq_lo_clr; /* Async req. filter lo 0x10c */
+ fwohcireg_t preq_hi; /* Async req. filter hi 0x110 */
+ fwohcireg_t preq_hi_clr; /* Async req. filter hi 0x114 */
+ fwohcireg_t preq_lo; /* Async req. filter lo 0x118 */
+ fwohcireg_t preq_lo_clr; /* Async req. filter lo 0x11c */
+
+ fwohcireg_t pys_upper; /* Physical Upper bound 0x120 */
+
+ fwohcireg_t dummy7[23]; /* dummy 0x124-0x17c */
+
+ /* 0x180, 0x184, 0x188, 0x18c */
+ /* 0x190, 0x194, 0x198, 0x19c */
+ /* 0x1a0, 0x1a4, 0x1a8, 0x1ac */
+ /* 0x1b0, 0x1b4, 0x1b8, 0x1bc */
+ /* 0x1c0, 0x1c4, 0x1c8, 0x1cc */
+ /* 0x1d0, 0x1d4, 0x1d8, 0x1dc */
+ /* 0x1e0, 0x1e4, 0x1e8, 0x1ec */
+ /* 0x1f0, 0x1f4, 0x1f8, 0x1fc */
+ struct ohci_dma dma_ch[0x4];
+
+ /* 0x200, 0x204, 0x208, 0x20c */
+ /* 0x210, 0x204, 0x208, 0x20c */
+ struct ohci_itdma dma_itch[0x20];
+
+ /* 0x400, 0x404, 0x408, 0x40c */
+ /* 0x410, 0x404, 0x408, 0x40c */
+ struct ohci_dma dma_irch[0x20];
+};
+
+#define OHCI_CNTL_CYCSRC (0x1 << 22)
+#define OHCI_CNTL_CYCMTR (0x1 << 21)
+#define OHCI_CNTL_CYCTIMER (0x1 << 20)
+#define OHCI_CNTL_PHYPKT (0x1 << 10)
+#define OHCI_CNTL_SID (0x1 << 9)
+
+#define OHCI_INT_DMA_ATRQ (0x1 << 0)
+#define OHCI_INT_DMA_ATRS (0x1 << 1)
+#define OHCI_INT_DMA_ARRQ (0x1 << 2)
+#define OHCI_INT_DMA_ARRS (0x1 << 3)
+#define OHCI_INT_DMA_PRRQ (0x1 << 4)
+#define OHCI_INT_DMA_PRRS (0x1 << 5)
+#define OHCI_INT_DMA_IT (0x1 << 6)
+#define OHCI_INT_DMA_IR (0x1 << 7)
+#define OHCI_INT_PW_ERR (0x1 << 8)
+#define OHCI_INT_LR_ERR (0x1 << 9)
+
+#define OHCI_INT_PHY_SID (0x1 << 16)
+#define OHCI_INT_PHY_BUS_R (0x1 << 17)
+
+#define OHCI_INT_REG_FAIL (0x1 << 18)
+
+#define OHCI_INT_PHY_INT (0x1 << 19)
+#define OHCI_INT_CYC_START (0x1 << 20)
+#define OHCI_INT_CYC_64SECOND (0x1 << 21)
+#define OHCI_INT_CYC_LOST (0x1 << 22)
+#define OHCI_INT_CYC_ERR (0x1 << 23)
+
+#define OHCI_INT_ERR (0x1 << 24)
+#define OHCI_INT_CYC_LONG (0x1 << 25)
+#define OHCI_INT_PHY_REG (0x1 << 26)
+
+#define OHCI_INT_EN (0x1 << 31)
+
+#define IP_CHANNELS 0x0234
+#define FWOHCI_MAXREC 2048
+
+#define OHCI_ISORA 0x02
+#define OHCI_ISORB 0x04
+
+#define FWOHCITCODE_PHY 0xe
diff --git a/stand/i386/libi386/Makefile b/stand/i386/libi386/Makefile
new file mode 100644
index 0000000..8d65513
--- /dev/null
+++ b/stand/i386/libi386/Makefile
@@ -0,0 +1,61 @@
+# $FreeBSD$
+
+HAVE_GPT= yes
+HAVE_GELI= yes
+
+.include <bsd.init.mk>
+
+LIB= i386
+INTERNALLIB=
+
+SRCS= biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \
+ biospci.c biossmap.c bootinfo.c bootinfo32.c bootinfo64.c \
+ comconsole.c devicename.c elf32_freebsd.c \
+ elf64_freebsd.c multiboot.c multiboot_tramp.S relocater_tramp.S \
+ i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \
+ smbios.c time.c vidconsole.c amd64_tramp.S spinconsole.c
+.PATH: ${ZFSSRC}
+SRCS+= devicename_stubs.c
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+CFLAGS+= -DCOMPORT=${BOOT_COMCONSOLE_PORT}
+
+BOOT_COMCONSOLE_SPEED?= 9600
+CFLAGS+= -DCOMSPEED=${BOOT_COMCONSOLE_SPEED}
+
+.ifdef(BOOT_BIOSDISK_DEBUG)
+# Make the disk code more talkative
+CFLAGS+= -DDISK_DEBUG
+.endif
+
+.if !defined(BOOT_HIDE_SERIAL_NUMBERS)
+# Export serial numbers, UUID, and asset tag from loader.
+CFLAGS+= -DSMBIOS_SERIAL_NUMBERS
+.if defined(BOOT_LITTLE_ENDIAN_UUID)
+# Use little-endian UUID format as defined in SMBIOS 2.6.
+CFLAGS+= -DSMBIOS_LITTLE_ENDIAN_UUID
+.elif defined(BOOT_NETWORK_ENDIAN_UUID)
+# Use network-endian UUID format for backward compatibility.
+CFLAGS+= -DSMBIOS_NETWORK_ENDIAN_UUID
+.endif
+.endif
+
+# Include simple terminal emulation (cons25-compatible)
+CFLAGS+= -DTERM_EMU
+
+# XXX: make alloca() useable
+CFLAGS+= -Dalloca=__builtin_alloca
+
+CFLAGS+= -I${BOOTSRC}/ficl -I${BOOTSRC}/ficl/i386 \
+ -I${LDRSRC} -I${BOOTSRC}/i386/common \
+ -I${BTXLIB} \
+ -I${SYSDIR}/contrib/dev/acpica/include
+
+# Handle FreeBSD specific %b and %D printf format specifiers
+CFLAGS+= ${FORMAT_EXTENSIONS}
+
+.include <bsd.lib.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.amd64_tramp.S= ${CLANG_NO_IAS}
+CFLAGS.multiboot_tramp.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/libi386/Makefile.depend b/stand/i386/libi386/Makefile.depend
new file mode 100644
index 0000000..df20c96
--- /dev/null
+++ b/stand/i386/libi386/Makefile.depend
@@ -0,0 +1,14 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libmd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/libi386/amd64_tramp.S b/stand/i386/libi386/amd64_tramp.S
new file mode 100644
index 0000000..6359b90
--- /dev/null
+++ b/stand/i386/libi386/amd64_tramp.S
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2003 Peter Wemm <peter@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Quick and dirty trampoline to get into 64 bit (long) mode and running
+ * with paging enabled so that we enter the kernel at its linked address.
+ */
+#define MSR_EFER 0xc0000080
+#define EFER_LME 0x00000100
+#define CR4_PAE 0x00000020
+#define CR4_PSE 0x00000010
+#define CR0_PG 0x80000000
+
+/* GRRR. Deal with BTX that links us for a non-zero location */
+#define VPBASE 0xa000
+#define VTOP(x) ((x) + VPBASE)
+
+ .data
+
+ .p2align 12,0x40
+
+ .globl PT4
+PT4:
+ .space 0x1000
+ .globl PT3
+PT3:
+ .space 0x1000
+ .globl PT2
+PT2:
+ .space 0x1000
+
+gdtdesc:
+ .word gdtend - gdt
+ .long VTOP(gdt) # low
+ .long 0 # high
+
+gdt:
+ .long 0 # null descriptor
+ .long 0
+ .long 0x00000000 # %cs
+ .long 0x00209800
+ .long 0x00000000 # %ds
+ .long 0x00008000
+gdtend:
+
+ .text
+ .code32
+
+ .globl amd64_tramp
+amd64_tramp:
+ /* Be sure that interrupts are disabled */
+ cli
+
+ /* Turn on EFER.LME */
+ movl $MSR_EFER, %ecx
+ rdmsr
+ orl $EFER_LME, %eax
+ wrmsr
+
+ /* Turn on PAE */
+ movl %cr4, %eax
+ orl $CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Set %cr3 for PT4 */
+ movl $VTOP(PT4), %eax
+ movl %eax, %cr3
+
+ /* Turn on paging (implicitly sets EFER.LMA) */
+ movl %cr0, %eax
+ orl $CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Now we're in compatibility mode. set %cs for long mode */
+ movl $VTOP(gdtdesc), %eax
+ movl VTOP(entry_hi), %esi
+ movl VTOP(entry_lo), %edi
+ lgdt (%eax)
+ ljmp $0x8, $VTOP(longmode)
+
+ .code64
+longmode:
+ /* We're still running V=P, jump to entry point */
+ movl %esi, %eax
+ salq $32, %rax
+ orq %rdi, %rax
+ pushq %rax
+ ret
diff --git a/stand/i386/libi386/biosacpi.c b/stand/i386/libi386/biosacpi.c
new file mode 100644
index 0000000..8167fca
--- /dev/null
+++ b/stand/i386/libi386/biosacpi.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 2001 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <machine/stdarg.h>
+#include <bootstrap.h>
+#include <btxv86.h>
+#include "libi386.h"
+
+#include "platform/acfreebsd.h"
+#include "acconfig.h"
+#define ACPI_SYSTEM_XFACE
+#include "actypes.h"
+#include "actbl.h"
+
+/*
+ * Detect ACPI and export information about the ACPI BIOS into the
+ * environment.
+ */
+
+static ACPI_TABLE_RSDP *biosacpi_find_rsdp(void);
+static ACPI_TABLE_RSDP *biosacpi_search_rsdp(char *base, int length);
+
+#define RSDP_CHECKSUM_LENGTH 20
+
+void
+biosacpi_detect(void)
+{
+ ACPI_TABLE_RSDP *rsdp;
+ char buf[24];
+ int revision;
+
+ /* locate and validate the RSDP */
+ if ((rsdp = biosacpi_find_rsdp()) == NULL)
+ return;
+
+ /*
+ * Report the RSDP to the kernel. While this can be found with
+ * a BIOS boot, the RSDP may be elsewhere when booted from UEFI.
+ * The old code used the 'hints' method to communite this to
+ * the kernel. However, while convenient, the 'hints' method
+ * is fragile and does not work when static hints are compiled
+ * into the kernel. Instead, move to setting different tunables
+ * that start with acpi. The old 'hints' can be removed before
+ * we branch for FreeBSD 12.
+ */
+ sprintf(buf, "0x%08x", VTOP(rsdp));
+ setenv("hint.acpi.0.rsdp", buf, 1);
+ setenv("acpi.rsdp", buf, 1);
+ revision = rsdp->Revision;
+ if (revision == 0)
+ revision = 1;
+ sprintf(buf, "%d", revision);
+ setenv("hint.acpi.0.revision", buf, 1);
+ setenv("acpi.revision", buf, 1);
+ strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId));
+ buf[sizeof(rsdp->OemId)] = '\0';
+ setenv("hint.acpi.0.oem", buf, 1);
+ setenv("acpi.oem", buf, 1);
+ sprintf(buf, "0x%08x", rsdp->RsdtPhysicalAddress);
+ setenv("hint.acpi.0.rsdt", buf, 1);
+ setenv("acpi.rsdt", buf, 1);
+ if (revision >= 2) {
+ /* XXX extended checksum? */
+ sprintf(buf, "0x%016llx", rsdp->XsdtPhysicalAddress);
+ setenv("hint.acpi.0.xsdt", buf, 1);
+ setenv("acpi.xsdt", buf, 1);
+ sprintf(buf, "%d", rsdp->Length);
+ setenv("hint.acpi.0.xsdt_length", buf, 1);
+ setenv("acpi.xsdt_length", buf, 1);
+ }
+}
+
+/*
+ * Find the RSDP in low memory. See section 5.2.2 of the ACPI spec.
+ */
+static ACPI_TABLE_RSDP *
+biosacpi_find_rsdp(void)
+{
+ ACPI_TABLE_RSDP *rsdp;
+ uint16_t *addr;
+
+ /* EBDA is the 1 KB addressed by the 16 bit pointer at 0x40E. */
+ addr = (uint16_t *)PTOV(0x40E);
+ if ((rsdp = biosacpi_search_rsdp((char *)(*addr << 4), 0x400)) != NULL)
+ return (rsdp);
+
+ /* Check the upper memory BIOS space, 0xe0000 - 0xfffff. */
+ if ((rsdp = biosacpi_search_rsdp((char *)0xe0000, 0x20000)) != NULL)
+ return (rsdp);
+
+ return (NULL);
+}
+
+static ACPI_TABLE_RSDP *
+biosacpi_search_rsdp(char *base, int length)
+{
+ ACPI_TABLE_RSDP *rsdp;
+ u_int8_t *cp, sum;
+ int ofs, idx;
+
+ /* search on 16-byte boundaries */
+ for (ofs = 0; ofs < length; ofs += 16) {
+ rsdp = (ACPI_TABLE_RSDP *)PTOV(base + ofs);
+
+ /* compare signature, validate checksum */
+ if (!strncmp(rsdp->Signature, ACPI_SIG_RSDP, strlen(ACPI_SIG_RSDP))) {
+ cp = (u_int8_t *)rsdp;
+ sum = 0;
+ for (idx = 0; idx < RSDP_CHECKSUM_LENGTH; idx++)
+ sum += *(cp + idx);
+ if (sum != 0)
+ continue;
+ return(rsdp);
+ }
+ }
+ return(NULL);
+}
diff --git a/stand/i386/libi386/bioscd.c b/stand/i386/libi386/bioscd.c
new file mode 100644
index 0000000..2e8fc3b
--- /dev/null
+++ b/stand/i386/libi386/bioscd.c
@@ -0,0 +1,452 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * BIOS CD device handling for CD's that have been booted off of via no
+ * emulation booting as defined in the El Torito standard.
+ *
+ * Ideas and algorithms from:
+ *
+ * - FreeBSD libi386/biosdisk.c
+ *
+ */
+
+#include <stand.h>
+
+#include <sys/param.h>
+#include <machine/bootinfo.h>
+
+#include <stdarg.h>
+
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <edd.h>
+#include "libi386.h"
+
+#define BIOSCD_SECSIZE 2048
+#define BUFSIZE (1 * BIOSCD_SECSIZE)
+#define MAXBCDEV 1
+
+/* Major numbers for devices we frontend for. */
+#define ACDMAJOR 117
+#define CDMAJOR 15
+
+#ifdef DISK_DEBUG
+# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
+#else
+# define DEBUG(fmt, args...)
+#endif
+
+struct specification_packet {
+ u_char sp_size;
+ u_char sp_bootmedia;
+ u_char sp_drive;
+ u_char sp_controller;
+ u_int sp_lba;
+ u_short sp_devicespec;
+ u_short sp_buffersegment;
+ u_short sp_loadsegment;
+ u_short sp_sectorcount;
+ u_short sp_cylsec;
+ u_char sp_head;
+};
+
+/*
+ * List of BIOS devices, translation from disk unit number to
+ * BIOS unit number.
+ */
+static struct bcinfo {
+ int bc_unit; /* BIOS unit number */
+ struct specification_packet bc_sp;
+ int bc_open; /* reference counter */
+ void *bc_bcache; /* buffer cache data */
+} bcinfo [MAXBCDEV];
+static int nbcinfo = 0;
+
+#define BC(dev) (bcinfo[(dev)->d_unit])
+
+static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
+static int bc_init(void);
+static int bc_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int bc_realstrategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int bc_open(struct open_file *f, ...);
+static int bc_close(struct open_file *f);
+static int bc_print(int verbose);
+
+struct devsw bioscd = {
+ "cd",
+ DEVT_CD,
+ bc_init,
+ bc_strategy,
+ bc_open,
+ bc_close,
+ noioctl,
+ bc_print,
+ NULL
+};
+
+/*
+ * Translate between BIOS device numbers and our private unit numbers.
+ */
+int
+bc_bios2unit(int biosdev)
+{
+ int i;
+
+ DEBUG("looking for bios device 0x%x", biosdev);
+ for (i = 0; i < nbcinfo; i++) {
+ DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
+ if (bcinfo[i].bc_unit == biosdev)
+ return(i);
+ }
+ return(-1);
+}
+
+int
+bc_unit2bios(int unit)
+{
+ if ((unit >= 0) && (unit < nbcinfo))
+ return(bcinfo[unit].bc_unit);
+ return(-1);
+}
+
+/*
+ * We can't quiz, we have to be told what device to use, so this functoin
+ * doesn't do anything. Instead, the loader calls bc_add() with the BIOS
+ * device number to add.
+ */
+static int
+bc_init(void)
+{
+
+ return (0);
+}
+
+int
+bc_add(int biosdev)
+{
+
+ if (nbcinfo >= MAXBCDEV)
+ return (-1);
+ bcinfo[nbcinfo].bc_unit = biosdev;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4b01;
+ v86.edx = biosdev;
+ v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
+ v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
+ v86int();
+ if ((v86.eax & 0xff00) != 0)
+ return (-1);
+
+ printf("BIOS CD is cd%d\n", nbcinfo);
+ nbcinfo++;
+ bcache_add_dev(nbcinfo); /* register cd device in bcache */
+ return(0);
+}
+
+/*
+ * Print information about disks
+ */
+static int
+bc_print(int verbose)
+{
+ char line[80];
+ int i, ret = 0;
+
+ if (nbcinfo == 0)
+ return (0);
+
+ printf("%s devices:", bioscd.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ for (i = 0; i < nbcinfo; i++) {
+ snprintf(line, sizeof(line), " cd%d: Device 0x%x\n", i,
+ bcinfo[i].bc_sp.sp_devicespec);
+ if ((ret = pager_output(line)) != 0)
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * Attempt to open the disk described by (dev) for use by (f).
+ */
+static int
+bc_open(struct open_file *f, ...)
+{
+ va_list ap;
+ struct i386_devdesc *dev;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct i386_devdesc *);
+ va_end(ap);
+ if (dev->d_unit >= nbcinfo) {
+ DEBUG("attempt to open nonexistent disk");
+ return(ENXIO);
+ }
+
+ BC(dev).bc_open++;
+ if (BC(dev).bc_bcache == NULL)
+ BC(dev).bc_bcache = bcache_allocate();
+ return(0);
+}
+
+static int
+bc_close(struct open_file *f)
+{
+ struct i386_devdesc *dev;
+
+ dev = (struct i386_devdesc *)f->f_devdata;
+ BC(dev).bc_open--;
+ if (BC(dev).bc_open == 0) {
+ bcache_free(BC(dev).bc_bcache);
+ BC(dev).bc_bcache = NULL;
+ }
+ return(0);
+}
+
+static int
+bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct bcache_devdata bcd;
+ struct i386_devdesc *dev;
+
+ dev = (struct i386_devdesc *)devdata;
+ bcd.dv_strategy = bc_realstrategy;
+ bcd.dv_devdata = devdata;
+ bcd.dv_cache = BC(dev).bc_bcache;
+
+ return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize));
+}
+
+static int
+bc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct i386_devdesc *dev;
+ int unit;
+ int blks;
+#ifdef BD_SUPPORT_FRAGS
+ char fragbuf[BIOSCD_SECSIZE];
+ size_t fragsize;
+
+ fragsize = size % BIOSCD_SECSIZE;
+#else
+ if (size % BIOSCD_SECSIZE)
+ return (EINVAL);
+#endif
+
+ if ((rw & F_MASK) != F_READ)
+ return(EROFS);
+ dev = (struct i386_devdesc *)devdata;
+ unit = dev->d_unit;
+ blks = size / BIOSCD_SECSIZE;
+ if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
+ return (EINVAL);
+ dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
+ DEBUG("read %d from %lld to %p", blks, dblk, buf);
+
+ if (rsize)
+ *rsize = 0;
+ if ((blks = bc_read(unit, dblk, blks, buf)) < 0) {
+ DEBUG("read error");
+ return (EIO);
+ } else {
+ if (size / BIOSCD_SECSIZE > blks) {
+ if (rsize)
+ *rsize = blks * BIOSCD_SECSIZE;
+ return (0);
+ }
+ }
+#ifdef BD_SUPPORT_FRAGS
+ DEBUG("frag read %d from %lld+%d to %p",
+ fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
+ if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf) != 1) {
+ if (blks) {
+ if (rsize)
+ *rsize = blks * BIOSCD_SECSIZE;
+ return (0);
+ }
+ DEBUG("frag read error");
+ return(EIO);
+ }
+ bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
+#endif
+ if (rsize)
+ *rsize = size;
+ return (0);
+}
+
+/* return negative value for an error, otherwise blocks read */
+static int
+bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
+{
+ u_int maxfer, resid, result, retry, x;
+ caddr_t bbuf, p, xp;
+ static struct edd_packet packet;
+ int biosdev;
+#ifdef DISK_DEBUG
+ int error;
+#endif
+
+ /* Just in case some idiot actually tries to read -1 blocks... */
+ if (blks < 0)
+ return (-1);
+
+ /* If nothing to do, just return succcess. */
+ if (blks == 0)
+ return (0);
+
+ /* Decide whether we have to bounce */
+ if (VTOP(dest) >> 20 != 0) {
+ /*
+ * The destination buffer is above first 1MB of
+ * physical memory so we have to arrange a suitable
+ * bounce buffer.
+ */
+ x = V86_IO_BUFFER_SIZE / BIOSCD_SECSIZE;
+ x = min(x, (unsigned)blks);
+ bbuf = PTOV(V86_IO_BUFFER);
+ maxfer = x;
+ } else {
+ bbuf = NULL;
+ maxfer = 0;
+ }
+
+ biosdev = bc_unit2bios(unit);
+ resid = blks;
+ p = dest;
+
+ while (resid > 0) {
+ if (bbuf)
+ xp = bbuf;
+ else
+ xp = p;
+ x = resid;
+ if (maxfer > 0)
+ x = min(x, maxfer);
+
+ /*
+ * Loop retrying the operation a couple of times. The BIOS
+ * may also retry.
+ */
+ for (retry = 0; retry < 3; retry++) {
+ /* If retrying, reset the drive */
+ if (retry > 0) {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0;
+ v86.edx = biosdev;
+ v86int();
+ }
+
+ packet.len = sizeof(struct edd_packet);
+ packet.count = x;
+ packet.off = VTOPOFF(xp);
+ packet.seg = VTOPSEG(xp);
+ packet.lba = dblk;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4200;
+ v86.edx = biosdev;
+ v86.ds = VTOPSEG(&packet);
+ v86.esi = VTOPOFF(&packet);
+ v86int();
+ result = V86_CY(v86.efl);
+ if (result == 0)
+ break;
+ /* fall back to 1 sector read */
+ x = 1;
+ }
+
+#ifdef DISK_DEBUG
+ error = (v86.eax >> 8) & 0xff;
+#endif
+ DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
+ VTOP(p), result ? "failed" : "ok");
+ DEBUG("unit %d status 0x%x", unit, error);
+
+ /* still an error? break off */
+ if (result != 0)
+ break;
+
+ if (bbuf != NULL)
+ bcopy(bbuf, p, x * BIOSCD_SECSIZE);
+ p += (x * BIOSCD_SECSIZE);
+ dblk += x;
+ resid -= x;
+ }
+
+/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */
+
+ if (blks - resid == 0)
+ return (-1); /* read failed */
+
+ return (blks - resid);
+}
+
+/*
+ * Return a suitable dev_t value for (dev).
+ */
+int
+bc_getdev(struct i386_devdesc *dev)
+{
+ int biosdev, unit;
+ int major;
+ int rootdev;
+
+ unit = dev->d_unit;
+ biosdev = bc_unit2bios(unit);
+ DEBUG("unit %d BIOS device %d", unit, biosdev);
+ if (biosdev == -1) /* not a BIOS device */
+ return(-1);
+
+ /*
+ * XXX: Need to examine device spec here to figure out if SCSI or
+ * ATAPI. No idea on how to figure out device number. All we can
+ * really pass to the kernel is what bus and device on which bus we
+ * were booted from, which dev_t isn't well suited to since those
+ * number don't match to unit numbers very well. We may just need
+ * to engage in a hack where we pass -C to the boot args if we are
+ * the boot device.
+ */
+ major = ACDMAJOR;
+ unit = 0; /* XXX */
+
+ /* XXX: Assume partition 'a'. */
+ rootdev = MAKEBOOTDEV(major, 0, unit, 0);
+ DEBUG("dev is 0x%x\n", rootdev);
+ return(rootdev);
+}
diff --git a/stand/i386/libi386/biosdisk.c b/stand/i386/libi386/biosdisk.c
new file mode 100644
index 0000000..a2147e2
--- /dev/null
+++ b/stand/i386/libi386/biosdisk.c
@@ -0,0 +1,1013 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * BIOS disk device handling.
+ *
+ * Ideas and algorithms from:
+ *
+ * - NetBSD libi386/biosdisk.c
+ * - FreeBSD biosboot/disk.c
+ *
+ */
+
+#include <sys/disk.h>
+#include <sys/limits.h>
+#include <stand.h>
+#include <machine/bootinfo.h>
+#include <stdarg.h>
+
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <edd.h>
+#include "disk.h"
+#include "libi386.h"
+
+#ifdef LOADER_GELI_SUPPORT
+#include "cons.h"
+#include "drv.h"
+#include "gpt.h"
+#include "part.h"
+#include <uuid.h>
+struct pentry {
+ struct ptable_entry part;
+ uint64_t flags;
+ union {
+ uint8_t bsd;
+ uint8_t mbr;
+ uuid_t gpt;
+ uint16_t vtoc8;
+ } type;
+ STAILQ_ENTRY(pentry) entry;
+};
+struct ptable {
+ enum ptable_type type;
+ uint16_t sectorsize;
+ uint64_t sectors;
+
+ STAILQ_HEAD(, pentry) entries;
+};
+
+#include "geliboot.c"
+#endif /* LOADER_GELI_SUPPORT */
+
+CTASSERT(sizeof(struct i386_devdesc) >= sizeof(struct disk_devdesc));
+
+#define BIOS_NUMDRIVES 0x475
+#define BIOSDISK_SECSIZE 512
+#define BUFSIZE (1 * BIOSDISK_SECSIZE)
+
+#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */
+#define WDMAJOR 0 /* major numbers for devices we frontend for */
+#define WFDMAJOR 1
+#define FDMAJOR 2
+#define DAMAJOR 4
+
+#ifdef DISK_DEBUG
+# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
+#else
+# define DEBUG(fmt, args...)
+#endif
+
+/*
+ * List of BIOS devices, translation from disk unit number to
+ * BIOS unit number.
+ */
+static struct bdinfo
+{
+ int bd_unit; /* BIOS unit number */
+ int bd_cyl; /* BIOS geometry */
+ int bd_hds;
+ int bd_sec;
+ int bd_flags;
+#define BD_MODEINT13 0x0000
+#define BD_MODEEDD1 0x0001
+#define BD_MODEEDD3 0x0002
+#define BD_MODEMASK 0x0003
+#define BD_FLOPPY 0x0004
+ int bd_type; /* BIOS 'drive type' (floppy only) */
+ uint16_t bd_sectorsize; /* Sector size */
+ uint64_t bd_sectors; /* Disk size */
+ int bd_open; /* reference counter */
+ void *bd_bcache; /* buffer cache data */
+} bdinfo [MAXBDDEV];
+static int nbdinfo = 0;
+
+#define BD(dev) (bdinfo[(dev)->d_unit])
+
+static int bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks,
+ caddr_t dest);
+static int bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks,
+ caddr_t dest);
+static int bd_int13probe(struct bdinfo *bd);
+
+static int bd_init(void);
+static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize);
+static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize);
+static int bd_open(struct open_file *f, ...);
+static int bd_close(struct open_file *f);
+static int bd_ioctl(struct open_file *f, u_long cmd, void *data);
+static int bd_print(int verbose);
+
+#ifdef LOADER_GELI_SUPPORT
+enum isgeli {
+ ISGELI_UNKNOWN,
+ ISGELI_NO,
+ ISGELI_YES
+};
+static enum isgeli geli_status[MAXBDDEV][MAXTBLENTS];
+
+int bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf,
+ size_t bytes);
+#endif /* LOADER_GELI_SUPPORT */
+
+struct devsw biosdisk = {
+ "disk",
+ DEVT_DISK,
+ bd_init,
+ bd_strategy,
+ bd_open,
+ bd_close,
+ bd_ioctl,
+ bd_print,
+ NULL
+};
+
+/*
+ * Translate between BIOS device numbers and our private unit numbers.
+ */
+int
+bd_bios2unit(int biosdev)
+{
+ int i;
+
+ DEBUG("looking for bios device 0x%x", biosdev);
+ for (i = 0; i < nbdinfo; i++) {
+ DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
+ if (bdinfo[i].bd_unit == biosdev)
+ return (i);
+ }
+ return (-1);
+}
+
+int
+bd_unit2bios(int unit)
+{
+
+ if ((unit >= 0) && (unit < nbdinfo))
+ return (bdinfo[unit].bd_unit);
+ return (-1);
+}
+
+/*
+ * Quiz the BIOS for disk devices, save a little info about them.
+ */
+static int
+bd_init(void)
+{
+ int base, unit, nfd = 0;
+
+#ifdef LOADER_GELI_SUPPORT
+ geli_init();
+#endif
+ /* sequence 0, 0x80 */
+ for (base = 0; base <= 0x80; base += 0x80) {
+ for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
+#ifndef VIRTUALBOX
+ /*
+ * Check the BIOS equipment list for number
+ * of fixed disks.
+ */
+ if(base == 0x80 &&
+ (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES)))
+ break;
+#endif
+ bdinfo[nbdinfo].bd_open = 0;
+ bdinfo[nbdinfo].bd_bcache = NULL;
+ bdinfo[nbdinfo].bd_unit = unit;
+ bdinfo[nbdinfo].bd_flags = unit < 0x80 ? BD_FLOPPY: 0;
+ if (!bd_int13probe(&bdinfo[nbdinfo]))
+ break;
+
+ /* XXX we need "disk aliases" to make this simpler */
+ printf("BIOS drive %c: is disk%d\n", (unit < 0x80) ?
+ ('A' + unit): ('C' + unit - 0x80), nbdinfo);
+ nbdinfo++;
+ if (base == 0x80)
+ nfd++;
+ }
+ }
+ bcache_add_dev(nbdinfo);
+ return(0);
+}
+
+/*
+ * Try to detect a device supported by the legacy int13 BIOS
+ */
+static int
+bd_int13probe(struct bdinfo *bd)
+{
+ struct edd_params params;
+ int ret = 1; /* assume success */
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x800;
+ v86.edx = bd->bd_unit;
+ v86int();
+
+ /* Don't error out if we get bad sector number, try EDD as well */
+ if (V86_CY(v86.efl) || /* carry set */
+ (v86.edx & 0xff) <= (unsigned)(bd->bd_unit & 0x7f)) /* unit # bad */
+ return (0); /* skip device */
+
+ if ((v86.ecx & 0x3f) == 0) /* absurd sector number */
+ ret = 0; /* set error */
+
+ /* Convert max cyl # -> # of cylinders */
+ bd->bd_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
+ /* Convert max head # -> # of heads */
+ bd->bd_hds = ((v86.edx & 0xff00) >> 8) + 1;
+ bd->bd_sec = v86.ecx & 0x3f;
+ bd->bd_type = v86.ebx & 0xff;
+ bd->bd_flags |= BD_MODEINT13;
+
+ /* Calculate sectors count from the geometry */
+ bd->bd_sectors = bd->bd_cyl * bd->bd_hds * bd->bd_sec;
+ bd->bd_sectorsize = BIOSDISK_SECSIZE;
+ DEBUG("unit 0x%x geometry %d/%d/%d", bd->bd_unit, bd->bd_cyl,
+ bd->bd_hds, bd->bd_sec);
+
+ /* Determine if we can use EDD with this device. */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4100;
+ v86.edx = bd->bd_unit;
+ v86.ebx = 0x55aa;
+ v86int();
+ if (V86_CY(v86.efl) || /* carry set */
+ (v86.ebx & 0xffff) != 0xaa55 || /* signature */
+ (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
+ return (ret); /* return code from int13 AH=08 */
+
+ /* EDD supported */
+ bd->bd_flags |= BD_MODEEDD1;
+ if ((v86.eax & 0xff00) >= 0x3000)
+ bd->bd_flags |= BD_MODEEDD3;
+ /* Get disk params */
+ params.len = sizeof(struct edd_params);
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4800;
+ v86.edx = bd->bd_unit;
+ v86.ds = VTOPSEG(&params);
+ v86.esi = VTOPOFF(&params);
+ v86int();
+ if (!V86_CY(v86.efl)) {
+ uint64_t total;
+
+ /*
+ * Sector size must be a multiple of 512 bytes.
+ * An alternate test would be to check power of 2,
+ * powerof2(params.sector_size).
+ */
+ if (params.sector_size % BIOSDISK_SECSIZE)
+ bd->bd_sectorsize = BIOSDISK_SECSIZE;
+ else
+ bd->bd_sectorsize = params.sector_size;
+
+ total = bd->bd_sectorsize * params.sectors;
+ if (params.sectors != 0) {
+ /* Only update if we did not overflow. */
+ if (total > params.sectors)
+ bd->bd_sectors = params.sectors;
+ }
+
+ total = (uint64_t)params.cylinders *
+ params.heads * params.sectors_per_track;
+ if (bd->bd_sectors < total)
+ bd->bd_sectors = total;
+
+ ret = 1;
+ }
+ DEBUG("unit 0x%x flags %x, sectors %llu, sectorsize %u",
+ bd->bd_unit, bd->bd_flags, bd->bd_sectors, bd->bd_sectorsize);
+ return (ret);
+}
+
+/*
+ * Print information about disks
+ */
+static int
+bd_print(int verbose)
+{
+ static char line[80];
+ struct disk_devdesc dev;
+ int i, ret = 0;
+
+ if (nbdinfo == 0)
+ return (0);
+
+ printf("%s devices:", biosdisk.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ for (i = 0; i < nbdinfo; i++) {
+ snprintf(line, sizeof(line),
+ " disk%d: BIOS drive %c (%ju X %u):\n", i,
+ (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit):
+ ('C' + bdinfo[i].bd_unit - 0x80),
+ (uintmax_t)bdinfo[i].bd_sectors,
+ bdinfo[i].bd_sectorsize);
+ if ((ret = pager_output(line)) != 0)
+ break;
+ dev.d_dev = &biosdisk;
+ dev.d_unit = i;
+ dev.d_slice = -1;
+ dev.d_partition = -1;
+ if (disk_open(&dev,
+ bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors,
+ bdinfo[i].bd_sectorsize) == 0) {
+ snprintf(line, sizeof(line), " disk%d", i);
+ ret = disk_print(&dev, line, verbose);
+ disk_close(&dev);
+ if (ret != 0)
+ return (ret);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Attempt to open the disk described by (dev) for use by (f).
+ *
+ * Note that the philosophy here is "give them exactly what
+ * they ask for". This is necessary because being too "smart"
+ * about what the user might want leads to complications.
+ * (eg. given no slice or partition value, with a disk that is
+ * sliced - are they after the first BSD slice, or the DOS
+ * slice before it?)
+ */
+static int
+bd_open(struct open_file *f, ...)
+{
+ struct disk_devdesc *dev, rdev;
+ struct disk_devdesc disk;
+ int err, g_err;
+ va_list ap;
+ uint64_t size;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct disk_devdesc *);
+ va_end(ap);
+
+ if (dev->d_unit < 0 || dev->d_unit >= nbdinfo)
+ return (EIO);
+ BD(dev).bd_open++;
+ if (BD(dev).bd_bcache == NULL)
+ BD(dev).bd_bcache = bcache_allocate();
+
+ /*
+ * Read disk size from partition.
+ * This is needed to work around buggy BIOS systems returning
+ * wrong (truncated) disk media size.
+ * During bd_probe() we tested if the mulitplication of bd_sectors
+ * would overflow so it should be safe to perform here.
+ */
+ disk.d_dev = dev->d_dev;
+ disk.d_type = dev->d_type;
+ disk.d_unit = dev->d_unit;
+ disk.d_opendata = NULL;
+ disk.d_slice = -1;
+ disk.d_partition = -1;
+ disk.d_offset = 0;
+ if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
+ BD(dev).bd_sectorsize) == 0) {
+
+ if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) {
+ size /= BD(dev).bd_sectorsize;
+ if (size > BD(dev).bd_sectors)
+ BD(dev).bd_sectors = size;
+ }
+ disk_close(&disk);
+ }
+
+ err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
+ BD(dev).bd_sectorsize);
+
+#ifdef LOADER_GELI_SUPPORT
+ static char gelipw[GELI_PW_MAXLEN];
+ char *passphrase;
+
+ if (err)
+ return (err);
+
+ /* if we already know there is no GELI, skip the rest */
+ if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_UNKNOWN)
+ return (err);
+
+ struct dsk dskp;
+ struct ptable *table = NULL;
+ struct ptable_entry part;
+ struct pentry *entry;
+ int geli_part = 0;
+
+ dskp.drive = bd_unit2bios(dev->d_unit);
+ dskp.type = dev->d_type;
+ dskp.unit = dev->d_unit;
+ dskp.slice = dev->d_slice;
+ dskp.part = dev->d_partition;
+ dskp.start = dev->d_offset;
+
+ memcpy(&rdev, dev, sizeof(rdev));
+ /* to read the GPT table, we need to read the first sector */
+ rdev.d_offset = 0;
+ /* We need the LBA of the end of the partition */
+ table = ptable_open(&rdev, BD(dev).bd_sectors,
+ BD(dev).bd_sectorsize, ptblread);
+ if (table == NULL) {
+ DEBUG("Can't read partition table");
+ /* soft failure, return the exit status of disk_open */
+ return (err);
+ }
+
+ if (table->type == PTABLE_GPT)
+ dskp.part = 255;
+
+ STAILQ_FOREACH(entry, &table->entries, entry) {
+ dskp.slice = entry->part.index;
+ dskp.start = entry->part.start;
+ if (is_geli(&dskp) == 0) {
+ geli_status[dev->d_unit][dskp.slice] = ISGELI_YES;
+ return (0);
+ }
+ if (geli_taste(bios_read, &dskp,
+ entry->part.end - entry->part.start) == 0) {
+ if (geli_havekey(&dskp) == 0) {
+ geli_status[dev->d_unit][dskp.slice] = ISGELI_YES;
+ geli_part++;
+ continue;
+ }
+ if ((passphrase = getenv("kern.geom.eli.passphrase"))
+ != NULL) {
+ /* Use the cached passphrase */
+ bcopy(passphrase, &gelipw, GELI_PW_MAXLEN);
+ }
+ if (geli_passphrase(&gelipw, dskp.unit, 'p',
+ (dskp.slice > 0 ? dskp.slice : dskp.part),
+ &dskp) == 0) {
+ setenv("kern.geom.eli.passphrase", &gelipw, 1);
+ bzero(gelipw, sizeof(gelipw));
+ geli_status[dev->d_unit][dskp.slice] = ISGELI_YES;
+ geli_part++;
+ continue;
+ }
+ } else
+ geli_status[dev->d_unit][dskp.slice] = ISGELI_NO;
+ }
+
+ /* none of the partitions on this disk have GELI */
+ if (geli_part == 0) {
+ /* found no GELI */
+ geli_status[dev->d_unit][dev->d_slice] = ISGELI_NO;
+ }
+#endif /* LOADER_GELI_SUPPORT */
+
+ return (err);
+}
+
+static int
+bd_close(struct open_file *f)
+{
+ struct disk_devdesc *dev;
+
+ dev = (struct disk_devdesc *)f->f_devdata;
+ BD(dev).bd_open--;
+ if (BD(dev).bd_open == 0) {
+ bcache_free(BD(dev).bd_bcache);
+ BD(dev).bd_bcache = NULL;
+ }
+ return (disk_close(dev));
+}
+
+static int
+bd_ioctl(struct open_file *f, u_long cmd, void *data)
+{
+ struct disk_devdesc *dev;
+ int rc;
+
+ dev = (struct disk_devdesc *)f->f_devdata;
+
+ rc = disk_ioctl(dev, cmd, data);
+ if (rc != ENOTTY)
+ return (rc);
+
+ switch (cmd) {
+ case DIOCGSECTORSIZE:
+ *(u_int *)data = BD(dev).bd_sectorsize;
+ break;
+ case DIOCGMEDIASIZE:
+ *(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
+ break;
+ default:
+ return (ENOTTY);
+ }
+ return (0);
+}
+
+static int
+bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct bcache_devdata bcd;
+ struct disk_devdesc *dev;
+
+ dev = (struct disk_devdesc *)devdata;
+ bcd.dv_strategy = bd_realstrategy;
+ bcd.dv_devdata = devdata;
+ bcd.dv_cache = BD(dev).bd_bcache;
+ return (bcache_strategy(&bcd, rw, dblk + dev->d_offset,
+ size, buf, rsize));
+}
+
+static int
+bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
+ uint64_t disk_blocks;
+ int blks, rc;
+#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
+ char fragbuf[BIOSDISK_SECSIZE];
+ size_t fragsize;
+
+ fragsize = size % BIOSDISK_SECSIZE;
+#else
+ if (size % BD(dev).bd_sectorsize)
+ panic("bd_strategy: %d bytes I/O not multiple of block size", size);
+#endif
+
+ DEBUG("open_disk %p", dev);
+
+ /*
+ * Check the value of the size argument. We do have quite small
+ * heap (64MB), but we do not know good upper limit, so we check against
+ * INT_MAX here. This will also protect us against possible overflows
+ * while translating block count to bytes.
+ */
+ if (size > INT_MAX) {
+ DEBUG("too large read: %zu bytes", size);
+ return (EIO);
+ }
+
+ blks = size / BD(dev).bd_sectorsize;
+ if (dblk > dblk + blks)
+ return (EIO);
+
+ if (rsize)
+ *rsize = 0;
+
+ /* Get disk blocks, this value is either for whole disk or for partition */
+ if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks)) {
+ /* DIOCGMEDIASIZE does return bytes. */
+ disk_blocks /= BD(dev).bd_sectorsize;
+ } else {
+ /* We should not get here. Just try to survive. */
+ disk_blocks = BD(dev).bd_sectors - dev->d_offset;
+ }
+
+ /* Validate source block address. */
+ if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks)
+ return (EIO);
+
+ /*
+ * Truncate if we are crossing disk or partition end.
+ */
+ if (dblk + blks >= dev->d_offset + disk_blocks) {
+ blks = dev->d_offset + disk_blocks - dblk;
+ size = blks * BD(dev).bd_sectorsize;
+ DEBUG("short read %d", blks);
+ }
+
+ switch (rw & F_MASK) {
+ case F_READ:
+ DEBUG("read %d from %lld to %p", blks, dblk, buf);
+
+ if (blks && (rc = bd_read(dev, dblk, blks, buf))) {
+ /* Filter out floppy controller errors */
+ if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) {
+ printf("read %d from %lld to %p, error: 0x%x", blks, dblk,
+ buf, rc);
+ }
+ return (EIO);
+ }
+#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
+ DEBUG("bd_strategy: frag read %d from %d+%d to %p",
+ fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
+ if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
+ DEBUG("frag read error");
+ return(EIO);
+ }
+ bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
+#endif
+ break;
+ case F_WRITE :
+ DEBUG("write %d from %d to %p", blks, dblk, buf);
+
+ if (blks && bd_write(dev, dblk, blks, buf)) {
+ DEBUG("write error");
+ return (EIO);
+ }
+#ifdef BD_SUPPORT_FRAGS
+ if(fragsize) {
+ DEBUG("Attempted to write a frag");
+ return (EIO);
+ }
+#endif
+ break;
+ default:
+ /* DO NOTHING */
+ return (EROFS);
+ }
+
+ if (rsize)
+ *rsize = size;
+ return (0);
+}
+
+static int
+bd_edd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
+ int write)
+{
+ static struct edd_packet packet;
+
+ packet.len = sizeof(struct edd_packet);
+ packet.count = blks;
+ packet.off = VTOPOFF(dest);
+ packet.seg = VTOPSEG(dest);
+ packet.lba = dblk;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ if (write)
+ /* Should we Write with verify ?? 0x4302 ? */
+ v86.eax = 0x4300;
+ else
+ v86.eax = 0x4200;
+ v86.edx = BD(dev).bd_unit;
+ v86.ds = VTOPSEG(&packet);
+ v86.esi = VTOPOFF(&packet);
+ v86int();
+ if (V86_CY(v86.efl))
+ return (v86.eax >> 8);
+ return (0);
+}
+
+static int
+bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
+ int write)
+{
+ u_int x, bpc, cyl, hd, sec;
+
+ bpc = BD(dev).bd_sec * BD(dev).bd_hds; /* blocks per cylinder */
+ x = dblk;
+ cyl = x / bpc; /* block # / blocks per cylinder */
+ x %= bpc; /* block offset into cylinder */
+ hd = x / BD(dev).bd_sec; /* offset / blocks per track */
+ sec = x % BD(dev).bd_sec; /* offset into track */
+
+ /* correct sector number for 1-based BIOS numbering */
+ sec++;
+
+ if (cyl > 1023)
+ /* CHS doesn't support cylinders > 1023. */
+ return (1);
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ if (write)
+ v86.eax = 0x300 | blks;
+ else
+ v86.eax = 0x200 | blks;
+ v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
+ v86.edx = (hd << 8) | BD(dev).bd_unit;
+ v86.es = VTOPSEG(dest);
+ v86.ebx = VTOPOFF(dest);
+ v86int();
+ if (V86_CY(v86.efl))
+ return (v86.eax >> 8);
+ return (0);
+}
+
+static int
+bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write)
+{
+ u_int x, sec, result, resid, retry, maxfer;
+ caddr_t p, xp, bbuf;
+
+ /* Just in case some idiot actually tries to read/write -1 blocks... */
+ if (blks < 0)
+ return (-1);
+
+ resid = blks;
+ p = dest;
+
+ /* Decide whether we have to bounce */
+ if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 &&
+ (VTOP(dest) >> 16) != (VTOP(dest +
+ blks * BD(dev).bd_sectorsize) >> 16))) {
+
+ /*
+ * There is a 64k physical boundary somewhere in the
+ * destination buffer, or the destination buffer is above
+ * first 1MB of physical memory so we have to arrange a
+ * suitable bounce buffer. Allocate a buffer twice as large
+ * as we need to. Use the bottom half unless there is a break
+ * there, in which case we use the top half.
+ */
+ x = V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize;
+ x = min(x, (unsigned)blks);
+ bbuf = PTOV(V86_IO_BUFFER);
+ maxfer = x; /* limit transfers to bounce region size */
+ } else {
+ bbuf = NULL;
+ maxfer = 0;
+ }
+
+ while (resid > 0) {
+ /*
+ * Play it safe and don't cross track boundaries.
+ * (XXX this is probably unnecessary)
+ */
+ sec = dblk % BD(dev).bd_sec; /* offset into track */
+ x = min(BD(dev).bd_sec - sec, resid);
+ if (maxfer > 0)
+ x = min(x, maxfer); /* fit bounce buffer */
+
+ /* where do we transfer to? */
+ xp = bbuf == NULL ? p : bbuf;
+
+ /*
+ * Put your Data In, Put your Data out,
+ * Put your Data In, and shake it all about
+ */
+ if (write && bbuf != NULL)
+ bcopy(p, bbuf, x * BD(dev).bd_sectorsize);
+
+ /*
+ * Loop retrying the operation a couple of times. The BIOS
+ * may also retry.
+ */
+ for (retry = 0; retry < 3; retry++) {
+ /* if retrying, reset the drive */
+ if (retry > 0) {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0;
+ v86.edx = BD(dev).bd_unit;
+ v86int();
+ }
+
+ if (BD(dev).bd_flags & BD_MODEEDD1)
+ result = bd_edd_io(dev, dblk, x, xp, write);
+ else
+ result = bd_chs_io(dev, dblk, x, xp, write);
+ if (result == 0)
+ break;
+ }
+
+ if (write)
+ DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
+ p, VTOP(p), dblk, result ? "failed" : "ok");
+ else
+ DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
+ dblk, p, VTOP(p), result ? "failed" : "ok");
+ if (result) {
+ return (result);
+ }
+ if (!write && bbuf != NULL)
+ bcopy(bbuf, p, x * BD(dev).bd_sectorsize);
+ p += (x * BD(dev).bd_sectorsize);
+ dblk += x;
+ resid -= x;
+ }
+
+/* hexdump(dest, (blks * BD(dev).bd_sectorsize)); */
+ return(0);
+}
+
+static int
+bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest)
+{
+#ifdef LOADER_GELI_SUPPORT
+ struct dsk dskp;
+ off_t p_off, diff;
+ daddr_t alignlba;
+ int err, n, alignblks;
+ char *tmpbuf;
+
+ /* if we already know there is no GELI, skip the rest */
+ if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_YES)
+ return (bd_io(dev, dblk, blks, dest, 0));
+
+ if (geli_status[dev->d_unit][dev->d_slice] == ISGELI_YES) {
+ /*
+ * Align reads to DEV_GELIBOOT_BSIZE bytes because partial
+ * sectors cannot be decrypted. Round the requested LBA down to
+ * nearest multiple of DEV_GELIBOOT_BSIZE bytes.
+ */
+ alignlba = rounddown2(dblk * BD(dev).bd_sectorsize,
+ DEV_GELIBOOT_BSIZE) / BD(dev).bd_sectorsize;
+ /*
+ * Round number of blocks to read up to nearest multiple of
+ * DEV_GELIBOOT_BSIZE
+ */
+ diff = (dblk - alignlba) * BD(dev).bd_sectorsize;
+ alignblks = roundup2(blks * BD(dev).bd_sectorsize + diff,
+ DEV_GELIBOOT_BSIZE) / BD(dev).bd_sectorsize;
+
+ /*
+ * If the read is rounded up to a larger size, use a temporary
+ * buffer here because the buffer provided by the caller may be
+ * too small.
+ */
+ if (diff == 0) {
+ tmpbuf = dest;
+ } else {
+ tmpbuf = malloc(alignblks * BD(dev).bd_sectorsize);
+ if (tmpbuf == NULL) {
+ return (-1);
+ }
+ }
+
+ err = bd_io(dev, alignlba, alignblks, tmpbuf, 0);
+ if (err)
+ return (err);
+
+ dskp.drive = bd_unit2bios(dev->d_unit);
+ dskp.type = dev->d_type;
+ dskp.unit = dev->d_unit;
+ dskp.slice = dev->d_slice;
+ dskp.part = dev->d_partition;
+ dskp.start = dev->d_offset;
+
+ /* GELI needs the offset relative to the partition start */
+ p_off = alignlba - dskp.start;
+
+ err = geli_read(&dskp, p_off * BD(dev).bd_sectorsize, tmpbuf,
+ alignblks * BD(dev).bd_sectorsize);
+ if (err)
+ return (err);
+
+ if (tmpbuf != dest) {
+ bcopy(tmpbuf + diff, dest, blks * BD(dev).bd_sectorsize);
+ free(tmpbuf);
+ }
+ return (0);
+ }
+#endif /* LOADER_GELI_SUPPORT */
+
+ return (bd_io(dev, dblk, blks, dest, 0));
+}
+
+static int
+bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest)
+{
+
+ return (bd_io(dev, dblk, blks, dest, 1));
+}
+
+/*
+ * Return the BIOS geometry of a given "fixed drive" in a format
+ * suitable for the legacy bootinfo structure. Since the kernel is
+ * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
+ * prefer to get the information directly, rather than rely on being
+ * able to put it together from information already maintained for
+ * different purposes and for a probably different number of drives.
+ *
+ * For valid drives, the geometry is expected in the format (31..0)
+ * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
+ * indicated by returning the geometry of a "1.2M" PC-format floppy
+ * disk. And, incidentally, what is returned is not the geometry as
+ * such but the highest valid cylinder, head, and sector numbers.
+ */
+u_int32_t
+bd_getbigeom(int bunit)
+{
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x800;
+ v86.edx = 0x80 + bunit;
+ v86int();
+ if (V86_CY(v86.efl))
+ return 0x4f010f;
+ return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
+ (v86.edx & 0xff00) | (v86.ecx & 0x3f);
+}
+
+/*
+ * Return a suitable dev_t value for (dev).
+ *
+ * In the case where it looks like (dev) is a SCSI disk, we allow the number of
+ * IDE disks to be specified in $num_ide_disks. There should be a Better Way.
+ */
+int
+bd_getdev(struct i386_devdesc *d)
+{
+ struct disk_devdesc *dev;
+ int biosdev;
+ int major;
+ int rootdev;
+ char *nip, *cp;
+ int i, unit;
+
+ dev = (struct disk_devdesc *)d;
+ biosdev = bd_unit2bios(dev->d_unit);
+ DEBUG("unit %d BIOS device %d", dev->d_unit, biosdev);
+ if (biosdev == -1) /* not a BIOS device */
+ return(-1);
+ if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
+ BD(dev).bd_sectorsize) != 0) /* oops, not a viable device */
+ return (-1);
+ else
+ disk_close(dev);
+
+ if (biosdev < 0x80) {
+ /* floppy (or emulated floppy) or ATAPI device */
+ if (bdinfo[dev->d_unit].bd_type == DT_ATAPI) {
+ /* is an ATAPI disk */
+ major = WFDMAJOR;
+ } else {
+ /* is a floppy disk */
+ major = FDMAJOR;
+ }
+ } else {
+ /* assume an IDE disk */
+ major = WDMAJOR;
+ }
+ /* default root disk unit number */
+ unit = biosdev & 0x7f;
+
+ /* XXX a better kludge to set the root disk unit number */
+ if ((nip = getenv("root_disk_unit")) != NULL) {
+ i = strtol(nip, &cp, 0);
+ /* check for parse error */
+ if ((cp != nip) && (*cp == 0))
+ unit = i;
+ }
+
+ rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition);
+ DEBUG("dev is 0x%x\n", rootdev);
+ return(rootdev);
+}
+
+#ifdef LOADER_GELI_SUPPORT
+int
+bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf, size_t bytes)
+{
+ struct disk_devdesc dev;
+
+ dev.d_dev = &biosdisk;
+ dev.d_type = priv->type;
+ dev.d_unit = priv->unit;
+ dev.d_slice = priv->slice;
+ dev.d_partition = priv->part;
+ dev.d_offset = priv->start;
+
+ off = off / BD(&dev).bd_sectorsize;
+ /* GELI gives us the offset relative to the partition start */
+ off += dev.d_offset;
+ bytes = bytes / BD(&dev).bd_sectorsize;
+
+ return (bd_io(&dev, off, bytes, buf, 0));
+}
+#endif /* LOADER_GELI_SUPPORT */
diff --git a/stand/i386/libi386/biosmem.c b/stand/i386/libi386/biosmem.c
new file mode 100644
index 0000000..7105757
--- /dev/null
+++ b/stand/i386/libi386/biosmem.c
@@ -0,0 +1,258 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Obtain memory configuration information from the BIOS
+ */
+#include <stand.h>
+#include <machine/pc/bios.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+#include "smbios.h"
+
+vm_offset_t memtop, memtop_copyin, high_heap_base;
+uint32_t bios_basemem, bios_extmem, high_heap_size;
+
+static struct bios_smap_xattr smap;
+
+/*
+ * Used to track which method was used to set BIOS memory
+ * regions.
+ */
+static uint8_t b_bios_probed;
+#define B_BASEMEM_E820 0x1
+#define B_BASEMEM_12 0x2
+#define B_EXTMEM_E820 0x4
+#define B_EXTMEM_E801 0x8
+#define B_EXTMEM_8800 0x10
+
+/*
+ * The minimum amount of memory to reserve in bios_extmem for the heap.
+ */
+#define HEAP_MIN (64 * 1024 * 1024)
+
+/*
+ * Products in this list need quirks to detect
+ * memory correctly. You need both maker and product as
+ * reported by smbios.
+ */
+/* e820 might not return useful extended memory */
+#define BQ_DISTRUST_E820_EXTMEM 0x1
+struct bios_getmem_quirks {
+ const char *bios_vendor;
+ const char *maker;
+ const char *product;
+ int quirk;
+};
+
+static struct bios_getmem_quirks quirks[] = {
+ {"coreboot", "Acer", "Peppy", BQ_DISTRUST_E820_EXTMEM},
+ {NULL, NULL, NULL, 0}
+};
+
+static int
+bios_getquirks(void)
+{
+ int i;
+
+ for (i = 0; quirks[i].quirk != 0; ++i) {
+ if (smbios_match(quirks[i].bios_vendor, quirks[i].maker,
+ quirks[i].product))
+ return (quirks[i].quirk);
+ }
+
+ return (0);
+}
+
+void
+bios_getmem(void)
+{
+ uint64_t size;
+
+ /* Parse system memory map */
+ v86.ebx = 0;
+ do {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x15; /* int 0x15 function 0xe820 */
+ v86.eax = 0xe820;
+ v86.ecx = sizeof(struct bios_smap_xattr);
+ v86.edx = SMAP_SIG;
+ v86.es = VTOPSEG(&smap);
+ v86.edi = VTOPOFF(&smap);
+ v86int();
+ if ((V86_CY(v86.efl)) || (v86.eax != SMAP_SIG))
+ break;
+ /* look for a low-memory segment that's large enough */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
+ (smap.length >= (512 * 1024))) {
+ bios_basemem = smap.length;
+ b_bios_probed |= B_BASEMEM_E820;
+ }
+
+ /* look for the first segment in 'extended' memory */
+ if ((smap.type == SMAP_TYPE_MEMORY) &&
+ (smap.base == 0x100000) &&
+ !(bios_getquirks() & BQ_DISTRUST_E820_EXTMEM)) {
+ bios_extmem = smap.length;
+ b_bios_probed |= B_EXTMEM_E820;
+ }
+
+ /*
+ * Look for the highest segment in 'extended' memory beyond
+ * 1MB but below 4GB.
+ */
+ if ((smap.type == SMAP_TYPE_MEMORY) &&
+ (smap.base > 0x100000) &&
+ (smap.base < 0x100000000ull)) {
+ size = smap.length;
+
+ /*
+ * If this segment crosses the 4GB boundary,
+ * truncate it.
+ */
+ if (smap.base + size > 0x100000000ull)
+ size = 0x100000000ull - smap.base;
+
+ /*
+ * To make maximum space for the kernel and the modules,
+ * set heap to use highest HEAP_MIN bytes below 4GB.
+ */
+ if (high_heap_base < smap.base && size >= HEAP_MIN) {
+ high_heap_base = smap.base + size - HEAP_MIN;
+ high_heap_size = HEAP_MIN;
+ }
+ }
+ } while (v86.ebx != 0);
+
+ /* Fall back to the old compatibility function for base memory */
+ if (bios_basemem == 0) {
+ v86.ctl = 0;
+ v86.addr = 0x12; /* int 0x12 */
+ v86int();
+
+ bios_basemem = (v86.eax & 0xffff) * 1024;
+ b_bios_probed |= B_BASEMEM_12;
+ }
+
+ /*
+ * Fall back through several compatibility functions for extended
+ * memory.
+ */
+ if (bios_extmem == 0) {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x15; /* int 0x15 function 0xe801 */
+ v86.eax = 0xe801;
+ v86int();
+ if (!(V86_CY(v86.efl))) {
+ /*
+ * Clear high_heap; it may end up overlapping
+ * with the segment we're determining here.
+ * Let the default "steal stuff from top of
+ * bios_extmem" code below pick up on it.
+ */
+ high_heap_size = 0;
+ high_heap_base = 0;
+
+ /*
+ * %cx is the number of 1KiB blocks between 1..16MiB.
+ * It can only be up to 0x3c00; if it's smaller then
+ * there's a PC AT memory hole so we can't treat
+ * it as contiguous.
+ */
+ bios_extmem = (v86.ecx & 0xffff) * 1024;
+ if (bios_extmem == (1024 * 0x3c00))
+ bios_extmem += (v86.edx & 0xffff) * 64 * 1024;
+
+ /* truncate bios_extmem */
+ if (bios_extmem > 0x3ff00000)
+ bios_extmem = 0x3ff00000;
+
+ b_bios_probed |= B_EXTMEM_E801;
+ }
+ }
+ if (bios_extmem == 0) {
+ v86.ctl = 0;
+ v86.addr = 0x15; /* int 0x15 function 0x88 */
+ v86.eax = 0x8800;
+ v86int();
+ bios_extmem = (v86.eax & 0xffff) * 1024;
+ b_bios_probed |= B_EXTMEM_8800;
+ }
+
+ /* Set memtop to actual top of memory */
+ if (high_heap_size != 0) {
+ memtop = memtop_copyin = high_heap_base;
+ } else {
+ memtop = memtop_copyin = 0x100000 + bios_extmem;
+ }
+
+ /*
+ * If we have extended memory and did not find a suitable heap
+ * region in the SMAP, use the last HEAP_MIN of 'extended' memory as a
+ * high heap candidate.
+ */
+ if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
+ high_heap_size = HEAP_MIN;
+ high_heap_base = memtop - HEAP_MIN;
+ memtop = memtop_copyin = high_heap_base;
+ }
+}
+
+static int
+command_biosmem(int argc, char *argv[])
+{
+ int bq = bios_getquirks();
+
+ printf("bios_basemem: 0x%llx\n", (unsigned long long)bios_basemem);
+ printf("bios_extmem: 0x%llx\n", (unsigned long long)bios_extmem);
+ printf("memtop: 0x%llx\n", (unsigned long long)memtop);
+ printf("high_heap_base: 0x%llx\n", (unsigned long long)high_heap_base);
+ printf("high_heap_size: 0x%llx\n", (unsigned long long)high_heap_size);
+ printf("bios_quirks: 0x%02x", bq);
+ if (bq & BQ_DISTRUST_E820_EXTMEM)
+ printf(" BQ_DISTRUST_E820_EXTMEM");
+ printf("\n");
+ printf("b_bios_probed: 0x%02x", (int)b_bios_probed);
+ if (b_bios_probed & B_BASEMEM_E820)
+ printf(" B_BASEMEM_E820");
+ if (b_bios_probed & B_BASEMEM_12)
+ printf(" B_BASEMEM_12");
+ if (b_bios_probed & B_EXTMEM_E820)
+ printf(" B_EXTMEM_E820");
+ if (b_bios_probed & B_EXTMEM_E801)
+ printf(" B_EXTMEM_E801");
+ if (b_bios_probed & B_EXTMEM_8800)
+ printf(" B_EXTMEM_8800");
+ printf("\n");
+
+ return (CMD_OK);
+}
+
+COMMAND_SET(biosmem, "biosmem", "show BIOS memory setup", command_biosmem);
diff --git a/stand/i386/libi386/biospci.c b/stand/i386/libi386/biospci.c
new file mode 100644
index 0000000..098e30c
--- /dev/null
+++ b/stand/i386/libi386/biospci.c
@@ -0,0 +1,588 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2016 Netflix, Inc
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * PnP enumerator using the PCI BIOS.
+ */
+
+#include <stand.h>
+#include <machine/stdarg.h>
+#include <bootstrap.h>
+#include <isapnp.h>
+#include <btxv86.h>
+#include "libi386.h"
+#ifdef BOOT_FORTH
+#include "ficl.h"
+#endif
+
+/*
+ * Stupid PCI BIOS interface doesn't let you simply enumerate everything
+ * that's there, instead you have to ask it if it has something.
+ *
+ * So we have to scan by class code, subclass code and sometimes programming
+ * interface.
+ */
+
+struct pci_progif
+{
+ int pi_code;
+ const char *pi_name;
+};
+
+static struct pci_progif progif_null[] = {
+ {0x0, NULL},
+ {-1, NULL}
+};
+
+static struct pci_progif progif_display[] = {
+ {0x0, "VGA"},
+ {0x1, "8514"},
+ {-1, NULL}
+};
+
+static struct pci_progif progif_ide[] = {
+ {0x00, NULL},
+ {0x01, NULL},
+ {0x02, NULL},
+ {0x03, NULL},
+ {0x04, NULL},
+ {0x05, NULL},
+ {0x06, NULL},
+ {0x07, NULL},
+ {0x08, NULL},
+ {0x09, NULL},
+ {0x0a, NULL},
+ {0x0b, NULL},
+ {0x0c, NULL},
+ {0x0d, NULL},
+ {0x0e, NULL},
+ {0x0f, NULL},
+ {0x80, NULL},
+ {0x81, NULL},
+ {0x82, NULL},
+ {0x83, NULL},
+ {0x84, NULL},
+ {0x85, NULL},
+ {0x86, NULL},
+ {0x87, NULL},
+ {0x88, NULL},
+ {0x89, NULL},
+ {0x8a, NULL},
+ {0x8b, NULL},
+ {0x8c, NULL},
+ {0x8d, NULL},
+ {0x8e, NULL},
+ {0x8f, NULL},
+ {-1, NULL}
+};
+
+static struct pci_progif progif_serial[] = {
+ {0x0, "8250"},
+ {0x1, "16450"},
+ {0x2, "16550"},
+ {-1, NULL}
+};
+
+static struct pci_progif progif_parallel[] = {
+ {0x0, "Standard"},
+ {0x1, "Bidirectional"},
+ {0x2, "ECP"},
+ {-1, NULL}
+};
+
+static struct pci_progif progif_firewire[] = {
+ {0x10, "OHCI"},
+ {-1, NULL}
+};
+
+struct pci_subclass
+{
+ int ps_subclass;
+ const char *ps_name;
+ struct pci_progif *ps_progif; /* if set, use for programming interface value(s) */
+};
+
+static struct pci_subclass subclass_old[] = {
+ {0x0, "Old non-VGA", progif_null},
+ {0x1, "Old VGA", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_subclass subclass_mass[] = {
+ {0x0, "SCSI", progif_null},
+ {0x1, "IDE", progif_ide},
+ {0x2, "Floppy disk", progif_null},
+ {0x3, "IPI", progif_null},
+ {0x4, "RAID", progif_null},
+ {0x80, "mass storage", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_subclass subclass_net[] = {
+ {0x0, "Ethernet", progif_null},
+ {0x1, "Token ring", progif_null},
+ {0x2, "FDDI", progif_null},
+ {0x3, "ATM", progif_null},
+ {0x80, "network", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_subclass subclass_display[] = {
+ {0x0, NULL, progif_display},
+ {0x1, "XGA", progif_null},
+ {0x80, "other", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_subclass subclass_comms[] = {
+ {0x0, "serial", progif_serial},
+ {0x1, "parallel", progif_parallel},
+ {0x80, "communications", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_subclass subclass_serial[] = {
+ {0x0, "FireWire", progif_firewire},
+ {0x1, "ACCESS.bus", progif_null},
+ {0x2, "SSA", progif_null},
+ {0x3, "USB", progif_null},
+ {0x4, "Fibrechannel", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_class
+{
+ int pc_class;
+ const char *pc_name;
+ struct pci_subclass *pc_subclass;
+} pci_classes[] = {
+ {0x0, "device", subclass_old},
+ {0x1, "controller", subclass_mass},
+ {0x2, "controller", subclass_net},
+ {0x3, "display", subclass_display},
+ {0x7, "controller", subclass_comms},
+ {0xc, "controller", subclass_serial},
+ {-1, NULL, NULL}
+};
+
+static void biospci_enumerate(void);
+static void biospci_addinfo(int devid, struct pci_class *pc, struct pci_subclass *psc, struct pci_progif *ppi);
+
+struct pnphandler biospcihandler =
+{
+ "PCI BIOS",
+ biospci_enumerate
+};
+static int biospci_version;
+
+#define PCI_BIOS_PRESENT 0xb101
+#define FIND_PCI_DEVICE 0xb102
+#define FIND_PCI_CLASS_CODE 0xb103
+#define GENERATE_SPECIAL_CYCLE 0xb106
+#define READ_CONFIG_BYTE 0xb108
+#define READ_CONFIG_WORD 0xb109
+#define READ_CONFIG_DWORD 0xb10a
+#define WRITE_CONFIG_BYTE 0xb10b
+#define WRITE_CONFIG_WORD 0xb10c
+#define WRITE_CONFIG_DWORD 0xb10d
+#define GET_IRQ_ROUTING_OPTIONS 0xb10e
+#define SET_PCI_IRQ 0xb10f
+
+#define PCI_INT 0x1a
+
+#define PCI_SIGNATURE 0x20494350 /* AKA "PCI " */
+
+void
+biospci_detect(void)
+{
+ uint16_t version, hwcap, maxbus;
+ char buf[24];
+
+ /* Find the PCI BIOS */
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = PCI_BIOS_PRESENT;
+ v86.edi = 0x0;
+ v86int();
+
+ /* Check for OK response */
+ if (V86_CY(v86.efl) || ((v86.eax & 0xff00) != 0) ||
+ (v86.edx != PCI_SIGNATURE))
+ return;
+
+ version = v86.ebx & 0xffff;
+ hwcap = v86.eax & 0xff;
+ maxbus = v86.ecx & 0xff;
+#if 0
+ printf("PCI BIOS %d.%d%s%s maxbus %d\n",
+ bcd2bin((version >> 8) & 0xf), bcd2bin(version & 0xf),
+ (hwcap & 1) ? " config1" : "", (hwcap & 2) ? " config2" : "",
+ maxbus);
+#endif
+ sprintf(buf, "%d", bcd2bin((version >> 8) & 0xf));
+ setenv("pcibios.major", buf, 1);
+ sprintf(buf, "%d", bcd2bin(version & 0xf));
+ setenv("pcibios.minor", buf, 1);
+ sprintf(buf, "%d", !!(hwcap & 1));
+ setenv("pcibios.config1", buf, 1);
+ sprintf(buf, "%d", !!(hwcap & 2));
+ setenv("pcibios.config2", buf, 1);
+ sprintf(buf, "%d", maxbus);
+ setenv("pcibios.maxbus", buf, 1);
+ biospci_version = bcd2bin((version >> 8) & 0xf) * 10 + bcd2bin(version & 0xf);
+}
+
+static void
+biospci_enumerate(void)
+{
+ int device_index, err;
+ uint32_t locator, devid;
+ struct pci_class *pc;
+ struct pci_subclass *psc;
+ struct pci_progif *ppi;
+
+ /* Iterate over known classes */
+ for (pc = pci_classes; pc->pc_class >= 0; pc++) {
+ /* Iterate over subclasses */
+ for (psc = pc->pc_subclass; psc->ps_subclass >= 0; psc++) {
+ /* Iterate over programming interfaces */
+ for (ppi = psc->ps_progif; ppi->pi_code >= 0; ppi++) {
+
+ /* Scan for matches */
+ for (device_index = 0; ; device_index++) {
+ /* Look for a match */
+ err = biospci_find_devclass((pc->pc_class << 16)
+ + (psc->ps_subclass << 8) + ppi->pi_code,
+ device_index, &locator);
+ if (err != 0)
+ break;
+
+ /* Read the device identifier from the nominated device */
+ err = biospci_read_config(locator, 0, 2, &devid);
+ if (err != 0)
+ break;
+
+ /* We have the device ID, create a PnP object and save everything */
+ biospci_addinfo(devid, pc, psc, ppi);
+ }
+ }
+ }
+ }
+}
+
+static void
+biospci_addinfo(int devid, struct pci_class *pc, struct pci_subclass *psc, struct pci_progif *ppi)
+{
+ struct pnpinfo *pi;
+ char desc[80];
+
+
+ /* build the description */
+ desc[0] = 0;
+ if (ppi->pi_name != NULL) {
+ strcat(desc, ppi->pi_name);
+ strcat(desc, " ");
+ }
+ if (psc->ps_name != NULL) {
+ strcat(desc, psc->ps_name);
+ strcat(desc, " ");
+ }
+ if (pc->pc_name != NULL)
+ strcat(desc, pc->pc_name);
+
+ pi = pnp_allocinfo();
+ pi->pi_desc = strdup(desc);
+ sprintf(desc,"0x%08x", devid);
+ pnp_addident(pi, desc);
+ pnp_addinfo(pi);
+}
+
+int
+biospci_find_devclass(uint32_t class, int index, uint32_t *locator)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = FIND_PCI_CLASS_CODE;
+ v86.ecx = class;
+ v86.esi = index;
+ v86int();
+
+ /* error */
+ if (V86_CY(v86.efl) || (v86.eax & 0xff00))
+ return (-1);
+
+ *locator = v86.ebx;
+ return (0);
+}
+
+static int
+biospci_find_device(uint32_t devid, int index, uint32_t *locator)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = FIND_PCI_DEVICE;
+ v86.edx = devid & 0xffff; /* EDX - Vendor ID */
+ v86.ecx = (devid >> 16) & 0xffff; /* ECX - Device ID */
+ v86.esi = index;
+ v86int();
+
+ /* error */
+ if (V86_CY(v86.efl) || (v86.eax & 0xff00))
+ return (-1);
+
+ *locator = v86.ebx;
+ return (0);
+}
+/*
+ * Configuration space access methods.
+ * width = 0(byte), 1(word) or 2(dword).
+ */
+int
+biospci_write_config(uint32_t locator, int offset, int width, uint32_t val)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = WRITE_CONFIG_BYTE + width;
+ v86.ebx = locator;
+ v86.edi = offset;
+ v86.ecx = val;
+ v86int();
+
+ /* error */
+ if (V86_CY(v86.efl) || (v86.eax & 0xff00))
+ return (-1);
+
+ return(0);
+}
+
+int
+biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = READ_CONFIG_BYTE + width;
+ v86.ebx = locator;
+ v86.edi = offset;
+ v86int();
+
+ /* error */
+ if (V86_CY(v86.efl) || (v86.eax & 0xff00))
+ return (-1);
+
+ *val = v86.ecx;
+ return (0);
+}
+
+uint32_t
+biospci_locator(int8_t bus, uint8_t device, uint8_t function)
+{
+
+ return ((bus << 8) | ((device & 0x1f) << 3) | (function & 0x7));
+}
+
+/*
+ * Counts the number of instances of devid we have in the system, as least as
+ * far as the PCI BIOS is able to tell.
+ */
+static int
+biospci_count_device_type(uint32_t devid)
+{
+ int i;
+
+ for (i = 0; 1; i++) {
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = FIND_PCI_DEVICE;
+ v86.edx = devid & 0xffff; /* EDX - Vendor ID */
+ v86.ecx = (devid >> 16) & 0xffff; /* ECX - Device ID */
+ v86.esi = i;
+ v86int();
+ if (V86_CY(v86.efl) || (v86.eax & 0xff00))
+ break;
+
+ }
+ return i;
+}
+
+#ifdef BOOT_FORTH
+/*
+ * pcibios-device-count (devid -- count)
+ *
+ * Returns the PCI BIOS' count of how many devices matching devid are in the system.
+ * devid is the 32-bit vendor + device.
+ */
+static void
+ficlPciBiosCountDevices(FICL_VM *pVM)
+{
+ uint32_t devid;
+ int i;
+
+ devid = stackPopINT(pVM->pStack);
+
+ i = biospci_count_device_type(devid);
+
+ stackPushINT(pVM->pStack, i);
+}
+
+/*
+ * pcibios-write-config (locator offset width value -- )
+ *
+ * Writes the specified config register.
+ * Locator is bus << 8 | device << 3 | fuction
+ * offset is the pci config register
+ * width is 0 for byte, 1 for word, 2 for dword
+ * value is the value to write
+ */
+static void
+ficlPciBiosWriteConfig(FICL_VM *pVM)
+{
+ uint32_t value, width, offset, locator;
+
+ value = stackPopINT(pVM->pStack);
+ width = stackPopINT(pVM->pStack);
+ offset = stackPopINT(pVM->pStack);
+ locator = stackPopINT(pVM->pStack);
+
+ biospci_write_config(locator, offset, width, value);
+}
+
+/*
+ * pcibios-read-config (locator offset width -- value)
+ *
+ * Reads the specified config register.
+ * Locator is bus << 8 | device << 3 | fuction
+ * offset is the pci config register
+ * width is 0 for byte, 1 for word, 2 for dword
+ * value is the value to read from the register
+ */
+static void
+ficlPciBiosReadConfig(FICL_VM *pVM)
+{
+ uint32_t value, width, offset, locator;
+
+ width = stackPopINT(pVM->pStack);
+ offset = stackPopINT(pVM->pStack);
+ locator = stackPopINT(pVM->pStack);
+
+ biospci_read_config(locator, offset, width, &value);
+
+ stackPushINT(pVM->pStack, value);
+}
+
+/*
+ * pcibios-find-devclass (class index -- locator)
+ *
+ * Finds the index'th instance of class in the pci tree.
+ * must be an exact match.
+ * class is the class to search for.
+ * index 0..N (set to 0, increment until error)
+ *
+ * Locator is bus << 8 | device << 3 | fuction (or -1 on error)
+ */
+static void
+ficlPciBiosFindDevclass(FICL_VM *pVM)
+{
+ uint32_t index, class, locator;
+
+ index = stackPopINT(pVM->pStack);
+ class = stackPopINT(pVM->pStack);
+
+ if (biospci_find_devclass(class, index, &locator))
+ locator = 0xffffffff;
+
+ stackPushINT(pVM->pStack, locator);
+}
+
+/*
+ * pcibios-find-device(devid index -- locator)
+ *
+ * Finds the index'th instance of devid in the pci tree.
+ * must be an exact match.
+ * class is the class to search for.
+ * index 0..N (set to 0, increment until error)
+ *
+ * Locator is bus << 8 | device << 3 | fuction (or -1 on error)
+ */
+static void
+ficlPciBiosFindDevice(FICL_VM *pVM)
+{
+ uint32_t index, devid, locator;
+
+ index = stackPopINT(pVM->pStack);
+ devid = stackPopINT(pVM->pStack);
+
+ if (biospci_find_device(devid, index, &locator))
+ locator = 0xffffffff;
+
+ stackPushINT(pVM->pStack, locator);
+}
+
+/*
+ * pcibios-find-device(bus device function -- locator)
+ *
+ * converts bus, device, function to locator.
+ *
+ * Locator is bus << 8 | device << 3 | fuction
+ */
+static void
+ficlPciBiosLocator(FICL_VM *pVM)
+{
+ uint32_t bus, device, function, locator;
+
+ function = stackPopINT(pVM->pStack);
+ device = stackPopINT(pVM->pStack);
+ bus = stackPopINT(pVM->pStack);
+
+ locator = biospci_locator(bus, device, function);
+
+ stackPushINT(pVM->pStack, locator);
+}
+
+/*
+ * Glue function to add the appropriate forth words to access pci bios
+ * functionality.
+ */
+static void ficlCompilePciBios(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+ dictAppendWord(dp, "pcibios-device-count", ficlPciBiosCountDevices, FW_DEFAULT);
+ dictAppendWord(dp, "pcibios-read-config", ficlPciBiosReadConfig, FW_DEFAULT);
+ dictAppendWord(dp, "pcibios-write-config", ficlPciBiosWriteConfig, FW_DEFAULT);
+ dictAppendWord(dp, "pcibios-find-devclass", ficlPciBiosFindDevclass, FW_DEFAULT);
+ dictAppendWord(dp, "pcibios-find-device", ficlPciBiosFindDevice, FW_DEFAULT);
+ dictAppendWord(dp, "pcibios-locator", ficlPciBiosLocator, FW_DEFAULT);
+
+ ficlSetEnv(pSys, "pcibios-version", biospci_version);
+}
+
+FICL_COMPILE_SET(ficlCompilePciBios);
+#endif
diff --git a/stand/i386/libi386/biospnp.c b/stand/i386/libi386/biospnp.c
new file mode 100644
index 0000000..30e55fc
--- /dev/null
+++ b/stand/i386/libi386/biospnp.c
@@ -0,0 +1,292 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * PnP BIOS enumerator.
+ */
+
+#include <stand.h>
+#include <machine/stdarg.h>
+#include <bootstrap.h>
+#include <isapnp.h>
+#include <btxv86.h>
+
+
+static int biospnp_init(void);
+static void biospnp_enumerate(void);
+
+struct pnphandler biospnphandler =
+{
+ "PnP BIOS",
+ biospnp_enumerate
+};
+
+struct pnp_ICstructure
+{
+ u_int8_t pnp_signature[4];
+ u_int8_t pnp_version;
+ u_int8_t pnp_length;
+ u_int16_t pnp_BIOScontrol;
+ u_int8_t pnp_checksum;
+ u_int32_t pnp_eventflag;
+ u_int16_t pnp_rmip;
+ u_int16_t pnp_rmcs;
+ u_int16_t pnp_pmip;
+ u_int32_t pnp_pmcs;
+ u_int8_t pnp_OEMdev[4];
+ u_int16_t pnp_rmds;
+ u_int32_t pnp_pmds;
+} __packed;
+
+struct pnp_devNode
+{
+ u_int16_t dn_size;
+ u_int8_t dn_handle;
+ u_int8_t dn_id[4];
+ u_int8_t dn_type[3];
+ u_int16_t dn_attrib;
+ u_int8_t dn_data[1];
+} __packed;
+
+struct pnp_isaConfiguration
+{
+ u_int8_t ic_revision;
+ u_int8_t ic_nCSN;
+ u_int16_t ic_rdport;
+ u_int16_t ic_reserved;
+} __packed;
+
+static struct pnp_ICstructure *pnp_Icheck = NULL;
+static u_int16_t pnp_NumNodes;
+static u_int16_t pnp_NodeSize;
+
+static void biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn);
+static int biospnp_call(int func, const char *fmt, ...);
+
+#define vsegofs(vptr) (((u_int32_t)VTOPSEG(vptr) << 16) + VTOPOFF(vptr))
+
+typedef void v86bios_t(u_int32_t, u_int32_t, u_int32_t, u_int32_t);
+v86bios_t *v86bios = (v86bios_t *)v86int;
+
+#define biospnp_f00(NumNodes, NodeSize) biospnp_call(0x00, "ll", NumNodes, NodeSize)
+#define biospnp_f01(Node, devNodeBuffer, Control) biospnp_call(0x01, "llw", Node, devNodeBuffer, Control)
+#define biospnp_f40(Configuration) biospnp_call(0x40, "l", Configuration)
+
+/* PnP BIOS return codes */
+#define PNP_SUCCESS 0x00
+#define PNP_FUNCTION_NOT_SUPPORTED 0x80
+
+/*
+ * Initialisation: locate the PnP BIOS, test that we can call it.
+ * Returns nonzero if the PnP BIOS is not usable on this system.
+ */
+static int
+biospnp_init(void)
+{
+ struct pnp_isaConfiguration icfg;
+ char *sigptr;
+ int result;
+
+ /* Search for the $PnP signature */
+ pnp_Icheck = NULL;
+ for (sigptr = PTOV(0xf0000); sigptr < PTOV(0xfffff); sigptr += 16)
+ if (!bcmp(sigptr, "$PnP", 4)) {
+ pnp_Icheck = (struct pnp_ICstructure *)sigptr;
+ break;
+ }
+
+ /* No signature, no BIOS */
+ if (pnp_Icheck == NULL)
+ return(1);
+
+ /*
+ * Fetch the system table parameters as a test of the BIOS
+ */
+ result = biospnp_f00(vsegofs(&pnp_NumNodes), vsegofs(&pnp_NodeSize));
+ if (result != PNP_SUCCESS) {
+ return(1);
+ }
+
+ /*
+ * Look for the PnP ISA configuration table
+ */
+ result = biospnp_f40(vsegofs(&icfg));
+ switch (result) {
+ case PNP_SUCCESS:
+ /* If the BIOS found some PnP devices, take its hint for the read port */
+ if ((icfg.ic_revision == 1) && (icfg.ic_nCSN > 0))
+ isapnp_readport = icfg.ic_rdport;
+ break;
+ case PNP_FUNCTION_NOT_SUPPORTED:
+ /* The BIOS says there is no ISA bus (should we trust that this works?) */
+ printf("PnP BIOS claims no ISA bus\n");
+ isapnp_readport = -1;
+ break;
+ }
+ return(0);
+}
+
+static void
+biospnp_enumerate(void)
+{
+ u_int8_t Node;
+ struct pnp_devNode *devNodeBuffer;
+ int result;
+ struct pnpinfo *pi;
+ int count;
+
+ /* Init/check state */
+ if (biospnp_init())
+ return;
+
+ devNodeBuffer = (struct pnp_devNode *)alloca(pnp_NodeSize);
+ Node = 0;
+ count = 1000;
+ while((Node != 0xff) && (count-- > 0)) {
+ result = biospnp_f01(vsegofs(&Node), vsegofs(devNodeBuffer), 0x1);
+ if (result != PNP_SUCCESS) {
+ printf("PnP BIOS node %d: error 0x%x\n", Node, result);
+ } else {
+ pi = pnp_allocinfo();
+ pnp_addident(pi, pnp_eisaformat(devNodeBuffer->dn_id));
+ biospnp_scanresdata(pi, devNodeBuffer);
+ pnp_addinfo(pi);
+ }
+ }
+}
+
+/*
+ * Scan the resource data in the node's data area for compatible device IDs
+ * and descriptions.
+ */
+static void
+biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn)
+{
+ u_int tag, i, rlen, dlen;
+ u_int8_t *p;
+ char *str;
+
+ p = dn->dn_data; /* point to resource data */
+ dlen = dn->dn_size - (p - (u_int8_t *)dn); /* length of resource data */
+
+ for (i = 0; i < dlen; i+= rlen) {
+ tag = p[i];
+ i++;
+ if (PNP_RES_TYPE(tag) == 0) {
+ rlen = PNP_SRES_LEN(tag);
+ /* small resource */
+ switch (PNP_SRES_NUM(tag)) {
+
+ case COMP_DEVICE_ID:
+ /* got a compatible device ID */
+ pnp_addident(pi, pnp_eisaformat(p + i));
+ break;
+
+ case END_TAG:
+ return;
+ }
+ } else {
+ /* large resource */
+ rlen = *(u_int16_t *)(p + i);
+ i += sizeof(u_int16_t);
+
+ switch(PNP_LRES_NUM(tag)) {
+
+ case ID_STRING_ANSI:
+ str = malloc(rlen + 1);
+ bcopy(p + i, str, rlen);
+ str[rlen] = 0;
+ if (pi->pi_desc == NULL) {
+ pi->pi_desc = str;
+ } else {
+ free(str);
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Make a 16-bit realmode PnP BIOS call.
+ *
+ * The first argument passed is the function number, the last is the
+ * BIOS data segment selector. Intermediate arguments may be 16 or
+ * 32 bytes in length, and are described by the format string.
+ *
+ * Arguments to the BIOS functions must be packed on the stack, hence
+ * this evil.
+ */
+static int
+biospnp_call(int func, const char *fmt, ...)
+{
+ va_list ap;
+ const char *p;
+ u_int8_t *argp;
+ u_int32_t args[4];
+ u_int32_t i;
+
+ /* function number first */
+ argp = (u_int8_t *)args;
+ *(u_int16_t *)argp = func;
+ argp += sizeof(u_int16_t);
+
+ /* take args according to format */
+ va_start(ap, fmt);
+ for (p = fmt; *p != 0; p++) {
+ switch(*p) {
+
+ case 'w':
+ i = va_arg(ap, u_int);
+ *(u_int16_t *)argp = i;
+ argp += sizeof(u_int16_t);
+ break;
+
+ case 'l':
+ i = va_arg(ap, u_int32_t);
+ *(u_int32_t *)argp = i;
+ argp += sizeof(u_int32_t);
+ break;
+ }
+ }
+ va_end(ap);
+
+ /* BIOS segment last */
+ *(u_int16_t *)argp = pnp_Icheck->pnp_rmds;
+ argp += sizeof(u_int16_t);
+
+ /* prepare for call */
+ v86.ctl = V86_ADDR | V86_CALLF;
+ v86.addr = ((u_int32_t)pnp_Icheck->pnp_rmcs << 16) + pnp_Icheck->pnp_rmip;
+
+ /* call with packed stack and return */
+ v86bios(args[0], args[1], args[2], args[3]);
+ return(v86.eax & 0xffff);
+}
diff --git a/stand/i386/libi386/biossmap.c b/stand/i386/libi386/biossmap.c
new file mode 100644
index 0000000..e95ea64
--- /dev/null
+++ b/stand/i386/libi386/biossmap.c
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Obtain memory configuration information from the BIOS
+ */
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/queue.h>
+#include <sys/stddef.h>
+#include <machine/metadata.h>
+#include <machine/pc/bios.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+struct smap_buf {
+ struct bios_smap smap;
+ uint32_t xattr; /* Extended attribute from ACPI 3.0 */
+ STAILQ_ENTRY(smap_buf) bufs;
+};
+
+#define SMAP_BUFSIZE offsetof(struct smap_buf, bufs)
+
+static struct bios_smap *smapbase;
+static uint32_t *smapattr;
+static u_int smaplen;
+
+void
+bios_getsmap(void)
+{
+ struct smap_buf buf;
+ STAILQ_HEAD(smap_head, smap_buf) head =
+ STAILQ_HEAD_INITIALIZER(head);
+ struct smap_buf *cur, *next;
+ u_int n, x;
+
+ STAILQ_INIT(&head);
+ n = 0;
+ x = 0;
+ v86.ebx = 0;
+ do {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x15;
+ v86.eax = 0xe820; /* int 0x15 function 0xe820 */
+ v86.ecx = SMAP_BUFSIZE;
+ v86.edx = SMAP_SIG;
+ v86.es = VTOPSEG(&buf);
+ v86.edi = VTOPOFF(&buf);
+ v86int();
+ if (V86_CY(v86.efl) || v86.eax != SMAP_SIG ||
+ v86.ecx < sizeof(buf.smap) || v86.ecx > SMAP_BUFSIZE)
+ break;
+
+ next = malloc(sizeof(*next));
+ if (next == NULL)
+ break;
+ next->smap = buf.smap;
+ if (v86.ecx == SMAP_BUFSIZE) {
+ next->xattr = buf.xattr;
+ x++;
+ }
+ STAILQ_INSERT_TAIL(&head, next, bufs);
+ n++;
+ } while (v86.ebx != 0);
+ smaplen = n;
+
+ if (smaplen > 0) {
+ smapbase = malloc(smaplen * sizeof(*smapbase));
+ if (smapbase != NULL) {
+ n = 0;
+ STAILQ_FOREACH(cur, &head, bufs)
+ smapbase[n++] = cur->smap;
+ }
+ if (smaplen == x) {
+ smapattr = malloc(smaplen * sizeof(*smapattr));
+ if (smapattr != NULL) {
+ n = 0;
+ STAILQ_FOREACH(cur, &head, bufs)
+ smapattr[n++] = cur->xattr &
+ SMAP_XATTR_MASK;
+ }
+ } else
+ smapattr = NULL;
+ cur = STAILQ_FIRST(&head);
+ while (cur != NULL) {
+ next = STAILQ_NEXT(cur, bufs);
+ free(cur);
+ cur = next;
+ }
+ }
+}
+
+void
+bios_addsmapdata(struct preloaded_file *kfp)
+{
+ size_t size;
+
+ if (smapbase == NULL || smaplen == 0)
+ return;
+ size = smaplen * sizeof(*smapbase);
+ file_addmetadata(kfp, MODINFOMD_SMAP, size, smapbase);
+ if (smapattr != NULL) {
+ size = smaplen * sizeof(*smapattr);
+ file_addmetadata(kfp, MODINFOMD_SMAP_XATTR, size, smapattr);
+ }
+}
+
+COMMAND_SET(smap, "smap", "show BIOS SMAP", command_smap);
+
+static int
+command_smap(int argc, char *argv[])
+{
+ u_int i;
+
+ if (smapbase == NULL || smaplen == 0)
+ return (CMD_ERROR);
+ if (smapattr != NULL)
+ for (i = 0; i < smaplen; i++)
+ printf("SMAP type=%02x base=%016llx len=%016llx attr=%02x\n",
+ (unsigned int)smapbase[i].type,
+ (unsigned long long)smapbase[i].base,
+ (unsigned long long)smapbase[i].length,
+ (unsigned int)smapattr[i]);
+ else
+ for (i = 0; i < smaplen; i++)
+ printf("SMAP type=%02x base=%016llx len=%016llx\n",
+ (unsigned int)smapbase[i].type,
+ (unsigned long long)smapbase[i].base,
+ (unsigned long long)smapbase[i].length);
+ return (CMD_OK);
+}
diff --git a/stand/i386/libi386/bootinfo.c b/stand/i386/libi386/bootinfo.c
new file mode 100644
index 0000000..c33cec7
--- /dev/null
+++ b/stand/i386/libi386/bootinfo.c
@@ -0,0 +1,170 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/boot.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+int
+bi_getboothowto(char *kargs)
+{
+ char *cp;
+ char *curpos, *next, *string;
+ int howto;
+ int active;
+ int i;
+ int vidconsole;
+
+ /* Parse kargs */
+ howto = 0;
+ if (kargs != NULL) {
+ cp = kargs;
+ active = 0;
+ while (*cp != 0) {
+ if (!active && (*cp == '-')) {
+ active = 1;
+ } else if (active)
+ switch (*cp) {
+ case 'a':
+ howto |= RB_ASKNAME;
+ break;
+ case 'C':
+ howto |= RB_CDROM;
+ break;
+ case 'd':
+ howto |= RB_KDB;
+ break;
+ case 'D':
+ howto |= RB_MULTIPLE;
+ break;
+ case 'm':
+ howto |= RB_MUTE;
+ break;
+ case 'g':
+ howto |= RB_GDB;
+ break;
+ case 'h':
+ howto |= RB_SERIAL;
+ break;
+ case 'p':
+ howto |= RB_PAUSE;
+ break;
+ case 'r':
+ howto |= RB_DFLTROOT;
+ break;
+ case 's':
+ howto |= RB_SINGLE;
+ break;
+ case 'v':
+ howto |= RB_VERBOSE;
+ break;
+ default:
+ active = 0;
+ break;
+ }
+ cp++;
+ }
+ }
+ /* get equivalents from the environment */
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+
+ /* Enable selected consoles */
+ string = next = strdup(getenv("console"));
+ vidconsole = 0;
+ while (next != NULL) {
+ curpos = strsep(&next, " ,");
+ if (*curpos == '\0')
+ continue;
+ if (!strcmp(curpos, "vidconsole"))
+ vidconsole = 1;
+ else if (!strcmp(curpos, "comconsole"))
+ howto |= RB_SERIAL;
+ else if (!strcmp(curpos, "nullconsole"))
+ howto |= RB_MUTE;
+ }
+
+ if (vidconsole && (howto & RB_SERIAL))
+ howto |= RB_MULTIPLE;
+
+ /*
+ * XXX: Note that until the kernel is ready to respect multiple consoles
+ * for the boot messages, the first named console is the primary console
+ */
+ if (!strcmp(string, "vidconsole"))
+ howto &= ~RB_SERIAL;
+
+ free(string);
+
+ return(howto);
+}
+
+void
+bi_setboothowto(int howto)
+{
+ int i;
+
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (howto & howto_names[i].mask)
+ setenv(howto_names[i].ev, "YES", 1);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+vm_offset_t
+bi_copyenv(vm_offset_t addr)
+{
+ struct env_var *ep;
+
+ /* traverse the environment */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ i386_copyin(ep->ev_name, addr, strlen(ep->ev_name));
+ addr += strlen(ep->ev_name);
+ i386_copyin("=", addr, 1);
+ addr++;
+ if (ep->ev_value != NULL) {
+ i386_copyin(ep->ev_value, addr, strlen(ep->ev_value));
+ addr += strlen(ep->ev_value);
+ }
+ i386_copyin("", addr, 1);
+ addr++;
+ }
+ i386_copyin("", addr, 1);
+ addr++;
+ return(addr);
+}
diff --git a/stand/i386/libi386/bootinfo32.c b/stand/i386/libi386/bootinfo32.c
new file mode 100644
index 0000000..494688f
--- /dev/null
+++ b/stand/i386/libi386/bootinfo32.c
@@ -0,0 +1,291 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <machine/bootinfo.h>
+#include <machine/metadata.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+
+static const size_t keybuf_size = sizeof(struct keybuf) +
+ (GELI_MAX_KEYS * sizeof(struct keybuf_ent));
+#endif
+
+static struct bootinfo bi;
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ i386_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c); \
+ if (c) \
+ i386_copyin(s, a, strlen(s) + 1); \
+ a += roundup(strlen(s) + 1, sizeof(u_long));\
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ i386_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(u_long)); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c); \
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ i386_copyin(mm->md_data, a, mm->md_size); \
+ a += roundup(mm->md_size, sizeof(u_long));\
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+static vm_offset_t
+bi_copymodules32(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ int c;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must come first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ MOD_ADDR(addr, fp->f_addr, c);
+ MOD_SIZE(addr, fp->f_size, c);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next)
+ if (!(md->md_type & MODINFOMD_NOCOPY))
+ MOD_METADATA(addr, md, c);
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Load the information expected by an i386 kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The 'bootinfo' struct is constructed, and copied into the kernel space.
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernendp)
+{
+ struct preloaded_file *xp, *kfp;
+ struct i386_devdesc *rootdev;
+ struct file_metadata *md;
+ vm_offset_t addr;
+ vm_offset_t kernend;
+ vm_offset_t envp;
+ vm_offset_t size;
+ vm_offset_t ssym, esym;
+ char *rootdevname;
+ int bootdevnr, i, howto;
+ char *kernelname;
+ const char *kernelpath;
+#ifdef LOADER_GELI_SUPPORT
+ char buf[keybuf_size];
+ struct keybuf *keybuf = (struct keybuf *)buf;
+#endif
+
+ howto = bi_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied device
+ * This should perhaps go to MI code and/or have $rootdev tested/set by
+ * MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ i386_getdev((void **)(&rootdev), rootdevname, NULL);
+ if (rootdev == NULL) { /* bad $rootdev/$currdev */
+ printf("can't determine root device\n");
+ return(EINVAL);
+ }
+
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(i386_fmtdev((void *)rootdev));
+
+ /* Do legacy rootdev guessing */
+
+ /* XXX - use a default bootdev of 0. Is this ok??? */
+ bootdevnr = 0;
+
+ switch(rootdev->d_type) {
+ case DEVT_CD:
+ /* Pass in BIOS device number. */
+ bi.bi_bios_dev = bc_unit2bios(rootdev->d_unit);
+ bootdevnr = bc_getdev(rootdev);
+ break;
+
+ case DEVT_DISK:
+ /* pass in the BIOS device number of the current disk */
+ bi.bi_bios_dev = bd_unit2bios(rootdev->d_unit);
+ bootdevnr = bd_getdev(rootdev);
+ break;
+
+ case DEVT_NET:
+ case DEVT_ZFS:
+ break;
+
+ default:
+ printf("WARNING - don't know how to boot from device type %d\n", rootdev->d_type);
+ }
+ if (bootdevnr == -1) {
+ printf("root device %s invalid\n", i386_fmtdev(rootdev));
+ return (EINVAL);
+ }
+ free(rootdev);
+
+ /* find the last module in the chain */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* copy our environment */
+ envp = addr;
+ addr = bi_copyenv(addr);
+
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf32 kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ kernend = 0; /* fill it in later */
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ bios_addsmapdata(kfp);
+#ifdef LOADER_GELI_SUPPORT
+ geli_fill_keybuf(keybuf);
+ file_addmetadata(kfp, MODINFOMD_KEYBUF, keybuf_size, buf);
+ bzero(buf, sizeof(buf));
+#endif
+
+ /* Figure out the size and location of the metadata */
+ *modulep = addr;
+ size = bi_copymodules32(0);
+ kernend = roundup(addr + size, PAGE_SIZE);
+ *kernendp = kernend;
+
+ /* patch MODINFOMD_KERNEND */
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ bcopy(&kernend, md->md_data, sizeof kernend);
+
+ /* copy module list and metadata */
+ (void)bi_copymodules32(addr);
+
+ ssym = esym = 0;
+ md = file_findmetadata(kfp, MODINFOMD_SSYM);
+ if (md != NULL)
+ ssym = *((vm_offset_t *)&(md->md_data));
+ md = file_findmetadata(kfp, MODINFOMD_ESYM);
+ if (md != NULL)
+ esym = *((vm_offset_t *)&(md->md_data));
+ if (ssym == 0 || esym == 0)
+ ssym = esym = 0; /* sanity */
+
+ /* legacy bootinfo structure */
+ kernelname = getenv("kernelname");
+ i386_getdev(NULL, kernelname, &kernelpath);
+ bi.bi_version = BOOTINFO_VERSION;
+ bi.bi_kernelname = 0; /* XXX char * -> kernel name */
+ bi.bi_nfs_diskless = 0; /* struct nfs_diskless * */
+ bi.bi_n_bios_used = 0; /* XXX would have to hook biosdisk driver for these */
+ for (i = 0; i < N_BIOS_GEOM; i++)
+ bi.bi_bios_geom[i] = bd_getbigeom(i);
+ bi.bi_size = sizeof(bi);
+ bi.bi_memsizes_valid = 1;
+ bi.bi_basemem = bios_basemem / 1024;
+ bi.bi_extmem = bios_extmem / 1024;
+ bi.bi_envp = envp;
+ bi.bi_modulep = *modulep;
+ bi.bi_kernend = kernend;
+ bi.bi_kernelname = VTOP(kernelpath);
+ bi.bi_symtab = ssym; /* XXX this is only the primary kernel symtab */
+ bi.bi_esymtab = esym;
+
+ /* legacy boot arguments */
+ *howtop = howto | RB_BOOTINFO;
+ *bootdevp = bootdevnr;
+ *bip = VTOP(&bi);
+
+ return(0);
+}
diff --git a/stand/i386/libi386/bootinfo64.c b/stand/i386/libi386/bootinfo64.c
new file mode 100644
index 0000000..02fcf72
--- /dev/null
+++ b/stand/i386/libi386/bootinfo64.c
@@ -0,0 +1,280 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <machine/bootinfo.h>
+#include <machine/cpufunc.h>
+#include <machine/metadata.h>
+#include <machine/psl.h>
+#include <machine/specialreg.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+
+static const size_t keybuf_size = sizeof(struct keybuf) +
+ (GELI_MAX_KEYS * sizeof(struct keybuf_ent));
+#endif
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ i386_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c); \
+ if (c) \
+ i386_copyin(s, a, strlen(s) + 1); \
+ a += roundup(strlen(s) + 1, sizeof(u_int64_t));\
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ i386_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(u_int64_t)); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c); \
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ i386_copyin(mm->md_data, a, mm->md_size); \
+ a += roundup(mm->md_size, sizeof(u_int64_t));\
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+static vm_offset_t
+bi_copymodules64(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ int c;
+ u_int64_t v;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must come first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ v = fp->f_addr;
+ MOD_ADDR(addr, v, c);
+ v = fp->f_size;
+ MOD_SIZE(addr, v, c);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next)
+ if (!(md->md_type & MODINFOMD_NOCOPY))
+ MOD_METADATA(addr, md, c);
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Check to see if this CPU supports long mode.
+ */
+static int
+bi_checkcpu(void)
+{
+ char *cpu_vendor;
+ int vendor[3];
+ int eflags;
+ unsigned int regs[4];
+
+ /* Check for presence of "cpuid". */
+ eflags = read_eflags();
+ write_eflags(eflags ^ PSL_ID);
+ if (!((eflags ^ read_eflags()) & PSL_ID))
+ return (0);
+
+ /* Fetch the vendor string. */
+ do_cpuid(0, regs);
+ vendor[0] = regs[1];
+ vendor[1] = regs[3];
+ vendor[2] = regs[2];
+ cpu_vendor = (char *)vendor;
+
+ /* Check for vendors that support AMD features. */
+ if (strncmp(cpu_vendor, INTEL_VENDOR_ID, 12) != 0 &&
+ strncmp(cpu_vendor, AMD_VENDOR_ID, 12) != 0 &&
+ strncmp(cpu_vendor, CENTAUR_VENDOR_ID, 12) != 0)
+ return (0);
+
+ /* Has to support AMD features. */
+ do_cpuid(0x80000000, regs);
+ if (!(regs[0] >= 0x80000001))
+ return (0);
+
+ /* Check for long mode. */
+ do_cpuid(0x80000001, regs);
+ return (regs[3] & AMDID_LM);
+}
+
+/*
+ * Load the information expected by an amd64 kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The 'bootinfo' struct is constructed, and copied into the kernel space.
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep,
+ vm_offset_t *kernendp, int add_smap)
+{
+ struct preloaded_file *xp, *kfp;
+ struct i386_devdesc *rootdev;
+ struct file_metadata *md;
+ u_int64_t kernend;
+ u_int64_t envp;
+ u_int64_t module;
+ vm_offset_t size;
+ char *rootdevname;
+ int howto;
+#ifdef LOADER_GELI_SUPPORT
+ char buf[keybuf_size];
+ struct keybuf *keybuf = (struct keybuf *)buf;
+#endif
+
+ if (!bi_checkcpu()) {
+ printf("CPU doesn't support long mode\n");
+ return (EINVAL);
+ }
+
+ howto = bi_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied device
+ * This should perhaps go to MI code and/or have $rootdev tested/set by
+ * MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ i386_getdev((void **)(&rootdev), rootdevname, NULL);
+ if (rootdev == NULL) { /* bad $rootdev/$currdev */
+ printf("can't determine root device\n");
+ return(EINVAL);
+ }
+
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(i386_fmtdev((void *)rootdev));
+
+ if (addr == 0) {
+ /* find the last module in the chain */
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ }
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* place the metadata before anything */
+ module = *modulep = addr;
+
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf64 kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ kernend = 0; /* fill it in later */
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ file_addmetadata(kfp, MODINFOMD_MODULEP, sizeof module, &module);
+ if (add_smap != 0)
+ bios_addsmapdata(kfp);
+
+#ifdef LOADER_GELI_SUPPORT
+ geli_fill_keybuf(keybuf);
+ file_addmetadata(kfp, MODINFOMD_KEYBUF, keybuf_size, buf);
+ bzero(buf, sizeof(buf));
+#endif
+
+ size = bi_copymodules64(0);
+
+ /* copy our environment */
+ envp = roundup(addr + size, PAGE_SIZE);
+ addr = bi_copyenv(envp);
+
+ /* set kernend */
+ kernend = roundup(addr, PAGE_SIZE);
+ *kernendp = kernend;
+
+ /* patch MODINFOMD_KERNEND */
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ bcopy(&kernend, md->md_data, sizeof kernend);
+
+ /* patch MODINFOMD_ENVP */
+ md = file_findmetadata(kfp, MODINFOMD_ENVP);
+ bcopy(&envp, md->md_data, sizeof envp);
+
+ /* copy module list and metadata */
+ (void)bi_copymodules64(*modulep);
+
+ return(0);
+}
diff --git a/stand/i386/libi386/comconsole.c b/stand/i386/libi386/comconsole.c
new file mode 100644
index 0000000..f98a1f7
--- /dev/null
+++ b/stand/i386/libi386/comconsole.c
@@ -0,0 +1,385 @@
+/*-
+ * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include <machine/cpufunc.h>
+#include <dev/ic/ns16550.h>
+#include <dev/pci/pcireg.h>
+#include "libi386.h"
+
+#define COMC_FMT 0x3 /* 8N1 */
+#define COMC_TXWAIT 0x40000 /* transmit timeout */
+#define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */
+#define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */
+
+#ifndef COMPORT
+#define COMPORT 0x3f8
+#endif
+#ifndef COMSPEED
+#define COMSPEED 9600
+#endif
+
+static void comc_probe(struct console *cp);
+static int comc_init(int arg);
+static void comc_putchar(int c);
+static int comc_getchar(void);
+static int comc_getspeed(void);
+static int comc_ischar(void);
+static int comc_parseint(const char *string);
+static uint32_t comc_parse_pcidev(const char *string);
+static int comc_pcidev_set(struct env_var *ev, int flags,
+ const void *value);
+static int comc_pcidev_handle(uint32_t locator);
+static int comc_port_set(struct env_var *ev, int flags,
+ const void *value);
+static void comc_setup(int speed, int port);
+static int comc_speed_set(struct env_var *ev, int flags,
+ const void *value);
+
+static int comc_curspeed;
+static int comc_port = COMPORT;
+static uint32_t comc_locator;
+
+struct console comconsole = {
+ "comconsole",
+ "serial port",
+ 0,
+ comc_probe,
+ comc_init,
+ comc_putchar,
+ comc_getchar,
+ comc_ischar
+};
+
+static void
+comc_probe(struct console *cp)
+{
+ char intbuf[16];
+ char *cons, *env;
+ int speed, port;
+ uint32_t locator;
+
+ if (comc_curspeed == 0) {
+ comc_curspeed = COMSPEED;
+ /*
+ * Assume that the speed was set by an earlier boot loader if
+ * comconsole is already the preferred console.
+ */
+ cons = getenv("console");
+ if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) ||
+ getenv("boot_multicons") != NULL) {
+ comc_curspeed = comc_getspeed();
+ }
+
+ env = getenv("comconsole_speed");
+ if (env != NULL) {
+ speed = comc_parseint(env);
+ if (speed > 0)
+ comc_curspeed = speed;
+ }
+
+ sprintf(intbuf, "%d", comc_curspeed);
+ unsetenv("comconsole_speed");
+ env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set,
+ env_nounset);
+
+ env = getenv("comconsole_port");
+ if (env != NULL) {
+ port = comc_parseint(env);
+ if (port > 0)
+ comc_port = port;
+ }
+
+ sprintf(intbuf, "%d", comc_port);
+ unsetenv("comconsole_port");
+ env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set,
+ env_nounset);
+
+ env = getenv("comconsole_pcidev");
+ if (env != NULL) {
+ locator = comc_parse_pcidev(env);
+ if (locator != 0)
+ comc_pcidev_handle(locator);
+ }
+
+ unsetenv("comconsole_pcidev");
+ env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set,
+ env_nounset);
+ }
+ comc_setup(comc_curspeed, comc_port);
+}
+
+static int
+comc_init(int arg)
+{
+
+ comc_setup(comc_curspeed, comc_port);
+
+ if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) ==
+ (C_PRESENTIN | C_PRESENTOUT))
+ return (CMD_OK);
+ return (CMD_ERROR);
+}
+
+static void
+comc_putchar(int c)
+{
+ int wait;
+
+ for (wait = COMC_TXWAIT; wait > 0; wait--)
+ if (inb(comc_port + com_lsr) & LSR_TXRDY) {
+ outb(comc_port + com_data, (u_char)c);
+ break;
+ }
+}
+
+static int
+comc_getchar(void)
+{
+ return (comc_ischar() ? inb(comc_port + com_data) : -1);
+}
+
+static int
+comc_ischar(void)
+{
+ return (inb(comc_port + com_lsr) & LSR_RXRDY);
+}
+
+static int
+comc_speed_set(struct env_var *ev, int flags, const void *value)
+{
+ int speed;
+
+ if (value == NULL || (speed = comc_parseint(value)) <= 0) {
+ printf("Invalid speed\n");
+ return (CMD_ERROR);
+ }
+
+ if (comc_curspeed != speed)
+ comc_setup(speed, comc_port);
+
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+
+ return (CMD_OK);
+}
+
+static int
+comc_port_set(struct env_var *ev, int flags, const void *value)
+{
+ int port;
+
+ if (value == NULL || (port = comc_parseint(value)) <= 0) {
+ printf("Invalid port\n");
+ return (CMD_ERROR);
+ }
+
+ if (comc_port != port)
+ comc_setup(comc_curspeed, port);
+
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+
+ return (CMD_OK);
+}
+
+/*
+ * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10.
+ * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0]
+ */
+static uint32_t
+comc_parse_pcidev(const char *string)
+{
+#ifdef NO_PCI
+ return (0);
+#else
+ char *p, *p1;
+ uint8_t bus, dev, func, bar;
+ uint32_t locator;
+ int pres;
+
+ pres = strtol(string, &p, 0);
+ if (p == string || *p != ':' || pres < 0 )
+ return (0);
+ bus = pres;
+ p1 = ++p;
+
+ pres = strtol(p1, &p, 0);
+ if (p == string || *p != ':' || pres < 0 )
+ return (0);
+ dev = pres;
+ p1 = ++p;
+
+ pres = strtol(p1, &p, 0);
+ if (p == string || (*p != ':' && *p != '\0') || pres < 0 )
+ return (0);
+ func = pres;
+
+ if (*p == ':') {
+ p1 = ++p;
+ pres = strtol(p1, &p, 0);
+ if (p == string || *p != '\0' || pres <= 0 )
+ return (0);
+ bar = pres;
+ } else
+ bar = 0x10;
+
+ locator = (bar << 16) | biospci_locator(bus, dev, func);
+ return (locator);
+#endif
+}
+
+static int
+comc_pcidev_handle(uint32_t locator)
+{
+#ifdef NO_PCI
+ return (CMD_ERROR);
+#else
+ char intbuf[64];
+ uint32_t port;
+
+ if (biospci_read_config(locator & 0xffff,
+ (locator & 0xff0000) >> 16, 2, &port) == -1) {
+ printf("Cannot read bar at 0x%x\n", locator);
+ return (CMD_ERROR);
+ }
+
+ /*
+ * biospci_read_config() sets port == 0xffffffff if the pcidev
+ * isn't found on the bus. Check for 0xffffffff and return to not
+ * panic in BTX.
+ */
+ if (port == 0xffffffff) {
+ printf("Cannot find specified pcidev\n");
+ return (CMD_ERROR);
+ }
+ if (!PCI_BAR_IO(port)) {
+ printf("Memory bar at 0x%x\n", locator);
+ return (CMD_ERROR);
+ }
+ port &= PCIM_BAR_IO_BASE;
+
+ sprintf(intbuf, "%d", port);
+ unsetenv("comconsole_port");
+ env_setenv("comconsole_port", EV_VOLATILE, intbuf,
+ comc_port_set, env_nounset);
+
+ comc_setup(comc_curspeed, port);
+ comc_locator = locator;
+
+ return (CMD_OK);
+#endif
+}
+
+static int
+comc_pcidev_set(struct env_var *ev, int flags, const void *value)
+{
+ uint32_t locator;
+ int error;
+
+ if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) {
+ printf("Invalid pcidev\n");
+ return (CMD_ERROR);
+ }
+ if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 &&
+ comc_locator != locator) {
+ error = comc_pcidev_handle(locator);
+ if (error != CMD_OK)
+ return (error);
+ }
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return (CMD_OK);
+}
+
+static void
+comc_setup(int speed, int port)
+{
+ static int TRY_COUNT = 1000000;
+ char intbuf[64];
+ int tries;
+
+ unsetenv("hw.uart.console");
+ comc_curspeed = speed;
+ comc_port = port;
+ if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0)
+ return;
+
+ outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT);
+ outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff);
+ outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8);
+ outb(comc_port + com_cfcr, COMC_FMT);
+ outb(comc_port + com_mcr, MCR_RTS | MCR_DTR);
+
+ tries = 0;
+ do
+ inb(comc_port + com_data);
+ while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT);
+
+ if (tries < TRY_COUNT) {
+ comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+ sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed);
+ env_setenv("hw.uart.console", EV_VOLATILE, intbuf, NULL, NULL);
+ } else
+ comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
+}
+
+static int
+comc_parseint(const char *speedstr)
+{
+ char *p;
+ int speed;
+
+ speed = strtol(speedstr, &p, 0);
+ if (p == speedstr || *p != '\0' || speed <= 0)
+ return (-1);
+
+ return (speed);
+}
+
+static int
+comc_getspeed(void)
+{
+ u_int divisor;
+ u_char dlbh;
+ u_char dlbl;
+ u_char cfcr;
+
+ cfcr = inb(comc_port + com_cfcr);
+ outb(comc_port + com_cfcr, CFCR_DLAB | cfcr);
+
+ dlbl = inb(comc_port + com_dlbl);
+ dlbh = inb(comc_port + com_dlbh);
+
+ outb(comc_port + com_cfcr, cfcr);
+
+ divisor = dlbh << 8 | dlbl;
+
+ /* XXX there should be more sanity checking. */
+ if (divisor == 0)
+ return (COMSPEED);
+ return (COMC_DIV2BPS(divisor));
+}
diff --git a/stand/i386/libi386/devicename.c b/stand/i386/libi386/devicename.c
new file mode 100644
index 0000000..c7705d7
--- /dev/null
+++ b/stand/i386/libi386/devicename.c
@@ -0,0 +1,206 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include "bootstrap.h"
+#include "disk.h"
+#include "libi386.h"
+#include "../zfs/libzfs.h"
+
+static int i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path);
+
+/*
+ * Point (dev) at an allocated device specifier for the device matching the
+ * path in (devspec). If it contains an explicit device specification,
+ * use that. If not, use the default device.
+ */
+int
+i386_getdev(void **vdev, const char *devspec, const char **path)
+{
+ struct i386_devdesc **dev = (struct i386_devdesc **)vdev;
+ int rv;
+
+ /*
+ * If it looks like this is just a path and no
+ * device, go with the current device.
+ */
+ if ((devspec == NULL) ||
+ (devspec[0] == '/') ||
+ (strchr(devspec, ':') == NULL)) {
+
+ if (((rv = i386_parsedev(dev, getenv("currdev"), NULL)) == 0) &&
+ (path != NULL))
+ *path = devspec;
+ return(rv);
+ }
+
+ /*
+ * Try to parse the device name off the beginning of the devspec
+ */
+ return(i386_parsedev(dev, devspec, path));
+}
+
+/*
+ * Point (dev) at an allocated device specifier matching the string version
+ * at the beginning of (devspec). Return a pointer to the remaining
+ * text in (path).
+ *
+ * In all cases, the beginning of (devspec) is compared to the names
+ * of known devices in the device switch, and then any following text
+ * is parsed according to the rules applied to the device type.
+ *
+ * For disk-type devices, the syntax is:
+ *
+ * disk<unit>[s<slice>][<partition>]:
+ *
+ */
+static int
+i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path)
+{
+ struct i386_devdesc *idev;
+ struct devsw *dv;
+ int i, unit, err;
+ char *cp;
+ const char *np;
+
+ /* minimum length check */
+ if (strlen(devspec) < 2)
+ return(EINVAL);
+
+ /* look for a device that matches */
+ for (i = 0, dv = NULL; devsw[i] != NULL; i++) {
+ if (!strncmp(devspec, devsw[i]->dv_name, strlen(devsw[i]->dv_name))) {
+ dv = devsw[i];
+ break;
+ }
+ }
+ if (dv == NULL)
+ return(ENOENT);
+ idev = malloc(sizeof(struct i386_devdesc));
+ err = 0;
+ np = (devspec + strlen(dv->dv_name));
+
+ switch(dv->dv_type) {
+ case DEVT_NONE: /* XXX what to do here? Do we care? */
+ break;
+
+ case DEVT_DISK:
+ err = disk_parsedev((struct disk_devdesc *)idev, np, path);
+ if (err != 0)
+ goto fail;
+ break;
+
+ case DEVT_CD:
+ case DEVT_NET:
+ unit = 0;
+
+ if (*np && (*np != ':')) {
+ unit = strtol(np, &cp, 0); /* get unit number if present */
+ if (cp == np) {
+ err = EUNIT;
+ goto fail;
+ }
+ } else {
+ cp = (char *)np;
+ }
+ if (*cp && (*cp != ':')) {
+ err = EINVAL;
+ goto fail;
+ }
+
+ idev->d_unit = unit;
+ if (path != NULL)
+ *path = (*cp == 0) ? cp : cp + 1;
+ break;
+ case DEVT_ZFS:
+ err = zfs_parsedev((struct zfs_devdesc *)idev, np, path);
+ if (err != 0)
+ goto fail;
+ break;
+ default:
+ err = EINVAL;
+ goto fail;
+ }
+ idev->d_dev = dv;
+ idev->d_type = dv->dv_type;
+ if (dev == NULL) {
+ free(idev);
+ } else {
+ *dev = idev;
+ }
+ return(0);
+
+ fail:
+ free(idev);
+ return(err);
+}
+
+
+char *
+i386_fmtdev(void *vdev)
+{
+ struct i386_devdesc *dev = (struct i386_devdesc *)vdev;
+ static char buf[128]; /* XXX device length constant? */
+
+ switch(dev->d_type) {
+ case DEVT_NONE:
+ strcpy(buf, "(no device)");
+ break;
+
+ case DEVT_CD:
+ case DEVT_NET:
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
+ break;
+
+ case DEVT_DISK:
+ return (disk_fmtdev(vdev));
+
+ case DEVT_ZFS:
+ return(zfs_fmtdev(vdev));
+ }
+ return(buf);
+}
+
+
+/*
+ * Set currdev to suit the value being supplied in (value)
+ */
+int
+i386_setcurrdev(struct env_var *ev, int flags, const void *value)
+{
+ struct i386_devdesc *ncurr;
+ int rv;
+
+ if ((rv = i386_parsedev(&ncurr, value, NULL)) != 0)
+ return(rv);
+ free(ncurr);
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return(0);
+}
diff --git a/stand/i386/libi386/elf32_freebsd.c b/stand/i386/libi386/elf32_freebsd.c
new file mode 100644
index 0000000..641e273
--- /dev/null
+++ b/stand/i386/libi386/elf32_freebsd.c
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <string.h>
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+static int elf32_exec(struct preloaded_file *amp);
+static int elf32_obj_exec(struct preloaded_file *amp);
+
+struct file_format i386_elf = { elf32_loadfile, elf32_exec };
+struct file_format i386_elf_obj = { elf32_obj_loadfile, elf32_obj_exec };
+
+/*
+ * There is an ELF kernel and one or more ELF modules loaded.
+ * We wish to start executing the kernel image, so make such
+ * preparations as are required, and do so.
+ */
+static int
+elf32_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *md;
+ Elf_Ehdr *ehdr;
+ vm_offset_t entry, bootinfop, modulep, kernend;
+ int boothowto, err, bootdev;
+
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return(EFTYPE);
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+
+ err = bi_load32(fp->f_args, &boothowto, &bootdev, &bootinfop, &modulep, &kernend);
+ if (err != 0)
+ return(err);
+ entry = ehdr->e_entry & 0xffffff;
+
+#ifdef DEBUG
+ printf("Start @ 0x%lx ...\n", entry);
+#endif
+
+ dev_cleanup();
+ __exec((void *)entry, boothowto, bootdev, 0, 0, 0, bootinfop, modulep, kernend);
+
+ panic("exec returned");
+}
+
+static int
+elf32_obj_exec(struct preloaded_file *fp)
+{
+ return (EFTYPE);
+}
diff --git a/stand/i386/libi386/elf64_freebsd.c b/stand/i386/libi386/elf64_freebsd.c
new file mode 100644
index 0000000..338a745
--- /dev/null
+++ b/stand/i386/libi386/elf64_freebsd.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <string.h>
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+static int elf64_exec(struct preloaded_file *amp);
+static int elf64_obj_exec(struct preloaded_file *amp);
+
+struct file_format amd64_elf = { elf64_loadfile, elf64_exec };
+struct file_format amd64_elf_obj = { elf64_obj_loadfile, elf64_obj_exec };
+
+#define PG_V 0x001
+#define PG_RW 0x002
+#define PG_U 0x004
+#define PG_PS 0x080
+
+typedef u_int64_t p4_entry_t;
+typedef u_int64_t p3_entry_t;
+typedef u_int64_t p2_entry_t;
+extern p4_entry_t PT4[];
+extern p3_entry_t PT3[];
+extern p2_entry_t PT2[];
+
+u_int32_t entry_hi;
+u_int32_t entry_lo;
+
+extern void amd64_tramp();
+
+/*
+ * There is an ELF kernel and one or more ELF modules loaded.
+ * We wish to start executing the kernel image, so make such
+ * preparations as are required, and do so.
+ */
+static int
+elf64_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *md;
+ Elf_Ehdr *ehdr;
+ vm_offset_t modulep, kernend;
+ int err;
+ int i;
+
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return(EFTYPE);
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+
+ err = bi_load64(fp->f_args, 0, &modulep, &kernend, 1);
+ if (err != 0)
+ return(err);
+
+ bzero(PT4, PAGE_SIZE);
+ bzero(PT3, PAGE_SIZE);
+ bzero(PT2, PAGE_SIZE);
+
+ /*
+ * This is kinda brutal, but every single 1GB VM memory segment points to
+ * the same first 1GB of physical memory. But it is more than adequate.
+ */
+ for (i = 0; i < 512; i++) {
+ /* Each slot of the level 4 pages points to the same level 3 page */
+ PT4[i] = (p4_entry_t)VTOP((uintptr_t)&PT3[0]);
+ PT4[i] |= PG_V | PG_RW | PG_U;
+
+ /* Each slot of the level 3 pages points to the same level 2 page */
+ PT3[i] = (p3_entry_t)VTOP((uintptr_t)&PT2[0]);
+ PT3[i] |= PG_V | PG_RW | PG_U;
+
+ /* The level 2 page slots are mapped with 2MB pages for 1GB. */
+ PT2[i] = i * (2 * 1024 * 1024);
+ PT2[i] |= PG_V | PG_RW | PG_PS | PG_U;
+ }
+
+ entry_lo = ehdr->e_entry & 0xffffffff;
+ entry_hi = (ehdr->e_entry >> 32) & 0xffffffff;
+#ifdef DEBUG
+ printf("Start @ %#llx ...\n", ehdr->e_entry);
+#endif
+
+ dev_cleanup();
+ __exec((void *)VTOP(amd64_tramp), modulep, kernend);
+
+ panic("exec returned");
+}
+
+static int
+elf64_obj_exec(struct preloaded_file *fp)
+{
+ return (EFTYPE);
+}
diff --git a/stand/i386/libi386/i386_copy.c b/stand/i386/libi386/i386_copy.c
new file mode 100644
index 0000000..3c05241
--- /dev/null
+++ b/stand/i386/libi386/i386_copy.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * MD primitives supporting placement of module data
+ *
+ * XXX should check load address/size against memory top.
+ */
+#include <stand.h>
+
+#include "libi386.h"
+#include "btxv86.h"
+
+ssize_t
+i386_copyin(const void *src, vm_offset_t dest, const size_t len)
+{
+ if (dest + len >= memtop) {
+ errno = EFBIG;
+ return(-1);
+ }
+
+ bcopy(src, PTOV(dest), len);
+ return(len);
+}
+
+ssize_t
+i386_copyout(const vm_offset_t src, void *dest, const size_t len)
+{
+ if (src + len >= memtop) {
+ errno = EFBIG;
+ return(-1);
+ }
+
+ bcopy(PTOV(src), dest, len);
+ return(len);
+}
+
+
+ssize_t
+i386_readin(const int fd, vm_offset_t dest, const size_t len)
+{
+
+ if (dest + len >= memtop_copyin) {
+ errno = EFBIG;
+ return(-1);
+ }
+
+ return (read(fd, PTOV(dest), len));
+}
diff --git a/stand/i386/libi386/i386_module.c b/stand/i386/libi386/i386_module.c
new file mode 100644
index 0000000..78ab61b
--- /dev/null
+++ b/stand/i386/libi386/i386_module.c
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * i386-specific module functionality.
+ *
+ */
+
+/*
+ * Use voodoo to load modules required by current hardware.
+ */
+int
+i386_autoload(void)
+{
+
+ /* XXX use PnP to locate stuff here */
+ return(0);
+}
diff --git a/stand/i386/libi386/libi386.h b/stand/i386/libi386/libi386.h
new file mode 100644
index 0000000..e65a060
--- /dev/null
+++ b/stand/i386/libi386/libi386.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+/*
+ * i386 fully-qualified device descriptor.
+ * Note, this must match the 'struct devdesc' declaration
+ * in bootstrap.h and also with struct zfs_devdesc for zfs
+ * support.
+ */
+struct i386_devdesc
+{
+ struct devsw *d_dev;
+ int d_type;
+ int d_unit;
+ union
+ {
+ struct
+ {
+ void *data;
+ int slice;
+ int partition;
+ off_t offset;
+ } biosdisk;
+ struct
+ {
+ void *data;
+ } bioscd;
+ struct
+ {
+ void *data;
+ uint64_t pool_guid;
+ uint64_t root_guid;
+ } zfs;
+ } d_kind;
+};
+
+/*
+ * relocater trampoline support.
+ */
+struct relocate_data {
+ uint32_t src;
+ uint32_t dest;
+ uint32_t size;
+};
+
+extern void relocater(void);
+
+/*
+ * The relocater_data[] is fixed size array allocated in relocater_tramp.S
+ */
+extern struct relocate_data relocater_data[];
+extern uint32_t relocater_size;
+
+extern uint16_t relocator_ip;
+extern uint16_t relocator_cs;
+extern uint16_t relocator_ds;
+extern uint16_t relocator_es;
+extern uint16_t relocator_fs;
+extern uint16_t relocator_gs;
+extern uint16_t relocator_ss;
+extern uint16_t relocator_sp;
+extern uint32_t relocator_esi;
+extern uint32_t relocator_eax;
+extern uint32_t relocator_ebx;
+extern uint32_t relocator_edx;
+extern uint32_t relocator_ebp;
+extern uint16_t relocator_a20_enabled;
+
+int i386_getdev(void **vdev, const char *devspec, const char **path);
+char *i386_fmtdev(void *vdev);
+int i386_setcurrdev(struct env_var *ev, int flags, const void *value);
+
+extern struct devdesc currdev; /* our current device */
+
+#define MAXDEV 31 /* maximum number of distinct devices */
+#define MAXBDDEV MAXDEV
+
+/* exported devices XXX rename? */
+extern struct devsw bioscd;
+extern struct devsw biosdisk;
+extern struct devsw pxedisk;
+extern struct fs_ops pxe_fsops;
+
+int bc_add(int biosdev); /* Register CD booted from. */
+int bc_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */
+int bc_bios2unit(int biosdev); /* xlate BIOS device -> bioscd unit */
+int bc_unit2bios(int unit); /* xlate bioscd unit -> BIOS device */
+uint32_t bd_getbigeom(int bunit); /* return geometry in bootinfo format */
+int bd_bios2unit(int biosdev); /* xlate BIOS device -> biosdisk unit */
+int bd_unit2bios(int unit); /* xlate biosdisk unit -> BIOS device */
+int bd_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */
+
+ssize_t i386_copyin(const void *src, vm_offset_t dest, const size_t len);
+ssize_t i386_copyout(const vm_offset_t src, void *dest, const size_t len);
+ssize_t i386_readin(const int fd, vm_offset_t dest, const size_t len);
+
+struct preloaded_file;
+void bios_addsmapdata(struct preloaded_file *);
+void bios_getsmap(void);
+
+void bios_getmem(void);
+extern uint32_t bios_basemem; /* base memory in bytes */
+extern uint32_t bios_extmem; /* extended memory in bytes */
+extern vm_offset_t memtop; /* last address of physical memory + 1 */
+extern vm_offset_t memtop_copyin; /* memtop less heap size for the cases */
+ /* when heap is at the top of */
+ /* extended memory; for other cases */
+ /* just the same as memtop */
+extern uint32_t high_heap_size; /* extended memory region available */
+extern vm_offset_t high_heap_base; /* for use as the heap */
+
+void biospci_detect(void);
+int biospci_find_devclass(uint32_t class, int index, uint32_t *locator);
+int biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val);
+uint32_t biospci_locator(int8_t bus, uint8_t device, uint8_t function);
+int biospci_write_config(uint32_t locator, int offset, int width, uint32_t val);
+
+void biosacpi_detect(void);
+
+int i386_autoload(void);
+
+int bi_getboothowto(char *kargs);
+void bi_setboothowto(int howto);
+vm_offset_t bi_copyenv(vm_offset_t addr);
+int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip,
+ vm_offset_t *modulep, vm_offset_t *kernend);
+int bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep,
+ vm_offset_t *kernend, int add_smap);
+
+void pxe_enable(void *pxeinfo);
diff --git a/stand/i386/libi386/multiboot.c b/stand/i386/libi386/multiboot.c
new file mode 100644
index 0000000..9aac640
--- /dev/null
+++ b/stand/i386/libi386/multiboot.c
@@ -0,0 +1,467 @@
+/*-
+ * Copyright (c) 2014 Roger Pau Monné <royger@FreeBSD.org>
+ * 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.
+ */
+
+/*
+ * This multiboot implementation only implements a subset of the full
+ * multiboot specification in order to be able to boot Xen and a
+ * FreeBSD Dom0. Trying to use it to boot other multiboot compliant
+ * kernels will most surely fail.
+ *
+ * The full multiboot specification can be found here:
+ * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/stdint.h>
+#define _MACHINE_ELF_WANT_32BIT
+#include <machine/elf.h>
+#include <string.h>
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "multiboot.h"
+#include "../i386/libi386/libi386.h"
+#include "../i386/btx/lib/btxv86.h"
+
+#define MULTIBOOT_SUPPORTED_FLAGS \
+ (MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO)
+#define NUM_MODULES 2
+#define METADATA_FIXED_SIZE (PAGE_SIZE*4)
+#define METADATA_MODULE_SIZE PAGE_SIZE
+
+#define METADATA_RESV_SIZE(mod_num) \
+ roundup(METADATA_FIXED_SIZE + METADATA_MODULE_SIZE * mod_num, PAGE_SIZE)
+
+extern int elf32_loadfile_raw(char *filename, u_int64_t dest,
+ struct preloaded_file **result, int multiboot);
+extern int elf64_load_modmetadata(struct preloaded_file *fp, u_int64_t dest);
+extern int elf64_obj_loadfile(char *filename, u_int64_t dest,
+ struct preloaded_file **result);
+
+static int multiboot_loadfile(char *, u_int64_t, struct preloaded_file **);
+static int multiboot_exec(struct preloaded_file *);
+
+static int multiboot_obj_loadfile(char *, u_int64_t, struct preloaded_file **);
+static int multiboot_obj_exec(struct preloaded_file *fp);
+
+struct file_format multiboot = { multiboot_loadfile, multiboot_exec };
+struct file_format multiboot_obj =
+ { multiboot_obj_loadfile, multiboot_obj_exec };
+
+extern void multiboot_tramp();
+
+static const char mbl_name[] = "FreeBSD Loader";
+
+static int
+num_modules(struct preloaded_file *kfp)
+{
+ struct kernel_module *kmp;
+ int mod_num = 0;
+
+ for (kmp = kfp->f_modules; kmp != NULL; kmp = kmp->m_next)
+ mod_num++;
+
+ return (mod_num);
+}
+
+static vm_offset_t
+max_addr(void)
+{
+ struct preloaded_file *fp;
+ vm_offset_t addr = 0;
+
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+ if (addr < (fp->f_addr + fp->f_size))
+ addr = fp->f_addr + fp->f_size;
+ }
+
+ return (addr);
+}
+
+static int
+multiboot_loadfile(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ uint32_t *magic;
+ int i, error;
+ caddr_t header_search;
+ ssize_t search_size;
+ int fd;
+ struct multiboot_header *header;
+ char *cmdline;
+
+ /*
+ * Read MULTIBOOT_SEARCH size in order to search for the
+ * multiboot magic header.
+ */
+ if (filename == NULL)
+ return (EFTYPE);
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return (errno);
+ header_search = malloc(MULTIBOOT_SEARCH);
+ if (header_search == NULL) {
+ close(fd);
+ return (ENOMEM);
+ }
+ search_size = read(fd, header_search, MULTIBOOT_SEARCH);
+ magic = (uint32_t *)header_search;
+
+ header = NULL;
+ for (i = 0; i < (search_size / sizeof(uint32_t)); i++) {
+ if (magic[i] == MULTIBOOT_HEADER_MAGIC) {
+ header = (struct multiboot_header *)&magic[i];
+ break;
+ }
+ }
+
+ if (header == NULL) {
+ error = EFTYPE;
+ goto out;
+ }
+
+ /* Valid multiboot header has been found, validate checksum */
+ if (header->magic + header->flags + header->checksum != 0) {
+ printf(
+ "Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n",
+ header->magic, header->flags, header->checksum);
+ error = EFTYPE;
+ goto out;
+ }
+
+ if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) {
+ printf("Unsupported multiboot flags found: 0x%x\n",
+ header->flags);
+ error = EFTYPE;
+ goto out;
+ }
+
+ error = elf32_loadfile_raw(filename, dest, result, 1);
+ if (error != 0) {
+ printf(
+ "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n",
+ error);
+ goto out;
+ }
+
+ /*
+ * f_addr is already aligned to PAGE_SIZE, make sure
+ * f_size it's also aligned so when the modules are loaded
+ * they are aligned to PAGE_SIZE.
+ */
+ (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE);
+
+out:
+ free(header_search);
+ close(fd);
+ return (error);
+}
+
+static int
+multiboot_exec(struct preloaded_file *fp)
+{
+ vm_offset_t module_start, last_addr, metadata_size;
+ vm_offset_t modulep, kernend, entry;
+ struct file_metadata *md;
+ Elf_Ehdr *ehdr;
+ struct multiboot_info *mb_info = NULL;
+ struct multiboot_mod_list *mb_mod = NULL;
+ char *cmdline = NULL;
+ size_t len;
+ int error, mod_num;
+
+ /*
+ * Don't pass the memory size found by the bootloader, the memory
+ * available to Dom0 will be lower than that.
+ */
+ unsetenv("smbios.memory.enabled");
+
+ /* Allocate the multiboot struct and fill the basic details. */
+ mb_info = malloc(sizeof(struct multiboot_info));
+ if (mb_info == NULL) {
+ error = ENOMEM;
+ goto error;
+ }
+ bzero(mb_info, sizeof(struct multiboot_info));
+ mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME;
+ mb_info->mem_lower = bios_basemem / 1024;
+ mb_info->mem_upper = bios_extmem / 1024;
+ mb_info->boot_loader_name = VTOP(mbl_name);
+
+ /* Set the Xen command line. */
+ if (fp->f_args == NULL) {
+ /* Add the Xen command line if it is set. */
+ cmdline = getenv("xen_cmdline");
+ if (cmdline != NULL) {
+ fp->f_args = strdup(cmdline);
+ if (fp->f_args == NULL) {
+ error = ENOMEM;
+ goto error;
+ }
+ }
+ }
+ if (fp->f_args != NULL) {
+ len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1;
+ cmdline = malloc(len);
+ if (cmdline == NULL) {
+ error = ENOMEM;
+ goto error;
+ }
+ snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args);
+ mb_info->cmdline = VTOP(cmdline);
+ mb_info->flags |= MULTIBOOT_INFO_CMDLINE;
+ }
+
+ /* Find the entry point of the Xen kernel and save it for later */
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) {
+ printf("Unable to find %s entry point\n", fp->f_name);
+ error = EINVAL;
+ goto error;
+ }
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+ entry = ehdr->e_entry & 0xffffff;
+
+ /*
+ * Prepare the multiboot module list, Xen assumes the first
+ * module is the Dom0 kernel, and the second one is the initramfs.
+ * This is not optimal for FreeBSD, that doesn't have a initramfs
+ * but instead loads modules dynamically and creates the metadata
+ * info on-the-fly.
+ *
+ * As expected, the first multiboot module is going to be the
+ * FreeBSD kernel loaded as a raw file. The second module is going
+ * to contain the metadata info and the loaded modules.
+ *
+ * On native FreeBSD loads all the modules and then places the
+ * metadata info at the end, but this is painful when running on Xen,
+ * because it relocates the second multiboot module wherever it
+ * likes. In order to workaround this limitation the metadata
+ * information is placed at the start of the second module and
+ * the original modulep value is saved together with the other
+ * metadata, so we can relocate everything.
+ *
+ * Native layout:
+ * fp->f_addr + fp->f_size
+ * +---------+----------------+------------+
+ * | | | |
+ * | Kernel | Modules | Metadata |
+ * | | | |
+ * +---------+----------------+------------+
+ * fp->f_addr modulep kernend
+ *
+ * Xen layout:
+ *
+ * Initial:
+ * fp->f_addr + fp->f_size
+ * +---------+----------+----------------+------------+
+ * | | | | |
+ * | Kernel | Reserved | Modules | Metadata |
+ * | | | | dry run |
+ * +---------+----------+----------------+------------+
+ * fp->f_addr
+ *
+ * After metadata polacement (ie: final):
+ * fp->f_addr + fp->f_size
+ * +-----------+---------+----------+----------------+
+ * | | | | |
+ * | Kernel | Free | Metadata | Modules |
+ * | | | | |
+ * +-----------+---------+----------+----------------+
+ * fp->f_addr modulep kernend
+ * \__________/ \__________________________/
+ * Multiboot module 0 Multiboot module 1
+ */
+
+ fp = file_findfile(NULL, "elf kernel");
+ if (fp == NULL) {
+ printf("No FreeBSD kernel provided, aborting\n");
+ error = EINVAL;
+ goto error;
+ }
+
+ if (fp->f_metadata != NULL) {
+ printf("FreeBSD kernel already contains metadata, aborting\n");
+ error = EINVAL;
+ goto error;
+ }
+
+
+ mb_mod = malloc(sizeof(struct multiboot_mod_list) * NUM_MODULES);
+ if (mb_mod == NULL) {
+ error = ENOMEM;
+ goto error;
+ }
+
+ bzero(mb_mod, sizeof(struct multiboot_mod_list) * NUM_MODULES);
+
+ /*
+ * Calculate how much memory is needed for the metatdata. We did
+ * an approximation of the maximum size when loading the kernel,
+ * but now we know the exact size, so we can release some of this
+ * preallocated memory if not needed.
+ */
+ last_addr = roundup(max_addr(), PAGE_SIZE);
+ mod_num = num_modules(fp);
+
+ /*
+ * Place the metadata after the last used address in order to
+ * calculate it's size, this will not be used.
+ */
+ error = bi_load64(fp->f_args, last_addr, &modulep, &kernend, 0);
+ if (error != 0) {
+ printf("bi_load64 failed: %d\n", error);
+ goto error;
+ }
+ metadata_size = roundup(kernend - last_addr, PAGE_SIZE);
+
+ /* Check that the size is not greater than what we have reserved */
+ if (metadata_size > METADATA_RESV_SIZE(mod_num)) {
+ printf("Required memory for metadata is greater than reserved "
+ "space, please increase METADATA_FIXED_SIZE and "
+ "METADATA_MODULE_SIZE and rebuild the loader\n");
+ error = ENOMEM;
+ goto error;
+ }
+
+ /* Clean the metadata added to the kernel in the bi_load64 dry run */
+ file_removemetadata(fp);
+
+ /*
+ * This is the position where the second multiboot module
+ * will be placed.
+ */
+ module_start = fp->f_addr + fp->f_size - metadata_size;
+
+ error = bi_load64(fp->f_args, module_start, &modulep, &kernend, 0);
+ if (error != 0) {
+ printf("bi_load64 failed: %d\n", error);
+ goto error;
+ }
+
+ mb_mod[0].mod_start = fp->f_addr;
+ mb_mod[0].mod_end = fp->f_addr + fp->f_size;
+ mb_mod[0].mod_end -= METADATA_RESV_SIZE(mod_num);
+
+ mb_mod[1].mod_start = module_start;
+ mb_mod[1].mod_end = last_addr;
+
+ mb_info->mods_count = NUM_MODULES;
+ mb_info->mods_addr = VTOP(mb_mod);
+ mb_info->flags |= MULTIBOOT_INFO_MODS;
+
+ dev_cleanup();
+ __exec((void *)VTOP(multiboot_tramp), (void *)entry,
+ (void *)VTOP(mb_info));
+
+ panic("exec returned");
+
+error:
+ if (mb_mod)
+ free(mb_mod);
+ if (mb_info)
+ free(mb_info);
+ if (cmdline)
+ free(cmdline);
+ return (error);
+}
+
+static int
+multiboot_obj_loadfile(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ struct preloaded_file *mfp, *kfp, *rfp;
+ struct kernel_module *kmp;
+ int error, mod_num;
+
+ /* See if there's a multiboot kernel loaded */
+ mfp = file_findfile(NULL, "elf multiboot kernel");
+ if (mfp == NULL)
+ return (EFTYPE);
+
+ /*
+ * We have a multiboot kernel loaded, see if there's a FreeBSD
+ * kernel loaded also.
+ */
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL) {
+ /*
+ * No kernel loaded, this must be it. The kernel has to
+ * be loaded as a raw file, it will be processed by
+ * Xen and correctly loaded as an ELF file.
+ */
+ rfp = file_loadraw(filename, "elf kernel", 0);
+ if (rfp == NULL) {
+ printf(
+ "Unable to load %s as a multiboot payload kernel\n",
+ filename);
+ return (EINVAL);
+ }
+
+ /* Load kernel metadata... */
+ setenv("kernelname", filename, 1);
+ error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size);
+ if (error) {
+ printf("Unable to load kernel %s metadata error: %d\n",
+ rfp->f_name, error);
+ return (EINVAL);
+ }
+
+ /*
+ * Save space at the end of the kernel in order to place
+ * the metadata information. We do an approximation of the
+ * max metadata size, this is not optimal but it's probably
+ * the best we can do at this point. Once all modules are
+ * loaded and the size of the metadata is known this
+ * space will be recovered if not used.
+ */
+ mod_num = num_modules(rfp);
+ rfp->f_size = roundup(rfp->f_size, PAGE_SIZE);
+ rfp->f_size += METADATA_RESV_SIZE(mod_num);
+ *result = rfp;
+ } else {
+ /* The rest should be loaded as regular modules */
+ error = elf64_obj_loadfile(filename, dest, result);
+ if (error != 0) {
+ printf("Unable to load %s as an object file, error: %d",
+ filename, error);
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+multiboot_obj_exec(struct preloaded_file *fp)
+{
+
+ return (EFTYPE);
+}
diff --git a/stand/i386/libi386/multiboot.h b/stand/i386/libi386/multiboot.h
new file mode 100644
index 0000000..819fa2e
--- /dev/null
+++ b/stand/i386/libi386/multiboot.h
@@ -0,0 +1,225 @@
+/* multiboot.h - Multiboot header file. */
+/* Copyright (C) 1999,2003,2007,2008,2009 Free Software Foundation, Inc.
+*
+* 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 ANY
+* DEVELOPER OR DISTRIBUTOR 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.
+*
+* $FreeBSD$
+*/
+
+#ifndef MULTIBOOT_HEADER
+#define MULTIBOOT_HEADER 1
+
+/* How many bytes from the start of the file we search for the header. */
+#define MULTIBOOT_SEARCH 8192
+
+/* The magic field should contain this. */
+#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
+
+/* This should be in %eax. */
+#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
+
+/* The bits in the required part of flags field we don't support. */
+#define MULTIBOOT_UNSUPPORTED 0x0000fffc
+
+/* Alignment of multiboot modules. */
+#define MULTIBOOT_MOD_ALIGN 0x00001000
+
+/* Alignment of the multiboot info structure. */
+#define MULTIBOOT_INFO_ALIGN 0x00000004
+
+/* Flags set in the 'flags' member of the multiboot header. */
+
+/* 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
+
+/* Flags to be set in the 'flags' member of the multiboot info structure. */
+
+/* is there basic lower/upper memory information? */
+#define MULTIBOOT_INFO_MEMORY 0x00000001
+/* is there a boot device set? */
+#define MULTIBOOT_INFO_BOOTDEV 0x00000002
+/* is the command-line defined? */
+#define MULTIBOOT_INFO_CMDLINE 0x00000004
+/* are there modules to do something with? */
+#define MULTIBOOT_INFO_MODS 0x00000008
+
+/* These next two are mutually exclusive */
+
+/* is there a symbol table loaded? */
+#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010
+/* is there an ELF section header table? */
+#define MULTIBOOT_INFO_ELF_SHDR 0X00000020
+
+/* is there a full memory map? */
+#define MULTIBOOT_INFO_MEM_MAP 0x00000040
+
+/* Is there drive info? */
+#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080
+
+/* Is there a config table? */
+#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100
+
+/* Is there a boot loader name? */
+#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200
+
+/* Is there a APM table? */
+#define MULTIBOOT_INFO_APM_TABLE 0x00000400
+
+/* Is there video information? */
+#define MULTIBOOT_INFO_VIDEO_INFO 0x00000800
+
+#ifndef ASM_FILE
+
+typedef unsigned short multiboot_uint16_t;
+typedef unsigned int multiboot_uint32_t;
+typedef unsigned long long multiboot_uint64_t;
+
+struct multiboot_header
+{
+/* Must be MULTIBOOT_MAGIC - see above. */
+ multiboot_uint32_t magic;
+
+/* Feature flags. */
+ multiboot_uint32_t flags;
+
+/* The above fields plus this one must equal 0 mod 2^32. */
+ multiboot_uint32_t checksum;
+
+/* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */
+ multiboot_uint32_t header_addr;
+ multiboot_uint32_t load_addr;
+ multiboot_uint32_t load_end_addr;
+ multiboot_uint32_t bss_end_addr;
+ multiboot_uint32_t entry_addr;
+
+/* These are only valid if MULTIBOOT_VIDEO_MODE is set. */
+ multiboot_uint32_t mode_type;
+ multiboot_uint32_t width;
+ multiboot_uint32_t height;
+ multiboot_uint32_t depth;
+};
+
+/* The symbol table for a.out. */
+struct multiboot_aout_symbol_table
+{
+ multiboot_uint32_t tabsize;
+ multiboot_uint32_t strsize;
+ multiboot_uint32_t addr;
+ multiboot_uint32_t reserved;
+};
+typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;
+
+/* The section header table for ELF. */
+struct multiboot_elf_section_header_table
+{
+ multiboot_uint32_t num;
+ multiboot_uint32_t size;
+ multiboot_uint32_t addr;
+ multiboot_uint32_t shndx;
+};
+typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t;
+
+struct multiboot_info
+{
+/* Multiboot info version number */
+ multiboot_uint32_t flags;
+
+/* Available memory from BIOS */
+ multiboot_uint32_t mem_lower;
+ multiboot_uint32_t mem_upper;
+
+/* "root" partition */
+ multiboot_uint32_t boot_device;
+
+/* Kernel command line */
+ multiboot_uint32_t cmdline;
+
+/* Boot-Module list */
+ multiboot_uint32_t mods_count;
+ multiboot_uint32_t mods_addr;
+
+ union
+ {
+ multiboot_aout_symbol_table_t aout_sym;
+ multiboot_elf_section_header_table_t elf_sec;
+ } u;
+
+/* Memory Mapping buffer */
+ multiboot_uint32_t mmap_length;
+ multiboot_uint32_t mmap_addr;
+
+/* Drive Info buffer */
+ multiboot_uint32_t drives_length;
+ multiboot_uint32_t drives_addr;
+
+/* ROM configuration table */
+ multiboot_uint32_t config_table;
+
+/* Boot Loader Name */
+ multiboot_uint32_t boot_loader_name;
+
+/* APM table */
+ multiboot_uint32_t apm_table;
+
+/* Video */
+ multiboot_uint32_t vbe_control_info;
+ multiboot_uint32_t vbe_mode_info;
+ multiboot_uint16_t vbe_mode;
+ multiboot_uint16_t vbe_interface_seg;
+ multiboot_uint16_t vbe_interface_off;
+ multiboot_uint16_t vbe_interface_len;
+};
+typedef struct multiboot_info multiboot_info_t;
+
+struct multiboot_mmap_entry
+{
+ multiboot_uint32_t size;
+ multiboot_uint64_t addr;
+ multiboot_uint64_t len;
+#define MULTIBOOT_MEMORY_AVAILABLE 1
+#define MULTIBOOT_MEMORY_RESERVED 2
+ multiboot_uint32_t type;
+} __attribute__((packed));
+typedef struct multiboot_mmap_entry multiboot_memory_map_t;
+
+struct multiboot_mod_list
+{
+/* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */
+ multiboot_uint32_t mod_start;
+ multiboot_uint32_t mod_end;
+
+/* Module command line */
+ multiboot_uint32_t cmdline;
+
+/* padding to take it to 16 bytes (must be zero) */
+ multiboot_uint32_t pad;
+};
+typedef struct multiboot_mod_list multiboot_module_t;
+
+#endif /* ! ASM_FILE */
+
+#endif /* ! MULTIBOOT_HEADER */
diff --git a/stand/i386/libi386/multiboot_tramp.S b/stand/i386/libi386/multiboot_tramp.S
new file mode 100644
index 0000000..0bd6043
--- /dev/null
+++ b/stand/i386/libi386/multiboot_tramp.S
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2014 Roger Pau Monné <royger@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define ASM_FILE
+#include "multiboot.h"
+
+/*
+ * The multiboot specification requires the executable to be launched
+ * with %cs set to a flat read/execute segment with offset 0 and limit
+ * 0xFFFFFFFF, and the rest of the segment registers (%ds, %es, %fs,
+ * %gs, %ss) to flat read/write segments with the same offset and limit.
+ * This is already done by the BTX code before calling multiboot_tramp,
+ * so there is no need to do anything here.
+ */
+
+ .globl multiboot_tramp
+multiboot_tramp:
+ /* Be sure that interrupts are disabled. */
+ cli
+
+ movl $MULTIBOOT_BOOTLOADER_MAGIC, %eax
+ /* Get the entry point and address of the multiboot_info parameter. */
+ movl 8(%esp), %ebx
+ movl 4(%esp), %ecx
+
+ call *%ecx
diff --git a/stand/i386/libi386/nullconsole.c b/stand/i386/libi386/nullconsole.c
new file mode 100644
index 0000000..ebb1e7e
--- /dev/null
+++ b/stand/i386/libi386/nullconsole.c
@@ -0,0 +1,88 @@
+/*-
+ * nullconsole.c
+ *
+ * Author: Doug Ambrisko <ambrisko@whistle.com>
+ * Copyright (c) 2000 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+
+static void nullc_probe(struct console *cp);
+static int nullc_init(int arg);
+static void nullc_putchar(int c);
+static int nullc_getchar(void);
+static int nullc_ischar(void);
+
+struct console nullconsole = {
+ "nullconsole",
+ "null port",
+ 0,
+ nullc_probe,
+ nullc_init,
+ nullc_putchar,
+ nullc_getchar,
+ nullc_ischar
+};
+
+static void
+nullc_probe(struct console *cp)
+{
+ cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+}
+
+static int
+nullc_init(int arg)
+{
+ return(0);
+}
+
+static void
+nullc_putchar(int c)
+{
+}
+
+static int
+nullc_getchar(void)
+{
+ return(-1);
+}
+
+static int
+nullc_ischar(void)
+{
+ return(0);
+}
diff --git a/stand/i386/libi386/pread.c b/stand/i386/libi386/pread.c
new file mode 100644
index 0000000..870e254
--- /dev/null
+++ b/stand/i386/libi386/pread.c
@@ -0,0 +1,80 @@
+/*
+ * $NetBSD: pread.c,v 1.2 1997/03/22 01:48:38 thorpej Exp $
+ */
+
+/*-
+ * Copyright (c) 1996
+ * Matthias Drochner. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by Matthias Drochner.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* read into destination in flat addr space */
+
+#include <stand.h>
+
+#include "libi386.h"
+
+#ifdef SAVE_MEMORY
+#define BUFSIZE (1*1024)
+#else
+#define BUFSIZE (4*1024)
+#endif
+
+static char buf[BUFSIZE];
+
+int
+pread(fd, dest, size)
+ int fd;
+ vm_offset_t dest;
+ int size;
+{
+ int rsize;
+
+ rsize = size;
+ while (rsize > 0) {
+ int count, got;
+
+ count = (rsize < BUFSIZE ? rsize : BUFSIZE);
+
+ got = read(fd, buf, count);
+ if (got < 0)
+ return (-1);
+
+ /* put to physical space */
+ vpbcopy(buf, dest, got);
+
+ dest += got;
+ rsize -= got;
+ if (got < count)
+ break; /* EOF */
+ }
+ return (size - rsize);
+}
diff --git a/stand/i386/libi386/pxe.c b/stand/i386/libi386/pxe.c
new file mode 100644
index 0000000..7cb4833
--- /dev/null
+++ b/stand/i386/libi386/pxe.c
@@ -0,0 +1,540 @@
+/*-
+ * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org>
+ * Copyright (c) 2000 Paul Saab <ps@freebsd.org>
+ * Copyright (c) 2000 John Baldwin <jhb@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/param.h>
+
+#include <net/ethernet.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include <net.h>
+#include <netif.h>
+#include <nfsv2.h>
+#include <iodesc.h>
+
+#include <bootp.h>
+#include <bootstrap.h>
+#include "btxv86.h"
+#include "pxe.h"
+
+/*
+ * Allocate the PXE buffers statically instead of sticking grimy fingers into
+ * BTX's private data area. The scratch buffer is used to send information to
+ * the PXE BIOS, and the data buffer is used to receive data from the PXE BIOS.
+ */
+#define PXE_BUFFER_SIZE 0x2000
+static char scratch_buffer[PXE_BUFFER_SIZE];
+static char data_buffer[PXE_BUFFER_SIZE];
+
+static pxenv_t *pxenv_p = NULL; /* PXENV+ */
+static pxe_t *pxe_p = NULL; /* !PXE */
+
+#ifdef PXE_DEBUG
+static int pxe_debug = 0;
+#endif
+
+void pxe_enable(void *pxeinfo);
+static void (*pxe_call)(int func);
+static void pxenv_call(int func);
+static void bangpxe_call(int func);
+
+static int pxe_init(void);
+static int pxe_print(int verbose);
+static void pxe_cleanup(void);
+
+static void pxe_perror(int error);
+static int pxe_netif_match(struct netif *nif, void *machdep_hint);
+static int pxe_netif_probe(struct netif *nif, void *machdep_hint);
+static void pxe_netif_init(struct iodesc *desc, void *machdep_hint);
+static ssize_t pxe_netif_get(struct iodesc *, void **, time_t);
+static ssize_t pxe_netif_put(struct iodesc *desc, void *pkt, size_t len);
+static void pxe_netif_end(struct netif *nif);
+
+extern struct netif_stats pxe_st[];
+extern u_int16_t __bangpxeseg;
+extern u_int16_t __bangpxeoff;
+extern void __bangpxeentry(void);
+extern u_int16_t __pxenvseg;
+extern u_int16_t __pxenvoff;
+extern void __pxenventry(void);
+
+struct netif_dif pxe_ifs[] = {
+/* dif_unit dif_nsel dif_stats dif_private */
+ {0, 1, &pxe_st[0], 0}
+};
+
+struct netif_stats pxe_st[nitems(pxe_ifs)];
+
+struct netif_driver pxenetif = {
+ .netif_bname = "pxenet",
+ .netif_match = pxe_netif_match,
+ .netif_probe = pxe_netif_probe,
+ .netif_init = pxe_netif_init,
+ .netif_get = pxe_netif_get,
+ .netif_put = pxe_netif_put,
+ .netif_end = pxe_netif_end,
+ .netif_ifs = pxe_ifs,
+ .netif_nifs = nitems(pxe_ifs)
+};
+
+struct netif_driver *netif_drivers[] = {
+ &pxenetif,
+ NULL
+};
+
+struct devsw pxedisk = {
+ .dv_name = "net",
+ .dv_type = DEVT_NET,
+ .dv_init = pxe_init,
+ .dv_strategy = NULL, /* Will be set in pxe_init */
+ .dv_open = NULL, /* Will be set in pxe_init */
+ .dv_close = NULL, /* Will be set in pxe_init */
+ .dv_ioctl = noioctl,
+ .dv_print = pxe_print,
+ .dv_cleanup = pxe_cleanup
+};
+
+/*
+ * This function is called by the loader to enable PXE support if we
+ * are booted by PXE. The passed in pointer is a pointer to the PXENV+
+ * structure.
+ */
+void
+pxe_enable(void *pxeinfo)
+{
+ pxenv_p = (pxenv_t *)pxeinfo;
+ pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 +
+ pxenv_p->PXEPtr.offset);
+ pxe_call = NULL;
+}
+
+/*
+ * return true if pxe structures are found/initialized,
+ * also figures out our IP information via the pxe cached info struct
+ */
+static int
+pxe_init(void)
+{
+ t_PXENV_GET_CACHED_INFO *gci_p;
+ int counter;
+ uint8_t checksum;
+ uint8_t *checkptr;
+ extern struct devsw netdev;
+
+ if (pxenv_p == NULL)
+ return (0);
+
+ /* look for "PXENV+" */
+ if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) {
+ pxenv_p = NULL;
+ return (0);
+ }
+
+ /* make sure the size is something we can handle */
+ if (pxenv_p->Length > sizeof(*pxenv_p)) {
+ printf("PXENV+ structure too large, ignoring\n");
+ pxenv_p = NULL;
+ return (0);
+ }
+
+ /*
+ * do byte checksum:
+ * add up each byte in the structure, the total should be 0
+ */
+ checksum = 0;
+ checkptr = (uint8_t *) pxenv_p;
+ for (counter = 0; counter < pxenv_p->Length; counter++)
+ checksum += *checkptr++;
+ if (checksum != 0) {
+ printf("PXENV+ structure failed checksum, ignoring\n");
+ pxenv_p = NULL;
+ return (0);
+ }
+
+ /*
+ * PXENV+ passed, so use that if !PXE is not available or
+ * the checksum fails.
+ */
+ pxe_call = pxenv_call;
+ if (pxenv_p->Version >= 0x0200) {
+ for (;;) {
+ if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) {
+ pxe_p = NULL;
+ break;
+ }
+ checksum = 0;
+ checkptr = (uint8_t *)pxe_p;
+ for (counter = 0; counter < pxe_p->StructLength;
+ counter++)
+ checksum += *checkptr++;
+ if (checksum != 0) {
+ pxe_p = NULL;
+ break;
+ }
+ pxe_call = bangpxe_call;
+ break;
+ }
+ }
+
+ pxedisk.dv_open = netdev.dv_open;
+ pxedisk.dv_close = netdev.dv_close;
+ pxedisk.dv_strategy = netdev.dv_strategy;
+
+ printf("\nPXE version %d.%d, real mode entry point ",
+ (uint8_t) (pxenv_p->Version >> 8),
+ (uint8_t) (pxenv_p->Version & 0xFF));
+ if (pxe_call == bangpxe_call)
+ printf("@%04x:%04x\n",
+ pxe_p->EntryPointSP.segment,
+ pxe_p->EntryPointSP.offset);
+ else
+ printf("@%04x:%04x\n",
+ pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset);
+
+ gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer;
+ bzero(gci_p, sizeof(*gci_p));
+ gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY;
+ pxe_call(PXENV_GET_CACHED_INFO);
+ if (gci_p->Status != 0) {
+ pxe_perror(gci_p->Status);
+ pxe_p = NULL;
+ return (0);
+ }
+ free(bootp_response);
+ if ((bootp_response = malloc(gci_p->BufferSize)) != NULL) {
+ bootp_response_size = gci_p->BufferSize;
+ bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset),
+ bootp_response, bootp_response_size);
+ }
+ return (1);
+}
+
+static int
+pxe_print(int verbose)
+{
+ if (pxe_call == NULL)
+ return (0);
+
+ printf("%s devices:", pxedisk.dv_name);
+ if (pager_output("\n") != 0)
+ return (1);
+ printf(" %s0:", pxedisk.dv_name);
+ if (verbose) {
+ printf(" %s:%s", inet_ntoa(rootip), rootpath);
+ }
+ return (pager_output("\n"));
+}
+
+static void
+pxe_cleanup(void)
+{
+#ifdef PXE_DEBUG
+ t_PXENV_UNLOAD_STACK *unload_stack_p =
+ (t_PXENV_UNLOAD_STACK *)scratch_buffer;
+ t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p =
+ (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer;
+#endif
+
+ if (pxe_call == NULL)
+ return;
+
+ pxe_call(PXENV_UNDI_SHUTDOWN);
+
+#ifdef PXE_DEBUG
+ if (pxe_debug && undi_shutdown_p->Status != 0)
+ printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n",
+ undi_shutdown_p->Status);
+#endif
+
+ pxe_call(PXENV_UNLOAD_STACK);
+
+#ifdef PXE_DEBUG
+ if (pxe_debug && unload_stack_p->Status != 0)
+ printf("pxe_cleanup: UNLOAD_STACK failed %x\n",
+ unload_stack_p->Status);
+#endif
+}
+
+void
+pxe_perror(int err)
+{
+ return;
+}
+
+void
+pxenv_call(int func)
+{
+#ifdef PXE_DEBUG
+ if (pxe_debug)
+ printf("pxenv_call %x\n", func);
+#endif
+
+ bzero(&v86, sizeof(v86));
+ bzero(data_buffer, sizeof(data_buffer));
+
+ __pxenvseg = pxenv_p->RMEntry.segment;
+ __pxenvoff = pxenv_p->RMEntry.offset;
+
+ v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
+ v86.es = VTOPSEG(scratch_buffer);
+ v86.edi = VTOPOFF(scratch_buffer);
+ v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry);
+ v86.ebx = func;
+ v86int();
+ v86.ctl = V86_FLAGS;
+}
+
+void
+bangpxe_call(int func)
+{
+#ifdef PXE_DEBUG
+ if (pxe_debug)
+ printf("bangpxe_call %x\n", func);
+#endif
+
+ bzero(&v86, sizeof(v86));
+ bzero(data_buffer, sizeof(data_buffer));
+
+ __bangpxeseg = pxe_p->EntryPointSP.segment;
+ __bangpxeoff = pxe_p->EntryPointSP.offset;
+
+ v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
+ v86.edx = VTOPSEG(scratch_buffer);
+ v86.eax = VTOPOFF(scratch_buffer);
+ v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry);
+ v86.ebx = func;
+ v86int();
+ v86.ctl = V86_FLAGS;
+}
+
+
+static int
+pxe_netif_match(struct netif *nif, void *machdep_hint)
+{
+ return (1);
+}
+
+static int
+pxe_netif_probe(struct netif *nif, void *machdep_hint)
+{
+ if (pxe_call == NULL)
+ return (-1);
+
+ return (0);
+}
+
+static void
+pxe_netif_end(struct netif *nif)
+{
+ t_PXENV_UNDI_CLOSE *undi_close_p;
+
+ undi_close_p = (t_PXENV_UNDI_CLOSE *)scratch_buffer;
+ bzero(undi_close_p, sizeof(*undi_close_p));
+ pxe_call(PXENV_UNDI_CLOSE);
+ if (undi_close_p->Status != 0)
+ printf("undi close failed: %x\n", undi_close_p->Status);
+}
+
+static void
+pxe_netif_init(struct iodesc *desc, void *machdep_hint)
+{
+ t_PXENV_UNDI_GET_INFORMATION *undi_info_p;
+ t_PXENV_UNDI_OPEN *undi_open_p;
+ uint8_t *mac;
+ int i, len;
+
+ undi_info_p = (t_PXENV_UNDI_GET_INFORMATION *)scratch_buffer;
+ bzero(undi_info_p, sizeof(*undi_info_p));
+ pxe_call(PXENV_UNDI_GET_INFORMATION);
+ if (undi_info_p->Status != 0) {
+ printf("undi get info failed: %x\n", undi_info_p->Status);
+ return;
+ }
+
+ /* Make sure the CurrentNodeAddress is valid. */
+ for (i = 0; i < undi_info_p->HwAddrLen; ++i) {
+ if (undi_info_p->CurrentNodeAddress[i] != 0)
+ break;
+ }
+ if (i < undi_info_p->HwAddrLen) {
+ for (i = 0; i < undi_info_p->HwAddrLen; ++i) {
+ if (undi_info_p->CurrentNodeAddress[i] != 0xff)
+ break;
+ }
+ }
+ if (i < undi_info_p->HwAddrLen)
+ mac = undi_info_p->CurrentNodeAddress;
+ else
+ mac = undi_info_p->PermNodeAddress;
+
+ len = min(sizeof (desc->myea), undi_info_p->HwAddrLen);
+ for (i = 0; i < len; ++i)
+ desc->myea[i] = mac[i];
+
+ if (bootp_response != NULL)
+ desc->xid = bootp_response->bp_xid;
+ else
+ desc->xid = 0;
+
+ undi_open_p = (t_PXENV_UNDI_OPEN *)scratch_buffer;
+ bzero(undi_open_p, sizeof(*undi_open_p));
+ undi_open_p->PktFilter = FLTR_DIRECTED | FLTR_BRDCST;
+ pxe_call(PXENV_UNDI_OPEN);
+ if (undi_open_p->Status != 0)
+ printf("undi open failed: %x\n", undi_open_p->Status);
+}
+
+static int
+pxe_netif_receive(void **pkt)
+{
+ t_PXENV_UNDI_ISR *isr = (t_PXENV_UNDI_ISR *)scratch_buffer;
+ char *buf, *ptr, *frame;
+ size_t size, rsize;
+
+ bzero(isr, sizeof(*isr));
+ isr->FuncFlag = PXENV_UNDI_ISR_IN_START;
+ pxe_call(PXENV_UNDI_ISR);
+ if (isr->Status != 0)
+ return (-1);
+
+ bzero(isr, sizeof(*isr));
+ isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
+ pxe_call(PXENV_UNDI_ISR);
+ if (isr->Status != 0)
+ return (-1);
+
+ while (isr->FuncFlag == PXENV_UNDI_ISR_OUT_TRANSMIT) {
+ /*
+ * Wait till transmit is done.
+ */
+ bzero(isr, sizeof(*isr));
+ isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ pxe_call(PXENV_UNDI_ISR);
+ if (isr->Status != 0 ||
+ isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE)
+ return (-1);
+ }
+
+ while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE) {
+ if (isr->Status != 0 ||
+ isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) {
+ return (-1);
+ }
+ bzero(isr, sizeof(*isr));
+ isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ pxe_call(PXENV_UNDI_ISR);
+ }
+
+ size = isr->FrameLength;
+ buf = malloc(size + ETHER_ALIGN);
+ if (buf == NULL)
+ return (-1);
+ ptr = buf + ETHER_ALIGN;
+ rsize = 0;
+
+ while (rsize < size) {
+ frame = (char *)((uintptr_t)isr->Frame.segment << 4);
+ frame += isr->Frame.offset;
+ bcopy(PTOV(frame), ptr, isr->BufferLength);
+ ptr += isr->BufferLength;
+ rsize += isr->BufferLength;
+
+ bzero(isr, sizeof(*isr));
+ isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ pxe_call(PXENV_UNDI_ISR);
+ if (isr->Status != 0) {
+ free(buf);
+ return (-1);
+ }
+
+ /* Did we got another update? */
+ if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_RECEIVE)
+ continue;
+ break;
+ }
+
+ *pkt = buf;
+ return (rsize);
+}
+
+static ssize_t
+pxe_netif_get(struct iodesc *desc, void **pkt, time_t timeout)
+{
+ time_t t;
+ void *ptr;
+ int ret = -1;
+
+ t = getsecs();
+ while ((getsecs() - t) < timeout) {
+ ret = pxe_netif_receive(&ptr);
+ if (ret != -1) {
+ *pkt = ptr;
+ break;
+ }
+ }
+ return (ret);
+}
+
+static ssize_t
+pxe_netif_put(struct iodesc *desc, void *pkt, size_t len)
+{
+ t_PXENV_UNDI_TRANSMIT *trans_p;
+ t_PXENV_UNDI_TBD *tbd_p;
+ char *data;
+
+ trans_p = (t_PXENV_UNDI_TRANSMIT *)scratch_buffer;
+ bzero(trans_p, sizeof(*trans_p));
+ tbd_p = (t_PXENV_UNDI_TBD *)(scratch_buffer + sizeof(*trans_p));
+ bzero(tbd_p, sizeof(*tbd_p));
+
+ data = scratch_buffer + sizeof(*trans_p) + sizeof(*tbd_p);
+
+ trans_p->TBD.segment = VTOPSEG(tbd_p);
+ trans_p->TBD.offset = VTOPOFF(tbd_p);
+
+ tbd_p->ImmedLength = len;
+ tbd_p->Xmit.segment = VTOPSEG(data);
+ tbd_p->Xmit.offset = VTOPOFF(data);
+ bcopy(pkt, data, len);
+
+ pxe_call(PXENV_UNDI_TRANSMIT);
+ if (trans_p->Status != 0) {
+ return (-1);
+ }
+
+ return (len);
+}
diff --git a/stand/i386/libi386/pxe.h b/stand/i386/libi386/pxe.h
new file mode 100644
index 0000000..119c86b
--- /dev/null
+++ b/stand/i386/libi386/pxe.h
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org>
+ * All rights reserved.
+ * Copyright (c) 2000 Paul Saab <ps@freebsd.org>
+ * All rights reserved.
+ * Copyright (c) 2000 John Baldwin <jhb@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * The typedefs and structures declared in this file
+ * clearly violate style(9), the reason for this is to conform to the
+ * typedefs/structure-names used in the Intel literature to avoid confusion.
+ *
+ * It's for your own good. :)
+ */
+
+/* It seems that intel didn't think about ABI,
+ * either that or 16bit ABI != 32bit ABI (which seems reasonable)
+ * I have to thank Intel for the hair loss I incurred trying to figure
+ * out why PXE was mis-reading structures I was passing it (at least
+ * from my point of view)
+ *
+ * Solution: use gcc's '__packed' to correctly align
+ * structures passed into PXE
+ * Question: does this really work for PXE's expected ABI?
+ */
+#define PACKED __packed
+
+#define S_SIZE(s) s, sizeof(s) - 1
+
+#define PXENFSROOTPATH "/pxeroot"
+
+typedef struct {
+ uint16_t offset;
+ uint16_t segment;
+} SEGOFF16_t;
+
+typedef struct {
+ uint16_t Seg_Addr;
+ uint32_t Phy_Addr;
+ uint16_t Seg_Size;
+} SEGDESC_t;
+
+typedef uint16_t SEGSEL_t;
+typedef uint16_t PXENV_STATUS_t;
+typedef uint32_t IP4_t;
+typedef uint32_t ADDR32_t;
+typedef uint16_t UDP_PORT_t;
+
+#define MAC_ADDR_LEN 16
+typedef uint8_t MAC_ADDR[MAC_ADDR_LEN];
+
+/* PXENV+ */
+typedef struct {
+ uint8_t Signature[6]; /* 'PXENV+' */
+ uint16_t Version; /* MSB = major, LSB = minor */
+ uint8_t Length; /* structure length */
+ uint8_t Checksum; /* checksum pad */
+ SEGOFF16_t RMEntry; /* SEG:OFF to PXE entry point */
+ /* don't use PMOffset and PMSelector (from the 2.1 PXE manual) */
+ uint32_t PMOffset; /* Protected mode entry */
+ SEGSEL_t PMSelector; /* Protected mode selector */
+ SEGSEL_t StackSeg; /* Stack segment address */
+ uint16_t StackSize; /* Stack segment size (bytes) */
+ SEGSEL_t BC_CodeSeg; /* BC Code segment address */
+ uint16_t BC_CodeSize; /* BC Code segment size (bytes) */
+ SEGSEL_t BC_DataSeg; /* BC Data segment address */
+ uint16_t BC_DataSize; /* BC Data segment size (bytes) */
+ SEGSEL_t UNDIDataSeg; /* UNDI Data segment address */
+ uint16_t UNDIDataSize; /* UNDI Data segment size (bytes) */
+ SEGSEL_t UNDICodeSeg; /* UNDI Code segment address */
+ uint16_t UNDICodeSize; /* UNDI Code segment size (bytes) */
+ SEGOFF16_t PXEPtr; /* SEG:OFF to !PXE struct,
+ only present when Version > 2.1 */
+} PACKED pxenv_t;
+
+/* !PXE */
+typedef struct {
+ uint8_t Signature[4];
+ uint8_t StructLength;
+ uint8_t StructCksum;
+ uint8_t StructRev;
+ uint8_t reserved_1;
+ SEGOFF16_t UNDIROMID;
+ SEGOFF16_t BaseROMID;
+ SEGOFF16_t EntryPointSP;
+ SEGOFF16_t EntryPointESP;
+ SEGOFF16_t StatusCallout;
+ uint8_t reserved_2;
+ uint8_t SegDescCn;
+ SEGSEL_t FirstSelector;
+ SEGDESC_t Stack;
+ SEGDESC_t UNDIData;
+ SEGDESC_t UNDICode;
+ SEGDESC_t UNDICodeWrite;
+ SEGDESC_t BC_Data;
+ SEGDESC_t BC_Code;
+ SEGDESC_t BC_CodeWrite;
+} PACKED pxe_t;
+
+#define PXENV_START_UNDI 0x0000
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t ax;
+ uint16_t bx;
+ uint16_t dx;
+ uint16_t di;
+ uint16_t es;
+} PACKED t_PXENV_START_UNDI;
+
+#define PXENV_UNDI_STARTUP 0x0001
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_STARTUP;
+
+#define PXENV_UNDI_CLEANUP 0x0002
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_CLEANUP;
+
+#define PXENV_UNDI_INITIALIZE 0x0003
+typedef struct {
+ PXENV_STATUS_t Status;
+ ADDR32_t ProtocolIni; /* Phys addr of a copy of the driver module */
+ uint8_t reserved[8];
+} PACKED t_PXENV_UNDI_INITALIZE;
+
+
+#define MAXNUM_MCADDR 8
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t MCastAddrCount;
+ MAC_ADDR McastAddr[MAXNUM_MCADDR];
+} PACKED t_PXENV_UNDI_MCAST_ADDRESS;
+
+#define PXENV_UNDI_RESET_ADAPTER 0x0004
+typedef struct {
+ PXENV_STATUS_t Status;
+ t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED t_PXENV_UNDI_RESET;
+
+#define PXENV_UNDI_SHUTDOWN 0x0005
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_SHUTDOWN;
+
+#define PXENV_UNDI_OPEN 0x0006
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t OpenFlag;
+ uint16_t PktFilter;
+# define FLTR_DIRECTED 0x0001
+# define FLTR_BRDCST 0x0002
+# define FLTR_PRMSCS 0x0004
+# define FLTR_SRC_RTG 0x0008
+
+ t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED t_PXENV_UNDI_OPEN;
+
+#define PXENV_UNDI_CLOSE 0x0007
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_CLOSE;
+
+#define PXENV_UNDI_TRANSMIT 0x0008
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t Protocol;
+# define P_UNKNOWN 0
+# define P_IP 1
+# define P_ARP 2
+# define P_RARP 3
+
+ uint8_t XmitFlag;
+# define XMT_DESTADDR 0x0000
+# define XMT_BROADCAST 0x0001
+
+ SEGOFF16_t DestAddr;
+ SEGOFF16_t TBD;
+ uint32_t Reserved[2];
+} PACKED t_PXENV_UNDI_TRANSMIT;
+
+#define MAX_DATA_BLKS 8
+typedef struct {
+ uint16_t ImmedLength;
+ SEGOFF16_t Xmit;
+ uint16_t DataBlkCount;
+ struct DataBlk {
+ uint8_t TDPtrType;
+ uint8_t TDRsvdByte;
+ uint16_t TDDataLen;
+ SEGOFF16_t TDDataPtr;
+ } DataBlock[MAX_DATA_BLKS];
+} PACKED t_PXENV_UNDI_TBD;
+
+#define PXENV_UNDI_SET_MCAST_ADDRESS 0x0009
+typedef struct {
+ PXENV_STATUS_t Status;
+ t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED t_PXENV_UNDI_SET_MCAST_ADDR;
+
+#define PXENV_UNDI_SET_STATION_ADDRESS 0x000A
+typedef struct {
+ PXENV_STATUS_t Status;
+ MAC_ADDR StationAddress; /* Temp MAC address to use */
+} PACKED t_PXENV_UNDI_SET_STATION_ADDR;
+
+#define PXENV_UNDI_SET_PACKET_FILTER 0x000B
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t filter; /* see UNDI_OPEN (0x0006) */
+} PACKED t_PXENV_UNDI_SET_PACKET_FILTER;
+
+#define PXENV_UNDI_GET_INFORMATION 0x000C
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t BaseIo; /* Adapter base I/O address */
+ uint16_t IntNumber; /* Adapter IRQ number */
+ uint16_t MaxTranUnit; /* Adapter maximum transmit unit */
+ uint16_t HwType; /* Type of protocol at the hardware addr */
+# define ETHER_TYPE 1
+# define EXP_ETHER_TYPE 2
+# define IEEE_TYPE 6
+# define ARCNET_TYPE 7
+
+ uint16_t HwAddrLen; /* Length of hardware address */
+ MAC_ADDR CurrentNodeAddress; /* Current hardware address */
+ MAC_ADDR PermNodeAddress; /* Permanent hardware address */
+ SEGSEL_t ROMAddress; /* Real mode ROM segment address */
+ uint16_t RxBufCt; /* Receive queue length */
+ uint16_t TxBufCt; /* Transmit queue length */
+} PACKED t_PXENV_UNDI_GET_INFORMATION;
+
+#define PXENV_UNDI_GET_STATISTICS 0x000D
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint32_t XmitGoodFrames; /* Number of successful transmissions */
+ uint32_t RcvGoodFrames; /* Number of good frames received */
+ uint32_t RcvCRCErrors; /* Number of frames with CRC errors */
+ uint32_t RcvResourceErrors; /* Number of frames dropped */
+} PACKED t_PXENV_UNDI_GET_STATISTICS;
+
+#define PXENV_UNDI_CLEAR_STATISTICS 0x000E
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_CLEAR_STATISTICS;
+
+#define PXENV_UNDI_INITIATE_DIAGS 0x000F
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_INITIATE_DIAGS;
+
+#define PXENV_UNDI_FORCE_INTERRUPT 0x0010
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_FORCE_INTERRUPT;
+
+#define PXENV_UNDI_GET_MCAST_ADDRESS 0x0011
+typedef struct {
+ PXENV_STATUS_t Status;
+ IP4_t InetAddr; /* IP mulicast address */
+ MAC_ADDR MediaAddr; /* MAC multicast address */
+} PACKED t_PXENV_UNDI_GET_MCAST_ADDR;
+
+#define PXENV_UNDI_GET_NIC_TYPE 0x0012
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t NicType; /* Type of NIC */
+# define PCI_NIC 2
+# define PnP_NIC 3
+# define CardBus_NIC 4
+
+ union {
+ struct {
+ uint16_t Vendor_ID;
+ uint16_t Dev_ID;
+ uint8_t Base_Class;
+ uint8_t Sub_Class;
+ uint8_t Prog_Intf;
+ uint8_t Rev;
+ uint16_t BusDevFunc;
+ uint16_t SubVendor_ID;
+ uint16_t SubDevice_ID;
+ } pci, cardbus;
+ struct {
+ uint32_t EISA_Dev_ID;
+ uint8_t Base_Class;
+ uint8_t Sub_Class;
+ uint8_t Prog_Intf;
+ uint16_t CardSelNum;
+ } pnp;
+ } info;
+} PACKED t_PXENV_UNDI_GET_NIC_TYPE;
+
+#define PXENV_UNDI_GET_IFACE_INFO 0x0013
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t IfaceType[16]; /* Name of MAC type in ASCII. */
+ uint32_t LinkSpeed; /* Defined in NDIS 2.0 spec */
+ uint32_t ServiceFlags; /* Defined in NDIS 2.0 spec */
+ uint32_t Reserved[4]; /* must be 0 */
+} PACKED t_PXENV_UNDI_GET_NDIS_INFO;
+
+#define PXENV_UNDI_ISR 0x0014
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t FuncFlag; /* PXENV_UNDI_ISR_OUT_xxx */
+ uint16_t BufferLength; /* Length of Frame */
+ uint16_t FrameLength; /* Total length of receiver frame */
+ uint16_t FrameHeaderLength; /* Length of the media header in Frame */
+ SEGOFF16_t Frame; /* receive buffer */
+ uint8_t ProtType; /* Protocol type */
+ uint8_t PktType; /* Packet Type */
+# define PXENV_UNDI_ISR_IN_START 1
+# define PXENV_UNDI_ISR_IN_PROCESS 2
+# define PXENV_UNDI_ISR_IN_GET_NEXT 3
+
+ /* one of these will be returned for PXENV_UNDI_ISR_IN_START */
+# define PXENV_UNDI_ISR_OUT_OURS 0
+# define PXENV_UNDI_ISR_OUT_NOT_OUTS 1
+
+ /*
+ * one of these will bre returned for PXEND_UNDI_ISR_IN_PROCESS
+ * and PXENV_UNDI_ISR_IN_GET_NEXT
+ */
+# define PXENV_UNDI_ISR_OUT_DONE 0
+# define PXENV_UNDI_ISR_OUT_TRANSMIT 2
+# define PXENV_UNDI_ISR_OUT_RECEIVE 3
+# define PXENV_UNDI_ISR_OUT_BUSY 4
+} PACKED t_PXENV_UNDI_ISR;
+
+#define PXENV_STOP_UNDI 0x0015
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_STOP_UNDI;
+
+#define PXENV_TFTP_OPEN 0x0020
+typedef struct {
+ PXENV_STATUS_t Status;
+ IP4_t ServerIPAddress;
+ IP4_t GatewayIPAddress;
+ uint8_t FileName[128];
+ UDP_PORT_t TFTPPort;
+ uint16_t PacketSize;
+} PACKED t_PXENV_TFTP_OPEN;
+
+#define PXENV_TFTP_CLOSE 0x0021
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_TFTP_CLOSE;
+
+#define PXENV_TFTP_READ 0x0022
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t PacketNumber;
+ uint16_t BufferSize;
+ SEGOFF16_t Buffer;
+} PACKED t_PXENV_TFTP_READ;
+
+#define PXENV_TFTP_READ_FILE 0x0023
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t FileName[128];
+ uint32_t BufferSize;
+ ADDR32_t Buffer;
+ IP4_t ServerIPAddress;
+ IP4_t GatewayIPAdress;
+ IP4_t McastIPAdress;
+ UDP_PORT_t TFTPClntPort;
+ UDP_PORT_t TFTPSrvPort;
+ uint16_t TFTPOpenTimeOut;
+ uint16_t TFTPReopenDelay;
+} PACKED t_PXENV_TFTP_READ_FILE;
+
+#define PXENV_TFTP_GET_FSIZE 0x0025
+typedef struct {
+ PXENV_STATUS_t Status;
+ IP4_t ServerIPAddress;
+ IP4_t GatewayIPAdress;
+ uint8_t FileName[128];
+ uint32_t FileSize;
+} PACKED t_PXENV_TFTP_GET_FSIZE;
+
+#define PXENV_UDP_OPEN 0x0030
+typedef struct {
+ PXENV_STATUS_t status;
+ IP4_t src_ip; /* IP address of this station */
+} PACKED t_PXENV_UDP_OPEN;
+
+#define PXENV_UDP_CLOSE 0x0031
+typedef struct {
+ PXENV_STATUS_t status;
+} PACKED t_PXENV_UDP_CLOSE;
+
+#define PXENV_UDP_READ 0x0032
+typedef struct {
+ PXENV_STATUS_t status;
+ IP4_t src_ip; /* IP of sender */
+ IP4_t dest_ip; /* Only accept packets sent to this IP */
+ UDP_PORT_t s_port; /* UDP source port of sender */
+ UDP_PORT_t d_port; /* Only accept packets sent to this port */
+ uint16_t buffer_size; /* Size of the packet buffer */
+ SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */
+} PACKED t_PXENV_UDP_READ;
+
+#define PXENV_UDP_WRITE 0x0033
+typedef struct {
+ PXENV_STATUS_t status;
+ IP4_t ip; /* dest ip addr */
+ IP4_t gw; /* ip gateway */
+ UDP_PORT_t src_port; /* source udp port */
+ UDP_PORT_t dst_port; /* destination udp port */
+ uint16_t buffer_size; /* Size of the packet buffer */
+ SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */
+} PACKED t_PXENV_UDP_WRITE;
+
+#define PXENV_UNLOAD_STACK 0x0070
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t reserved[10];
+} PACKED t_PXENV_UNLOAD_STACK;
+
+
+#define PXENV_GET_CACHED_INFO 0x0071
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t PacketType; /* type (defined right here) */
+# define PXENV_PACKET_TYPE_DHCP_DISCOVER 1
+# define PXENV_PACKET_TYPE_DHCP_ACK 2
+# define PXENV_PACKET_TYPE_BINL_REPLY 3
+ uint16_t BufferSize; /* max to copy, leave at 0 for pointer */
+ SEGOFF16_t Buffer; /* copy to, leave at 0 for pointer */
+ uint16_t BufferLimit; /* max size of buffer in BC dataseg ? */
+} PACKED t_PXENV_GET_CACHED_INFO;
+
+
+/* structure filled in by PXENV_GET_CACHED_INFO
+ * (how we determine which IP we downloaded the initial bootstrap from)
+ * words can't describe...
+ */
+typedef struct {
+ uint8_t opcode;
+# define BOOTP_REQ 1
+# define BOOTP_REP 2
+ uint8_t Hardware; /* hardware type */
+ uint8_t Hardlen; /* hardware addr len */
+ uint8_t Gatehops; /* zero it */
+ uint32_t ident; /* random number chosen by client */
+ uint16_t seconds; /* seconds since did initial bootstrap */
+ uint16_t Flags; /* seconds since did initial bootstrap */
+# define BOOTP_BCAST 0x8000 /* ? */
+ IP4_t cip; /* Client IP */
+ IP4_t yip; /* Your IP */
+ IP4_t sip; /* IP to use for next boot stage */
+ IP4_t gip; /* Relay IP ? */
+ MAC_ADDR CAddr; /* Client hardware address */
+ uint8_t Sname[64]; /* Server's hostname (Optional) */
+ uint8_t bootfile[128]; /* boot filename */
+ union {
+# if 1
+# define BOOTP_DHCPVEND 1024 /* DHCP extended vendor field size */
+# else
+# define BOOTP_DHCPVEND 312 /* DHCP standard vendor field size */
+# endif
+ uint8_t d[BOOTP_DHCPVEND]; /* raw array of vendor/dhcp options */
+ struct {
+ uint8_t magic[4]; /* DHCP magic cookie */
+# ifndef VM_RFC1048
+# define VM_RFC1048 0x63825363L /* ? */
+# endif
+ uint32_t flags; /* bootp flags/opcodes */
+ uint8_t pad[56]; /* I don't think intel knows what a
+ union does... */
+ } v;
+ } vendor;
+} PACKED BOOTPLAYER;
+
+#define PXENV_RESTART_TFTP 0x0073
+#define t_PXENV_RESTART_TFTP t_PXENV_TFTP_READ_FILE
+
+#define PXENV_START_BASE 0x0075
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_START_BASE;
+
+#define PXENV_STOP_BASE 0x0076
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_STOP_BASE;
diff --git a/stand/i386/libi386/pxetramp.s b/stand/i386/libi386/pxetramp.s
new file mode 100644
index 0000000..dcf1441
--- /dev/null
+++ b/stand/i386/libi386/pxetramp.s
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2000 Peter Wemm
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+# $FreeBSD$
+
+# ph33r this
+
+ .globl __bangpxeentry, __bangpxeseg, __bangpxeoff
+ .globl __pxenventry, __pxenvseg, __pxenvoff
+
+ .code16
+ .p2align 4,0x90
+__bangpxeentry:
+ push %dx # seg:data
+ push %ax # off:data
+ push %bx # int16 func
+ .byte 0x9a # far call
+__bangpxeoff: .word 0x0000 # offset
+__bangpxeseg: .word 0x0000 # segment
+ add $6, %sp # restore stack
+ .byte 0xcb # to vm86int
+#
+__pxenventry:
+ .byte 0x9a # far call
+__pxenvoff: .word 0x0000 # offset
+__pxenvseg: .word 0x0000 # segment
+ .byte 0xcb # to vm86int
diff --git a/stand/i386/libi386/relocater_tramp.S b/stand/i386/libi386/relocater_tramp.S
new file mode 100644
index 0000000..6db4a75
--- /dev/null
+++ b/stand/i386/libi386/relocater_tramp.S
@@ -0,0 +1,358 @@
+/*-
+ * Copyright 2015 Toomas Soome <tsoome@me.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+/*
+ * relocate is needed to support loading code which has to be located
+ * below 1MB, as both BTX and loader are using low memory area.
+ *
+ * relocate and start loaded code. Since loaded code may need to be
+ * placed to already occupied memory area, this code is moved to safe
+ * memory area and then btx __exec will be called with physical pointer
+ * to this area. __exec will set pointer to %eax and use call *%eax,
+ * so on entry, we have new "base" address in %eax.
+ *
+ * Relocate will first set up and load new safe GDT to shut down BTX,
+ * then loaded code will be relocated to final memory location,
+ * then machine will be switched from 32bit protected mode to 16bit
+ * protected mode following by switch to real mode with A20 enabled or
+ * disabled. Finally the loaded code will be started and it will take
+ * over the whole system.
+ *
+ * For now, the known "safe" memory area for relocate is 0x600,
+ * the actual "free" memory is supposed to start from 0x500, leaving
+ * first 0x100 bytes in reserve. As relocate code+data is very small,
+ * it will leave enough space to set up boot blocks to 0:7c00 or load
+ * linux kernel below 1MB space.
+ */
+/*
+ * segment selectors
+ */
+ .set SEL_SCODE,0x8
+ .set SEL_SDATA,0x10
+ .set SEL_RCODE,0x18
+ .set SEL_RDATA,0x20
+
+ .p2align 4
+ .globl relocater
+relocater:
+ cli
+ /*
+ * set up GDT from new location
+ */
+ movl %eax, %esi /* our base address */
+ add $(relocater.1-relocater), %eax
+ jmp *%eax
+relocater.1:
+ /* set up jump */
+ lea (relocater.2-relocater)(%esi), %eax
+ movl %eax, (jump_vector-relocater) (%esi)
+
+ /* set up gdt */
+ lea (gdt-relocater) (%esi), %eax
+ movl %eax, (gdtaddr-relocater) (%esi)
+
+ /* load gdt */
+ lgdt (gdtdesc - relocater) (%esi)
+ lidt (idt-relocater) (%esi)
+
+ /* update cs */
+ ljmp *(jump_vector-relocater) (%esi)
+
+ .code32
+relocater.2:
+ xorl %eax, %eax
+ movb $SEL_SDATA, %al
+ movw %ax, %ss
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movl %cr0, %eax /* disable paging */
+ andl $~0x80000000,%eax
+ movl %eax, %cr0
+ xorl %ecx, %ecx /* flush TLB */
+ movl %ecx, %cr3
+ cld
+/*
+ * relocate data loop. load source, dest and size from
+ * relocater_data[i], 0 value will stop the loop.
+ * registers used for move: %esi, %edi, %ecx.
+ * %ebx to keep base
+ * %edx for relocater_data offset
+ */
+ movl %esi, %ebx /* base address */
+ xorl %edx, %edx
+loop.1:
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %eax
+ testl %eax, %eax
+ jz loop.2
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %esi
+ inc %edx
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %edi
+ inc %edx
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %ecx
+ inc %edx
+ rep
+ movsb
+ jmp loop.1
+loop.2:
+ movl %ebx, %esi /* restore esi */
+ /*
+ * data is relocated, switch to 16bit mode
+ */
+ lea (relocater.3-relocater)(%esi), %eax
+ movl %eax, (jump_vector-relocater) (%esi)
+ movl $SEL_RCODE, %eax
+ movl %eax, (jump_vector-relocater+4) (%esi)
+
+ ljmp *(jump_vector-relocater) (%esi)
+relocater.3:
+ .code16
+
+ movw $SEL_RDATA, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ lidt (idt-relocater) (%esi)
+ lea (relocater.4-relocater)(%esi), %eax
+ movl %eax, (jump_vector-relocater) (%esi)
+ xorl %eax, %eax
+ movl %eax, (jump_vector-relocater+4) (%esi)
+ /* clear PE */
+ movl %cr0, %eax
+ dec %al
+ movl %eax, %cr0
+ ljmp *(jump_vector-relocater) (%esi)
+relocater.4:
+ xorw %ax, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ /*
+ * set real mode irq offsets
+ */
+ movw $0x7008,%bx
+ in $0x21,%al # Save master
+ push %ax # IMR
+ in $0xa1,%al # Save slave
+ push %ax # IMR
+ movb $0x11,%al # ICW1 to
+ outb %al,$0x20 # master,
+ outb %al,$0xa0 # slave
+ movb %bl,%al # ICW2 to
+ outb %al,$0x21 # master
+ movb %bh,%al # ICW2 to
+ outb %al,$0xa1 # slave
+ movb $0x4,%al # ICW3 to
+ outb %al,$0x21 # master
+ movb $0x2,%al # ICW3 to
+ outb %al,$0xa1 # slave
+ movb $0x1,%al # ICW4 to
+ outb %al,$0x21 # master,
+ outb %al,$0xa1 # slave
+ pop %ax # Restore slave
+ outb %al,$0xa1 # IMR
+ pop %ax # Restore master
+ outb %al,$0x21 # IMR
+ # done
+ /*
+ * Should A20 be left enabled?
+ */
+ /* movw imm16, %ax */
+ .byte 0xb8
+ .globl relocator_a20_enabled
+relocator_a20_enabled:
+ .word 0
+ test %ax, %ax
+ jnz a20_done
+
+ movw $0xa00, %ax
+ movw %ax, %sp
+ movw %ax, %bp
+
+ /* Disable A20 */
+ movw $0x2400, %ax
+ int $0x15
+# jnc a20_done
+
+ call a20_check_state
+ testb %al, %al
+ jz a20_done
+
+ inb $0x92
+ andb $(~0x03), %al
+ outb $0x92
+ jmp a20_done
+
+a20_check_state:
+ movw $100, %cx
+1:
+ xorw %ax, %ax
+ movw %ax, %ds
+ decw %ax
+ movw %ax, %es
+ xorw %ax, %ax
+ movw $0x8000, %ax
+ movw %ax, %si
+ addw $0x10, %ax
+ movw %ax, %di
+ movb %ds:(%si), %dl
+ movb %es:(%di), %al
+ movb %al, %dh
+ decb %dh
+ movb %dh, %ds:(%si)
+ outb %al, $0x80
+ outb %al, $0x80
+ movb %es:(%di), %dh
+ subb %dh, %al
+ xorb $1, %al
+ movb %dl, %ds:(%si)
+ testb %al, %al
+ jz a20_done
+ loop 1b
+ ret
+a20_done:
+ /*
+ * set up registers
+ */
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_ds
+relocator_ds: .word 0
+ movw %ax, %ds
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_es
+relocator_es: .word 0
+ movw %ax, %es
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_fs
+relocator_fs: .word 0
+ movw %ax, %fs
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_gs
+relocator_gs: .word 0
+ movw %ax, %gs
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_ss
+relocator_ss: .word 0
+ movw %ax, %ss
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_sp
+relocator_sp: .word 0
+ movzwl %ax, %esp
+
+ /* movw imm32, %eax. */
+ .byte 0x66, 0xb8
+ .globl relocator_esi
+relocator_esi: .long 0
+ movl %eax, %esi
+
+ /* movw imm32, %edx. */
+ .byte 0x66, 0xba
+ .globl relocator_edx
+relocator_edx: .long 0
+
+ /* movw imm32, %ebx. */
+ .byte 0x66, 0xbb
+ .globl relocator_ebx
+relocator_ebx: .long 0
+
+ /* movw imm32, %eax. */
+ .byte 0x66, 0xb8
+ .globl relocator_eax
+relocator_eax: .long 0
+
+ /* movw imm32, %ebp. */
+ .byte 0x66, 0xbd
+ .globl relocator_ebp
+relocator_ebp: .long 0
+
+ sti
+ .byte 0xea /* ljmp */
+ .globl relocator_ip
+relocator_ip:
+ .word 0
+ .globl relocator_cs
+relocator_cs:
+ .word 0
+
+/* GDT to reset BTX */
+ .code32
+ .p2align 4
+jump_vector: .long 0
+ .long SEL_SCODE
+
+gdt: .word 0x0, 0x0 /* null entry */
+ .byte 0x0, 0x0, 0x0, 0x0
+ .word 0xffff, 0x0 /* SEL_SCODE */
+ .byte 0x0, 0x9a, 0xcf, 0x0
+ .word 0xffff, 0x0 /* SEL_SDATA */
+ .byte 0x0, 0x92, 0xcf, 0x0
+ .word 0xffff, 0x0 /* SEL_RCODE */
+ .byte 0x0, 0x9a, 0x0f, 0x0
+ .word 0xffff, 0x0 /* SEL_RDATA */
+ .byte 0x0, 0x92, 0x0f, 0x0
+gdt.1:
+
+gdtdesc: .word gdt.1 - gdt - 1 /* limit */
+gdtaddr: .long 0 /* base */
+
+idt: .word 0x3ff
+ .long 0
+
+ .globl relocater_data
+relocater_data:
+ .long 0 /* src */
+ .long 0 /* dest */
+ .long 0 /* size */
+ .long 0 /* src */
+ .long 0 /* dest */
+ .long 0 /* size */
+ .long 0 /* src */
+ .long 0 /* dest */
+ .long 0 /* size */
+ .long 0
+
+ .globl relocater_size
+relocater_size:
+ .long relocater_size-relocater
diff --git a/stand/i386/libi386/smbios.c b/stand/i386/libi386/smbios.c
new file mode 100644
index 0000000..2aa62fa
--- /dev/null
+++ b/stand/i386/libi386/smbios.c
@@ -0,0 +1,454 @@
+/*-
+ * Copyright (c) 2005-2009 Jung-uk Kim <jkim@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include <sys/endian.h>
+
+#ifdef EFI
+/* In EFI, we don't need PTOV(). */
+#define PTOV(x) (caddr_t)(x)
+#else
+#include "btxv86.h"
+#endif
+#include "smbios.h"
+
+/*
+ * Detect SMBIOS and export information about the SMBIOS into the
+ * environment.
+ *
+ * System Management BIOS Reference Specification, v2.6 Final
+ * http://www.dmtf.org/standards/published_documents/DSP0134_2.6.0.pdf
+ */
+
+/*
+ * 2.1.1 SMBIOS Structure Table Entry Point
+ *
+ * "On non-EFI systems, the SMBIOS Entry Point structure, described below, can
+ * be located by application software by searching for the anchor-string on
+ * paragraph (16-byte) boundaries within the physical memory address range
+ * 000F0000h to 000FFFFFh. This entry point encapsulates an intermediate anchor
+ * string that is used by some existing DMI browsers."
+ */
+#define SMBIOS_START 0xf0000
+#define SMBIOS_LENGTH 0x10000
+#define SMBIOS_STEP 0x10
+#define SMBIOS_SIG "_SM_"
+#define SMBIOS_DMI_SIG "_DMI_"
+
+#define SMBIOS_GET8(base, off) (*(uint8_t *)((base) + (off)))
+#define SMBIOS_GET16(base, off) (*(uint16_t *)((base) + (off)))
+#define SMBIOS_GET32(base, off) (*(uint32_t *)((base) + (off)))
+
+#define SMBIOS_GETLEN(base) SMBIOS_GET8(base, 0x01)
+#define SMBIOS_GETSTR(base) ((base) + SMBIOS_GETLEN(base))
+
+struct smbios_attr {
+ int probed;
+ caddr_t addr;
+ size_t length;
+ size_t count;
+ int major;
+ int minor;
+ int ver;
+ const char* bios_vendor;
+ const char* maker;
+ const char* product;
+ uint32_t enabled_memory;
+ uint32_t old_enabled_memory;
+ uint8_t enabled_sockets;
+ uint8_t populated_sockets;
+};
+
+static struct smbios_attr smbios;
+
+static uint8_t
+smbios_checksum(const caddr_t addr, const uint8_t len)
+{
+ uint8_t sum;
+ int i;
+
+ for (sum = 0, i = 0; i < len; i++)
+ sum += SMBIOS_GET8(addr, i);
+ return (sum);
+}
+
+static caddr_t
+smbios_sigsearch(const caddr_t addr, const uint32_t len)
+{
+ caddr_t cp;
+
+ /* Search on 16-byte boundaries. */
+ for (cp = addr; cp < addr + len; cp += SMBIOS_STEP)
+ if (strncmp(cp, SMBIOS_SIG, 4) == 0 &&
+ smbios_checksum(cp, SMBIOS_GET8(cp, 0x05)) == 0 &&
+ strncmp(cp + 0x10, SMBIOS_DMI_SIG, 5) == 0 &&
+ smbios_checksum(cp + 0x10, 0x0f) == 0)
+ return (cp);
+ return (NULL);
+}
+
+static const char*
+smbios_getstring(caddr_t addr, const int offset)
+{
+ caddr_t cp;
+ int i, idx;
+
+ idx = SMBIOS_GET8(addr, offset);
+ if (idx != 0) {
+ cp = SMBIOS_GETSTR(addr);
+ for (i = 1; i < idx; i++)
+ cp += strlen(cp) + 1;
+ return cp;
+ }
+ return (NULL);
+}
+
+static void
+smbios_setenv(const char *name, caddr_t addr, const int offset)
+{
+ const char* val;
+
+ val = smbios_getstring(addr, offset);
+ if (val != NULL)
+ setenv(name, val, 1);
+}
+
+#ifdef SMBIOS_SERIAL_NUMBERS
+
+#define UUID_SIZE 16
+#define UUID_TYPE uint32_t
+#define UUID_STEP sizeof(UUID_TYPE)
+#define UUID_ALL_BITS (UUID_SIZE / UUID_STEP)
+#define UUID_GET(base, off) (*(UUID_TYPE *)((base) + (off)))
+
+static void
+smbios_setuuid(const char *name, const caddr_t addr, const int ver)
+{
+ char uuid[37];
+ int byteorder, i, ones, zeros;
+ UUID_TYPE n;
+ uint32_t f1;
+ uint16_t f2, f3;
+
+ for (i = 0, ones = 0, zeros = 0; i < UUID_SIZE; i += UUID_STEP) {
+ n = UUID_GET(addr, i) + 1;
+ if (zeros == 0 && n == 0)
+ ones++;
+ else if (ones == 0 && n == 1)
+ zeros++;
+ else
+ break;
+ }
+
+ if (ones != UUID_ALL_BITS && zeros != UUID_ALL_BITS) {
+ /*
+ * 3.3.2.1 System UUID
+ *
+ * "Although RFC 4122 recommends network byte order for all
+ * fields, the PC industry (including the ACPI, UEFI, and
+ * Microsoft specifications) has consistently used
+ * little-endian byte encoding for the first three fields:
+ * time_low, time_mid, time_hi_and_version. The same encoding,
+ * also known as wire format, should also be used for the
+ * SMBIOS representation of the UUID."
+ *
+ * Note: We use network byte order for backward compatibility
+ * unless SMBIOS version is 2.6+ or little-endian is forced.
+ */
+#if defined(SMBIOS_LITTLE_ENDIAN_UUID)
+ byteorder = LITTLE_ENDIAN;
+#elif defined(SMBIOS_NETWORK_ENDIAN_UUID)
+ byteorder = BIG_ENDIAN;
+#else
+ byteorder = ver < 0x0206 ? BIG_ENDIAN : LITTLE_ENDIAN;
+#endif
+ if (byteorder != LITTLE_ENDIAN) {
+ f1 = ntohl(SMBIOS_GET32(addr, 0));
+ f2 = ntohs(SMBIOS_GET16(addr, 4));
+ f3 = ntohs(SMBIOS_GET16(addr, 6));
+ } else {
+ f1 = le32toh(SMBIOS_GET32(addr, 0));
+ f2 = le16toh(SMBIOS_GET16(addr, 4));
+ f3 = le16toh(SMBIOS_GET16(addr, 6));
+ }
+ sprintf(uuid,
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ f1, f2, f3, SMBIOS_GET8(addr, 8), SMBIOS_GET8(addr, 9),
+ SMBIOS_GET8(addr, 10), SMBIOS_GET8(addr, 11),
+ SMBIOS_GET8(addr, 12), SMBIOS_GET8(addr, 13),
+ SMBIOS_GET8(addr, 14), SMBIOS_GET8(addr, 15));
+ setenv(name, uuid, 1);
+ }
+}
+
+#undef UUID_SIZE
+#undef UUID_TYPE
+#undef UUID_STEP
+#undef UUID_ALL_BITS
+#undef UUID_GET
+
+#endif
+
+static caddr_t
+smbios_parse_table(const caddr_t addr)
+{
+ caddr_t cp;
+ int proc, size, osize, type;
+
+ type = SMBIOS_GET8(addr, 0); /* 3.1.2 Structure Header Format */
+ switch(type) {
+ case 0: /* 3.3.1 BIOS Information (Type 0) */
+ smbios_setenv("smbios.bios.vendor", addr, 0x04);
+ smbios_setenv("smbios.bios.version", addr, 0x05);
+ smbios_setenv("smbios.bios.reldate", addr, 0x08);
+ break;
+
+ case 1: /* 3.3.2 System Information (Type 1) */
+ smbios_setenv("smbios.system.maker", addr, 0x04);
+ smbios_setenv("smbios.system.product", addr, 0x05);
+ smbios_setenv("smbios.system.version", addr, 0x06);
+#ifdef SMBIOS_SERIAL_NUMBERS
+ smbios_setenv("smbios.system.serial", addr, 0x07);
+ smbios_setuuid("smbios.system.uuid", addr + 0x08, smbios.ver);
+#endif
+ if (smbios.major > 2 ||
+ (smbios.major == 2 && smbios.minor >= 4)) {
+ smbios_setenv("smbios.system.sku", addr, 0x19);
+ smbios_setenv("smbios.system.family", addr, 0x1a);
+ }
+ break;
+
+ case 2: /* 3.3.3 Base Board (or Module) Information (Type 2) */
+ smbios_setenv("smbios.planar.maker", addr, 0x04);
+ smbios_setenv("smbios.planar.product", addr, 0x05);
+ smbios_setenv("smbios.planar.version", addr, 0x06);
+#ifdef SMBIOS_SERIAL_NUMBERS
+ smbios_setenv("smbios.planar.serial", addr, 0x07);
+ smbios_setenv("smbios.planar.tag", addr, 0x08);
+#endif
+ smbios_setenv("smbios.planar.location", addr, 0x0a);
+ break;
+
+ case 3: /* 3.3.4 System Enclosure or Chassis (Type 3) */
+ smbios_setenv("smbios.chassis.maker", addr, 0x04);
+ smbios_setenv("smbios.chassis.version", addr, 0x06);
+#ifdef SMBIOS_SERIAL_NUMBERS
+ smbios_setenv("smbios.chassis.serial", addr, 0x07);
+ smbios_setenv("smbios.chassis.tag", addr, 0x08);
+#endif
+ break;
+
+ case 4: /* 3.3.5 Processor Information (Type 4) */
+ /*
+ * Offset 18h: Processor Status
+ *
+ * Bit 7 Reserved, must be 0
+ * Bit 6 CPU Socket Populated
+ * 1 - CPU Socket Populated
+ * 0 - CPU Socket Unpopulated
+ * Bit 5:3 Reserved, must be zero
+ * Bit 2:0 CPU Status
+ * 0h - Unknown
+ * 1h - CPU Enabled
+ * 2h - CPU Disabled by User via BIOS Setup
+ * 3h - CPU Disabled by BIOS (POST Error)
+ * 4h - CPU is Idle, waiting to be enabled
+ * 5-6h - Reserved
+ * 7h - Other
+ */
+ proc = SMBIOS_GET8(addr, 0x18);
+ if ((proc & 0x07) == 1)
+ smbios.enabled_sockets++;
+ if ((proc & 0x40) != 0)
+ smbios.populated_sockets++;
+ break;
+
+ case 6: /* 3.3.7 Memory Module Information (Type 6, Obsolete) */
+ /*
+ * Offset 0Ah: Enabled Size
+ *
+ * Bit 7 Bank connection
+ * 1 - Double-bank connection
+ * 0 - Single-bank connection
+ * Bit 6:0 Size (n), where 2**n is the size in MB
+ * 7Dh - Not determinable (Installed Size only)
+ * 7Eh - Module is installed, but no memory
+ * has been enabled
+ * 7Fh - Not installed
+ */
+ osize = SMBIOS_GET8(addr, 0x0a) & 0x7f;
+ if (osize > 0 && osize < 22)
+ smbios.old_enabled_memory += 1 << (osize + 10);
+ break;
+
+ case 17: /* 3.3.18 Memory Device (Type 17) */
+ /*
+ * Offset 0Ch: Size
+ *
+ * Bit 15 Granularity
+ * 1 - Value is in kilobytes units
+ * 0 - Value is in megabytes units
+ * Bit 14:0 Size
+ */
+ size = SMBIOS_GET16(addr, 0x0c);
+ if (size != 0 && size != 0xffff)
+ smbios.enabled_memory += (size & 0x8000) != 0 ?
+ (size & 0x7fff) : (size << 10);
+ break;
+
+ default: /* skip other types */
+ break;
+ }
+
+ /* Find structure terminator. */
+ cp = SMBIOS_GETSTR(addr);
+ while (SMBIOS_GET16(cp, 0) != 0)
+ cp++;
+
+ return (cp + 2);
+}
+
+static caddr_t
+smbios_find_struct(int type)
+{
+ caddr_t dmi;
+ size_t i;
+
+ if (smbios.addr == NULL)
+ return (NULL);
+
+ for (dmi = smbios.addr, i = 0;
+ dmi < smbios.addr + smbios.length && i < smbios.count; i++) {
+ if (SMBIOS_GET8(dmi, 0) == type)
+ return dmi;
+ /* Find structure terminator. */
+ dmi = SMBIOS_GETSTR(dmi);
+ while (SMBIOS_GET16(dmi, 0) != 0)
+ dmi++;
+ dmi += 2;
+ }
+
+ return (NULL);
+}
+
+static void
+smbios_probe(const caddr_t addr)
+{
+ caddr_t saddr, info;
+ uintptr_t paddr;
+
+ if (smbios.probed)
+ return;
+ smbios.probed = 1;
+
+ /* Search signatures and validate checksums. */
+ saddr = smbios_sigsearch(addr ? addr : PTOV(SMBIOS_START),
+ SMBIOS_LENGTH);
+ if (saddr == NULL)
+ return;
+
+ smbios.length = SMBIOS_GET16(saddr, 0x16); /* Structure Table Length */
+ paddr = SMBIOS_GET32(saddr, 0x18); /* Structure Table Address */
+ smbios.count = SMBIOS_GET16(saddr, 0x1c); /* No of SMBIOS Structures */
+ smbios.ver = SMBIOS_GET8(saddr, 0x1e); /* SMBIOS BCD Revision */
+
+ if (smbios.ver != 0) {
+ smbios.major = smbios.ver >> 4;
+ smbios.minor = smbios.ver & 0x0f;
+ if (smbios.major > 9 || smbios.minor > 9)
+ smbios.ver = 0;
+ }
+ if (smbios.ver == 0) {
+ smbios.major = SMBIOS_GET8(saddr, 0x06);/* SMBIOS Major Version */
+ smbios.minor = SMBIOS_GET8(saddr, 0x07);/* SMBIOS Minor Version */
+ }
+ smbios.ver = (smbios.major << 8) | smbios.minor;
+ smbios.addr = PTOV(paddr);
+
+ /* Get system information from SMBIOS */
+ info = smbios_find_struct(0x00);
+ if (info != NULL) {
+ smbios.bios_vendor = smbios_getstring(info, 0x04);
+ }
+ info = smbios_find_struct(0x01);
+ if (info != NULL) {
+ smbios.maker = smbios_getstring(info, 0x04);
+ smbios.product = smbios_getstring(info, 0x05);
+ }
+}
+
+void
+smbios_detect(const caddr_t addr)
+{
+ char buf[16];
+ caddr_t dmi;
+ size_t i;
+
+ smbios_probe(addr);
+ if (smbios.addr == NULL)
+ return;
+
+ for (dmi = smbios.addr, i = 0;
+ dmi < smbios.addr + smbios.length && i < smbios.count; i++)
+ dmi = smbios_parse_table(dmi);
+
+ sprintf(buf, "%d.%d", smbios.major, smbios.minor);
+ setenv("smbios.version", buf, 1);
+ if (smbios.enabled_memory > 0 || smbios.old_enabled_memory > 0) {
+ sprintf(buf, "%u", smbios.enabled_memory > 0 ?
+ smbios.enabled_memory : smbios.old_enabled_memory);
+ setenv("smbios.memory.enabled", buf, 1);
+ }
+ if (smbios.enabled_sockets > 0) {
+ sprintf(buf, "%u", smbios.enabled_sockets);
+ setenv("smbios.socket.enabled", buf, 1);
+ }
+ if (smbios.populated_sockets > 0) {
+ sprintf(buf, "%u", smbios.populated_sockets);
+ setenv("smbios.socket.populated", buf, 1);
+ }
+}
+
+static int
+smbios_match_str(const char* s1, const char* s2)
+{
+ return (s1 == NULL || (s2 != NULL && !strcmp(s1, s2)));
+}
+
+int
+smbios_match(const char* bios_vendor, const char* maker,
+ const char* product)
+{
+ /* XXXRP currently, only called from non-EFI. */
+ smbios_probe(NULL);
+ return (smbios_match_str(bios_vendor, smbios.bios_vendor) &&
+ smbios_match_str(maker, smbios.maker) &&
+ smbios_match_str(product, smbios.product));
+}
diff --git a/stand/i386/libi386/smbios.h b/stand/i386/libi386/smbios.h
new file mode 100644
index 0000000..03fc07e
--- /dev/null
+++ b/stand/i386/libi386/smbios.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2015 Rui Paulo <rpaulo@FreeBSD.org>
+ * 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 ``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.
+ *
+ * $FreeBSD$
+ */
+#ifndef _SMBIOS_H_
+#define _SMBIOS_H_
+
+void smbios_detect(const caddr_t);
+int smbios_match(const char *, const char *, const char *);
+
+#endif /* _SMBIOS_H_ */
diff --git a/stand/i386/libi386/spinconsole.c b/stand/i386/libi386/spinconsole.c
new file mode 100644
index 0000000..1daac35
--- /dev/null
+++ b/stand/i386/libi386/spinconsole.c
@@ -0,0 +1,112 @@
+/*-
+ * spinconsole.c
+ *
+ * Author: Maksym Sobolyev <sobomax@sippysoft.com>
+ * Copyright (c) 2009 Sippy Software, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+
+static void spinc_probe(struct console *cp);
+static int spinc_init(int arg);
+static void spinc_putchar(int c);
+static int spinc_getchar(void);
+static int spinc_ischar(void);
+
+extern struct console *consoles[];
+
+struct console spinconsole = {
+ "spinconsole",
+ "spin port",
+ 0,
+ spinc_probe,
+ spinc_init,
+ spinc_putchar,
+ spinc_getchar,
+ spinc_ischar
+};
+
+static struct console *parent = NULL;
+
+static void
+spinc_probe(struct console *cp)
+{
+
+ if (parent == NULL)
+ parent = consoles[0];
+ parent->c_probe(cp);
+}
+
+static int
+spinc_init(int arg)
+{
+
+ return(parent->c_init(arg));
+}
+
+static void
+spinc_putchar(int c)
+{
+ static unsigned tw_chars = 0x5C2D2F7C; /* "\-/|" */
+ static time_t lasttime = 0;
+ time_t now;
+
+ now = time(0);
+ if (now < (lasttime + 1))
+ return;
+#ifdef TERM_EMU
+ if (lasttime > 0)
+ parent->c_out('\b');
+#endif
+ lasttime = now;
+ parent->c_out((char)tw_chars);
+ tw_chars = (tw_chars >> 8) | ((tw_chars & (unsigned long)0xFF) << 24);
+}
+
+static int
+spinc_getchar(void)
+{
+
+ return(-1);
+}
+
+static int
+spinc_ischar(void)
+{
+
+ return(0);
+}
diff --git a/stand/i386/libi386/time.c b/stand/i386/libi386/time.c
new file mode 100644
index 0000000..7636ace
--- /dev/null
+++ b/stand/i386/libi386/time.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <btxv86.h>
+#include "bootstrap.h"
+#include "libi386.h"
+
+time_t getsecs(void);
+static int bios_seconds(void);
+
+/*
+ * Return the BIOS time-of-day value.
+ *
+ * XXX uses undocumented BCD support from libstand.
+ */
+static int
+bios_seconds(void)
+{
+ int hr, minute, sec;
+
+ v86.ctl = 0;
+ v86.addr = 0x1a; /* int 0x1a, function 2 */
+ v86.eax = 0x0200;
+ v86int();
+
+ hr = bcd2bin((v86.ecx & 0xff00) >> 8); /* hour in %ch */
+ minute = bcd2bin(v86.ecx & 0xff); /* minute in %cl */
+ sec = bcd2bin((v86.edx & 0xff00) >> 8); /* second in %dh */
+
+ return (hr * 3600 + minute * 60 + sec);
+}
+
+/*
+ * Return the time in seconds since the beginning of the day.
+ *
+ * Some BIOSes (notably qemu) don't correctly read the RTC
+ * registers in an atomic way, sometimes returning bogus values.
+ * Therefore we "debounce" the reading by accepting it only when
+ * we got 8 identical values in succession.
+ *
+ * If we pass midnight, don't wrap back to 0.
+ */
+time_t
+time(time_t *t)
+{
+ static time_t lasttime;
+ time_t now, check;
+ int same, try;
+
+ same = try = 0;
+ check = bios_seconds();
+ do {
+ now = check;
+ check = bios_seconds();
+ if (check != now)
+ same = 0;
+ } while (++same < 8 && ++try < 1000);
+
+ if (now < lasttime)
+ now += 24 * 3600;
+ lasttime = now;
+
+ if (t != NULL)
+ *t = now;
+ return(now);
+}
+
+time_t
+getsecs(void)
+{
+ time_t n = 0;
+ time(&n);
+ return n;
+}
+
+/*
+ * Use the BIOS Wait function to pause for (period) microseconds.
+ *
+ * Resolution of this function is variable, but typically around
+ * 1ms.
+ */
+void
+delay(int period)
+{
+ v86.ctl = 0;
+ v86.addr = 0x15; /* int 0x15, function 0x86 */
+ v86.eax = 0x8600;
+ v86.ecx = period >> 16;
+ v86.edx = period & 0xffff;
+ v86int();
+}
diff --git a/stand/i386/libi386/vidconsole.c b/stand/i386/libi386/vidconsole.c
new file mode 100644
index 0000000..073d531
--- /dev/null
+++ b/stand/i386/libi386/vidconsole.c
@@ -0,0 +1,632 @@
+/*-
+ * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
+ * Copyright (c) 1997 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp)
+ * 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.
+ *
+ * Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <machine/psl.h>
+#include "libi386.h"
+
+#if KEYBOARD_PROBE
+#include <machine/cpufunc.h>
+
+static int probe_keyboard(void);
+#endif
+static void vidc_probe(struct console *cp);
+static int vidc_init(int arg);
+static void vidc_putchar(int c);
+static int vidc_getchar(void);
+static int vidc_ischar(void);
+
+static int vidc_started;
+
+#ifdef TERM_EMU
+#define MAXARGS 8
+#define DEFAULT_FGCOLOR 7
+#define DEFAULT_BGCOLOR 0
+
+void end_term(void);
+void bail_out(int c);
+void vidc_term_emu(int c);
+void get_pos(int *x, int *y);
+void curs_move(int *_x, int *_y, int x, int y);
+void write_char(int c, int fg, int bg);
+void scroll_up(int rows, int fg, int bg);
+void CD(void);
+void CM(void);
+void HO(void);
+
+static int args[MAXARGS], argc;
+static int fg_c, bg_c, curx, cury;
+static int esc;
+#endif
+
+
+struct console vidconsole = {
+ "vidconsole",
+ "internal video/keyboard",
+ 0,
+ vidc_probe,
+ vidc_init,
+ vidc_putchar,
+ vidc_getchar,
+ vidc_ischar
+};
+
+static void
+vidc_probe(struct console *cp)
+{
+
+ /* look for a keyboard */
+#if KEYBOARD_PROBE
+ if (probe_keyboard())
+#endif
+ {
+
+ cp->c_flags |= C_PRESENTIN;
+ }
+
+ /* XXX for now, always assume we can do BIOS screen output */
+ cp->c_flags |= C_PRESENTOUT;
+}
+
+static int
+vidc_init(int arg)
+{
+ int i;
+
+ if (vidc_started && arg == 0)
+ return (0);
+ vidc_started = 1;
+#ifdef TERM_EMU
+ /* Init terminal emulator */
+ end_term();
+ get_pos(&curx, &cury);
+ curs_move(&curx, &cury, curx, cury);
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+#endif
+ for (i = 0; i < 10 && vidc_ischar(); i++)
+ (void)vidc_getchar();
+ return (0); /* XXX reinit? */
+}
+
+void
+vidc_biosputchar(int c)
+{
+
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0xe00 | (c & 0xff);
+ v86.ebx = 0x7;
+ v86int();
+}
+
+static void
+vidc_rawputchar(int c)
+{
+ int i;
+
+ if (c == '\t')
+ /* lame tab expansion */
+ for (i = 0; i < 8; i++)
+ vidc_rawputchar(' ');
+ else {
+#ifndef TERM_EMU
+ vidc_biosputchar(c);
+#else
+ /* Emulate AH=0eh (teletype output) */
+ switch(c) {
+ case '\a':
+ vidc_biosputchar(c);
+ return;
+ case '\r':
+ curx = 0;
+ curs_move(&curx, &cury, curx, cury);
+ return;
+ case '\n':
+ cury++;
+ if (cury > 24) {
+ scroll_up(1, fg_c, bg_c);
+ cury--;
+ } else {
+ curs_move(&curx, &cury, curx, cury);
+ }
+ return;
+ case '\b':
+ if (curx > 0) {
+ curx--;
+ curs_move(&curx, &cury, curx, cury);
+ /* write_char(' ', fg_c, bg_c); XXX destructive(!) */
+ return;
+ }
+ return;
+ default:
+ write_char(c, fg_c, bg_c);
+ curx++;
+ if (curx > 79) {
+ curx = 0;
+ cury++;
+ }
+ if (cury > 24) {
+ curx = 0;
+ scroll_up(1, fg_c, bg_c);
+ cury--;
+ }
+ }
+ curs_move(&curx, &cury, curx, cury);
+#endif
+ }
+}
+
+#ifdef TERM_EMU
+
+/* Get cursor position on the screen. Result is in edx. Sets
+ * curx and cury appropriately.
+ */
+void
+get_pos(int *x, int *y)
+{
+
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0300;
+ v86.ebx = 0x0;
+ v86int();
+ *x = v86.edx & 0x00ff;
+ *y = (v86.edx & 0xff00) >> 8;
+}
+
+/* Move cursor to x rows and y cols (0-based). */
+void
+curs_move(int *_x, int *_y, int x, int y)
+{
+
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0200;
+ v86.ebx = 0x0;
+ v86.edx = ((0x00ff & y) << 8) + (0x00ff & x);
+ v86int();
+ *_x = x;
+ *_y = y;
+ /* If there is ctrl char at this position, cursor would be invisible.
+ * Make it a space instead.
+ */
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0800;
+ v86.ebx = 0x0;
+ v86int();
+#define isvisible(c) (((c) >= 32) && ((c) < 255))
+ if (!isvisible(v86.eax & 0x00ff)) {
+ write_char(' ', fg_c, bg_c);
+ }
+}
+
+/* Scroll up the whole window by a number of rows. If rows==0,
+ * clear the window. fg and bg are attributes for the new lines
+ * inserted in the window.
+ */
+void
+scroll_up(int rows, int fgcol, int bgcol)
+{
+
+ if (rows == 0)
+ rows = 25;
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0600 + (0x00ff & rows);
+ v86.ebx = (bgcol << 12) + (fgcol << 8);
+ v86.ecx = 0x0;
+ v86.edx = 0x184f;
+ v86int();
+}
+
+/* Write character and attribute at cursor position. */
+void
+write_char(int c, int fgcol, int bgcol)
+{
+
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0900 + (0x00ff & c);
+ v86.ebx = (bgcol << 4) + fgcol;
+ v86.ecx = 0x1;
+ v86int();
+}
+
+/**************************************************************/
+/*
+ * Screen manipulation functions. They use accumulated data in
+ * args[] and argc variables.
+ *
+ */
+
+/* Clear display from current position to end of screen */
+void
+CD(void)
+{
+
+ get_pos(&curx, &cury);
+ if (curx > 0) {
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0600;
+ v86.ebx = (bg_c << 4) + fg_c;
+ v86.ecx = (cury << 8) + curx;
+ v86.edx = (cury << 8) + 79;
+ v86int();
+ if (++cury > 24) {
+ end_term();
+ return;
+ }
+ }
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0600;
+ v86.ebx = (bg_c << 4) + fg_c;
+ v86.ecx = (cury << 8) + 0;
+ v86.edx = (24 << 8) + 79;
+ v86int();
+ end_term();
+}
+
+/* Absolute cursor move to args[0] rows and args[1] columns
+ * (the coordinates are 1-based).
+ */
+void
+CM(void)
+{
+
+ if (args[0] > 0)
+ args[0]--;
+ if (args[1] > 0)
+ args[1]--;
+ curs_move(&curx, &cury, args[1], args[0]);
+ end_term();
+}
+
+/* Home cursor (left top corner) */
+void
+HO(void)
+{
+
+ argc = 1;
+ args[0] = args[1] = 1;
+ CM();
+}
+
+/* Clear internal state of the terminal emulation code */
+void
+end_term(void)
+{
+
+ esc = 0;
+ argc = -1;
+}
+
+/* Gracefully exit ESC-sequence processing in case of misunderstanding */
+void
+bail_out(int c)
+{
+ char buf[16], *ch;
+ int i;
+
+ if (esc) {
+ vidc_rawputchar('\033');
+ if (esc != '\033')
+ vidc_rawputchar(esc);
+ for (i = 0; i <= argc; ++i) {
+ sprintf(buf, "%d", args[i]);
+ ch = buf;
+ while (*ch)
+ vidc_rawputchar(*ch++);
+ }
+ }
+ vidc_rawputchar(c);
+ end_term();
+}
+
+static void
+get_arg(int c)
+{
+
+ if (argc < 0)
+ argc = 0;
+ args[argc] *= 10;
+ args[argc] += c - '0';
+}
+
+/* Emulate basic capabilities of cons25 terminal */
+void
+vidc_term_emu(int c)
+{
+ static int ansi_col[] = {
+ 0, 4, 2, 6, 1, 5, 3, 7,
+ };
+ int t;
+ int i;
+
+ switch (esc) {
+ case 0:
+ switch (c) {
+ case '\033':
+ esc = c;
+ break;
+ default:
+ vidc_rawputchar(c);
+ break;
+ }
+ break;
+
+ case '\033':
+ switch (c) {
+ case '[':
+ esc = c;
+ args[0] = 0;
+ argc = -1;
+ break;
+ default:
+ bail_out(c);
+ break;
+ }
+ break;
+
+ case '[':
+ switch (c) {
+ case ';':
+ if (argc < 0) /* XXX */
+ argc = 0;
+ else if (argc + 1 >= MAXARGS)
+ bail_out(c);
+ else
+ args[++argc] = 0;
+ break;
+ case 'H':
+ if (argc < 0)
+ HO();
+ else if (argc == 1)
+ CM();
+ else
+ bail_out(c);
+ break;
+ case 'J':
+ if (argc < 0)
+ CD();
+ else
+ bail_out(c);
+ break;
+ case 'm':
+ if (argc < 0) {
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+ }
+ for (i = 0; i <= argc; ++i) {
+ switch (args[i]) {
+ case 0: /* back to normal */
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+ break;
+ case 1: /* bold */
+ fg_c |= 0x8;
+ break;
+ case 4: /* underline */
+ case 5: /* blink */
+ bg_c |= 0x8;
+ break;
+ case 7: /* reverse */
+ t = fg_c;
+ fg_c = bg_c;
+ bg_c = t;
+ break;
+ case 30: case 31: case 32: case 33:
+ case 34: case 35: case 36: case 37:
+ fg_c = ansi_col[args[i] - 30];
+ break;
+ case 39: /* normal */
+ fg_c = DEFAULT_FGCOLOR;
+ break;
+ case 40: case 41: case 42: case 43:
+ case 44: case 45: case 46: case 47:
+ bg_c = ansi_col[args[i] - 40];
+ break;
+ case 49: /* normal */
+ bg_c = DEFAULT_BGCOLOR;
+ break;
+ }
+ }
+ end_term();
+ break;
+ default:
+ if (isdigit(c))
+ get_arg(c);
+ else
+ bail_out(c);
+ break;
+ }
+ break;
+
+ default:
+ bail_out(c);
+ break;
+ }
+}
+#endif
+
+static void
+vidc_putchar(int c)
+{
+#ifdef TERM_EMU
+ vidc_term_emu(c);
+#else
+ vidc_rawputchar(c);
+#endif
+}
+
+static int
+vidc_getchar(void)
+{
+
+ if (vidc_ischar()) {
+ v86.ctl = 0;
+ v86.addr = 0x16;
+ v86.eax = 0x0;
+ v86int();
+ return (v86.eax & 0xff);
+ } else {
+ return (-1);
+ }
+}
+
+static int
+vidc_ischar(void)
+{
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x16;
+ v86.eax = 0x100;
+ v86int();
+ return (!V86_ZR(v86.efl));
+}
+
+#if KEYBOARD_PROBE
+
+#define PROBE_MAXRETRY 5
+#define PROBE_MAXWAIT 400
+#define IO_DUMMY 0x84
+#define IO_KBD 0x060 /* 8042 Keyboard */
+
+/* selected defines from kbdio.h */
+#define KBD_STATUS_PORT 4 /* status port, read */
+#define KBD_DATA_PORT 0 /* data port, read/write
+ * also used as keyboard command
+ * and mouse command port
+ */
+#define KBDC_ECHO 0x00ee
+#define KBDS_ANY_BUFFER_FULL 0x0001
+#define KBDS_INPUT_BUFFER_FULL 0x0002
+#define KBD_ECHO 0x00ee
+
+/* 7 microsec delay necessary for some keyboard controllers */
+static void
+delay7(void)
+{
+ /*
+ * I know this is broken, but no timer is available yet at this stage...
+ * See also comments in `delay1ms()'.
+ */
+ inb(IO_DUMMY); inb(IO_DUMMY);
+ inb(IO_DUMMY); inb(IO_DUMMY);
+ inb(IO_DUMMY); inb(IO_DUMMY);
+}
+
+/*
+ * This routine uses an inb to an unused port, the time to execute that
+ * inb is approximately 1.25uS. This value is pretty constant across
+ * all CPU's and all buses, with the exception of some PCI implentations
+ * that do not forward this I/O address to the ISA bus as they know it
+ * is not a valid ISA bus address, those machines execute this inb in
+ * 60 nS :-(.
+ *
+ */
+static void
+delay1ms(void)
+{
+ int i = 800;
+ while (--i >= 0)
+ (void)inb(0x84);
+}
+
+/*
+ * We use the presence/absence of a keyboard to determine whether the internal
+ * console can be used for input.
+ *
+ * Perform a simple test on the keyboard; issue the ECHO command and see
+ * if the right answer is returned. We don't do anything as drastic as
+ * full keyboard reset; it will be too troublesome and take too much time.
+ */
+static int
+probe_keyboard(void)
+{
+ int retry = PROBE_MAXRETRY;
+ int wait;
+ int i;
+
+ while (--retry >= 0) {
+ /* flush any noise */
+ while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) {
+ delay7();
+ inb(IO_KBD + KBD_DATA_PORT);
+ delay1ms();
+ }
+
+ /* wait until the controller can accept a command */
+ for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
+ if (((i = inb(IO_KBD + KBD_STATUS_PORT))
+ & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0)
+ break;
+ if (i & KBDS_ANY_BUFFER_FULL) {
+ delay7();
+ inb(IO_KBD + KBD_DATA_PORT);
+ }
+ delay1ms();
+ }
+ if (wait <= 0)
+ continue;
+
+ /* send the ECHO command */
+ outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO);
+
+ /* wait for a response */
+ for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
+ if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)
+ break;
+ delay1ms();
+ }
+ if (wait <= 0)
+ continue;
+
+ delay7();
+ i = inb(IO_KBD + KBD_DATA_PORT);
+#ifdef PROBE_KBD_BEBUG
+ printf("probe_keyboard: got 0x%x.\n", i);
+#endif
+ if (i == KBD_ECHO) {
+ /* got the right answer */
+ return (1);
+ }
+ }
+
+ return (0);
+}
+#endif /* KEYBOARD_PROBE */
diff --git a/stand/i386/loader/Makefile b/stand/i386/loader/Makefile
new file mode 100644
index 0000000..40ac46c
--- /dev/null
+++ b/stand/i386/loader/Makefile
@@ -0,0 +1,89 @@
+# $FreeBSD$
+
+HAVE_GELI= yes
+
+LOADER_NET_SUPPORT?= yes
+LOADER_NFS_SUPPORT?= yes
+LOADER_TFTP_SUPPORT?= yes
+LOADER_CD9660_SUPPORT?= no
+LOADER_EXT2FS_SUPPORT?= no
+LOADER_MSDOS_SUPPORT?= no
+LOADER_UFS_SUPPORT?= yes
+LOADER_GZIP_SUPPORT?= yes
+LOADER_BZIP2_SUPPORT?= yes
+
+.include <bsd.init.mk>
+
+MK_SSP= no
+
+LOADER?= loader
+PROG= ${LOADER}.sym
+MAN=
+INTERNALPROG=
+NEWVERSWHAT?= "bootstrap loader" x86
+VERSION_FILE= ${.CURDIR}/../loader/version
+
+.PATH: ${BOOTSRC}/i386/loader
+
+# architecture-specific loader code
+SRCS= main.c conf.c vers.c chain.c
+
+# Include bcache code.
+HAVE_BCACHE= yes
+
+# Enable PnP and ISA-PnP code.
+HAVE_PNP= yes
+HAVE_ISABUS= yes
+
+.if ${MK_LOADER_FIREWIRE} == "yes"
+CFLAGS+= -DLOADER_FIREWIRE_SUPPORT
+LIBFIREWIRE= ${BOOTOBJ}/i386/libfirewire/libfirewire.a
+.endif
+
+.if exists(${.CURDIR}/help.i386)
+HELP_FILES+= help.i386
+.else
+HELP_FILES=
+.endif
+
+# Always add MI sources
+.include "${BOOTSRC}/loader.mk"
+
+CLEANFILES+= ${LOADER} ${LOADER}.bin
+
+CFLAGS+= -Wall
+LDFLAGS+= -static -Ttext 0x0
+
+# i386 standalone support library
+LIBI386= ${BOOTOBJ}/i386/libi386/libi386.a
+CFLAGS+= -I${BOOTSRC}/i386
+
+# BTX components
+CFLAGS+= -I${BTXLIB}
+
+# Debug me!
+#CFLAGS+= -g
+#LDFLAGS+= -g
+
+${LOADER}: ${LOADER}.bin ${BTXLDR} ${BTXKERN}
+ btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \
+ -b ${BTXKERN} ${LOADER}.bin
+
+${LOADER}.bin: ${LOADER}.sym
+ strip -R .comment -R .note -o ${.TARGET} ${.ALLSRC}
+
+FILES+= ${LOADER}
+# XXX INSTALLFLAGS_loader= -b
+FILESMODE_${LOADER}= ${BINMODE} -b
+
+# XXX crt0.o needs to be first for pxeboot(8) to work
+OBJS= ${BTXCRT}
+
+DPADD= ${LIBFICL32} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBGELIBOOT} ${LIBSA32}
+LDADD= ${LIBFICL32} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBGELIBOOT} ${LIBSA32}
+
+.if ${MACHINE_CPUARCH} == "amd64"
+CFLAGS+= -DLOADER_PREFER_AMD64
+.endif
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/loader/Makefile.depend b/stand/i386/loader/Makefile.depend
new file mode 100644
index 0000000..89d5422
--- /dev/null
+++ b/stand/i386/loader/Makefile.depend
@@ -0,0 +1,21 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libstand \
+ sys/boot/ficl32 \
+ sys/boot/geli \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/btxldr \
+ sys/boot/i386/btx/lib \
+ sys/boot/i386/libi386 \
+ sys/boot/libstand32 \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/loader/chain.c b/stand/i386/loader/chain.c
new file mode 100644
index 0000000..d6810ef
--- /dev/null
+++ b/stand/i386/loader/chain.c
@@ -0,0 +1,134 @@
+/*-
+ * Copyright 2015 Toomas Soome <tsoome@me.com>
+ * 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.
+ */
+
+/*
+ * Chain loader to load BIOS boot block either from MBR or PBR.
+ *
+ * Note the boot block location 0000:7c000 conflicts with loader, so we need to
+ * read in to temporary space and relocate on exec, when btx is stopped.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/diskmbr.h>
+
+#include "bootstrap.h"
+#include "libi386/libi386.h"
+#include "btxv86.h"
+
+/*
+ * The MBR/VBR is located in first sector of disk/partition.
+ * Read 512B to temporary location and set up relocation. Then
+ * exec relocator.
+ */
+#define SECTOR_SIZE (512)
+
+COMMAND_SET(chain, "chain", "chain load boot block from device", command_chain);
+
+static int
+command_chain(int argc, char *argv[])
+{
+ int fd, len, size = SECTOR_SIZE;
+ struct stat st;
+ vm_offset_t mem = 0x100000;
+ struct i386_devdesc *rootdev;
+
+ if (argc == 1) {
+ command_errmsg = "no device or file name specified";
+ return (CMD_ERROR);
+ }
+ if (argc != 2) {
+ command_errmsg = "invalid trailing arguments";
+ return (CMD_ERROR);
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd == -1) {
+ command_errmsg = "open failed";
+ return (CMD_ERROR);
+ }
+
+ len = strlen(argv[1]);
+ if (argv[1][len-1] != ':') {
+ if (fstat(fd, &st) == -1) {
+ command_errmsg = "stat failed";
+ close(fd);
+ return (CMD_ERROR);
+ }
+ size = st.st_size;
+ } else if (strncmp(argv[1], "disk", 4) != 0) {
+ command_errmsg = "can only use disk device";
+ close(fd);
+ return (CMD_ERROR);
+ }
+
+ i386_getdev((void **)(&rootdev), argv[1], NULL);
+ if (rootdev == NULL) {
+ command_errmsg = "can't determine root device";
+ return (CMD_ERROR);
+ }
+
+ if (archsw.arch_readin(fd, mem, SECTOR_SIZE) != SECTOR_SIZE) {
+ command_errmsg = "failed to read disk";
+ close(fd);
+ return (CMD_ERROR);
+ }
+ close(fd);
+
+ if (*((uint16_t *)PTOV(mem + DOSMAGICOFFSET)) != DOSMAGIC) {
+ command_errmsg = "wrong magic";
+ return (CMD_ERROR);
+ }
+
+ relocater_data[0].src = mem;
+ relocater_data[0].dest = 0x7C00;
+ relocater_data[0].size = SECTOR_SIZE;
+
+ relocator_edx = bd_unit2bios(rootdev->d_unit);
+ relocator_esi = relocater_size;
+ relocator_ds = 0;
+ relocator_es = 0;
+ relocator_fs = 0;
+ relocator_gs = 0;
+ relocator_ss = 0;
+ relocator_cs = 0;
+ relocator_sp = 0x7C00;
+ relocator_ip = 0x7C00;
+ relocator_a20_enabled = 0;
+
+ i386_copyin(relocater, 0x600, relocater_size);
+
+ dev_cleanup();
+
+ __exec((void *)0x600);
+
+ panic("exec returned");
+ return (CMD_ERROR); /* not reached */
+}
diff --git a/stand/i386/loader/conf.c b/stand/i386/loader/conf.c
new file mode 100644
index 0000000..29ce1e3
--- /dev/null
+++ b/stand/i386/loader/conf.c
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include "libi386/libi386.h"
+#if defined(LOADER_ZFS_SUPPORT)
+#include "../zfs/libzfs.h"
+#endif
+
+/*
+ * We could use linker sets for some or all of these, but
+ * then we would have to control what ended up linked into
+ * the bootstrap. So it's easier to conditionalise things
+ * here.
+ *
+ * XXX rename these arrays to be consistent and less namespace-hostile
+ *
+ * XXX as libi386 and biosboot merge, some of these can become linker sets.
+ */
+
+#if defined(LOADER_FIREWIRE_SUPPORT)
+extern struct devsw fwohci;
+#endif
+
+/* Exported for libstand */
+struct devsw *devsw[] = {
+ &bioscd,
+ &biosdisk,
+#if defined(LOADER_NFS_SUPPORT) || defined(LOADER_TFTP_SUPPORT)
+ &pxedisk,
+#endif
+#if defined(LOADER_FIREWIRE_SUPPORT)
+ &fwohci,
+#endif
+#if defined(LOADER_ZFS_SUPPORT)
+ &zfs_dev,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#if defined(LOADER_ZFS_SUPPORT)
+ &zfs_fsops,
+#endif
+ &ufs_fsops,
+ &ext2fs_fsops,
+ &dosfs_fsops,
+ &cd9660_fsops,
+#if defined(LOADER_NANDFS_SUPPORT)
+ &nandfs_fsops,
+#endif
+#ifdef LOADER_NFS_SUPPORT
+ &nfs_fsops,
+#endif
+#ifdef LOADER_TFTP_SUPPORT
+ &tftp_fsops,
+#endif
+#ifdef LOADER_GZIP_SUPPORT
+ &gzipfs_fsops,
+#endif
+#ifdef LOADER_BZIP2_SUPPORT
+ &bzipfs_fsops,
+#endif
+#ifdef LOADER_SPLIT_SUPPORT
+ &splitfs_fsops,
+#endif
+ NULL
+};
+
+/* Exported for i386 only */
+/*
+ * Sort formats so that those that can detect based on arguments
+ * rather than reading the file go first.
+ */
+extern struct file_format i386_elf;
+extern struct file_format i386_elf_obj;
+extern struct file_format amd64_elf;
+extern struct file_format amd64_elf_obj;
+extern struct file_format multiboot;
+extern struct file_format multiboot_obj;
+
+struct file_format *file_formats[] = {
+ &multiboot,
+ &multiboot_obj,
+#ifdef LOADER_PREFER_AMD64
+ &amd64_elf,
+ &amd64_elf_obj,
+#endif
+ &i386_elf,
+ &i386_elf_obj,
+#ifndef LOADER_PREFER_AMD64
+ &amd64_elf,
+ &amd64_elf_obj,
+#endif
+ NULL
+};
+
+/*
+ * Consoles
+ *
+ * We don't prototype these in libi386.h because they require
+ * data structures from bootstrap.h as well.
+ */
+extern struct console vidconsole;
+extern struct console comconsole;
+#if defined(LOADER_FIREWIRE_SUPPORT)
+extern struct console dconsole;
+#endif
+extern struct console nullconsole;
+extern struct console spinconsole;
+
+struct console *consoles[] = {
+ &vidconsole,
+ &comconsole,
+#if defined(LOADER_FIREWIRE_SUPPORT)
+ &dconsole,
+#endif
+ &nullconsole,
+ &spinconsole,
+ NULL
+};
+
+extern struct pnphandler isapnphandler;
+extern struct pnphandler biospnphandler;
+extern struct pnphandler biospcihandler;
+
+struct pnphandler *pnphandlers[] = {
+ &biospnphandler, /* should go first, as it may set isapnp_readport */
+ &isapnphandler,
+ &biospcihandler,
+ NULL
+};
diff --git a/stand/i386/loader/help.i386 b/stand/i386/loader/help.i386
new file mode 100644
index 0000000..0ff6286
--- /dev/null
+++ b/stand/i386/loader/help.i386
@@ -0,0 +1,54 @@
+################################################################################
+# Treboot DReboot the system
+
+ reboot
+
+ Causes the system to immediately reboot.
+
+################################################################################
+# Theap DDisplay memory management statistics
+
+ heap
+
+ Requests debugging output from the heap manager. For debugging use
+ only.
+
+################################################################################
+# Tset Snum_ide_disks DSet the number of IDE disks
+
+ NOTE: this variable is deprecated, use root_disk_unit instead.
+
+ set num_ide_disks=<value>
+
+ When booting from a SCSI disk on a system with one or more IDE disks,
+ and where the IDE disks are the default boot device, it is necessary
+ to tell the kernel how many IDE disks there are in order to have it
+ correctly locate the SCSI disk you are booting from.
+
+################################################################################
+# Tset Sroot_disk_unit DForce the root disk unit number.
+
+ set root_disk_unit=<value>
+
+ If the code which detects the disk unit number for the root disk is
+ confused, eg. by a mix of SCSI and IDE disks, or IDE disks with
+ gaps in the sequence (eg. no primary slave), the unit number can be
+ forced by setting this variable.
+
+################################################################################
+# Tsmap DDisplay BIOS SMAP table
+
+ smap
+
+ Displays the BIOS SMAP (system memory map) table.
+
+################################################################################
+# Tchain DChain load disk block
+
+ chain disk:
+
+ chain will read stage1 (MBR or VBR) boot block from specified device
+ to address 0000:7C00 and attempts to run it. Use lsdev to get available
+ device names. Disk name must end with colon.
+
+################################################################################
diff --git a/stand/i386/loader/loader.rc b/stand/i386/loader/loader.rc
new file mode 100644
index 0000000..287c05e
--- /dev/null
+++ b/stand/i386/loader/loader.rc
@@ -0,0 +1,18 @@
+\ Loader.rc
+\ $FreeBSD$
+\
+\ Includes additional commands
+include /boot/loader.4th
+try-include /boot/loader.rc.local
+
+\ Reads and processes loader.conf variables
+initialize
+
+\ Tests for password -- executes autoboot first if a password was defined
+check-password
+
+\ Load in the boot menu
+include /boot/beastie.4th
+
+\ Start the boot menu
+beastie-start
diff --git a/stand/i386/loader/main.c b/stand/i386/loader/main.c
new file mode 100644
index 0000000..81bc2ff
--- /dev/null
+++ b/stand/i386/loader/main.c
@@ -0,0 +1,477 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * MD bootstrap main() and assorted miscellaneous
+ * commands.
+ */
+
+#include <stand.h>
+#include <stddef.h>
+#include <string.h>
+#include <machine/bootinfo.h>
+#include <machine/cpufunc.h>
+#include <machine/psl.h>
+#include <sys/disk.h>
+#include <sys/reboot.h>
+#include <common/drv.h>
+
+#include "bootstrap.h"
+#include "common/bootargs.h"
+#include "libi386/libi386.h"
+#include "libi386/smbios.h"
+#include "btxv86.h"
+
+#ifdef LOADER_ZFS_SUPPORT
+#include "../zfs/libzfs.h"
+#endif
+
+CTASSERT(sizeof(struct bootargs) == BOOTARGS_SIZE);
+CTASSERT(offsetof(struct bootargs, bootinfo) == BA_BOOTINFO);
+CTASSERT(offsetof(struct bootargs, bootflags) == BA_BOOTFLAGS);
+CTASSERT(offsetof(struct bootinfo, bi_size) == BI_SIZE);
+
+/* Arguments passed in from the boot1/boot2 loader */
+static struct bootargs *kargs;
+
+static u_int32_t initial_howto;
+static u_int32_t initial_bootdev;
+static struct bootinfo *initial_bootinfo;
+
+struct arch_switch archsw; /* MI/MD interface boundary */
+
+static void extract_currdev(void);
+static int isa_inb(int port);
+static void isa_outb(int port, int value);
+void exit(int code);
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+struct geli_boot_args *gargs;
+#endif
+#ifdef LOADER_ZFS_SUPPORT
+struct zfs_boot_args *zargs;
+static void i386_zfs_probe(void);
+#endif
+
+/* from vers.c */
+extern char bootprog_info[];
+
+/* XXX debugging */
+extern char end[];
+
+static void *heap_top;
+static void *heap_bottom;
+
+int
+main(void)
+{
+ int i;
+
+ /* Pick up arguments */
+ kargs = (void *)__args;
+ initial_howto = kargs->howto;
+ initial_bootdev = kargs->bootdev;
+ initial_bootinfo = kargs->bootinfo ? (struct bootinfo *)PTOV(kargs->bootinfo) : NULL;
+
+ /* Initialize the v86 register set to a known-good state. */
+ bzero(&v86, sizeof(v86));
+ v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
+
+ /*
+ * Initialise the heap as early as possible. Once this is done, malloc() is usable.
+ */
+ bios_getmem();
+
+#if defined(LOADER_BZIP2_SUPPORT) || defined(LOADER_FIREWIRE_SUPPORT) || \
+ defined(LOADER_GPT_SUPPORT) || defined(LOADER_ZFS_SUPPORT)
+ if (high_heap_size > 0) {
+ heap_top = PTOV(high_heap_base + high_heap_size);
+ heap_bottom = PTOV(high_heap_base);
+ if (high_heap_base < memtop_copyin)
+ memtop_copyin = high_heap_base;
+ } else
+#endif
+ {
+ heap_top = (void *)PTOV(bios_basemem);
+ heap_bottom = (void *)end;
+ }
+ setheap(heap_bottom, heap_top);
+
+ /*
+ * XXX Chicken-and-egg problem; we want to have console output early, but some
+ * console attributes may depend on reading from eg. the boot device, which we
+ * can't do yet.
+ *
+ * We can use printf() etc. once this is done.
+ * If the previous boot stage has requested a serial console, prefer that.
+ */
+ bi_setboothowto(initial_howto);
+ if (initial_howto & RB_MULTIPLE) {
+ if (initial_howto & RB_SERIAL)
+ setenv("console", "comconsole vidconsole", 1);
+ else
+ setenv("console", "vidconsole comconsole", 1);
+ } else if (initial_howto & RB_SERIAL)
+ setenv("console", "comconsole", 1);
+ else if (initial_howto & RB_MUTE)
+ setenv("console", "nullconsole", 1);
+ cons_probe();
+
+ /*
+ * Initialise the block cache. Set the upper limit.
+ */
+ bcache_init(32768, 512);
+
+ /*
+ * Special handling for PXE and CD booting.
+ */
+ if (kargs->bootinfo == 0) {
+ /*
+ * We only want the PXE disk to try to init itself in the below
+ * walk through devsw if we actually booted off of PXE.
+ */
+ if (kargs->bootflags & KARGS_FLAGS_PXE)
+ pxe_enable(kargs->pxeinfo ? PTOV(kargs->pxeinfo) : NULL);
+ else if (kargs->bootflags & KARGS_FLAGS_CD)
+ bc_add(initial_bootdev);
+ }
+
+ archsw.arch_autoload = i386_autoload;
+ archsw.arch_getdev = i386_getdev;
+ archsw.arch_copyin = i386_copyin;
+ archsw.arch_copyout = i386_copyout;
+ archsw.arch_readin = i386_readin;
+ archsw.arch_isainb = isa_inb;
+ archsw.arch_isaoutb = isa_outb;
+#ifdef LOADER_ZFS_SUPPORT
+ archsw.arch_zfs_probe = i386_zfs_probe;
+
+#ifdef LOADER_GELI_SUPPORT
+ if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) {
+ zargs = (struct zfs_boot_args *)(kargs + 1);
+ if (zargs != NULL && zargs->size >= offsetof(struct zfs_boot_args, gelipw)) {
+ if (zargs->size >= offsetof(struct zfs_boot_args, keybuf_sentinel) &&
+ zargs->keybuf_sentinel == KEYBUF_SENTINEL) {
+ geli_save_keybuf(zargs->keybuf);
+ }
+ if (zargs->gelipw[0] != '\0') {
+ setenv("kern.geom.eli.passphrase", zargs->gelipw, 1);
+ explicit_bzero(zargs->gelipw, sizeof(zargs->gelipw));
+ }
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+#else /* !LOADER_ZFS_SUPPORT */
+#ifdef LOADER_GELI_SUPPORT
+ if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) {
+ gargs = (struct geli_boot_args *)(kargs + 1);
+ if (gargs != NULL && gargs->size >= offsetof(struct geli_boot_args, gelipw)) {
+ if (gargs->keybuf_sentinel == KEYBUF_SENTINEL) {
+ geli_save_keybuf(gargs->keybuf);
+ }
+ if (gargs->gelipw[0] != '\0') {
+ setenv("kern.geom.eli.passphrase", gargs->gelipw, 1);
+ explicit_bzero(gargs->gelipw, sizeof(gargs->gelipw));
+ }
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+#endif /* LOADER_ZFS_SUPPORT */
+
+ /*
+ * March through the device switch probing for things.
+ */
+ for (i = 0; devsw[i] != NULL; i++)
+ if (devsw[i]->dv_init != NULL)
+ (devsw[i]->dv_init)();
+ printf("BIOS %dkB/%dkB available memory\n", bios_basemem / 1024, bios_extmem / 1024);
+ if (initial_bootinfo != NULL) {
+ initial_bootinfo->bi_basemem = bios_basemem / 1024;
+ initial_bootinfo->bi_extmem = bios_extmem / 1024;
+ }
+
+ /* detect ACPI for future reference */
+ biosacpi_detect();
+
+ /* detect SMBIOS for future reference */
+ smbios_detect(NULL);
+
+ /* detect PCI BIOS for future reference */
+ biospci_detect();
+
+ printf("\n%s", bootprog_info);
+
+ extract_currdev(); /* set $currdev and $loaddev */
+ setenv("LINES", "24", 1); /* optional */
+
+ bios_getsmap();
+
+ interact(NULL);
+
+ /* if we ever get here, it is an error */
+ return (1);
+}
+
+/*
+ * Set the 'current device' by (if possible) recovering the boot device as
+ * supplied by the initial bootstrap.
+ *
+ * XXX should be extended for netbooting.
+ */
+static void
+extract_currdev(void)
+{
+ struct i386_devdesc new_currdev;
+#ifdef LOADER_ZFS_SUPPORT
+ char buf[20];
+#endif
+ int biosdev = -1;
+
+ /* Assume we are booting from a BIOS disk by default */
+ new_currdev.d_dev = &biosdisk;
+
+ /* new-style boot loaders such as pxeldr and cdldr */
+ if (kargs->bootinfo == 0) {
+ if ((kargs->bootflags & KARGS_FLAGS_CD) != 0) {
+ /* we are booting from a CD with cdboot */
+ new_currdev.d_dev = &bioscd;
+ new_currdev.d_unit = bc_bios2unit(initial_bootdev);
+ } else if ((kargs->bootflags & KARGS_FLAGS_PXE) != 0) {
+ /* we are booting from pxeldr */
+ new_currdev.d_dev = &pxedisk;
+ new_currdev.d_unit = 0;
+ } else {
+ /* we don't know what our boot device is */
+ new_currdev.d_kind.biosdisk.slice = -1;
+ new_currdev.d_kind.biosdisk.partition = 0;
+ biosdev = -1;
+ }
+#ifdef LOADER_ZFS_SUPPORT
+ } else if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) {
+ zargs = NULL;
+ /* check for new style extended argument */
+ if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0)
+ zargs = (struct zfs_boot_args *)(kargs + 1);
+
+ if (zargs != NULL &&
+ zargs->size >= offsetof(struct zfs_boot_args, primary_pool)) {
+ /* sufficient data is provided */
+ new_currdev.d_kind.zfs.pool_guid = zargs->pool;
+ new_currdev.d_kind.zfs.root_guid = zargs->root;
+ if (zargs->size >= sizeof(*zargs) && zargs->primary_vdev != 0) {
+ sprintf(buf, "%llu", zargs->primary_pool);
+ setenv("vfs.zfs.boot.primary_pool", buf, 1);
+ sprintf(buf, "%llu", zargs->primary_vdev);
+ setenv("vfs.zfs.boot.primary_vdev", buf, 1);
+ }
+ } else {
+ /* old style zfsboot block */
+ new_currdev.d_kind.zfs.pool_guid = kargs->zfspool;
+ new_currdev.d_kind.zfs.root_guid = 0;
+ }
+ new_currdev.d_dev = &zfs_dev;
+#endif
+ } else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) {
+ /* The passed-in boot device is bad */
+ new_currdev.d_kind.biosdisk.slice = -1;
+ new_currdev.d_kind.biosdisk.partition = 0;
+ biosdev = -1;
+ } else {
+ new_currdev.d_kind.biosdisk.slice = B_SLICE(initial_bootdev) - 1;
+ new_currdev.d_kind.biosdisk.partition = B_PARTITION(initial_bootdev);
+ biosdev = initial_bootinfo->bi_bios_dev;
+
+ /*
+ * If we are booted by an old bootstrap, we have to guess at the BIOS
+ * unit number. We will lose if there is more than one disk type
+ * and we are not booting from the lowest-numbered disk type
+ * (ie. SCSI when IDE also exists).
+ */
+ if ((biosdev == 0) && (B_TYPE(initial_bootdev) != 2)) /* biosdev doesn't match major */
+ biosdev = 0x80 + B_UNIT(initial_bootdev); /* assume harddisk */
+ }
+ new_currdev.d_type = new_currdev.d_dev->dv_type;
+
+ /*
+ * If we are booting off of a BIOS disk and we didn't succeed in determining
+ * which one we booted off of, just use disk0: as a reasonable default.
+ */
+ if ((new_currdev.d_type == biosdisk.dv_type) &&
+ ((new_currdev.d_unit = bd_bios2unit(biosdev)) == -1)) {
+ printf("Can't work out which disk we are booting from.\n"
+ "Guessed BIOS device 0x%x not found by probes, defaulting to disk0:\n", biosdev);
+ new_currdev.d_unit = 0;
+ }
+
+#ifdef LOADER_ZFS_SUPPORT
+ if (new_currdev.d_type == DEVT_ZFS)
+ init_zfs_bootenv(zfs_fmtdev(&new_currdev));
+#endif
+
+ env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev),
+ i386_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset,
+ env_nounset);
+}
+
+COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
+
+static int
+command_reboot(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; devsw[i] != NULL; ++i)
+ if (devsw[i]->dv_cleanup != NULL)
+ (devsw[i]->dv_cleanup)();
+
+ printf("Rebooting...\n");
+ delay(1000000);
+ __exit(0);
+}
+
+/* provide this for panic, as it's not in the startup code */
+void
+exit(int code)
+{
+ __exit(code);
+}
+
+COMMAND_SET(heap, "heap", "show heap usage", command_heap);
+
+static int
+command_heap(int argc, char *argv[])
+{
+ mallocstats();
+ printf("heap base at %p, top at %p, upper limit at %p\n", heap_bottom,
+ sbrk(0), heap_top);
+ return(CMD_OK);
+}
+
+#ifdef LOADER_ZFS_SUPPORT
+COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
+ command_lszfs);
+
+static int
+command_lszfs(int argc, char *argv[])
+{
+ int err;
+
+ if (argc != 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ err = zfs_list(argv[1]);
+ if (err != 0) {
+ command_errmsg = strerror(err);
+ return (CMD_ERROR);
+ }
+
+ return (CMD_OK);
+}
+
+COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments",
+ command_reloadbe);
+
+static int
+command_reloadbe(int argc, char *argv[])
+{
+ int err;
+ char *root;
+
+ if (argc > 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ if (argc == 2) {
+ err = zfs_bootenv(argv[1]);
+ } else {
+ root = getenv("zfs_be_root");
+ if (root == NULL) {
+ /* There does not appear to be a ZFS pool here, exit without error */
+ return (CMD_OK);
+ }
+ err = zfs_bootenv(root);
+ }
+
+ if (err != 0) {
+ command_errmsg = strerror(err);
+ return (CMD_ERROR);
+ }
+
+ return (CMD_OK);
+}
+#endif
+
+/* ISA bus access functions for PnP. */
+static int
+isa_inb(int port)
+{
+
+ return (inb(port));
+}
+
+static void
+isa_outb(int port, int value)
+{
+
+ outb(port, value);
+}
+
+#ifdef LOADER_ZFS_SUPPORT
+static void
+i386_zfs_probe(void)
+{
+ char devname[32];
+ int unit;
+
+ /*
+ * Open all the disks we can find and see if we can reconstruct
+ * ZFS pools from them.
+ */
+ for (unit = 0; unit < MAXBDDEV; unit++) {
+ if (bd_unit2bios(unit) == -1)
+ break;
+ sprintf(devname, "disk%d:", unit);
+ zfs_probe_dev(devname, NULL);
+ }
+}
+
+uint64_t
+ldi_get_size(void *priv)
+{
+ int fd = (uintptr_t) priv;
+ uint64_t size;
+
+ ioctl(fd, DIOCGMEDIASIZE, &size);
+ return (size);
+}
+#endif
diff --git a/stand/i386/loader/version b/stand/i386/loader/version
new file mode 100644
index 0000000..7a2acaf
--- /dev/null
+++ b/stand/i386/loader/version
@@ -0,0 +1,14 @@
+$FreeBSD$
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+1.1: New calling conventions for fopen.
+1.0: New semantics for finding the kernel, new boot.
+0.8: Set/getenv & cia, copyin/out.
+0.7: Supports large KVM
+0.6: Increased dictionary size -- supports loader.4th
+0.5: First release version
+0.2: Initial integration with BTX
+0.1: Initial i386 version, inspiration and some structure from the
+ NetBSD version.
diff --git a/stand/i386/mbr/Makefile b/stand/i386/mbr/Makefile
new file mode 100644
index 0000000..9bfa0bd
--- /dev/null
+++ b/stand/i386/mbr/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+PROG= mbr
+STRIP=
+BINMODE=${NOBINMODE}
+MAN=
+SRCS= ${PROG}.s
+
+# MBR flags: 0x80 -- try packet interface (also known as EDD or LBA)
+BOOT_MBR_FLAGS?= 0x80
+
+ORG= 0x600
+
+AFLAGS+=--defsym FLAGS=${BOOT_MBR_FLAGS}
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/mbr/Makefile.depend b/stand/i386/mbr/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/mbr/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/mbr/mbr.s b/stand/i386/mbr/mbr.s
new file mode 100644
index 0000000..3cfc20d
--- /dev/null
+++ b/stand/i386/mbr/mbr.s
@@ -0,0 +1,157 @@
+#
+# Copyright (c) 1999 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+# A 512 byte MBR boot manager that simply boots the active partition.
+
+ .set LOAD,0x7c00 # Load address
+ .set EXEC,0x600 # Execution address
+ .set PT_OFF,0x1be # Partition table
+ .set MAGIC,0xaa55 # Magic: bootable
+ .set FL_PACKET,0x80 # Flag: try EDD
+
+ .set NHRDRV,0x475 # Number of hard drives
+
+ .globl start # Entry point
+ .code16
+
+#
+# Setup the segment registers for flat addressing and setup the stack.
+#
+start: cld # String ops inc
+ xorw %ax,%ax # Zero
+ movw %ax,%es # Address
+ movw %ax,%ds # data
+ movw %ax,%ss # Set up
+ movw $LOAD,%sp # stack
+#
+# Relocate ourself to a lower address so that we are out of the way when
+# we load in the bootstrap from the partition to boot.
+#
+ movw $main-EXEC+LOAD,%si # Source
+ movw $main,%di # Destination
+ movw $0x200-(main-start),%cx # Byte count
+ rep # Relocate
+ movsb # code
+#
+# Jump to the relocated code.
+#
+ jmp main-LOAD+EXEC # To relocated code
+#
+# Scan the partition table looking for an active entry. Note that %ch is
+# zero from the repeated string instruction above. We save the offset of
+# the active partition in %si and scan the entire table to ensure that only
+# one partition is marked active.
+#
+main: xorw %si,%si # No active partition
+ movw $partbl,%bx # Partition table
+ movb $0x4,%cl # Number of entries
+main.1: cmpb %ch,(%bx) # Null entry?
+ je main.2 # Yes
+ jg err_pt # If 0x1..0x7f
+ testw %si,%si # Active already found?
+ jnz err_pt # Yes
+ movw %bx,%si # Point to active
+main.2: addb $0x10,%bl # Till
+ loop main.1 # done
+ testw %si,%si # Active found?
+ jnz main.3 # Yes
+ int $0x18 # BIOS: Diskless boot
+#
+# Ok, we've found a possible active partition. Check to see that the drive
+# is a valid hard drive number.
+#
+main.3: cmpb $0x80,%dl # Drive valid?
+ jb main.4 # No
+ movb NHRDRV,%dh # Calculate the highest
+ addb $0x80,%dh # drive number available
+ cmpb %dh,%dl # Within range?
+ jb main.5 # Yes
+main.4: movb (%si),%dl # Load drive
+#
+# Ok, now that we have a valid drive and partition entry, load the CHS from
+# the partition entry and read the sector from the disk.
+#
+main.5: movw %sp,%di # Save stack pointer
+ movb 0x1(%si),%dh # Load head
+ movw 0x2(%si),%cx # Load cylinder:sector
+ movw $LOAD,%bx # Transfer buffer
+ testb $FL_PACKET,flags # Try EDD?
+ jz main.7 # No.
+ pushw %cx # Save %cx
+ pushw %bx # Save %bx
+ movw $0x55aa,%bx # Magic
+ movb $0x41,%ah # BIOS: EDD extensions
+ int $0x13 # present?
+ jc main.6 # No.
+ cmpw $0xaa55,%bx # Magic ok?
+ jne main.6 # No.
+ testb $0x1,%cl # Packet mode present?
+ jz main.6 # No.
+ popw %bx # Restore %bx
+ pushl $0x0 # Set the LBA
+ pushl 0x8(%si) # address
+ pushw %es # Set the address of
+ pushw %bx # the transfer buffer
+ pushw $0x1 # Read 1 sector
+ pushw $0x10 # Packet length
+ movw %sp,%si # Packer pointer
+ movw $0x4200,%ax # BIOS: LBA Read from disk
+ jmp main.8 # Skip the CHS setup
+main.6: popw %bx # Restore %bx
+ popw %cx # Restore %cx
+main.7: movw $0x201,%ax # BIOS: Read from disk
+main.8: int $0x13 # Call the BIOS
+ movw %di,%sp # Restore stack
+ jc err_rd # If error
+#
+# Now that we've loaded the bootstrap, check for the 0xaa55 signature. If it
+# is present, execute the bootstrap we just loaded.
+#
+ cmpw $MAGIC,0x1fe(%bx) # Bootable?
+ jne err_os # No
+ jmp *%bx # Invoke bootstrap
+#
+# Various error message entry points.
+#
+err_pt: movw $msg_pt,%si # "Invalid partition
+ jmp putstr # table"
+
+err_rd: movw $msg_rd,%si # "Error loading
+ jmp putstr # operating system"
+
+err_os: movw $msg_os,%si # "Missing operating
+ jmp putstr # system"
+#
+# Output an ASCIZ string to the console via the BIOS.
+#
+putstr.0: movw $0x7,%bx # Page:attribute
+ movb $0xe,%ah # BIOS: Display
+ int $0x10 # character
+putstr: lodsb # Get character
+ testb %al,%al # End of string?
+ jnz putstr.0 # No
+putstr.1: jmp putstr.1 # Await reset
+
+msg_pt: .asciz "Invalid partition table"
+msg_rd: .asciz "Error loading operating system"
+msg_os: .asciz "Missing operating system"
+
+ .org PT_OFF-1,0x90
+flags: .byte FLAGS # Flags
+
+partbl: .fill 0x10,0x4,0x0 # Partition table
+ .word MAGIC # Magic number
diff --git a/stand/i386/pmbr/Makefile b/stand/i386/pmbr/Makefile
new file mode 100644
index 0000000..1bfe0ee
--- /dev/null
+++ b/stand/i386/pmbr/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= pmbr
+STRIP=
+BINMODE=${NOBINMODE}
+MAN=
+SRCS= ${PROG}.s
+
+ORG= 0x600
+
+AFLAGS+=--defsym FLAGS=${BOOT_MBR_FLAGS}
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/pmbr/Makefile.depend b/stand/i386/pmbr/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/pmbr/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/pmbr/pmbr.s b/stand/i386/pmbr/pmbr.s
new file mode 100644
index 0000000..1a75881
--- /dev/null
+++ b/stand/i386/pmbr/pmbr.s
@@ -0,0 +1,252 @@
+#-
+# Copyright (c) 2007 Yahoo!, Inc.
+# All rights reserved.
+# Written by: John Baldwin <jhb@FreeBSD.org>
+#
+# 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 author nor the names of any co-contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+# Partly from: src/sys/boot/i386/mbr/mbr.s 1.7
+
+# A 512 byte PMBR boot manager that looks for a FreeBSD boot GPT partition
+# and boots it.
+
+ .set LOAD,0x7c00 # Load address
+ .set EXEC,0x600 # Execution address
+ .set MAGIC,0xaa55 # Magic: bootable
+ .set SECSIZE,0x200 # Size of a single disk sector
+ .set DISKSIG,440 # Disk signature offset
+ .set STACK,EXEC+SECSIZE*4 # Stack address
+ .set GPT_ADDR,STACK # GPT header address
+ .set GPT_SIG,0
+ .set GPT_SIG_0,0x20494645 # "EFI "
+ .set GPT_SIG_1,0x54524150 # "PART"
+ .set GPT_MYLBA,24
+ .set GPT_PART_LBA,72
+ .set GPT_NPART,80
+ .set GPT_PART_SIZE,84
+ .set PART_ADDR,GPT_ADDR+SECSIZE # GPT partition array address
+ .set PART_TYPE,0
+ .set PART_START_LBA,32
+ .set PART_END_LBA,40
+ .set DPBUF,PART_ADDR+SECSIZE
+ .set DPBUF_SEC,0x10 # Number of sectors
+
+ .set NHRDRV,0x475 # Number of hard drives
+
+ .globl start # Entry point
+ .code16
+
+#
+# Setup the segment registers for flat addressing and setup the stack.
+#
+start: cld # String ops inc
+ xorw %ax,%ax # Zero
+ movw %ax,%es # Address
+ movw %ax,%ds # data
+ movw %ax,%ss # Set up
+ movw $STACK,%sp # stack
+#
+# Relocate ourself to a lower address so that we have more room to load
+# other sectors.
+#
+ movw $main-EXEC+LOAD,%si # Source
+ movw $main,%di # Destination
+ movw $SECSIZE-(main-start),%cx # Byte count
+ rep # Relocate
+ movsb # code
+#
+# Jump to the relocated code.
+#
+ jmp main-LOAD+EXEC # To relocated code
+#
+# Validate drive number in %dl.
+#
+main: cmpb $0x80,%dl # Drive valid?
+ jb main.1 # No
+ movb NHRDRV,%dh # Calculate the highest
+ addb $0x80,%dh # drive number available
+ cmpb %dh,%dl # Within range?
+ jb main.2 # Yes
+main.1: movb $0x80,%dl # Assume drive 0x80
+#
+# Load the GPT header and verify signature. Try LBA 1 for the primary one and
+# the last LBA for the backup if it is broken.
+#
+main.2: call getdrvparams # Read drive parameters
+ movb $1,%dh # %dh := 1 (reading primary)
+main.2a: movw $GPT_ADDR,%bx
+ movw $lba,%si
+ call read # Read header and check GPT sig
+ cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG
+ jnz main.2b
+ cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4
+ jnz main.2b
+ jmp load_part
+main.2b: cmpb $1,%dh # Reading primary?
+ jne err_pt # If no - invalid table found
+#
+# Try alternative LBAs from the last sector for the GPT header.
+#
+main.3: movb $0,%dh # %dh := 0 (reading backup)
+ movw $DPBUF+DPBUF_SEC,%si # %si = last sector + 1
+ movw $lba,%di # %di = $lba
+main.3a: decl (%si) # 0x0(%si) = last sec (0-31)
+ movw $2,%cx
+ rep
+ movsw # $lastsec--, copy it to $lba
+ jmp main.2a # Read the next sector
+#
+# Load a partition table sector from disk and look for a FreeBSD boot
+# partition.
+#
+load_part: movw $GPT_ADDR+GPT_PART_LBA,%si
+ movw $PART_ADDR,%bx
+ call read
+scan: movw %bx,%si # Compare partition UUID
+ movw $boot_uuid,%di # with FreeBSD boot UUID
+ movb $0x10,%cl
+ repe cmpsb
+ jnz next_part # Didn't match, next partition
+#
+# We found a boot partition. Load it into RAM starting at 0x7c00.
+#
+ movw %bx,%di # Save partition pointer in %di
+ leaw PART_START_LBA(%di),%si
+ movw $LOAD/16,%bx
+ movw %bx,%es
+ xorw %bx,%bx
+load_boot: push %si # Save %si
+ call read
+ pop %si # Restore
+ movl PART_END_LBA(%di),%eax # See if this was the last LBA
+ cmpl (%si),%eax
+ jnz next_boot
+ movl PART_END_LBA+4(%di),%eax
+ cmpl 4(%si),%eax
+ jnz next_boot
+ mov %bx,%es # Reset %es to zero
+ jmp LOAD # Jump to boot code
+next_boot: incl (%si) # Next LBA
+ adcl $0,4(%si)
+ mov %es,%ax # Adjust segment for next
+ addw $SECSIZE/16,%ax # sector
+ cmp $0x9000,%ax # Don't load past 0x90000,
+ jae err_big # 545k should be enough for
+ mov %ax,%es # any boot code. :)
+ jmp load_boot
+#
+# Move to the next partition. If we walk off the end of the sector, load
+# the next sector. We assume that partition entries are smaller than 64k
+# and that they won't span a sector boundary.
+#
+# XXX: Should we int 0x18 instead of err_noboot if we hit the end of the table?
+#
+next_part: decl GPT_ADDR+GPT_NPART # Was this the last partition?
+ jz err_noboot
+ movw GPT_ADDR+GPT_PART_SIZE,%ax
+ addw %ax,%bx # Next partition
+ cmpw $PART_ADDR+0x200,%bx # Still in sector?
+ jb scan
+ incl GPT_ADDR+GPT_PART_LBA # Next sector
+ adcl $0,GPT_ADDR+GPT_PART_LBA+4
+ jmp load_part
+#
+# Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating
+# a EDD packet on the stack and passing it to the BIOS. Trashes %ax and %si.
+#
+read: pushl 0x4(%si) # Set the LBA
+ pushl 0x0(%si) # address
+ pushw %es # Set the address of
+ pushw %bx # the transfer buffer
+ pushw $0x1 # Read 1 sector
+ pushw $0x10 # Packet length
+ movw %sp,%si # Packer pointer
+ movw $0x4200,%ax # BIOS: LBA Read from disk
+ int $0x13 # Call the BIOS
+ add $0x10,%sp # Restore stack
+ jc err_rd # If error
+ ret
+#
+# Check the number of LBAs on the drive index %dx. Trashes %ax and %si.
+#
+getdrvparams:
+ movw $DPBUF,%si # Set the address of result buf
+ movw $0x001e,(%si) # len
+ movw $0x4800,%ax # BIOS: Read Drive Parameters
+ int $0x13 # Call the BIOS
+ jc err_rd # "I/O error" if error
+ ret
+#
+# Various error message entry points.
+#
+err_big: movw $msg_big,%si # "Boot loader too
+ jmp putstr # large"
+
+err_pt: movw $msg_pt,%si # "Invalid partition
+ jmp putstr # table"
+
+err_rd: movw $msg_rd,%si # "I/O error loading
+ jmp putstr # boot loader"
+
+err_noboot: movw $msg_noboot,%si # "Missing boot
+ jmp putstr # loader"
+#
+# Output an ASCIZ string to the console via the BIOS.
+#
+putstr.0: movw $0x7,%bx # Page:attribute
+ movb $0xe,%ah # BIOS: Display
+ int $0x10 # character
+putstr: lodsb # Get character
+ testb %al,%al # End of string?
+ jnz putstr.0 # No
+putstr.1: jmp putstr.1 # Await reset
+
+msg_big: .asciz "Boot loader too large"
+msg_pt: .asciz "Invalid partition table"
+msg_rd: .asciz "I/O error loading boot loader"
+msg_noboot: .asciz "Missing boot loader"
+
+lba: .quad 1 # LBA of GPT header
+
+boot_uuid: .long 0x83bd6b9d
+ .word 0x7f41
+ .word 0x11dc
+ .byte 0xbe
+ .byte 0x0b
+ .byte 0x00
+ .byte 0x15
+ .byte 0x60
+ .byte 0xb8
+ .byte 0x4f
+ .byte 0x0f
+
+ .org DISKSIG,0x90
+sig: .long 0 # OS Disk Signature
+ .word 0 # "Unknown" in PMBR
+
+partbl: .fill 0x10,0x4,0x0 # Partition table
+ .word MAGIC # Magic number
diff --git a/stand/i386/pxeldr/Makefile b/stand/i386/pxeldr/Makefile
new file mode 100644
index 0000000..819283c
--- /dev/null
+++ b/stand/i386/pxeldr/Makefile
@@ -0,0 +1,47 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= ${LDR}
+INTERNALPROG=
+FILES= ${BOOT}
+MAN= ${BOOT}.8
+SRCS= ${LDR}.S
+CLEANFILES+= ${BOOT}
+
+BOOT= pxeboot
+LDR= pxeldr
+ORG= 0x7c00
+LOADER= loader
+
+.if defined(BOOT_PXELDR_PROBE_KEYBOARD)
+CFLAGS+=-DPROBE_KEYBOARD
+.endif
+
+.if defined(BOOT_PXELDR_ALWAYS_SERIAL)
+CFLAGS+=-DALWAYS_SERIAL
+.endif
+
+CFLAGS+=-I${BOOTSRC}/i386/common
+
+LOADERBIN= ${BOOTOBJ}/i386/loader/loader.bin
+
+CLEANFILES+= ${BOOT}.tmp
+
+${BOOT}: ${LDR} ${LOADER}
+ cat ${LDR} ${LOADER} > ${.TARGET}.tmp
+ ${DD} if=${.TARGET}.tmp of=${.TARGET} obs=2k conv=osync
+ rm ${.TARGET}.tmp
+
+LDFLAGS+=${LDFLAGS_BIN}
+
+CLEANFILES+= ${LOADER}
+
+${LOADER}: ${LOADERBIN} ${BTXLDR} ${BTXKERN}
+ btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \
+ -b ${BTXKERN} ${LOADERBIN}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.pxeldr.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/pxeldr/Makefile.depend b/stand/i386/pxeldr/Makefile.depend
new file mode 100644
index 0000000..c6f574a
--- /dev/null
+++ b/stand/i386/pxeldr/Makefile.depend
@@ -0,0 +1,15 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/btxldr \
+ sys/boot/i386/loader \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/pxeldr/pxeboot.8 b/stand/i386/pxeldr/pxeboot.8
new file mode 100644
index 0000000..0194e21
--- /dev/null
+++ b/stand/i386/pxeldr/pxeboot.8
@@ -0,0 +1,158 @@
+.\" Copyright (c) 1999 Doug White
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 27, 2017
+.Dt PXEBOOT 8
+.Os
+.Sh NAME
+.Nm pxeboot
+.Nd Preboot Execution Environment (PXE) bootloader
+.Sh DESCRIPTION
+The
+.Nm
+bootloader is a modified version of the system third-stage bootstrap
+.Xr loader 8
+configured to run under Intel's Preboot Execution Environment (PXE) system.
+PXE is a form of smart boot ROM, built into Intel EtherExpress Pro/100 and
+3Com 3c905c Ethernet cards, and Ethernet-equipped Intel motherboards.
+PXE supports DHCP configuration and provides low-level NIC access services.
+.Pp
+The DHCP client will set a DHCP user class named
+.Va FreeBSD
+to allow flexible configuration of the DHCP server.
+.Pp
+The
+.Nm
+bootloader retrieves the kernel, modules,
+and other files either via NFS over UDP or by TFTP,
+selectable through compile-time options.
+In combination with a memory file system image or NFS-mounted root file system,
+.Nm
+allows for easy,
+EEPROM-burner free construction of diskless machines.
+.Pp
+The
+.Nm
+binary is loaded just like any other boot file,
+by specifying it in the DHCP server's configuration file.
+Below is a sample configuration for the ISC DHCP v2 server:
+.Bd -literal -offset indent
+option domain-name "example.com";
+option routers 10.0.0.1;
+option subnet-mask 255.255.255.0;
+option broadcast-address 10.0.0.255;
+option domain-name-servers 10.0.0.1;
+server-name "DHCPserver";
+server-identifier 10.0.0.1;
+
+default-lease-time 120;
+max-lease-time 120;
+
+subnet 10.0.0.0 netmask 255.255.255.0 {
+ filename "pxeboot";
+ range 10.0.0.10 10.0.0.254;
+ if exists user-class and option user-class = "FreeBSD" {
+ option root-path "tftp://10.0.0.1/FreeBSD";
+ }
+}
+
+.Ed
+.Nm
+recognizes
+.Va next-server
+and
+.Va option root-path
+directives as the server and path to NFS mount for file requests,
+respectively, or the server to make TFTP requests to.
+Note that
+.Nm
+expects to fetch
+.Pa /boot/loader.rc
+from the specified server before loading any other files.
+.Pp
+Valid
+.Va option root-path
+Syntax is the following
+.Bl -tag -width <scheme>://ip/path indent
+.It /path
+path to the root filesystem on the NFS server
+.It ip:/path
+path to the root filesystem on the NFS server
+.Ar ip
+.It nfs:/path
+path to the root filesystem on the NFS server
+.It nfs://ip/path
+path to the root filesystem on the NFS server
+.Ar ip
+.It tftp:/path
+path to the root filesystem on the TFTP server
+.It tftp://ip/path
+path to the root filesystem on the TFTP server
+.Ar ip
+.El
+.Pp
+.Nm
+defaults to a conservative 1024 byte NFS data packet size.
+This may be changed by setting the
+.Va nfs.read_size
+variable in
+.Pa /boot/loader.conf .
+Valid values range from 1024 to 16384 bytes.
+.Pp
+In all other respects,
+.Nm
+acts just like
+.Xr loader 8 .
+.Pp
+As PXE is still in its infancy, some firmware versions may not work
+properly.
+The
+.Nm
+bootloader has been extensively tested on version 0.99 of Intel firmware;
+pre-release versions of the newer 2.0 firmware are known to have
+problems.
+Check with the device's manufacturer for their latest stable release.
+.Pp
+For further information on Intel's PXE specifications and Wired for
+Management (WfM) systems, see
+.Li http://www.intel.com/design/archives/wfm/ .
+.Sh SEE ALSO
+.Xr loader 8
+.Sh HISTORY
+The
+.Nm
+bootloader first appeared in
+.Fx 4.1 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+bootloader was written by
+.An John Baldwin Aq jhb@FreeBSD.org
+and
+.An Paul Saab Aq ps@FreeBSD.org .
+This manual page was written by
+.An Doug White Aq dwhite@FreeBSD.org .
diff --git a/stand/i386/pxeldr/pxeldr.S b/stand/i386/pxeldr/pxeldr.S
new file mode 100644
index 0000000..ee1e18a
--- /dev/null
+++ b/stand/i386/pxeldr/pxeldr.S
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2000 John Baldwin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * This simple program is a preloader for the normal boot3 loader. It is simply
+ * prepended to the beginning of a fully built and btxld'd loader. It then
+ * copies the loader to the address boot2 normally loads it, emulates the
+ * boot[12] environment (protected mode, a bootinfo struct, etc.), and then jumps
+ * to the start of btxldr to start the boot process. This method allows a stock
+ * /boot/loader to be booted over the network via PXE w/o having to write a
+ * separate PXE-aware client just to load the loader.
+ */
+
+#include <sys/reboot.h>
+#include <bootargs.h>
+
+/*
+ * Memory locations.
+ */
+ .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k
+ .set MEM_ARG,0x900 # Arguments at start
+ .set MEM_ARG_BTX,0xa100 # Where we move them to so the
+ # BTX client can see them
+ .set MEM_ARG_SIZE,0x18 # Size of the arguments
+ .set MEM_BTX_ADDRESS,0x9000 # where BTX lives
+ .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute
+ .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader
+ .set MEM_BTX_CLIENT,0xa000 # where BTX clients live
+ .set MEM_BIOS_KEYBOARD,0x496 # BDA byte with keyboard bit
+/*
+ * a.out header fields
+ */
+ .set AOUT_TEXT,0x04 # text segment size
+ .set AOUT_DATA,0x08 # data segment size
+ .set AOUT_BSS,0x0c # zero'd BSS size
+ .set AOUT_SYMBOLS,0x10 # symbol table
+ .set AOUT_ENTRY,0x14 # entry point
+ .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header
+/*
+ * Segment selectors.
+ */
+ .set SEL_SDATA,0x8 # Supervisor data
+ .set SEL_RDATA,0x10 # Real mode data
+ .set SEL_SCODE,0x18 # PM-32 code
+ .set SEL_SCODE16,0x20 # PM-16 code
+/*
+ * BTX constants
+ */
+ .set INT_SYS,0x30 # BTX syscall interrupt
+/*
+ * Bit in MEM_BIOS_KEYBOARD that is set if an enhanced keyboard is present
+ */
+ .set KEYBOARD_BIT,0x10
+/*
+ * We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry
+ * point)
+ */
+ .code16
+ .globl start
+ .org 0x0, 0x0
+/*
+ * BTX program loader for PXE network booting
+ */
+start: cld # string ops inc
+ xorw %ax, %ax # zero %ax
+ movw %ax, %ss # setup the
+ movw $start, %sp # stack
+ movw %es, %cx # save PXENV+ segment
+ movw %ax, %ds # setup the
+ movw %ax, %es # data segments
+ andl $0xffff, %ecx # clear upper words
+ andl $0xffff, %ebx # of %ebx and %ecx
+ shll $4, %ecx # calculate the offset of
+ addl %ebx, %ecx # the PXENV+ struct and
+ pushl %ecx # save it on the stack
+ movw $welcome_msg, %si # %ds:(%si) -> welcome message
+ callw putstr # display the welcome message
+/*
+ * Setup the arguments that the loader is expecting from boot[12]
+ */
+ movw $bootinfo_msg, %si # %ds:(%si) -> boot args message
+ callw putstr # display the message
+ movw $MEM_ARG, %bx # %ds:(%bx) -> boot args
+ movw %bx, %di # %es:(%di) -> boot args
+ xorl %eax, %eax # zero %eax
+ movw $(MEM_ARG_SIZE/4), %cx # Size of arguments in 32-bit
+ # dwords
+ rep # Clear the arguments
+ stosl # to zero
+ orb $KARGS_FLAGS_PXE, 0x8(%bx) # kargs->bootflags |=
+ # KARGS_FLAGS_PXE
+ popl 0xc(%bx) # kargs->pxeinfo = *PXENV+
+#ifdef ALWAYS_SERIAL
+/*
+ * set the RBX_SERIAL bit in the howto byte.
+ */
+ orl $RB_SERIAL, (%bx) # enable serial console
+#endif
+#ifdef PROBE_KEYBOARD
+/*
+ * Look at the BIOS data area to see if we have an enhanced keyboard. If not,
+ * set the RBX_DUAL and RBX_SERIAL bits in the howto byte.
+ */
+ testb $KEYBOARD_BIT, MEM_BIOS_KEYBOARD # keyboard present?
+ jnz keyb # yes, so skip
+ orl $(RB_MULTIPLE | RB_SERIAL), (%bx) # enable serial console
+keyb:
+#endif
+/*
+ * Turn on the A20 address line
+ */
+ callw seta20 # Turn A20 on
+/*
+ * Relocate the loader and BTX using a very lazy protected mode
+ */
+ movw $relocate_msg, %si # Display the
+ callw putstr # relocation message
+ movl end+AOUT_ENTRY, %edi # %edi is the destination
+ movl $(end+AOUT_HEADER), %esi # %esi is
+ # the start of the text
+ # segment
+ movl end+AOUT_TEXT, %ecx # %ecx = length of the text
+ # segment
+ lgdt gdtdesc # setup our own gdt
+ cli # turn off interrupts
+ movl %cr0, %eax # Turn on
+ orb $0x1, %al # protected
+ movl %eax, %cr0 # mode
+ ljmp $SEL_SCODE,$pm_start # long jump to clear the
+ # instruction pre-fetch queue
+ .code32
+pm_start: movw $SEL_SDATA, %ax # Initialize
+ movw %ax, %ds # %ds and
+ movw %ax, %es # %es to a flat selector
+ rep # Relocate the
+ movsb # text segment
+ addl $(MEM_PAGE_SIZE - 1), %edi # pad %edi out to a new page
+ andl $~(MEM_PAGE_SIZE - 1), %edi # for the data segment
+ movl end+AOUT_DATA, %ecx # size of the data segment
+ rep # Relocate the
+ movsb # data segment
+ movl end+AOUT_BSS, %ecx # size of the bss
+ xorl %eax, %eax # zero %eax
+ addb $3, %cl # round %ecx up to
+ shrl $2, %ecx # a multiple of 4
+ rep # zero the
+ stosl # bss
+ movl end+AOUT_ENTRY, %esi # %esi -> relocated loader
+ addl $MEM_BTX_OFFSET, %esi # %esi -> BTX in the loader
+ movl $MEM_BTX_ADDRESS, %edi # %edi -> where BTX needs to go
+ movzwl 0xa(%esi), %ecx # %ecx -> length of BTX
+ rep # Relocate
+ movsb # BTX
+ ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM
+ .code16
+pm_16: movw $SEL_RDATA, %ax # Initialize
+ movw %ax, %ds # %ds and
+ movw %ax, %es # %es to a real mode selector
+ movl %cr0, %eax # Turn off
+ andb $~0x1, %al # protected
+ movl %eax, %cr0 # mode
+ ljmp $0,$pm_end # Long jump to clear the
+ # instruction pre-fetch queue
+pm_end: sti # Turn interrupts back on now
+/*
+ * Copy the BTX client to MEM_BTX_CLIENT
+ */
+ xorw %ax, %ax # zero %ax and set
+ movw %ax, %ds # %ds and %es
+ movw %ax, %es # to segment 0
+ movw $MEM_BTX_CLIENT, %di # Prepare to relocate
+ movw $btx_client, %si # the simple btx client
+ movw $(btx_client_end-btx_client), %cx # length of btx client
+ rep # Relocate the
+ movsb # simple BTX client
+/*
+ * Copy the boot[12] args to where the BTX client can see them
+ */
+ movw $MEM_ARG, %si # where the args are at now
+ movw $MEM_ARG_BTX, %di # where the args are moving to
+ movw $(MEM_ARG_SIZE/4), %cx # size of the arguments in longs
+ rep # Relocate
+ movsl # the words
+/*
+ * Save the entry point so the client can get to it later on
+ */
+ movl end+AOUT_ENTRY, %eax # load the entry point
+ stosl # add it to the end of the
+ # arguments
+/*
+ * Now we just start up BTX and let it do the rest
+ */
+ movw $jump_message, %si # Display the
+ callw putstr # jump message
+ ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point
+
+/*
+ * Display a null-terminated string
+ */
+putstr: lodsb # load %al from %ds:(%si)
+ testb %al,%al # stop at null
+ jnz putc # if the char != null, output it
+ retw # return when null is hit
+putc: movw $0x7,%bx # attribute for output
+ movb $0xe,%ah # BIOS: put_char
+ int $0x10 # call BIOS, print char in %al
+ jmp putstr # keep looping
+
+/*
+ * Enable A20. Put an upper limit on the amount of time we wait for the
+ * keyboard controller to get ready (65K x ISA access time). If
+ * we wait more than that amount, the hardware is probably
+ * legacy-free and simply doesn't have a keyboard controller.
+ * Thus, the A20 line is already enabled.
+ */
+seta20: cli # Disable interrupts
+ xor %cx,%cx # Clear
+seta20.1: inc %cx # Increment, overflow?
+ jz seta20.3 # Yes
+ inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.1 # Yes
+ movb $0xd1,%al # Command: Write
+ outb %al,$0x64 # output port
+seta20.2: inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.2 # Yes
+ movb $0xdf,%al # Enable
+ outb %al,$0x60 # A20
+seta20.3: sti # Enable interrupts
+ retw # To caller
+
+/*
+ * BTX client to start btxldr
+ */
+ .code32
+btx_client: movl $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi
+ # %ds:(%esi) -> end
+ # of boot[12] args
+ movl $(MEM_ARG_SIZE/4), %ecx # Number of words to push
+ std # Go backwards
+push_arg: lodsl # Read argument
+ pushl %eax # Push it onto the stack
+ loop push_arg # Push all of the arguments
+ cld # In case anyone depends on this
+ pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of
+ # the loader
+ pushl %eax # Emulate a near call
+ movl $0x1, %eax # 'exec' system call
+ int $INT_SYS # BTX system call
+btx_client_end:
+ .code16
+
+ .p2align 4
+/*
+ * Global descriptor table.
+ */
+gdt: .word 0x0,0x0,0x0,0x0 # Null entry
+ .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit)
+ .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit)
+gdt.1:
+/*
+ * Pseudo-descriptors.
+ */
+gdtdesc: .word gdt.1-gdt-1 # Limit
+ .long gdt # Base
+
+welcome_msg: .asciz "PXE Loader 1.00\r\n\n"
+bootinfo_msg: .asciz "Building the boot loader arguments\r\n"
+relocate_msg: .asciz "Relocating the loader and the BTX\r\n"
+jump_message: .asciz "Starting the BTX loader\r\n"
+
+ .p2align 4
+end:
diff --git a/stand/i386/zfsboot/Makefile b/stand/i386/zfsboot/Makefile
new file mode 100644
index 0000000..f0523df
--- /dev/null
+++ b/stand/i386/zfsboot/Makefile
@@ -0,0 +1,93 @@
+# $FreeBSD$
+
+HAVE_GELI=yes
+
+.include <bsd.init.mk>
+
+.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/common ${SASRC}
+
+FILES= zfsboot
+MAN= zfsboot.8
+
+NM?= nm
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+REL1= 0x700
+ORG1= 0x7c00
+ORG2= 0x2000
+
+CFLAGS+=-DBOOTPROG=\"zfsboot\" \
+ -O1 \
+ -DZFS -DBOOT2 \
+ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} \
+ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ -I${LDRSRC} \
+ -I${BOOTSRC}/i386/common \
+ -I${BOOTSRC}/i386 \
+ -I${ZFSSRC} \
+ -I${SYSDIR}/cddl/boot/zfs \
+ -I${BTXLIB} \
+ -I${BOOTSRC}/i386/boot2 \
+ -Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+ -Winline
+
+CFLAGS.gcc+= --param max-inline-insns-single=100
+.if ${MACHINE} == "amd64"
+LIBZFSBOOT=${BOOTOBJ}/zfs32/libzfsboot.a
+.else
+LIBZFSBOOT=${BOOTOBJ}/zfs/libzfsboot.a
+.endif
+
+LD_FLAGS+=${LD_FLAGS_BIN}
+
+CLEANFILES+= zfsboot
+
+zfsboot: zfsboot1 zfsboot2
+ cat zfsboot1 zfsboot2 > zfsboot
+
+CLEANFILES+= zfsboot1 zfsldr.out zfsldr.o
+
+zfsboot1: zfsldr.out
+ ${OBJCOPY} -S -O binary zfsldr.out ${.TARGET}
+
+zfsldr.out: zfsldr.o
+ ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} zfsldr.o
+
+CLEANFILES+= zfsboot2 zfsboot.ld zfsboot.ldr zfsboot.bin zfsboot.out \
+ zfsboot.o zfsboot.s zfsboot.s.tmp sio.o cons.o drv.o
+
+# We currently allow 128k bytes for zfsboot - in practice it could be
+# any size up to 3.5Mb but keeping it fixed size simplifies zfsldr.
+#
+BOOT2SIZE= 131072
+
+zfsboot2: zfsboot.ld
+ @set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \
+ echo "$$x bytes available"; test $$x -ge 0
+ ${DD} if=${.ALLSRC} of=${.TARGET} obs=${BOOT2SIZE} conv=osync
+
+zfsboot.ld: zfsboot.ldr zfsboot.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l zfsboot.ldr \
+ -o ${.TARGET} -P 1 zfsboot.bin
+
+zfsboot.ldr:
+ cp /dev/null ${.TARGET}
+
+zfsboot.bin: zfsboot.out
+ ${OBJCOPY} -S -O binary zfsboot.out ${.TARGET}
+
+zfsboot.out: ${BTXCRT} zfsboot.o sio.o drv.o cons.o
+ ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBZFSBOOT} ${LIBGELIBOOT} ${LIBSA32}
+
+SRCS= zfsboot.c
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.zfsldr.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/zfsboot/Makefile.depend b/stand/i386/zfsboot/Makefile.depend
new file mode 100644
index 0000000..63a43d8
--- /dev/null
+++ b/stand/i386/zfsboot/Makefile.depend
@@ -0,0 +1,16 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/lib \
+ sys/boot/libstand32 \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/zfsboot/zfsboot.8 b/stand/i386/zfsboot/zfsboot.8
new file mode 100644
index 0000000..6450939
--- /dev/null
+++ b/stand/i386/zfsboot/zfsboot.8
@@ -0,0 +1,133 @@
+.\" Copyright (c) 2014 Andriy Gapon <avg@FreeBSD.org>
+.\" 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 15, 2014
+.Dt ZFSBOOT 8
+.Os
+.Sh NAME
+.Nm zfsboot
+.Nd bootcode for ZFS on BIOS-based computers
+.Sh DESCRIPTION
+.Nm
+is used on BIOS-based computers to boot from a filesystem in
+a ZFS pool.
+.Nm
+is installed in two parts on a disk or a partition used by a ZFS pool.
+The first part, a single-sector starter boot block, is installed
+at the beginning of the disk or partition.
+The second part, a main boot block, is installed at a special offset
+within the disk or partition.
+Both areas are reserved by the ZFS on-disk specification for boot use.
+If
+.Nm
+is installed in a partition, then that partition should be made
+bootable using appropriate configuration and boot blocks described in
+.Xr boot 8 .
+.Sh BOOTING
+The
+.Nm
+boot process is very similar to that of
+.Xr gptzfsboot 8 .
+One significant difference is that
+.Nm
+does not currently support the GPT partitioning scheme.
+Thus only whole disks and MBR partitions, traditionally referred to as
+slices, are probed for ZFS disk labels.
+See the BUGS section in
+.Xr gptzfsboot 8
+for some limitations of the MBR scheme support.
+.Sh USAGE
+.Nm
+supports all the same prompt and configuration file arguments as
+.Xr gptzfsboot 8 .
+.Sh FILES
+.Bl -tag -width /boot/zfsboot -compact
+.It Pa /boot/zfsboot
+boot code binary
+.It Pa /boot.config
+parameters for the boot block
+.Pq optional
+.It Pa /boot/config
+alternative parameters for the boot block
+.Pq optional
+.El
+.Sh EXAMPLES
+.Nm
+is typically installed using
+.Xr dd 1 .
+To install
+.Nm
+on the
+.Pa ada0
+drive:
+.Bd -literal -offset indent
+dd if=/boot/zfsboot of=/dev/ada0 count=1
+dd if=/boot/zfsboot of=/dev/ada0 iseek=1 oseek=1024
+.Ed
+.Pp
+If the drive is currently in use, the GEOM safety will prevent writes
+and must be disabled before running the above commands:
+.Bd -literal -offset indent
+sysctl kern.geom.debugflags=0x10
+.Ed
+.Pp
+.Nm
+can also be installed in an MBR slice:
+.Bd -literal -offset indent
+gpart create -s mbr ada0
+gpart add -t freebsd ada0
+gpart create -s BSD ada0s1
+gpart bootcode -b /boot/boot0 ada0
+gpart set -a active -i 1 ada0
+dd if=/boot/zfsboot of=/dev/ada0s1 count=1
+dd if=/boot/zfsboot of=/dev/ada0s1 iseek=1 oseek=1024
+.Ed
+.Pp
+Note that commands to create and populate a pool are not shown
+in the example above.
+.Sh SEE ALSO
+.Xr dd 1 ,
+.Xr boot.config 5 ,
+.Xr boot 8 ,
+.Xr gptzfsboot 8 ,
+.Xr loader 8 ,
+.Xr zfsloader 8 ,
+.Xr zpool 8
+.Sh HISTORY
+.Nm
+appeared in FreeBSD 7.3.
+.Sh AUTHORS
+This manual page was written by
+.An Andriy Gapon Aq avg@FreeBSD.org .
+.Sh BUGS
+Installing
+.Nm
+with
+.Xr dd 1
+is a hack.
+ZFS needs a command to properly install
+.Nm
+onto a ZFS-controlled disk or partition.
diff --git a/stand/i386/zfsboot/zfsboot.c b/stand/i386/zfsboot/zfsboot.c
new file mode 100644
index 0000000..88b5231
--- /dev/null
+++ b/stand/i386/zfsboot/zfsboot.c
@@ -0,0 +1,1151 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/diskmbr.h>
+#ifdef GPT
+#include <sys/gpt.h>
+#endif
+#include <sys/reboot.h>
+#include <sys/queue.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <machine/pc/bios.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <a.out.h>
+
+#include <btxv86.h>
+
+#include "lib.h"
+#include "rbx.h"
+#include "drv.h"
+#include "edd.h"
+#include "util.h"
+#include "cons.h"
+#include "bootargs.h"
+#include "paths.h"
+
+#include "libzfs.h"
+
+#define ARGS 0x900
+#define NOPT 14
+#define NDEV 3
+
+#define BIOS_NUMDRIVES 0x475
+#define DRV_HARD 0x80
+#define DRV_MASK 0x7f
+
+#define TYPE_AD 0
+#define TYPE_DA 1
+#define TYPE_MAXHARD TYPE_DA
+#define TYPE_FD 2
+
+#define DEV_GELIBOOT_BSIZE 4096
+
+extern uint32_t _end;
+
+#ifdef GPT
+static const uuid_t freebsd_zfs_uuid = GPT_ENT_TYPE_FREEBSD_ZFS;
+#endif
+static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
+static const unsigned char flags[NOPT] = {
+ RBX_DUAL,
+ RBX_SERIAL,
+ RBX_ASKNAME,
+ RBX_CDROM,
+ RBX_CONFIG,
+ RBX_KDB,
+ RBX_GDB,
+ RBX_MUTE,
+ RBX_NOINTR,
+ RBX_PAUSE,
+ RBX_QUIET,
+ RBX_DFLTROOT,
+ RBX_SINGLE,
+ RBX_VERBOSE
+};
+uint32_t opts;
+
+static const unsigned char dev_maj[NDEV] = {30, 4, 2};
+
+static char cmd[512];
+static char cmddup[512];
+static char kname[1024];
+static char rootname[256];
+static int comspeed = SIOSPD;
+static struct bootinfo bootinfo;
+static uint32_t bootdev;
+static struct zfs_boot_args zfsargs;
+static struct zfsmount zfsmount;
+
+vm_offset_t high_heap_base;
+uint32_t bios_basemem, bios_extmem, high_heap_size;
+
+static struct bios_smap smap;
+
+/*
+ * The minimum amount of memory to reserve in bios_extmem for the heap.
+ */
+#define HEAP_MIN (64 * 1024 * 1024)
+
+static char *heap_next;
+static char *heap_end;
+
+/* Buffers that must not span a 64k boundary. */
+#define READ_BUF_SIZE 8192
+struct dmadat {
+ char rdbuf[READ_BUF_SIZE]; /* for reading large things */
+ char secbuf[READ_BUF_SIZE]; /* for MBR/disklabel */
+};
+static struct dmadat *dmadat;
+
+void exit(int);
+void reboot(void);
+static void load(void);
+static int parse_cmd(void);
+static void bios_getmem(void);
+void *malloc(size_t n);
+void free(void *ptr);
+int main(void);
+
+void *
+malloc(size_t n)
+{
+ char *p = heap_next;
+ if (p + n > heap_end) {
+ printf("malloc failure\n");
+ for (;;)
+ ;
+ /* NOTREACHED */
+ return (0);
+ }
+ heap_next += n;
+ return (p);
+}
+
+void
+free(void *ptr)
+{
+
+ return;
+}
+
+static char *
+strdup(const char *s)
+{
+ char *p = malloc(strlen(s) + 1);
+ strcpy(p, s);
+ return (p);
+}
+
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.c"
+static char gelipw[GELI_PW_MAXLEN];
+static struct keybuf *gelibuf;
+#endif
+
+#include "zfsimpl.c"
+
+/*
+ * Read from a dnode (which must be from a ZPL filesystem).
+ */
+static int
+zfs_read(spa_t *spa, const dnode_phys_t *dnode, off_t *offp, void *start, size_t size)
+{
+ const znode_phys_t *zp = (const znode_phys_t *) dnode->dn_bonus;
+ size_t n;
+ int rc;
+
+ n = size;
+ if (*offp + n > zp->zp_size)
+ n = zp->zp_size - *offp;
+
+ rc = dnode_read(spa, dnode, *offp, start, n);
+ if (rc)
+ return (-1);
+ *offp += n;
+
+ return (n);
+}
+
+/*
+ * Current ZFS pool
+ */
+static spa_t *spa;
+static spa_t *primary_spa;
+static vdev_t *primary_vdev;
+
+/*
+ * A wrapper for dskread that doesn't have to worry about whether the
+ * buffer pointer crosses a 64k boundary.
+ */
+static int
+vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
+{
+ char *p;
+ daddr_t lba, alignlba;
+ off_t diff;
+ unsigned int nb, alignnb;
+ struct dsk *dsk = (struct dsk *) priv;
+
+ if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
+ return -1;
+
+ p = buf;
+ lba = off / DEV_BSIZE;
+ lba += dsk->start;
+ /*
+ * Align reads to 4k else 4k sector GELIs will not decrypt.
+ * Round LBA down to nearest multiple of DEV_GELIBOOT_BSIZE bytes.
+ */
+ alignlba = rounddown2(off, DEV_GELIBOOT_BSIZE) / DEV_BSIZE;
+ /*
+ * The read must be aligned to DEV_GELIBOOT_BSIZE bytes relative to the
+ * start of the GELI partition, not the start of the actual disk.
+ */
+ alignlba += dsk->start;
+ diff = (lba - alignlba) * DEV_BSIZE;
+
+ while (bytes > 0) {
+ nb = bytes / DEV_BSIZE;
+ /*
+ * Ensure that the read size plus the leading offset does not
+ * exceed the size of the read buffer.
+ */
+ if (nb > (READ_BUF_SIZE - diff) / DEV_BSIZE)
+ nb = (READ_BUF_SIZE - diff) / DEV_BSIZE;
+ /*
+ * Round the number of blocks to read up to the nearest multiple
+ * of DEV_GELIBOOT_BSIZE.
+ */
+ alignnb = roundup2(nb * DEV_BSIZE + diff, DEV_GELIBOOT_BSIZE)
+ / DEV_BSIZE;
+
+ if (drvread(dsk, dmadat->rdbuf, alignlba, alignnb))
+ return -1;
+#ifdef LOADER_GELI_SUPPORT
+ /* decrypt */
+ if (is_geli(dsk) == 0) {
+ if (geli_read(dsk, ((alignlba - dsk->start) *
+ DEV_BSIZE), dmadat->rdbuf, alignnb * DEV_BSIZE))
+ return (-1);
+ }
+#endif
+ memcpy(p, dmadat->rdbuf + diff, nb * DEV_BSIZE);
+ p += nb * DEV_BSIZE;
+ lba += nb;
+ alignlba += alignnb;
+ bytes -= nb * DEV_BSIZE;
+ /* Don't need the leading offset after the first block. */
+ diff = 0;
+ }
+
+ return 0;
+}
+
+static int
+vdev_write(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
+{
+ char *p;
+ daddr_t lba;
+ unsigned int nb;
+ struct dsk *dsk = (struct dsk *) priv;
+
+ if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
+ return -1;
+
+ p = buf;
+ lba = off / DEV_BSIZE;
+ lba += dsk->start;
+ while (bytes > 0) {
+ nb = bytes / DEV_BSIZE;
+ if (nb > READ_BUF_SIZE / DEV_BSIZE)
+ nb = READ_BUF_SIZE / DEV_BSIZE;
+ memcpy(dmadat->rdbuf, p, nb * DEV_BSIZE);
+ if (drvwrite(dsk, dmadat->rdbuf, lba, nb))
+ return -1;
+ p += nb * DEV_BSIZE;
+ lba += nb;
+ bytes -= nb * DEV_BSIZE;
+ }
+
+ return 0;
+}
+
+static int
+xfsread(const dnode_phys_t *dnode, off_t *offp, void *buf, size_t nbyte)
+{
+ if ((size_t)zfs_read(spa, dnode, offp, buf, nbyte) != nbyte) {
+ printf("Invalid format\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Read Pad2 (formerly "Boot Block Header") area of the first
+ * vdev label of the given vdev.
+ */
+static int
+vdev_read_pad2(vdev_t *vdev, char *buf, size_t size)
+{
+ blkptr_t bp;
+ char *tmp = zap_scratch;
+ off_t off = offsetof(vdev_label_t, vl_pad2);
+
+ if (size > VDEV_PAD_SIZE)
+ size = VDEV_PAD_SIZE;
+
+ BP_ZERO(&bp);
+ BP_SET_LSIZE(&bp, VDEV_PAD_SIZE);
+ BP_SET_PSIZE(&bp, VDEV_PAD_SIZE);
+ BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
+ BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
+ DVA_SET_OFFSET(BP_IDENTITY(&bp), off);
+ if (vdev_read_phys(vdev, &bp, tmp, off, 0))
+ return (EIO);
+ memcpy(buf, tmp, size);
+ return (0);
+}
+
+static int
+vdev_clear_pad2(vdev_t *vdev)
+{
+ char *zeroes = zap_scratch;
+ uint64_t *end;
+ off_t off = offsetof(vdev_label_t, vl_pad2);
+
+ memset(zeroes, 0, VDEV_PAD_SIZE);
+ end = (uint64_t *)(zeroes + VDEV_PAD_SIZE);
+ /* ZIO_CHECKSUM_LABEL magic and pre-calcualted checksum for all zeros */
+ end[-5] = 0x0210da7ab10c7a11;
+ end[-4] = 0x97f48f807f6e2a3f;
+ end[-3] = 0xaf909f1658aacefc;
+ end[-2] = 0xcbd1ea57ff6db48b;
+ end[-1] = 0x6ec692db0d465fab;
+ if (vdev_write(vdev, vdev->v_read_priv, off, zeroes, VDEV_PAD_SIZE))
+ return (EIO);
+ return (0);
+}
+
+static void
+bios_getmem(void)
+{
+ uint64_t size;
+
+ /* Parse system memory map */
+ v86.ebx = 0;
+ do {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x15; /* int 0x15 function 0xe820*/
+ v86.eax = 0xe820;
+ v86.ecx = sizeof(struct bios_smap);
+ v86.edx = SMAP_SIG;
+ v86.es = VTOPSEG(&smap);
+ v86.edi = VTOPOFF(&smap);
+ v86int();
+ if (V86_CY(v86.efl) || (v86.eax != SMAP_SIG))
+ break;
+ /* look for a low-memory segment that's large enough */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
+ (smap.length >= (512 * 1024)))
+ bios_basemem = smap.length;
+ /* look for the first segment in 'extended' memory */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) {
+ bios_extmem = smap.length;
+ }
+
+ /*
+ * Look for the largest segment in 'extended' memory beyond
+ * 1MB but below 4GB.
+ */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) &&
+ (smap.base < 0x100000000ull)) {
+ size = smap.length;
+
+ /*
+ * If this segment crosses the 4GB boundary, truncate it.
+ */
+ if (smap.base + size > 0x100000000ull)
+ size = 0x100000000ull - smap.base;
+
+ if (size > high_heap_size) {
+ high_heap_size = size;
+ high_heap_base = smap.base;
+ }
+ }
+ } while (v86.ebx != 0);
+
+ /* Fall back to the old compatibility function for base memory */
+ if (bios_basemem == 0) {
+ v86.ctl = 0;
+ v86.addr = 0x12; /* int 0x12 */
+ v86int();
+
+ bios_basemem = (v86.eax & 0xffff) * 1024;
+ }
+
+ /* Fall back through several compatibility functions for extended memory */
+ if (bios_extmem == 0) {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x15; /* int 0x15 function 0xe801*/
+ v86.eax = 0xe801;
+ v86int();
+ if (!V86_CY(v86.efl)) {
+ bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024;
+ }
+ }
+ if (bios_extmem == 0) {
+ v86.ctl = 0;
+ v86.addr = 0x15; /* int 0x15 function 0x88*/
+ v86.eax = 0x8800;
+ v86int();
+ bios_extmem = (v86.eax & 0xffff) * 1024;
+ }
+
+ /*
+ * If we have extended memory and did not find a suitable heap
+ * region in the SMAP, use the last 3MB of 'extended' memory as a
+ * high heap candidate.
+ */
+ if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
+ high_heap_size = HEAP_MIN;
+ high_heap_base = bios_extmem + 0x100000 - HEAP_MIN;
+ }
+}
+
+/*
+ * Try to detect a device supported by the legacy int13 BIOS
+ */
+static int
+int13probe(int drive)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x800;
+ v86.edx = drive;
+ v86int();
+
+ if (!V86_CY(v86.efl) && /* carry clear */
+ ((v86.edx & 0xff) != (drive & DRV_MASK))) { /* unit # OK */
+ if ((v86.ecx & 0x3f) == 0) { /* absurd sector size */
+ return(0); /* skip device */
+ }
+ return (1);
+ }
+ return(0);
+}
+
+/*
+ * We call this when we find a ZFS vdev - ZFS consumes the dsk
+ * structure so we must make a new one.
+ */
+static struct dsk *
+copy_dsk(struct dsk *dsk)
+{
+ struct dsk *newdsk;
+
+ newdsk = malloc(sizeof(struct dsk));
+ *newdsk = *dsk;
+ return (newdsk);
+}
+
+/*
+ * Get disk size from eax=0x800 and 0x4800. We need to probe both
+ * because 0x4800 may not be available and we would like to get more
+ * or less correct disk size - if it is possible at all.
+ * Note we do not really want to touch drv.c because that code is shared
+ * with boot2 and we can not afford to grow that code.
+ */
+static uint64_t
+drvsize_ext(struct dsk *dskp)
+{
+ uint64_t size, tmp;
+ int cyl, hds, sec;
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x800;
+ v86.edx = dskp->drive;
+ v86int();
+
+ /* Don't error out if we get bad sector number, try EDD as well */
+ if (V86_CY(v86.efl) || /* carry set */
+ (v86.edx & 0xff) <= (unsigned)(dskp->drive & 0x7f)) /* unit # bad */
+ return (0);
+
+ cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
+ /* Convert max head # -> # of heads */
+ hds = ((v86.edx & 0xff00) >> 8) + 1;
+ sec = v86.ecx & 0x3f;
+
+ size = (uint64_t)cyl * hds * sec;
+
+ /* Determine if we can use EDD with this device. */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4100;
+ v86.edx = dskp->drive;
+ v86.ebx = 0x55aa;
+ v86int();
+ if (V86_CY(v86.efl) || /* carry set */
+ (v86.ebx & 0xffff) != 0xaa55 || /* signature */
+ (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
+ return (size);
+
+ tmp = drvsize(dskp);
+ if (tmp > size)
+ size = tmp;
+
+ return (size);
+}
+
+/*
+ * The "layered" ioctl to read disk/partition size. Unfortunately
+ * the zfsboot case is hardest, because we do not have full software
+ * stack available, so we need to do some manual work here.
+ */
+uint64_t
+ldi_get_size(void *priv)
+{
+ struct dsk *dskp = priv;
+ uint64_t size = dskp->size;
+
+ if (dskp->start == 0)
+ size = drvsize_ext(dskp);
+
+ return (size * DEV_BSIZE);
+}
+
+static void
+probe_drive(struct dsk *dsk)
+{
+#ifdef GPT
+ struct gpt_hdr hdr;
+ struct gpt_ent *ent;
+ unsigned part, entries_per_sec;
+ daddr_t slba;
+#endif
+#if defined(GPT) || defined(LOADER_GELI_SUPPORT)
+ daddr_t elba;
+#endif
+
+ struct dos_partition *dp;
+ char *sec;
+ unsigned i;
+
+ /*
+ * If we find a vdev on the whole disk, stop here.
+ */
+ if (vdev_probe(vdev_read, dsk, NULL) == 0)
+ return;
+
+#ifdef LOADER_GELI_SUPPORT
+ /*
+ * Taste the disk, if it is GELI encrypted, decrypt it and check to see if
+ * it is a usable vdev then. Otherwise dig
+ * out the partition table and probe each slice/partition
+ * in turn for a vdev or GELI encrypted vdev.
+ */
+ elba = drvsize_ext(dsk);
+ if (elba > 0) {
+ elba--;
+ }
+ if (geli_taste(vdev_read, dsk, elba) == 0) {
+ if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw, dsk->unit,
+ ':', 0, dsk) == 0) {
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ return;
+ }
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+
+ sec = dmadat->secbuf;
+ dsk->start = 0;
+
+#ifdef GPT
+ /*
+ * First check for GPT.
+ */
+ if (drvread(dsk, sec, 1, 1)) {
+ return;
+ }
+ memcpy(&hdr, sec, sizeof(hdr));
+ if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0 ||
+ hdr.hdr_lba_self != 1 || hdr.hdr_revision < 0x00010000 ||
+ hdr.hdr_entsz < sizeof(*ent) || DEV_BSIZE % hdr.hdr_entsz != 0) {
+ goto trymbr;
+ }
+
+ /*
+ * Probe all GPT partitions for the presence of ZFS pools. We
+ * return the spa_t for the first we find (if requested). This
+ * will have the effect of booting from the first pool on the
+ * disk.
+ *
+ * If no vdev is found, GELI decrypting the device and try again
+ */
+ entries_per_sec = DEV_BSIZE / hdr.hdr_entsz;
+ slba = hdr.hdr_lba_table;
+ elba = slba + hdr.hdr_entries / entries_per_sec;
+ while (slba < elba) {
+ dsk->start = 0;
+ if (drvread(dsk, sec, slba, 1))
+ return;
+ for (part = 0; part < entries_per_sec; part++) {
+ ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz);
+ if (memcmp(&ent->ent_type, &freebsd_zfs_uuid,
+ sizeof(uuid_t)) == 0) {
+ dsk->start = ent->ent_lba_start;
+ dsk->size = ent->ent_lba_end - ent->ent_lba_start + 1;
+ dsk->slice = part + 1;
+ dsk->part = 255;
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ /*
+ * This slice had a vdev. We need a new dsk
+ * structure now since the vdev now owns this one.
+ */
+ dsk = copy_dsk(dsk);
+ }
+#ifdef LOADER_GELI_SUPPORT
+ else if (geli_taste(vdev_read, dsk, ent->ent_lba_end -
+ ent->ent_lba_start) == 0) {
+ if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw,
+ dsk->unit, 'p', dsk->slice, dsk) == 0) {
+ /*
+ * This slice has GELI, check it for ZFS.
+ */
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ /*
+ * This slice had a vdev. We need a new dsk
+ * structure now since the vdev now owns this one.
+ */
+ dsk = copy_dsk(dsk);
+ }
+ break;
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+ }
+ }
+ slba++;
+ }
+ return;
+trymbr:
+#endif /* GPT */
+
+ if (drvread(dsk, sec, DOSBBSECTOR, 1))
+ return;
+ dp = (void *)(sec + DOSPARTOFF);
+
+ for (i = 0; i < NDOSPART; i++) {
+ if (!dp[i].dp_typ)
+ continue;
+ dsk->start = dp[i].dp_start;
+ dsk->size = dp[i].dp_size;
+ dsk->slice = i + 1;
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ dsk = copy_dsk(dsk);
+ }
+#ifdef LOADER_GELI_SUPPORT
+ else if (geli_taste(vdev_read, dsk, dp[i].dp_size -
+ dp[i].dp_start) == 0) {
+ if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw, dsk->unit,
+ 's', i, dsk) == 0) {
+ /*
+ * This slice has GELI, check it for ZFS.
+ */
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ /*
+ * This slice had a vdev. We need a new dsk
+ * structure now since the vdev now owns this one.
+ */
+ dsk = copy_dsk(dsk);
+ }
+ break;
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+ }
+}
+
+int
+main(void)
+{
+ dnode_phys_t dn;
+ off_t off;
+ struct dsk *dsk;
+ int autoboot, i;
+ int nextboot;
+ int rc;
+
+ dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+
+ bios_getmem();
+
+ if (high_heap_size > 0) {
+ heap_end = PTOV(high_heap_base + high_heap_size);
+ heap_next = PTOV(high_heap_base);
+ } else {
+ heap_next = (char *)dmadat + sizeof(*dmadat);
+ heap_end = (char *)PTOV(bios_basemem);
+ }
+
+ dsk = malloc(sizeof(struct dsk));
+ dsk->drive = *(uint8_t *)PTOV(ARGS);
+ dsk->type = dsk->drive & DRV_HARD ? TYPE_AD : TYPE_FD;
+ dsk->unit = dsk->drive & DRV_MASK;
+ dsk->slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
+ dsk->part = 0;
+ dsk->start = 0;
+ dsk->size = 0;
+
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+ bootinfo.bi_basemem = bios_basemem / 1024;
+ bootinfo.bi_extmem = bios_extmem / 1024;
+ bootinfo.bi_memsizes_valid++;
+ bootinfo.bi_bios_dev = dsk->drive;
+
+ bootdev = MAKEBOOTDEV(dev_maj[dsk->type],
+ dsk->slice, dsk->unit, dsk->part);
+
+ /* Process configuration file */
+
+ autoboot = 1;
+
+#ifdef LOADER_GELI_SUPPORT
+ geli_init();
+#endif
+ zfs_init();
+
+ /*
+ * Probe the boot drive first - we will try to boot from whatever
+ * pool we find on that drive.
+ */
+ probe_drive(dsk);
+
+ /*
+ * Probe the rest of the drives that the bios knows about. This
+ * will find any other available pools and it may fill in missing
+ * vdevs for the boot pool.
+ */
+#ifndef VIRTUALBOX
+ for (i = 0; i < *(unsigned char *)PTOV(BIOS_NUMDRIVES); i++)
+#else
+ for (i = 0; i < MAXBDDEV; i++)
+#endif
+ {
+ if ((i | DRV_HARD) == *(uint8_t *)PTOV(ARGS))
+ continue;
+
+ if (!int13probe(i | DRV_HARD))
+ break;
+
+ dsk = malloc(sizeof(struct dsk));
+ dsk->drive = i | DRV_HARD;
+ dsk->type = dsk->drive & TYPE_AD;
+ dsk->unit = i;
+ dsk->slice = 0;
+ dsk->part = 0;
+ dsk->start = 0;
+ dsk->size = 0;
+ probe_drive(dsk);
+ }
+
+ /*
+ * The first discovered pool, if any, is the pool.
+ */
+ spa = spa_get_primary();
+ if (!spa) {
+ printf("%s: No ZFS pools located, can't boot\n", BOOTPROG);
+ for (;;)
+ ;
+ }
+
+ primary_spa = spa;
+ primary_vdev = spa_get_primary_vdev(spa);
+
+ nextboot = 0;
+ rc = vdev_read_pad2(primary_vdev, cmd, sizeof(cmd));
+ if (vdev_clear_pad2(primary_vdev))
+ printf("failed to clear pad2 area of primary vdev\n");
+ if (rc == 0) {
+ if (*cmd) {
+ /*
+ * We could find an old-style ZFS Boot Block header here.
+ * Simply ignore it.
+ */
+ if (*(uint64_t *)cmd != 0x2f5b007b10c) {
+ /*
+ * Note that parse() is destructive to cmd[] and we also want
+ * to honor RBX_QUIET option that could be present in cmd[].
+ */
+ nextboot = 1;
+ memcpy(cmddup, cmd, sizeof(cmd));
+ if (parse_cmd()) {
+ printf("failed to parse pad2 area of primary vdev\n");
+ reboot();
+ }
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("zfs nextboot: %s\n", cmddup);
+ }
+ /* Do not process this command twice */
+ *cmd = 0;
+ }
+ } else
+ printf("failed to read pad2 area of primary vdev\n");
+
+ /* Mount ZFS only if it's not already mounted via nextboot parsing. */
+ if (zfsmount.spa == NULL &&
+ (zfs_spa_init(spa) != 0 || zfs_mount(spa, 0, &zfsmount) != 0)) {
+ printf("%s: failed to mount default pool %s\n",
+ BOOTPROG, spa->spa_name);
+ autoboot = 0;
+ } else if (zfs_lookup(&zfsmount, PATH_CONFIG, &dn) == 0 ||
+ zfs_lookup(&zfsmount, PATH_DOTCONFIG, &dn) == 0) {
+ off = 0;
+ zfs_read(spa, &dn, &off, cmd, sizeof(cmd));
+ }
+
+ if (*cmd) {
+ /*
+ * Note that parse_cmd() is destructive to cmd[] and we also want
+ * to honor RBX_QUIET option that could be present in cmd[].
+ */
+ memcpy(cmddup, cmd, sizeof(cmd));
+ if (parse_cmd())
+ autoboot = 0;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%s: %s\n", PATH_CONFIG, cmddup);
+ /* Do not process this command twice */
+ *cmd = 0;
+ }
+
+ /* Do not risk waiting at the prompt forever. */
+ if (nextboot && !autoboot)
+ reboot();
+
+ /*
+ * Try to exec /boot/loader. If interrupted by a keypress,
+ * or in case of failure, try to load a kernel directly instead.
+ */
+
+ if (autoboot && !*kname) {
+ memcpy(kname, PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS));
+ if (!keyhit(3)) {
+ load();
+ memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
+ }
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ if (!autoboot || !OPT_CHECK(RBX_QUIET)) {
+ printf("\nFreeBSD/x86 boot\n");
+ if (zfs_rlookup(spa, zfsmount.rootobj, rootname) != 0)
+ printf("Default: %s/<0x%llx>:%s\n"
+ "boot: ",
+ spa->spa_name, zfsmount.rootobj, kname);
+ else if (rootname[0] != '\0')
+ printf("Default: %s/%s:%s\n"
+ "boot: ",
+ spa->spa_name, rootname, kname);
+ else
+ printf("Default: %s:%s\n"
+ "boot: ",
+ spa->spa_name, kname);
+ }
+ if (ioctrl & IO_SERIAL)
+ sio_flush();
+ if (!autoboot || keyhit(5))
+ getstr(cmd, sizeof(cmd));
+ else if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ putchar('\n');
+ autoboot = 0;
+ if (parse_cmd())
+ putchar('\a');
+ else
+ load();
+ }
+}
+
+/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
+void
+exit(int x)
+{
+ __exit(x);
+}
+
+void
+reboot(void)
+{
+ __exit(0);
+}
+
+static void
+load(void)
+{
+ union {
+ struct exec ex;
+ Elf32_Ehdr eh;
+ } hdr;
+ static Elf32_Phdr ep[2];
+ static Elf32_Shdr es[2];
+ caddr_t p;
+ dnode_phys_t dn;
+ off_t off;
+ uint32_t addr, x;
+ int fmt, i, j;
+
+ if (zfs_lookup(&zfsmount, kname, &dn)) {
+ printf("\nCan't find %s\n", kname);
+ return;
+ }
+ off = 0;
+ if (xfsread(&dn, &off, &hdr, sizeof(hdr)))
+ return;
+ if (N_GETMAGIC(hdr.ex) == ZMAGIC)
+ fmt = 0;
+ else if (IS_ELF(hdr.eh))
+ fmt = 1;
+ else {
+ printf("Invalid %s\n", "format");
+ return;
+ }
+ if (fmt == 0) {
+ addr = hdr.ex.a_entry & 0xffffff;
+ p = PTOV(addr);
+ off = PAGE_SIZE;
+ if (xfsread(&dn, &off, p, hdr.ex.a_text))
+ return;
+ p += roundup2(hdr.ex.a_text, PAGE_SIZE);
+ if (xfsread(&dn, &off, p, hdr.ex.a_data))
+ return;
+ p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
+ p += sizeof(hdr.ex.a_syms);
+ if (hdr.ex.a_syms) {
+ if (xfsread(&dn, &off, p, hdr.ex.a_syms))
+ return;
+ p += hdr.ex.a_syms;
+ if (xfsread(&dn, &off, p, sizeof(int)))
+ return;
+ x = *(uint32_t *)p;
+ p += sizeof(int);
+ x -= sizeof(int);
+ if (xfsread(&dn, &off, p, x))
+ return;
+ p += x;
+ }
+ } else {
+ off = hdr.eh.e_phoff;
+ for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
+ if (xfsread(&dn, &off, ep + j, sizeof(ep[0])))
+ return;
+ if (ep[j].p_type == PT_LOAD)
+ j++;
+ }
+ for (i = 0; i < 2; i++) {
+ p = PTOV(ep[i].p_paddr & 0xffffff);
+ off = ep[i].p_offset;
+ if (xfsread(&dn, &off, p, ep[i].p_filesz))
+ return;
+ }
+ p += roundup2(ep[1].p_memsz, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
+ off = hdr.eh.e_shoff + sizeof(es[0]) *
+ (hdr.eh.e_shstrndx + 1);
+ if (xfsread(&dn, &off, &es, sizeof(es)))
+ return;
+ for (i = 0; i < 2; i++) {
+ memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
+ p += sizeof(es[i].sh_size);
+ off = es[i].sh_offset;
+ if (xfsread(&dn, &off, p, es[i].sh_size))
+ return;
+ p += es[i].sh_size;
+ }
+ }
+ addr = hdr.eh.e_entry & 0xffffff;
+ }
+ bootinfo.bi_esymtab = VTOP(p);
+ bootinfo.bi_kernelname = VTOP(kname);
+ zfsargs.size = sizeof(zfsargs);
+ zfsargs.pool = zfsmount.spa->spa_guid;
+ zfsargs.root = zfsmount.rootobj;
+ zfsargs.primary_pool = primary_spa->spa_guid;
+#ifdef LOADER_GELI_SUPPORT
+ explicit_bzero(gelipw, sizeof(gelipw));
+ gelibuf = malloc(sizeof(struct keybuf) + (GELI_MAX_KEYS * sizeof(struct keybuf_ent)));
+ geli_fill_keybuf(gelibuf);
+ zfsargs.notapw = '\0';
+ zfsargs.keybuf_sentinel = KEYBUF_SENTINEL;
+ zfsargs.keybuf = gelibuf;
+#else
+ zfsargs.gelipw[0] = '\0';
+#endif
+ if (primary_vdev != NULL)
+ zfsargs.primary_vdev = primary_vdev->v_guid;
+ else
+ printf("failed to detect primary vdev\n");
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
+ bootdev,
+ KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
+ (uint32_t) spa->spa_guid,
+ (uint32_t) (spa->spa_guid >> 32),
+ VTOP(&bootinfo),
+ zfsargs);
+}
+
+static int
+zfs_mount_ds(char *dsname)
+{
+ uint64_t newroot;
+ spa_t *newspa;
+ char *q;
+
+ q = strchr(dsname, '/');
+ if (q)
+ *q++ = '\0';
+ newspa = spa_find_by_name(dsname);
+ if (newspa == NULL) {
+ printf("\nCan't find ZFS pool %s\n", dsname);
+ return -1;
+ }
+
+ if (zfs_spa_init(newspa))
+ return -1;
+
+ newroot = 0;
+ if (q) {
+ if (zfs_lookup_dataset(newspa, q, &newroot)) {
+ printf("\nCan't find dataset %s in ZFS pool %s\n",
+ q, newspa->spa_name);
+ return -1;
+ }
+ }
+ if (zfs_mount(newspa, newroot, &zfsmount)) {
+ printf("\nCan't mount ZFS dataset\n");
+ return -1;
+ }
+ spa = newspa;
+ return (0);
+}
+
+static int
+parse_cmd(void)
+{
+ char *arg = cmd;
+ char *ep, *p, *q;
+ const char *cp;
+ int c, i, j;
+
+ while ((c = *arg++)) {
+ if (c == ' ' || c == '\t' || c == '\n')
+ continue;
+ for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+ ep = p;
+ if (*p)
+ *p++ = 0;
+ if (c == '-') {
+ while ((c = *arg++)) {
+ if (c == 'P') {
+ if (*(uint8_t *)PTOV(0x496) & 0x10) {
+ cp = "yes";
+ } else {
+ opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
+ cp = "no";
+ }
+ printf("Keyboard: %s\n", cp);
+ continue;
+ } else if (c == 'S') {
+ j = 0;
+ while ((unsigned int)(i = *arg++ - '0') <= 9)
+ j = j * 10 + i;
+ if (j > 0 && i == -'0') {
+ comspeed = j;
+ break;
+ }
+ /* Fall through to error below ('S' not in optstr[]). */
+ }
+ for (i = 0; c != optstr[i]; i++)
+ if (i == NOPT - 1)
+ return -1;
+ opts ^= OPT_SET(flags[i]);
+ }
+ ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
+ OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
+ if (ioctrl & IO_SERIAL) {
+ if (sio_init(115200 / comspeed) != 0)
+ ioctrl &= ~IO_SERIAL;
+ }
+ } if (c == '?') {
+ dnode_phys_t dn;
+
+ if (zfs_lookup(&zfsmount, arg, &dn) == 0) {
+ zap_list(spa, &dn);
+ }
+ return -1;
+ } else {
+ arg--;
+
+ /*
+ * Report pool status if the comment is 'status'. Lets
+ * hope no-one wants to load /status as a kernel.
+ */
+ if (!strcmp(arg, "status")) {
+ spa_all_status();
+ return -1;
+ }
+
+ /*
+ * If there is "zfs:" prefix simply ignore it.
+ */
+ if (strncmp(arg, "zfs:", 4) == 0)
+ arg += 4;
+
+ /*
+ * If there is a colon, switch pools.
+ */
+ q = strchr(arg, ':');
+ if (q) {
+ *q++ = '\0';
+ if (zfs_mount_ds(arg) != 0)
+ return -1;
+ arg = q;
+ }
+ if ((i = ep - arg)) {
+ if ((size_t)i >= sizeof(kname))
+ return -1;
+ memcpy(kname, arg, i + 1);
+ }
+ }
+ arg = p;
+ }
+ return 0;
+}
diff --git a/stand/i386/zfsboot/zfsldr.S b/stand/i386/zfsboot/zfsldr.S
new file mode 100644
index 0000000..3e85e27
--- /dev/null
+++ b/stand/i386/zfsboot/zfsldr.S
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+/* Memory Locations */
+ .set MEM_ARG,0x900 # Arguments
+ .set MEM_ORG,0x7c00 # Origin
+ .set MEM_BUF,0x8000 # Load area
+ .set MEM_BTX,0x9000 # BTX start
+ .set MEM_JMP,0x9010 # BTX entry point
+ .set MEM_USR,0xa000 # Client start
+ .set BDA_BOOT,0x472 # Boot howto flag
+
+/* Partition Constants */
+ .set PRT_OFF,0x1be # Partition offset
+ .set PRT_NUM,0x4 # Partitions
+ .set PRT_BSD,0xa5 # Partition type
+
+/* Misc. Constants */
+ .set SIZ_PAG,0x1000 # Page size
+ .set SIZ_SEC,0x200 # Sector size
+ .set COPY_BLKS,0x8 # Number of blocks
+ # to copy for boot2
+ .set COPY_BLK_SZ,0x8000 # Copy in 32k blocks; must be
+ # a multiple of 16 bytes
+ .set NSECT,(COPY_BLK_SZ / SIZ_SEC * COPY_BLKS)
+ .globl start
+ .code16
+
+/*
+ * Load the rest of zfsboot2 and BTX up, copy the parts to the right locations,
+ * and start it all up.
+ */
+
+/*
+ * Setup the segment registers to flat addressing (segment 0) and setup the
+ * stack to end just below the start of our code.
+ */
+start: cld # String ops inc
+ xor %cx,%cx # Zero
+ mov %cx,%es # Address
+ mov %cx,%ds # data
+ mov %cx,%ss # Set up
+ mov $start,%sp # stack
+/*
+ * Load the MBR and look for the first FreeBSD slice. We use the fake
+ * partition entry below that points to the MBR when we call read.
+ * The first pass looks for the first active FreeBSD slice. The
+ * second pass looks for the first non-active FreeBSD slice if the
+ * first one fails.
+ */
+ call check_edd # Make sure EDD works
+ mov $part4,%si # Dummy partition
+ xor %eax,%eax # Read MBR
+ movl $MEM_BUF,%ebx # from first
+ call read # sector
+ mov $0x1,%cx # Two passes
+main.1: mov $MEM_BUF+PRT_OFF,%si # Partition table
+ movb $0x1,%dh # Partition
+main.2: cmpb $PRT_BSD,0x4(%si) # Our partition type?
+ jne main.3 # No
+ jcxz main.5 # If second pass
+ testb $0x80,(%si) # Active?
+ jnz main.5 # Yes
+main.3: add $0x10,%si # Next entry
+ incb %dh # Partition
+ cmpb $0x1+PRT_NUM,%dh # In table?
+ jb main.2 # Yes
+ dec %cx # Do two
+ jcxz main.1 # passes
+/*
+ * If we get here, we didn't find any FreeBSD slices at all, so print an
+ * error message and die.
+ */
+ mov $msg_part,%si # Message
+ jmp error # Error
+
+/*
+ * Ok, we have a slice and drive in %dx now, so use that to locate and
+ * load boot2. %si references the start of the slice we are looking
+ * for, so go ahead and load up the COPY_BLKS*COPY_BLK_SZ/SIZ_SEC sectors
+ * starting at sector 1024 (i.e. after the two vdev labels). We don't
+ * have do anything fancy here to allow for an extra copy of boot1 and
+ * a partition table (compare to this section of the UFS bootstrap) so we
+ * just load it all at 0x9000. The first part of boot2 is BTX, which wants
+ * to run at 0x9000. The boot2.bin binary starts right after the end of BTX,
+ * so we have to figure out where the start of it is and then move the
+ * binary to 0xc000. Normally, BTX clients start at MEM_USR, or 0xa000,
+ * but when we use btxld to create zfsboot2, we use an entry point of
+ * 0x2000. That entry point is relative to MEM_USR; thus boot2.bin
+ * starts at 0xc000.
+ *
+ * The load area and the target area for the client overlap so we have
+ * to use a decrementing string move. We also play segment register
+ * games with the destination address for the move so that the client
+ * can be larger than 16k (which would overflow the zero segment since
+ * the client starts at 0xc000).
+ */
+main.5: mov %dx,MEM_ARG # Save args
+ mov $NSECT,%cx # Sector count
+ movl $1024,%eax # Offset to boot2
+ mov $MEM_BTX,%ebx # Destination buffer
+main.6: pushal # Save params
+ call read # Read disk
+ popal # Restore
+ incl %eax # Advance to
+ add $SIZ_SEC,%ebx # next sector
+ loop main.6 # If not last, read another
+
+ mov $MEM_BTX,%bx # BTX
+ mov 0xa(%bx),%si # Get BTX length and set
+ add %bx,%si # %si to start of boot2
+ dec %si # Set %ds:%si to point at the
+ mov %si,%ax # last byte we want to copy
+ shr $4,%ax # from boot2, with %si made as
+ add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # small as possible.
+ and $0xf,%si #
+ mov %ax,%ds #
+ mov $(MEM_USR+2*SIZ_PAG)/16,%ax # Set %es:(-1) to point at
+ add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # the last byte we
+ mov %ax,%es # want to copy boot2 into.
+ mov $COPY_BLKS,%bx # Copy COPY_BLKS 32k blocks
+copyloop:
+ add $COPY_BLK_SZ,%si # Adjust %ds:%si to point at
+ mov %ds,%ax # the end of the next 32k to
+ sub $COPY_BLK_SZ/16,%ax # copy from boot2
+ mov %ax,%ds
+ mov $COPY_BLK_SZ-1,%di # Adjust %es:%di to point at
+ mov %es,%ax # the end of the next 32k into
+ sub $COPY_BLK_SZ/16,%ax # which we want boot2 copied
+ mov %ax,%es
+ mov $COPY_BLK_SZ,%cx # Copy 32k
+ std
+ rep movsb
+ dec %bx
+ jnz copyloop
+ mov %cx,%ds # Reset %ds and %es
+ mov %cx,%es
+ cld # Back to increment
+
+/*
+ * Enable A20 so we can access memory above 1 meg.
+ * Use the zero-valued %cx as a timeout for embedded hardware which do not
+ * have a keyboard controller.
+ */
+seta20: cli # Disable interrupts
+seta20.1: dec %cx # Timeout?
+ jz seta20.3 # Yes
+ inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.1 # Yes
+ movb $0xd1,%al # Command: Write
+ outb %al,$0x64 # output port
+seta20.2: inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.2 # Yes
+ movb $0xdf,%al # Enable
+ outb %al,$0x60 # A20
+seta20.3: sti # Enable interrupts
+
+ jmp start+MEM_JMP-MEM_ORG # Start BTX
+
+
+/*
+ * Read a sector from the disk. Sets up an EDD packet on the stack
+ * and passes it to read. We assume that the destination address is
+ * always segment-aligned.
+ *
+ * %eax - int - LBA to read in relative to partition start
+ * %ebx - ptr - destination address
+ * %dl - byte - drive to read from
+ * %si - ptr - MBR partition entry
+ */
+read: xor %ecx,%ecx # Get
+ addl 0x8(%si),%eax # LBA
+ adc $0,%ecx
+ pushl %ecx # Starting absolute block
+ pushl %eax # block number
+ shr $4,%ebx # Convert to segment
+ push %bx # Address of
+ push $0 # transfer buffer
+ push $0x1 # Read 1 sector
+ push $0x10 # Size of packet
+ mov %sp,%si # Packet pointer
+ mov $0x42,%ah # BIOS: Extended
+ int $0x13 # read
+ jc read.1 # If error, fail
+ lea 0x10(%si),%sp # Clear stack
+ ret # If success, return
+read.1: mov %ah,%al # Format
+ mov $read_err,%di # error
+ call hex8 # code
+ mov $msg_read,%si # Set the error message and
+ # fall through to the error
+ # routine
+/*
+ * Print out the error message pointed to by %ds:(%si) followed
+ * by a prompt, wait for a keypress, and then reboot the machine.
+ */
+error: callw putstr # Display message
+ mov $prompt,%si # Display
+ callw putstr # prompt
+ xorb %ah,%ah # BIOS: Get
+ int $0x16 # keypress
+ movw $0x1234, BDA_BOOT # Do a warm boot
+ ljmp $0xffff,$0x0 # reboot the machine
+/*
+ * Display a null-terminated string using the BIOS output.
+ */
+putstr.0: mov $0x7,%bx # Page:attribute
+ movb $0xe,%ah # BIOS: Display
+ int $0x10 # character
+putstr: lodsb # Get char
+ testb %al,%al # End of string?
+ jne putstr.0 # No
+ ret # To caller
+/*
+ * Check to see if the disk supports EDD. zfsboot requires EDD and does not
+ * support older C/H/S disk I/O.
+ */
+check_edd: cmpb $0x80,%dl # Hard drive?
+ jb check_edd.1 # No, fail to boot
+ mov $0x55aa,%bx # Magic
+ push %dx # Save
+ movb $0x41,%ah # BIOS: Check
+ int $0x13 # extensions present
+ pop %dx # Restore
+ jc check_edd.1 # If error, fail
+ cmp $0xaa55,%bx # Magic?
+ jne check_edd.1 # No, so fail
+ testb $0x1,%cl # Packet interface?
+ jz check_edd.1 # No, so fail
+ ret # EDD ok, keep booting
+check_edd.1: mov $msg_chs,%si # Warn that CHS is
+ jmp error # unsupported and fail
+/*
+ * AL to hex, saving the result to [EDI].
+ */
+hex8: push %ax # Save
+ shrb $0x4,%al # Do upper
+ call hex8.1 # 4
+ pop %ax # Restore
+hex8.1: andb $0xf,%al # Get lower 4
+ cmpb $0xa,%al # Convert
+ sbbb $0x69,%al # to hex
+ das # digit
+ orb $0x20,%al # To lower case
+ stosb # Save char
+ ret # (Recursive)
+
+/* Messages */
+
+msg_chs: .asciz "CHS not supported"
+msg_read: .ascii "Read error: "
+read_err: .asciz "XX"
+msg_part: .asciz "Boot error"
+
+prompt: .asciz "\r\n"
+
+ .org PRT_OFF,0x90
+
+/* Partition table */
+
+ .fill 0x30,0x1,0x0
+part4: .byte 0x80, 0x00, 0x01, 0x00
+ .byte 0xa5, 0xfe, 0xff, 0xff
+ .byte 0x00, 0x00, 0x00, 0x00
+ .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh
+
+ .word 0xaa55 # Magic number
diff --git a/stand/i386/zfsloader/Makefile b/stand/i386/zfsloader/Makefile
new file mode 100644
index 0000000..a9fa913
--- /dev/null
+++ b/stand/i386/zfsloader/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+LOADER= zfsloader
+NEWVERSWHAT= "ZFS enabled bootstrap loader" x86
+HAVE_ZFS= yes
+
+.include "${.CURDIR}/../loader/Makefile"
diff --git a/stand/i386/zfsloader/Makefile.depend b/stand/i386/zfsloader/Makefile.depend
new file mode 100644
index 0000000..15b0c98
--- /dev/null
+++ b/stand/i386/zfsloader/Makefile.depend
@@ -0,0 +1,22 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libstand \
+ sys/boot/ficl32 \
+ sys/boot/geli \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/btxldr \
+ sys/boot/i386/btx/lib \
+ sys/boot/i386/libi386 \
+ sys/boot/libstand32 \
+ sys/boot/zfs \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/kshim/bsd_busspace.c b/stand/kshim/bsd_busspace.c
new file mode 100644
index 0000000..778c449
--- /dev/null
+++ b/stand/kshim/bsd_busspace.c
@@ -0,0 +1,216 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2013 Hans Petter Selasky. 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 <bsd_kernel.h>
+
+struct burst {
+ uint32_t dw0;
+ uint32_t dw1;
+ uint32_t dw2;
+ uint32_t dw3;
+ uint32_t dw4;
+ uint32_t dw5;
+ uint32_t dw6;
+ uint32_t dw7;
+};
+
+int
+bus_space_subregion(bus_space_tag_t t, bus_space_handle_t bsh,
+ bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
+{
+
+ *nbshp = bsh + offset;
+ return (0);
+}
+
+void
+bus_space_read_multi_1(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint8_t *datap, bus_size_t count)
+{
+ while (count--) {
+ *datap++ = bus_space_read_1(t, h, offset);
+ }
+}
+
+void
+bus_space_read_multi_2(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint16_t *datap, bus_size_t count)
+{
+ while (count--) {
+ *datap++ = bus_space_read_2(t, h, offset);
+ }
+}
+
+void
+bus_space_read_multi_4(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint32_t *datap, bus_size_t count)
+{
+ h += offset;
+
+ while (count--) {
+ *datap++ = *((volatile uint32_t *)h);
+ }
+}
+
+void
+bus_space_write_multi_1(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint8_t *datap, bus_size_t count)
+{
+ while (count--) {
+ uint8_t temp = *datap++;
+
+ bus_space_write_1(t, h, offset, temp);
+ }
+}
+
+void
+bus_space_write_multi_2(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint16_t *datap, bus_size_t count)
+{
+ while (count--) {
+ uint16_t temp = *datap++;
+
+ bus_space_write_2(t, h, offset, temp);
+ }
+}
+
+void
+bus_space_write_multi_4(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint32_t *datap, bus_size_t count)
+{
+ h += offset;
+
+ while (count--) {
+ *((volatile uint32_t *)h) = *datap++;
+ }
+}
+
+void
+bus_space_write_1(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint8_t data)
+{
+ *((volatile uint8_t *)(h + offset)) = data;
+}
+
+void
+bus_space_write_2(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint16_t data)
+{
+ *((volatile uint16_t *)(h + offset)) = data;
+}
+
+void
+bus_space_write_4(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint32_t data)
+{
+ *((volatile uint32_t *)(h + offset)) = data;
+}
+
+uint8_t
+bus_space_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset)
+{
+ return (*((volatile uint8_t *)(h + offset)));
+}
+
+uint16_t
+bus_space_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset)
+{
+ return (*((volatile uint16_t *)(h + offset)));
+}
+
+uint32_t
+bus_space_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset)
+{
+ return (*((volatile uint32_t *)(h + offset)));
+}
+
+void
+bus_space_read_region_1(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint8_t *datap, bus_size_t count)
+{
+ h += offset;
+
+ while (count--) {
+ *datap++ = *((volatile uint8_t *)h);
+ h += 1;
+ }
+}
+
+void
+bus_space_write_region_1(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint8_t *datap, bus_size_t count)
+{
+ h += offset;
+
+ while (count--) {
+ *((volatile uint8_t *)h) = *datap++;
+ h += 1;
+ }
+}
+
+void
+bus_space_read_region_4(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint32_t *datap, bus_size_t count)
+{
+ enum { BURST = sizeof(struct burst) / 4 };
+
+ h += offset;
+
+ while (count >= BURST) {
+ *(struct burst *)datap = *((/* volatile */ struct burst *)h);
+
+ h += BURST * 4;
+ datap += BURST;
+ count -= BURST;
+ }
+
+ while (count--) {
+ *datap++ = *((volatile uint32_t *)h);
+ h += 4;
+ }
+}
+
+void
+bus_space_write_region_4(bus_space_tag_t t, bus_space_handle_t h,
+ bus_size_t offset, uint32_t *datap, bus_size_t count)
+{
+ enum { BURST = sizeof(struct burst) / 4 };
+
+ h += offset;
+
+ while (count >= BURST) {
+ *((/* volatile */ struct burst *)h) = *(struct burst *)datap;
+
+ h += BURST * 4;
+ datap += BURST;
+ count -= BURST;
+ }
+
+ while (count--) {
+ *((volatile uint32_t *)h) = *datap++;
+ h += 4;
+ }
+}
diff --git a/stand/kshim/bsd_global.h b/stand/kshim/bsd_global.h
new file mode 100644
index 0000000..63bba75
--- /dev/null
+++ b/stand/kshim/bsd_global.h
@@ -0,0 +1,68 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2013 Hans Petter Selasky. 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.
+ */
+
+#ifndef _BSD_GLOBAL_H_
+#define _BSD_GLOBAL_H_
+
+#include <bsd_kernel.h>
+
+#include <sys/gpio.h>
+
+#define USB_DEBUG_VAR usb_debug
+#include <dev/usb/usb_freebsd_loader.h>
+#include <dev/usb/usb_endian.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_dynamic.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_dev.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_msctest.h>
+#include <dev/usb/usb_pci.h>
+#include <dev/usb/usb_pf.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usb_ioctl.h>
+#include <dev/usb/usb_generic.h>
+#include <dev/usb/quirk/usb_quirk.h>
+#include <dev/usb/template/usb_template.h>
+#include <dev/usb/controller/ehci.h>
+#include <dev/usb/controller/ehcireg.h>
+
+extern struct usb_process usb_process[USB_PROC_MAX];
+
+#endif /* _BSD_GLOBAL_H_ */
diff --git a/stand/kshim/bsd_kernel.c b/stand/kshim/bsd_kernel.c
new file mode 100644
index 0000000..75eccb1
--- /dev/null
+++ b/stand/kshim/bsd_kernel.c
@@ -0,0 +1,1459 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2013 Hans Petter Selasky. 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 <bsd_global.h>
+
+struct usb_process usb_process[USB_PROC_MAX];
+
+static device_t usb_pci_root;
+
+/*------------------------------------------------------------------------*
+ * Implementation of mutex API
+ *------------------------------------------------------------------------*/
+
+struct mtx Giant;
+int (*bus_alloc_resource_any_cb)(struct resource *res, device_t dev,
+ int type, int *rid, unsigned int flags);
+int (*ofw_bus_status_ok_cb)(device_t dev);
+int (*ofw_bus_is_compatible_cb)(device_t dev, char *name);
+
+static void
+mtx_system_init(void *arg)
+{
+ mtx_init(&Giant, "Giant", NULL, MTX_DEF | MTX_RECURSE);
+}
+SYSINIT(mtx_system_init, SI_SUB_LOCK, SI_ORDER_MIDDLE, mtx_system_init, NULL);
+
+int
+bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
+ bus_size_t boundary, bus_addr_t lowaddr,
+ bus_addr_t highaddr, bus_dma_filter_t *filter,
+ void *filterarg, bus_size_t maxsize, int nsegments,
+ bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
+ void *lockfuncarg, bus_dma_tag_t *dmat)
+{
+ struct bus_dma_tag *ret;
+
+ ret = malloc(sizeof(struct bus_dma_tag), XXX, XXX);
+ if (*dmat == NULL)
+ return (ENOMEM);
+ ret->alignment = alignment;
+ ret->maxsize = maxsize;
+
+ *dmat = ret;
+
+ return (0);
+}
+
+int
+bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
+ bus_dmamap_t *mapp)
+{
+ void *addr;
+
+ addr = malloc(dmat->maxsize + dmat->alignment, XXX, XXX);
+ if (addr == NULL)
+ return (ENOMEM);
+
+ *mapp = addr;
+ addr = (void*)(((uintptr_t)addr + dmat->alignment - 1) & ~(dmat->alignment - 1));
+
+ *vaddr = addr;
+ return (0);
+}
+
+int
+bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
+ bus_size_t buflen, bus_dmamap_callback_t *callback,
+ void *callback_arg, int flags)
+{
+ bus_dma_segment_t segs[1];
+
+ segs[0].ds_addr = (uintptr_t)buf;
+ segs[0].ds_len = buflen;
+
+ (*callback)(callback_arg, segs, 1, 0);
+
+ return (0);
+}
+
+void
+bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
+{
+
+ free(map, XXX);
+}
+
+int
+bus_dma_tag_destroy(bus_dma_tag_t dmat)
+{
+
+ free(dmat, XXX);
+ return (0);
+}
+
+struct resource *
+bus_alloc_resource_any(device_t dev, int type, int *rid, unsigned int flags)
+{
+ struct resource *res;
+ int ret = EINVAL;
+
+ res = malloc(sizeof(*res), XXX, XXX);
+ if (res == NULL)
+ return (NULL);
+
+ res->__r_i = malloc(sizeof(struct resource_i), XXX, XXX);
+ if (res->__r_i == NULL) {
+ free(res, XXX);
+ return (NULL);
+ }
+
+ if (bus_alloc_resource_any_cb != NULL)
+ ret = (*bus_alloc_resource_any_cb)(res, dev, type, rid, flags);
+ if (ret == 0)
+ return (res);
+
+ free(res->__r_i, XXX);
+ free(res, XXX);
+ return (NULL);
+}
+
+int
+bus_alloc_resources(device_t dev, struct resource_spec *rs,
+ struct resource **res)
+{
+ int i;
+
+ for (i = 0; rs[i].type != -1; i++)
+ res[i] = NULL;
+ for (i = 0; rs[i].type != -1; i++) {
+ res[i] = bus_alloc_resource_any(dev,
+ rs[i].type, &rs[i].rid, rs[i].flags);
+ if (res[i] == NULL && !(rs[i].flags & RF_OPTIONAL)) {
+ bus_release_resources(dev, rs, res);
+ return (ENXIO);
+ }
+ }
+ return (0);
+}
+
+void
+bus_release_resources(device_t dev, const struct resource_spec *rs,
+ struct resource **res)
+{
+ int i;
+
+ for (i = 0; rs[i].type != -1; i++)
+ if (res[i] != NULL) {
+ bus_release_resource(
+ dev, rs[i].type, rs[i].rid, res[i]);
+ res[i] = NULL;
+ }
+}
+
+int
+bus_setup_intr(device_t dev, struct resource *r, int flags,
+ driver_filter_t filter, driver_intr_t handler, void *arg, void **cookiep)
+{
+
+ dev->dev_irq_filter = filter;
+ dev->dev_irq_fn = handler;
+ dev->dev_irq_arg = arg;
+
+ return (0);
+}
+
+int
+bus_teardown_intr(device_t dev, struct resource *r, void *cookie)
+{
+
+ dev->dev_irq_filter = NULL;
+ dev->dev_irq_fn = NULL;
+ dev->dev_irq_arg = NULL;
+
+ return (0);
+}
+
+int
+bus_release_resource(device_t dev, int type, int rid, struct resource *r)
+{
+ /* Resource releasing is not supported */
+ return (EINVAL);
+}
+
+int
+bus_generic_attach(device_t dev)
+{
+ device_t child;
+
+ TAILQ_FOREACH(child, &dev->dev_children, dev_link) {
+ device_probe_and_attach(child);
+ }
+
+ return (0);
+}
+
+bus_space_tag_t
+rman_get_bustag(struct resource *r)
+{
+
+ return (r->r_bustag);
+}
+
+bus_space_handle_t
+rman_get_bushandle(struct resource *r)
+{
+
+ return (r->r_bushandle);
+}
+
+u_long
+rman_get_size(struct resource *r)
+{
+
+ return (r->__r_i->r_end - r->__r_i->r_start + 1);
+}
+
+int
+ofw_bus_status_okay(device_t dev)
+{
+ if (ofw_bus_status_ok_cb == NULL)
+ return (0);
+
+ return ((*ofw_bus_status_ok_cb)(dev));
+}
+
+int
+ofw_bus_is_compatible(device_t dev, char *name)
+{
+ if (ofw_bus_is_compatible_cb == NULL)
+ return (0);
+
+ return ((*ofw_bus_is_compatible_cb)(dev, name));
+}
+
+void
+mtx_init(struct mtx *mtx, const char *name, const char *type, int opt)
+{
+ mtx->owned = 0;
+ mtx->parent = mtx;
+}
+
+void
+mtx_lock(struct mtx *mtx)
+{
+ mtx = mtx->parent;
+ mtx->owned++;
+}
+
+void
+mtx_unlock(struct mtx *mtx)
+{
+ mtx = mtx->parent;
+ mtx->owned--;
+}
+
+int
+mtx_owned(struct mtx *mtx)
+{
+ mtx = mtx->parent;
+ return (mtx->owned != 0);
+}
+
+void
+mtx_destroy(struct mtx *mtx)
+{
+ /* NOP */
+}
+
+/*------------------------------------------------------------------------*
+ * Implementation of shared/exclusive mutex API
+ *------------------------------------------------------------------------*/
+
+void
+sx_init_flags(struct sx *sx, const char *name, int flags)
+{
+ sx->owned = 0;
+}
+
+void
+sx_destroy(struct sx *sx)
+{
+ /* NOP */
+}
+
+void
+sx_xlock(struct sx *sx)
+{
+ sx->owned++;
+}
+
+void
+sx_xunlock(struct sx *sx)
+{
+ sx->owned--;
+}
+
+int
+sx_xlocked(struct sx *sx)
+{
+ return (sx->owned != 0);
+}
+
+/*------------------------------------------------------------------------*
+ * Implementaiton of condition variable API
+ *------------------------------------------------------------------------*/
+
+void
+cv_init(struct cv *cv, const char *desc)
+{
+ cv->sleeping = 0;
+}
+
+void
+cv_destroy(struct cv *cv)
+{
+ /* NOP */
+}
+
+void
+cv_wait(struct cv *cv, struct mtx *mtx)
+{
+ cv_timedwait(cv, mtx, -1);
+}
+
+int
+cv_timedwait(struct cv *cv, struct mtx *mtx, int timo)
+{
+ int start = ticks;
+ int delta;
+ int time = 0;
+
+ if (cv->sleeping)
+ return (EWOULDBLOCK); /* not allowed */
+
+ cv->sleeping = 1;
+
+ while (cv->sleeping) {
+ if (timo >= 0) {
+ delta = ticks - start;
+ if (delta >= timo || delta < 0)
+ break;
+ }
+ mtx_unlock(mtx);
+
+ usb_idle();
+
+ if (++time >= (1000000 / hz)) {
+ time = 0;
+ callout_process(1);
+ }
+
+ /* Sleep for 1 us */
+ delay(1);
+
+ mtx_lock(mtx);
+ }
+
+ if (cv->sleeping) {
+ cv->sleeping = 0;
+ return (EWOULDBLOCK); /* not allowed */
+ }
+ return (0);
+}
+
+void
+cv_signal(struct cv *cv)
+{
+ cv->sleeping = 0;
+}
+
+void
+cv_broadcast(struct cv *cv)
+{
+ cv->sleeping = 0;
+}
+
+/*------------------------------------------------------------------------*
+ * Implementation of callout API
+ *------------------------------------------------------------------------*/
+
+static void callout_proc_msg(struct usb_proc_msg *);
+
+volatile int ticks = 0;
+
+static LIST_HEAD(, callout) head_callout = LIST_HEAD_INITIALIZER(&head_callout);
+
+static struct mtx mtx_callout;
+static struct usb_proc_msg callout_msg[2];
+
+static void
+callout_system_init(void *arg)
+{
+ mtx_init(&mtx_callout, "callout-mtx", NULL, MTX_DEF | MTX_RECURSE);
+
+ callout_msg[0].pm_callback = &callout_proc_msg;
+ callout_msg[1].pm_callback = &callout_proc_msg;
+}
+SYSINIT(callout_system_init, SI_SUB_LOCK, SI_ORDER_MIDDLE, callout_system_init, NULL);
+
+static void
+callout_callback(struct callout *c)
+{
+ mtx_lock(c->mtx);
+
+ mtx_lock(&mtx_callout);
+ if (c->entry.le_prev != NULL) {
+ LIST_REMOVE(c, entry);
+ c->entry.le_prev = NULL;
+ }
+ mtx_unlock(&mtx_callout);
+
+ if (c->c_func != NULL)
+ (c->c_func) (c->c_arg);
+
+ if (!(c->flags & CALLOUT_RETURNUNLOCKED))
+ mtx_unlock(c->mtx);
+}
+
+void
+callout_process(int timeout)
+{
+ ticks += timeout;
+ usb_proc_msignal(usb_process + 2, &callout_msg[0], &callout_msg[1]);
+}
+
+static void
+callout_proc_msg(struct usb_proc_msg *pmsg)
+{
+ struct callout *c;
+ int delta;
+
+repeat:
+ mtx_lock(&mtx_callout);
+
+ LIST_FOREACH(c, &head_callout, entry) {
+
+ delta = c->timeout - ticks;
+ if (delta < 0) {
+ mtx_unlock(&mtx_callout);
+
+ callout_callback(c);
+
+ goto repeat;
+ }
+ }
+ mtx_unlock(&mtx_callout);
+}
+
+void
+callout_init_mtx(struct callout *c, struct mtx *mtx, int flags)
+{
+ memset(c, 0, sizeof(*c));
+
+ if (mtx == NULL)
+ mtx = &Giant;
+
+ c->mtx = mtx;
+ c->flags = (flags & CALLOUT_RETURNUNLOCKED);
+}
+
+void
+callout_reset(struct callout *c, int to_ticks,
+ void (*func) (void *), void *arg)
+{
+ callout_stop(c);
+
+ c->c_func = func;
+ c->c_arg = arg;
+ c->timeout = ticks + to_ticks;
+
+ mtx_lock(&mtx_callout);
+ LIST_INSERT_HEAD(&head_callout, c, entry);
+ mtx_unlock(&mtx_callout);
+}
+
+void
+callout_stop(struct callout *c)
+{
+ mtx_lock(&mtx_callout);
+
+ if (c->entry.le_prev != NULL) {
+ LIST_REMOVE(c, entry);
+ c->entry.le_prev = NULL;
+ }
+ mtx_unlock(&mtx_callout);
+
+ c->c_func = NULL;
+ c->c_arg = NULL;
+}
+
+void
+callout_drain(struct callout *c)
+{
+ if (c->mtx == NULL)
+ return; /* not initialised */
+
+ mtx_lock(c->mtx);
+ callout_stop(c);
+ mtx_unlock(c->mtx);
+}
+
+int
+callout_pending(struct callout *c)
+{
+ int retval;
+
+ mtx_lock(&mtx_callout);
+ retval = (c->entry.le_prev != NULL);
+ mtx_unlock(&mtx_callout);
+
+ return (retval);
+}
+
+/*------------------------------------------------------------------------*
+ * Implementation of device API
+ *------------------------------------------------------------------------*/
+
+static const char unknown_string[] = { "unknown" };
+
+static TAILQ_HEAD(, module_data) module_head =
+ TAILQ_HEAD_INITIALIZER(module_head);
+
+static uint8_t
+devclass_equal(const char *a, const char *b)
+{
+ char ta, tb;
+
+ if (a == b)
+ return (1);
+
+ while (1) {
+ ta = *a;
+ tb = *b;
+ if (ta != tb)
+ return (0);
+ if (ta == 0)
+ break;
+ a++;
+ b++;
+ }
+ return (1);
+}
+
+int
+bus_generic_resume(device_t dev)
+{
+ return (0);
+}
+
+int
+bus_generic_shutdown(device_t dev)
+{
+ return (0);
+}
+
+int
+bus_generic_suspend(device_t dev)
+{
+ return (0);
+}
+
+int
+bus_generic_print_child(device_t dev, device_t child)
+{
+ return (0);
+}
+
+void
+bus_generic_driver_added(device_t dev, driver_t *driver)
+{
+ return;
+}
+
+device_t
+device_get_parent(device_t dev)
+{
+ return (dev ? dev->dev_parent : NULL);
+}
+
+void
+device_set_interrupt(device_t dev, driver_filter_t *filter,
+ driver_intr_t *fn, void *arg)
+{
+ dev->dev_irq_filter = filter;
+ dev->dev_irq_fn = fn;
+ dev->dev_irq_arg = arg;
+}
+
+void
+device_run_interrupts(device_t parent)
+{
+ device_t child;
+
+ if (parent == NULL)
+ return;
+
+ TAILQ_FOREACH(child, &parent->dev_children, dev_link) {
+ int status;
+ if (child->dev_irq_filter != NULL)
+ status = child->dev_irq_filter(child->dev_irq_arg);
+ else
+ status = FILTER_SCHEDULE_THREAD;
+
+ if (status == FILTER_SCHEDULE_THREAD) {
+ if (child->dev_irq_fn != NULL)
+ (child->dev_irq_fn) (child->dev_irq_arg);
+ }
+ }
+}
+
+void
+device_set_ivars(device_t dev, void *ivars)
+{
+ dev->dev_aux = ivars;
+}
+
+void *
+device_get_ivars(device_t dev)
+{
+ return (dev ? dev->dev_aux : NULL);
+}
+
+int
+device_get_unit(device_t dev)
+{
+ return (dev ? dev->dev_unit : 0);
+}
+
+int
+bus_generic_detach(device_t dev)
+{
+ device_t child;
+ int error;
+
+ if (!dev->dev_attached)
+ return (EBUSY);
+
+ TAILQ_FOREACH(child, &dev->dev_children, dev_link) {
+ if ((error = device_detach(child)) != 0)
+ return (error);
+ }
+ return (0);
+}
+
+const char *
+device_get_nameunit(device_t dev)
+{
+ if (dev && dev->dev_nameunit[0])
+ return (dev->dev_nameunit);
+
+ return (unknown_string);
+}
+
+static uint8_t
+devclass_create(devclass_t *dc_pp)
+{
+ if (dc_pp == NULL) {
+ return (1);
+ }
+ if (dc_pp[0] == NULL) {
+ dc_pp[0] = malloc(sizeof(**(dc_pp)),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+ if (dc_pp[0] == NULL) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static const struct module_data *
+devclass_find_create(const char *classname)
+{
+ const struct module_data *mod;
+
+ TAILQ_FOREACH(mod, &module_head, entry) {
+ if (devclass_equal(mod->mod_name, classname)) {
+ if (devclass_create(mod->devclass_pp)) {
+ continue;
+ }
+ return (mod);
+ }
+ }
+ return (NULL);
+}
+
+static uint8_t
+devclass_add_device(const struct module_data *mod, device_t dev)
+{
+ device_t *pp_dev;
+ device_t *end;
+ uint8_t unit;
+
+ pp_dev = mod->devclass_pp[0]->dev_list;
+ end = pp_dev + DEVCLASS_MAXUNIT;
+ unit = 0;
+
+ while (pp_dev != end) {
+ if (*pp_dev == NULL) {
+ *pp_dev = dev;
+ dev->dev_unit = unit;
+ dev->dev_module = mod;
+ snprintf(dev->dev_nameunit,
+ sizeof(dev->dev_nameunit),
+ "%s%d", device_get_name(dev), unit);
+ return (0);
+ }
+ pp_dev++;
+ unit++;
+ }
+ DPRINTF("Could not add device to devclass.\n");
+ return (1);
+}
+
+static void
+devclass_delete_device(const struct module_data *mod, device_t dev)
+{
+ if (mod == NULL) {
+ return;
+ }
+ mod->devclass_pp[0]->dev_list[dev->dev_unit] = NULL;
+ dev->dev_module = NULL;
+}
+
+static device_t
+make_device(device_t parent, const char *name)
+{
+ device_t dev = NULL;
+ const struct module_data *mod = NULL;
+
+ if (name) {
+
+ mod = devclass_find_create(name);
+
+ if (!mod) {
+
+ DPRINTF("%s:%d:%s: can't find device "
+ "class %s\n", __FILE__, __LINE__,
+ __FUNCTION__, name);
+
+ goto done;
+ }
+ }
+ dev = malloc(sizeof(*dev),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+ if (dev == NULL)
+ goto done;
+
+ dev->dev_parent = parent;
+ TAILQ_INIT(&dev->dev_children);
+
+ if (name) {
+ dev->dev_fixed_class = 1;
+ if (devclass_add_device(mod, dev)) {
+ goto error;
+ }
+ }
+done:
+ return (dev);
+
+error:
+ if (dev) {
+ free(dev, M_DEVBUF);
+ }
+ return (NULL);
+}
+
+device_t
+device_add_child(device_t dev, const char *name, int unit)
+{
+ device_t child;
+
+ if (unit != -1) {
+ device_printf(dev, "Unit is not -1\n");
+ }
+ child = make_device(dev, name);
+ if (child == NULL) {
+ device_printf(dev, "Could not add child '%s'\n", name);
+ goto done;
+ }
+ if (dev == NULL) {
+ /* no parent */
+ goto done;
+ }
+ TAILQ_INSERT_TAIL(&dev->dev_children, child, dev_link);
+done:
+ return (child);
+}
+
+int
+device_delete_child(device_t dev, device_t child)
+{
+ int error = 0;
+ device_t grandchild;
+
+ /* detach parent before deleting children, if any */
+ error = device_detach(child);
+ if (error)
+ goto done;
+
+ /* remove children second */
+ while ((grandchild = TAILQ_FIRST(&child->dev_children))) {
+ error = device_delete_child(child, grandchild);
+ if (error) {
+ device_printf(dev, "Error deleting child!\n");
+ goto done;
+ }
+ }
+
+ devclass_delete_device(child->dev_module, child);
+
+ if (dev != NULL) {
+ /* remove child from parent */
+ TAILQ_REMOVE(&dev->dev_children, child, dev_link);
+ }
+ free(child, M_DEVBUF);
+
+done:
+ return (error);
+}
+
+int
+device_delete_children(device_t dev)
+{
+ device_t child;
+ int error = 0;
+
+ while ((child = TAILQ_FIRST(&dev->dev_children))) {
+ error = device_delete_child(dev, child);
+ if (error) {
+ device_printf(dev, "Error deleting child!\n");
+ break;
+ }
+ }
+ return (error);
+}
+
+void
+device_quiet(device_t dev)
+{
+ dev->dev_quiet = 1;
+}
+
+const char *
+device_get_desc(device_t dev)
+{
+ if (dev)
+ return &(dev->dev_desc[0]);
+ return (unknown_string);
+}
+
+static int
+default_method(void)
+{
+ /* do nothing */
+ DPRINTF("Default method called\n");
+ return (0);
+}
+
+void *
+device_get_method(device_t dev, const char *what)
+{
+ const struct device_method *mtod;
+
+ mtod = dev->dev_module->driver->methods;
+ while (mtod->func != NULL) {
+ if (devclass_equal(mtod->desc, what)) {
+ return (mtod->func);
+ }
+ mtod++;
+ }
+ return ((void *)&default_method);
+}
+
+const char *
+device_get_name(device_t dev)
+{
+ if (dev == NULL)
+ return (unknown_string);
+
+ return (dev->dev_module->driver->name);
+}
+
+static int
+device_allocate_softc(device_t dev)
+{
+ const struct module_data *mod;
+
+ mod = dev->dev_module;
+
+ if ((dev->dev_softc_alloc == 0) &&
+ (mod->driver->size != 0)) {
+ dev->dev_sc = malloc(mod->driver->size,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+ if (dev->dev_sc == NULL)
+ return (ENOMEM);
+
+ dev->dev_softc_alloc = 1;
+ }
+ return (0);
+}
+
+int
+device_probe_and_attach(device_t dev)
+{
+ const struct module_data *mod;
+ const char *bus_name_parent;
+
+ bus_name_parent = device_get_name(device_get_parent(dev));
+
+ if (dev->dev_attached)
+ return (0); /* fail-safe */
+
+ if (dev->dev_fixed_class) {
+
+ mod = dev->dev_module;
+
+ if (DEVICE_PROBE(dev) <= 0) {
+
+ if (device_allocate_softc(dev) == 0) {
+
+ if (DEVICE_ATTACH(dev) == 0) {
+ /* success */
+ dev->dev_attached = 1;
+ return (0);
+ }
+ }
+ }
+ device_detach(dev);
+
+ goto error;
+ }
+ /*
+ * Else find a module for our device, if any
+ */
+
+ TAILQ_FOREACH(mod, &module_head, entry) {
+ if (devclass_equal(mod->bus_name, bus_name_parent)) {
+ if (devclass_create(mod->devclass_pp)) {
+ continue;
+ }
+ if (devclass_add_device(mod, dev)) {
+ continue;
+ }
+ if (DEVICE_PROBE(dev) <= 0) {
+
+ if (device_allocate_softc(dev) == 0) {
+
+ if (DEVICE_ATTACH(dev) == 0) {
+ /* success */
+ dev->dev_attached = 1;
+ return (0);
+ }
+ }
+ }
+ /* else try next driver */
+
+ device_detach(dev);
+ }
+ }
+
+error:
+ return (ENODEV);
+}
+
+int
+device_detach(device_t dev)
+{
+ const struct module_data *mod = dev->dev_module;
+ int error;
+
+ if (dev->dev_attached) {
+
+ error = DEVICE_DETACH(dev);
+ if (error) {
+ return error;
+ }
+ dev->dev_attached = 0;
+ }
+ device_set_softc(dev, NULL);
+
+ if (dev->dev_fixed_class == 0)
+ devclass_delete_device(mod, dev);
+
+ return (0);
+}
+
+void
+device_set_softc(device_t dev, void *softc)
+{
+ if (dev->dev_softc_alloc) {
+ free(dev->dev_sc, M_DEVBUF);
+ dev->dev_sc = NULL;
+ }
+ dev->dev_sc = softc;
+ dev->dev_softc_alloc = 0;
+}
+
+void *
+device_get_softc(device_t dev)
+{
+ if (dev == NULL)
+ return (NULL);
+
+ return (dev->dev_sc);
+}
+
+int
+device_is_attached(device_t dev)
+{
+ return (dev->dev_attached);
+}
+
+void
+device_set_desc(device_t dev, const char *desc)
+{
+ snprintf(dev->dev_desc, sizeof(dev->dev_desc), "%s", desc);
+}
+
+void
+device_set_desc_copy(device_t dev, const char *desc)
+{
+ device_set_desc(dev, desc);
+}
+
+void *
+devclass_get_softc(devclass_t dc, int unit)
+{
+ return (device_get_softc(devclass_get_device(dc, unit)));
+}
+
+int
+devclass_get_maxunit(devclass_t dc)
+{
+ int max_unit = 0;
+
+ if (dc) {
+ max_unit = DEVCLASS_MAXUNIT;
+ while (max_unit--) {
+ if (dc->dev_list[max_unit]) {
+ break;
+ }
+ }
+ max_unit++;
+ }
+ return (max_unit);
+}
+
+device_t
+devclass_get_device(devclass_t dc, int unit)
+{
+ return (((unit < 0) || (unit >= DEVCLASS_MAXUNIT) || (dc == NULL)) ?
+ NULL : dc->dev_list[unit]);
+}
+
+devclass_t
+devclass_find(const char *classname)
+{
+ const struct module_data *mod;
+
+ TAILQ_FOREACH(mod, &module_head, entry) {
+ if (devclass_equal(mod->driver->name, classname))
+ return (mod->devclass_pp[0]);
+ }
+ return (NULL);
+}
+
+void
+module_register(void *data)
+{
+ struct module_data *mdata = data;
+
+ TAILQ_INSERT_TAIL(&module_head, mdata, entry);
+}
+
+/*------------------------------------------------------------------------*
+ * System startup
+ *------------------------------------------------------------------------*/
+
+static void
+sysinit_run(const void **ppdata)
+{
+ const struct sysinit *psys;
+
+ while ((psys = *ppdata) != NULL) {
+ (psys->func) (psys->data);
+ ppdata++;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * USB process API
+ *------------------------------------------------------------------------*/
+
+static int usb_do_process(struct usb_process *);
+static int usb_proc_level = -1;
+static struct mtx usb_proc_mtx;
+
+void
+usb_idle(void)
+{
+ int old_level = usb_proc_level;
+ int old_giant = Giant.owned;
+ int worked;
+
+ device_run_interrupts(usb_pci_root);
+
+ do {
+ worked = 0;
+ Giant.owned = 0;
+
+ while (++usb_proc_level < USB_PROC_MAX)
+ worked |= usb_do_process(usb_process + usb_proc_level);
+
+ usb_proc_level = old_level;
+ Giant.owned = old_giant;
+
+ } while (worked);
+}
+
+void
+usb_init(void)
+{
+ sysinit_run(sysinit_data);
+}
+
+void
+usb_uninit(void)
+{
+ sysinit_run(sysuninit_data);
+}
+
+static void
+usb_process_init_sub(struct usb_process *up)
+{
+ TAILQ_INIT(&up->up_qhead);
+
+ cv_init(&up->up_cv, "-");
+ cv_init(&up->up_drain, "usbdrain");
+
+ up->up_mtx = &usb_proc_mtx;
+}
+
+static void
+usb_process_init(void *arg)
+{
+ uint8_t x;
+
+ mtx_init(&usb_proc_mtx, "usb-proc-mtx", NULL, MTX_DEF | MTX_RECURSE);
+
+ for (x = 0; x != USB_PROC_MAX; x++)
+ usb_process_init_sub(&usb_process[x]);
+
+}
+SYSINIT(usb_process_init, SI_SUB_LOCK, SI_ORDER_MIDDLE, usb_process_init, NULL);
+
+static int
+usb_do_process(struct usb_process *up)
+{
+ struct usb_proc_msg *pm;
+ int worked = 0;
+
+ mtx_lock(&usb_proc_mtx);
+
+repeat:
+ pm = TAILQ_FIRST(&up->up_qhead);
+
+ if (pm != NULL) {
+
+ worked = 1;
+
+ (pm->pm_callback) (pm);
+
+ if (pm == TAILQ_FIRST(&up->up_qhead)) {
+ /* nothing changed */
+ TAILQ_REMOVE(&up->up_qhead, pm, pm_qentry);
+ pm->pm_qentry.tqe_prev = NULL;
+ }
+ goto repeat;
+ }
+ mtx_unlock(&usb_proc_mtx);
+
+ return (worked);
+}
+
+void *
+usb_proc_msignal(struct usb_process *up, void *_pm0, void *_pm1)
+{
+ struct usb_proc_msg *pm0 = _pm0;
+ struct usb_proc_msg *pm1 = _pm1;
+ struct usb_proc_msg *pm2;
+ usb_size_t d;
+ uint8_t t;
+
+ t = 0;
+
+ if (pm0->pm_qentry.tqe_prev) {
+ t |= 1;
+ }
+ if (pm1->pm_qentry.tqe_prev) {
+ t |= 2;
+ }
+ if (t == 0) {
+ /*
+ * No entries are queued. Queue "pm0" and use the existing
+ * message number.
+ */
+ pm2 = pm0;
+ } else if (t == 1) {
+ /* Check if we need to increment the message number. */
+ if (pm0->pm_num == up->up_msg_num) {
+ up->up_msg_num++;
+ }
+ pm2 = pm1;
+ } else if (t == 2) {
+ /* Check if we need to increment the message number. */
+ if (pm1->pm_num == up->up_msg_num) {
+ up->up_msg_num++;
+ }
+ pm2 = pm0;
+ } else if (t == 3) {
+ /*
+ * Both entries are queued. Re-queue the entry closest to
+ * the end.
+ */
+ d = (pm1->pm_num - pm0->pm_num);
+
+ /* Check sign after subtraction */
+ if (d & 0x80000000) {
+ pm2 = pm0;
+ } else {
+ pm2 = pm1;
+ }
+
+ TAILQ_REMOVE(&up->up_qhead, pm2, pm_qentry);
+ } else {
+ pm2 = NULL; /* panic - should not happen */
+ }
+
+ /* Put message last on queue */
+
+ pm2->pm_num = up->up_msg_num;
+ TAILQ_INSERT_TAIL(&up->up_qhead, pm2, pm_qentry);
+
+ return (pm2);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_proc_is_gone
+ *
+ * Return values:
+ * 0: USB process is running
+ * Else: USB process is tearing down
+ *------------------------------------------------------------------------*/
+uint8_t
+usb_proc_is_gone(struct usb_process *up)
+{
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_proc_mwait
+ *
+ * This function will return when the USB process message pointed to
+ * by "pm" is no longer on a queue. This function must be called
+ * having "usb_proc_mtx" locked.
+ *------------------------------------------------------------------------*/
+void
+usb_proc_mwait(struct usb_process *up, void *_pm0, void *_pm1)
+{
+ struct usb_proc_msg *pm0 = _pm0;
+ struct usb_proc_msg *pm1 = _pm1;
+
+ /* Just remove the messages from the queue. */
+ if (pm0->pm_qentry.tqe_prev) {
+ TAILQ_REMOVE(&up->up_qhead, pm0, pm_qentry);
+ pm0->pm_qentry.tqe_prev = NULL;
+ }
+ if (pm1->pm_qentry.tqe_prev) {
+ TAILQ_REMOVE(&up->up_qhead, pm1, pm_qentry);
+ pm1->pm_qentry.tqe_prev = NULL;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * SYSTEM attach
+ *------------------------------------------------------------------------*/
+
+#ifdef USB_PCI_PROBE_LIST
+static device_method_t pci_methods[] = {
+ DEVMETHOD_END
+};
+
+static driver_t pci_driver = {
+ .name = "pci",
+ .methods = pci_methods,
+};
+
+static devclass_t pci_devclass;
+
+DRIVER_MODULE(pci, pci, pci_driver, pci_devclass, 0, 0);
+
+static const char *usb_pci_devices[] = {
+ USB_PCI_PROBE_LIST
+};
+
+#define USB_PCI_USB_MAX (sizeof(usb_pci_devices) / sizeof(void *))
+
+static device_t usb_pci_dev[USB_PCI_USB_MAX];
+
+static void
+usb_pci_mod_load(void *arg)
+{
+ uint32_t x;
+
+ usb_pci_root = device_add_child(NULL, "pci", -1);
+ if (usb_pci_root == NULL)
+ return;
+
+ for (x = 0; x != USB_PCI_USB_MAX; x++) {
+ usb_pci_dev[x] = device_add_child(usb_pci_root, usb_pci_devices[x], -1);
+ if (usb_pci_dev[x] == NULL)
+ continue;
+ if (device_probe_and_attach(usb_pci_dev[x])) {
+ device_printf(usb_pci_dev[x],
+ "WARNING: Probe and attach failed!\n");
+ }
+ }
+}
+SYSINIT(usb_pci_mod_load, SI_SUB_RUN_SCHEDULER, SI_ORDER_MIDDLE, usb_pci_mod_load, 0);
+
+static void
+usb_pci_mod_unload(void *arg)
+{
+ uint32_t x;
+
+ for (x = 0; x != USB_PCI_USB_MAX; x++) {
+ if (usb_pci_dev[x]) {
+ device_detach(usb_pci_dev[x]);
+ device_delete_child(usb_pci_root, usb_pci_dev[x]);
+ }
+ }
+ if (usb_pci_root)
+ device_delete_child(NULL, usb_pci_root);
+}
+SYSUNINIT(usb_pci_mod_unload, SI_SUB_RUN_SCHEDULER, SI_ORDER_MIDDLE, usb_pci_mod_unload, 0);
+#endif
+
+/*------------------------------------------------------------------------*
+ * MALLOC API
+ *------------------------------------------------------------------------*/
+
+#ifndef HAVE_MALLOC
+#define USB_POOL_ALIGN 8
+
+static uint8_t usb_pool[USB_POOL_SIZE] __aligned(USB_POOL_ALIGN);
+static uint32_t usb_pool_rem = USB_POOL_SIZE;
+static uint32_t usb_pool_entries;
+
+struct malloc_hdr {
+ TAILQ_ENTRY(malloc_hdr) entry;
+ uint32_t size;
+} __aligned(USB_POOL_ALIGN);
+
+static TAILQ_HEAD(, malloc_hdr) malloc_head =
+ TAILQ_HEAD_INITIALIZER(malloc_head);
+
+void *
+usb_malloc(unsigned long size)
+{
+ struct malloc_hdr *hdr;
+
+ size = (size + USB_POOL_ALIGN - 1) & ~(USB_POOL_ALIGN - 1);
+ size += sizeof(struct malloc_hdr);
+
+ TAILQ_FOREACH(hdr, &malloc_head, entry) {
+ if (hdr->size == size)
+ break;
+ }
+
+ if (hdr) {
+ DPRINTF("MALLOC: Entries = %d; Remainder = %d; Size = %d\n",
+ (int)usb_pool_entries, (int)usb_pool_rem, (int)size);
+
+ TAILQ_REMOVE(&malloc_head, hdr, entry);
+ memset(hdr + 1, 0, hdr->size - sizeof(*hdr));
+ return (hdr + 1);
+ }
+ if (usb_pool_rem >= size) {
+ hdr = (void *)(usb_pool + USB_POOL_SIZE - usb_pool_rem);
+ hdr->size = size;
+
+ usb_pool_rem -= size;
+ usb_pool_entries++;
+
+ DPRINTF("MALLOC: Entries = %d; Remainder = %d; Size = %d\n",
+ (int)usb_pool_entries, (int)usb_pool_rem, (int)size);
+
+ memset(hdr + 1, 0, hdr->size - sizeof(*hdr));
+ return (hdr + 1);
+ }
+ return (NULL);
+}
+
+void
+usb_free(void *arg)
+{
+ struct malloc_hdr *hdr;
+
+ if (arg == NULL)
+ return;
+
+ hdr = arg;
+ hdr--;
+
+ TAILQ_INSERT_TAIL(&malloc_head, hdr, entry);
+}
+#endif
+
+char *
+usb_strdup(const char *str)
+{
+ char *tmp;
+ int len;
+
+ len = 1 + strlen(str);
+
+ tmp = malloc(len,XXX,XXX);
+ if (tmp == NULL)
+ return (NULL);
+
+ memcpy(tmp, str, len);
+ return (tmp);
+}
diff --git a/stand/kshim/bsd_kernel.h b/stand/kshim/bsd_kernel.h
new file mode 100644
index 0000000..66106ac
--- /dev/null
+++ b/stand/kshim/bsd_kernel.h
@@ -0,0 +1,647 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 Hans Petter Selasky. 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.
+ */
+
+#ifndef _BSD_KERNEL_H_
+#define _BSD_KERNEL_H_
+
+#define _KERNEL
+#undef __FreeBSD_version
+#define __FreeBSD_version 1100000
+
+#include <sys/cdefs.h>
+#include <sys/queue.h>
+#include <sys/errno.h>
+
+#define howmany(x, y) (((x)+((y)-1))/(y))
+#define nitems(x) (sizeof((x)) / sizeof((x)[0]))
+#define isalpha(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z'))
+#define isdigit(x) ((x) >= '0' && (x) <= '9')
+#define panic(...) do { printf("USB PANIC: " __VA_ARGS__); while (1) ; } while (0)
+#define rebooting 0
+#define M_USB 0
+#define M_USBDEV 0
+#define USB_PROC_MAX 3
+#define USB_BUS_GIANT_PROC(bus) (usb_process + 2)
+#define USB_BUS_NON_GIANT_BULK_PROC(bus) (usb_process + 2)
+#define USB_BUS_NON_GIANT_ISOC_PROC(bus) (usb_process + 2)
+#define USB_BUS_EXPLORE_PROC(bus) (usb_process + 0)
+#define USB_BUS_CONTROL_XFER_PROC(bus) (usb_process + 1)
+#define SYSCTL_DECL(...)
+struct sysctl_req {
+ void *newptr;
+};
+#define SYSCTL_HANDLER_ARGS void *oidp, void *arg1, \
+ uint32_t arg2, struct sysctl_req *req
+#define SYSCTL_NODE(name,...) struct { } name __used
+#define SYSCTL_INT(...)
+#define SYSCTL_UINT(...)
+#define SYSCTL_PROC(...)
+#define TUNABLE_INT(...)
+#define MALLOC_DECLARE(...)
+#define MALLOC_DEFINE(...)
+#define EVENTHANDLER_DECLARE(...)
+#define EVENTHANDLER_INVOKE(...)
+#define KASSERT(...)
+#define SCHEDULER_STOPPED(x) (0)
+#define PI_SWI(...) (0)
+#define UNIQ_NAME(x) x
+#define UNIQ_NAME_STR(x) #x
+#define DEVCLASS_MAXUNIT 32
+#define MOD_LOAD 1
+#define MOD_UNLOAD 2
+#define DEVMETHOD(what,func) { #what, (void *)&func }
+#define DEVMETHOD_END {0,0}
+#define EARLY_DRIVER_MODULE(a, b, c, d, e, f, g) DRIVER_MODULE(a, b, c, d, e, f)
+#define DRIVER_MODULE(name, busname, driver, devclass, evh, arg) \
+ static struct module_data bsd_##name##_##busname##_driver_mod = { \
+ evh, arg, #busname, #name, #busname "/" #name, \
+ &driver, &devclass, { 0, 0 } }; \
+SYSINIT(bsd_##name##_##busname##_driver_mod, SI_SUB_DRIVERS, \
+ SI_ORDER_MIDDLE, module_register, \
+ &bsd_##name##_##busname##_driver_mod)
+#define SYSINIT(uniq, subs, order, _func, _data) \
+const struct sysinit UNIQ_NAME(sysinit_##uniq) = { \
+ .func = (_func), \
+ .data = __DECONST(void *, _data) \
+}; \
+SYSINIT_ENTRY(uniq##_entry, "sysinit", (subs), \
+ (order), "const struct sysinit", \
+ UNIQ_NAME_STR(sysinit_##uniq), "SYSINIT")
+
+#define SYSUNINIT(uniq, subs, order, _func, _data) \
+const struct sysinit UNIQ_NAME(sysuninit_##uniq) = { \
+ .func = (_func), \
+ .data = __DECONST(void *, _data) \
+}; \
+SYSINIT_ENTRY(uniq##_entry, "sysuninit", (subs), \
+ (order), "const struct sysuninit", \
+ UNIQ_NAME_STR(sysuninit_##uniq), "SYSUNINIT")
+#define MODULE_DEPEND(...)
+#define MODULE_VERSION(...)
+#define NULL ((void *)0)
+#define BUS_SPACE_BARRIER_READ 0x01
+#define BUS_SPACE_BARRIER_WRITE 0x02
+#define hz 1000
+#undef PAGE_SIZE
+#define PAGE_SIZE 4096
+#undef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#undef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define MTX_DEF 0
+#define MTX_SPIN 0
+#define MTX_RECURSE 0
+#define SX_DUPOK 0
+#define SX_NOWITNESS 0
+#define WITNESS_WARN(...)
+#define cold 0
+#define BUS_PROBE_GENERIC 0
+#define BUS_PROBE_DEFAULT (-20)
+#define CALLOUT_RETURNUNLOCKED 0x1
+#undef ffs
+#define ffs(x) __builtin_ffs(x)
+#undef va_list
+#define va_list __builtin_va_list
+#undef va_size
+#define va_size(type) __builtin_va_size(type)
+#undef va_start
+#define va_start(ap, last) __builtin_va_start(ap, last)
+#undef va_end
+#define va_end(ap) __builtin_va_end(ap)
+#undef va_arg
+#define va_arg(ap, type) __builtin_va_arg((ap), type)
+#define DEVICE_ATTACH(dev, ...) \
+ (((device_attach_t *)(device_get_method(dev, "device_attach")))(dev,## __VA_ARGS__))
+#define DEVICE_DETACH(dev, ...) \
+ (((device_detach_t *)(device_get_method(dev, "device_detach")))(dev,## __VA_ARGS__))
+#define DEVICE_PROBE(dev, ...) \
+ (((device_probe_t *)(device_get_method(dev, "device_probe")))(dev,## __VA_ARGS__))
+#define DEVICE_RESUME(dev, ...) \
+ (((device_resume_t *)(device_get_method(dev, "device_resume")))(dev,## __VA_ARGS__))
+#define DEVICE_SHUTDOWN(dev, ...) \
+ (((device_shutdown_t *)(device_get_method(dev, "device_shutdown")))(dev,## __VA_ARGS__))
+#define DEVICE_SUSPEND(dev, ...) \
+ (((device_suspend_t *)(device_get_method(dev, "device_suspend")))(dev,## __VA_ARGS__))
+#define USB_HANDLE_REQUEST(dev, ...) \
+ (((usb_handle_request_t *)(device_get_method(dev, "usb_handle_request")))(dev,## __VA_ARGS__))
+#define USB_TAKE_CONTROLLER(dev, ...) \
+ (((usb_take_controller_t *)(device_get_method(dev, "usb_take_controller")))(dev,## __VA_ARGS__))
+#define GPIO_PIN_SET(dev, ...) \
+ (((gpio_pin_set_t *)(device_get_method(dev, "gpio_pin_set")))(dev,## __VA_ARGS__))
+#define GPIO_PIN_SETFLAGS(dev, ...) \
+ (((gpio_pin_setflags_t *)(device_get_method(dev, "gpio_pin_setflags")))(dev,## __VA_ARGS__))
+
+enum {
+ SI_SUB_DUMMY = 0x0000000,
+ SI_SUB_LOCK = 0x1B00000,
+ SI_SUB_KLD = 0x2000000,
+ SI_SUB_DRIVERS = 0x3100000,
+ SI_SUB_PSEUDO = 0x7000000,
+ SI_SUB_KICK_SCHEDULER = 0xa000000,
+ SI_SUB_RUN_SCHEDULER = 0xfffffff
+};
+
+enum {
+ SI_ORDER_FIRST = 0x0000000,
+ SI_ORDER_SECOND = 0x0000001,
+ SI_ORDER_THIRD = 0x0000002,
+ SI_ORDER_FOURTH = 0x0000003,
+ SI_ORDER_MIDDLE = 0x1000000,
+ SI_ORDER_ANY = 0xfffffff /* last */
+};
+
+struct uio;
+struct thread;
+struct malloc_type;
+struct usb_process;
+
+#ifndef HAVE_STANDARD_DEFS
+#define _UINT8_T_DECLARED
+typedef unsigned char uint8_t;
+#define _INT8_T_DECLARED
+typedef signed char int8_t;
+#define _UINT16_T_DECLARED
+typedef unsigned short uint16_t;
+#define _INT16_T_DECLARED
+typedef signed short int16_t;
+#define _UINT32_T_DECLARED
+typedef unsigned int uint32_t;
+#define _INT32_T_DECLARED
+typedef signed int int32_t;
+#define _UINT64_T_DECLARED
+typedef unsigned long long uint64_t;
+#define _INT16_T_DECLARED
+typedef signed long long int64_t;
+
+typedef uint16_t uid_t;
+typedef uint16_t gid_t;
+typedef uint16_t mode_t;
+
+typedef uint8_t *caddr_t;
+#define _UINTPTR_T_DECLARED
+typedef unsigned long uintptr_t;
+
+#define _SIZE_T_DECLARED
+typedef unsigned long size_t;
+typedef unsigned long u_long;
+#endif
+
+typedef unsigned long bus_addr_t;
+typedef unsigned long bus_size_t;
+
+typedef struct bus_dma_segment {
+ bus_addr_t ds_addr; /* DMA address */
+ bus_size_t ds_len; /* length of transfer */
+} bus_dma_segment_t;
+
+struct bus_dma_tag {
+ uint32_t alignment;
+ uint32_t maxsize;
+};
+
+typedef void *bus_dmamap_t;
+typedef struct bus_dma_tag *bus_dma_tag_t;
+
+typedef enum {
+ BUS_DMA_LOCK = 0x01,
+ BUS_DMA_UNLOCK = 0x02,
+} bus_dma_lock_op_t;
+
+typedef void *bus_space_tag_t;
+typedef uint8_t *bus_space_handle_t;
+typedef int bus_dma_filter_t(void *, bus_addr_t);
+typedef void bus_dma_lock_t(void *, bus_dma_lock_op_t);
+
+typedef uint32_t bool;
+
+/* SYSINIT API */
+
+#include <sysinit.h>
+
+struct sysinit {
+ void (*func) (void *arg);
+ void *data;
+};
+
+/* MUTEX API */
+
+struct mtx {
+ int owned;
+ struct mtx *parent;
+};
+
+#define mtx_assert(...) do { } while (0)
+void mtx_init(struct mtx *, const char *, const char *, int);
+void mtx_lock(struct mtx *);
+void mtx_unlock(struct mtx *);
+#define mtx_lock_spin(x) mtx_lock(x)
+#define mtx_unlock_spin(x) mtx_unlock(x)
+int mtx_owned(struct mtx *);
+void mtx_destroy(struct mtx *);
+
+extern struct mtx Giant;
+
+/* SX API */
+
+struct sx {
+ int owned;
+};
+
+#define sx_assert(...) do { } while (0)
+#define sx_init(...) sx_init_flags(__VA_ARGS__, 0)
+void sx_init_flags(struct sx *, const char *, int);
+void sx_destroy(struct sx *);
+void sx_xlock(struct sx *);
+void sx_xunlock(struct sx *);
+int sx_xlocked(struct sx *);
+
+/* CONDVAR API */
+
+struct cv {
+ int sleeping;
+};
+
+void cv_init(struct cv *, const char *desc);
+void cv_destroy(struct cv *);
+void cv_wait(struct cv *, struct mtx *);
+int cv_timedwait(struct cv *, struct mtx *, int);
+void cv_signal(struct cv *);
+void cv_broadcast(struct cv *);
+
+/* CALLOUT API */
+
+typedef void callout_fn_t (void *);
+
+extern volatile int ticks;
+
+struct callout {
+ LIST_ENTRY(callout) entry;
+ callout_fn_t *c_func;
+ void *c_arg;
+ struct mtx *mtx;
+ int flags;
+ int timeout;
+};
+
+void callout_init_mtx(struct callout *, struct mtx *, int);
+void callout_reset(struct callout *, int, callout_fn_t *, void *);
+void callout_stop(struct callout *);
+void callout_drain(struct callout *);
+int callout_pending(struct callout *);
+void callout_process(int timeout);
+
+/* DEVICE API */
+
+struct driver;
+struct devclass;
+struct device;
+struct module;
+struct module_data;
+
+typedef struct driver driver_t;
+typedef struct devclass *devclass_t;
+typedef struct device *device_t;
+typedef void (driver_intr_t)(void *arg);
+typedef int (driver_filter_t)(void *arg);
+#define FILTER_STRAY 0x01
+#define FILTER_HANDLED 0x02
+#define FILTER_SCHEDULE_THREAD 0x04
+
+typedef int device_attach_t (device_t dev);
+typedef int device_detach_t (device_t dev);
+typedef int device_resume_t (device_t dev);
+typedef int device_shutdown_t (device_t dev);
+typedef int device_probe_t (device_t dev);
+typedef int device_suspend_t (device_t dev);
+typedef int gpio_pin_set_t (device_t dev, uint32_t, unsigned int);
+typedef int gpio_pin_setflags_t (device_t dev, uint32_t, uint32_t);
+
+typedef int bus_child_location_str_t (device_t parent, device_t child, char *buf, size_t buflen);
+typedef int bus_child_pnpinfo_str_t (device_t parent, device_t child, char *buf, size_t buflen);
+typedef void bus_driver_added_t (device_t dev, driver_t *driver);
+
+struct device_method {
+ const char *desc;
+ void *const func;
+};
+
+typedef struct device_method device_method_t;
+
+struct device {
+ TAILQ_HEAD(device_list, device) dev_children;
+ TAILQ_ENTRY(device) dev_link;
+
+ struct device *dev_parent;
+ const struct module_data *dev_module;
+ void *dev_sc;
+ void *dev_aux;
+ driver_filter_t *dev_irq_filter;
+ driver_intr_t *dev_irq_fn;
+ void *dev_irq_arg;
+
+ uint16_t dev_unit;
+
+ char dev_nameunit[64];
+ char dev_desc[64];
+
+ uint8_t dev_res_alloc:1;
+ uint8_t dev_quiet:1;
+ uint8_t dev_softc_set:1;
+ uint8_t dev_softc_alloc:1;
+ uint8_t dev_attached:1;
+ uint8_t dev_fixed_class:1;
+ uint8_t dev_unit_manual:1;
+};
+
+struct devclass {
+ device_t dev_list[DEVCLASS_MAXUNIT];
+};
+
+struct driver {
+ const char *name;
+ const struct device_method *methods;
+ uint32_t size;
+};
+
+struct module_data {
+ int (*callback) (struct module *, int, void *arg);
+ void *arg;
+ const char *bus_name;
+ const char *mod_name;
+ const char *long_name;
+ const struct driver *driver;
+ struct devclass **devclass_pp;
+ TAILQ_ENTRY(module_data) entry;
+};
+
+device_t device_get_parent(device_t dev);
+void *device_get_method(device_t dev, const char *what);
+const char *device_get_name(device_t dev);
+const char *device_get_nameunit(device_t dev);
+
+#define device_printf(dev, fmt,...) \
+ printf("%s: " fmt, device_get_nameunit(dev),## __VA_ARGS__)
+device_t device_add_child(device_t dev, const char *name, int unit);
+void device_quiet(device_t dev);
+void device_set_interrupt(device_t dev, driver_filter_t *, driver_intr_t *, void *);
+void device_run_interrupts(device_t parent);
+void device_set_ivars(device_t dev, void *ivars);
+void *device_get_ivars(device_t dev);
+const char *device_get_desc(device_t dev);
+int device_probe_and_attach(device_t dev);
+int device_detach(device_t dev);
+void *device_get_softc(device_t dev);
+void device_set_softc(device_t dev, void *softc);
+int device_delete_child(device_t dev, device_t child);
+int device_delete_children(device_t dev);
+int device_is_attached(device_t dev);
+void device_set_desc(device_t dev, const char *desc);
+void device_set_desc_copy(device_t dev, const char *desc);
+int device_get_unit(device_t dev);
+void *devclass_get_softc(devclass_t dc, int unit);
+int devclass_get_maxunit(devclass_t dc);
+device_t devclass_get_device(devclass_t dc, int unit);
+devclass_t devclass_find(const char *classname);
+
+#define bus_get_dma_tag(...) (NULL)
+int bus_generic_detach(device_t dev);
+int bus_generic_resume(device_t dev);
+int bus_generic_shutdown(device_t dev);
+int bus_generic_suspend(device_t dev);
+int bus_generic_print_child(device_t dev, device_t child);
+void bus_generic_driver_added(device_t dev, driver_t *driver);
+int bus_space_subregion(bus_space_tag_t t, bus_space_handle_t bsh,
+ bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp);
+
+/* BUS SPACE API */
+
+void bus_space_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint8_t data);
+void bus_space_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint16_t data);
+void bus_space_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint32_t data);
+
+uint8_t bus_space_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset);
+uint16_t bus_space_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset);
+uint32_t bus_space_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset);
+
+void bus_space_read_multi_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint8_t *datap, bus_size_t count);
+void bus_space_read_multi_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint16_t *datap, bus_size_t count);
+void bus_space_read_multi_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint32_t *datap, bus_size_t count);
+
+void bus_space_write_multi_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint8_t *datap, bus_size_t count);
+void bus_space_write_multi_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint16_t *datap, bus_size_t count);
+void bus_space_write_multi_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint32_t *datap, bus_size_t count);
+
+void bus_space_read_region_1(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, uint8_t *datap, bus_size_t count);
+void bus_space_write_region_1(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, uint8_t *datap, bus_size_t count);
+void bus_space_read_region_4(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, uint32_t *datap, bus_size_t count);
+void bus_space_write_region_4(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, uint32_t *datap, bus_size_t count);
+
+void bus_space_barrier(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, bus_size_t length, int flags);
+
+void module_register(void *);
+
+/* LIB-C */
+
+void *memset(void *, int, size_t len);
+void *memcpy(void *, const void *, size_t len);
+int printf(const char *,...) __printflike(1, 2);
+int snprintf(char *restrict str, size_t size, const char *restrict format,...) __printflike(3, 4);
+size_t strlen(const char *s);
+
+/* MALLOC API */
+
+#undef malloc
+#define malloc(s,x,f) usb_malloc(s)
+void *usb_malloc(size_t);
+
+#undef free
+#define free(p,x) usb_free(p)
+void usb_free(void *);
+
+#define strdup(p,x) usb_strdup(p)
+char *usb_strdup(const char *str);
+
+/* ENDIANNESS */
+
+#ifndef HAVE_ENDIAN_DEFS
+
+/* Assume little endian */
+
+#define htole64(x) ((uint64_t)(x))
+#define le64toh(x) ((uint64_t)(x))
+
+#define htole32(x) ((uint32_t)(x))
+#define le32toh(x) ((uint32_t)(x))
+
+#define htole16(x) ((uint16_t)(x))
+#define le16toh(x) ((uint16_t)(x))
+
+#define be32toh(x) ((uint32_t)(x))
+#define htobe32(x) ((uint32_t)(x))
+
+#else
+#include <sys/endian.h>
+#endif
+
+/* USB */
+
+typedef int usb_handle_request_t (device_t dev, const void *req, void **pptr, uint16_t *plen, uint16_t offset, uint8_t *pstate);
+typedef int usb_take_controller_t (device_t dev);
+
+void usb_idle(void);
+void usb_init(void);
+void usb_uninit(void);
+
+/* set some defaults */
+
+#ifndef USB_POOL_SIZE
+#define USB_POOL_SIZE (1024*1024) /* 1 MByte */
+#endif
+
+int pause(const char *, int);
+void DELAY(unsigned int);
+
+/* OTHER */
+
+struct selinfo {
+};
+
+/* SYSTEM STARTUP API */
+
+extern const void *sysinit_data[];
+extern const void *sysuninit_data[];
+
+/* Resources */
+
+enum intr_type {
+ INTR_TYPE_TTY = 1,
+ INTR_TYPE_BIO = 2,
+ INTR_TYPE_NET = 4,
+ INTR_TYPE_CAM = 8,
+ INTR_TYPE_MISC = 16,
+ INTR_TYPE_CLK = 32,
+ INTR_TYPE_AV = 64,
+ INTR_EXCL = 256, /* exclusive interrupt */
+ INTR_MPSAFE = 512, /* this interrupt is SMP safe */
+ INTR_ENTROPY = 1024, /* this interrupt provides entropy */
+ INTR_MD1 = 4096, /* flag reserved for MD use */
+ INTR_MD2 = 8192, /* flag reserved for MD use */
+ INTR_MD3 = 16384, /* flag reserved for MD use */
+ INTR_MD4 = 32768 /* flag reserved for MD use */
+};
+
+struct resource_i {
+ u_long r_start; /* index of the first entry in this resource */
+ u_long r_end; /* index of the last entry (inclusive) */
+};
+
+struct resource {
+ struct resource_i *__r_i;
+ bus_space_tag_t r_bustag; /* bus_space tag */
+ bus_space_handle_t r_bushandle; /* bus_space handle */
+};
+
+struct resource_spec {
+ int type;
+ int rid;
+ int flags;
+};
+
+#define SYS_RES_IRQ 1 /* interrupt lines */
+#define SYS_RES_DRQ 2 /* isa dma lines */
+#define SYS_RES_MEMORY 3 /* i/o memory */
+#define SYS_RES_IOPORT 4 /* i/o ports */
+
+#define RF_ALLOCATED 0x0001 /* resource has been reserved */
+#define RF_ACTIVE 0x0002 /* resource allocation has been activated */
+#define RF_SHAREABLE 0x0004 /* resource permits contemporaneous sharing */
+#define RF_SPARE1 0x0008
+#define RF_SPARE2 0x0010
+#define RF_FIRSTSHARE 0x0020 /* first in sharing list */
+#define RF_PREFETCHABLE 0x0040 /* resource is prefetchable */
+#define RF_OPTIONAL 0x0080 /* for bus_alloc_resources() */
+
+int bus_alloc_resources(device_t, struct resource_spec *, struct resource **);
+int bus_release_resource(device_t, int, int, struct resource *);
+void bus_release_resources(device_t, const struct resource_spec *,
+ struct resource **);
+struct resource *bus_alloc_resource_any(device_t, int, int *, unsigned int);
+int bus_generic_attach(device_t);
+bus_space_tag_t rman_get_bustag(struct resource *);
+bus_space_handle_t rman_get_bushandle(struct resource *);
+u_long rman_get_size(struct resource *);
+int bus_setup_intr(device_t, struct resource *, int, driver_filter_t,
+ driver_intr_t, void *, void **);
+int bus_teardown_intr(device_t, struct resource *, void *);
+int ofw_bus_status_okay(device_t);
+int ofw_bus_is_compatible(device_t, char *);
+
+extern int (*bus_alloc_resource_any_cb)(struct resource *res, device_t dev,
+ int type, int *rid, unsigned int flags);
+extern int (*ofw_bus_status_ok_cb)(device_t dev);
+extern int (*ofw_bus_is_compatible_cb)(device_t dev, char *name);
+
+#ifndef strlcpy
+#define strlcpy(d,s,n) snprintf((d),(n),"%s",(s))
+#endif
+
+/* Should be defined in user application since it is machine-dependent */
+extern int delay(unsigned int);
+
+/* BUS dma */
+#define BUS_SPACE_MAXADDR_24BIT 0xFFFFFF
+#define BUS_SPACE_MAXADDR_32BIT 0xFFFFFFFF
+#define BUS_SPACE_MAXADDR 0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE_24BIT 0xFFFFFF
+#define BUS_SPACE_MAXSIZE_32BIT 0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE 0xFFFFFFFF
+
+#define BUS_DMA_WAITOK 0x00 /* safe to sleep (pseudo-flag) */
+#define BUS_DMA_NOWAIT 0x01 /* not safe to sleep */
+#define BUS_DMA_ALLOCNOW 0x02 /* perform resource allocation now */
+#define BUS_DMA_COHERENT 0x04 /* hint: map memory in a coherent way */
+#define BUS_DMA_ZERO 0x08 /* allocate zero'ed memory */
+#define BUS_DMA_BUS1 0x10 /* placeholders for bus functions... */
+#define BUS_DMA_BUS2 0x20
+#define BUS_DMA_BUS3 0x40
+#define BUS_DMA_BUS4 0x80
+
+typedef void bus_dmamap_callback_t(void *, bus_dma_segment_t *, int, int);
+
+int
+bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
+ bus_size_t boundary, bus_addr_t lowaddr,
+ bus_addr_t highaddr, bus_dma_filter_t *filter,
+ void *filterarg, bus_size_t maxsize, int nsegments,
+ bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
+ void *lockfuncarg, bus_dma_tag_t *dmat);
+
+int bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
+ bus_dmamap_t *mapp);
+void bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map);
+int bus_dma_tag_destroy(bus_dma_tag_t dmat);
+
+#endif /* _BSD_KERNEL_H_ */
diff --git a/stand/kshim/kshim.mk b/stand/kshim/kshim.mk
new file mode 100644
index 0000000..7e9b00d
--- /dev/null
+++ b/stand/kshim/kshim.mk
@@ -0,0 +1,79 @@
+#
+# $FreeBSD$
+#
+# Copyright (c) 2013 Hans Petter Selasky.
+# Copyright (c) 2014 SRI International
+# All rights reserved.
+#
+# This software was developed by SRI International and the University of
+# Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+# ("CTSRD"), as part of the DARPA CRASH research programme.
+#
+# 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.
+#
+
+KSHIM_DIR:= ${.PARSEDIR}
+.PATH: ${KSHIM_DIR}
+
+CFLAGS+= -I${KSHIM_DIR}
+CFLAGS+= -I${KSHIM_DIR}/../..
+CFLAGS+= -DUSB_GLOBAL_INCLUDE_FILE=\"bsd_global.h\"
+CFLAGS+= -DHAVE_ENDIAN_DEFS
+
+#
+# Single threaded BSD kernel
+#
+KSRCS+= bsd_kernel.c
+
+#
+# BUSSPACE implementation
+#
+KSRCS+= bsd_busspace.c
+
+SRCS+= sysinit_data.c
+SRCS+= sysuninit_data.c
+
+CLEANFILES+= sysinit.bin
+CLEANFILES+= sysinit_data.c
+CLEANFILES+= sysuninit_data.c
+
+SRCS+= ${KSRCS}
+SYSINIT_OBJS= ${KSRCS:R:C/$/.osys/}
+CLEANFILES+= ${SYSINIT_OBJS}
+
+#
+# SYSINIT() and SYSUNINIT() handling
+#
+
+sysinit_data.c: sysinit.bin
+ sysinit -i sysinit.bin -o ${.TARGET} -k sysinit -s sysinit_data
+
+sysuninit_data.c: sysinit.bin
+ sysinit -i sysinit.bin -o ${.TARGET} -R -k sysuninit -s sysuninit_data
+
+.for KSRC in ${KSRCS:R}
+${KSRC}.osys: ${KSRC}.o
+ ${OBJCOPY} -j ".debug.sysinit" -O binary ${KSRC}.o ${KSRC}.osys
+.endfor
+
+sysinit.bin: ${SYSINIT_OBJS}
+ cat ${.ALLSRC} > sysinit.bin
diff --git a/stand/kshim/sysinit.h b/stand/kshim/sysinit.h
new file mode 100644
index 0000000..a7a450e
--- /dev/null
+++ b/stand/kshim/sysinit.h
@@ -0,0 +1,57 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2013 Hans Petter Selasky. 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.
+ */
+
+#ifndef _SYSINIT_H_
+#define _SYSINIT_H_
+
+struct sysinit_data {
+ uint8_t b_keyword_name[64];
+ uint8_t b_debug_info[128];
+ uint8_t b_global_type[128];
+ uint8_t b_global_name[128];
+ uint8_t b_file_name[256];
+ uint32_t dw_endian32;
+ uint32_t dw_msb_value; /* must be non-zero, else entry is skipped */
+ uint32_t dw_lsb_value;
+ uint32_t dw_file_line;
+} __attribute__((__packed__));
+
+#define SYSINIT_ENTRY(uniq, keyword, msb, lsb, g_type, g_name, debug) \
+ static const struct sysinit_data sysinit_##uniq \
+ __attribute__((__section__(".debug.sysinit"), \
+ __used__, __aligned__(1))) = { \
+ .b_keyword_name = { keyword }, \
+ .b_debug_info = { debug }, \
+ .b_global_type = { g_type }, \
+ .b_global_name = { g_name }, \
+ .b_file_name = { __FILE__ }, \
+ .dw_endian32 = 0x76543210U, \
+ .dw_msb_value = (msb), \
+ .dw_lsb_value = (lsb), \
+ .dw_file_line = __LINE__ \
+}
+
+#endif /* _SYSINIT_H_ */
diff --git a/stand/libsa/Makefile b/stand/libsa/Makefile
new file mode 100644
index 0000000..ea32f3e
--- /dev/null
+++ b/stand/libsa/Makefile
@@ -0,0 +1,160 @@
+# $FreeBSD$
+# Originally from $NetBSD: Makefile,v 1.21 1997/10/26 22:08:38 lukem Exp $
+#
+# Notes:
+# - We don't use the libc strerror/sys_errlist because the string table is
+# quite large.
+#
+
+PACKAGE=lib${LIB}
+MK_PROFILE= no
+MK_SSP= no
+
+.include <bsd.init.mk>
+
+INTERNALLIB=
+LIBSA_CPUARCH?=${MACHINE_CPUARCH}
+LIBC_SRC= ${SRCTOP}/lib/libc
+
+LIB?= sa
+NO_PIC=
+WARNS?= 0
+
+CFLAGS+= -I${SASRC}
+
+# standalone components and stuff we have modified locally
+SRCS+= gzguts.h zutil.h __main.c assert.c bcd.c environment.c getopt.c gets.c \
+ globals.c pager.c panic.c printf.c strdup.c strerror.c strtol.c strtoul.c \
+ random.c sbrk.c twiddle.c zalloc.c zalloc_malloc.c
+
+# private (pruned) versions of libc string functions
+SRCS+= strcasecmp.c
+
+.PATH: ${LIBC_SRC}/net
+
+SRCS+= ntoh.c
+
+# string functions from libc
+.PATH: ${LIBC_SRC}/string
+SRCS+= bcmp.c bcopy.c bzero.c ffs.c fls.c \
+ memccpy.c memchr.c memcmp.c memcpy.c memmove.c memset.c \
+ qdivrem.c strcat.c strchr.c strcmp.c strcpy.c stpcpy.c stpncpy.c \
+ strcspn.c strlcat.c strlcpy.c strlen.c strncat.c strncmp.c strncpy.c \
+ strnlen.c strpbrk.c strrchr.c strsep.c strspn.c strstr.c strtok.c swab.c
+.if ${MACHINE_CPUARCH} == "arm"
+.PATH: ${LIBC_SRC}/arm/gen
+
+# Do not generate movt/movw, because the relocation fixup for them does not
+# translate to the -Bsymbolic -pie format required by self_reloc() in loader(8).
+# Also, the fpu is not available in a standalone environment.
+.if ${COMPILER_VERSION} < 30800
+CFLAGS.clang+= -mllvm -arm-use-movt=0
+.else
+CFLAGS.clang+= -mno-movt
+.endif
+CFLAGS.clang+= -mfpu=none
+
+# Compiler support functions
+.PATH: ${SRCTOP}/contrib/compiler-rt/lib/builtins/
+# __clzsi2 and ctzsi2 for various builtin functions
+SRCS+= clzsi2.c ctzsi2.c
+# Divide and modulus functions called by the compiler
+SRCS+= divmoddi4.c divmodsi4.c divdi3.c divsi3.c moddi3.c modsi3.c
+SRCS+= udivmoddi4.c udivmodsi4.c udivdi3.c udivsi3.c umoddi3.c umodsi3.c
+
+.PATH: ${SRCTOP}/contrib/compiler-rt/lib/builtins/arm/
+SRCS+= aeabi_idivmod.S aeabi_ldivmod.S aeabi_uidivmod.S aeabi_uldivmod.S
+SRCS+= aeabi_memcmp.S aeabi_memcpy.S aeabi_memmove.S aeabi_memset.S
+.endif
+
+.if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "riscv"
+.PATH: ${LIBC_SRC}/${MACHINE_CPUARCH}/gen
+.endif
+
+.if ${MACHINE_CPUARCH} == "powerpc"
+.PATH: ${LIBC_SRC}/quad
+SRCS+= ashldi3.c ashrdi3.c
+SRCS+= syncicache.c
+.endif
+
+# uuid functions from libc
+.PATH: ${LIBC_SRC}/uuid
+SRCS+= uuid_create_nil.c uuid_equal.c uuid_from_string.c uuid_is_nil.c uuid_to_string.c
+
+# _setjmp/_longjmp
+.PATH: ${SASRC}/${LIBSA_CPUARCH}
+SRCS+= _setjmp.S
+
+# decompression functionality from libbz2
+# NOTE: to actually test this functionality after libbz2 upgrade compile
+# loader(8) with LOADER_BZIP2_SUPPORT defined
+.PATH: ${SRCTOP}/contrib/bzip2
+CFLAGS+= -DBZ_NO_STDIO -DBZ_NO_COMPRESS
+SRCS+= libsa_bzlib_private.h
+
+.for file in bzlib.c crctable.c decompress.c huffman.c randtable.c
+SRCS+= _${file}
+CLEANFILES+= _${file}
+
+_${file}: ${file}
+ sed "s|bzlib_private\.h|libsa_bzlib_private.h|" \
+ ${.ALLSRC} > ${.TARGET}
+.endfor
+
+CLEANFILES+= libsa_bzlib_private.h
+libsa_bzlib_private.h: bzlib_private.h
+ sed -e 's|<stdlib.h>|"stand.h"|' \
+ ${.ALLSRC} > ${.TARGET}
+
+# decompression functionality from zlib
+.PATH: ${SRCTOP}/contrib/zlib
+CFLAGS+=-DHAVE_MEMCPY -I${SRCTOP}/contrib/zlib
+SRCS+= adler32.c crc32.c libsa_zutil.h libsa_gzguts.h
+
+.for file in infback.c inffast.c inflate.c inftrees.c zutil.c
+SRCS+= _${file}
+CLEANFILES+= _${file}
+
+_${file}: ${file}
+ sed -e "s|zutil\.h|libsa_zutil.h|" \
+ -e "s|gzguts\.h|libsa_gzguts.h|" \
+ ${.ALLSRC} > ${.TARGET}
+.endfor
+
+# depend on stand.h being able to be included multiple times
+.for file in zutil.h gzguts.h
+CLEANFILES+= libsa_${file}
+libsa_${file}: ${file}
+ sed -e 's|<fcntl.h>|"stand.h"|' \
+ -e 's|<stddef.h>|"stand.h"|' \
+ -e 's|<string.h>|"stand.h"|' \
+ -e 's|<stdio.h>|"stand.h"|' \
+ -e 's|<stdlib.h>|"stand.h"|' \
+ ${.ALLSRC} > ${.TARGET}
+.endfor
+
+# io routines
+SRCS+= closeall.c dev.c ioctl.c nullfs.c stat.c \
+ fstat.c close.c lseek.c open.c read.c write.c readdir.c
+
+# network routines
+SRCS+= arp.c ether.c ip.c inet_ntoa.c in_cksum.c net.c udp.c netif.c rpc.c
+
+# network info services:
+SRCS+= bootp.c rarp.c bootparam.c
+
+# boot filesystems
+SRCS+= ufs.c nfs.c cd9660.c tftp.c gzipfs.c bzipfs.c
+SRCS+= dosfs.c ext2fs.c
+SRCS+= splitfs.c
+SRCS+= pkgfs.c
+.if ${MK_NAND} != "no"
+SRCS+= nandfs.c
+.endif
+
+# explicit_bzero
+.PATH: ${SYSDIR}/libkern
+SRCS+= explicit_bzero.c
+
+.include <bsd.stand.mk>
+.include <bsd.lib.mk>
diff --git a/stand/libsa/Makefile.depend b/stand/libsa/Makefile.depend
new file mode 100644
index 0000000..1d86fce
--- /dev/null
+++ b/stand/libsa/Makefile.depend
@@ -0,0 +1,15 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/libbz2 \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/libsa/__main.c b/stand/libsa/__main.c
new file mode 100644
index 0000000..e38f338
--- /dev/null
+++ b/stand/libsa/__main.c
@@ -0,0 +1,43 @@
+/* $NetBSD: __main.c,v 1.4 1996/03/14 18:52:03 christos Exp $ */
+
+/*
+ * Copyright (c) 1993 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+void __main(void);
+
+void
+__main(void)
+{
+}
diff --git a/stand/libsa/amd64/_setjmp.S b/stand/libsa/amd64/_setjmp.S
new file mode 100644
index 0000000..e841f49
--- /dev/null
+++ b/stand/libsa/amd64/_setjmp.S
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+ .asciz "@(#)_setjmp.s 5.1 (Berkeley) 4/23/90"
+#endif /* LIBC_SCCS and not lint */
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * C library -- _setjmp, _longjmp
+ *
+ * _longjmp(a,v)
+ * will generate a "return(v)" from the last call to
+ * _setjmp(a)
+ * by restoring registers from the environment 'a'.
+ * The previous signal state is NOT restored.
+ */
+
+ENTRY(_setjmp)
+ movq %rdi,%rax
+ movq 0(%rsp),%rdx /* retval */
+ movq %rdx, 0(%rax) /* 0; retval */
+ movq %rbx, 8(%rax) /* 1; rbx */
+ movq %rsp,16(%rax) /* 2; rsp */
+ movq %rbp,24(%rax) /* 3; rbp */
+ movq %r12,32(%rax) /* 4; r12 */
+ movq %r13,40(%rax) /* 5; r13 */
+ movq %r14,48(%rax) /* 6; r14 */
+ movq %r15,56(%rax) /* 7; r15 */
+ fnstcw 64(%rax) /* 8; fpu cw */
+ stmxcsr 68(%rax) /* and mxcsr */
+ xorq %rax,%rax
+ ret
+END(_setjmp)
+
+ .weak CNAME(_longjmp)
+ENTRY(_longjmp)
+ movq %rdi,%rdx
+ /* Restore the mxcsr, but leave exception flags intact. */
+ stmxcsr -4(%rsp)
+ movl 68(%rdx),%eax
+ andl $0xffffffc0,%eax
+ movl -4(%rsp),%edi
+ andl $0x3f,%edi
+ xorl %eax,%edi
+ movl %edi,-4(%rsp)
+ ldmxcsr -4(%rsp)
+ movq %rsi,%rax /* retval */
+ movq 0(%rdx),%rcx
+ movq 8(%rdx),%rbx
+ movq 16(%rdx),%rsp
+ movq 24(%rdx),%rbp
+ movq 32(%rdx),%r12
+ movq 40(%rdx),%r13
+ movq 48(%rdx),%r14
+ movq 56(%rdx),%r15
+ fldcw 64(%rdx)
+ testq %rax,%rax
+ jnz 1f
+ incq %rax
+1: movq %rcx,0(%rsp)
+ ret
+END(_longjmp)
diff --git a/stand/libsa/arp.c b/stand/libsa/arp.c
new file mode 100644
index 0000000..61b5f1f
--- /dev/null
+++ b/stand/libsa/arp.c
@@ -0,0 +1,305 @@
+/* $NetBSD: arp.c,v 1.18 1997/07/07 15:52:49 drochner Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: arp.c,v 1.5 93/07/15 05:52:26 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <netinet/in_systm.h>
+
+#include <string.h>
+
+#include "stand.h"
+#include "net.h"
+
+/* Cache stuff */
+#define ARP_NUM 8 /* need at most 3 arp entries */
+
+struct arp_list {
+ struct in_addr addr;
+ u_char ea[6];
+} arp_list[ARP_NUM] = {
+ /* XXX - net order `INADDR_BROADCAST' must be a constant */
+ { {0xffffffff}, BA }
+};
+int arp_num = 1;
+
+/* Local forwards */
+static ssize_t arpsend(struct iodesc *, void *, size_t);
+static ssize_t arprecv(struct iodesc *, void **, void **, time_t);
+
+/* Broadcast an ARP packet, asking who has addr on interface d */
+u_char *
+arpwhohas(struct iodesc *d, struct in_addr addr)
+{
+ int i;
+ struct ether_arp *ah;
+ struct arp_list *al;
+ void *pkt;
+ struct {
+ struct ether_header eh;
+ struct {
+ struct ether_arp arp;
+ u_char pad[18]; /* 60 - sizeof(...) */
+ } data;
+ } wbuf;
+
+ /* Try for cached answer first */
+ for (i = 0, al = arp_list; i < arp_num; ++i, ++al)
+ if (addr.s_addr == al->addr.s_addr)
+ return (al->ea);
+
+ /* Don't overflow cache */
+ if (arp_num > ARP_NUM - 1) {
+ arp_num = 1; /* recycle */
+ printf("arpwhohas: overflowed arp_list!\n");
+ }
+
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("arpwhohas: send request for %s\n", inet_ntoa(addr));
+#endif
+
+ bzero((char*)&wbuf.data, sizeof(wbuf.data));
+ ah = &wbuf.data.arp;
+ ah->arp_hrd = htons(ARPHRD_ETHER);
+ ah->arp_pro = htons(ETHERTYPE_IP);
+ ah->arp_hln = sizeof(ah->arp_sha); /* hardware address length */
+ ah->arp_pln = sizeof(ah->arp_spa); /* protocol address length */
+ ah->arp_op = htons(ARPOP_REQUEST);
+ MACPY(d->myea, ah->arp_sha);
+ bcopy(&d->myip, ah->arp_spa, sizeof(ah->arp_spa));
+ /* Leave zeros in arp_tha */
+ bcopy(&addr, ah->arp_tpa, sizeof(ah->arp_tpa));
+
+ /* Store ip address in cache (incomplete entry). */
+ al->addr = addr;
+
+ pkt = NULL;
+ ah = NULL;
+ i = sendrecv(d,
+ arpsend, &wbuf.data, sizeof(wbuf.data),
+ arprecv, &pkt, (void **)&ah);
+ if (i == -1) {
+ panic("arp: no response for %s\n",
+ inet_ntoa(addr));
+ }
+
+ /* Store ethernet address in cache */
+#ifdef ARP_DEBUG
+ if (debug) {
+ struct ether_header *eh;
+
+ eh = (struct ether_header *)((uintptr_t)pkt + ETHER_ALIGN);
+ printf("arp: response from %s\n",
+ ether_sprintf(eh->ether_shost));
+ printf("arp: cacheing %s --> %s\n",
+ inet_ntoa(addr), ether_sprintf(ah->arp_sha));
+ }
+#endif
+ MACPY(ah->arp_sha, al->ea);
+ ++arp_num;
+
+ free(pkt);
+ return (al->ea);
+}
+
+static ssize_t
+arpsend(struct iodesc *d, void *pkt, size_t len)
+{
+
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("arpsend: called\n");
+#endif
+
+ return (sendether(d, pkt, len, bcea, ETHERTYPE_ARP));
+}
+
+/*
+ * Returns 0 if this is the packet we're waiting for
+ * else -1 (and errno == 0)
+ */
+static ssize_t
+arprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft)
+{
+ ssize_t n;
+ struct ether_arp *ah;
+ u_int16_t etype; /* host order */
+ void *ptr;
+
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("arprecv: ");
+#endif
+
+ ptr = NULL;
+ n = readether(d, &ptr, (void **)&ah, tleft, &etype);
+ errno = 0; /* XXX */
+ if (n == -1 || n < sizeof(struct ether_arp)) {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("bad len=%d\n", n);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ if (etype != ETHERTYPE_ARP) {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("not arp type=%d\n", etype);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* Ethernet address now checked in readether() */
+ if (ah->arp_hrd != htons(ARPHRD_ETHER) ||
+ ah->arp_pro != htons(ETHERTYPE_IP) ||
+ ah->arp_hln != sizeof(ah->arp_sha) ||
+ ah->arp_pln != sizeof(ah->arp_spa) )
+ {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("bad hrd/pro/hln/pln\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ if (ah->arp_op == htons(ARPOP_REQUEST)) {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("is request\n");
+#endif
+ arp_reply(d, ah);
+ free(ptr);
+ return (-1);
+ }
+
+ if (ah->arp_op != htons(ARPOP_REPLY)) {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("not ARP reply\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* Is the reply from the source we want? */
+ if (bcmp(&arp_list[arp_num].addr,
+ ah->arp_spa, sizeof(ah->arp_spa)))
+ {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("unwanted address\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+ /* We don't care who the reply was sent to. */
+
+ /* We have our answer. */
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("got it\n");
+#endif
+ *pkt = ptr;
+ *payload = ah;
+ return (n);
+}
+
+/*
+ * Convert an ARP request into a reply and send it.
+ * Notes: Re-uses buffer. Pad to length = 46.
+ */
+void
+arp_reply(struct iodesc *d, void *pkt)
+{
+ struct ether_arp *arp = pkt;
+
+ if (arp->arp_hrd != htons(ARPHRD_ETHER) ||
+ arp->arp_pro != htons(ETHERTYPE_IP) ||
+ arp->arp_hln != sizeof(arp->arp_sha) ||
+ arp->arp_pln != sizeof(arp->arp_spa) )
+ {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("arp_reply: bad hrd/pro/hln/pln\n");
+#endif
+ return;
+ }
+
+ if (arp->arp_op != htons(ARPOP_REQUEST)) {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("arp_reply: not request!\n");
+#endif
+ return;
+ }
+
+ /* If we are not the target, ignore the request. */
+ if (bcmp(arp->arp_tpa, &d->myip, sizeof(arp->arp_tpa)))
+ return;
+
+#ifdef ARP_DEBUG
+ if (debug) {
+ printf("arp_reply: to %s\n", ether_sprintf(arp->arp_sha));
+ }
+#endif
+
+ arp->arp_op = htons(ARPOP_REPLY);
+ /* source becomes target */
+ bcopy(arp->arp_sha, arp->arp_tha, sizeof(arp->arp_tha));
+ bcopy(arp->arp_spa, arp->arp_tpa, sizeof(arp->arp_tpa));
+ /* here becomes source */
+ bcopy(d->myea, arp->arp_sha, sizeof(arp->arp_sha));
+ bcopy(&d->myip, arp->arp_spa, sizeof(arp->arp_spa));
+
+ /*
+ * No need to get fancy here. If the send fails, the
+ * requestor will just ask again.
+ */
+ (void) sendether(d, pkt, sizeof(*arp) + 18,
+ arp->arp_tha, ETHERTYPE_ARP);
+}
diff --git a/stand/libsa/assert.c b/stand/libsa/assert.c
new file mode 100644
index 0000000..8eec63a
--- /dev/null
+++ b/stand/libsa/assert.c
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1998 Michael Smith.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+
+#include "stand.h"
+
+void
+__assert(const char *func, const char *file, int line, const char *expression)
+{
+ if (func == NULL)
+ panic("Assertion failed: (%s), file %s, line %d.\n",
+ expression, file, line);
+ else
+ panic(
+ "Assertion failed: (%s), function %s, file %s, line %d.\n",
+ expression, func, file, line);
+}
diff --git a/stand/libsa/bcd.c b/stand/libsa/bcd.c
new file mode 100644
index 0000000..7bd67c9
--- /dev/null
+++ b/stand/libsa/bcd.c
@@ -0,0 +1,38 @@
+/*
+ * Some data-tables that are often used.
+ * Cannot be copyrighted.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+u_char const bcd2bin_data[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 0, 0, 0, 0, 0,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0, 0, 0, 0, 0, 0,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0, 0, 0, 0, 0, 0,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0, 0, 0, 0, 0, 0,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0, 0, 0, 0, 0, 0,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99
+};
+
+u_char const bin2bcd_data[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99
+};
+
+/* This is actually used with radix [2..36] */
+char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
diff --git a/stand/libsa/bootp.c b/stand/libsa/bootp.c
new file mode 100644
index 0000000..9f08faf
--- /dev/null
+++ b/stand/libsa/bootp.c
@@ -0,0 +1,791 @@
+/* $NetBSD: bootp.c,v 1.14 1998/02/16 11:10:54 drochner Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: bootp.c,v 1.4 93/09/11 03:13:51 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/limits.h>
+#include <sys/endian.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <string.h>
+
+#define BOOTP_DEBUGxx
+#define SUPPORT_DHCP
+
+#define DHCP_ENV_NOVENDOR 1 /* do not parse vendor options */
+#define DHCP_ENV_PXE 10 /* assume pxe vendor options */
+#define DHCP_ENV_FREEBSD 11 /* assume freebsd vendor options */
+/* set DHCP_ENV to one of the values above to export dhcp options to kenv */
+#define DHCP_ENV DHCP_ENV_NO_VENDOR
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+#include "bootp.h"
+
+
+struct in_addr servip;
+
+static time_t bot;
+
+static char vm_rfc1048[4] = VM_RFC1048;
+#ifdef BOOTP_VEND_CMU
+static char vm_cmu[4] = VM_CMU;
+#endif
+
+/* Local forwards */
+static ssize_t bootpsend(struct iodesc *, void *, size_t);
+static ssize_t bootprecv(struct iodesc *, void **, void **, time_t);
+static int vend_rfc1048(u_char *, u_int);
+#ifdef BOOTP_VEND_CMU
+static void vend_cmu(u_char *);
+#endif
+
+#ifdef DHCP_ENV /* export the dhcp response to kenv */
+struct dhcp_opt;
+static void setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts);
+#else
+#define setenv_(a, b, c)
+#endif
+
+#ifdef SUPPORT_DHCP
+static char expected_dhcpmsgtype = -1, dhcp_ok;
+struct in_addr dhcp_serverip;
+#endif
+struct bootp *bootp_response;
+size_t bootp_response_size;
+
+static void
+bootp_fill_request(unsigned char *bp_vend)
+{
+ /*
+ * We are booting from PXE, we want to send the string
+ * 'PXEClient' to the DHCP server so you have the option of
+ * only responding to PXE aware dhcp requests.
+ */
+ bp_vend[0] = TAG_CLASSID;
+ bp_vend[1] = 9;
+ bcopy("PXEClient", &bp_vend[2], 9);
+ bp_vend[11] = TAG_USER_CLASS;
+ /* len of each user class + number of user class */
+ bp_vend[12] = 8;
+ /* len of the first user class */
+ bp_vend[13] = 7;
+ bcopy("FreeBSD", &bp_vend[14], 7);
+ bp_vend[21] = TAG_PARAM_REQ;
+ bp_vend[22] = 7;
+ bp_vend[23] = TAG_ROOTPATH;
+ bp_vend[24] = TAG_HOSTNAME;
+ bp_vend[25] = TAG_SWAPSERVER;
+ bp_vend[26] = TAG_GATEWAY;
+ bp_vend[27] = TAG_SUBNET_MASK;
+ bp_vend[28] = TAG_INTF_MTU;
+ bp_vend[29] = TAG_SERVERID;
+ bp_vend[30] = TAG_END;
+}
+
+/* Fetch required bootp infomation */
+void
+bootp(int sock)
+{
+ void *pkt;
+ struct iodesc *d;
+ struct bootp *bp;
+ struct {
+ u_char header[HEADER_SIZE];
+ struct bootp wbootp;
+ } wbuf;
+ struct bootp *rbootp;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootp: socket=%d\n", sock);
+#endif
+ if (!bot)
+ bot = getsecs();
+
+ if (!(d = socktodesc(sock))) {
+ printf("bootp: bad socket. %d\n", sock);
+ return;
+ }
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootp: d=%lx\n", (long)d);
+#endif
+
+ bp = &wbuf.wbootp;
+ bzero(bp, sizeof(*bp));
+
+ bp->bp_op = BOOTREQUEST;
+ bp->bp_htype = 1; /* 10Mb Ethernet (48 bits) */
+ bp->bp_hlen = 6;
+ bp->bp_xid = htonl(d->xid);
+ MACPY(d->myea, bp->bp_chaddr);
+ strncpy(bp->bp_file, bootfile, sizeof(bp->bp_file));
+ bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048));
+#ifdef SUPPORT_DHCP
+ bp->bp_vend[4] = TAG_DHCP_MSGTYPE;
+ bp->bp_vend[5] = 1;
+ bp->bp_vend[6] = DHCPDISCOVER;
+ bootp_fill_request(&bp->bp_vend[7]);
+
+#else
+ bp->bp_vend[4] = TAG_END;
+#endif
+
+ d->myip.s_addr = INADDR_ANY;
+ d->myport = htons(IPPORT_BOOTPC);
+ d->destip.s_addr = INADDR_BROADCAST;
+ d->destport = htons(IPPORT_BOOTPS);
+
+#ifdef SUPPORT_DHCP
+ expected_dhcpmsgtype = DHCPOFFER;
+ dhcp_ok = 0;
+#endif
+
+ if(sendrecv(d,
+ bootpsend, bp, sizeof(*bp),
+ bootprecv, &pkt, (void **)&rbootp) == -1) {
+ printf("bootp: no reply\n");
+ return;
+ }
+
+#ifdef SUPPORT_DHCP
+ if(dhcp_ok) {
+ u_int32_t leasetime;
+ bp->bp_vend[6] = DHCPREQUEST;
+ bp->bp_vend[7] = TAG_REQ_ADDR;
+ bp->bp_vend[8] = 4;
+ bcopy(&rbootp->bp_yiaddr, &bp->bp_vend[9], 4);
+ bp->bp_vend[13] = TAG_SERVERID;
+ bp->bp_vend[14] = 4;
+ bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4);
+ bp->bp_vend[19] = TAG_LEASETIME;
+ bp->bp_vend[20] = 4;
+ leasetime = htonl(300);
+ bcopy(&leasetime, &bp->bp_vend[21], 4);
+ bootp_fill_request(&bp->bp_vend[25]);
+
+ expected_dhcpmsgtype = DHCPACK;
+
+ free(pkt);
+ if(sendrecv(d,
+ bootpsend, bp, sizeof(*bp),
+ bootprecv, &pkt, (void **)&rbootp) == -1) {
+ printf("DHCPREQUEST failed\n");
+ return;
+ }
+ }
+#endif
+
+ myip = d->myip = rbootp->bp_yiaddr;
+ servip = rbootp->bp_siaddr;
+ if (rootip.s_addr == INADDR_ANY)
+ rootip = servip;
+ bcopy(rbootp->bp_file, bootfile, sizeof(bootfile));
+ bootfile[sizeof(bootfile) - 1] = '\0';
+
+ if (!netmask) {
+ if (IN_CLASSA(ntohl(myip.s_addr)))
+ netmask = htonl(IN_CLASSA_NET);
+ else if (IN_CLASSB(ntohl(myip.s_addr)))
+ netmask = htonl(IN_CLASSB_NET);
+ else
+ netmask = htonl(IN_CLASSC_NET);
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("'native netmask' is %s\n", intoa(netmask));
+#endif
+ }
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("mask: %s\n", intoa(netmask));
+#endif
+
+ /* We need a gateway if root is on a different net */
+ if (!SAMENET(myip, rootip, netmask)) {
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("need gateway for root ip\n");
+#endif
+ }
+
+ /* Toss gateway if on a different net */
+ if (!SAMENET(myip, gateip, netmask)) {
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("gateway ip (%s) bad\n", inet_ntoa(gateip));
+#endif
+ gateip.s_addr = 0;
+ }
+
+ /* Bump xid so next request will be unique. */
+ ++d->xid;
+ free(pkt);
+}
+
+/* Transmit a bootp request */
+static ssize_t
+bootpsend(struct iodesc *d, void *pkt, size_t len)
+{
+ struct bootp *bp;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootpsend: d=%lx called.\n", (long)d);
+#endif
+
+ bp = pkt;
+ bp->bp_secs = htons((u_short)(getsecs() - bot));
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootpsend: calling sendudp\n");
+#endif
+
+ return (sendudp(d, pkt, len));
+}
+
+static ssize_t
+bootprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft)
+{
+ ssize_t n;
+ struct bootp *bp;
+ void *ptr;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootp_recvoffer: called\n");
+#endif
+
+ ptr = NULL;
+ n = readudp(d, &ptr, (void **)&bp, tleft);
+ if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE)
+ goto bad;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootprecv: checked. bp = %p, n = %zd\n", bp, n);
+#endif
+ if (bp->bp_xid != htonl(d->xid)) {
+#ifdef BOOTP_DEBUG
+ if (debug) {
+ printf("bootprecv: expected xid 0x%lx, got 0x%x\n",
+ d->xid, ntohl(bp->bp_xid));
+ }
+#endif
+ goto bad;
+ }
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootprecv: got one!\n");
+#endif
+
+ /* Suck out vendor info */
+ if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) {
+ int vsize = n - offsetof(struct bootp, bp_vend);
+ if (vend_rfc1048(bp->bp_vend, vsize) != 0)
+ goto bad;
+
+ /* Save copy of bootp reply or DHCP ACK message */
+ if (bp->bp_op == BOOTREPLY &&
+ ((dhcp_ok == 1 && expected_dhcpmsgtype == DHCPACK) ||
+ dhcp_ok == 0)) {
+ free(bootp_response);
+ bootp_response = malloc(n);
+ if (bootp_response != NULL) {
+ bootp_response_size = n;
+ bcopy(bp, bootp_response, bootp_response_size);
+ }
+ }
+ }
+#ifdef BOOTP_VEND_CMU
+ else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0)
+ vend_cmu(bp->bp_vend);
+#endif
+ else
+ printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend);
+
+ *pkt = ptr;
+ *payload = bp;
+ return (n);
+bad:
+ free(ptr);
+ errno = 0;
+ return (-1);
+}
+
+int
+dhcp_try_rfc1048(u_char *cp, u_int len)
+{
+
+ expected_dhcpmsgtype = DHCPACK;
+ if (bcmp(vm_rfc1048, cp, sizeof(vm_rfc1048)) == 0) {
+ return (vend_rfc1048(cp, len));
+ }
+ return (-1);
+}
+
+static int
+vend_rfc1048(u_char *cp, u_int len)
+{
+ u_char *ep;
+ int size;
+ u_char tag;
+ const char *val;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("vend_rfc1048 bootp info. len=%d\n", len);
+#endif
+ ep = cp + len;
+
+ /* Step over magic cookie */
+ cp += sizeof(int);
+
+ setenv_(cp, ep, NULL);
+
+ while (cp < ep) {
+ tag = *cp++;
+ size = *cp++;
+ if (tag == TAG_END)
+ break;
+
+ if (tag == TAG_SUBNET_MASK) {
+ bcopy(cp, &netmask, sizeof(netmask));
+ }
+ if (tag == TAG_GATEWAY) {
+ bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr));
+ }
+ if (tag == TAG_SWAPSERVER) {
+ /* let it override bp_siaddr */
+ bcopy(cp, &rootip.s_addr, sizeof(rootip.s_addr));
+ }
+ if (tag == TAG_ROOTPATH) {
+ if ((val = getenv("dhcp.root-path")) == NULL)
+ val = (const char *)cp;
+ strlcpy(rootpath, val, sizeof(rootpath));
+ }
+ if (tag == TAG_HOSTNAME) {
+ if ((val = getenv("dhcp.host-name")) == NULL)
+ val = (const char *)cp;
+ strlcpy(hostname, val, sizeof(hostname));
+ }
+ if (tag == TAG_INTF_MTU) {
+ intf_mtu = 0;
+ if ((val = getenv("dhcp.interface-mtu")) != NULL) {
+ unsigned long tmp;
+ char *end;
+
+ errno = 0;
+ /*
+ * Do not allow MTU to exceed max IPv4 packet
+ * size, max value of 16-bit word.
+ */
+ tmp = strtoul(val, &end, 0);
+ if (errno != 0 ||
+ *val == '\0' || *end != '\0' ||
+ tmp > USHRT_MAX) {
+ printf("%s: bad value: \"%s\", "
+ "ignoring\n",
+ "dhcp.interface-mtu", val);
+ } else {
+ intf_mtu = (u_int)tmp;
+ }
+ }
+ if (intf_mtu <= 0)
+ intf_mtu = be16dec(cp);
+ }
+#ifdef SUPPORT_DHCP
+ if (tag == TAG_DHCP_MSGTYPE) {
+ if(*cp != expected_dhcpmsgtype)
+ return(-1);
+ dhcp_ok = 1;
+ }
+ if (tag == TAG_SERVERID) {
+ bcopy(cp, &dhcp_serverip.s_addr,
+ sizeof(dhcp_serverip.s_addr));
+ }
+#endif
+ cp += size;
+ }
+ return(0);
+}
+
+#ifdef BOOTP_VEND_CMU
+static void
+vend_cmu(u_char *cp)
+{
+ struct cmu_vend *vp;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("vend_cmu bootp info.\n");
+#endif
+ vp = (struct cmu_vend *)cp;
+
+ if (vp->v_smask.s_addr != 0) {
+ netmask = vp->v_smask.s_addr;
+ }
+ if (vp->v_dgate.s_addr != 0) {
+ gateip = vp->v_dgate;
+ }
+}
+#endif
+
+#ifdef DHCP_ENV
+/*
+ * Parse DHCP options and store them into kenv variables.
+ * Original code from Danny Braniss, modifications by Luigi Rizzo.
+ *
+ * The parser is driven by tables which specify the type and name of
+ * each dhcp option and how it appears in kenv.
+ * The first entry in the list contains the prefix used to set the kenv
+ * name (including the . if needed), the last entry must have a 0 tag.
+ * Entries do not need to be sorted though it helps for readability.
+ *
+ * Certain vendor-specific tables can be enabled according to DHCP_ENV.
+ * Set it to 0 if you don't want any.
+ */
+enum opt_fmt { __NONE = 0,
+ __8 = 1, __16 = 2, __32 = 4, /* Unsigned fields, value=size */
+ __IP, /* IPv4 address */
+ __TXT, /* C string */
+ __BYTES, /* byte sequence, printed %02x */
+ __INDIR, /* name=value */
+ __ILIST, /* name=value;name=value ... */
+ __VE, /* vendor specific, recurse */
+};
+
+struct dhcp_opt {
+ uint8_t tag;
+ uint8_t fmt;
+ const char *desc;
+};
+
+static struct dhcp_opt vndr_opt[] = { /* Vendor Specific Options */
+#if DHCP_ENV == DHCP_ENV_FREEBSD /* FreeBSD table in the original code */
+ {0, 0, "FreeBSD"}, /* prefix */
+ {1, __TXT, "kernel"},
+ {2, __TXT, "kernelname"},
+ {3, __TXT, "kernel_options"},
+ {4, __IP, "usr-ip"},
+ {5, __TXT, "conf-path"},
+ {6, __TXT, "rc.conf0"},
+ {7, __TXT, "rc.conf1"},
+ {8, __TXT, "rc.conf2"},
+ {9, __TXT, "rc.conf3"},
+ {10, __TXT, "rc.conf4"},
+ {11, __TXT, "rc.conf5"},
+ {12, __TXT, "rc.conf6"},
+ {13, __TXT, "rc.conf7"},
+ {14, __TXT, "rc.conf8"},
+ {15, __TXT, "rc.conf9"},
+
+ {20, __TXT, "boot.nfsroot.options"},
+
+ {245, __INDIR, ""},
+ {246, __INDIR, ""},
+ {247, __INDIR, ""},
+ {248, __INDIR, ""},
+ {249, __INDIR, ""},
+ {250, __INDIR, ""},
+ {251, __INDIR, ""},
+ {252, __INDIR, ""},
+ {253, __INDIR, ""},
+ {254, __INDIR, ""},
+
+#elif DHCP_ENV == DHCP_ENV_PXE /* some pxe options, RFC4578 */
+ {0, 0, "pxe"}, /* prefix */
+ {93, __16, "system-architecture"},
+ {94, __BYTES, "network-interface"},
+ {97, __BYTES, "machine-identifier"},
+#else /* default (empty) table */
+ {0, 0, "dhcp.vendor."}, /* prefix */
+#endif
+ {0, __TXT, "%soption-%d"}
+};
+
+static struct dhcp_opt dhcp_opt[] = {
+ /* DHCP Option names, formats and codes, from RFC2132. */
+ {0, 0, "dhcp."}, // prefix
+ {1, __IP, "subnet-mask"},
+ {2, __32, "time-offset"}, /* this is signed */
+ {3, __IP, "routers"},
+ {4, __IP, "time-servers"},
+ {5, __IP, "ien116-name-servers"},
+ {6, __IP, "domain-name-servers"},
+ {7, __IP, "log-servers"},
+ {8, __IP, "cookie-servers"},
+ {9, __IP, "lpr-servers"},
+ {10, __IP, "impress-servers"},
+ {11, __IP, "resource-location-servers"},
+ {12, __TXT, "host-name"},
+ {13, __16, "boot-size"},
+ {14, __TXT, "merit-dump"},
+ {15, __TXT, "domain-name"},
+ {16, __IP, "swap-server"},
+ {17, __TXT, "root-path"},
+ {18, __TXT, "extensions-path"},
+ {19, __8, "ip-forwarding"},
+ {20, __8, "non-local-source-routing"},
+ {21, __IP, "policy-filter"},
+ {22, __16, "max-dgram-reassembly"},
+ {23, __8, "default-ip-ttl"},
+ {24, __32, "path-mtu-aging-timeout"},
+ {25, __16, "path-mtu-plateau-table"},
+ {26, __16, "interface-mtu"},
+ {27, __8, "all-subnets-local"},
+ {28, __IP, "broadcast-address"},
+ {29, __8, "perform-mask-discovery"},
+ {30, __8, "mask-supplier"},
+ {31, __8, "perform-router-discovery"},
+ {32, __IP, "router-solicitation-address"},
+ {33, __IP, "static-routes"},
+ {34, __8, "trailer-encapsulation"},
+ {35, __32, "arp-cache-timeout"},
+ {36, __8, "ieee802-3-encapsulation"},
+ {37, __8, "default-tcp-ttl"},
+ {38, __32, "tcp-keepalive-interval"},
+ {39, __8, "tcp-keepalive-garbage"},
+ {40, __TXT, "nis-domain"},
+ {41, __IP, "nis-servers"},
+ {42, __IP, "ntp-servers"},
+ {43, __VE, "vendor-encapsulated-options"},
+ {44, __IP, "netbios-name-servers"},
+ {45, __IP, "netbios-dd-server"},
+ {46, __8, "netbios-node-type"},
+ {47, __TXT, "netbios-scope"},
+ {48, __IP, "x-font-servers"},
+ {49, __IP, "x-display-managers"},
+ {50, __IP, "dhcp-requested-address"},
+ {51, __32, "dhcp-lease-time"},
+ {52, __8, "dhcp-option-overload"},
+ {53, __8, "dhcp-message-type"},
+ {54, __IP, "dhcp-server-identifier"},
+ {55, __8, "dhcp-parameter-request-list"},
+ {56, __TXT, "dhcp-message"},
+ {57, __16, "dhcp-max-message-size"},
+ {58, __32, "dhcp-renewal-time"},
+ {59, __32, "dhcp-rebinding-time"},
+ {60, __TXT, "vendor-class-identifier"},
+ {61, __TXT, "dhcp-client-identifier"},
+ {64, __TXT, "nisplus-domain"},
+ {65, __IP, "nisplus-servers"},
+ {66, __TXT, "tftp-server-name"},
+ {67, __TXT, "bootfile-name"},
+ {68, __IP, "mobile-ip-home-agent"},
+ {69, __IP, "smtp-server"},
+ {70, __IP, "pop-server"},
+ {71, __IP, "nntp-server"},
+ {72, __IP, "www-server"},
+ {73, __IP, "finger-server"},
+ {74, __IP, "irc-server"},
+ {75, __IP, "streettalk-server"},
+ {76, __IP, "streettalk-directory-assistance-server"},
+ {77, __TXT, "user-class"},
+ {85, __IP, "nds-servers"},
+ {86, __TXT, "nds-tree-name"},
+ {87, __TXT, "nds-context"},
+ {210, __TXT, "authenticate"},
+
+ /* use the following entries for arbitrary variables */
+ {246, __ILIST, ""},
+ {247, __ILIST, ""},
+ {248, __ILIST, ""},
+ {249, __ILIST, ""},
+ {250, __INDIR, ""},
+ {251, __INDIR, ""},
+ {252, __INDIR, ""},
+ {253, __INDIR, ""},
+ {254, __INDIR, ""},
+ {0, __TXT, "%soption-%d"}
+};
+
+/*
+ * parse a dhcp response, set environment variables translating options
+ * names and values according to the tables above. Also set dhcp.tags
+ * to the list of selected tags.
+ */
+static void
+setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts)
+{
+ u_char *ncp;
+ u_char tag;
+ char tags[512], *tp; /* the list of tags */
+
+#define FLD_SEP ',' /* separator in list of elements */
+ ncp = cp;
+ tp = tags;
+ if (opts == NULL)
+ opts = dhcp_opt;
+
+ while (ncp < ep) {
+ unsigned int size; /* option size */
+ char *vp, *endv, buf[256]; /* the value buffer */
+ struct dhcp_opt *op;
+
+ tag = *ncp++; /* extract tag and size */
+ size = *ncp++;
+ cp = ncp; /* current payload */
+ ncp += size; /* point to the next option */
+
+ if (tag == TAG_END)
+ break;
+ if (tag == 0)
+ continue;
+
+ for (op = opts+1; op->tag && op->tag != tag; op++)
+ ;
+ /* if not found we end up on the default entry */
+
+ /*
+ * Copy data into the buffer. libstand does not have snprintf so we
+ * need to be careful with sprintf(). With strings, the source is
+ * always <256 char so shorter than the buffer so we are safe; with
+ * other arguments, the longest string is inet_ntoa which is 16 bytes
+ * so we make sure to have always enough room in the string before
+ * trying an sprint.
+ */
+ vp = buf;
+ *vp = '\0';
+ endv = buf + sizeof(buf) - 1 - 16; /* last valid write position */
+
+ switch(op->fmt) {
+ case __NONE:
+ break; /* should not happen */
+
+ case __VE: /* recurse, vendor specific */
+ setenv_(cp, cp+size, vndr_opt);
+ break;
+
+ case __IP: /* ip address */
+ for (; size > 0 && vp < endv; size -= 4, cp += 4) {
+ struct in_addr in_ip; /* ip addresses */
+ if (vp != buf)
+ *vp++ = FLD_SEP;
+ bcopy(cp, &in_ip.s_addr, sizeof(in_ip.s_addr));
+ sprintf(vp, "%s", inet_ntoa(in_ip));
+ vp += strlen(vp);
+ }
+ break;
+
+ case __BYTES: /* opaque byte string */
+ for (; size > 0 && vp < endv; size -= 1, cp += 1) {
+ sprintf(vp, "%02x", *cp);
+ vp += strlen(vp);
+ }
+ break;
+
+ case __TXT:
+ bcopy(cp, buf, size); /* cannot overflow */
+ buf[size] = 0;
+ break;
+
+ case __32:
+ case __16:
+ case __8: /* op->fmt is also the length of each field */
+ for (; size > 0 && vp < endv; size -= op->fmt, cp += op->fmt) {
+ uint32_t v;
+ if (op->fmt == __32)
+ v = (cp[0]<<24) + (cp[1]<<16) + (cp[2]<<8) + cp[3];
+ else if (op->fmt == __16)
+ v = (cp[0]<<8) + cp[1];
+ else
+ v = cp[0];
+ if (vp != buf)
+ *vp++ = FLD_SEP;
+ sprintf(vp, "%u", v);
+ vp += strlen(vp);
+ }
+ break;
+
+ case __INDIR: /* name=value */
+ case __ILIST: /* name=value;name=value... */
+ bcopy(cp, buf, size); /* cannot overflow */
+ buf[size] = '\0';
+ for (endv = buf; endv; endv = vp) {
+ u_char *s = NULL; /* semicolon ? */
+
+ /* skip leading whitespace */
+ while (*endv && strchr(" \t\n\r", *endv))
+ endv++;
+ vp = strchr(endv, '='); /* find name=value separator */
+ if (!vp)
+ break;
+ *vp++ = 0;
+ if (op->fmt == __ILIST && (s = strchr(vp, ';')))
+ *s++ = '\0';
+ setenv(endv, vp, 1);
+ vp = s; /* prepare for next round */
+ }
+ buf[0] = '\0'; /* option already done */
+ }
+
+ if (tp - tags < sizeof(tags) - 5) { /* add tag to the list */
+ if (tp != tags)
+ *tp++ = FLD_SEP;
+ sprintf(tp, "%d", tag);
+ tp += strlen(tp);
+ }
+ if (buf[0]) {
+ char env[128]; /* the string name */
+
+ if (op->tag == 0)
+ sprintf(env, op->desc, opts[0].desc, tag);
+ else
+ sprintf(env, "%s%s", opts[0].desc, op->desc);
+ /*
+ * Do not replace existing values in the environment, so that
+ * locally-obtained values can override server-provided values.
+ */
+ setenv(env, buf, 0);
+ }
+ }
+ if (tp != tags) {
+ char env[128]; /* the string name */
+ sprintf(env, "%stags", opts[0].desc);
+ setenv(env, tags, 1);
+ }
+}
+#endif /* additional dhcp */
diff --git a/stand/libsa/bootp.h b/stand/libsa/bootp.h
new file mode 100644
index 0000000..2e7049b
--- /dev/null
+++ b/stand/libsa/bootp.h
@@ -0,0 +1,151 @@
+/* $NetBSD: bootp.h,v 1.4 1997/09/06 13:55:57 drochner Exp $ */
+
+/*
+ * Bootstrap Protocol (BOOTP). RFC951 and RFC1048.
+ *
+ * This file specifies the "implementation-independent" BOOTP protocol
+ * information which is common to both client and server.
+ *
+ * Copyright 1988 by Carnegie Mellon.
+ *
+ * Permission to use, copy, modify, and distribute this program for any
+ * purpose and without fee is hereby granted, provided that this copyright
+ * and permission notice appear on all copies and supporting documentation,
+ * the name of Carnegie Mellon not be used in advertising or publicity
+ * pertaining to distribution of the program without specific prior
+ * permission, and notice be given in supporting documentation that copying
+ * and distribution is by permission of Carnegie Mellon and Stanford
+ * University. Carnegie Mellon makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOTP_H_
+#define _BOOTP_H_
+
+struct bootp {
+ unsigned char bp_op; /* packet opcode type */
+ unsigned char bp_htype; /* hardware addr type */
+ unsigned char bp_hlen; /* hardware addr length */
+ unsigned char bp_hops; /* gateway hops */
+ unsigned int bp_xid; /* transaction ID */
+ unsigned short bp_secs; /* seconds since boot began */
+ unsigned short bp_flags;
+ struct in_addr bp_ciaddr; /* client IP address */
+ struct in_addr bp_yiaddr; /* 'your' IP address */
+ struct in_addr bp_siaddr; /* server IP address */
+ struct in_addr bp_giaddr; /* gateway IP address */
+ unsigned char bp_chaddr[16]; /* client hardware address */
+ unsigned char bp_sname[64]; /* server host name */
+ unsigned char bp_file[128]; /* boot file name */
+#ifdef SUPPORT_DHCP
+#define BOOTP_VENDSIZE 312
+#else
+#define BOOTP_VENDSIZE 64
+#endif
+ unsigned char bp_vend[BOOTP_VENDSIZE]; /* vendor-specific area */
+};
+
+/*
+ * UDP port numbers, server and client.
+ */
+#define IPPORT_BOOTPS 67
+#define IPPORT_BOOTPC 68
+
+#define BOOTREPLY 2
+#define BOOTREQUEST 1
+
+
+/*
+ * Vendor magic cookie (v_magic) for CMU
+ */
+#define VM_CMU "CMU"
+
+/*
+ * Vendor magic cookie (v_magic) for RFC1048
+ */
+#define VM_RFC1048 { 99, 130, 83, 99 }
+
+
+
+/*
+ * RFC1048 tag values used to specify what information is being supplied in
+ * the vendor field of the packet.
+ */
+
+#define TAG_PAD ((unsigned char) 0)
+#define TAG_SUBNET_MASK ((unsigned char) 1)
+#define TAG_TIME_OFFSET ((unsigned char) 2)
+#define TAG_GATEWAY ((unsigned char) 3)
+#define TAG_TIME_SERVER ((unsigned char) 4)
+#define TAG_NAME_SERVER ((unsigned char) 5)
+#define TAG_DOMAIN_SERVER ((unsigned char) 6)
+#define TAG_LOG_SERVER ((unsigned char) 7)
+#define TAG_COOKIE_SERVER ((unsigned char) 8)
+#define TAG_LPR_SERVER ((unsigned char) 9)
+#define TAG_IMPRESS_SERVER ((unsigned char) 10)
+#define TAG_RLP_SERVER ((unsigned char) 11)
+#define TAG_HOSTNAME ((unsigned char) 12)
+#define TAG_BOOTSIZE ((unsigned char) 13)
+#define TAG_DUMPFILE ((unsigned char) 14)
+#define TAG_DOMAINNAME ((unsigned char) 15)
+#define TAG_SWAPSERVER ((unsigned char) 16)
+#define TAG_ROOTPATH ((unsigned char) 17)
+#define TAG_INTF_MTU ((unsigned char) 26)
+
+#ifdef SUPPORT_DHCP
+#define TAG_REQ_ADDR ((unsigned char) 50)
+#define TAG_LEASETIME ((unsigned char) 51)
+#define TAG_OVERLOAD ((unsigned char) 52)
+#define TAG_DHCP_MSGTYPE ((unsigned char) 53)
+#define TAG_SERVERID ((unsigned char) 54)
+#define TAG_PARAM_REQ ((unsigned char) 55)
+#define TAG_MSG ((unsigned char) 56)
+#define TAG_MAXSIZE ((unsigned char) 57)
+#define TAG_T1 ((unsigned char) 58)
+#define TAG_T2 ((unsigned char) 59)
+#define TAG_CLASSID ((unsigned char) 60)
+#define TAG_CLIENTID ((unsigned char) 61)
+#define TAG_USER_CLASS ((unsigned char) 77)
+#endif
+
+#define TAG_END ((unsigned char) 255)
+
+#ifdef SUPPORT_DHCP
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#endif
+
+/*
+ * "vendor" data permitted for CMU bootp clients.
+ */
+
+struct cmu_vend {
+ unsigned char v_magic[4]; /* magic number */
+ unsigned int v_flags; /* flags/opcodes, etc. */
+ struct in_addr v_smask; /* Subnet mask */
+ struct in_addr v_dgate; /* Default gateway */
+ struct in_addr v_dns1, v_dns2; /* Domain name servers */
+ struct in_addr v_ins1, v_ins2; /* IEN-116 name servers */
+ struct in_addr v_ts1, v_ts2; /* Time servers */
+ unsigned char v_unused[25]; /* currently unused */
+};
+
+
+/* v_flags values */
+#define VF_SMASK 1 /* Subnet mask field contains valid data */
+
+/* cached bootp response/dhcp ack */
+extern struct bootp *bootp_response;
+extern size_t bootp_response_size;
+
+int dhcp_try_rfc1048(u_char *cp, u_int len);
+
+#endif /* _BOOTP_H_ */
diff --git a/stand/libsa/bootparam.c b/stand/libsa/bootparam.c
new file mode 100644
index 0000000..1de2d53
--- /dev/null
+++ b/stand/libsa/bootparam.c
@@ -0,0 +1,435 @@
+/* $NetBSD: bootparam.c,v 1.11 1997/06/26 19:11:32 drochner Exp $ */
+
+/*
+ * Copyright (c) 1995 Gordon W. Ross
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * 4. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Gordon W. Ross
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * RPC/bootparams
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <string.h>
+
+#include "rpcv2.h"
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+#include "rpc.h"
+#include "bootparam.h"
+
+#ifdef DEBUG_RPC
+#define RPC_PRINTF(a) printf a
+#else
+#define RPC_PRINTF(a)
+#endif
+
+struct in_addr bp_server_addr; /* net order */
+n_short bp_server_port; /* net order */
+
+/*
+ * RPC definitions for bootparamd
+ */
+#define BOOTPARAM_PROG 100026
+#define BOOTPARAM_VERS 1
+#define BOOTPARAM_WHOAMI 1
+#define BOOTPARAM_GETFILE 2
+
+/*
+ * Inet address in RPC messages
+ * (Note, really four ints, NOT chars. Blech.)
+ */
+struct xdr_inaddr {
+ u_int32_t atype;
+ int32_t addr[4];
+};
+
+int xdr_inaddr_encode(char **p, struct in_addr ia);
+int xdr_inaddr_decode(char **p, struct in_addr *ia);
+
+int xdr_string_encode(char **p, char *str, int len);
+int xdr_string_decode(char **p, char *str, int *len_p);
+
+
+/*
+ * RPC: bootparam/whoami
+ * Given client IP address, get:
+ * client name (hostname)
+ * domain name (domainname)
+ * gateway address
+ *
+ * The hostname and domainname are set here for convenience.
+ *
+ * Note - bpsin is initialized to the broadcast address,
+ * and will be replaced with the bootparam server address
+ * after this call is complete. Have to use PMAP_PROC_CALL
+ * to make sure we get responses only from a servers that
+ * know about us (don't want to broadcast a getport call).
+ */
+int
+bp_whoami(int sockfd)
+{
+ /* RPC structures for PMAPPROC_CALLIT */
+ struct args {
+ u_int32_t prog;
+ u_int32_t vers;
+ u_int32_t proc;
+ u_int32_t arglen;
+ struct xdr_inaddr xina;
+ } *args;
+ struct repl {
+ u_int16_t _pad;
+ u_int16_t port;
+ u_int32_t encap_len;
+ /* encapsulated data here */
+ n_long capsule[64];
+ } *repl;
+ struct {
+ n_long h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ char *send_tail, *recv_head;
+ struct iodesc *d;
+ void *pkt;
+ int len, x, rc;
+
+ RPC_PRINTF(("bp_whoami: myip=%s\n", inet_ntoa(myip)));
+
+ rc = -1;
+ if (!(d = socktodesc(sockfd))) {
+ RPC_PRINTF(("bp_whoami: bad socket. %d\n", sockfd));
+ return (rc);
+ }
+ args = &sdata.d;
+
+ /*
+ * Build request args for PMAPPROC_CALLIT.
+ */
+ args->prog = htonl(BOOTPARAM_PROG);
+ args->vers = htonl(BOOTPARAM_VERS);
+ args->proc = htonl(BOOTPARAM_WHOAMI);
+ args->arglen = htonl(sizeof(struct xdr_inaddr));
+ send_tail = (char*) &args->xina;
+
+ /*
+ * append encapsulated data (client IP address)
+ */
+ if (xdr_inaddr_encode(&send_tail, myip))
+ return (rc);
+
+ /* RPC: portmap/callit */
+ d->myport = htons(--rpc_port);
+ d->destip.s_addr = INADDR_BROADCAST; /* XXX: subnet bcast? */
+ /* rpc_call will set d->destport */
+
+ pkt = NULL;
+ len = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_CALLIT,
+ args, send_tail - (char*)args, (void **)&repl, &pkt);
+ if (len < 8) {
+ printf("bootparamd: 'whoami' call failed\n");
+ goto done;
+ }
+
+ /* Save bootparam server address (from IP header). */
+ rpc_fromaddr(repl, &bp_server_addr, &bp_server_port);
+
+ /*
+ * Note that bp_server_port is now 111 due to the
+ * indirect call (using PMAPPROC_CALLIT), so get the
+ * actual port number from the reply data.
+ */
+ bp_server_port = repl->port;
+
+ RPC_PRINTF(("bp_whoami: server at %s:%d\n",
+ inet_ntoa(bp_server_addr), ntohs(bp_server_port)));
+
+ /* We have just done a portmap call, so cache the portnum. */
+ rpc_pmap_putcache(bp_server_addr,
+ BOOTPARAM_PROG,
+ BOOTPARAM_VERS,
+ (int)ntohs(bp_server_port));
+
+ /*
+ * Parse the encapsulated results from bootparam/whoami
+ */
+ x = ntohl(repl->encap_len);
+ if (len < x) {
+ printf("bp_whoami: short reply, %d < %d\n", len, x);
+ goto done;
+ }
+ recv_head = (char*) repl->capsule;
+
+ /* client name */
+ hostnamelen = MAXHOSTNAMELEN-1;
+ if (xdr_string_decode(&recv_head, hostname, &hostnamelen)) {
+ RPC_PRINTF(("bp_whoami: bad hostname\n"));
+ goto done;
+ }
+
+ /* domain name */
+ domainnamelen = MAXHOSTNAMELEN-1;
+ if (xdr_string_decode(&recv_head, domainname, &domainnamelen)) {
+ RPC_PRINTF(("bp_whoami: bad domainname\n"));
+ goto done;
+ }
+
+ /* gateway address */
+ if (xdr_inaddr_decode(&recv_head, &gateip)) {
+ RPC_PRINTF(("bp_whoami: bad gateway\n"));
+ goto done;
+ }
+
+ /* success */
+ rc = 0;
+done:
+ free(pkt);
+ return (rc);
+}
+
+
+/*
+ * RPC: bootparam/getfile
+ * Given client name and file "key", get:
+ * server name
+ * server IP address
+ * server pathname
+ */
+int
+bp_getfile(int sockfd, char *key, struct in_addr *serv_addr, char *pathname)
+{
+ struct {
+ n_long h[RPC_HEADER_WORDS];
+ n_long d[64];
+ } sdata;
+ void *pkt;
+ char serv_name[FNAME_SIZE];
+ char *rdata, *send_tail;
+ /* misc... */
+ struct iodesc *d;
+ int rc = -1, sn_len, path_len, rlen;
+
+ if (!(d = socktodesc(sockfd))) {
+ RPC_PRINTF(("bp_getfile: bad socket. %d\n", sockfd));
+ return (-1);
+ }
+
+ send_tail = (char*) sdata.d;
+
+ /*
+ * Build request message.
+ */
+
+ /* client name (hostname) */
+ if (xdr_string_encode(&send_tail, hostname, hostnamelen)) {
+ RPC_PRINTF(("bp_getfile: bad client\n"));
+ return (-1);
+ }
+
+ /* key name (root or swap) */
+ if (xdr_string_encode(&send_tail, key, strlen(key))) {
+ RPC_PRINTF(("bp_getfile: bad key\n"));
+ return (-1);
+ }
+
+ /* RPC: bootparam/getfile */
+ d->myport = htons(--rpc_port);
+ d->destip = bp_server_addr;
+ /* rpc_call will set d->destport */
+ pkt = NULL;
+ rlen = rpc_call(d,
+ BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_GETFILE,
+ sdata.d, send_tail - (char*)sdata.d,
+ (void **)&rdata, &pkt);
+ if (rlen < 4) {
+ RPC_PRINTF(("bp_getfile: short reply\n"));
+ errno = EBADRPC;
+ goto done;
+ }
+
+ /*
+ * Parse result message.
+ */
+
+ /* server name */
+ sn_len = FNAME_SIZE-1;
+ if (xdr_string_decode(&rdata, serv_name, &sn_len)) {
+ RPC_PRINTF(("bp_getfile: bad server name\n"));
+ goto done;
+ }
+
+ /* server IP address (mountd/NFS) */
+ if (xdr_inaddr_decode(&rdata, serv_addr)) {
+ RPC_PRINTF(("bp_getfile: bad server addr\n"));
+ goto done;
+ }
+
+ /* server pathname */
+ path_len = MAXPATHLEN-1;
+ if (xdr_string_decode(&rdata, pathname, &path_len)) {
+ RPC_PRINTF(("bp_getfile: bad server path\n"));
+ goto done;
+ }
+
+ /* success */
+ rc = 0;
+done:
+ free(pkt);
+ return (rc);
+}
+
+
+/*
+ * eXternal Data Representation routines.
+ * (but with non-standard args...)
+ */
+
+
+int
+xdr_string_encode(char **pkt, char *str, int len)
+{
+ uint32_t *lenp;
+ char *datap;
+ int padlen = (len + 3) & ~3; /* padded length */
+
+ /* The data will be int aligned. */
+ lenp = (uint32_t *) *pkt;
+ *pkt += sizeof(*lenp);
+ *lenp = htonl(len);
+
+ datap = *pkt;
+ *pkt += padlen;
+ bcopy(str, datap, len);
+
+ return (0);
+}
+
+int
+xdr_string_decode(char **pkt, char *str, int *len_p)
+{
+ uint32_t *lenp;
+ char *datap;
+ int slen; /* string length */
+ int plen; /* padded length */
+
+ /* The data will be int aligned. */
+ lenp = (uint32_t *) *pkt;
+ *pkt += sizeof(*lenp);
+ slen = ntohl(*lenp);
+ plen = (slen + 3) & ~3;
+
+ if (slen > *len_p)
+ slen = *len_p;
+ datap = *pkt;
+ *pkt += plen;
+ bcopy(datap, str, slen);
+
+ str[slen] = '\0';
+ *len_p = slen;
+
+ return (0);
+}
+
+
+int
+xdr_inaddr_encode(char **pkt, struct in_addr ia)
+{
+ struct xdr_inaddr *xi;
+ u_char *cp;
+ int32_t *ip;
+ union {
+ n_long l; /* network order */
+ u_char c[4];
+ } uia;
+
+ /* The data will be int aligned. */
+ xi = (struct xdr_inaddr *) *pkt;
+ *pkt += sizeof(*xi);
+ xi->atype = htonl(1);
+ uia.l = ia.s_addr;
+ cp = uia.c;
+ ip = xi->addr;
+ /*
+ * Note: the htonl() calls below DO NOT
+ * imply that uia.l is in host order.
+ * In fact this needs it in net order.
+ */
+ *ip++ = htonl((unsigned int)*cp++);
+ *ip++ = htonl((unsigned int)*cp++);
+ *ip++ = htonl((unsigned int)*cp++);
+ *ip++ = htonl((unsigned int)*cp++);
+
+ return (0);
+}
+
+int
+xdr_inaddr_decode(char **pkt, struct in_addr *ia)
+{
+ struct xdr_inaddr *xi;
+ u_char *cp;
+ int32_t *ip;
+ union {
+ n_long l; /* network order */
+ u_char c[4];
+ } uia;
+
+ /* The data will be int aligned. */
+ xi = (struct xdr_inaddr *) *pkt;
+ *pkt += sizeof(*xi);
+ if (xi->atype != htonl(1)) {
+ RPC_PRINTF(("xdr_inaddr_decode: bad addrtype=%d\n",
+ ntohl(xi->atype)));
+ return(-1);
+ }
+
+ cp = uia.c;
+ ip = xi->addr;
+ /*
+ * Note: the ntohl() calls below DO NOT
+ * imply that uia.l is in host order.
+ * In fact this needs it in net order.
+ */
+ *cp++ = ntohl(*ip++);
+ *cp++ = ntohl(*ip++);
+ *cp++ = ntohl(*ip++);
+ *cp++ = ntohl(*ip++);
+ ia->s_addr = uia.l;
+
+ return (0);
+}
diff --git a/stand/libsa/bootparam.h b/stand/libsa/bootparam.h
new file mode 100644
index 0000000..6f0c773
--- /dev/null
+++ b/stand/libsa/bootparam.h
@@ -0,0 +1,5 @@
+/* $NetBSD: bootparam.h,v 1.3 1998/01/05 19:19:41 perry Exp $ */
+
+int bp_whoami(int sock);
+int bp_getfile(int sock, char *key, struct in_addr *addrp, char *path);
+
diff --git a/stand/libsa/bzipfs.c b/stand/libsa/bzipfs.c
new file mode 100644
index 0000000..ff1514e
--- /dev/null
+++ b/stand/libsa/bzipfs.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 1998 Michael Smith.
+ * Copyright (c) 2000 Maxim Sobolev
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifndef REGRESSION
+#include "stand.h"
+#else
+#include <stdlib.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/unistd.h>
+
+struct open_file {
+ int f_flags; /* see F_* below */
+ void *f_fsdata; /* file system specific data */
+};
+#define F_READ 0x0001 /* file opened for reading */
+#define EOFFSET (ELAST+8) /* relative seek not supported */
+static inline u_int min(u_int a, u_int b) { return(a < b ? a : b); }
+#define panic(x, y) abort()
+#endif
+
+#include <sys/stat.h>
+#include <string.h>
+#include <bzlib.h>
+
+#define BZ_BUFSIZE 2048 /* XXX larger? */
+
+struct bz_file
+{
+ int bzf_rawfd;
+ bz_stream bzf_bzstream;
+ char bzf_buf[BZ_BUFSIZE];
+ int bzf_endseen;
+};
+
+static int bzf_fill(struct bz_file *z);
+static int bzf_open(const char *path, struct open_file *f);
+static int bzf_close(struct open_file *f);
+static int bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t bzf_seek(struct open_file *f, off_t offset, int where);
+static int bzf_stat(struct open_file *f, struct stat *sb);
+
+#ifndef REGRESSION
+struct fs_ops bzipfs_fsops = {
+ "bzip",
+ bzf_open,
+ bzf_close,
+ bzf_read,
+ null_write,
+ bzf_seek,
+ bzf_stat,
+ null_readdir
+};
+#endif
+
+static int
+bzf_fill(struct bz_file *bzf)
+{
+ int result;
+ int req;
+
+ req = BZ_BUFSIZE - bzf->bzf_bzstream.avail_in;
+ result = 0;
+
+ /* If we need more */
+ if (req > 0) {
+ /* move old data to bottom of buffer */
+ if (req < BZ_BUFSIZE)
+ bcopy(bzf->bzf_buf + req, bzf->bzf_buf, BZ_BUFSIZE - req);
+
+ /* read to fill buffer and update availibility data */
+ result = read(bzf->bzf_rawfd, bzf->bzf_buf + bzf->bzf_bzstream.avail_in, req);
+ bzf->bzf_bzstream.next_in = bzf->bzf_buf;
+ if (result >= 0)
+ bzf->bzf_bzstream.avail_in += result;
+ }
+ return(result);
+}
+
+/*
+ * Adapted from get_byte/check_header in libz
+ *
+ * Returns 0 if the header is OK, nonzero if not.
+ */
+static int
+get_byte(struct bz_file *bzf)
+{
+ if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1))
+ return(-1);
+ bzf->bzf_bzstream.avail_in--;
+ return(*(bzf->bzf_bzstream.next_in)++);
+}
+
+static int bz_magic[3] = {'B', 'Z', 'h'}; /* bzip2 magic header */
+
+static int
+check_header(struct bz_file *bzf)
+{
+ unsigned int len;
+ int c;
+
+ /* Check the bzip2 magic header */
+ for (len = 0; len < 3; len++) {
+ c = get_byte(bzf);
+ if (c != bz_magic[len]) {
+ return(1);
+ }
+ }
+ /* Check that the block size is valid */
+ c = get_byte(bzf);
+ if (c < '1' || c > '9')
+ return(1);
+
+ /* Put back bytes that we've took from the input stream */
+ bzf->bzf_bzstream.next_in -= 4;
+ bzf->bzf_bzstream.avail_in += 4;
+
+ return(0);
+}
+
+static int
+bzf_open(const char *fname, struct open_file *f)
+{
+ static char *bzfname;
+ int rawfd;
+ struct bz_file *bzf;
+ char *cp;
+ int error;
+ struct stat sb;
+
+ /* Have to be in "just read it" mode */
+ if (f->f_flags != F_READ)
+ return(EPERM);
+
+ /* If the name already ends in .gz or .bz2, ignore it */
+ if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
+ || !strcmp(cp, ".bz2") || !strcmp(cp, ".split")))
+ return(ENOENT);
+
+ /* Construct new name */
+ bzfname = malloc(strlen(fname) + 5);
+ if (bzfname == NULL)
+ return(ENOMEM);
+ sprintf(bzfname, "%s.bz2", fname);
+
+ /* Try to open the compressed datafile */
+ rawfd = open(bzfname, O_RDONLY);
+ free(bzfname);
+ if (rawfd == -1)
+ return(ENOENT);
+
+ if (fstat(rawfd, &sb) < 0) {
+ printf("bzf_open: stat failed\n");
+ close(rawfd);
+ return(ENOENT);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ printf("bzf_open: not a file\n");
+ close(rawfd);
+ return(EISDIR); /* best guess */
+ }
+
+ /* Allocate a bz_file structure, populate it */
+ bzf = malloc(sizeof(struct bz_file));
+ if (bzf == NULL)
+ return(ENOMEM);
+ bzero(bzf, sizeof(struct bz_file));
+ bzf->bzf_rawfd = rawfd;
+
+ /* Verify that the file is bzipped */
+ if (check_header(bzf)) {
+ close(bzf->bzf_rawfd);
+ free(bzf);
+ return(EFTYPE);
+ }
+
+ /* Initialise the inflation engine */
+ if ((error = BZ2_bzDecompressInit(&(bzf->bzf_bzstream), 0, 1)) != BZ_OK) {
+ printf("bzf_open: BZ2_bzDecompressInit returned %d\n", error);
+ close(bzf->bzf_rawfd);
+ free(bzf);
+ return(EIO);
+ }
+
+ /* Looks OK, we'll take it */
+ f->f_fsdata = bzf;
+ return(0);
+}
+
+static int
+bzf_close(struct open_file *f)
+{
+ struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
+
+ BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
+ close(bzf->bzf_rawfd);
+ free(bzf);
+ return(0);
+}
+
+static int
+bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
+ int error;
+
+ bzf->bzf_bzstream.next_out = buf; /* where and how much */
+ bzf->bzf_bzstream.avail_out = size;
+
+ while (bzf->bzf_bzstream.avail_out && bzf->bzf_endseen == 0) {
+ if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1)) {
+ printf("bzf_read: fill error\n");
+ return(EIO);
+ }
+ if (bzf->bzf_bzstream.avail_in == 0) { /* oops, unexpected EOF */
+ printf("bzf_read: unexpected EOF\n");
+ if (bzf->bzf_bzstream.avail_out == size)
+ return(EIO);
+ break;
+ }
+
+ error = BZ2_bzDecompress(&bzf->bzf_bzstream); /* decompression pass */
+ if (error == BZ_STREAM_END) { /* EOF, all done */
+ bzf->bzf_endseen = 1;
+ break;
+ }
+ if (error != BZ_OK) { /* argh, decompression error */
+ printf("bzf_read: BZ2_bzDecompress returned %d\n", error);
+ return(EIO);
+ }
+ }
+ if (resid != NULL)
+ *resid = bzf->bzf_bzstream.avail_out;
+ return(0);
+}
+
+static int
+bzf_rewind(struct open_file *f)
+{
+ struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
+ struct bz_file *bzf_tmp;
+
+ /*
+ * Since bzip2 does not have an equivalent inflateReset function a crude
+ * one needs to be provided. The functions all called in such a way that
+ * at any time an error occurs a roll back can be done (effectively making
+ * this rewind 'atomic', either the reset occurs successfully or not at all,
+ * with no 'undefined' state happening).
+ */
+
+ /* Allocate a bz_file structure, populate it */
+ bzf_tmp = malloc(sizeof(struct bz_file));
+ if (bzf_tmp == NULL)
+ return(-1);
+ bzero(bzf_tmp, sizeof(struct bz_file));
+ bzf_tmp->bzf_rawfd = bzf->bzf_rawfd;
+
+ /* Initialise the inflation engine */
+ if (BZ2_bzDecompressInit(&(bzf_tmp->bzf_bzstream), 0, 1) != BZ_OK) {
+ free(bzf_tmp);
+ return(-1);
+ }
+
+ /* Seek back to the beginning of the file */
+ if (lseek(bzf->bzf_rawfd, 0, SEEK_SET) == -1) {
+ BZ2_bzDecompressEnd(&(bzf_tmp->bzf_bzstream));
+ free(bzf_tmp);
+ return(-1);
+ }
+
+ /* Free old bz_file data */
+ BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
+ free(bzf);
+
+ /* Use the new bz_file data */
+ f->f_fsdata = bzf_tmp;
+
+ return(0);
+}
+
+static off_t
+bzf_seek(struct open_file *f, off_t offset, int where)
+{
+ struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
+ off_t target;
+ char discard[16];
+
+ switch (where) {
+ case SEEK_SET:
+ target = offset;
+ break;
+ case SEEK_CUR:
+ target = offset + bzf->bzf_bzstream.total_out_lo32;
+ break;
+ default:
+ errno = EINVAL;
+ return(-1);
+ }
+
+ /* Can we get there from here? */
+ if (target < bzf->bzf_bzstream.total_out_lo32 && bzf_rewind(f) != 0) {
+ errno = EOFFSET;
+ return -1;
+ }
+
+ /* if bzf_rewind was called then bzf has changed */
+ bzf = (struct bz_file *)f->f_fsdata;
+
+ /* skip forwards if required */
+ while (target > bzf->bzf_bzstream.total_out_lo32) {
+ errno = bzf_read(f, discard, min(sizeof(discard),
+ target - bzf->bzf_bzstream.total_out_lo32), NULL);
+ if (errno)
+ return(-1);
+ }
+ /* This is where we are (be honest if we overshot) */
+ return(bzf->bzf_bzstream.total_out_lo32);
+}
+
+static int
+bzf_stat(struct open_file *f, struct stat *sb)
+{
+ struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
+ int result;
+
+ /* stat as normal, but indicate that size is unknown */
+ if ((result = fstat(bzf->bzf_rawfd, sb)) == 0)
+ sb->st_size = -1;
+ return(result);
+}
+
+void
+bz_internal_error(int errorcode)
+{
+ panic("bzipfs: critical error %d in bzip2 library occured\n", errorcode);
+}
+
+#ifdef REGRESSION
+/* Small test case, open and decompress test.bz2 */
+int main()
+{
+ struct open_file f;
+ char buf[1024];
+ size_t resid;
+ int err;
+
+ memset(&f, '\0', sizeof(f));
+ f.f_flags = F_READ;
+ err = bzf_open("test", &f);
+ if (err != 0)
+ exit(1);
+ do {
+ err = bzf_read(&f, buf, sizeof(buf), &resid);
+ } while (err == 0 && resid != sizeof(buf));
+
+ if (err != 0)
+ exit(2);
+ exit(0);
+}
+#endif
diff --git a/stand/libsa/cd9660.c b/stand/libsa/cd9660.c
new file mode 100644
index 0000000..c561883
--- /dev/null
+++ b/stand/libsa/cd9660.c
@@ -0,0 +1,600 @@
+/* $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $ */
+
+/*
+ * Copyright (C) 1996 Wolfgang Solfrank.
+ * Copyright (C) 1996 TooLs GmbH.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Stand-alone ISO9660 file reading package.
+ *
+ * Note: This doesn't support Rock Ridge extensions, extended attributes,
+ * blocksizes other than 2048 bytes, multi-extent files, etc.
+ */
+#include <sys/param.h>
+#include <string.h>
+#include <sys/dirent.h>
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_rrip.h>
+
+#include "stand.h"
+
+#define SUSP_CONTINUATION "CE"
+#define SUSP_PRESENT "SP"
+#define SUSP_STOP "ST"
+#define SUSP_EXTREF "ER"
+#define RRIP_NAME "NM"
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char signature [ISODCL ( 5, 6)];
+ u_char len_skp [ISODCL ( 7, 7)]; /* 711 */
+} ISO_SUSP_PRESENT;
+
+static int buf_read_file(struct open_file *f, char **buf_p,
+ size_t *size_p);
+static int cd9660_open(const char *path, struct open_file *f);
+static int cd9660_close(struct open_file *f);
+static int cd9660_read(struct open_file *f, void *buf, size_t size,
+ size_t *resid);
+static int cd9660_write(struct open_file *f, void *buf, size_t size,
+ size_t *resid);
+static off_t cd9660_seek(struct open_file *f, off_t offset, int where);
+static int cd9660_stat(struct open_file *f, struct stat *sb);
+static int cd9660_readdir(struct open_file *f, struct dirent *d);
+static int dirmatch(struct open_file *f, const char *path,
+ struct iso_directory_record *dp, int use_rrip, int lenskip);
+static int rrip_check(struct open_file *f, struct iso_directory_record *dp,
+ int *lenskip);
+static char *rrip_lookup_name(struct open_file *f,
+ struct iso_directory_record *dp, int lenskip, size_t *len);
+static ISO_SUSP_HEADER *susp_lookup_record(struct open_file *f,
+ const char *identifier, struct iso_directory_record *dp,
+ int lenskip);
+
+struct fs_ops cd9660_fsops = {
+ "cd9660",
+ cd9660_open,
+ cd9660_close,
+ cd9660_read,
+ cd9660_write,
+ cd9660_seek,
+ cd9660_stat,
+ cd9660_readdir
+};
+
+#define F_ISDIR 0x0001 /* Directory */
+#define F_ROOTDIR 0x0002 /* Root directory */
+#define F_RR 0x0004 /* Rock Ridge on this volume */
+
+struct file {
+ int f_flags; /* file flags */
+ off_t f_off; /* Current offset within file */
+ daddr_t f_bno; /* Starting block number */
+ off_t f_size; /* Size of file */
+ daddr_t f_buf_blkno; /* block number of data block */
+ char *f_buf; /* buffer for data block */
+ int f_susp_skip; /* len_skip for SUSP records */
+};
+
+struct ptable_ent {
+ char namlen [ISODCL( 1, 1)]; /* 711 */
+ char extlen [ISODCL( 2, 2)]; /* 711 */
+ char block [ISODCL( 3, 6)]; /* 732 */
+ char parent [ISODCL( 7, 8)]; /* 722 */
+ char name [1];
+};
+#define PTFIXSZ 8
+#define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
+
+#define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
+
+static ISO_SUSP_HEADER *
+susp_lookup_record(struct open_file *f, const char *identifier,
+ struct iso_directory_record *dp, int lenskip)
+{
+ static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE];
+ ISO_SUSP_HEADER *sh;
+ ISO_RRIP_CONT *shc;
+ char *p, *end;
+ int error;
+ size_t read;
+
+ p = dp->name + isonum_711(dp->name_len) + lenskip;
+ /* Names of even length have a padding byte after the name. */
+ if ((isonum_711(dp->name_len) & 1) == 0)
+ p++;
+ end = (char *)dp + isonum_711(dp->length);
+ while (p + 3 < end) {
+ sh = (ISO_SUSP_HEADER *)p;
+ if (bcmp(sh->type, identifier, 2) == 0)
+ return (sh);
+ if (bcmp(sh->type, SUSP_STOP, 2) == 0)
+ return (NULL);
+ if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) {
+ shc = (ISO_RRIP_CONT *)sh;
+ error = f->f_dev->dv_strategy(f->f_devdata, F_READ,
+ cdb2devb(isonum_733(shc->location)),
+ ISO_DEFAULT_BLOCK_SIZE, susp_buffer, &read);
+
+ /* Bail if it fails. */
+ if (error != 0 || read != ISO_DEFAULT_BLOCK_SIZE)
+ return (NULL);
+ p = susp_buffer + isonum_733(shc->offset);
+ end = p + isonum_733(shc->length);
+ } else {
+ /* Ignore this record and skip to the next. */
+ p += isonum_711(sh->length);
+
+ /* Avoid infinite loops with corrupted file systems */
+ if (isonum_711(sh->length) == 0)
+ return (NULL);
+ }
+ }
+ return (NULL);
+}
+
+static char *
+rrip_lookup_name(struct open_file *f, struct iso_directory_record *dp,
+ int lenskip, size_t *len)
+{
+ ISO_RRIP_ALTNAME *p;
+
+ if (len == NULL)
+ return (NULL);
+
+ p = (ISO_RRIP_ALTNAME *)susp_lookup_record(f, RRIP_NAME, dp, lenskip);
+ if (p == NULL)
+ return (NULL);
+ switch (*p->flags) {
+ case ISO_SUSP_CFLAG_CURRENT:
+ *len = 1;
+ return (".");
+ case ISO_SUSP_CFLAG_PARENT:
+ *len = 2;
+ return ("..");
+ case 0:
+ *len = isonum_711(p->h.length) - 5;
+ return ((char *)p + 5);
+ default:
+ /*
+ * We don't handle hostnames or continued names as they are
+ * too hard, so just bail and use the default name.
+ */
+ return (NULL);
+ }
+}
+
+static int
+rrip_check(struct open_file *f, struct iso_directory_record *dp, int *lenskip)
+{
+ ISO_SUSP_PRESENT *sp;
+ ISO_RRIP_EXTREF *er;
+ char *p;
+
+ /* First, see if we can find a SP field. */
+ p = dp->name + isonum_711(dp->name_len);
+ if (p > (char *)dp + isonum_711(dp->length))
+ return (0);
+ sp = (ISO_SUSP_PRESENT *)p;
+ if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0)
+ return (0);
+ if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT))
+ return (0);
+ if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef)
+ return (0);
+ *lenskip = isonum_711(sp->len_skp);
+
+ /*
+ * Now look for an ER field. If RRIP is present, then there must
+ * be at least one of these. It would be more pedantic to walk
+ * through the list of fields looking for a Rock Ridge ER field.
+ */
+ er = (ISO_RRIP_EXTREF *)susp_lookup_record(f, SUSP_EXTREF, dp, 0);
+ if (er == NULL)
+ return (0);
+ return (1);
+}
+
+static int
+dirmatch(struct open_file *f, const char *path, struct iso_directory_record *dp,
+ int use_rrip, int lenskip)
+{
+ size_t len;
+ char *cp;
+ int i, icase;
+
+ if (use_rrip)
+ cp = rrip_lookup_name(f, dp, lenskip, &len);
+ else
+ cp = NULL;
+ if (cp == NULL) {
+ len = isonum_711(dp->name_len);
+ cp = dp->name;
+ icase = 1;
+ } else
+ icase = 0;
+ for (i = len; --i >= 0; path++, cp++) {
+ if (!*path || *path == '/')
+ break;
+ if (*path == *cp)
+ continue;
+ if (!icase && toupper(*path) == *cp)
+ continue;
+ return 0;
+ }
+ if (*path && *path != '/')
+ return 0;
+ /*
+ * Allow stripping of trailing dots and the version number.
+ * Note that this will find the first instead of the last version
+ * of a file.
+ */
+ if (i >= 0 && (*cp == ';' || *cp == '.')) {
+ /* This is to prevent matching of numeric extensions */
+ if (*cp == '.' && cp[1] != ';')
+ return 0;
+ while (--i >= 0)
+ if (*++cp != ';' && (*cp < '0' || *cp > '9'))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+cd9660_open(const char *path, struct open_file *f)
+{
+ struct file *fp = NULL;
+ void *buf;
+ struct iso_primary_descriptor *vd;
+ size_t buf_size, read, dsize, off;
+ daddr_t bno, boff;
+ struct iso_directory_record rec;
+ struct iso_directory_record *dp = NULL;
+ int rc, first, use_rrip, lenskip;
+
+ /* First find the volume descriptor */
+ buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
+ vd = buf;
+ for (bno = 16;; bno++) {
+ twiddle(1);
+ rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
+ ISO_DEFAULT_BLOCK_SIZE, buf, &read);
+ if (rc)
+ goto out;
+ if (read != ISO_DEFAULT_BLOCK_SIZE) {
+ rc = EIO;
+ goto out;
+ }
+ rc = EINVAL;
+ if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
+ goto out;
+ if (isonum_711(vd->type) == ISO_VD_END)
+ goto out;
+ if (isonum_711(vd->type) == ISO_VD_PRIMARY)
+ break;
+ }
+ if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
+ goto out;
+
+ rec = *(struct iso_directory_record *) vd->root_directory_record;
+ if (*path == '/') path++; /* eat leading '/' */
+
+ first = 1;
+ use_rrip = 0;
+ while (*path) {
+ bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
+ dsize = isonum_733(rec.size);
+ off = 0;
+ boff = 0;
+
+ while (off < dsize) {
+ if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
+ twiddle(1);
+ rc = f->f_dev->dv_strategy
+ (f->f_devdata, F_READ,
+ cdb2devb(bno + boff),
+ ISO_DEFAULT_BLOCK_SIZE,
+ buf, &read);
+ if (rc)
+ goto out;
+ if (read != ISO_DEFAULT_BLOCK_SIZE) {
+ rc = EIO;
+ goto out;
+ }
+ boff++;
+ dp = (struct iso_directory_record *) buf;
+ }
+ if (isonum_711(dp->length) == 0) {
+ /* skip to next block, if any */
+ off = boff * ISO_DEFAULT_BLOCK_SIZE;
+ continue;
+ }
+
+ /* See if RRIP is in use. */
+ if (first)
+ use_rrip = rrip_check(f, dp, &lenskip);
+
+ if (dirmatch(f, path, dp, use_rrip,
+ first ? 0 : lenskip)) {
+ first = 0;
+ break;
+ } else
+ first = 0;
+
+ dp = (struct iso_directory_record *)
+ ((char *) dp + isonum_711(dp->length));
+ /* If the new block has zero length, it is padding. */
+ if (isonum_711(dp->length) == 0) {
+ /* Skip to next block, if any. */
+ off = boff * ISO_DEFAULT_BLOCK_SIZE;
+ continue;
+ }
+ off += isonum_711(dp->length);
+ }
+ if (off >= dsize) {
+ rc = ENOENT;
+ goto out;
+ }
+
+ rec = *dp;
+ while (*path && *path != '/') /* look for next component */
+ path++;
+ if (*path) path++; /* skip '/' */
+ }
+
+ /* allocate file system specific data structure */
+ fp = malloc(sizeof(struct file));
+ bzero(fp, sizeof(struct file));
+ f->f_fsdata = (void *)fp;
+
+ if ((isonum_711(rec.flags) & 2) != 0) {
+ fp->f_flags = F_ISDIR;
+ }
+ if (first) {
+ fp->f_flags |= F_ROOTDIR;
+
+ /* Check for Rock Ridge since we didn't in the loop above. */
+ bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
+ twiddle(1);
+ rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
+ ISO_DEFAULT_BLOCK_SIZE, buf, &read);
+ if (rc)
+ goto out;
+ if (read != ISO_DEFAULT_BLOCK_SIZE) {
+ rc = EIO;
+ goto out;
+ }
+ dp = (struct iso_directory_record *)buf;
+ use_rrip = rrip_check(f, dp, &lenskip);
+ }
+ if (use_rrip) {
+ fp->f_flags |= F_RR;
+ fp->f_susp_skip = lenskip;
+ }
+ fp->f_off = 0;
+ fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
+ fp->f_size = isonum_733(rec.size);
+ free(buf);
+
+ return 0;
+
+out:
+ if (fp)
+ free(fp);
+ free(buf);
+
+ return rc;
+}
+
+static int
+cd9660_close(struct open_file *f)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ f->f_fsdata = NULL;
+ free(fp);
+
+ return 0;
+}
+
+static int
+buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ daddr_t blkno, blkoff;
+ int rc = 0;
+ size_t read;
+
+ blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
+ blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
+
+ if (blkno != fp->f_buf_blkno) {
+ if (fp->f_buf == (char *)0)
+ fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
+
+ twiddle(16);
+ rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
+ cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE,
+ fp->f_buf, &read);
+ if (rc)
+ return (rc);
+ if (read != ISO_DEFAULT_BLOCK_SIZE)
+ return (EIO);
+
+ fp->f_buf_blkno = blkno;
+ }
+
+ *buf_p = fp->f_buf + blkoff;
+ *size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
+
+ if (*size_p > fp->f_size - fp->f_off)
+ *size_p = fp->f_size - fp->f_off;
+ return (rc);
+}
+
+static int
+cd9660_read(struct open_file *f, void *start, size_t size, size_t *resid)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ char *buf, *addr;
+ size_t buf_size, csize;
+ int rc = 0;
+
+ addr = start;
+ while (size) {
+ if (fp->f_off < 0 || fp->f_off >= fp->f_size)
+ break;
+
+ rc = buf_read_file(f, &buf, &buf_size);
+ if (rc)
+ break;
+
+ csize = size > buf_size ? buf_size : size;
+ bcopy(buf, addr, csize);
+
+ fp->f_off += csize;
+ addr += csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+ return (rc);
+}
+
+static int
+cd9660_readdir(struct open_file *f, struct dirent *d)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct iso_directory_record *ep;
+ size_t buf_size, reclen, namelen;
+ int error = 0;
+ int lenskip;
+ char *buf, *name;
+
+again:
+ if (fp->f_off >= fp->f_size)
+ return (ENOENT);
+ error = buf_read_file(f, &buf, &buf_size);
+ if (error)
+ return (error);
+ ep = (struct iso_directory_record *)buf;
+
+ if (isonum_711(ep->length) == 0) {
+ daddr_t blkno;
+
+ /* skip to next block, if any */
+ blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
+ fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
+ goto again;
+ }
+
+ if (fp->f_flags & F_RR) {
+ if (fp->f_flags & F_ROOTDIR && fp->f_off == 0)
+ lenskip = 0;
+ else
+ lenskip = fp->f_susp_skip;
+ name = rrip_lookup_name(f, ep, lenskip, &namelen);
+ } else
+ name = NULL;
+ if (name == NULL) {
+ namelen = isonum_711(ep->name_len);
+ name = ep->name;
+ if (namelen == 1) {
+ if (ep->name[0] == 0)
+ name = ".";
+ else if (ep->name[0] == 1) {
+ namelen = 2;
+ name = "..";
+ }
+ }
+ }
+ reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
+ reclen = (reclen + 3) & ~3;
+
+ d->d_fileno = isonum_733(ep->extent);
+ d->d_reclen = reclen;
+ if (isonum_711(ep->flags) & 2)
+ d->d_type = DT_DIR;
+ else
+ d->d_type = DT_REG;
+ d->d_namlen = namelen;
+
+ bcopy(name, d->d_name, d->d_namlen);
+ d->d_name[d->d_namlen] = 0;
+
+ fp->f_off += isonum_711(ep->length);
+ return (0);
+}
+
+static int
+cd9660_write(struct open_file *f __unused, void *start __unused, size_t size __unused, size_t *resid __unused)
+{
+ return EROFS;
+}
+
+static off_t
+cd9660_seek(struct open_file *f, off_t offset, int where)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ switch (where) {
+ case SEEK_SET:
+ fp->f_off = offset;
+ break;
+ case SEEK_CUR:
+ fp->f_off += offset;
+ break;
+ case SEEK_END:
+ fp->f_off = fp->f_size - offset;
+ break;
+ default:
+ return -1;
+ }
+ return fp->f_off;
+}
+
+static int
+cd9660_stat(struct open_file *f, struct stat *sb)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ /* only important stuff */
+ sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
+ if (fp->f_flags & F_ISDIR)
+ sb->st_mode |= S_IFDIR;
+ else
+ sb->st_mode |= S_IFREG;
+ sb->st_uid = sb->st_gid = 0;
+ sb->st_size = fp->f_size;
+ return 0;
+}
diff --git a/stand/libsa/close.c b/stand/libsa/close.c
new file mode 100644
index 0000000..939f025
--- /dev/null
+++ b/stand/libsa/close.c
@@ -0,0 +1,98 @@
+/* $NetBSD: close.c,v 1.7 1997/01/22 00:38:09 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)close.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+int
+close(int fd)
+{
+ struct open_file *f = &files[fd];
+ int err1 = 0, err2 = 0;
+
+ if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (f->f_rabuf != NULL) {
+ free(f->f_rabuf);
+ f->f_rabuf = NULL;
+ }
+ if (!(f->f_flags & F_RAW) && f->f_ops)
+ err1 = (f->f_ops->fo_close)(f);
+ if (!(f->f_flags & F_NODEV) && f->f_dev)
+ err2 = (f->f_dev->dv_close)(f);
+ if (f->f_devdata != NULL)
+ devclose(f);
+ f->f_flags = 0;
+ if (err1) {
+ errno = err1;
+ return (-1);
+ }
+ if (err2) {
+ errno = err2;
+ return (-1);
+ }
+ return (0);
+}
diff --git a/stand/libsa/closeall.c b/stand/libsa/closeall.c
new file mode 100644
index 0000000..a661426
--- /dev/null
+++ b/stand/libsa/closeall.c
@@ -0,0 +1,76 @@
+/* $NetBSD: closeall.c,v 1.1 1996/01/13 22:25:36 leo Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)close.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+void
+closeall()
+{
+ int i;
+
+ for (i = 0; i < SOPEN_MAX; i++)
+ if (files[i].f_flags != 0)
+ (void)close(i);
+}
diff --git a/stand/libsa/crc32.c b/stand/libsa/crc32.c
new file mode 100644
index 0000000..1a3e3a3
--- /dev/null
+++ b/stand/libsa/crc32.c
@@ -0,0 +1,108 @@
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * 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+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include "crc32.h"
+
+static const uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+uint32_t
+crc32(const void *buf, size_t size)
+{
+ const uint8_t *p = buf;
+ uint32_t crc;
+
+ crc = ~0U;
+ while (size--)
+ crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+ return (crc ^ ~0U);
+}
diff --git a/stand/libsa/crc32.h b/stand/libsa/crc32.h
new file mode 100644
index 0000000..adfd628
--- /dev/null
+++ b/stand/libsa/crc32.h
@@ -0,0 +1,13 @@
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CRC32_H_
+#define _CRC32_H_
+
+uint32_t crc32(const void *buf, size_t size);
+
+#endif /* !_CRC32_H_ */
diff --git a/stand/libsa/dev.c b/stand/libsa/dev.c
new file mode 100644
index 0000000..482c082
--- /dev/null
+++ b/stand/libsa/dev.c
@@ -0,0 +1,61 @@
+/* $NetBSD: dev.c,v 1.4 1994/10/30 21:48:23 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dev.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/reboot.h>
+
+#include "stand.h"
+
+int
+nodev()
+{
+ return (ENXIO);
+}
+
+void
+nullsys()
+{
+}
+
+/* ARGSUSED */
+int
+noioctl(f, cmd, data)
+ struct open_file *f;
+ u_long cmd;
+ void *data;
+{
+ return (EINVAL);
+}
diff --git a/stand/libsa/dosfs.c b/stand/libsa/dosfs.c
new file mode 100644
index 0000000..6bbe961
--- /dev/null
+++ b/stand/libsa/dosfs.c
@@ -0,0 +1,868 @@
+/*
+ * Copyright (c) 1996, 1998 Robert Nordier
+ * 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(S) ``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(S) 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems,
+ * also supports VFAT.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <stddef.h>
+
+#include "stand.h"
+
+#include "dosfs.h"
+
+
+static int dos_open(const char *path, struct open_file *fd);
+static int dos_close(struct open_file *fd);
+static int dos_read(struct open_file *fd, void *buf, size_t size, size_t *resid);
+static off_t dos_seek(struct open_file *fd, off_t offset, int whence);
+static int dos_stat(struct open_file *fd, struct stat *sb);
+static int dos_readdir(struct open_file *fd, struct dirent *d);
+
+struct fs_ops dosfs_fsops = {
+ "dosfs",
+ dos_open,
+ dos_close,
+ dos_read,
+ null_write,
+ dos_seek,
+ dos_stat,
+ dos_readdir
+};
+
+#define SECSIZ 512 /* sector size */
+#define SSHIFT 9 /* SECSIZ shift */
+#define DEPSEC 16 /* directory entries per sector */
+#define DSHIFT 4 /* DEPSEC shift */
+#define LOCLUS 2 /* lowest cluster number */
+#define FATBLKSZ 0x20000 /* size of block in the FAT cache buffer */
+
+/* DOS "BIOS Parameter Block" */
+typedef struct {
+ u_char secsiz[2]; /* sector size */
+ u_char spc; /* sectors per cluster */
+ u_char ressec[2]; /* reserved sectors */
+ u_char fats; /* FATs */
+ u_char dirents[2]; /* root directory entries */
+ u_char secs[2]; /* total sectors */
+ u_char media; /* media descriptor */
+ u_char spf[2]; /* sectors per FAT */
+ u_char spt[2]; /* sectors per track */
+ u_char heads[2]; /* drive heads */
+ u_char hidsec[4]; /* hidden sectors */
+ u_char lsecs[4]; /* huge sectors */
+ u_char lspf[4]; /* huge sectors per FAT */
+ u_char xflg[2]; /* flags */
+ u_char vers[2]; /* filesystem version */
+ u_char rdcl[4]; /* root directory start cluster */
+ u_char infs[2]; /* filesystem info sector */
+ u_char bkbs[2]; /* backup boot sector */
+} DOS_BPB;
+
+/* Initial portion of DOS boot sector */
+typedef struct {
+ u_char jmp[3]; /* usually 80x86 'jmp' opcode */
+ u_char oem[8]; /* OEM name and version */
+ DOS_BPB bpb; /* BPB */
+} DOS_BS;
+
+/* Supply missing "." and ".." root directory entries */
+static const char *const dotstr[2] = {".", ".."};
+static DOS_DE dot[2] = {
+ {". ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
+ {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}},
+ {".. ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
+ {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}
+};
+
+/* The usual conversion macros to avoid multiplication and division */
+#define bytsec(n) ((n) >> SSHIFT)
+#define secbyt(s) ((s) << SSHIFT)
+#define entsec(e) ((e) >> DSHIFT)
+#define bytblk(fs, n) ((n) >> (fs)->bshift)
+#define blkbyt(fs, b) ((b) << (fs)->bshift)
+#define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT))
+#define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT))
+
+/* Convert cluster number to offset within filesystem */
+#define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS))
+
+/* Convert cluster number to logical sector number */
+#define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS))
+
+/* Convert cluster number to offset within FAT */
+#define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \
+ (sz) == 16 ? (c) << 1 : \
+ (c) << 2)
+
+/* Does cluster number reference a valid data cluster? */
+#define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus)
+
+/* Get start cluster from directory entry */
+#define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \
+ ((u_int)cv2((de)->dex.h_clus) << 16) | \
+ cv2((de)->clus))
+
+static int parsebs(DOS_FS *, DOS_BS *);
+static int namede(DOS_FS *, const char *, DOS_DE **);
+static int lookup(DOS_FS *, u_int, const char *, DOS_DE **);
+static void cp_xdnm(u_char *, DOS_XDE *);
+static void cp_sfn(u_char *, DOS_DE *);
+static off_t fsize(DOS_FS *, DOS_DE *);
+static int fatcnt(DOS_FS *, u_int);
+static int fatget(DOS_FS *, u_int *);
+static int fatend(u_int, u_int);
+static int ioread(DOS_FS *, u_int, void *, size_t);
+static int ioget(struct open_file *, daddr_t, void *, size_t);
+
+static int
+dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum)
+{
+ int err;
+ size_t io_size;
+ daddr_t offset_in_fat, max_offset_in_fat;
+
+ offset_in_fat = ((daddr_t)blknum) * FATBLKSZ;
+ max_offset_in_fat = secbyt(fs->spf);
+ io_size = FATBLKSZ;
+ if (offset_in_fat > max_offset_in_fat)
+ offset_in_fat = max_offset_in_fat;
+ if (offset_in_fat + io_size > max_offset_in_fat)
+ io_size = ((size_t)(max_offset_in_fat - offset_in_fat));
+
+ if (io_size != 0) {
+ err = ioget(fd, fs->lsnfat + bytsec(offset_in_fat),
+ fs->fatbuf, io_size);
+ if (err != 0) {
+ fs->fatbuf_blknum = ((u_int)(-1));
+ return (err);
+ }
+ }
+ if (io_size < FATBLKSZ)
+ memset(fs->fatbuf + io_size, 0, FATBLKSZ - io_size);
+
+ fs->fatbuf_blknum = blknum;
+ return (0);
+}
+
+/*
+ * Mount DOS filesystem
+ */
+static int
+dos_mount(DOS_FS *fs, struct open_file *fd)
+{
+ int err;
+ u_char *buf;
+
+ bzero(fs, sizeof(DOS_FS));
+ fs->fd = fd;
+
+ if ((buf = malloc(secbyt(1))) == NULL)
+ return (errno);
+ if ((err = ioget(fs->fd, 0, buf, secbyt(1))) ||
+ (err = parsebs(fs, (DOS_BS *)buf))) {
+ free(buf);
+ return (err);
+ }
+ free(buf);
+
+ if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL)
+ return (errno);
+ err = dos_read_fatblk(fs, fd, 0);
+ if (err != 0) {
+ free(fs->fatbuf);
+ return (err);
+ }
+
+ fs->root = dot[0];
+ fs->root.name[0] = ' ';
+ if (fs->fatsz == 32) {
+ fs->root.clus[0] = fs->rdcl & 0xff;
+ fs->root.clus[1] = (fs->rdcl >> 8) & 0xff;
+ fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff;
+ fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff;
+ }
+ return (0);
+}
+
+/*
+ * Unmount mounted filesystem
+ */
+static int
+dos_unmount(DOS_FS *fs)
+{
+ if (fs->links)
+ return (EBUSY);
+ free(fs->fatbuf);
+ free(fs);
+ return (0);
+}
+
+/*
+ * Open DOS file
+ */
+static int
+dos_open(const char *path, struct open_file *fd)
+{
+ DOS_DE *de;
+ DOS_FILE *f;
+ DOS_FS *fs;
+ u_int size, clus;
+ int err;
+
+ /* Allocate mount structure, associate with open */
+ if ((fs = malloc(sizeof(DOS_FS))) == NULL)
+ return (errno);
+ if ((err = dos_mount(fs, fd))) {
+ free(fs);
+ return (err);
+ }
+
+ if ((err = namede(fs, path, &de))) {
+ dos_unmount(fs);
+ return (err);
+ }
+
+ clus = stclus(fs->fatsz, de);
+ size = cv4(de->size);
+
+ if ((!(de->attr & FA_DIR) && (!clus != !size)) ||
+ ((de->attr & FA_DIR) && size) ||
+ (clus && !okclus(fs, clus))) {
+ dos_unmount(fs);
+ return (EINVAL);
+ }
+ if ((f = malloc(sizeof(DOS_FILE))) == NULL) {
+ err = errno;
+ dos_unmount(fs);
+ return (err);
+ }
+ bzero(f, sizeof(DOS_FILE));
+ f->fs = fs;
+ fs->links++;
+ f->de = *de;
+ fd->f_fsdata = (void *)f;
+ return (0);
+}
+
+/*
+ * Read from file
+ */
+static int
+dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
+{
+ off_t size;
+ u_int nb, off, clus, c, cnt, n;
+ DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
+ int err = 0;
+
+ /*
+ * as ioget() can be called *a lot*, use twiddle here.
+ * also 4 seems to be good value not to slow loading down too much:
+ * with 270MB file (~540k ioget() calls, twiddle can easily waste 4-5sec.
+ */
+ twiddle(4);
+ nb = (u_int)nbyte;
+ if ((size = fsize(f->fs, &f->de)) == -1)
+ return (EINVAL);
+ if (nb > (n = size - f->offset))
+ nb = n;
+ off = f->offset;
+ if ((clus = stclus(f->fs->fatsz, &f->de)))
+ off &= f->fs->bsize - 1;
+ c = f->c;
+ cnt = nb;
+ while (cnt) {
+ n = 0;
+ if (!c) {
+ if ((c = clus))
+ n = bytblk(f->fs, f->offset);
+ } else if (!off)
+ n++;
+ while (n--) {
+ if ((err = fatget(f->fs, &c)))
+ goto out;
+ if (!okclus(f->fs, c)) {
+ err = EINVAL;
+ goto out;
+ }
+ }
+ if (!clus || (n = f->fs->bsize - off) > cnt)
+ n = cnt;
+ if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) :
+ secbyt(f->fs->lsndir)) + off, buf, n)))
+ goto out;
+ f->offset += n;
+ f->c = c;
+ off = 0;
+ buf = (char *)buf + n;
+ cnt -= n;
+ }
+ out:
+ if (resid)
+ *resid = nbyte - nb + cnt;
+ return (err);
+}
+
+/*
+ * Reposition within file
+ */
+static off_t
+dos_seek(struct open_file *fd, off_t offset, int whence)
+{
+ off_t off;
+ u_int size;
+ DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
+
+ size = cv4(f->de.size);
+ switch (whence) {
+ case SEEK_SET:
+ off = 0;
+ break;
+ case SEEK_CUR:
+ off = f->offset;
+ break;
+ case SEEK_END:
+ off = size;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ off += offset;
+ if (off < 0 || off > size) {
+ errno = EINVAL;
+ return (-1);
+ }
+ f->offset = (u_int)off;
+ f->c = 0;
+ return (off);
+}
+
+/*
+ * Close open file
+ */
+static int
+dos_close(struct open_file *fd)
+{
+ DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
+ DOS_FS *fs = f->fs;
+
+ f->fs->links--;
+ free(f);
+ dos_unmount(fs);
+ return (0);
+}
+
+/*
+ * Return some stat information on a file.
+ */
+static int
+dos_stat(struct open_file *fd, struct stat *sb)
+{
+ DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
+
+ /* only important stuff */
+ sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444;
+ sb->st_nlink = 1;
+ sb->st_uid = 0;
+ sb->st_gid = 0;
+ if ((sb->st_size = fsize(f->fs, &f->de)) == -1)
+ return (EINVAL);
+ return (0);
+}
+
+static int
+dos_checksum(char *name, char *ext)
+{
+ int x, i;
+ char buf[11];
+
+ bcopy(name, buf, 8);
+ bcopy(ext, buf+8, 3);
+ x = 0;
+ for (i = 0; i < 11; i++) {
+ x = ((x & 1) << 7) | (x >> 1);
+ x += buf[i];
+ x &= 0xff;
+ }
+ return (x);
+}
+
+static int
+dos_readdir(struct open_file *fd, struct dirent *d)
+{
+ /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */
+ u_char fn[261];
+ DOS_DIR dd;
+ size_t res;
+ u_int chk, i, x, xdn;
+ int err;
+
+ x = chk = 0;
+ while (1) {
+ xdn = x;
+ x = 0;
+ err = dos_read(fd, &dd, sizeof(dd), &res);
+ if (err)
+ return (err);
+ if (res == sizeof(dd))
+ return (ENOENT);
+ if (dd.de.name[0] == 0)
+ return (ENOENT);
+
+ /* Skip deleted entries */
+ if (dd.de.name[0] == 0xe5)
+ continue;
+
+ /* Check if directory entry is volume label */
+ if (dd.de.attr & FA_LABEL) {
+ /*
+ * If volume label set, check if the current entry is
+ * extended entry (FA_XDE) for long file names.
+ */
+ if ((dd.de.attr & FA_MASK) == FA_XDE) {
+ /*
+ * Read through all following extended entries
+ * to get the long file name. 0x40 marks the
+ * last entry containing part of long file name.
+ */
+ if (dd.xde.seq & 0x40)
+ chk = dd.xde.chk;
+ else if (dd.xde.seq != xdn - 1 || dd.xde.chk != chk)
+ continue;
+ x = dd.xde.seq & ~0x40;
+ if (x < 1 || x > 20) {
+ x = 0;
+ continue;
+ }
+ cp_xdnm(fn, &dd.xde);
+ } else {
+ /* skip only volume label entries */
+ continue;
+ }
+ } else {
+ if (xdn == 1) {
+ x = dos_checksum(dd.de.name, dd.de.ext);
+ if (x == chk)
+ break;
+ } else {
+ cp_sfn(fn, &dd.de);
+ break;
+ }
+ x = 0;
+ }
+ }
+
+ d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0];
+ d->d_reclen = sizeof(*d);
+ d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG;
+ memcpy(d->d_name, fn, sizeof(d->d_name));
+ return (0);
+}
+
+/*
+ * Parse DOS boot sector
+ */
+static int
+parsebs(DOS_FS *fs, DOS_BS *bs)
+{
+ u_int sc;
+
+ if ((bs->jmp[0] != 0x69 &&
+ bs->jmp[0] != 0xe9 &&
+ (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) ||
+ bs->bpb.media < 0xf0)
+ return (EINVAL);
+ if (cv2(bs->bpb.secsiz) != SECSIZ)
+ return (EINVAL);
+ if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1))
+ return (EINVAL);
+ fs->bsize = secbyt(fs->spc);
+ fs->bshift = ffs(fs->bsize) - 1;
+ if ((fs->spf = cv2(bs->bpb.spf))) {
+ if (bs->bpb.fats != 2)
+ return (EINVAL);
+ if (!(fs->dirents = cv2(bs->bpb.dirents)))
+ return (EINVAL);
+ } else {
+ if (!(fs->spf = cv4(bs->bpb.lspf)))
+ return (EINVAL);
+ if (!bs->bpb.fats || bs->bpb.fats > 16)
+ return (EINVAL);
+ if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS)
+ return (EINVAL);
+ }
+ if (!(fs->lsnfat = cv2(bs->bpb.ressec)))
+ return (EINVAL);
+ fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats;
+ fs->lsndta = fs->lsndir + entsec(fs->dirents);
+ if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs)))
+ return (EINVAL);
+ if (fs->lsndta > sc)
+ return (EINVAL);
+ if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS)
+ return (EINVAL);
+ fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32;
+ sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1;
+ if (fs->xclus > sc)
+ fs->xclus = sc;
+ return (0);
+}
+
+/*
+ * Return directory entry from path
+ */
+static int
+namede(DOS_FS *fs, const char *path, DOS_DE **dep)
+{
+ char name[256];
+ DOS_DE *de;
+ char *s;
+ size_t n;
+ int err;
+
+ err = 0;
+ de = &fs->root;
+ while (*path) {
+ while (*path == '/')
+ path++;
+ if (*path == '\0')
+ break;
+ if (!(s = strchr(path, '/')))
+ s = strchr(path, 0);
+ if ((n = s - path) > 255)
+ return (ENAMETOOLONG);
+ memcpy(name, path, n);
+ name[n] = 0;
+ path = s;
+ if (!(de->attr & FA_DIR))
+ return (ENOTDIR);
+ if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de)))
+ return (err);
+ }
+ *dep = de;
+ return (0);
+}
+
+/*
+ * Lookup path segment
+ */
+static int
+lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep)
+{
+ static DOS_DIR dir[DEPSEC];
+ u_char lfn[261];
+ u_char sfn[13];
+ u_int nsec, lsec, xdn, chk, sec, ent, x;
+ int err, ok, i;
+
+ if (!clus)
+ for (ent = 0; ent < 2; ent++)
+ if (!strcasecmp(name, dotstr[ent])) {
+ *dep = dot + ent;
+ return (0);
+ }
+ if (!clus && fs->fatsz == 32)
+ clus = fs->rdcl;
+ nsec = !clus ? entsec(fs->dirents) : fs->spc;
+ lsec = 0;
+ xdn = chk = 0;
+ for (;;) {
+ if (!clus && !lsec)
+ lsec = fs->lsndir;
+ else if (okclus(fs, clus))
+ lsec = blklsn(fs, clus);
+ else
+ return (EINVAL);
+ for (sec = 0; sec < nsec; sec++) {
+ if ((err = ioget(fs->fd, lsec + sec, dir, secbyt(1))))
+ return (err);
+ for (ent = 0; ent < DEPSEC; ent++) {
+ if (!*dir[ent].de.name)
+ return (ENOENT);
+ if (*dir[ent].de.name != 0xe5) {
+ if ((dir[ent].de.attr & FA_MASK) == FA_XDE) {
+ x = dir[ent].xde.seq;
+ if (x & 0x40 || (x + 1 == xdn &&
+ dir[ent].xde.chk == chk)) {
+ if (x & 0x40) {
+ chk = dir[ent].xde.chk;
+ x &= ~0x40;
+ }
+ if (x >= 1 && x <= 20) {
+ cp_xdnm(lfn, &dir[ent].xde);
+ xdn = x;
+ continue;
+ }
+ }
+ } else if (!(dir[ent].de.attr & FA_LABEL)) {
+ if ((ok = xdn == 1)) {
+ x = dos_checksum(dir[ent].de.name, dir[ent].de.ext);
+ ok = chk == x &&
+ !strcasecmp(name, (const char *)lfn);
+ }
+ if (!ok) {
+ cp_sfn(sfn, &dir[ent].de);
+ ok = !strcasecmp(name, (const char *)sfn);
+ }
+ if (ok) {
+ *dep = &dir[ent].de;
+ return (0);
+ }
+ }
+ }
+ xdn = 0;
+ }
+ }
+ if (!clus)
+ break;
+ if ((err = fatget(fs, &clus)))
+ return (err);
+ if (fatend(fs->fatsz, clus))
+ break;
+ }
+ return (ENOENT);
+}
+
+/*
+ * Copy name from extended directory entry
+ */
+static void
+cp_xdnm(u_char *lfn, DOS_XDE *xde)
+{
+ static struct {
+ u_int off;
+ u_int dim;
+ } ix[3] = {
+ {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2},
+ {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2},
+ {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2}
+ };
+ u_char *p;
+ u_int n, x, c;
+
+ lfn += 13 * ((xde->seq & ~0x40) - 1);
+ for (n = 0; n < 3; n++)
+ for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x;
+ p += 2, x--) {
+ if ((c = cv2(p)) && (c < 32 || c > 127))
+ c = '?';
+ if (!(*lfn++ = c))
+ return;
+ }
+ if (xde->seq & 0x40)
+ *lfn = 0;
+}
+
+/*
+ * Copy short filename
+ */
+static void
+cp_sfn(u_char *sfn, DOS_DE *de)
+{
+ u_char *p;
+ int j, i;
+
+ p = sfn;
+ if (*de->name != ' ') {
+ for (j = 7; de->name[j] == ' '; j--);
+ for (i = 0; i <= j; i++)
+ *p++ = de->name[i];
+ if (*de->ext != ' ') {
+ *p++ = '.';
+ for (j = 2; de->ext[j] == ' '; j--);
+ for (i = 0; i <= j; i++)
+ *p++ = de->ext[i];
+ }
+ }
+ *p = 0;
+ if (*sfn == 5)
+ *sfn = 0xe5;
+}
+
+/*
+ * Return size of file in bytes
+ */
+static off_t
+fsize(DOS_FS *fs, DOS_DE *de)
+{
+ u_long size;
+ u_int c;
+ int n;
+
+ if (!(size = cv4(de->size)) && de->attr & FA_DIR) {
+ if (!(c = cv2(de->clus)))
+ size = fs->dirents * sizeof(DOS_DE);
+ else {
+ if ((n = fatcnt(fs, c)) == -1)
+ return (n);
+ size = blkbyt(fs, n);
+ }
+ }
+ return (size);
+}
+
+/*
+ * Count number of clusters in chain
+ */
+static int
+fatcnt(DOS_FS *fs, u_int c)
+{
+ int n;
+
+ for (n = 0; okclus(fs, c); n++)
+ if (fatget(fs, &c))
+ return (-1);
+ return (fatend(fs->fatsz, c) ? n : -1);
+}
+
+/*
+ * Get next cluster in cluster chain. Use in core fat cache unless
+ * the number of current 128K block in FAT has changed.
+ */
+static int
+fatget(DOS_FS *fs, u_int *c)
+{
+ u_int val_in, val_out, offset, blknum, nbyte;
+ const u_char *p_entry;
+ int err;
+
+ /* check input value to prevent overflow in fatoff() */
+ val_in = *c;
+ if (val_in & 0xf0000000)
+ return (EINVAL);
+
+ /* ensure that current 128K FAT block is cached */
+ offset = fatoff(fs->fatsz, val_in);
+ nbyte = fs->fatsz != 32 ? 2 : 4;
+ if (offset + nbyte > secbyt(fs->spf))
+ return (EINVAL);
+ blknum = offset / FATBLKSZ;
+ offset %= FATBLKSZ;
+ if (offset + nbyte > FATBLKSZ)
+ return (EINVAL);
+ if (blknum != fs->fatbuf_blknum) {
+ err = dos_read_fatblk(fs, fs->fd, blknum);
+ if (err != 0)
+ return (err);
+ }
+ p_entry = fs->fatbuf + offset;
+
+ /* extract cluster number from FAT entry */
+ switch (fs->fatsz) {
+ case 32:
+ val_out = cv4(p_entry);
+ val_out &= 0x0fffffff;
+ break;
+ case 16:
+ val_out = cv2(p_entry);
+ break;
+ case 12:
+ val_out = cv2(p_entry);
+ if (val_in & 1)
+ val_out >>= 4;
+ else
+ val_out &= 0xfff;
+ break;
+ default:
+ return (EINVAL);
+ }
+ *c = val_out;
+ return (0);
+}
+
+/*
+ * Is cluster an end-of-chain marker?
+ */
+static int
+fatend(u_int sz, u_int c)
+{
+ return (c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7));
+}
+
+/*
+ * Offset-based I/O primitive
+ */
+static int
+ioread(DOS_FS *fs, u_int offset, void *buf, size_t nbyte)
+{
+ char *s;
+ u_int off, n;
+ int err;
+ u_char local_buf[SECSIZ];
+
+ s = buf;
+ if ((off = offset & (SECSIZ - 1))) {
+ offset -= off;
+ if ((n = SECSIZ - off) > nbyte)
+ n = nbyte;
+ if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf))))
+ return (err);
+ memcpy(s, local_buf + off, n);
+ offset += SECSIZ;
+ s += n;
+ nbyte -= n;
+ }
+ n = nbyte & (SECSIZ - 1);
+ if (nbyte -= n) {
+ if ((err = ioget(fs->fd, bytsec(offset), s, nbyte)))
+ return (err);
+ offset += nbyte;
+ s += nbyte;
+ }
+ if (n) {
+ if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf))))
+ return (err);
+ memcpy(s, local_buf, n);
+ }
+ return (0);
+}
+
+/*
+ * Sector-based I/O primitive
+ */
+static int
+ioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size)
+{
+ size_t rsize;
+ int rv;
+
+ /* Make sure we get full read or error. */
+ rsize = 0;
+ rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec,
+ size, buf, &rsize);
+ if ((rv == 0) && (size != rsize))
+ rv = EIO;
+ return (rv);
+}
diff --git a/stand/libsa/dosfs.h b/stand/libsa/dosfs.h
new file mode 100644
index 0000000..5b11e0f
--- /dev/null
+++ b/stand/libsa/dosfs.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1996, 1998 Robert Nordier
+ * 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(S) ``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(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef DOSIO_H
+#define DOSIO_H
+
+/*
+ * DOS file attributes
+ */
+
+#define FA_RDONLY 001 /* read-only */
+#define FA_HIDDEN 002 /* hidden file */
+#define FA_SYSTEM 004 /* system file */
+#define FA_LABEL 010 /* volume label */
+#define FA_DIR 020 /* directory */
+#define FA_ARCH 040 /* archive (file modified) */
+#define FA_XDE 017 /* extended directory entry */
+#define FA_MASK 077 /* all attributes */
+
+/*
+ * Macros to convert DOS-format 16-bit and 32-bit quantities
+ */
+
+#define cv2(p) ((u_int16_t)(p)[0] | \
+ ((u_int16_t)(p)[1] << 010))
+#define cv4(p) ((u_int32_t)(p)[0] | \
+ ((u_int32_t)(p)[1] << 010) | \
+ ((u_int32_t)(p)[2] << 020) | \
+ ((u_int32_t)(p)[3] << 030))
+
+/*
+ * Directory, filesystem, and file structures.
+ */
+
+typedef struct {
+ u_char x_case; /* case */
+ u_char c_hsec; /* created: secs/100 */
+ u_char c_time[2]; /* created: time */
+ u_char c_date[2]; /* created: date */
+ u_char a_date[2]; /* accessed: date */
+ u_char h_clus[2]; /* clus[hi] */
+} DOS_DEX;
+
+typedef struct {
+ u_char name[8]; /* name */
+ u_char ext[3]; /* extension */
+ u_char attr; /* attributes */
+ DOS_DEX dex; /* VFAT/FAT32 only */
+ u_char time[2]; /* modified: time */
+ u_char date[2]; /* modified: date */
+ u_char clus[2]; /* starting cluster */
+ u_char size[4]; /* size */
+} DOS_DE;
+
+typedef struct {
+ u_char seq; /* flags */
+ u_char name1[5][2]; /* 1st name area */
+ u_char attr; /* (see fat_de) */
+ u_char res; /* reserved */
+ u_char chk; /* checksum */
+ u_char name2[6][2]; /* 2nd name area */
+ u_char clus[2]; /* (see fat_de) */
+ u_char name3[2][2]; /* 3rd name area */
+} DOS_XDE;
+
+typedef union {
+ DOS_DE de; /* standard directory entry */
+ DOS_XDE xde; /* extended directory entry */
+} DOS_DIR;
+
+typedef struct {
+ struct open_file *fd; /* file descriptor */
+ u_char *fatbuf; /* FAT cache buffer */
+ u_int fatbuf_blknum; /* number of 128K block in FAT cache buffer */
+ u_int links; /* active links to structure */
+ u_int spc; /* sectors per cluster */
+ u_int bsize; /* cluster size in bytes */
+ u_int bshift; /* cluster conversion shift */
+ u_int dirents; /* root directory entries */
+ u_int spf; /* sectors per fat */
+ u_int rdcl; /* root directory start cluster */
+ u_int lsnfat; /* start of fat */
+ u_int lsndir; /* start of root dir */
+ u_int lsndta; /* start of data area */
+ u_int fatsz; /* FAT entry size */
+ u_int xclus; /* maximum cluster number */
+ DOS_DE root;
+} DOS_FS;
+
+typedef struct {
+ DOS_FS *fs; /* associated filesystem */
+ DOS_DE de; /* directory entry */
+ u_int offset; /* current offset */
+ u_int c; /* last cluster read */
+} DOS_FILE;
+
+#endif /* !DOSIO_H */
diff --git a/stand/libsa/environment.c b/stand/libsa/environment.c
new file mode 100644
index 0000000..83349f1
--- /dev/null
+++ b/stand/libsa/environment.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 1998 Michael Smith.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Manage an environment-like space in which string variables may be stored.
+ * Provide support for some method-like operations for setting/retrieving
+ * variables in order to allow some type strength.
+ */
+
+#include "stand.h"
+
+#include <string.h>
+
+static void env_discard(struct env_var *ev);
+
+struct env_var *environ = NULL;
+
+/*
+ * Look up (name) and return it's env_var structure.
+ */
+struct env_var *
+env_getenv(const char *name)
+{
+ struct env_var *ev;
+
+ for (ev = environ; ev != NULL; ev = ev->ev_next)
+ if (!strcmp(ev->ev_name, name))
+ break;
+ return(ev);
+}
+
+/*
+ * Some notes:
+ *
+ * If the EV_VOLATILE flag is set, a copy of the variable is made.
+ * If EV_DYNAMIC is set, the variable has been allocated with
+ * malloc and ownership transferred to the environment.
+ * If (value) is NULL, the variable is set but has no value.
+ */
+int
+env_setenv(const char *name, int flags, const void *value,
+ ev_sethook_t sethook, ev_unsethook_t unsethook)
+{
+ struct env_var *ev, *curr, *last;
+
+ if ((ev = env_getenv(name)) != NULL) {
+ /*
+ * If there's a set hook, let it do the work (unless we are working
+ * for one already.
+ */
+ if ((ev->ev_sethook != NULL) && !(flags & EV_NOHOOK))
+ return (ev->ev_sethook(ev, flags, value));
+
+ /* If there is data in the variable, discard it. */
+ if (ev->ev_value != NULL && (ev->ev_flags & EV_DYNAMIC) != 0)
+ free(ev->ev_value);
+ ev->ev_value = NULL;
+ ev->ev_flags &= ~EV_DYNAMIC;
+
+ } else {
+
+ /*
+ * New variable; create and sort into list
+ */
+ ev = malloc(sizeof(struct env_var));
+ ev->ev_name = strdup(name);
+ ev->ev_value = NULL;
+ ev->ev_flags = 0;
+ /* hooks can only be set when the variable is instantiated */
+ ev->ev_sethook = sethook;
+ ev->ev_unsethook = unsethook;
+
+ /* Sort into list */
+ ev->ev_prev = NULL;
+ ev->ev_next = NULL;
+ /* Search for the record to insert before */
+ for (last = NULL, curr = environ;
+ curr != NULL;
+ last = curr, curr = curr->ev_next) {
+
+ if (strcmp(ev->ev_name, curr->ev_name) < 0) {
+ if (curr->ev_prev) {
+ curr->ev_prev->ev_next = ev;
+ } else {
+ environ = ev;
+ }
+ ev->ev_next = curr;
+ ev->ev_prev = curr->ev_prev;
+ curr->ev_prev = ev;
+ break;
+ }
+ }
+ if (curr == NULL) {
+ if (last == NULL) {
+ environ = ev;
+ } else {
+ last->ev_next = ev;
+ ev->ev_prev = last;
+ }
+ }
+ }
+
+ /* If we have a new value, use it */
+ if (flags & EV_VOLATILE) {
+ ev->ev_value = strdup(value);
+ ev->ev_flags |= EV_DYNAMIC;
+ } else {
+ ev->ev_value = (char *)value;
+ ev->ev_flags |= flags & EV_DYNAMIC;
+ }
+
+ return(0);
+}
+
+char *
+getenv(const char *name)
+{
+ struct env_var *ev;
+
+ /* Set but no value gives empty string */
+ if ((ev = env_getenv(name)) != NULL) {
+ if (ev->ev_value != NULL)
+ return(ev->ev_value);
+ return("");
+ }
+ return(NULL);
+}
+
+int
+setenv(const char *name, const char *value, int overwrite)
+{
+ /* No guarantees about state, always assume volatile */
+ if (overwrite || (env_getenv(name) == NULL))
+ return(env_setenv(name, EV_VOLATILE, value, NULL, NULL));
+ return(0);
+}
+
+int
+putenv(char *string)
+{
+ char *value, *copy;
+ int result;
+
+ copy = strdup(string);
+ if ((value = strchr(copy, '=')) != NULL)
+ *(value++) = 0;
+ result = setenv(copy, value, 1);
+ free(copy);
+ return(result);
+}
+
+int
+unsetenv(const char *name)
+{
+ struct env_var *ev;
+ int err;
+
+ err = 0;
+ if ((ev = env_getenv(name)) == NULL) {
+ err = ENOENT;
+ } else {
+ if (ev->ev_unsethook != NULL)
+ err = ev->ev_unsethook(ev);
+ if (err == 0) {
+ env_discard(ev);
+ }
+ }
+ return(err);
+}
+
+static void
+env_discard(struct env_var *ev)
+{
+ if (ev->ev_prev)
+ ev->ev_prev->ev_next = ev->ev_next;
+ if (ev->ev_next)
+ ev->ev_next->ev_prev = ev->ev_prev;
+ if (environ == ev)
+ environ = ev->ev_next;
+ free(ev->ev_name);
+ if (ev->ev_value != NULL && (ev->ev_flags & EV_DYNAMIC) != 0)
+ free(ev->ev_value);
+ free(ev);
+}
+
+int
+env_noset(struct env_var *ev __unused, int flags __unused,
+ const void *value __unused)
+{
+ return(EPERM);
+}
+
+int
+env_nounset(struct env_var *ev __unused)
+{
+ return(EPERM);
+}
diff --git a/stand/libsa/ether.c b/stand/libsa/ether.c
new file mode 100644
index 0000000..7ab9d74
--- /dev/null
+++ b/stand/libsa/ether.c
@@ -0,0 +1,147 @@
+/* $NetBSD: ether.c,v 1.11 1997/07/07 15:52:50 drochner Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: net.c,v 1.9 93/08/06 19:32:15 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <string.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+
+/* Caller must leave room for ethernet header in front!! */
+ssize_t
+sendether(struct iodesc *d, void *pkt, size_t len, uint8_t *dea, int etype)
+{
+ ssize_t n;
+ struct ether_header *eh;
+
+#ifdef ETHER_DEBUG
+ if (debug)
+ printf("sendether: called\n");
+#endif
+
+ eh = (struct ether_header *)pkt - 1;
+ len += sizeof(*eh);
+
+ MACPY(d->myea, eh->ether_shost); /* by byte */
+ MACPY(dea, eh->ether_dhost); /* by byte */
+ eh->ether_type = htons(etype);
+
+ n = netif_put(d, eh, len);
+ if (n == -1 || n < sizeof(*eh))
+ return (-1);
+
+ n -= sizeof(*eh);
+ return (n);
+}
+
+/*
+ * Get a packet of any Ethernet type, with our address or
+ * the broadcast address. Save the Ether type in etype.
+ * Unless there is an error, we pass the whole packet and the unencapsulated
+ * data.
+ */
+ssize_t
+readether(struct iodesc *d, void **pkt, void **payload, time_t tleft,
+ uint16_t *etype)
+{
+ ssize_t n;
+ struct ether_header *eh;
+ void *ptr;
+
+#ifdef ETHER_DEBUG
+ if (debug)
+ printf("readether: called\n");
+#endif
+
+ ptr = NULL;
+ n = netif_get(d, &ptr, tleft);
+ if (n == -1 || n < sizeof(*eh)) {
+ free(ptr);
+ return (-1);
+ }
+
+ eh = (struct ether_header *)((uintptr_t)ptr + ETHER_ALIGN);
+ /* Validate Ethernet address. */
+ if (bcmp(d->myea, eh->ether_dhost, 6) != 0 &&
+ bcmp(bcea, eh->ether_dhost, 6) != 0) {
+#ifdef ETHER_DEBUG
+ if (debug)
+ printf("readether: not ours (ea=%s)\n",
+ ether_sprintf(eh->ether_dhost));
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ *pkt = ptr;
+ *payload = (void *)((uintptr_t)eh + sizeof(*eh));
+ *etype = ntohs(eh->ether_type);
+
+ n -= sizeof(*eh);
+ return (n);
+}
+
+/*
+ * Convert Ethernet address to printable (loggable) representation.
+ */
+static char digits[] = "0123456789abcdef";
+char *
+ether_sprintf(u_char *ap)
+{
+ int i;
+ static char etherbuf[18];
+ char *cp = etherbuf;
+
+ for (i = 0; i < 6; i++) {
+ *cp++ = digits[*ap >> 4];
+ *cp++ = digits[*ap++ & 0xf];
+ *cp++ = ':';
+ }
+ *--cp = 0;
+ return (etherbuf);
+}
diff --git a/stand/libsa/ext2fs.c b/stand/libsa/ext2fs.c
new file mode 100644
index 0000000..d0b91e0
--- /dev/null
+++ b/stand/libsa/ext2fs.c
@@ -0,0 +1,908 @@
+/*-
+ * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright (c) 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: David Golub
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include "stand.h"
+#include "string.h"
+
+static int ext2fs_open(const char *path, struct open_file *f);
+static int ext2fs_close(struct open_file *f);
+static int ext2fs_read(struct open_file *f, void *buf,
+ size_t size, size_t *resid);
+static off_t ext2fs_seek(struct open_file *f, off_t offset, int where);
+static int ext2fs_stat(struct open_file *f, struct stat *sb);
+static int ext2fs_readdir(struct open_file *f, struct dirent *d);
+
+static int dtmap[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR,
+ DT_BLK, DT_FIFO, DT_SOCK, DT_LNK };
+#define EXTFTODT(x) (x) > sizeof(dtmap) / sizeof(dtmap[0]) ? \
+ DT_UNKNOWN : dtmap[x]
+
+struct fs_ops ext2fs_fsops = {
+ "ext2fs",
+ ext2fs_open,
+ ext2fs_close,
+ ext2fs_read,
+ null_write,
+ ext2fs_seek,
+ ext2fs_stat,
+ ext2fs_readdir
+};
+
+#define EXT2_SBSIZE 1024
+#define EXT2_SBLOCK (1024 / DEV_BSIZE) /* block offset of superblock */
+#define EXT2_MAGIC 0xef53
+#define EXT2_ROOTINO 2
+
+#define EXT2_REV0 0 /* original revision of ext2 */
+#define EXT2_R0_ISIZE 128 /* inode size */
+#define EXT2_R0_FIRSTINO 11 /* first inode */
+
+#define EXT2_MINBSHIFT 10 /* mininum block shift */
+#define EXT2_MINFSHIFT 10 /* mininum frag shift */
+
+#define NDADDR 12 /* # of direct blocks */
+#define NIADDR 3 /* # of indirect blocks */
+
+/*
+ * file system block to disk address
+ */
+#define fsb_to_db(fs, blk) ((blk) << (fs)->fs_fsbtodb)
+
+/*
+ * inode to block group offset
+ * inode to block group
+ * inode to disk address
+ * inode to block offset
+ */
+#define ino_to_bgo(fs, ino) (((ino) - 1) % (fs)->fs_ipg)
+#define ino_to_bg(fs, ino) (((ino) - 1) / (fs)->fs_ipg)
+#define ino_to_db(fs, bg, ino) \
+ fsb_to_db(fs, ((bg)[ino_to_bg(fs, ino)].bg_inotbl + \
+ ino_to_bgo(fs, ino) / (fs)->fs_ipb))
+#define ino_to_bo(fs, ino) (ino_to_bgo(fs, ino) % (fs)->fs_ipb)
+
+#define nindir(fs) \
+ ((fs)->fs_bsize / sizeof(u_int32_t))
+#define lblkno(fs, loc) /* loc / bsize */ \
+ ((loc) >> (fs)->fs_bshift)
+#define smalllblktosize(fs, blk) /* blk * bsize */ \
+ ((blk) << (fs)->fs_bshift)
+#define blkoff(fs, loc) /* loc % bsize */ \
+ ((loc) & (fs)->fs_bmask)
+#define fragroundup(fs, size) /* roundup(size, fsize) */ \
+ (((size) + (fs)->fs_fmask) & ~(fs)->fs_fmask)
+#define dblksize(fs, dip, lbn) \
+ (((lbn) >= NDADDR || (dip)->di_size >= smalllblktosize(fs, (lbn) + 1)) \
+ ? (fs)->fs_bsize \
+ : (fragroundup(fs, blkoff(fs, (dip)->di_size))))
+
+/*
+ * superblock describing ext2fs
+ */
+struct ext2fs_disk {
+ u_int32_t fd_inodes; /* # of inodes */
+ u_int32_t fd_blocks; /* # of blocks */
+ u_int32_t fd_resblk; /* # of reserved blocks */
+ u_int32_t fd_freeblk; /* # of free blocks */
+ u_int32_t fd_freeino; /* # of free inodes */
+ u_int32_t fd_firstblk; /* first data block */
+ u_int32_t fd_bsize; /* block size */
+ u_int32_t fd_fsize; /* frag size */
+ u_int32_t fd_bpg; /* blocks per group */
+ u_int32_t fd_fpg; /* frags per group */
+ u_int32_t fd_ipg; /* inodes per group */
+ u_int32_t fd_mtime; /* mount time */
+ u_int32_t fd_wtime; /* write time */
+ u_int16_t fd_mount; /* # of mounts */
+ int16_t fd_maxmount; /* max # of mounts */
+ u_int16_t fd_magic; /* magic number */
+ u_int16_t fd_state; /* state */
+ u_int16_t fd_eflag; /* error flags */
+ u_int16_t fd_mnrrev; /* minor revision */
+ u_int32_t fd_lastchk; /* last check */
+ u_int32_t fd_chkintvl; /* maximum check interval */
+ u_int32_t fd_os; /* os */
+ u_int32_t fd_revision; /* revision */
+ u_int16_t fd_uid; /* uid for reserved blocks */
+ u_int16_t fd_gid; /* gid for reserved blocks */
+
+ u_int32_t fd_firstino; /* first non-reserved inode */
+ u_int16_t fd_isize; /* inode size */
+ u_int16_t fd_nblkgrp; /* block group # of superblock */
+ u_int32_t fd_fcompat; /* compatible features */
+ u_int32_t fd_fincompat; /* incompatible features */
+ u_int32_t fd_frocompat; /* read-only compatibilties */
+ u_int8_t fd_uuid[16]; /* volume uuid */
+ char fd_volname[16]; /* volume name */
+ char fd_fsmnt[64]; /* name last mounted on */
+ u_int32_t fd_bitmap; /* compression bitmap */
+
+ u_int8_t fd_nblkpa; /* # of blocks to preallocate */
+ u_int8_t fd_ndblkpa; /* # of dir blocks to preallocate */
+};
+
+struct ext2fs_core {
+ int fc_bsize; /* block size */
+ int fc_bshift; /* block shift amount */
+ int fc_bmask; /* block mask */
+ int fc_fsize; /* frag size */
+ int fc_fshift; /* frag shift amount */
+ int fc_fmask; /* frag mask */
+ int fc_isize; /* inode size */
+ int fc_imask; /* inode mask */
+ int fc_firstino; /* first non-reserved inode */
+ int fc_ipb; /* inodes per block */
+ int fc_fsbtodb; /* fsb to ds shift */
+};
+
+struct ext2fs {
+ struct ext2fs_disk fs_fd;
+ char fs_pad[EXT2_SBSIZE - sizeof(struct ext2fs_disk)];
+ struct ext2fs_core fs_fc;
+
+#define fs_magic fs_fd.fd_magic
+#define fs_revision fs_fd.fd_revision
+#define fs_blocks fs_fd.fd_blocks
+#define fs_firstblk fs_fd.fd_firstblk
+#define fs_bpg fs_fd.fd_bpg
+#define fs_ipg fs_fd.fd_ipg
+
+#define fs_bsize fs_fc.fc_bsize
+#define fs_bshift fs_fc.fc_bshift
+#define fs_bmask fs_fc.fc_bmask
+#define fs_fsize fs_fc.fc_fsize
+#define fs_fshift fs_fc.fc_fshift
+#define fs_fmask fs_fc.fc_fmask
+#define fs_isize fs_fc.fc_isize
+#define fs_imask fs_fc.fc_imask
+#define fs_firstino fs_fc.fc_firstino
+#define fs_ipb fs_fc.fc_ipb
+#define fs_fsbtodb fs_fc.fc_fsbtodb
+};
+
+struct ext2blkgrp {
+ u_int32_t bg_blkmap; /* block bitmap */
+ u_int32_t bg_inomap; /* inode bitmap */
+ u_int32_t bg_inotbl; /* inode table */
+ u_int16_t bg_nfblk; /* # of free blocks */
+ u_int16_t bg_nfino; /* # of free inodes */
+ u_int16_t bg_ndirs; /* # of dirs */
+ char bg_pad[14];
+};
+
+struct ext2dinode {
+ u_int16_t di_mode; /* mode */
+ u_int16_t di_uid; /* uid */
+ u_int32_t di_size; /* byte size */
+ u_int32_t di_atime; /* access time */
+ u_int32_t di_ctime; /* creation time */
+ u_int32_t di_mtime; /* modification time */
+ u_int32_t di_dtime; /* deletion time */
+ u_int16_t di_gid; /* gid */
+ u_int16_t di_nlink; /* link count */
+ u_int32_t di_nblk; /* block count */
+ u_int32_t di_flags; /* file flags */
+
+ u_int32_t di_osdep1; /* os dependent stuff */
+
+ u_int32_t di_db[NDADDR]; /* direct blocks */
+ u_int32_t di_ib[NIADDR]; /* indirect blocks */
+ u_int32_t di_version; /* version */
+ u_int32_t di_facl; /* file acl */
+ u_int32_t di_dacl; /* dir acl */
+ u_int32_t di_faddr; /* fragment addr */
+
+ u_int8_t di_frag; /* fragment number */
+ u_int8_t di_fsize; /* fragment size */
+
+ char di_pad[10];
+
+#define di_shortlink di_db
+};
+
+#define EXT2_MAXNAMLEN 255
+
+struct ext2dirent {
+ u_int32_t d_ino; /* inode */
+ u_int16_t d_reclen; /* directory entry length */
+ u_int8_t d_namlen; /* name length */
+ u_int8_t d_type; /* file type */
+ char d_name[EXT2_MAXNAMLEN];
+};
+
+struct file {
+ off_t f_seekp; /* seek pointer */
+ struct ext2fs *f_fs; /* pointer to super-block */
+ struct ext2blkgrp *f_bg; /* pointer to blkgrp map */
+ struct ext2dinode f_di; /* copy of on-disk inode */
+ int f_nindir[NIADDR]; /* number of blocks mapped by
+ indirect block at level i */
+ char *f_blk[NIADDR]; /* buffer for indirect block
+ at level i */
+ size_t f_blksize[NIADDR]; /* size of buffer */
+ daddr_t f_blkno[NIADDR]; /* disk address of block in
+ buffer */
+ char *f_buf; /* buffer for data block */
+ size_t f_buf_size; /* size of data block */
+ daddr_t f_buf_blkno; /* block number of data block */
+};
+
+/* forward decls */
+static int read_inode(ino_t inumber, struct open_file *f);
+static int block_map(struct open_file *f, daddr_t file_block,
+ daddr_t *disk_block_p);
+static int buf_read_file(struct open_file *f, char **buf_p,
+ size_t *size_p);
+static int search_directory(char *name, struct open_file *f,
+ ino_t *inumber_p);
+
+/*
+ * Open a file.
+ */
+static int
+ext2fs_open(const char *upath, struct open_file *f)
+{
+ struct file *fp;
+ struct ext2fs *fs;
+ size_t buf_size;
+ ino_t inumber, parent_inumber;
+ int i, len, groups, bg_per_blk, blkgrps, mult;
+ int nlinks = 0;
+ int error = 0;
+ char *cp, *ncp, *path = NULL, *buf = NULL;
+ char namebuf[MAXPATHLEN+1];
+ char c;
+
+ /* allocate file system specific data structure */
+ fp = malloc(sizeof(struct file));
+ if (fp == NULL)
+ return (ENOMEM);
+ bzero(fp, sizeof(struct file));
+ f->f_fsdata = (void *)fp;
+
+ /* allocate space and read super block */
+ fs = (struct ext2fs *)malloc(sizeof(*fs));
+ fp->f_fs = fs;
+ twiddle(1);
+ error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ EXT2_SBLOCK, EXT2_SBSIZE, (char *)fs, &buf_size);
+ if (error)
+ goto out;
+
+ if (buf_size != EXT2_SBSIZE || fs->fs_magic != EXT2_MAGIC) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * compute in-core values for the superblock
+ */
+ fs->fs_bshift = EXT2_MINBSHIFT + fs->fs_fd.fd_bsize;
+ fs->fs_bsize = 1 << fs->fs_bshift;
+ fs->fs_bmask = fs->fs_bsize - 1;
+
+ fs->fs_fshift = EXT2_MINFSHIFT + fs->fs_fd.fd_fsize;
+ fs->fs_fsize = 1 << fs->fs_fshift;
+ fs->fs_fmask = fs->fs_fsize - 1;
+
+ if (fs->fs_revision == EXT2_REV0) {
+ fs->fs_isize = EXT2_R0_ISIZE;
+ fs->fs_firstino = EXT2_R0_FIRSTINO;
+ } else {
+ fs->fs_isize = fs->fs_fd.fd_isize;
+ fs->fs_firstino = fs->fs_fd.fd_firstino;
+ }
+ fs->fs_imask = fs->fs_isize - 1;
+ fs->fs_ipb = fs->fs_bsize / fs->fs_isize;
+ fs->fs_fsbtodb = (fs->fs_bsize / DEV_BSIZE) - 1;
+
+ /*
+ * we have to load in the "group descriptors" here
+ */
+ groups = howmany(fs->fs_blocks - fs->fs_firstblk, fs->fs_bpg);
+ bg_per_blk = fs->fs_bsize / sizeof(struct ext2blkgrp);
+ blkgrps = howmany(groups, bg_per_blk);
+ len = blkgrps * fs->fs_bsize;
+
+ fp->f_bg = malloc(len);
+ twiddle(1);
+ error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ EXT2_SBLOCK + EXT2_SBSIZE / DEV_BSIZE, len,
+ (char *)fp->f_bg, &buf_size);
+ if (error)
+ goto out;
+
+ /*
+ * XXX
+ * validation of values? (blocksize, descriptors, etc?)
+ */
+
+ /*
+ * Calculate indirect block levels.
+ */
+ mult = 1;
+ for (i = 0; i < NIADDR; i++) {
+ mult *= nindir(fs);
+ fp->f_nindir[i] = mult;
+ }
+
+ inumber = EXT2_ROOTINO;
+ if ((error = read_inode(inumber, f)) != 0)
+ goto out;
+
+ path = strdup(upath);
+ if (path == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ cp = path;
+ while (*cp) {
+ /*
+ * Remove extra separators
+ */
+ while (*cp == '/')
+ cp++;
+ if (*cp == '\0')
+ break;
+
+ /*
+ * Check that current node is a directory.
+ */
+ if (! S_ISDIR(fp->f_di.di_mode)) {
+ error = ENOTDIR;
+ goto out;
+ }
+
+ /*
+ * Get next component of path name.
+ */
+ len = 0;
+
+ ncp = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ if (++len > EXT2_MAXNAMLEN) {
+ error = ENOENT;
+ goto out;
+ }
+ cp++;
+ }
+ *cp = '\0';
+
+ /*
+ * Look up component in current directory.
+ * Save directory inumber in case we find a
+ * symbolic link.
+ */
+ parent_inumber = inumber;
+ error = search_directory(ncp, f, &inumber);
+ *cp = c;
+ if (error)
+ goto out;
+
+ /*
+ * Open next component.
+ */
+ if ((error = read_inode(inumber, f)) != 0)
+ goto out;
+
+ /*
+ * Check for symbolic link.
+ */
+ if (S_ISLNK(fp->f_di.di_mode)) {
+ int link_len = fp->f_di.di_size;
+ int len;
+
+ len = strlen(cp);
+ if (link_len + len > MAXPATHLEN ||
+ ++nlinks > MAXSYMLINKS) {
+ error = ENOENT;
+ goto out;
+ }
+
+ bcopy(cp, &namebuf[link_len], len + 1);
+ if (fp->f_di.di_nblk == 0) {
+ bcopy(fp->f_di.di_shortlink,
+ namebuf, link_len);
+ } else {
+ /*
+ * Read file for symbolic link
+ */
+ struct ext2fs *fs = fp->f_fs;
+ daddr_t disk_block;
+ size_t buf_size;
+
+ if (! buf)
+ buf = malloc(fs->fs_bsize);
+ error = block_map(f, (daddr_t)0, &disk_block);
+ if (error)
+ goto out;
+
+ twiddle(1);
+ error = (f->f_dev->dv_strategy)(f->f_devdata,
+ F_READ, fsb_to_db(fs, disk_block),
+ fs->fs_bsize, buf, &buf_size);
+ if (error)
+ goto out;
+
+ bcopy((char *)buf, namebuf, link_len);
+ }
+
+ /*
+ * If relative pathname, restart at parent directory.
+ * If absolute pathname, restart at root.
+ */
+ cp = namebuf;
+ if (*cp != '/')
+ inumber = parent_inumber;
+ else
+ inumber = (ino_t)EXT2_ROOTINO;
+
+ if ((error = read_inode(inumber, f)) != 0)
+ goto out;
+ }
+ }
+
+ /*
+ * Found terminal component.
+ */
+ error = 0;
+ fp->f_seekp = 0;
+out:
+ if (buf)
+ free(buf);
+ if (path)
+ free(path);
+ if (error) {
+ if (fp->f_buf)
+ free(fp->f_buf);
+ free(fp->f_fs);
+ free(fp);
+ }
+ return (error);
+}
+
+/*
+ * Read a new inode into a file structure.
+ */
+static int
+read_inode(ino_t inumber, struct open_file *f)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct ext2fs *fs = fp->f_fs;
+ struct ext2dinode *dp;
+ char *buf;
+ size_t rsize;
+ int level, error = 0;
+
+ /*
+ * Read inode and save it.
+ */
+ buf = malloc(fs->fs_bsize);
+ twiddle(1);
+ error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ ino_to_db(fs, fp->f_bg, inumber), fs->fs_bsize, buf, &rsize);
+ if (error)
+ goto out;
+ if (rsize != fs->fs_bsize) {
+ error = EIO;
+ goto out;
+ }
+
+ dp = (struct ext2dinode *)buf;
+ fp->f_di = dp[ino_to_bo(fs, inumber)];
+
+ /* clear out old buffers */
+ for (level = 0; level < NIADDR; level++)
+ fp->f_blkno[level] = -1;
+ fp->f_buf_blkno = -1;
+ fp->f_seekp = 0;
+
+out:
+ free(buf);
+ return (error);
+}
+
+/*
+ * Given an offset in a file, find the disk block number that
+ * contains that block.
+ */
+static int
+block_map(struct open_file *f, daddr_t file_block, daddr_t *disk_block_p)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct ext2fs *fs = fp->f_fs;
+ daddr_t ind_block_num;
+ int32_t *ind_p;
+ int idx, level;
+ int error;
+
+ /*
+ * Index structure of an inode:
+ *
+ * di_db[0..NDADDR-1] hold block numbers for blocks
+ * 0..NDADDR-1
+ *
+ * di_ib[0] index block 0 is the single indirect block
+ * holds block numbers for blocks
+ * NDADDR .. NDADDR + NINDIR(fs)-1
+ *
+ * di_ib[1] index block 1 is the double indirect block
+ * holds block numbers for INDEX blocks for blocks
+ * NDADDR + NINDIR(fs) ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
+ *
+ * di_ib[2] index block 2 is the triple indirect block
+ * holds block numbers for double-indirect
+ * blocks for blocks
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2
+ * + NINDIR(fs)**3 - 1
+ */
+
+ if (file_block < NDADDR) {
+ /* Direct block. */
+ *disk_block_p = fp->f_di.di_db[file_block];
+ return (0);
+ }
+
+ file_block -= NDADDR;
+
+ /*
+ * nindir[0] = NINDIR
+ * nindir[1] = NINDIR**2
+ * nindir[2] = NINDIR**3
+ * etc
+ */
+ for (level = 0; level < NIADDR; level++) {
+ if (file_block < fp->f_nindir[level])
+ break;
+ file_block -= fp->f_nindir[level];
+ }
+ if (level == NIADDR) {
+ /* Block number too high */
+ return (EFBIG);
+ }
+
+ ind_block_num = fp->f_di.di_ib[level];
+
+ for (; level >= 0; level--) {
+ if (ind_block_num == 0) {
+ *disk_block_p = 0; /* missing */
+ return (0);
+ }
+
+ if (fp->f_blkno[level] != ind_block_num) {
+ if (fp->f_blk[level] == (char *)0)
+ fp->f_blk[level] =
+ malloc(fs->fs_bsize);
+ twiddle(1);
+ error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsb_to_db(fp->f_fs, ind_block_num), fs->fs_bsize,
+ fp->f_blk[level], &fp->f_blksize[level]);
+ if (error)
+ return (error);
+ if (fp->f_blksize[level] != fs->fs_bsize)
+ return (EIO);
+ fp->f_blkno[level] = ind_block_num;
+ }
+
+ ind_p = (int32_t *)fp->f_blk[level];
+
+ if (level > 0) {
+ idx = file_block / fp->f_nindir[level - 1];
+ file_block %= fp->f_nindir[level - 1];
+ } else {
+ idx = file_block;
+ }
+ ind_block_num = ind_p[idx];
+ }
+
+ *disk_block_p = ind_block_num;
+
+ return (0);
+}
+
+/*
+ * Read a portion of a file into an internal buffer. Return
+ * the location in the buffer and the amount in the buffer.
+ */
+static int
+buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct ext2fs *fs = fp->f_fs;
+ long off;
+ daddr_t file_block;
+ daddr_t disk_block;
+ size_t block_size;
+ int error = 0;
+
+ off = blkoff(fs, fp->f_seekp);
+ file_block = lblkno(fs, fp->f_seekp);
+ block_size = dblksize(fs, &fp->f_di, file_block);
+
+ if (file_block != fp->f_buf_blkno) {
+ error = block_map(f, file_block, &disk_block);
+ if (error)
+ goto done;
+
+ if (fp->f_buf == (char *)0)
+ fp->f_buf = malloc(fs->fs_bsize);
+
+ if (disk_block == 0) {
+ bzero(fp->f_buf, block_size);
+ fp->f_buf_size = block_size;
+ } else {
+ twiddle(4);
+ error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsb_to_db(fs, disk_block), block_size,
+ fp->f_buf, &fp->f_buf_size);
+ if (error)
+ goto done;
+ }
+ fp->f_buf_blkno = file_block;
+ }
+
+ /*
+ * Return address of byte in buffer corresponding to
+ * offset, and size of remainder of buffer after that
+ * byte.
+ */
+ *buf_p = fp->f_buf + off;
+ *size_p = block_size - off;
+
+ /*
+ * But truncate buffer at end of file.
+ */
+ if (*size_p > fp->f_di.di_size - fp->f_seekp)
+ *size_p = fp->f_di.di_size - fp->f_seekp;
+done:
+ return (error);
+}
+
+/*
+ * Search a directory for a name and return its
+ * i_number.
+ */
+static int
+search_directory(char *name, struct open_file *f, ino_t *inumber_p)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct ext2dirent *dp, *edp;
+ char *buf;
+ size_t buf_size;
+ int namlen, length;
+ int error;
+
+ length = strlen(name);
+ fp->f_seekp = 0;
+ while (fp->f_seekp < fp->f_di.di_size) {
+ error = buf_read_file(f, &buf, &buf_size);
+ if (error)
+ return (error);
+ dp = (struct ext2dirent *)buf;
+ edp = (struct ext2dirent *)(buf + buf_size);
+ while (dp < edp) {
+ if (dp->d_ino == (ino_t)0)
+ goto next;
+ namlen = dp->d_namlen;
+ if (namlen == length &&
+ strncmp(name, dp->d_name, length) == 0) {
+ /* found entry */
+ *inumber_p = dp->d_ino;
+ return (0);
+ }
+ next:
+ dp = (struct ext2dirent *)((char *)dp + dp->d_reclen);
+ }
+ fp->f_seekp += buf_size;
+ }
+ return (ENOENT);
+}
+
+static int
+ext2fs_close(struct open_file *f)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ int level;
+
+ f->f_fsdata = (void *)0;
+ if (fp == (struct file *)0)
+ return (0);
+
+ for (level = 0; level < NIADDR; level++) {
+ if (fp->f_blk[level])
+ free(fp->f_blk[level]);
+ }
+ if (fp->f_buf)
+ free(fp->f_buf);
+ if (fp->f_bg)
+ free(fp->f_bg);
+ free(fp->f_fs);
+ free(fp);
+ return (0);
+}
+
+static int
+ext2fs_read(struct open_file *f, void *addr, size_t size, size_t *resid)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ size_t csize, buf_size;
+ char *buf;
+ int error = 0;
+
+ while (size != 0) {
+ if (fp->f_seekp >= fp->f_di.di_size)
+ break;
+
+ error = buf_read_file(f, &buf, &buf_size);
+ if (error)
+ break;
+
+ csize = size;
+ if (csize > buf_size)
+ csize = buf_size;
+
+ bcopy(buf, addr, csize);
+
+ fp->f_seekp += csize;
+ addr = (char *)addr + csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+ return (error);
+}
+
+static off_t
+ext2fs_seek(struct open_file *f, off_t offset, int where)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ switch (where) {
+ case SEEK_SET:
+ fp->f_seekp = offset;
+ break;
+ case SEEK_CUR:
+ fp->f_seekp += offset;
+ break;
+ case SEEK_END:
+ fp->f_seekp = fp->f_di.di_size - offset;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ return (fp->f_seekp);
+}
+
+static int
+ext2fs_stat(struct open_file *f, struct stat *sb)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ /* only important stuff */
+ sb->st_mode = fp->f_di.di_mode;
+ sb->st_uid = fp->f_di.di_uid;
+ sb->st_gid = fp->f_di.di_gid;
+ sb->st_size = fp->f_di.di_size;
+ return (0);
+}
+
+static int
+ext2fs_readdir(struct open_file *f, struct dirent *d)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct ext2dirent *ed;
+ char *buf;
+ size_t buf_size;
+ int error;
+
+ /*
+ * assume that a directory entry will not be split across blocks
+ */
+again:
+ if (fp->f_seekp >= fp->f_di.di_size)
+ return (ENOENT);
+ error = buf_read_file(f, &buf, &buf_size);
+ if (error)
+ return (error);
+ ed = (struct ext2dirent *)buf;
+ fp->f_seekp += ed->d_reclen;
+ if (ed->d_ino == (ino_t)0)
+ goto again;
+ d->d_type = EXTFTODT(ed->d_type);
+ strncpy(d->d_name, ed->d_name, ed->d_namlen);
+ d->d_name[ed->d_namlen] = '\0';
+ return (0);
+}
diff --git a/stand/libsa/fstat.c b/stand/libsa/fstat.c
new file mode 100644
index 0000000..13bed08
--- /dev/null
+++ b/stand/libsa/fstat.c
@@ -0,0 +1,61 @@
+/* $NetBSD: fstat.c,v 1.1 1996/01/13 22:25:38 leo Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)stat.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+int
+fstat(fd, sb)
+ int fd;
+ struct stat *sb;
+{
+ struct open_file *f = &files[fd];
+
+ if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) {
+ errno = EBADF;
+ return (-1);
+ }
+
+ /* operation not defined on raw devices */
+ if (f->f_flags & F_RAW) {
+ errno = EOPNOTSUPP;
+ return (-1);
+ }
+
+ errno = (f->f_ops->fo_stat)(f, sb);
+ if (errno)
+ return (-1);
+ return (0);
+}
diff --git a/stand/libsa/getopt.c b/stand/libsa/getopt.c
new file mode 100644
index 0000000..cfe405a
--- /dev/null
+++ b/stand/libsa/getopt.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
+#endif /* LIBC_SCCS and not lint */
+
+#include "stand.h"
+#include <string.h>
+
+int opterr = 1, /* if error message should be printed */
+ optind = 1, /* index into parent argv vector */
+ optopt, /* character checked for validity */
+ optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt(int nargc, char * const *nargv, const char *ostr)
+{
+ static char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ ++optind;
+ place = EMSG;
+ return (-1);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (optopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)printf("illegal option -- %c\n", optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ }
+ else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if (*ostr == ':')
+ return (BADARG);
+ if (opterr)
+ (void)printf("option requires an argument -- %c\n", optopt);
+ return (BADCH);
+ }
+ else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
diff --git a/stand/libsa/gets.c b/stand/libsa/gets.c
new file mode 100644
index 0000000..7d54b00
--- /dev/null
+++ b/stand/libsa/gets.c
@@ -0,0 +1,112 @@
+/* $NetBSD: gets.c,v 1.6 1995/10/11 21:16:57 pk Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)gets.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+/* gets() with constrained input length */
+
+void
+ngets(char *buf, int n)
+{
+ int c;
+ char *lp;
+
+ for (lp = buf;;)
+ switch (c = getchar() & 0177) {
+ case '\n':
+ case '\r':
+ *lp = '\0';
+ putchar('\n');
+ return;
+ case '\b':
+ case '\177':
+ if (lp > buf) {
+ lp--;
+ putchar('\b');
+ putchar(' ');
+ putchar('\b');
+ }
+ break;
+ case 'r'&037: {
+ char *p;
+
+ putchar('\n');
+ for (p = buf; p < lp; ++p)
+ putchar(*p);
+ break;
+ }
+ case 'u'&037:
+ case 'w'&037:
+ lp = buf;
+ putchar('\n');
+ break;
+ default:
+ if ((n < 1) || ((lp - buf) < n - 1)) {
+ *lp++ = c;
+ putchar(c);
+ }
+ }
+ /*NOTREACHED*/
+}
+
+int
+fgetstr(char *buf, int size, int fd)
+{
+ char c;
+ int err, len;
+
+ size--; /* leave space for terminator */
+ len = 0;
+ while (size != 0) {
+ err = read(fd, &c, sizeof(c));
+ if (err < 0) /* read error */
+ return(-1);
+ if (err == 0) { /* EOF */
+ if (len == 0)
+ return(-1); /* nothing to read */
+ break;
+ }
+ if ((c == '\r') || /* line terminators */
+ (c == '\n'))
+ break;
+ *buf++ = c; /* keep char */
+ size--;
+ len++;
+ }
+ *buf = 0;
+ return(len);
+}
+
diff --git a/stand/libsa/globals.c b/stand/libsa/globals.c
new file mode 100644
index 0000000..8fb56cf
--- /dev/null
+++ b/stand/libsa/globals.c
@@ -0,0 +1,38 @@
+/* $NetBSD: globals.c,v 1.3 1995/09/18 21:19:27 pk Exp $ */
+
+/*
+ * globals.c:
+ *
+ * global variables should be separate, so nothing else
+ * must be included extraneously.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include "stand.h"
+#include "net.h"
+
+u_char bcea[6] = BA; /* broadcast ethernet address */
+
+char rootpath[FNAME_SIZE] = "/"; /* root mount path */
+char bootfile[FNAME_SIZE]; /* bootp says to boot this */
+char hostname[FNAME_SIZE]; /* our hostname */
+int hostnamelen;
+char domainname[FNAME_SIZE]; /* our DNS domain */
+int domainnamelen;
+int netproto = NET_NONE; /* Network prototol */
+char ifname[IFNAME_SIZE]; /* name of interface (e.g. "le0") */
+struct in_addr myip; /* my ip address */
+struct in_addr nameip; /* DNS server ip address */
+struct in_addr rootip; /* root ip address */
+struct in_addr swapip; /* swap ip address */
+struct in_addr gateip; /* gateway ip address */
+n_long netmask = 0xffffff00; /* subnet or net mask */
+u_int intf_mtu; /* interface mtu from bootp/dhcp */
+int errno; /* our old friend */
+
diff --git a/stand/libsa/gpt.c b/stand/libsa/gpt.c
new file mode 100644
index 0000000..7ab3fc6
--- /dev/null
+++ b/stand/libsa/gpt.c
@@ -0,0 +1,379 @@
+/*-
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * 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 AUTHORS 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 AUTHORS 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/gpt.h>
+
+#ifndef LITTLE_ENDIAN
+#error gpt.c works only for little endian architectures
+#endif
+
+#include "crc32.h"
+#include "drv.h"
+#include "util.h"
+#include "gpt.h"
+
+static struct gpt_hdr hdr_primary, hdr_backup, *gpthdr;
+static uint64_t hdr_primary_lba, hdr_backup_lba;
+static struct gpt_ent table_primary[MAXTBLENTS], table_backup[MAXTBLENTS];
+static struct gpt_ent *gpttable;
+static int curent, bootonce;
+
+/*
+ * Buffer below 64kB passed on gptread(), which can hold at least
+ * one sector of data (512 bytes).
+ */
+static char *secbuf;
+
+static void
+gptupdate(const char *which, struct dsk *dskp, struct gpt_hdr *hdr,
+ struct gpt_ent *table)
+{
+ int entries_per_sec, firstent;
+ daddr_t slba;
+
+ /*
+ * We need to update the following for both primary and backup GPT:
+ * 1. Sector on disk that contains current partition.
+ * 2. Partition table checksum.
+ * 3. Header checksum.
+ * 4. Header on disk.
+ */
+
+ entries_per_sec = DEV_BSIZE / hdr->hdr_entsz;
+ slba = curent / entries_per_sec;
+ firstent = slba * entries_per_sec;
+ bcopy(&table[firstent], secbuf, DEV_BSIZE);
+ slba += hdr->hdr_lba_table;
+ if (drvwrite(dskp, secbuf, slba, 1)) {
+ printf("%s: unable to update %s GPT partition table\n",
+ BOOTPROG, which);
+ return;
+ }
+ hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz);
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size);
+ bzero(secbuf, DEV_BSIZE);
+ bcopy(hdr, secbuf, hdr->hdr_size);
+ if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) {
+ printf("%s: unable to update %s GPT header\n", BOOTPROG, which);
+ return;
+ }
+}
+
+int
+gptfind(const uuid_t *uuid, struct dsk *dskp, int part)
+{
+ struct gpt_ent *ent;
+ int firsttry;
+
+ if (part >= 0) {
+ if (part == 0 || part > gpthdr->hdr_entries) {
+ printf("%s: invalid partition index\n", BOOTPROG);
+ return (-1);
+ }
+ ent = &gpttable[part - 1];
+ if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) {
+ printf("%s: specified partition is not UFS\n",
+ BOOTPROG);
+ return (-1);
+ }
+ curent = part - 1;
+ goto found;
+ }
+
+ firsttry = (curent == -1);
+ curent++;
+ if (curent >= gpthdr->hdr_entries) {
+ curent = gpthdr->hdr_entries;
+ return (-1);
+ }
+ if (bootonce) {
+ /*
+ * First look for partition with both GPT_ENT_ATTR_BOOTME and
+ * GPT_ENT_ATTR_BOOTONCE flags.
+ */
+ for (; curent < gpthdr->hdr_entries; curent++) {
+ ent = &gpttable[curent];
+ if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0)
+ continue;
+ if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME))
+ continue;
+ if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTONCE))
+ continue;
+ /* Ok, found one. */
+ goto found;
+ }
+ bootonce = 0;
+ curent = 0;
+ }
+ for (; curent < gpthdr->hdr_entries; curent++) {
+ ent = &gpttable[curent];
+ if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0)
+ continue;
+ if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME))
+ continue;
+ if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE)
+ continue;
+ /* Ok, found one. */
+ goto found;
+ }
+ if (firsttry) {
+ /*
+ * No partition with BOOTME flag was found, try to boot from
+ * first UFS partition.
+ */
+ for (curent = 0; curent < gpthdr->hdr_entries; curent++) {
+ ent = &gpttable[curent];
+ if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0)
+ continue;
+ /* Ok, found one. */
+ goto found;
+ }
+ }
+ return (-1);
+found:
+ dskp->part = curent + 1;
+ ent = &gpttable[curent];
+ dskp->start = ent->ent_lba_start;
+ if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) {
+ /*
+ * Clear BOOTME, but leave BOOTONCE set before trying to
+ * boot from this partition.
+ */
+ if (hdr_primary_lba > 0) {
+ table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME;
+ gptupdate("primary", dskp, &hdr_primary, table_primary);
+ }
+ if (hdr_backup_lba > 0) {
+ table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME;
+ gptupdate("backup", dskp, &hdr_backup, table_backup);
+ }
+ }
+ return (0);
+}
+
+static int
+gptread_hdr(const char *which, struct dsk *dskp, struct gpt_hdr *hdr,
+ uint64_t hdrlba)
+{
+ uint32_t crc;
+
+ if (drvread(dskp, secbuf, hdrlba, 1)) {
+ printf("%s: unable to read %s GPT header\n", BOOTPROG, which);
+ return (-1);
+ }
+ bcopy(secbuf, hdr, sizeof(*hdr));
+ if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 ||
+ hdr->hdr_lba_self != hdrlba || hdr->hdr_revision < 0x00010000 ||
+ hdr->hdr_entsz < sizeof(struct gpt_ent) ||
+ hdr->hdr_entries > MAXTBLENTS || DEV_BSIZE % hdr->hdr_entsz != 0) {
+ printf("%s: invalid %s GPT header\n", BOOTPROG, which);
+ return (-1);
+ }
+ crc = hdr->hdr_crc_self;
+ hdr->hdr_crc_self = 0;
+ if (crc32(hdr, hdr->hdr_size) != crc) {
+ printf("%s: %s GPT header checksum mismatch\n", BOOTPROG,
+ which);
+ return (-1);
+ }
+ hdr->hdr_crc_self = crc;
+ return (0);
+}
+
+void
+gptbootfailed(struct dsk *dskp)
+{
+
+ if (!(gpttable[curent].ent_attr & GPT_ENT_ATTR_BOOTONCE))
+ return;
+
+ if (hdr_primary_lba > 0) {
+ table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE;
+ table_primary[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED;
+ gptupdate("primary", dskp, &hdr_primary, table_primary);
+ }
+ if (hdr_backup_lba > 0) {
+ table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE;
+ table_backup[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED;
+ gptupdate("backup", dskp, &hdr_backup, table_backup);
+ }
+}
+
+static void
+gptbootconv(const char *which, struct dsk *dskp, struct gpt_hdr *hdr,
+ struct gpt_ent *table)
+{
+ struct gpt_ent *ent;
+ daddr_t slba;
+ int table_updated, sector_updated;
+ int entries_per_sec, nent, part;
+
+ table_updated = 0;
+ entries_per_sec = DEV_BSIZE / hdr->hdr_entsz;
+ for (nent = 0, slba = hdr->hdr_lba_table;
+ slba < hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec;
+ slba++, nent += entries_per_sec) {
+ sector_updated = 0;
+ for (part = 0; part < entries_per_sec; part++) {
+ ent = &table[nent + part];
+ if ((ent->ent_attr & (GPT_ENT_ATTR_BOOTME |
+ GPT_ENT_ATTR_BOOTONCE |
+ GPT_ENT_ATTR_BOOTFAILED)) !=
+ GPT_ENT_ATTR_BOOTONCE) {
+ continue;
+ }
+ ent->ent_attr &= ~GPT_ENT_ATTR_BOOTONCE;
+ ent->ent_attr |= GPT_ENT_ATTR_BOOTFAILED;
+ table_updated = 1;
+ sector_updated = 1;
+ }
+ if (!sector_updated)
+ continue;
+ bcopy(&table[nent], secbuf, DEV_BSIZE);
+ if (drvwrite(dskp, secbuf, slba, 1)) {
+ printf("%s: unable to update %s GPT partition table\n",
+ BOOTPROG, which);
+ }
+ }
+ if (!table_updated)
+ return;
+ hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz);
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size);
+ bzero(secbuf, DEV_BSIZE);
+ bcopy(hdr, secbuf, hdr->hdr_size);
+ if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1))
+ printf("%s: unable to update %s GPT header\n", BOOTPROG, which);
+}
+
+static int
+gptread_table(const char *which, const uuid_t *uuid, struct dsk *dskp,
+ struct gpt_hdr *hdr, struct gpt_ent *table)
+{
+ struct gpt_ent *ent;
+ int entries_per_sec;
+ int part, nent;
+ daddr_t slba;
+
+ if (hdr->hdr_entries == 0)
+ return (0);
+
+ entries_per_sec = DEV_BSIZE / hdr->hdr_entsz;
+ slba = hdr->hdr_lba_table;
+ nent = 0;
+ for (;;) {
+ if (drvread(dskp, secbuf, slba, 1)) {
+ printf("%s: unable to read %s GPT partition table\n",
+ BOOTPROG, which);
+ return (-1);
+ }
+ ent = (struct gpt_ent *)secbuf;
+ for (part = 0; part < entries_per_sec; part++, ent++) {
+ bcopy(ent, &table[nent], sizeof(table[nent]));
+ if (++nent >= hdr->hdr_entries)
+ break;
+ }
+ if (nent >= hdr->hdr_entries)
+ break;
+ slba++;
+ }
+ if (crc32(table, nent * hdr->hdr_entsz) != hdr->hdr_crc_table) {
+ printf("%s: %s GPT table checksum mismatch\n", BOOTPROG, which);
+ return (-1);
+ }
+ return (0);
+}
+
+int
+gptread(const uuid_t *uuid, struct dsk *dskp, char *buf)
+{
+ uint64_t altlba;
+
+ /*
+ * Read and verify both GPT headers: primary and backup.
+ */
+
+ secbuf = buf;
+ hdr_primary_lba = hdr_backup_lba = 0;
+ curent = -1;
+ bootonce = 1;
+ dskp->start = 0;
+
+ if (gptread_hdr("primary", dskp, &hdr_primary, 1) == 0 &&
+ gptread_table("primary", uuid, dskp, &hdr_primary,
+ table_primary) == 0) {
+ hdr_primary_lba = hdr_primary.hdr_lba_self;
+ gpthdr = &hdr_primary;
+ gpttable = table_primary;
+ }
+
+ if (hdr_primary_lba > 0) {
+ /*
+ * If primary header is valid, we can get backup
+ * header location from there.
+ */
+ altlba = hdr_primary.hdr_lba_alt;
+ } else {
+ altlba = drvsize(dskp);
+ if (altlba > 0)
+ altlba--;
+ }
+ if (altlba == 0)
+ printf("%s: unable to locate backup GPT header\n", BOOTPROG);
+ else if (gptread_hdr("backup", dskp, &hdr_backup, altlba) == 0 &&
+ gptread_table("backup", uuid, dskp, &hdr_backup,
+ table_backup) == 0) {
+ hdr_backup_lba = hdr_backup.hdr_lba_self;
+ if (hdr_primary_lba == 0) {
+ gpthdr = &hdr_backup;
+ gpttable = table_backup;
+ printf("%s: using backup GPT\n", BOOTPROG);
+ }
+ }
+
+ /*
+ * Convert all BOOTONCE without BOOTME flags into BOOTFAILED.
+ * BOOTONCE without BOOTME means that we tried to boot from it,
+ * but failed after leaving gptboot and machine was rebooted.
+ * We don't want to leave partitions marked as BOOTONCE only,
+ * because when we boot successfully start-up scripts should
+ * find at most one partition with only BOOTONCE flag and this
+ * will mean that we booted from that partition.
+ */
+ if (hdr_primary_lba != 0)
+ gptbootconv("primary", dskp, &hdr_primary, table_primary);
+ if (hdr_backup_lba != 0)
+ gptbootconv("backup", dskp, &hdr_backup, table_backup);
+
+ if (hdr_primary_lba == 0 && hdr_backup_lba == 0)
+ return (-1);
+ return (0);
+}
diff --git a/stand/libsa/gpt.h b/stand/libsa/gpt.h
new file mode 100644
index 0000000..9d48564
--- /dev/null
+++ b/stand/libsa/gpt.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _GPT_H_
+#define _GPT_H_
+
+#include <uuid.h>
+#include <drv.h>
+
+#define MAXTBLENTS 128
+
+int gptread(const uuid_t *uuid, struct dsk *dskp, char *buf);
+int gptfind(const uuid_t *uuid, struct dsk *dskp, int part);
+void gptbootfailed(struct dsk *dskp);
+
+#endif /* !_GPT_H_ */
diff --git a/stand/libsa/gzipfs.c b/stand/libsa/gzipfs.c
new file mode 100644
index 0000000..6057c28
--- /dev/null
+++ b/stand/libsa/gzipfs.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 1998 Michael Smith.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+#include <sys/stat.h>
+#include <string.h>
+#include <zlib.h>
+
+#define Z_BUFSIZE 2048 /* XXX larger? */
+
+struct z_file
+{
+ int zf_rawfd;
+ off_t zf_dataoffset;
+ z_stream zf_zstream;
+ char zf_buf[Z_BUFSIZE];
+ int zf_endseen;
+};
+
+static int zf_fill(struct z_file *z);
+static int zf_open(const char *path, struct open_file *f);
+static int zf_close(struct open_file *f);
+static int zf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t zf_seek(struct open_file *f, off_t offset, int where);
+static int zf_stat(struct open_file *f, struct stat *sb);
+
+struct fs_ops gzipfs_fsops = {
+ "zip",
+ zf_open,
+ zf_close,
+ zf_read,
+ null_write,
+ zf_seek,
+ zf_stat,
+ null_readdir
+};
+
+static int
+zf_fill(struct z_file *zf)
+{
+ int result;
+ int req;
+
+ req = Z_BUFSIZE - zf->zf_zstream.avail_in;
+ result = 0;
+
+ /* If we need more */
+ if (req > 0) {
+ /* move old data to bottom of buffer */
+ if (req < Z_BUFSIZE)
+ bcopy(zf->zf_buf + req, zf->zf_buf, Z_BUFSIZE - req);
+
+ /* read to fill buffer and update availibility data */
+ result = read(zf->zf_rawfd, zf->zf_buf + zf->zf_zstream.avail_in, req);
+ zf->zf_zstream.next_in = zf->zf_buf;
+ if (result >= 0)
+ zf->zf_zstream.avail_in += result;
+ }
+ return(result);
+}
+
+/*
+ * Adapted from get_byte/check_header in libz
+ *
+ * Returns 0 if the header is OK, nonzero if not.
+ */
+static int
+get_byte(struct z_file *zf, off_t *curoffp)
+{
+ if ((zf->zf_zstream.avail_in == 0) && (zf_fill(zf) == -1))
+ return(-1);
+ zf->zf_zstream.avail_in--;
+ ++*curoffp;
+ return(*(zf->zf_zstream.next_in)++);
+}
+
+static int 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 */
+
+static int
+check_header(struct z_file *zf)
+{
+ int method; /* method byte */
+ int flags; /* flags byte */
+ uInt len;
+ int c;
+
+ zf->zf_dataoffset = 0;
+ /* Check the gzip magic header */
+ for (len = 0; len < 2; len++) {
+ c = get_byte(zf, &zf->zf_dataoffset);
+ if (c != gz_magic[len]) {
+ return(1);
+ }
+ }
+ method = get_byte(zf, &zf->zf_dataoffset);
+ flags = get_byte(zf, &zf->zf_dataoffset);
+ if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ return(1);
+ }
+
+ /* Discard time, xflags and OS code: */
+ for (len = 0; len < 6; len++) (void)get_byte(zf, &zf->zf_dataoffset);
+
+ if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
+ len = (uInt)get_byte(zf, &zf->zf_dataoffset);
+ len += ((uInt)get_byte(zf, &zf->zf_dataoffset))<<8;
+ /* len is garbage if EOF but the loop below will quit anyway */
+ while (len-- != 0 && get_byte(zf, &zf->zf_dataoffset) != -1) ;
+ }
+ if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
+ while ((c = get_byte(zf, &zf->zf_dataoffset)) != 0 && c != -1) ;
+ }
+ if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
+ while ((c = get_byte(zf, &zf->zf_dataoffset)) != 0 && c != -1) ;
+ }
+ if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
+ for (len = 0; len < 2; len++) c = get_byte(zf, &zf->zf_dataoffset);
+ }
+ /* if there's data left, we're in business */
+ return((c == -1) ? 1 : 0);
+}
+
+static int
+zf_open(const char *fname, struct open_file *f)
+{
+ static char *zfname;
+ int rawfd;
+ struct z_file *zf;
+ char *cp;
+ int error;
+ struct stat sb;
+
+ /* Have to be in "just read it" mode */
+ if (f->f_flags != F_READ)
+ return(EPERM);
+
+ /* If the name already ends in .gz or .bz2, ignore it */
+ if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
+ || !strcmp(cp, ".bz2") || !strcmp(cp, ".split")))
+ return(ENOENT);
+
+ /* Construct new name */
+ zfname = malloc(strlen(fname) + 4);
+ if (zfname == NULL)
+ return(ENOMEM);
+ sprintf(zfname, "%s.gz", fname);
+
+ /* Try to open the compressed datafile */
+ rawfd = open(zfname, O_RDONLY);
+ free(zfname);
+ if (rawfd == -1)
+ return(ENOENT);
+
+ if (fstat(rawfd, &sb) < 0) {
+ printf("zf_open: stat failed\n");
+ close(rawfd);
+ return(ENOENT);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ printf("zf_open: not a file\n");
+ close(rawfd);
+ return(EISDIR); /* best guess */
+ }
+
+ /* Allocate a z_file structure, populate it */
+ zf = malloc(sizeof(struct z_file));
+ if (zf == NULL)
+ return(ENOMEM);
+ bzero(zf, sizeof(struct z_file));
+ zf->zf_rawfd = rawfd;
+
+ /* Verify that the file is gzipped */
+ if (check_header(zf)) {
+ close(zf->zf_rawfd);
+ free(zf);
+ return(EFTYPE);
+ }
+
+ /* Initialise the inflation engine */
+ if ((error = inflateInit2(&(zf->zf_zstream), -15)) != Z_OK) {
+ printf("zf_open: inflateInit returned %d : %s\n", error, zf->zf_zstream.msg);
+ close(zf->zf_rawfd);
+ free(zf);
+ return(EIO);
+ }
+
+ /* Looks OK, we'll take it */
+ f->f_fsdata = zf;
+ return(0);
+}
+
+static int
+zf_close(struct open_file *f)
+{
+ struct z_file *zf = (struct z_file *)f->f_fsdata;
+
+ inflateEnd(&(zf->zf_zstream));
+ close(zf->zf_rawfd);
+ free(zf);
+ return(0);
+}
+
+static int
+zf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ struct z_file *zf = (struct z_file *)f->f_fsdata;
+ int error;
+
+ zf->zf_zstream.next_out = buf; /* where and how much */
+ zf->zf_zstream.avail_out = size;
+
+ while (zf->zf_zstream.avail_out && zf->zf_endseen == 0) {
+ if ((zf->zf_zstream.avail_in == 0) && (zf_fill(zf) == -1)) {
+ printf("zf_read: fill error\n");
+ return(EIO);
+ }
+ if (zf->zf_zstream.avail_in == 0) { /* oops, unexpected EOF */
+ printf("zf_read: unexpected EOF\n");
+ if (zf->zf_zstream.avail_out == size)
+ return(EIO);
+ break;
+ }
+
+ error = inflate(&zf->zf_zstream, Z_SYNC_FLUSH); /* decompression pass */
+ if (error == Z_STREAM_END) { /* EOF, all done */
+ zf->zf_endseen = 1;
+ break;
+ }
+ if (error != Z_OK) { /* argh, decompression error */
+ printf("inflate: %s\n", zf->zf_zstream.msg);
+ return(EIO);
+ }
+ }
+ if (resid != NULL)
+ *resid = zf->zf_zstream.avail_out;
+ return(0);
+}
+
+static int
+zf_rewind(struct open_file *f)
+{
+ struct z_file *zf = (struct z_file *)f->f_fsdata;
+
+ if (lseek(zf->zf_rawfd, zf->zf_dataoffset, SEEK_SET) == -1)
+ return(-1);
+ zf->zf_zstream.avail_in = 0;
+ zf->zf_zstream.next_in = NULL;
+ zf->zf_endseen = 0;
+ (void)inflateReset(&zf->zf_zstream);
+
+ return(0);
+}
+
+static off_t
+zf_seek(struct open_file *f, off_t offset, int where)
+{
+ struct z_file *zf = (struct z_file *)f->f_fsdata;
+ off_t target;
+ char discard[16];
+
+ switch (where) {
+ case SEEK_SET:
+ target = offset;
+ break;
+ case SEEK_CUR:
+ target = offset + zf->zf_zstream.total_out;
+ break;
+ default:
+ errno = EINVAL;
+ return(-1);
+ }
+
+ /* rewind if required */
+ if (target < zf->zf_zstream.total_out && zf_rewind(f) != 0)
+ return(-1);
+
+ /* skip forwards if required */
+ while (target > zf->zf_zstream.total_out) {
+ errno = zf_read(f, discard, min(sizeof(discard),
+ target - zf->zf_zstream.total_out), NULL);
+ if (errno)
+ return(-1);
+ }
+ /* This is where we are (be honest if we overshot) */
+ return(zf->zf_zstream.total_out);
+}
+
+
+static int
+zf_stat(struct open_file *f, struct stat *sb)
+{
+ struct z_file *zf = (struct z_file *)f->f_fsdata;
+ int result;
+
+ /* stat as normal, but indicate that size is unknown */
+ if ((result = fstat(zf->zf_rawfd, sb)) == 0)
+ sb->st_size = -1;
+ return(result);
+}
+
+
+
diff --git a/stand/libsa/i386/_setjmp.S b/stand/libsa/i386/_setjmp.S
new file mode 100644
index 0000000..cc9de5c
--- /dev/null
+++ b/stand/libsa/i386/_setjmp.S
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_RCS) && !defined(lint)
+ .text
+ .asciz "$FreeBSD$"
+#endif /* LIBC_RCS and not lint */
+
+/*
+ * C library -- _setjmp, _longjmp
+ *
+ * _longjmp(a,v)
+ * will generate a "return(v)" from the last call to
+ * _setjmp(a)
+ * by restoring registers from the environment 'a'.
+ * The previous signal state is NOT restored.
+ */
+
+#include <machine/asm.h>
+
+ENTRY(_setjmp)
+ movl 4(%esp),%eax
+ movl 0(%esp),%edx
+ movl %edx, 0(%eax) /* rta */
+ movl %ebx, 4(%eax)
+ movl %esp, 8(%eax)
+ movl %ebp,12(%eax)
+ movl %esi,16(%eax)
+ movl %edi,20(%eax)
+ xorl %eax,%eax
+ ret
+END(_setjmp)
+
+ENTRY(_longjmp)
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ movl 0(%edx),%ecx
+ movl 4(%edx),%ebx
+ movl 8(%edx),%esp
+ movl 12(%edx),%ebp
+ movl 16(%edx),%esi
+ movl 20(%edx),%edi
+ testl %eax,%eax
+ jnz 1f
+ incl %eax
+1: movl %ecx,0(%esp)
+ ret
+END(_longjmp)
diff --git a/stand/libsa/in_cksum.c b/stand/libsa/in_cksum.c
new file mode 100644
index 0000000..e6051bf
--- /dev/null
+++ b/stand/libsa/in_cksum.c
@@ -0,0 +1,94 @@
+/* $NetBSD: in_cksum.c,v 1.6 2000/03/31 19:55:09 castor Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: in_cksum.c,v 1.1 92/09/11 01:15:55 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <machine/endian.h>
+
+#include "stand.h"
+
+/*
+ * Checksum routine for Internet Protocol family headers.
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ * In particular, it should not be this one.
+ */
+int
+in_cksum(p, len)
+ void *p;
+ int len;
+{
+ int sum = 0, oddbyte = 0, v = 0;
+ u_char *cp = p;
+
+ /* we assume < 2^16 bytes being summed */
+ while (len > 0) {
+ if (oddbyte) {
+ sum += v + *cp++;
+ len--;
+ }
+ if (((long)cp & 1) == 0) {
+ while ((len -= 2) >= 0) {
+ sum += *(u_short *)cp;
+ cp += 2;
+ }
+ } else {
+ while ((len -= 2) >= 0) {
+#if BYTE_ORDER == BIG_ENDIAN
+ sum += *cp++ << 8;
+ sum += *cp++;
+#else
+ sum += *cp++;
+ sum += *cp++ << 8;
+#endif
+ }
+ }
+ if ((oddbyte = len & 1) != 0)
+#if BYTE_ORDER == BIG_ENDIAN
+ v = *cp << 8;
+#else
+ v = *cp;
+#endif
+ }
+ if (oddbyte)
+ sum += v;
+ sum = (sum >> 16) + (sum & 0xffff); /* add in accumulated carries */
+ sum += sum >> 16; /* add potential last carry */
+ return (0xffff & ~sum);
+}
diff --git a/stand/libsa/inet_ntoa.c b/stand/libsa/inet_ntoa.c
new file mode 100644
index 0000000..5bc8533
--- /dev/null
+++ b/stand/libsa/inet_ntoa.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)inet_ntoa.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "stand.h"
+
+/*
+ * Convert network-format internet address
+ * to base 256 d.d.d.d representation.
+ */
+char *
+inet_ntoa(in)
+ struct in_addr in;
+{
+ static const char fmt[] = "%u.%u.%u.%u";
+ static char ret[sizeof "255.255.255.255"];
+ unsigned char *src = (unsigned char *) &in;
+
+ sprintf(ret, fmt, src[0], src[1], src[2], src[3]);
+ return (ret);
+}
+
+/*
+ * Weak aliases for applications that use certain private entry points,
+ * and fail to include <arpa/inet.h>.
+ */
+#undef inet_ntoa
+__weak_reference(__inet_ntoa, inet_ntoa);
diff --git a/stand/libsa/ioctl.c b/stand/libsa/ioctl.c
new file mode 100644
index 0000000..a4512af
--- /dev/null
+++ b/stand/libsa/ioctl.c
@@ -0,0 +1,88 @@
+/* $NetBSD: ioctl.c,v 1.4 1994/10/30 21:48:24 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ioctl.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+int
+ioctl(fd, cmd, arg)
+ int fd;
+ u_long cmd;
+ char *arg;
+{
+ struct open_file *f = &files[fd];
+
+ if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (f->f_flags & F_RAW) {
+ errno = (f->f_dev->dv_ioctl)(f, cmd, arg);
+ if (errno)
+ return (-1);
+ return (0);
+ }
+ errno = EIO;
+ return (-1);
+}
diff --git a/stand/libsa/iodesc.h b/stand/libsa/iodesc.h
new file mode 100644
index 0000000..0da1946
--- /dev/null
+++ b/stand/libsa/iodesc.h
@@ -0,0 +1,52 @@
+/* $NetBSD: iodesc.h,v 1.4 1995/09/23 03:31:50 gwr Exp $ */
+
+/*
+ * Copyright (c) 1993 Adam Glass
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __SYS_LIBNETBOOT_IODESC_H
+#define __SYS_LIBNETBOOT_IODESC_H
+
+struct iodesc {
+ struct in_addr destip; /* dest. ip addr, net order */
+ struct in_addr myip; /* local ip addr, net order */
+ u_short destport; /* dest. port, net order */
+ u_short myport; /* local port, net order */
+ u_long xid; /* transaction identification */
+ u_char myea[6]; /* my ethernet address */
+ struct netif *io_netif;
+};
+
+#endif /* __SYS_LIBNETBOOT_IODESC_H */
diff --git a/stand/libsa/ip.c b/stand/libsa/ip.c
new file mode 100644
index 0000000..f2293b3
--- /dev/null
+++ b/stand/libsa/ip.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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 University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * The send and receive functions were originally implemented in udp.c and
+ * moved here. Also it is likely some more cleanup can be done, especially
+ * once we will implement the support for tcp.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <string.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+
+#include <netinet/in_pcb.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include "stand.h"
+#include "net.h"
+
+typedef STAILQ_HEAD(ipqueue, ip_queue) ip_queue_t;
+struct ip_queue {
+ void *ipq_pkt;
+ struct ip *ipq_hdr;
+ STAILQ_ENTRY(ip_queue) ipq_next;
+};
+
+/*
+ * Fragment re-assembly queue.
+ */
+struct ip_reasm {
+ struct in_addr ip_src;
+ struct in_addr ip_dst;
+ uint16_t ip_id;
+ uint8_t ip_proto;
+ uint8_t ip_ttl;
+ size_t ip_total_size;
+ ip_queue_t ip_queue;
+ void *ip_pkt;
+ struct ip *ip_hdr;
+ STAILQ_ENTRY(ip_reasm) ip_next;
+};
+
+STAILQ_HEAD(ire_list, ip_reasm) ire_list = STAILQ_HEAD_INITIALIZER(ire_list);
+
+/* Caller must leave room for ethernet and ip headers in front!! */
+ssize_t
+sendip(struct iodesc *d, void *pkt, size_t len, uint8_t proto)
+{
+ ssize_t cc;
+ struct ip *ip;
+ u_char *ea;
+
+#ifdef NET_DEBUG
+ if (debug) {
+ printf("sendip: proto: %x d=%p called.\n", proto, (void *)d);
+ if (d) {
+ printf("saddr: %s:%d",
+ inet_ntoa(d->myip), ntohs(d->myport));
+ printf(" daddr: %s:%d\n",
+ inet_ntoa(d->destip), ntohs(d->destport));
+ }
+ }
+#endif
+
+ ip = (struct ip *)pkt - 1;
+ len += sizeof(*ip);
+
+ bzero(ip, sizeof(*ip));
+
+ ip->ip_v = IPVERSION; /* half-char */
+ ip->ip_hl = sizeof(*ip) >> 2; /* half-char */
+ ip->ip_len = htons(len);
+ ip->ip_p = proto; /* char */
+ ip->ip_ttl = IPDEFTTL; /* char */
+ ip->ip_src = d->myip;
+ ip->ip_dst = d->destip;
+ ip->ip_sum = in_cksum(ip, sizeof(*ip)); /* short, but special */
+
+ if (ip->ip_dst.s_addr == INADDR_BROADCAST || ip->ip_src.s_addr == 0 ||
+ netmask == 0 || SAMENET(ip->ip_src, ip->ip_dst, netmask))
+ ea = arpwhohas(d, ip->ip_dst);
+ else
+ ea = arpwhohas(d, gateip);
+
+ cc = sendether(d, ip, len, ea, ETHERTYPE_IP);
+ if (cc == -1)
+ return (-1);
+ if (cc != len)
+ panic("sendip: bad write (%zd != %zd)", cc, len);
+ return (cc - sizeof(*ip));
+}
+
+static void
+ip_reasm_free(struct ip_reasm *ipr)
+{
+ struct ip_queue *ipq;
+
+ while ((ipq = STAILQ_FIRST(&ipr->ip_queue)) != NULL) {
+ STAILQ_REMOVE_HEAD(&ipr->ip_queue, ipq_next);
+ free(ipq->ipq_pkt);
+ free(ipq);
+ }
+ free(ipr->ip_pkt);
+ free(ipr);
+}
+
+static int
+ip_reasm_add(struct ip_reasm *ipr, void *pkt, struct ip *ip)
+{
+ struct ip_queue *ipq, *prev, *p;
+
+ if ((ipq = calloc(1, sizeof (*ipq))) == NULL)
+ return (1);
+
+ ipq->ipq_pkt = pkt;
+ ipq->ipq_hdr = ip;
+
+ prev = NULL;
+ STAILQ_FOREACH(p, &ipr->ip_queue, ipq_next) {
+ if ((ntohs(p->ipq_hdr->ip_off) & IP_OFFMASK) <
+ (ntohs(ip->ip_off) & IP_OFFMASK)) {
+ prev = p;
+ continue;
+ }
+ if (prev == NULL)
+ break;
+
+ STAILQ_INSERT_AFTER(&ipr->ip_queue, prev, ipq, ipq_next);
+ return (0);
+ }
+ STAILQ_INSERT_HEAD(&ipr->ip_queue, ipq, ipq_next);
+ return (0);
+}
+
+/*
+ * Receive a IP packet and validate it is for us.
+ */
+static ssize_t
+readipv4(struct iodesc *d, void **pkt, void **payload, time_t tleft,
+ uint8_t proto)
+{
+ ssize_t n;
+ size_t hlen;
+ struct ether_header *eh;
+ struct ip *ip;
+ struct udphdr *uh;
+ uint16_t etype; /* host order */
+ char *ptr;
+ struct ip_reasm *ipr;
+ struct ip_queue *ipq, *last;
+
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readip: called\n");
+#endif
+
+ ip = NULL;
+ ptr = NULL;
+ n = readether(d, (void **)&ptr, (void **)&ip, tleft, &etype);
+ if (n == -1 || n < sizeof(*ip) + sizeof(*uh)) {
+ free(ptr);
+ return (-1);
+ }
+
+ /* Ethernet address checks now in readether() */
+
+ /* Need to respond to ARP requests. */
+ if (etype == ETHERTYPE_ARP) {
+ struct arphdr *ah = (void *)ip;
+ if (ah->ar_op == htons(ARPOP_REQUEST)) {
+ /* Send ARP reply */
+ arp_reply(d, ah);
+ }
+ free(ptr);
+ errno = EAGAIN; /* Call me again. */
+ return (-1);
+ }
+
+ if (etype != ETHERTYPE_IP) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readip: not IP. ether_type=%x\n", etype);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* Check ip header */
+ if (ip->ip_v != IPVERSION || /* half char */
+ ip->ip_p != proto) {
+#ifdef NET_DEBUG
+ if (debug) {
+ printf("readip: IP version or proto. ip_v=%d ip_p=%d\n",
+ ip->ip_v, ip->ip_p);
+ }
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ hlen = ip->ip_hl << 2;
+ if (hlen < sizeof(*ip) ||
+ in_cksum(ip, hlen) != 0) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readip: short hdr or bad cksum.\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+ if (n < ntohs(ip->ip_len)) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readip: bad length %d < %d.\n",
+ (int)n, ntohs(ip->ip_len));
+#endif
+ free(ptr);
+ return (-1);
+ }
+ if (d->myip.s_addr && ip->ip_dst.s_addr != d->myip.s_addr) {
+#ifdef NET_DEBUG
+ if (debug) {
+ printf("readip: bad saddr %s != ", inet_ntoa(d->myip));
+ printf("%s\n", inet_ntoa(ip->ip_dst));
+ }
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* Unfragmented packet. */
+ if ((ntohs(ip->ip_off) & IP_MF) == 0 &&
+ (ntohs(ip->ip_off) & IP_OFFMASK) == 0) {
+ uh = (struct udphdr *)((uintptr_t)ip + sizeof (*ip));
+ /* If there were ip options, make them go away */
+ if (hlen != sizeof(*ip)) {
+ bcopy(((u_char *)ip) + hlen, uh, uh->uh_ulen - hlen);
+ ip->ip_len = htons(sizeof(*ip));
+ n -= hlen - sizeof(*ip);
+ }
+
+ n = (n > (ntohs(ip->ip_len) - sizeof(*ip))) ?
+ ntohs(ip->ip_len) - sizeof(*ip) : n;
+ *pkt = ptr;
+ *payload = (void *)((uintptr_t)ip + sizeof(*ip));
+ return (n);
+ }
+
+ STAILQ_FOREACH(ipr, &ire_list, ip_next) {
+ if (ipr->ip_src.s_addr == ip->ip_src.s_addr &&
+ ipr->ip_dst.s_addr == ip->ip_dst.s_addr &&
+ ipr->ip_id == ip->ip_id &&
+ ipr->ip_proto == ip->ip_p)
+ break;
+ }
+
+ /* Allocate new reassembly entry */
+ if (ipr == NULL) {
+ if ((ipr = calloc(1, sizeof (*ipr))) == NULL) {
+ free(ptr);
+ return (-1);
+ }
+
+ ipr->ip_src = ip->ip_src;
+ ipr->ip_dst = ip->ip_dst;
+ ipr->ip_id = ip->ip_id;
+ ipr->ip_proto = ip->ip_p;
+ ipr->ip_ttl = MAXTTL;
+ STAILQ_INIT(&ipr->ip_queue);
+ STAILQ_INSERT_TAIL(&ire_list, ipr, ip_next);
+ }
+
+ if (ip_reasm_add(ipr, ptr, ip) != 0) {
+ STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next);
+ free(ipr);
+ free(ptr);
+ return (-1);
+ }
+
+ if ((ntohs(ip->ip_off) & IP_MF) == 0) {
+ ipr->ip_total_size = (8 * (ntohs(ip->ip_off) & IP_OFFMASK));
+ ipr->ip_total_size += n + sizeof (*ip);
+ ipr->ip_total_size += sizeof (struct ether_header);
+
+ ipr->ip_pkt = malloc(ipr->ip_total_size + 2);
+ if (ipr->ip_pkt == NULL) {
+ STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next);
+ ip_reasm_free(ipr);
+ return (-1);
+ }
+ }
+
+ /*
+ * If we do not have re-assembly buffer ipr->ip_pkt, we are still
+ * missing fragments, so just restart the read.
+ */
+ if (ipr->ip_pkt == NULL) {
+ errno = EAGAIN;
+ return (-1);
+ }
+
+ /*
+ * Walk the packet list in reassembly queue, if we got all the
+ * fragments, build the packet.
+ */
+ n = 0;
+ last = NULL;
+ STAILQ_FOREACH(ipq, &ipr->ip_queue, ipq_next) {
+ if ((ntohs(ipq->ipq_hdr->ip_off) & IP_OFFMASK) != n / 8) {
+ STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next);
+ ip_reasm_free(ipr);
+ return (-1);
+ }
+
+ n += ntohs(ipq->ipq_hdr->ip_len) - (ipq->ipq_hdr->ip_hl << 2);
+ last = ipq;
+ }
+ if ((ntohs(last->ipq_hdr->ip_off) & IP_MF) != 0) {
+ errno = EAGAIN;
+ return (-1);
+ }
+
+ ipq = STAILQ_FIRST(&ipr->ip_queue);
+ /* Fabricate ethernet header */
+ eh = (struct ether_header *)((uintptr_t)ipr->ip_pkt + 2);
+ bcopy((void *)((uintptr_t)ipq->ipq_pkt + 2), eh, sizeof (*eh));
+
+ /* Fabricate IP header */
+ ipr->ip_hdr = (struct ip *)((uintptr_t)eh + sizeof (*eh));
+ bcopy(ipq->ipq_hdr, ipr->ip_hdr, sizeof (*ipr->ip_hdr));
+ ipr->ip_hdr->ip_hl = sizeof (*ipr->ip_hdr) >> 2;
+ ipr->ip_hdr->ip_len = htons(n);
+ ipr->ip_hdr->ip_sum = 0;
+ ipr->ip_hdr->ip_sum = in_cksum(ipr->ip_hdr, sizeof (*ipr->ip_hdr));
+
+ n = 0;
+ ptr = (char *)((uintptr_t)ipr->ip_hdr + sizeof (*ipr->ip_hdr));
+ STAILQ_FOREACH(ipq, &ipr->ip_queue, ipq_next) {
+ char *data;
+ size_t len;
+
+ hlen = ipq->ipq_hdr->ip_hl << 2;
+ len = ntohs(ipq->ipq_hdr->ip_len) - hlen;
+ data = (char *)((uintptr_t)ipq->ipq_hdr + hlen);
+
+ bcopy(data, ptr + n, len);
+ n += len;
+ }
+
+ *pkt = ipr->ip_pkt;
+ ipr->ip_pkt = NULL; /* Avoid free from ip_reasm_free() */
+ *payload = ptr;
+
+ /* Clean up the reassembly list */
+ while ((ipr = STAILQ_FIRST(&ire_list)) != NULL) {
+ STAILQ_REMOVE_HEAD(&ire_list, ip_next);
+ ip_reasm_free(ipr);
+ }
+ return (n);
+}
+
+/*
+ * Receive a IP packet.
+ */
+ssize_t
+readip(struct iodesc *d, void **pkt, void **payload, time_t tleft,
+ uint8_t proto)
+{
+ time_t t;
+ ssize_t ret = -1;
+
+ t = getsecs();
+ while ((getsecs() - t) < tleft) {
+ errno = 0;
+ ret = readipv4(d, pkt, payload, tleft, proto);
+ if (errno != EAGAIN)
+ break;
+ }
+ return (ret);
+}
diff --git a/stand/libsa/libstand.3 b/stand/libsa/libstand.3
new file mode 100644
index 0000000..acb979f
--- /dev/null
+++ b/stand/libsa/libstand.3
@@ -0,0 +1,676 @@
+.\" Copyright (c) Michael Smith
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 6, 2004
+.Dt LIBSTAND 3
+.Os
+.Sh NAME
+.Nm libstand
+.Nd support library for standalone executables
+.Sh SYNOPSIS
+.In stand.h
+.Sh DESCRIPTION
+The
+.Nm
+library provides a set of supporting functions for standalone
+applications, mimicking where possible the standard
+.Bx
+programming
+environment.
+The following sections group these functions by kind.
+Unless specifically described here, see the corresponding section 3
+manpages for the given functions.
+.Sh STRING FUNCTIONS
+String functions are available as documented in
+.Xr string 3
+and
+.Xr bstring 3 .
+.Sh MEMORY ALLOCATION
+.Bl -hang -width 10n
+.It Xo
+.Ft "void *"
+.Fn malloc "size_t size"
+.Xc
+.Pp
+Allocate
+.Fa size
+bytes of memory from the heap using a best-fit algorithm.
+.It Xo
+.Ft void
+.Fn free "void *ptr"
+.Xc
+.Pp
+Free the allocated object at
+.Fa ptr .
+.It Xo
+.Ft void
+.Fn setheap "void *start" "void *limit"
+.Xc
+.Pp
+Initialise the heap.
+This function must be called before calling
+.Fn alloc
+for the first time.
+The region between
+.Fa start
+and
+.Fa limit
+will be used for the heap; attempting to allocate beyond this will result
+in a panic.
+.It Xo
+.Ft "char *"
+.Fn sbrk "int junk"
+.Xc
+.Pp
+Provides the behaviour of
+.Fn sbrk 0 ,
+i.e., returns the highest point that the heap has reached.
+This value can
+be used during testing to determine the actual heap usage.
+The
+.Fa junk
+argument is ignored.
+.El
+.Sh ENVIRONMENT
+A set of functions are provided for manipulating a flat variable space similar
+to the traditional shell-supported environment.
+Major enhancements are support
+for set/unset hook functions.
+.Bl -hang -width 10n
+.It Xo
+.Ft "char *"
+.Fn getenv "const char *name"
+.Xc
+.It Xo
+.Ft int
+.Fn setenv "const char *name" "const char *value" "int overwrite"
+.Xc
+.It Xo
+.Ft int
+.Fn putenv "char *string"
+.Xc
+.It Xo
+.Ft int
+.Fn unsetenv "const char *name"
+.Xc
+.Pp
+These functions behave similarly to their standard library counterparts.
+.It Xo
+.Ft "struct env_var *"
+.Fn env_getenv "const char *name"
+.Xc
+.Pp
+Looks up a variable in the environment and returns its entire
+data structure.
+.It Xo
+.Ft int
+.Fn env_setenv "const char *name" "int flags" "const void *value" "ev_sethook_t sethook" "ev_unsethook_t unsethook"
+.Xc
+.Pp
+Creates a new or sets an existing environment variable called
+.Fa name .
+If creating a new variable, the
+.Fa sethook
+and
+.Fa unsethook
+arguments may be specified.
+.Pp
+The set hook is invoked whenever an attempt
+is made to set the variable, unless the EV_NOHOOK flag is set.
+Typically
+a set hook will validate the
+.Fa value
+argument, and then call
+.Fn env_setenv
+again with EV_NOHOOK set to actually save the value.
+The predefined function
+.Fn env_noset
+may be specified to refuse all attempts to set a variable.
+.Pp
+The unset hook is invoked when an attempt is made to unset a variable.
+If it
+returns zero, the variable will be unset.
+The predefined function
+.Fa env_nounset
+may be used to prevent a variable being unset.
+.El
+.Sh STANDARD LIBRARY SUPPORT
+.Bl -hang -width 10n
+.It Xo
+.Ft int
+.Fn getopt "int argc" "char * const *argv" "const char *optstring"
+.Xc
+.It Xo
+.Ft long
+.Fn strtol "const char *nptr" "char **endptr" "int base"
+.Xc
+.It Xo
+.Ft void
+.Fn srandom "unsigned long seed"
+.Xc
+.It Xo
+.Ft "long"
+.Fn random void
+.Xc
+.It Xo
+.Ft "char *"
+.Fn strerror "int error"
+.Xc
+.Pp
+Returns error messages for the subset of errno values supported by
+.Nm .
+.It Fn assert expression
+.Pp
+Requires
+.In assert.h .
+.It Xo
+.Ft int
+.Fn setjmp "jmp_buf env"
+.Xc
+.It Xo
+.Ft void
+.Fn longjmp "jmp_buf env" "int val"
+.Xc
+.Pp
+Defined as
+.Fn _setjmp
+and
+.Fn _longjmp
+respectively as there is no signal state to manipulate.
+Requires
+.In setjmp.h .
+.El
+.Sh CHARACTER I/O
+.Bl -hang -width 10n
+.It Xo
+.Ft void
+.Fn gets "char *buf"
+.Xc
+.Pp
+Read characters from the console into
+.Fa buf .
+All of the standard cautions apply to this function.
+.It Xo
+.Ft void
+.Fn ngets "char *buf" "int size"
+.Xc
+.Pp
+Read at most
+.Fa size
+- 1 characters from the console into
+.Fa buf .
+If
+.Fa size
+is less than 1, the function's behaviour is as for
+.Fn gets .
+.It Xo
+.Ft int
+.Fn fgetstr "char *buf" "int size" "int fd"
+.Xc
+.Pp
+Read a line of at most
+.Fa size
+characters into
+.Fa buf .
+Line terminating characters are stripped, and the buffer is always
+.Dv NUL
+terminated.
+Returns the number of characters in
+.Fa buf
+if successful, or -1 if a read error occurs.
+.It Xo
+.Ft int
+.Fn printf "const char *fmt" "..."
+.Xc
+.It Xo
+.Ft void
+.Fn vprintf "const char *fmt" "va_list ap"
+.Xc
+.It Xo
+.Ft int
+.Fn sprintf "char *buf" "const char *fmt" "..."
+.Xc
+.It Xo
+.Ft void
+.Fn vsprintf "char *buf" "const char *fmt" "va_list ap"
+.Xc
+.Pp
+The *printf functions implement a subset of the standard
+.Fn printf
+family functionality and some extensions.
+The following standard conversions
+are supported: c,d,n,o,p,s,u,x.
+The following modifiers are supported:
++,-,#,*,0,field width,precision,l.
+.Pp
+The
+.Li b
+conversion is provided to decode error registers.
+Its usage is:
+.Bd -ragged -offset indent
+printf(
+.Qq reg=%b\en ,
+regval,
+.Qq <base><arg>*
+);
+.Ed
+.Pp
+where <base> is the output expressed as a control character, e.g.\& \e10 gives
+octal, \e20 gives hex.
+Each <arg> is a sequence of characters, the first of
+which gives the bit number to be inspected (origin 1) and the next characters
+(up to a character less than 32) give the text to be displayed if the bit is set.
+Thus
+.Bd -ragged -offset indent
+printf(
+.Qq reg=%b\en ,
+3,
+.Qq \e10\e2BITTWO\e1BITONE
+);
+.Ed
+.Pp
+would give the output
+.Bd -ragged -offset indent
+reg=3<BITTWO,BITONE>
+.Ed
+.Pp
+The
+.Li D
+conversion provides a hexdump facility, e.g.
+.Bd -ragged -offset indent
+printf(
+.Qq %6D ,
+ptr,
+.Qq \&:
+); gives
+.Qq XX:XX:XX:XX:XX:XX
+.Ed
+.Bd -ragged -offset indent
+printf(
+.Qq %*D ,
+len,
+ptr,
+.Qq "\ "
+); gives
+.Qq XX XX XX ...
+.Ed
+.El
+.Sh CHARACTER TESTS AND CONVERSIONS
+.Bl -hang -width 10n
+.It Xo
+.Ft int
+.Fn isupper "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn islower "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn isspace "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn isdigit "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn isxdigit "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn isascii "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn isalpha "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn toupper "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn tolower "int c"
+.Xc
+.El
+.Sh FILE I/O
+.Bl -hang -width 10n
+.It Xo
+.Ft int
+.Fn open "const char *path" "int flags"
+.Xc
+.Pp
+Similar to the behaviour as specified in
+.Xr open 2 ,
+except that file creation is not supported, so the mode parameter is not
+required.
+The
+.Fa flags
+argument may be one of O_RDONLY, O_WRONLY and O_RDWR (although no file systems
+currently support writing).
+.It Xo
+.Ft int
+.Fn close "int fd"
+.Xc
+.It Xo
+.Ft void
+.Fn closeall void
+.Xc
+.Pp
+Close all open files.
+.It Xo
+.Ft ssize_t
+.Fn read "int fd" "void *buf" "size_t len"
+.Xc
+.It Xo
+.Ft ssize_t
+.Fn write "int fd" "void *buf" "size_t len"
+.Xc
+.Pp
+(No file systems currently support writing.)
+.It Xo
+.Ft off_t
+.Fn lseek "int fd" "off_t offset" "int whence"
+.Xc
+.Pp
+Files being automatically uncompressed during reading cannot seek backwards
+from the current point.
+.It Xo
+.Ft int
+.Fn stat "const char *path" "struct stat *sb"
+.Xc
+.It Xo
+.Ft int
+.Fn fstat "int fd" "struct stat *sb"
+.Xc
+.Pp
+The
+.Fn stat
+and
+.Fn fstat
+functions only fill out the following fields in the
+.Fa sb
+structure: st_mode,st_nlink,st_uid,st_gid,st_size.
+The
+.Nm tftp
+file system cannot provide meaningful values for this call, and the
+.Nm cd9660
+file system always reports files having uid/gid of zero.
+.El
+.Sh PAGER
+The
+.Nm
+library supplies a simple internal pager to ease reading the output of large
+commands.
+.Bl -hang -width 10n
+.It Xo
+.Ft void
+.Fn pager_open
+.Xc
+.Pp
+Initialises the pager and tells it that the next line output will be the top of the
+display.
+The environment variable LINES is consulted to determine the number of
+lines to be displayed before pausing.
+.It Xo
+.Ft void
+.Fn pager_close void
+.Xc
+.Pp
+Closes the pager.
+.It Xo
+.Ft int
+.Fn pager_output "const char *lines"
+.Xc
+.Pp
+Sends the lines in the
+.Dv NUL Ns
+-terminated buffer at
+.Fa lines
+to the pager.
+Newline characters are counted in order to determine the number
+of lines being output (wrapped lines are not accounted for).
+The
+.Fn pager_output
+function will return zero when all of the lines have been output, or nonzero
+if the display was paused and the user elected to quit.
+.It Xo
+.Ft int
+.Fn pager_file "const char *fname"
+.Xc
+.Pp
+Attempts to open and display the file
+.Fa fname .
+Returns -1 on error, 0 at EOF, or 1 if the user elects to quit while reading.
+.El
+.Sh MISC
+.Bl -hang -width 10n
+.It Xo
+.Ft void
+.Fn twiddle void
+.Xc
+.Pp
+Successive calls emit the characters in the sequence |,/,-,\\ followed by a
+backspace in order to provide reassurance to the user.
+.El
+.Sh REQUIRED LOW-LEVEL SUPPORT
+The following resources are consumed by
+.Nm
+- stack, heap, console and devices.
+.Pp
+The stack must be established before
+.Nm
+functions can be invoked.
+Stack requirements vary depending on the functions
+and file systems used by the consumer and the support layer functions detailed
+below.
+.Pp
+The heap must be established before calling
+.Fn alloc
+or
+.Fn open
+by calling
+.Fn setheap .
+Heap usage will vary depending on the number of simultaneously open files,
+as well as client behaviour.
+Automatic decompression will allocate more
+than 64K of data per open file.
+.Pp
+Console access is performed via the
+.Fn getchar ,
+.Fn putchar
+and
+.Fn ischar
+functions detailed below.
+.Pp
+Device access is initiated via
+.Fn devopen
+and is performed through the
+.Fn dv_strategy ,
+.Fn dv_ioctl
+and
+.Fn dv_close
+functions in the device switch structure that
+.Fn devopen
+returns.
+.Pp
+The consumer must provide the following support functions:
+.Bl -hang -width 10n
+.It Xo
+.Ft int
+.Fn getchar void
+.Xc
+.Pp
+Return a character from the console, used by
+.Fn gets ,
+.Fn ngets
+and pager functions.
+.It Xo
+.Ft int
+.Fn ischar void
+.Xc
+.Pp
+Returns nonzero if a character is waiting from the console.
+.It Xo
+.Ft void
+.Fn putchar int
+.Xc
+.Pp
+Write a character to the console, used by
+.Fn gets ,
+.Fn ngets ,
+.Fn *printf ,
+.Fn panic
+and
+.Fn twiddle
+and thus by many other functions for debugging and informational output.
+.It Xo
+.Ft int
+.Fn devopen "struct open_file *of" "const char *name" "const char **file"
+.Xc
+.Pp
+Open the appropriate device for the file named in
+.Fa name ,
+returning in
+.Fa file
+a pointer to the remaining body of
+.Fa name
+which does not refer to the device.
+The
+.Va f_dev
+field in
+.Fa of
+will be set to point to the
+.Vt devsw
+structure for the opened device if successful.
+Device identifiers must
+always precede the path component, but may otherwise be arbitrarily formatted.
+Used by
+.Fn open
+and thus for all device-related I/O.
+.It Xo
+.Ft int
+.Fn devclose "struct open_file *of"
+.Xc
+.Pp
+Close the device allocated for
+.Fa of .
+The device driver itself will already have been called for the close; this call
+should clean up any allocation made by devopen only.
+.It Xo
+.Ft void
+.Fn panic "const char *msg" "..."
+.Xc
+.Pp
+Signal a fatal and unrecoverable error condition.
+The
+.Fa msg ...
+arguments are as for
+.Fn printf .
+.El
+.Sh INTERNAL FILE SYSTEMS
+Internal file systems are enabled by the consumer exporting the array
+.Vt struct fs_ops *file_system[] ,
+which should be initialised with pointers
+to
+.Vt struct fs_ops
+structures.
+The following file system handlers are supplied by
+.Nm ,
+the consumer may supply other file systems of their own:
+.Bl -hang -width ".Va cd9660_fsops"
+.It Va ufs_fsops
+The
+.Bx
+UFS.
+.It Va ext2fs_fsops
+Linux ext2fs file system.
+.It Va tftp_fsops
+File access via TFTP.
+.It Va nfs_fsops
+File access via NFS.
+.It Va cd9660_fsops
+ISO 9660 (CD-ROM) file system.
+.It Va gzipfs_fsops
+Stacked file system supporting gzipped files.
+When trying the gzipfs file system,
+.Nm
+appends
+.Li .gz
+to the end of the filename, and then tries to locate the file using the other
+file systems.
+Placement of this file system in the
+.Va file_system[]
+array determines whether gzipped files will be opened in preference to non-gzipped
+files.
+It is only possible to seek a gzipped file forwards, and
+.Fn stat
+and
+.Fn fstat
+on gzipped files will report an invalid length.
+.It Va bzipfs_fsops
+The same as
+.Va gzipfs_fsops ,
+but for
+.Xr bzip2 1 Ns -compressed
+files.
+.El
+.Pp
+The array of
+.Vt struct fs_ops
+pointers should be terminated with a NULL.
+.Sh DEVICES
+Devices are exported by the supporting code via the array
+.Vt struct devsw *devsw[]
+which is a NULL terminated array of pointers to device switch structures.
+.Sh HISTORY
+The
+.Nm
+library contains contributions from many sources, including:
+.Bl -bullet -compact
+.It
+.Nm libsa
+from
+.Nx
+.It
+.Nm libc
+and
+.Nm libkern
+from
+.Fx 3.0 .
+.It
+.Nm zalloc
+from
+.An Matthew Dillon Aq Mt dillon@backplane.com
+.El
+.Pp
+The reorganisation and port to
+.Fx 3.0 ,
+the environment functions and this manpage were written by
+.An Mike Smith Aq Mt msmith@FreeBSD.org .
+.Sh BUGS
+The lack of detailed memory usage data is unhelpful.
diff --git a/stand/libsa/lseek.c b/stand/libsa/lseek.c
new file mode 100644
index 0000000..b9debd1
--- /dev/null
+++ b/stand/libsa/lseek.c
@@ -0,0 +1,141 @@
+/* $NetBSD: lseek.c,v 1.4 1997/01/22 00:38:10 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lseek.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+off_t
+lseek(int fd, off_t offset, int where)
+{
+ off_t bufpos, filepos, target;
+ struct open_file *f = &files[fd];
+
+ if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) {
+ errno = EBADF;
+ return (-1);
+ }
+
+ if (f->f_flags & F_RAW) {
+ /*
+ * On RAW devices, update internal offset.
+ */
+ switch (where) {
+ case SEEK_SET:
+ f->f_offset = offset;
+ break;
+ case SEEK_CUR:
+ f->f_offset += offset;
+ break;
+ default:
+ errno = EOFFSET;
+ return (-1);
+ }
+ return (f->f_offset);
+ }
+
+ /*
+ * If there is some unconsumed data in the readahead buffer and it
+ * contains the desired offset, simply adjust the buffer offset and
+ * length. We don't bother with SEEK_END here, since the code to
+ * handle it would fail in the same cases where the non-readahead
+ * code fails (namely, for streams which cannot seek backward and whose
+ * size isn't known in advance).
+ */
+ if (f->f_ralen != 0 && where != SEEK_END) {
+ if ((filepos = (f->f_ops->fo_seek)(f, (off_t)0, SEEK_CUR)) == -1)
+ return (-1);
+ bufpos = filepos - f->f_ralen;
+ switch (where) {
+ case SEEK_SET:
+ target = offset;
+ break;
+ case SEEK_CUR:
+ target = bufpos + offset;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ if (bufpos <= target && target < filepos) {
+ f->f_raoffset += target - bufpos;
+ f->f_ralen -= target - bufpos;
+ return (target);
+ }
+ }
+
+ /*
+ * If this is a relative seek, we need to correct the offset for
+ * bytes that we have already read but the caller doesn't know
+ * about.
+ */
+ if (where == SEEK_CUR)
+ offset -= f->f_ralen;
+
+ /*
+ * Invalidate the readahead buffer.
+ */
+ f->f_ralen = 0;
+
+ return (f->f_ops->fo_seek)(f, offset, where);
+}
diff --git a/stand/libsa/mips/_setjmp.S b/stand/libsa/mips/_setjmp.S
new file mode 100644
index 0000000..0289b0e
--- /dev/null
+++ b/stand/libsa/mips/_setjmp.S
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/regnum.h>
+#include <machine/asm.h>
+
+#if 0
+#if defined(LIBC_SCCS)
+ .text
+ .asciz "$OpenBSD: _setjmp.S,v 1.6 1996/09/23 21:27:53 imp Exp $"
+#endif /* LIBC_SCCS */
+#endif
+
+/*
+ * C library -- _setjmp, _longjmp
+ *
+ * _longjmp(a,v)
+ * will generate a "return(v)" from
+ * the last call to
+ * _setjmp(a)
+ * by restoring registers from the stack,
+ * The previous signal state is NOT restored.
+ */
+
+LEAF(_setjmp)
+ .set noreorder
+ REG_LI v0, 0xACEDBADE # sigcontext magic number
+ REG_S ra, (2 * SZREG)(a0) # sc_pc = return address
+ REG_S v0, (3 * SZREG)(a0) # saved in sc_regs[0]
+ REG_S s0, ((S0 + 3) * SZREG)(a0)
+ REG_S s1, ((S1 + 3) * SZREG)(a0)
+ REG_S s2, ((S2 + 3) * SZREG)(a0)
+ REG_S s3, ((S3 + 3) * SZREG)(a0)
+ REG_S s4, ((S4 + 3) * SZREG)(a0)
+ REG_S s5, ((S5 + 3) * SZREG)(a0)
+ REG_S s6, ((S6 + 3) * SZREG)(a0)
+ REG_S s7, ((S7 + 3) * SZREG)(a0)
+ REG_S sp, ((SP + 3) * SZREG)(a0)
+ REG_S s8, ((S8 + 3) * SZREG)(a0)
+ j ra
+ move v0, zero
+END(_setjmp)
+
+LEAF(_longjmp)
+#ifdef ABICALLS
+ subu sp, sp, 32
+ .cprestore 16
+#endif
+ .set noreorder
+ REG_L v0, (3 * SZREG)(a0) # get magic number
+ REG_L ra, (2 * SZREG)(a0)
+ bne v0, 0xACEDBADE, botch # jump if error
+
+ addu sp, sp, 32 # does not matter, sanity
+ REG_L s0, ((S0 + 3) * SZREG)(a0)
+ REG_L s1, ((S1 + 3) * SZREG)(a0)
+ REG_L s2, ((S2 + 3) * SZREG)(a0)
+ REG_L s3, ((S3 + 3) * SZREG)(a0)
+ REG_L s4, ((S4 + 3) * SZREG)(a0)
+ REG_L s5, ((S5 + 3) * SZREG)(a0)
+ REG_L s6, ((S6 + 3) * SZREG)(a0)
+ REG_L s7, ((S7 + 3) * SZREG)(a0)
+ REG_L sp, ((SP + 3) * SZREG)(a0)
+ REG_L s8, ((S8 + 3) * SZREG)(a0)
+
+ j ra
+ move v0, a1
+botch:
+ jal _C_LABEL(longjmperror)
+ nop
+ jal _C_LABEL(abort)
+ nop
+END(_longjmp)
diff --git a/stand/libsa/nandfs.c b/stand/libsa/nandfs.c
new file mode 100644
index 0000000..fe3a8ab
--- /dev/null
+++ b/stand/libsa/nandfs.c
@@ -0,0 +1,1061 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stdint.h>
+#include <ufs/ufs/dinode.h>
+#include <fs/nandfs/nandfs_fs.h>
+#include "stand.h"
+#include "string.h"
+#include "zlib.h"
+
+#define DEBUG
+#undef DEBUG
+#ifdef DEBUG
+#define NANDFS_DEBUG(fmt, args...) do { \
+ printf("NANDFS_DEBUG:" fmt "\n", ##args); } while (0)
+#else
+#define NANDFS_DEBUG(fmt, args...)
+#endif
+
+struct nandfs_mdt {
+ uint32_t entries_per_block;
+ uint32_t entries_per_group;
+ uint32_t blocks_per_group;
+ uint32_t groups_per_desc_block; /* desc is super group */
+ uint32_t blocks_per_desc_block; /* desc is super group */
+};
+
+struct bmap_buf {
+ LIST_ENTRY(bmap_buf) list;
+ nandfs_daddr_t blknr;
+ uint64_t *map;
+};
+
+struct nandfs_node {
+ struct nandfs_inode *inode;
+ LIST_HEAD(, bmap_buf) bmap_bufs;
+};
+struct nandfs {
+ int nf_blocksize;
+ int nf_sectorsize;
+ int nf_cpno;
+
+ struct open_file *nf_file;
+ struct nandfs_node *nf_opened_node;
+ u_int nf_offset;
+ uint8_t *nf_buf;
+ int64_t nf_buf_blknr;
+
+ struct nandfs_fsdata *nf_fsdata;
+ struct nandfs_super_block *nf_sb;
+ struct nandfs_segment_summary nf_segsum;
+ struct nandfs_checkpoint nf_checkpoint;
+ struct nandfs_super_root nf_sroot;
+ struct nandfs_node nf_ifile;
+ struct nandfs_node nf_datfile;
+ struct nandfs_node nf_cpfile;
+ struct nandfs_mdt nf_datfile_mdt;
+ struct nandfs_mdt nf_ifile_mdt;
+
+ int nf_nindir[NIADDR];
+};
+
+static int nandfs_open(const char *, struct open_file *);
+static int nandfs_close(struct open_file *);
+static int nandfs_read(struct open_file *, void *, size_t, size_t *);
+static off_t nandfs_seek(struct open_file *, off_t, int);
+static int nandfs_stat(struct open_file *, struct stat *);
+static int nandfs_readdir(struct open_file *, struct dirent *);
+
+static int nandfs_buf_read(struct nandfs *, void **, size_t *);
+static struct nandfs_node *nandfs_lookup_path(struct nandfs *, const char *);
+static int nandfs_read_inode(struct nandfs *, struct nandfs_node *,
+ nandfs_lbn_t, u_int, void *, int);
+static int nandfs_read_blk(struct nandfs *, nandfs_daddr_t, void *, int);
+static int nandfs_bmap_lookup(struct nandfs *, struct nandfs_node *,
+ nandfs_lbn_t, nandfs_daddr_t *, int);
+static int nandfs_get_checkpoint(struct nandfs *, uint64_t,
+ struct nandfs_checkpoint *);
+static nandfs_daddr_t nandfs_vtop(struct nandfs *, nandfs_daddr_t);
+static void nandfs_calc_mdt_consts(int, struct nandfs_mdt *, int);
+static void nandfs_mdt_trans(struct nandfs_mdt *, uint64_t,
+ nandfs_daddr_t *, uint32_t *);
+static int ioread(struct open_file *, off_t, void *, u_int);
+static int nandfs_probe_sectorsize(struct open_file *);
+
+struct fs_ops nandfs_fsops = {
+ "nandfs",
+ nandfs_open,
+ nandfs_close,
+ nandfs_read,
+ null_write,
+ nandfs_seek,
+ nandfs_stat,
+ nandfs_readdir
+};
+
+#define NINDIR(fs) ((fs)->nf_blocksize / sizeof(nandfs_daddr_t))
+
+/* from NetBSD's src/sys/net/if_ethersubr.c */
+static uint32_t
+nandfs_crc32(uint32_t crc, const uint8_t *buf, size_t len)
+{
+ static const uint32_t crctab[] = {
+ 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+ 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+ 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
+ };
+ size_t i;
+
+ crc = crc ^ ~0U;
+ for (i = 0; i < len; i++) {
+ crc ^= buf[i];
+ crc = (crc >> 4) ^ crctab[crc & 0xf];
+ crc = (crc >> 4) ^ crctab[crc & 0xf];
+ }
+ return (crc ^ ~0U);
+}
+
+static int
+nandfs_check_fsdata_crc(struct nandfs_fsdata *fsdata)
+{
+ uint32_t fsdata_crc, comp_crc;
+
+ if (fsdata->f_magic != NANDFS_FSDATA_MAGIC)
+ return (0);
+
+ /* Preserve crc */
+ fsdata_crc = fsdata->f_sum;
+
+ /* Calculate */
+ fsdata->f_sum = (0);
+ comp_crc = nandfs_crc32(0, (uint8_t *)fsdata, fsdata->f_bytes);
+
+ /* Restore */
+ fsdata->f_sum = fsdata_crc;
+
+ /* Check CRC */
+ return (fsdata_crc == comp_crc);
+}
+
+static int
+nandfs_check_superblock_crc(struct nandfs_fsdata *fsdata,
+ struct nandfs_super_block *super)
+{
+ uint32_t super_crc, comp_crc;
+
+ /* Check super block magic */
+ if (super->s_magic != NANDFS_SUPER_MAGIC)
+ return (0);
+
+ /* Preserve CRC */
+ super_crc = super->s_sum;
+
+ /* Calculate */
+ super->s_sum = (0);
+ comp_crc = nandfs_crc32(0, (uint8_t *)super, fsdata->f_sbbytes);
+
+ /* Restore */
+ super->s_sum = super_crc;
+
+ /* Check CRC */
+ return (super_crc == comp_crc);
+}
+
+static int
+nandfs_find_super_block(struct nandfs *fs, struct open_file *f)
+{
+ struct nandfs_super_block *sb;
+ int i, j, n, s;
+ int sectors_to_read, error;
+
+ sb = malloc(fs->nf_sectorsize);
+ if (sb == NULL)
+ return (ENOMEM);
+
+ memset(fs->nf_sb, 0, sizeof(*fs->nf_sb));
+
+ sectors_to_read = (NANDFS_NFSAREAS * fs->nf_fsdata->f_erasesize) /
+ fs->nf_sectorsize;
+ for (i = 0; i < sectors_to_read; i++) {
+ NANDFS_DEBUG("reading i %d offset %d\n", i,
+ i * fs->nf_sectorsize);
+ error = ioread(f, i * fs->nf_sectorsize, (char *)sb,
+ fs->nf_sectorsize);
+ if (error) {
+ NANDFS_DEBUG("error %d\n", error);
+ continue;
+ }
+ n = fs->nf_sectorsize / sizeof(struct nandfs_super_block);
+ s = 0;
+ if ((i * fs->nf_sectorsize) % fs->nf_fsdata->f_erasesize == 0) {
+ if (fs->nf_sectorsize == sizeof(struct nandfs_fsdata))
+ continue;
+ else {
+ s += (sizeof(struct nandfs_fsdata) /
+ sizeof(struct nandfs_super_block));
+ }
+ }
+
+ for (j = s; j < n; j++) {
+ if (!nandfs_check_superblock_crc(fs->nf_fsdata, &sb[j]))
+ continue;
+ NANDFS_DEBUG("magic %x wtime %jd, lastcp 0x%jx\n",
+ sb[j].s_magic, sb[j].s_wtime, sb[j].s_last_cno);
+ if (sb[j].s_last_cno > fs->nf_sb->s_last_cno)
+ memcpy(fs->nf_sb, &sb[j], sizeof(*fs->nf_sb));
+ }
+ }
+
+ free(sb);
+
+ return (fs->nf_sb->s_magic != 0 ? 0 : EINVAL);
+}
+
+static int
+nandfs_find_fsdata(struct nandfs *fs, struct open_file *f)
+{
+ int offset, error, i;
+
+ NANDFS_DEBUG("starting\n");
+
+ offset = 0;
+ for (i = 0; i < 64 * NANDFS_NFSAREAS; i++) {
+ error = ioread(f, offset, (char *)fs->nf_fsdata,
+ sizeof(struct nandfs_fsdata));
+ if (error)
+ return (error);
+ if (fs->nf_fsdata->f_magic == NANDFS_FSDATA_MAGIC) {
+ NANDFS_DEBUG("found at %x, volume %s\n", offset,
+ fs->nf_fsdata->f_volume_name);
+ if (nandfs_check_fsdata_crc(fs->nf_fsdata))
+ break;
+ }
+ offset += fs->nf_sectorsize;
+ }
+
+ return (error);
+}
+
+static int
+nandfs_read_structures(struct nandfs *fs, struct open_file *f)
+{
+ int error;
+
+ error = nandfs_find_fsdata(fs, f);
+ if (error)
+ return (error);
+
+ error = nandfs_find_super_block(fs, f);
+
+ if (error == 0)
+ NANDFS_DEBUG("selected sb with w_time %jd last_pseg %jx\n",
+ fs->nf_sb->s_wtime, fs->nf_sb->s_last_pseg);
+
+ return (error);
+}
+
+static int
+nandfs_mount(struct nandfs *fs, struct open_file *f)
+{
+ int err = 0, level;
+ uint64_t last_pseg;
+
+ fs->nf_fsdata = malloc(sizeof(struct nandfs_fsdata));
+ fs->nf_sb = malloc(sizeof(struct nandfs_super_block));
+
+ err = nandfs_read_structures(fs, f);
+ if (err) {
+ free(fs->nf_fsdata);
+ free(fs->nf_sb);
+ return (err);
+ }
+
+ fs->nf_blocksize = 1 << (fs->nf_fsdata->f_log_block_size + 10);
+
+ NANDFS_DEBUG("using superblock with wtime %jd\n", fs->nf_sb->s_wtime);
+
+ fs->nf_cpno = fs->nf_sb->s_last_cno;
+ last_pseg = fs->nf_sb->s_last_pseg;
+
+ /*
+ * Calculate indirect block levels.
+ */
+ nandfs_daddr_t mult;
+
+ mult = 1;
+ for (level = 0; level < NIADDR; level++) {
+ mult *= NINDIR(fs);
+ fs->nf_nindir[level] = mult;
+ }
+
+ nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_datfile_mdt,
+ fs->nf_fsdata->f_dat_entry_size);
+
+ nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_ifile_mdt,
+ fs->nf_fsdata->f_inode_size);
+
+ err = ioread(f, last_pseg * fs->nf_blocksize, &fs->nf_segsum,
+ sizeof(struct nandfs_segment_summary));
+ if (err) {
+ free(fs->nf_sb);
+ free(fs->nf_fsdata);
+ return (err);
+ }
+
+ err = ioread(f, (last_pseg + fs->nf_segsum.ss_nblocks - 1) *
+ fs->nf_blocksize, &fs->nf_sroot, sizeof(struct nandfs_super_root));
+ if (err) {
+ free(fs->nf_sb);
+ free(fs->nf_fsdata);
+ return (err);
+ }
+
+ fs->nf_datfile.inode = &fs->nf_sroot.sr_dat;
+ LIST_INIT(&fs->nf_datfile.bmap_bufs);
+ fs->nf_cpfile.inode = &fs->nf_sroot.sr_cpfile;
+ LIST_INIT(&fs->nf_cpfile.bmap_bufs);
+
+ err = nandfs_get_checkpoint(fs, fs->nf_cpno, &fs->nf_checkpoint);
+ if (err) {
+ free(fs->nf_sb);
+ free(fs->nf_fsdata);
+ return (err);
+ }
+
+ NANDFS_DEBUG("checkpoint cp_cno=%lld\n", fs->nf_checkpoint.cp_cno);
+ NANDFS_DEBUG("checkpoint cp_inodes_count=%lld\n",
+ fs->nf_checkpoint.cp_inodes_count);
+ NANDFS_DEBUG("checkpoint cp_ifile_inode.i_blocks=%lld\n",
+ fs->nf_checkpoint.cp_ifile_inode.i_blocks);
+
+ fs->nf_ifile.inode = &fs->nf_checkpoint.cp_ifile_inode;
+ LIST_INIT(&fs->nf_ifile.bmap_bufs);
+ return (0);
+}
+
+#define NINDIR(fs) ((fs)->nf_blocksize / sizeof(nandfs_daddr_t))
+
+static int
+nandfs_open(const char *path, struct open_file *f)
+{
+ struct nandfs *fs;
+ struct nandfs_node *node;
+ int err, bsize, level;
+
+ NANDFS_DEBUG("nandfs_open('%s', %p)\n", path, f);
+
+ fs = malloc(sizeof(struct nandfs));
+ f->f_fsdata = fs;
+ fs->nf_file = f;
+
+ bsize = nandfs_probe_sectorsize(f);
+ if (bsize < 0) {
+ printf("Cannot probe medium sector size\n");
+ return (EINVAL);
+ }
+
+ fs->nf_sectorsize = bsize;
+
+ /*
+ * Calculate indirect block levels.
+ */
+ nandfs_daddr_t mult;
+
+ mult = 1;
+ for (level = 0; level < NIADDR; level++) {
+ mult *= NINDIR(fs);
+ fs->nf_nindir[level] = mult;
+ }
+
+ NANDFS_DEBUG("fs %p nf_sectorsize=%x\n", fs, fs->nf_sectorsize);
+
+ err = nandfs_mount(fs, f);
+ if (err) {
+ NANDFS_DEBUG("Cannot mount nandfs: %s\n", strerror(err));
+ return (err);
+ }
+
+ node = nandfs_lookup_path(fs, path);
+ if (node == NULL)
+ return (EINVAL);
+
+ fs->nf_offset = 0;
+ fs->nf_buf = NULL;
+ fs->nf_buf_blknr = -1;
+ fs->nf_opened_node = node;
+ LIST_INIT(&fs->nf_opened_node->bmap_bufs);
+ return (0);
+}
+
+static void
+nandfs_free_node(struct nandfs_node *node)
+{
+ struct bmap_buf *bmap, *tmp;
+
+ free(node->inode);
+ LIST_FOREACH_SAFE(bmap, &node->bmap_bufs, list, tmp) {
+ LIST_REMOVE(bmap, list);
+ free(bmap->map);
+ free(bmap);
+ }
+ free(node);
+}
+
+static int
+nandfs_close(struct open_file *f)
+{
+ struct nandfs *fs = f->f_fsdata;
+
+ NANDFS_DEBUG("nandfs_close(%p)\n", f);
+
+ if (fs->nf_buf != NULL)
+ free(fs->nf_buf);
+
+ nandfs_free_node(fs->nf_opened_node);
+ free(fs->nf_sb);
+ free(fs);
+ return (0);
+}
+
+static int
+nandfs_read(struct open_file *f, void *addr, size_t size, size_t *resid)
+{
+ struct nandfs *fs = (struct nandfs *)f->f_fsdata;
+ size_t csize, buf_size;
+ void *buf;
+ int error = 0;
+
+ NANDFS_DEBUG("nandfs_read(file=%p, addr=%p, size=%d)\n", f, addr, size);
+
+ while (size != 0) {
+ if (fs->nf_offset >= fs->nf_opened_node->inode->i_size)
+ break;
+
+ error = nandfs_buf_read(fs, &buf, &buf_size);
+ if (error)
+ break;
+
+ csize = size;
+ if (csize > buf_size)
+ csize = buf_size;
+
+ bcopy(buf, addr, csize);
+
+ fs->nf_offset += csize;
+ addr = (char *)addr + csize;
+ size -= csize;
+ }
+
+ if (resid)
+ *resid = size;
+ return (error);
+}
+
+static off_t
+nandfs_seek(struct open_file *f, off_t offset, int where)
+{
+ struct nandfs *fs = f->f_fsdata;
+ off_t off;
+ u_int size;
+
+ NANDFS_DEBUG("nandfs_seek(file=%p, offset=%lld, where=%d)\n", f,
+ offset, where);
+
+ size = fs->nf_opened_node->inode->i_size;
+
+ switch (where) {
+ case SEEK_SET:
+ off = 0;
+ break;
+ case SEEK_CUR:
+ off = fs->nf_offset;
+ break;
+ case SEEK_END:
+ off = size;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ off += offset;
+ if (off < 0 || off > size) {
+ errno = EINVAL;
+ return(-1);
+ }
+
+ fs->nf_offset = (u_int)off;
+
+ return (off);
+}
+
+static int
+nandfs_stat(struct open_file *f, struct stat *sb)
+{
+ struct nandfs *fs = f->f_fsdata;
+
+ NANDFS_DEBUG("nandfs_stat(file=%p, stat=%p)\n", f, sb);
+
+ sb->st_size = fs->nf_opened_node->inode->i_size;
+ sb->st_mode = fs->nf_opened_node->inode->i_mode;
+ sb->st_uid = fs->nf_opened_node->inode->i_uid;
+ sb->st_gid = fs->nf_opened_node->inode->i_gid;
+ return (0);
+}
+
+static int
+nandfs_readdir(struct open_file *f, struct dirent *d)
+{
+ struct nandfs *fs = f->f_fsdata;
+ struct nandfs_dir_entry *dirent;
+ void *buf;
+ size_t buf_size;
+
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)\n", f, d);
+
+ if (fs->nf_offset >= fs->nf_opened_node->inode->i_size) {
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) ENOENT\n",
+ f, d);
+ return (ENOENT);
+ }
+
+ if (nandfs_buf_read(fs, &buf, &buf_size)) {
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)"
+ "buf_read failed\n", f, d);
+ return (EIO);
+ }
+
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) moving forward\n",
+ f, d);
+
+ dirent = (struct nandfs_dir_entry *)buf;
+ fs->nf_offset += dirent->rec_len;
+ strncpy(d->d_name, dirent->name, dirent->name_len);
+ d->d_name[dirent->name_len] = '\0';
+ d->d_type = dirent->file_type;
+ return (0);
+}
+
+static int
+nandfs_buf_read(struct nandfs *fs, void **buf_p, size_t *size_p)
+{
+ nandfs_daddr_t blknr, blkoff;
+
+ blknr = fs->nf_offset / fs->nf_blocksize;
+ blkoff = fs->nf_offset % fs->nf_blocksize;
+
+ if (blknr != fs->nf_buf_blknr) {
+ if (fs->nf_buf == NULL)
+ fs->nf_buf = malloc(fs->nf_blocksize);
+
+ if (nandfs_read_inode(fs, fs->nf_opened_node, blknr, 1,
+ fs->nf_buf, 0))
+ return (EIO);
+
+ fs->nf_buf_blknr = blknr;
+ }
+
+ *buf_p = fs->nf_buf + blkoff;
+ *size_p = fs->nf_blocksize - blkoff;
+
+ NANDFS_DEBUG("nandfs_buf_read buf_p=%p size_p=%d\n", *buf_p, *size_p);
+
+ if (*size_p > fs->nf_opened_node->inode->i_size - fs->nf_offset)
+ *size_p = fs->nf_opened_node->inode->i_size - fs->nf_offset;
+
+ return (0);
+}
+
+static struct nandfs_node *
+nandfs_lookup_node(struct nandfs *fs, uint64_t ino)
+{
+ uint64_t blocknr;
+ int entrynr;
+ struct nandfs_inode *buffer;
+ struct nandfs_node *node;
+ struct nandfs_inode *inode;
+
+ NANDFS_DEBUG("nandfs_lookup_node ino=%lld\n", ino);
+
+ if (ino == 0) {
+ printf("nandfs_lookup_node: invalid inode requested\n");
+ return (NULL);
+ }
+
+ buffer = malloc(fs->nf_blocksize);
+ inode = malloc(sizeof(struct nandfs_inode));
+ node = malloc(sizeof(struct nandfs_node));
+
+ nandfs_mdt_trans(&fs->nf_ifile_mdt, ino, &blocknr, &entrynr);
+
+ if (nandfs_read_inode(fs, &fs->nf_ifile, blocknr, 1, buffer, 0))
+ return (NULL);
+
+ memcpy(inode, &buffer[entrynr], sizeof(struct nandfs_inode));
+ node->inode = inode;
+ free(buffer);
+ return (node);
+}
+
+static struct nandfs_node *
+nandfs_lookup_path(struct nandfs *fs, const char *path)
+{
+ struct nandfs_node *node;
+ struct nandfs_dir_entry *dirent;
+ char *namebuf;
+ uint64_t i, done, pinode, inode;
+ int nlinks = 0, counter, len, link_len, nameidx;
+ uint8_t *buffer, *orig;
+ char *strp, *lpath;
+
+ buffer = malloc(fs->nf_blocksize);
+ orig = buffer;
+
+ namebuf = malloc(2 * MAXPATHLEN + 2);
+ strncpy(namebuf, path, MAXPATHLEN);
+ namebuf[MAXPATHLEN] = '\0';
+ done = nameidx = 0;
+ lpath = namebuf;
+
+ /* Get the root inode */
+ node = nandfs_lookup_node(fs, NANDFS_ROOT_INO);
+ inode = NANDFS_ROOT_INO;
+
+ while ((strp = strsep(&lpath, "/")) != NULL) {
+ if (*strp == '\0')
+ continue;
+ if ((node->inode->i_mode & IFMT) != IFDIR) {
+ nandfs_free_node(node);
+ node = NULL;
+ goto out;
+ }
+
+ len = strlen(strp);
+ NANDFS_DEBUG("%s: looking for %s\n", __func__, strp);
+ for (i = 0; i < node->inode->i_blocks; i++) {
+ if (nandfs_read_inode(fs, node, i, 1, orig, 0)) {
+ node = NULL;
+ goto out;
+ }
+
+ buffer = orig;
+ done = counter = 0;
+ while (1) {
+ dirent =
+ (struct nandfs_dir_entry *)(void *)buffer;
+ NANDFS_DEBUG("%s: dirent.name = %s\n",
+ __func__, dirent->name);
+ NANDFS_DEBUG("%s: dirent.rec_len = %d\n",
+ __func__, dirent->rec_len);
+ NANDFS_DEBUG("%s: dirent.inode = %lld\n",
+ __func__, dirent->inode);
+ if (len == dirent->name_len &&
+ (strncmp(strp, dirent->name, len) == 0) &&
+ dirent->inode != 0) {
+ nandfs_free_node(node);
+ node = nandfs_lookup_node(fs,
+ dirent->inode);
+ pinode = inode;
+ inode = dirent->inode;
+ done = 1;
+ break;
+ }
+
+ counter += dirent->rec_len;
+ buffer += dirent->rec_len;
+
+ if (counter == fs->nf_blocksize)
+ break;
+ }
+
+ if (done)
+ break;
+ }
+
+ if (!done) {
+ node = NULL;
+ goto out;
+ }
+
+ NANDFS_DEBUG("%s: %.*s has mode %o\n", __func__,
+ dirent->name_len, dirent->name, node->inode->i_mode);
+
+ if ((node->inode->i_mode & IFMT) == IFLNK) {
+ NANDFS_DEBUG("%s: %.*s is symlink\n",
+ __func__, dirent->name_len, dirent->name);
+ link_len = node->inode->i_size;
+
+ if (++nlinks > MAXSYMLINKS) {
+ nandfs_free_node(node);
+ node = NULL;
+ goto out;
+ }
+
+ if (nandfs_read_inode(fs, node, 0, 1, orig, 0)) {
+ nandfs_free_node(node);
+ node = NULL;
+ goto out;
+ }
+
+ NANDFS_DEBUG("%s: symlink is %.*s\n",
+ __func__, link_len, (char *)orig);
+
+ nameidx = (nameidx == 0) ? MAXPATHLEN + 1 : 0;
+ bcopy((char *)orig, namebuf + nameidx,
+ (unsigned)link_len);
+ if (lpath != NULL) {
+ namebuf[nameidx + link_len++] = '/';
+ strncpy(namebuf + nameidx + link_len, lpath,
+ MAXPATHLEN - link_len);
+ namebuf[nameidx + MAXPATHLEN] = '\0';
+ } else
+ namebuf[nameidx + link_len] = '\0';
+
+ NANDFS_DEBUG("%s: strp=%s, lpath=%s, namebuf0=%s, "
+ "namebuf1=%s, idx=%d\n", __func__, strp, lpath,
+ namebuf + 0, namebuf + MAXPATHLEN + 1, nameidx);
+
+ lpath = namebuf + nameidx;
+
+ nandfs_free_node(node);
+
+ /*
+ * If absolute pathname, restart at root. Otherwise
+ * continue with out parent inode.
+ */
+ inode = (orig[0] == '/') ? NANDFS_ROOT_INO : pinode;
+ node = nandfs_lookup_node(fs, inode);
+ }
+ }
+
+out:
+ free(namebuf);
+ free(orig);
+ return (node);
+}
+
+static int
+nandfs_read_inode(struct nandfs *fs, struct nandfs_node *node,
+ nandfs_daddr_t blknr, u_int nblks, void *buf, int raw)
+{
+ uint64_t *pblks;
+ uint64_t *vblks;
+ u_int i;
+ int error;
+
+ pblks = malloc(nblks * sizeof(uint64_t));
+ vblks = malloc(nblks * sizeof(uint64_t));
+
+ NANDFS_DEBUG("nandfs_read_inode fs=%p node=%p blknr=%lld nblks=%d\n",
+ fs, node, blknr, nblks);
+ for (i = 0; i < nblks; i++) {
+ error = nandfs_bmap_lookup(fs, node, blknr + i, &vblks[i], raw);
+ if (error) {
+ free(pblks);
+ free(vblks);
+ return (error);
+ }
+ if (raw == 0)
+ pblks[i] = nandfs_vtop(fs, vblks[i]);
+ else
+ pblks[i] = vblks[i];
+ }
+
+ for (i = 0; i < nblks; i++) {
+ if (ioread(fs->nf_file, pblks[i] * fs->nf_blocksize, buf,
+ fs->nf_blocksize)) {
+ free(pblks);
+ free(vblks);
+ return (EIO);
+ }
+
+ buf = (void *)((uintptr_t)buf + fs->nf_blocksize);
+ }
+
+ free(pblks);
+ free(vblks);
+ return (0);
+}
+
+static int
+nandfs_read_blk(struct nandfs *fs, nandfs_daddr_t blknr, void *buf, int phys)
+{
+ uint64_t pblknr;
+
+ pblknr = (phys ? blknr : nandfs_vtop(fs, blknr));
+
+ return (ioread(fs->nf_file, pblknr * fs->nf_blocksize, buf,
+ fs->nf_blocksize));
+}
+
+static int
+nandfs_get_checkpoint(struct nandfs *fs, uint64_t cpno,
+ struct nandfs_checkpoint *cp)
+{
+ uint64_t blocknr;
+ int blockoff, cp_per_block, dlen;
+ uint8_t *buf;
+
+ NANDFS_DEBUG("nandfs_get_checkpoint(fs=%p cpno=%lld)\n", fs, cpno);
+
+ buf = malloc(fs->nf_blocksize);
+
+ cpno += NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET - 1;
+ dlen = fs->nf_fsdata->f_checkpoint_size;
+ cp_per_block = fs->nf_blocksize / dlen;
+ blocknr = cpno / cp_per_block;
+ blockoff = (cpno % cp_per_block) * dlen;
+
+ if (nandfs_read_inode(fs, &fs->nf_cpfile, blocknr, 1, buf, 0)) {
+ free(buf);
+ return (EINVAL);
+ }
+
+ memcpy(cp, buf + blockoff, sizeof(struct nandfs_checkpoint));
+ free(buf);
+
+ return (0);
+}
+
+static uint64_t *
+nandfs_get_map(struct nandfs *fs, struct nandfs_node *node, nandfs_daddr_t blknr,
+ int phys)
+{
+ struct bmap_buf *bmap;
+ uint64_t *map;
+
+ LIST_FOREACH(bmap, &node->bmap_bufs, list) {
+ if (bmap->blknr == blknr)
+ return (bmap->map);
+ }
+
+ map = malloc(fs->nf_blocksize);
+ if (nandfs_read_blk(fs, blknr, map, phys)) {
+ free(map);
+ return (NULL);
+ }
+
+ bmap = malloc(sizeof(struct bmap_buf));
+ bmap->blknr = blknr;
+ bmap->map = map;
+
+ LIST_INSERT_HEAD(&node->bmap_bufs, bmap, list);
+
+ NANDFS_DEBUG("%s:(node=%p, map=%p)\n", __func__, node, map);
+ return (map);
+}
+
+static int
+nandfs_bmap_lookup(struct nandfs *fs, struct nandfs_node *node,
+ nandfs_lbn_t lblknr, nandfs_daddr_t *vblknr, int phys)
+{
+ struct nandfs_inode *ino;
+ nandfs_daddr_t ind_block_num;
+ uint64_t *map;
+ int idx;
+ int level;
+
+ ino = node->inode;
+
+ if (lblknr < NDADDR) {
+ *vblknr = ino->i_db[lblknr];
+ return (0);
+ }
+
+ lblknr -= NDADDR;
+
+ /*
+ * nindir[0] = NINDIR
+ * nindir[1] = NINDIR**2
+ * nindir[2] = NINDIR**3
+ * etc
+ */
+ for (level = 0; level < NIADDR; level++) {
+ NANDFS_DEBUG("lblknr=%jx fs->nf_nindir[%d]=%d\n", lblknr, level, fs->nf_nindir[level]);
+ if (lblknr < fs->nf_nindir[level])
+ break;
+ lblknr -= fs->nf_nindir[level];
+ }
+
+ if (level == NIADDR) {
+ /* Block number too high */
+ NANDFS_DEBUG("lblknr %jx too high\n", lblknr);
+ return (EFBIG);
+ }
+
+ ind_block_num = ino->i_ib[level];
+
+ for (; level >= 0; level--) {
+ if (ind_block_num == 0) {
+ *vblknr = 0; /* missing */
+ return (0);
+ }
+
+ twiddle(1);
+ NANDFS_DEBUG("calling get_map with %jx\n", ind_block_num);
+ map = nandfs_get_map(fs, node, ind_block_num, phys);
+ if (map == NULL)
+ return (EIO);
+
+ if (level > 0) {
+ idx = lblknr / fs->nf_nindir[level - 1];
+ lblknr %= fs->nf_nindir[level - 1];
+ } else
+ idx = lblknr;
+
+ ind_block_num = ((nandfs_daddr_t *)map)[idx];
+ }
+
+ *vblknr = ind_block_num;
+
+ return (0);
+}
+
+static nandfs_daddr_t
+nandfs_vtop(struct nandfs *fs, nandfs_daddr_t vblocknr)
+{
+ nandfs_lbn_t blocknr;
+ nandfs_daddr_t pblocknr;
+ int entrynr;
+ struct nandfs_dat_entry *dat;
+
+ dat = malloc(fs->nf_blocksize);
+ nandfs_mdt_trans(&fs->nf_datfile_mdt, vblocknr, &blocknr, &entrynr);
+
+ if (nandfs_read_inode(fs, &fs->nf_datfile, blocknr, 1, dat, 1)) {
+ free(dat);
+ return (0);
+ }
+
+ NANDFS_DEBUG("nandfs_vtop entrynr=%d vblocknr=%lld pblocknr=%lld\n",
+ entrynr, vblocknr, dat[entrynr].de_blocknr);
+
+ pblocknr = dat[entrynr].de_blocknr;
+ free(dat);
+ return (pblocknr);
+}
+
+static void
+nandfs_calc_mdt_consts(int blocksize, struct nandfs_mdt *mdt, int entry_size)
+{
+
+ mdt->entries_per_group = blocksize * 8; /* bits in sector */
+ mdt->entries_per_block = blocksize / entry_size;
+ mdt->blocks_per_group =
+ (mdt->entries_per_group -1) / mdt->entries_per_block + 1 + 1;
+ mdt->groups_per_desc_block =
+ blocksize / sizeof(struct nandfs_block_group_desc);
+ mdt->blocks_per_desc_block =
+ mdt->groups_per_desc_block * mdt->blocks_per_group + 1;
+}
+
+static void
+nandfs_mdt_trans(struct nandfs_mdt *mdt, uint64_t index,
+ nandfs_daddr_t *blocknr, uint32_t *entry_in_block)
+{
+ nandfs_daddr_t blknr;
+ uint64_t group, group_offset, blocknr_in_group;
+ uint64_t desc_block, desc_offset;
+
+ /* Calculate our offset in the file */
+ group = index / mdt->entries_per_group;
+ group_offset = index % mdt->entries_per_group;
+ desc_block = group / mdt->groups_per_desc_block;
+ desc_offset = group % mdt->groups_per_desc_block;
+ blocknr_in_group = group_offset / mdt->entries_per_block;
+
+ /* To descgroup offset */
+ blknr = 1 + desc_block * mdt->blocks_per_desc_block;
+
+ /* To group offset */
+ blknr += desc_offset * mdt->blocks_per_group;
+
+ /* To actual file block */
+ blknr += 1 + blocknr_in_group;
+
+ *blocknr = blknr;
+ *entry_in_block = group_offset % mdt->entries_per_block;
+}
+
+static int
+ioread(struct open_file *f, off_t pos, void *buf, u_int length)
+{
+ void *buffer;
+ int err;
+ int bsize = ((struct nandfs *)f->f_fsdata)->nf_sectorsize;
+ u_int off, nsec;
+
+ off = pos % bsize;
+ pos /= bsize;
+ nsec = howmany(length, bsize);
+
+ NANDFS_DEBUG("pos=%lld length=%d off=%d nsec=%d\n", pos, length,
+ off, nsec);
+
+ buffer = malloc(nsec * bsize);
+
+ err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, pos,
+ nsec * bsize, buffer, NULL);
+
+ memcpy(buf, (void *)((uintptr_t)buffer + off), length);
+ free(buffer);
+
+ return (err);
+}
+
+static int
+nandfs_probe_sectorsize(struct open_file *f)
+{
+ void *buffer;
+ int i, err;
+
+ buffer = malloc(16 * 1024);
+
+ NANDFS_DEBUG("probing for sector size: ");
+
+ for (i = 512; i < (16 * 1024); i <<= 1) {
+ NANDFS_DEBUG("%d ", i);
+ err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 0, i,
+ buffer, NULL);
+
+ if (err == 0) {
+ NANDFS_DEBUG("found");
+ free(buffer);
+ return (i);
+ }
+ }
+
+ free(buffer);
+ NANDFS_DEBUG("not found\n");
+ return (-1);
+}
diff --git a/stand/libsa/net.c b/stand/libsa/net.c
new file mode 100644
index 0000000..175ca5a
--- /dev/null
+++ b/stand/libsa/net.c
@@ -0,0 +1,283 @@
+/* $NetBSD: net.c,v 1.20 1997/12/26 22:41:30 scottr Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: net.c,v 1.9 93/08/06 19:32:15 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <string.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+
+#include <netinet/in_pcb.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include "stand.h"
+#include "net.h"
+
+/*
+ * Send a packet and wait for a reply, with exponential backoff.
+ *
+ * The send routine must return the actual number of bytes written,
+ * or -1 on error.
+ *
+ * The receive routine can indicate success by returning the number of
+ * bytes read; it can return 0 to indicate EOF; it can return -1 with a
+ * non-zero errno to indicate failure; finally, it can return -1 with a
+ * zero errno to indicate it isn't done yet.
+ */
+ssize_t
+sendrecv(struct iodesc *d,
+ ssize_t (*sproc)(struct iodesc *, void *, size_t),
+ void *sbuf, size_t ssize,
+ ssize_t (*rproc)(struct iodesc *, void **, void **, time_t),
+ void **pkt, void **payload)
+{
+ ssize_t cc;
+ time_t t, tmo, tlast;
+ long tleft;
+
+#ifdef NET_DEBUG
+ if (debug)
+ printf("sendrecv: called\n");
+#endif
+
+ tmo = MINTMO;
+ tlast = 0;
+ tleft = 0;
+ t = getsecs();
+ for (;;) {
+ if (tleft <= 0) {
+ if (tmo >= MAXTMO) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ cc = (*sproc)(d, sbuf, ssize);
+ if (cc != -1 && cc < ssize)
+ panic("sendrecv: short write! (%zd < %zd)",
+ cc, ssize);
+
+ tleft = tmo;
+ tmo += MINTMO;
+ if (tmo > MAXTMO)
+ tmo = MAXTMO;
+
+ if (cc == -1) {
+ /* Error on transmit; wait before retrying */
+ while ((getsecs() - t) < tmo)
+ ;
+ tleft = 0;
+ continue;
+ }
+
+ tlast = t;
+ }
+
+ /* Try to get a packet and process it. */
+ cc = (*rproc)(d, pkt, payload, tleft);
+ /* Return on data, EOF or real error. */
+ if (cc != -1 || errno != 0)
+ return (cc);
+
+ /* Timed out or didn't get the packet we're waiting for */
+ t = getsecs();
+ tleft -= t - tlast;
+ tlast = t;
+ }
+}
+
+/*
+ * Like inet_addr() in the C library, but we only accept base-10.
+ * Return values are in network order.
+ */
+n_long
+inet_addr(char *cp)
+{
+ u_long val;
+ int n;
+ char c;
+ u_int parts[4];
+ u_int *pp = parts;
+
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, other=decimal.
+ */
+ val = 0;
+ while ((c = *cp) != '\0') {
+ if (c >= '0' && c <= '9') {
+ val = (val * 10) + (c - '0');
+ cp++;
+ continue;
+ }
+ break;
+ }
+ if (*cp == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16-bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3 || val > 0xff)
+ goto bad;
+ *pp++ = val, cp++;
+ } else
+ break;
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (*cp != '\0')
+ goto bad;
+
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ n = pp - parts + 1;
+ switch (n) {
+
+ case 1: /* a -- 32 bits */
+ break;
+
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffff)
+ goto bad;
+ val |= parts[0] << 24;
+ break;
+
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff)
+ goto bad;
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff)
+ goto bad;
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ }
+
+ return (htonl(val));
+ bad:
+ return (htonl(INADDR_NONE));
+}
+
+char *
+inet_ntoa(struct in_addr ia)
+{
+ return (intoa(ia.s_addr));
+}
+
+/* Similar to inet_ntoa() */
+char *
+intoa(n_long addr)
+{
+ char *cp;
+ u_int byte;
+ int n;
+ static char buf[17]; /* strlen(".255.255.255.255") + 1 */
+
+ addr = ntohl(addr);
+ cp = &buf[sizeof buf];
+ *--cp = '\0';
+
+ n = 4;
+ do {
+ byte = addr & 0xff;
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if (byte > 0) {
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if (byte > 0)
+ *--cp = byte + '0';
+ }
+ *--cp = '.';
+ addr >>= 8;
+ } while (--n > 0);
+
+ return (cp+1);
+}
+
+static char *
+number(char *s, int *n)
+{
+ for (*n = 0; isdigit(*s); s++)
+ *n = (*n * 10) + *s - '0';
+ return s;
+}
+
+n_long
+ip_convertaddr(char *p)
+{
+#define IP_ANYADDR 0
+ n_long addr = 0, n;
+
+ if (p == (char *)0 || *p == '\0')
+ return IP_ANYADDR;
+ p = number(p, &n);
+ addr |= (n << 24) & 0xff000000;
+ if (*p == '\0' || *p++ != '.')
+ return IP_ANYADDR;
+ p = number(p, &n);
+ addr |= (n << 16) & 0xff0000;
+ if (*p == '\0' || *p++ != '.')
+ return IP_ANYADDR;
+ p = number(p, &n);
+ addr |= (n << 8) & 0xff00;
+ if (*p == '\0' || *p++ != '.')
+ return IP_ANYADDR;
+ p = number(p, &n);
+ addr |= n & 0xff;
+ if (*p != '\0')
+ return IP_ANYADDR;
+
+ return htonl(addr);
+}
diff --git a/stand/libsa/net.h b/stand/libsa/net.h
new file mode 100644
index 0000000..f91435c
--- /dev/null
+++ b/stand/libsa/net.h
@@ -0,0 +1,133 @@
+/* $NetBSD: net.h,v 1.10 1995/10/20 00:46:30 cgd Exp $ */
+
+/*
+ * Copyright (c) 1993 Adam Glass
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _STAND_NET_H
+#define _STAND_NET_H
+#ifndef _KERNEL /* XXX - see <netinet/in.h> */
+#undef __IPADDR
+#define __IPADDR(x) htonl((u_int32_t)(x))
+#endif
+
+#include "iodesc.h"
+
+#define BA { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
+
+enum net_proto {
+ NET_NONE,
+ NET_NFS,
+ NET_TFTP
+};
+
+/* Returns true if n_long's on the same net */
+#define SAMENET(a1, a2, m) ((a1.s_addr & m) == (a2.s_addr & m))
+
+#define MACPY(s, d) bcopy((char *)s, (char *)d, 6)
+
+#define MAXTMO 120 /* seconds */
+#define MINTMO 2 /* seconds */
+
+#define FNAME_SIZE 128
+#define IFNAME_SIZE 16
+#define RECV_SIZE 1536 /* XXX delete this */
+
+/*
+ * How much room to leave for headers:
+ * 14: struct ether_header
+ * 20: struct ip
+ * 8: struct udphdr
+ * That's 42 but let's pad it out to 48 bytes.
+ */
+#define ETHER_SIZE 14
+#define HEADER_SIZE 48
+
+extern u_char bcea[6];
+extern char rootpath[FNAME_SIZE];
+extern char bootfile[FNAME_SIZE];
+extern char hostname[FNAME_SIZE];
+extern int hostnamelen;
+extern char domainname[FNAME_SIZE];
+extern int domainnamelen;
+extern int netproto;
+extern char ifname[IFNAME_SIZE];
+
+/* All of these are in network order. */
+extern struct in_addr myip;
+extern struct in_addr rootip;
+extern struct in_addr swapip;
+extern struct in_addr gateip;
+extern struct in_addr nameip;
+extern n_long netmask;
+extern u_int intf_mtu;
+
+extern int debug; /* defined in the machdep sources */
+
+extern struct iodesc sockets[SOPEN_MAX];
+
+/* ARP/RevARP functions: */
+u_char *arpwhohas(struct iodesc *, struct in_addr);
+void arp_reply(struct iodesc *, void *);
+int rarp_getipaddress(int);
+
+/* Link functions: */
+ssize_t sendether(struct iodesc *d, void *pkt, size_t len,
+ u_char *dea, int etype);
+ssize_t readether(struct iodesc *, void **, void **, time_t, uint16_t *);
+
+ssize_t sendip(struct iodesc *, void *, size_t, uint8_t);
+ssize_t readip(struct iodesc *, void **, void **, time_t, uint8_t);
+ssize_t sendudp(struct iodesc *, void *, size_t);
+ssize_t readudp(struct iodesc *, void **, void **, time_t);
+ssize_t sendrecv(struct iodesc *,
+ ssize_t (*)(struct iodesc *, void *, size_t),
+ void *, size_t,
+ ssize_t (*)(struct iodesc *, void **, void **, time_t),
+ void **, void **);
+
+/* bootp/DHCP */
+void bootp(int);
+
+/* Utilities: */
+char *ether_sprintf(u_char *);
+int in_cksum(void *, int);
+char *inet_ntoa(struct in_addr);
+char *intoa(n_long); /* similar to inet_ntoa */
+n_long inet_addr(char *);
+
+/* Machine-dependent functions: */
+time_t getsecs(void);
+#endif /* ! _STAND_NET_H */
diff --git a/stand/libsa/netif.c b/stand/libsa/netif.c
new file mode 100644
index 0000000..105f9a3
--- /dev/null
+++ b/stand/libsa/netif.c
@@ -0,0 +1,316 @@
+/* $NetBSD: netif.c,v 1.10 1997/09/06 13:57:14 drochner Exp $ */
+
+/*
+ * Copyright (c) 1993 Adam Glass
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam Glass.
+ * 4. 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 Adam Glass ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/mount.h>
+#include <string.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+
+struct iodesc sockets[SOPEN_MAX];
+#ifdef NETIF_DEBUG
+int netif_debug = 0;
+#endif
+
+/*
+ * netif_init:
+ *
+ * initialize the generic network interface layer
+ */
+
+void
+netif_init(void)
+{
+ struct netif_driver *drv;
+ int d, i;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("netif_init: called\n");
+#endif
+ for (d = 0; netif_drivers[d]; d++) {
+ drv = netif_drivers[d];
+ for (i = 0; i < drv->netif_nifs; i++)
+ drv->netif_ifs[i].dif_used = 0;
+ }
+}
+
+int
+netif_match(struct netif *nif, void *machdep_hint)
+{
+ struct netif_driver *drv = nif->nif_driver;
+
+#if NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_match (%d)\n", drv->netif_bname,
+ nif->nif_unit, nif->nif_sel);
+#endif
+ return drv->netif_match(nif, machdep_hint);
+}
+
+struct netif *
+netif_select(void *machdep_hint)
+{
+ int d, u, unit_done, s;
+ struct netif_driver *drv;
+ struct netif cur_if;
+ static struct netif best_if;
+ int best_val;
+ int val;
+
+ best_val = 0;
+ best_if.nif_driver = NULL;
+
+ for (d = 0; netif_drivers[d] != NULL; d++) {
+ cur_if.nif_driver = netif_drivers[d];
+ drv = cur_if.nif_driver;
+
+ for (u = 0; u < drv->netif_nifs; u++) {
+ cur_if.nif_unit = u;
+ unit_done = 0;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("\t%s%d:", drv->netif_bname,
+ cur_if.nif_unit);
+#endif
+
+ for (s = 0; s < drv->netif_ifs[u].dif_nsel; s++) {
+ cur_if.nif_sel = s;
+
+ if (drv->netif_ifs[u].dif_used & (1 << s)) {
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf(" [%d used]", s);
+#endif
+ continue;
+ }
+
+ val = netif_match(&cur_if, machdep_hint);
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf(" [%d -> %d]", s, val);
+#endif
+ if (val > best_val) {
+ best_val = val;
+ best_if = cur_if;
+ }
+ }
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("\n");
+#endif
+ }
+ }
+
+ if (best_if.nif_driver == NULL)
+ return NULL;
+
+ best_if.nif_driver->
+ netif_ifs[best_if.nif_unit].dif_used |= (1 << best_if.nif_sel);
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("netif_select: %s%d(%d) wins\n",
+ best_if.nif_driver->netif_bname,
+ best_if.nif_unit, best_if.nif_sel);
+#endif
+ return &best_if;
+}
+
+int
+netif_probe(struct netif *nif, void *machdep_hint)
+{
+ struct netif_driver *drv = nif->nif_driver;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_probe\n", drv->netif_bname, nif->nif_unit);
+#endif
+ return drv->netif_probe(nif, machdep_hint);
+}
+
+void
+netif_attach(struct netif *nif, struct iodesc *desc, void *machdep_hint)
+{
+ struct netif_driver *drv = nif->nif_driver;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_attach\n", drv->netif_bname, nif->nif_unit);
+#endif
+ desc->io_netif = nif;
+#ifdef PARANOID
+ if (drv->netif_init == NULL)
+ panic("%s%d: no netif_init support\n", drv->netif_bname,
+ nif->nif_unit);
+#endif
+ drv->netif_init(desc, machdep_hint);
+ bzero(drv->netif_ifs[nif->nif_unit].dif_stats,
+ sizeof(struct netif_stats));
+}
+
+void
+netif_detach(struct netif *nif)
+{
+ struct netif_driver *drv = nif->nif_driver;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_detach\n", drv->netif_bname, nif->nif_unit);
+#endif
+#ifdef PARANOID
+ if (drv->netif_end == NULL)
+ panic("%s%d: no netif_end support\n", drv->netif_bname,
+ nif->nif_unit);
+#endif
+ drv->netif_end(nif);
+}
+
+ssize_t
+netif_get(struct iodesc *desc, void **pkt, time_t timo)
+{
+#ifdef NETIF_DEBUG
+ struct netif *nif = desc->io_netif;
+#endif
+ struct netif_driver *drv = desc->io_netif->nif_driver;
+ ssize_t rv;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_get\n", drv->netif_bname, nif->nif_unit);
+#endif
+#ifdef PARANOID
+ if (drv->netif_get == NULL)
+ panic("%s%d: no netif_get support\n", drv->netif_bname,
+ nif->nif_unit);
+#endif
+ rv = drv->netif_get(desc, pkt, timo);
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_get returning %d\n", drv->netif_bname,
+ nif->nif_unit, (int)rv);
+#endif
+ return (rv);
+}
+
+ssize_t
+netif_put(struct iodesc *desc, void *pkt, size_t len)
+{
+#ifdef NETIF_DEBUG
+ struct netif *nif = desc->io_netif;
+#endif
+ struct netif_driver *drv = desc->io_netif->nif_driver;
+ ssize_t rv;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_put\n", drv->netif_bname, nif->nif_unit);
+#endif
+#ifdef PARANOID
+ if (drv->netif_put == NULL)
+ panic("%s%d: no netif_put support\n", drv->netif_bname,
+ nif->nif_unit);
+#endif
+ rv = drv->netif_put(desc, pkt, len);
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_put returning %d\n", drv->netif_bname,
+ nif->nif_unit, (int)rv);
+#endif
+ return (rv);
+}
+
+struct iodesc *
+socktodesc(int sock)
+{
+ if (sock >= SOPEN_MAX) {
+ errno = EBADF;
+ return (NULL);
+ }
+ return (&sockets[sock]);
+}
+
+int
+netif_open(void *machdep_hint)
+{
+ int fd;
+ struct iodesc *s;
+ struct netif *nif;
+
+ /* find a free socket */
+ for (fd = 0, s = sockets; fd < SOPEN_MAX; fd++, s++)
+ if (s->io_netif == (struct netif *)0)
+ goto fnd;
+ errno = EMFILE;
+ return (-1);
+
+fnd:
+ bzero(s, sizeof(*s));
+ netif_init();
+ nif = netif_select(machdep_hint);
+ if (!nif)
+ panic("netboot: no interfaces left untried");
+ if (netif_probe(nif, machdep_hint)) {
+ printf("netboot: couldn't probe %s%d\n",
+ nif->nif_driver->netif_bname, nif->nif_unit);
+ errno = EINVAL;
+ return (-1);
+ }
+ netif_attach(nif, s, machdep_hint);
+
+ return (fd);
+}
+
+int
+netif_close(int sock)
+{
+ if (sock >= SOPEN_MAX) {
+ errno = EBADF;
+ return (-1);
+ }
+ netif_detach(sockets[sock].io_netif);
+ sockets[sock].io_netif = (struct netif *)0;
+
+ return (0);
+}
diff --git a/stand/libsa/netif.h b/stand/libsa/netif.h
new file mode 100644
index 0000000..44165ab
--- /dev/null
+++ b/stand/libsa/netif.h
@@ -0,0 +1,65 @@
+/* $NetBSD: netif.h,v 1.4 1995/09/14 23:45:30 pk Exp $ */
+
+/* $FreeBSD$ */
+
+#ifndef __SYS_LIBNETBOOT_NETIF_H
+#define __SYS_LIBNETBOOT_NETIF_H
+#include "iodesc.h"
+
+struct netif_driver {
+ const char *netif_bname;
+ int (*netif_match)(struct netif *, void *);
+ int (*netif_probe)(struct netif *, void *);
+ void (*netif_init)(struct iodesc *, void *);
+ ssize_t (*netif_get)(struct iodesc *, void **, time_t);
+ ssize_t (*netif_put)(struct iodesc *, void *, size_t);
+ void (*netif_end)(struct netif *);
+ struct netif_dif *netif_ifs;
+ int netif_nifs;
+};
+
+struct netif_dif {
+ int dif_unit;
+ int dif_nsel;
+ struct netif_stats *dif_stats;
+ void *dif_private;
+ /* the following fields are used internally by the netif layer */
+ u_long dif_used;
+};
+
+struct netif_stats {
+ int collisions;
+ int collision_error;
+ int missed;
+ int sent;
+ int received;
+ int deferred;
+ int overflow;
+};
+
+struct netif {
+ struct netif_driver *nif_driver;
+ int nif_unit;
+ int nif_sel;
+ void *nif_devdata;
+};
+
+extern struct netif_driver *netif_drivers[]; /* machdep */
+extern int n_netif_drivers;
+
+extern int netif_debug;
+
+void netif_init(void);
+struct netif *netif_select(void *);
+int netif_probe(struct netif *, void *);
+void netif_attach(struct netif *, struct iodesc *, void *);
+void netif_detach(struct netif *);
+ssize_t netif_get(struct iodesc *, void **, time_t);
+ssize_t netif_put(struct iodesc *, void *, size_t);
+
+int netif_open(void *);
+int netif_close(int);
+
+struct iodesc *socktodesc(int);
+
+#endif /* __SYS_LIBNETBOOT_NETIF_H */
diff --git a/stand/libsa/nfs.c b/stand/libsa/nfs.c
new file mode 100644
index 0000000..7e2e1ab
--- /dev/null
+++ b/stand/libsa/nfs.c
@@ -0,0 +1,860 @@
+/* $NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $ */
+
+/*-
+ * Copyright (c) 1993 John Brezak
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stddef.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include "rpcv2.h"
+#include "nfsv2.h"
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+#include "rpc.h"
+
+#define NFS_DEBUGxx
+
+#define NFSREAD_MIN_SIZE 1024
+#define NFSREAD_MAX_SIZE 16384
+
+/* NFSv3 definitions */
+#define NFS_V3MAXFHSIZE 64
+#define NFS_VER3 3
+#define RPCMNT_VER3 3
+#define NFSPROCV3_LOOKUP 3
+#define NFSPROCV3_READLINK 5
+#define NFSPROCV3_READ 6
+#define NFSPROCV3_READDIR 16
+
+typedef struct {
+ uint32_t val[2];
+} n_quad;
+
+struct nfsv3_time {
+ uint32_t nfs_sec;
+ uint32_t nfs_nsec;
+};
+
+struct nfsv3_fattrs {
+ uint32_t fa_type;
+ uint32_t fa_mode;
+ uint32_t fa_nlink;
+ uint32_t fa_uid;
+ uint32_t fa_gid;
+ n_quad fa_size;
+ n_quad fa_used;
+ n_quad fa_rdev;
+ n_quad fa_fsid;
+ n_quad fa_fileid;
+ struct nfsv3_time fa_atime;
+ struct nfsv3_time fa_mtime;
+ struct nfsv3_time fa_ctime;
+};
+
+/*
+ * For NFSv3, the file handle is variable in size, so most fixed sized
+ * structures for arguments won't work. For most cases, a structure
+ * that starts with any fixed size section is followed by an array
+ * that covers the maximum size required.
+ */
+struct nfsv3_readdir_repl {
+ uint32_t errno;
+ uint32_t ok;
+ struct nfsv3_fattrs fa;
+ uint32_t cookiev0;
+ uint32_t cookiev1;
+};
+
+struct nfsv3_readdir_entry {
+ uint32_t follows;
+ uint32_t fid0;
+ uint32_t fid1;
+ uint32_t len;
+ uint32_t nameplus[0];
+};
+
+struct nfs_iodesc {
+ struct iodesc *iodesc;
+ off_t off;
+ uint32_t fhsize;
+ u_char fh[NFS_V3MAXFHSIZE];
+ struct nfsv3_fattrs fa; /* all in network order */
+ uint64_t cookie;
+};
+
+/*
+ * XXX interactions with tftp? See nfswrapper.c for a confusing
+ * issue.
+ */
+int nfs_open(const char *path, struct open_file *f);
+static int nfs_close(struct open_file *f);
+static int nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static int nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t nfs_seek(struct open_file *f, off_t offset, int where);
+static int nfs_stat(struct open_file *f, struct stat *sb);
+static int nfs_readdir(struct open_file *f, struct dirent *d);
+
+struct nfs_iodesc nfs_root_node;
+
+struct fs_ops nfs_fsops = {
+ "nfs",
+ nfs_open,
+ nfs_close,
+ nfs_read,
+ nfs_write,
+ nfs_seek,
+ nfs_stat,
+ nfs_readdir
+};
+
+static int nfs_read_size = NFSREAD_MIN_SIZE;
+
+/*
+ * Improve boot performance over NFS
+ */
+static void
+set_nfs_read_size(void)
+{
+ char *env, *end;
+ char buf[10];
+
+ if ((env = getenv("nfs.read_size")) != NULL) {
+ errno = 0;
+ nfs_read_size = (int)strtol(env, &end, 0);
+ if (errno != 0 || *env == '\0' || *end != '\0') {
+ printf("%s: bad value: \"%s\", defaulting to %d\n",
+ "nfs.read_size", env, NFSREAD_MIN_SIZE);
+ nfs_read_size = NFSREAD_MIN_SIZE;
+ }
+ }
+ if (nfs_read_size < NFSREAD_MIN_SIZE) {
+ printf("%s: bad value: \"%d\", defaulting to %d\n",
+ "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
+ nfs_read_size = NFSREAD_MIN_SIZE;
+ }
+ if (nfs_read_size > NFSREAD_MAX_SIZE) {
+ printf("%s: bad value: \"%d\", defaulting to %d\n",
+ "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
+ nfs_read_size = NFSREAD_MAX_SIZE;
+ }
+ snprintf(buf, sizeof (buf), "%d", nfs_read_size);
+ setenv("nfs.read_size", buf, 1);
+}
+
+/*
+ * Fetch the root file handle (call mount daemon)
+ * Return zero or error number.
+ */
+int
+nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp)
+{
+ void *pkt = NULL;
+ int len;
+ struct args {
+ uint32_t len;
+ char path[FNAME_SIZE];
+ } *args;
+ struct repl {
+ uint32_t errno;
+ uint32_t fhsize;
+ u_char fh[NFS_V3MAXFHSIZE];
+ uint32_t authcnt;
+ uint32_t auth[7];
+ } *repl;
+ struct {
+ uint32_t h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ size_t cc;
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_getrootfh: %s\n", path);
+#endif
+
+ args = &sdata.d;
+
+ bzero(args, sizeof(*args));
+ len = strlen(path);
+ if (len > sizeof(args->path))
+ len = sizeof(args->path);
+ args->len = htonl(len);
+ bcopy(path, args->path, len);
+ len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t));
+
+ cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
+ args, len, (void **)&repl, &pkt);
+ if (cc == -1) {
+ free(pkt);
+ /* errno was set by rpc_call */
+ return (errno);
+ }
+ if (cc < 2 * sizeof (uint32_t)) {
+ free(pkt);
+ return (EBADRPC);
+ }
+ if (repl->errno != 0) {
+ free(pkt);
+ return (ntohl(repl->errno));
+ }
+ *fhlenp = ntohl(repl->fhsize);
+ bcopy(repl->fh, fhp, *fhlenp);
+
+ set_nfs_read_size();
+ free(pkt);
+ return (0);
+}
+
+/*
+ * Lookup a file. Store handle and attributes.
+ * Return zero or error number.
+ */
+int
+nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
+{
+ void *pkt = NULL;
+ int len, rlen, pos;
+ struct args {
+ uint32_t fhsize;
+ uint32_t fhplusname[1 +
+ (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof(uint32_t)];
+ } *args;
+ struct repl {
+ uint32_t errno;
+ uint32_t fhsize;
+ uint32_t fhplusattr[(NFS_V3MAXFHSIZE +
+ 2 * (sizeof(uint32_t) +
+ sizeof(struct nfsv3_fattrs))) / sizeof(uint32_t)];
+ } *repl;
+ struct {
+ uint32_t h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ ssize_t cc;
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("lookupfh: called\n");
+#endif
+
+ args = &sdata.d;
+
+ bzero(args, sizeof(*args));
+ args->fhsize = htonl(d->fhsize);
+ bcopy(d->fh, args->fhplusname, d->fhsize);
+ len = strlen(name);
+ if (len > FNAME_SIZE)
+ len = FNAME_SIZE;
+ pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
+ args->fhplusname[pos++] = htonl(len);
+ bcopy(name, &args->fhplusname[pos], len);
+ len = sizeof(uint32_t) + pos * sizeof(uint32_t) +
+ roundup(len, sizeof(uint32_t));
+
+ cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP,
+ args, len, (void **)&repl, &pkt);
+ if (cc == -1) {
+ free(pkt);
+ return (errno); /* XXX - from rpc_call */
+ }
+ if (cc < 2 * sizeof(uint32_t)) {
+ free(pkt);
+ return (EIO);
+ }
+ if (repl->errno != 0) {
+ free(pkt);
+ /* saerrno.h now matches NFS error numbers. */
+ return (ntohl(repl->errno));
+ }
+ newfd->fhsize = ntohl(repl->fhsize);
+ bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize);
+ pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
+ if (repl->fhplusattr[pos++] == 0) {
+ free(pkt);
+ return (EIO);
+ }
+ bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa));
+ free(pkt);
+ return (0);
+}
+
+#ifndef NFS_NOSYMLINK
+/*
+ * Get the destination of a symbolic link.
+ */
+int
+nfs_readlink(struct nfs_iodesc *d, char *buf)
+{
+ void *pkt = NULL;
+ struct args {
+ uint32_t fhsize;
+ u_char fh[NFS_V3MAXFHSIZE];
+ } *args;
+ struct repl {
+ uint32_t errno;
+ uint32_t ok;
+ struct nfsv3_fattrs fa;
+ uint32_t len;
+ u_char path[NFS_MAXPATHLEN];
+ } *repl;
+ struct {
+ uint32_t h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ ssize_t cc;
+ int rc = 0;
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("readlink: called\n");
+#endif
+
+ args = &sdata.d;
+
+ bzero(args, sizeof(*args));
+ args->fhsize = htonl(d->fhsize);
+ bcopy(d->fh, args->fh, d->fhsize);
+ cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK,
+ args, sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
+ (void **)&repl, &pkt);
+ if (cc == -1)
+ return (errno);
+
+ if (cc < 2 * sizeof(uint32_t)) {
+ rc = EIO;
+ goto done;
+ }
+
+ if (repl->errno != 0) {
+ rc = ntohl(repl->errno);
+ goto done;
+ }
+
+ if (repl->ok == 0) {
+ rc = EIO;
+ goto done;
+ }
+
+ repl->len = ntohl(repl->len);
+ if (repl->len > NFS_MAXPATHLEN) {
+ rc = ENAMETOOLONG;
+ goto done;
+ }
+
+ bcopy(repl->path, buf, repl->len);
+ buf[repl->len] = 0;
+done:
+ free(pkt);
+ return (rc);
+}
+#endif
+
+/*
+ * Read data from a file.
+ * Return transfer count or -1 (and set errno)
+ */
+ssize_t
+nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
+{
+ void *pkt = NULL;
+ struct args {
+ uint32_t fhsize;
+ uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof(uint32_t) + 3];
+ } *args;
+ struct repl {
+ uint32_t errno;
+ uint32_t ok;
+ struct nfsv3_fattrs fa;
+ uint32_t count;
+ uint32_t eof;
+ uint32_t len;
+ u_char data[NFSREAD_MAX_SIZE];
+ } *repl;
+ struct {
+ uint32_t h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ size_t cc;
+ long x;
+ int hlen, rlen, pos;
+
+ args = &sdata.d;
+
+ bzero(args, sizeof(*args));
+ args->fhsize = htonl(d->fhsize);
+ bcopy(d->fh, args->fhoffcnt, d->fhsize);
+ pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
+ args->fhoffcnt[pos++] = 0;
+ args->fhoffcnt[pos++] = htonl((uint32_t)off);
+ if (len > nfs_read_size)
+ len = nfs_read_size;
+ args->fhoffcnt[pos] = htonl((uint32_t)len);
+ hlen = offsetof(struct repl, data[0]);
+
+ cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ,
+ args, 4 * sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
+ (void **)&repl, &pkt);
+ if (cc == -1) {
+ /* errno was already set by rpc_call */
+ return (-1);
+ }
+ if (cc < hlen) {
+ errno = EBADRPC;
+ free(pkt);
+ return (-1);
+ }
+ if (repl->errno != 0) {
+ errno = ntohl(repl->errno);
+ free(pkt);
+ return (-1);
+ }
+ rlen = cc - hlen;
+ x = ntohl(repl->count);
+ if (rlen < x) {
+ printf("nfsread: short packet, %d < %ld\n", rlen, x);
+ errno = EBADRPC;
+ free(pkt);
+ return (-1);
+ }
+ bcopy(repl->data, addr, x);
+ free(pkt);
+ return (x);
+}
+
+/*
+ * Open a file.
+ * return zero or error number
+ */
+int
+nfs_open(const char *upath, struct open_file *f)
+{
+ struct iodesc *desc;
+ struct nfs_iodesc *currfd;
+ char buf[2 * NFS_V3MAXFHSIZE + 3];
+ u_char *fh;
+ char *cp;
+ int i;
+#ifndef NFS_NOSYMLINK
+ struct nfs_iodesc *newfd;
+ struct nfsv3_fattrs *fa;
+ char *ncp;
+ int c;
+ char namebuf[NFS_MAXPATHLEN + 1];
+ char linkbuf[NFS_MAXPATHLEN + 1];
+ int nlinks = 0;
+#endif
+ int error;
+ char *path;
+
+ if (netproto != NET_NFS)
+ return (EINVAL);
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
+#endif
+ if (!rootpath[0]) {
+ printf("no rootpath, no nfs\n");
+ return (ENXIO);
+ }
+
+ if (f->f_dev->dv_type != DEVT_NET)
+ return (EINVAL);
+
+ if (!(desc = socktodesc(*(int *)(f->f_devdata))))
+ return (EINVAL);
+
+ /* Bind to a reserved port. */
+ desc->myport = htons(--rpc_port);
+ desc->destip = rootip;
+ if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
+ nfs_root_node.fh)))
+ return (error);
+ nfs_root_node.fa.fa_type = htonl(NFDIR);
+ nfs_root_node.fa.fa_mode = htonl(0755);
+ nfs_root_node.fa.fa_nlink = htonl(2);
+ nfs_root_node.iodesc = desc;
+
+ fh = &nfs_root_node.fh[0];
+ buf[0] = 'X';
+ cp = &buf[1];
+ for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
+ sprintf(cp, "%02x", fh[i]);
+ sprintf(cp, "X");
+ setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
+ setenv("boot.nfsroot.path", rootpath, 1);
+ setenv("boot.nfsroot.nfshandle", buf, 1);
+ sprintf(buf, "%d", nfs_root_node.fhsize);
+ setenv("boot.nfsroot.nfshandlelen", buf, 1);
+
+ /* Allocate file system specific data structure */
+ currfd = malloc(sizeof(*newfd));
+ if (currfd == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+#ifndef NFS_NOSYMLINK
+ bcopy(&nfs_root_node, currfd, sizeof(*currfd));
+ newfd = NULL;
+
+ cp = path = strdup(upath);
+ if (path == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ while (*cp) {
+ /*
+ * Remove extra separators
+ */
+ while (*cp == '/')
+ cp++;
+
+ if (*cp == '\0')
+ break;
+ /*
+ * Check that current node is a directory.
+ */
+ if (currfd->fa.fa_type != htonl(NFDIR)) {
+ error = ENOTDIR;
+ goto out;
+ }
+
+ /* allocate file system specific data structure */
+ newfd = malloc(sizeof(*newfd));
+ if (newfd == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ newfd->iodesc = currfd->iodesc;
+
+ /*
+ * Get next component of path name.
+ */
+ {
+ int len = 0;
+
+ ncp = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ if (++len > NFS_MAXNAMLEN) {
+ error = ENOENT;
+ goto out;
+ }
+ cp++;
+ }
+ *cp = '\0';
+ }
+
+ /* lookup a file handle */
+ error = nfs_lookupfh(currfd, ncp, newfd);
+ *cp = c;
+ if (error)
+ goto out;
+
+ /*
+ * Check for symbolic link
+ */
+ if (newfd->fa.fa_type == htonl(NFLNK)) {
+ int link_len, len;
+
+ error = nfs_readlink(newfd, linkbuf);
+ if (error)
+ goto out;
+
+ link_len = strlen(linkbuf);
+ len = strlen(cp);
+
+ if (link_len + len > MAXPATHLEN
+ || ++nlinks > MAXSYMLINKS) {
+ error = ENOENT;
+ goto out;
+ }
+
+ bcopy(cp, &namebuf[link_len], len + 1);
+ bcopy(linkbuf, namebuf, link_len);
+
+ /*
+ * If absolute pathname, restart at root.
+ * If relative pathname, restart at parent directory.
+ */
+ cp = namebuf;
+ if (*cp == '/')
+ bcopy(&nfs_root_node, currfd, sizeof(*currfd));
+
+ free(newfd);
+ newfd = NULL;
+
+ continue;
+ }
+
+ free(currfd);
+ currfd = newfd;
+ newfd = NULL;
+ }
+
+ error = 0;
+
+out:
+ free(newfd);
+ free(path);
+#else
+ currfd->iodesc = desc;
+
+ error = nfs_lookupfh(&nfs_root_node, upath, currfd);
+#endif
+ if (!error) {
+ currfd->off = 0;
+ currfd->cookie = 0;
+ f->f_fsdata = (void *)currfd;
+ return (0);
+ }
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_open: %s lookupfh failed: %s\n",
+ path, strerror(error));
+#endif
+ free(currfd);
+
+ return (error);
+}
+
+int
+nfs_close(struct open_file *f)
+{
+ struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_close: fp=0x%lx\n", (u_long)fp);
+#endif
+
+ free(fp);
+ f->f_fsdata = NULL;
+
+ return (0);
+}
+
+/*
+ * read a portion of a file
+ */
+int
+nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
+ ssize_t cc;
+ char *addr = buf;
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_read: size=%lu off=%d\n", (u_long)size,
+ (int)fp->off);
+#endif
+ while ((int)size > 0) {
+ twiddle(16);
+ cc = nfs_readdata(fp, fp->off, (void *)addr, size);
+ /* XXX maybe should retry on certain errors */
+ if (cc == -1) {
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_read: read: %s", strerror(errno));
+#endif
+ return (errno); /* XXX - from nfs_readdata */
+ }
+ if (cc == 0) {
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_read: hit EOF unexpectantly");
+#endif
+ goto ret;
+ }
+ fp->off += cc;
+ addr += cc;
+ size -= cc;
+ }
+ret:
+ if (resid)
+ *resid = size;
+
+ return (0);
+}
+
+/*
+ * Not implemented.
+ */
+int
+nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ return (EROFS);
+}
+
+off_t
+nfs_seek(struct open_file *f, off_t offset, int where)
+{
+ struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
+ uint32_t size = ntohl(d->fa.fa_size.val[1]);
+
+ switch (where) {
+ case SEEK_SET:
+ d->off = offset;
+ break;
+ case SEEK_CUR:
+ d->off += offset;
+ break;
+ case SEEK_END:
+ d->off = size - offset;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ return (d->off);
+}
+
+/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
+int nfs_stat_types[9] = {
+ 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
+
+int
+nfs_stat(struct open_file *f, struct stat *sb)
+{
+ struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
+ uint32_t ftype, mode;
+
+ ftype = ntohl(fp->fa.fa_type);
+ mode = ntohl(fp->fa.fa_mode);
+ mode |= nfs_stat_types[ftype & 7];
+
+ sb->st_mode = mode;
+ sb->st_nlink = ntohl(fp->fa.fa_nlink);
+ sb->st_uid = ntohl(fp->fa.fa_uid);
+ sb->st_gid = ntohl(fp->fa.fa_gid);
+ sb->st_size = ntohl(fp->fa.fa_size.val[1]);
+
+ return (0);
+}
+
+static int
+nfs_readdir(struct open_file *f, struct dirent *d)
+{
+ struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
+ struct nfsv3_readdir_repl *repl;
+ struct nfsv3_readdir_entry *rent;
+ static void *pkt = NULL;
+ static char *buf;
+ static struct nfs_iodesc *pfp = NULL;
+ static uint64_t cookie = 0;
+ size_t cc;
+ int pos, rc;
+
+ struct args {
+ uint32_t fhsize;
+ uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
+ } *args;
+ struct {
+ uint32_t h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+
+ if (fp != pfp || fp->off != cookie) {
+ pfp = NULL;
+ refill:
+ free(pkt);
+ pkt = NULL;
+ args = &sdata.d;
+ bzero(args, sizeof(*args));
+
+ args->fhsize = htonl(fp->fhsize);
+ bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
+ pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
+ args->fhpluscookie[pos++] = htonl(fp->off >> 32);
+ args->fhpluscookie[pos++] = htonl(fp->off);
+ args->fhpluscookie[pos++] = htonl(fp->cookie >> 32);
+ args->fhpluscookie[pos++] = htonl(fp->cookie);
+ args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
+
+ cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
+ args, 6 * sizeof(uint32_t) +
+ roundup(fp->fhsize, sizeof(uint32_t)),
+ (void **)&buf, &pkt);
+ if (cc == -1) {
+ rc = errno;
+ goto err;
+ }
+ repl = (struct nfsv3_readdir_repl *)buf;
+ if (repl->errno != 0) {
+ rc = ntohl(repl->errno);
+ goto err;
+ }
+ pfp = fp;
+ cookie = fp->off;
+ fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) |
+ ntohl(repl->cookiev1);
+ buf += sizeof (struct nfsv3_readdir_repl);
+ }
+ rent = (struct nfsv3_readdir_entry *)buf;
+
+ if (rent->follows == 0) {
+ /* fid0 is actually eof */
+ if (rent->fid0 != 0) {
+ rc = ENOENT;
+ goto err;
+ }
+ goto refill;
+ }
+
+ d->d_namlen = ntohl(rent->len);
+ bcopy(rent->nameplus, d->d_name, d->d_namlen);
+ d->d_name[d->d_namlen] = '\0';
+
+ pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t);
+ fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
+ ntohl(rent->nameplus[pos + 1]);
+ pos += 2;
+ buf = (u_char *)&rent->nameplus[pos];
+ return (0);
+
+err:
+ free(pkt);
+ pkt = NULL;
+ pfp = NULL;
+ cookie = 0;
+ return (rc);
+}
diff --git a/stand/libsa/nfsv2.h b/stand/libsa/nfsv2.h
new file mode 100644
index 0000000..184a47b
--- /dev/null
+++ b/stand/libsa/nfsv2.h
@@ -0,0 +1,121 @@
+/* $FreeBSD$ */
+/* $NetBSD: nfsv2.h,v 1.2 1996/02/26 23:05:23 gwr Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsv2.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * nfs definitions as per the version 2 specs
+ */
+
+/*
+ * Constants as defined in the Sun NFS Version 2 spec.
+ * "NFS: Network File System Protocol Specification" RFC1094
+ */
+
+#define NFS_PORT 2049
+#define NFS_PROG 100003
+#define NFS_VER2 2
+#define NFS_MAXDGRAMDATA 8192
+#define NFS_MAXDATA 32768
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS_FHSIZE 32
+#define NFS_MAXPKTHDR 404
+#define NFS_MAXPACKET (NFS_MAXPKTHDR+NFS_MAXDATA)
+#define NFS_MINPACKET 20
+#define NFS_FABLKSIZE 512 /* Size in bytes of a block wrt fa_blocks */
+#define NFS_READDIRSIZE 1024
+
+/* Stat numbers for rpc returns */
+#define NFS_OK 0
+#define NFSERR_PERM 1
+#define NFSERR_NOENT 2
+#define NFSERR_IO 5
+#define NFSERR_NXIO 6
+#define NFSERR_ACCES 13
+#define NFSERR_EXIST 17
+#define NFSERR_NODEV 19
+#define NFSERR_NOTDIR 20
+#define NFSERR_ISDIR 21
+#define NFSERR_FBIG 27
+#define NFSERR_NOSPC 28
+#define NFSERR_ROFS 30
+#define NFSERR_NAMETOL 63
+#define NFSERR_NOTEMPTY 66
+#define NFSERR_DQUOT 69
+#define NFSERR_STALE 70
+#define NFSERR_WFLUSH 99
+
+/* Sizes in bytes of various nfs rpc components */
+#define NFSX_FH 32
+#define NFSX_UNSIGNED 4
+#define NFSX_FATTR 68
+#define NFSX_SATTR 32
+#define NFSX_STATFS 20
+#define NFSX_COOKIE 4
+
+/* nfs rpc procedure numbers */
+#define NFSPROC_NULL 0
+#define NFSPROC_GETATTR 1
+#define NFSPROC_SETATTR 2
+#define NFSPROC_NOOP 3
+#define NFSPROC_ROOT NFSPROC_NOOP /* Obsolete */
+#define NFSPROC_LOOKUP 4
+#define NFSPROC_READLINK 5
+#define NFSPROC_READ 6
+#define NFSPROC_WRITECACHE NFSPROC_NOOP /* Obsolete */
+#define NFSPROC_WRITE 8
+#define NFSPROC_CREATE 9
+#define NFSPROC_REMOVE 10
+#define NFSPROC_RENAME 11
+#define NFSPROC_LINK 12
+#define NFSPROC_SYMLINK 13
+#define NFSPROC_MKDIR 14
+#define NFSPROC_RMDIR 15
+#define NFSPROC_READDIR 16
+#define NFSPROC_STATFS 17
+
+#define NFS_NPROCS 18
+
+
+/* File types */
+typedef enum {
+ NFNON=0,
+ NFREG=1,
+ NFDIR=2,
+ NFBLK=3,
+ NFCHR=4,
+ NFLNK=5
+} nfstype;
diff --git a/stand/libsa/nullfs.c b/stand/libsa/nullfs.c
new file mode 100644
index 0000000..e4c0b7c
--- /dev/null
+++ b/stand/libsa/nullfs.c
@@ -0,0 +1,105 @@
+/* $NetBSD: nullfs.c,v 1.1 1996/01/13 22:25:39 leo Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)open.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+/*
+ * Null filesystem
+ */
+int null_open (const char *path, struct open_file *f)
+{
+ return EINVAL;
+}
+
+int null_close(struct open_file *f)
+{
+ return 0;
+}
+
+int null_read (struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ return EIO;
+}
+
+int null_write (struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ return EIO;
+}
+
+off_t null_seek (struct open_file *f, off_t offset, int where)
+{
+ errno = EIO;
+ return -1;
+}
+
+int null_stat (struct open_file *f, struct stat *sb)
+{
+ return EIO;
+}
+
+int null_readdir(struct open_file *f, struct dirent *d)
+{
+ return EIO;
+}
diff --git a/stand/libsa/open.c b/stand/libsa/open.c
new file mode 100644
index 0000000..214e51b
--- /dev/null
+++ b/stand/libsa/open.c
@@ -0,0 +1,159 @@
+/* $NetBSD: open.c,v 1.16 1997/01/28 09:41:03 pk Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)open.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+struct fs_ops *exclusive_file_system;
+
+struct open_file files[SOPEN_MAX];
+
+static int
+o_gethandle(void)
+{
+ int fd;
+
+ for (fd = 0; fd < SOPEN_MAX; fd++)
+ if (files[fd].f_flags == 0)
+ return(fd);
+ return(-1);
+}
+
+static void
+o_rainit(struct open_file *f)
+{
+ f->f_rabuf = malloc(SOPEN_RASIZE);
+ f->f_ralen = 0;
+ f->f_raoffset = 0;
+}
+
+int
+open(const char *fname, int mode)
+{
+ struct fs_ops *fs;
+ struct open_file *f;
+ int fd, i, error, besterror;
+ const char *file;
+
+ if ((fd = o_gethandle()) == -1) {
+ errno = EMFILE;
+ return(-1);
+ }
+
+ f = &files[fd];
+ f->f_flags = mode + 1;
+ f->f_dev = (struct devsw *)0;
+ f->f_ops = (struct fs_ops *)0;
+ f->f_offset = 0;
+ f->f_devdata = NULL;
+ file = (char *)0;
+
+ if (exclusive_file_system != NULL) {
+ fs = exclusive_file_system;
+ error = (fs->fo_open)(fname, f);
+ if (error == 0)
+ goto ok;
+ goto err;
+ }
+
+ error = devopen(f, fname, &file);
+ if (error ||
+ (((f->f_flags & F_NODEV) == 0) && f->f_dev == (struct devsw *)0))
+ goto err;
+
+ /* see if we opened a raw device; otherwise, 'file' is the file name. */
+ if (file == (char *)0 || *file == '\0') {
+ f->f_flags |= F_RAW;
+ f->f_rabuf = NULL;
+ return (fd);
+ }
+
+ /* pass file name to the different filesystem open routines */
+ besterror = ENOENT;
+ for (i = 0; file_system[i] != NULL; i++) {
+ fs = file_system[i];
+ error = (fs->fo_open)(file, f);
+ if (error == 0)
+ goto ok;
+ if (error != EINVAL)
+ besterror = error;
+ }
+ error = besterror;
+
+ fail:
+ if ((f->f_flags & F_NODEV) == 0 && f->f_dev != NULL)
+ f->f_dev->dv_close(f);
+ if (error)
+ devclose(f);
+
+ err:
+ f->f_flags = 0;
+ errno = error;
+ return (-1);
+
+ ok:
+ f->f_ops = fs;
+ o_rainit(f);
+ return (fd);
+}
diff --git a/stand/libsa/pager.c b/stand/libsa/pager.c
new file mode 100644
index 0000000..a966b0b
--- /dev/null
+++ b/stand/libsa/pager.c
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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.
+ */
+/*
+ * Simple paged-output and paged-viewing functions
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+#include <string.h>
+
+static int p_maxlines = -1;
+static int p_freelines;
+
+static char *pager_prompt1 = " --more-- <space> page down <enter> line down <q> quit ";
+static char *pager_blank = " ";
+
+/*
+ * 'open' the pager
+ */
+void
+pager_open(void)
+{
+ int nlines;
+ char *cp, *lp;
+
+ nlines = 24; /* sensible default */
+ if ((cp = getenv("LINES")) != NULL) {
+ nlines = strtol(cp, &lp, 0);
+ }
+
+ p_maxlines = nlines - 1;
+ if (p_maxlines < 1)
+ p_maxlines = 1;
+ p_freelines = p_maxlines;
+}
+
+/*
+ * 'close' the pager
+ */
+void
+pager_close(void)
+{
+ p_maxlines = -1;
+}
+
+/*
+ * Emit lines to the pager; may not return until the user
+ * has responded to the prompt.
+ *
+ * Will return nonzero if the user enters 'q' or 'Q' at the prompt.
+ *
+ * XXX note that this watches outgoing newlines (and eats them), but
+ * does not handle wrap detection (req. count of columns).
+ */
+
+int
+pager_output(const char *cp)
+{
+ int action;
+
+ if (cp == NULL)
+ return(0);
+
+ for (;;) {
+ if (*cp == 0)
+ return(0);
+
+ putchar(*cp); /* always emit character */
+
+ if (*(cp++) == '\n') { /* got a newline? */
+ p_freelines--;
+ if (p_freelines <= 0) {
+ printf("%s", pager_prompt1);
+ action = 0;
+ while (action == 0) {
+ switch(getchar()) {
+ case '\r':
+ case '\n':
+ p_freelines = 1;
+ action = 1;
+ break;
+ case ' ':
+ p_freelines = p_maxlines;
+ action = 1;
+ break;
+ case 'q':
+ case 'Q':
+ action = 2;
+ break;
+ default:
+ break;
+ }
+ }
+ printf("\r%s\r", pager_blank);
+ if (action == 2)
+ return(1);
+ }
+ }
+ }
+}
+
+/*
+ * Display from (fd).
+ */
+int
+pager_file(const char *fname)
+{
+ char buf[80];
+ size_t hmuch;
+ int fd;
+ int result;
+
+ if ((fd = open(fname, O_RDONLY)) == -1) {
+ printf("can't open '%s': %s\n", fname, strerror(errno));
+ return(-1);
+ }
+
+ for (;;) {
+ hmuch = read(fd, buf, sizeof(buf) - 1);
+ if (hmuch == -1) {
+ result = -1;
+ break;
+ }
+ if (hmuch == 0) {
+ result = 0;
+ break;
+ }
+ buf[hmuch] = 0;
+ if (pager_output(buf)) {
+ result = 1;
+ break;
+ }
+ }
+ close(fd);
+ return(result);
+}
diff --git a/stand/libsa/panic.c b/stand/libsa/panic.c
new file mode 100644
index 0000000..6e4c76d
--- /dev/null
+++ b/stand/libsa/panic.c
@@ -0,0 +1,59 @@
+/*
+ * $NetBSD: panic.c,v 1.2 1997/03/22 01:48:36 thorpej Exp $
+ */
+/*-
+ * Copyright (c) 1996
+ * Matthias Drochner. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by Matthias Drochner.
+ * 4. 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.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <machine/stdarg.h>
+
+extern void exit(int) __dead2;
+
+void
+panic(const char *fmt,...)
+{
+ va_list ap;
+
+ printf("panic: ");
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+
+ printf("--> Press a key on the console to reboot <--\n");
+ getchar();
+ printf("Rebooting...\n");
+ exit(1);
+}
diff --git a/stand/libsa/pkgfs.c b/stand/libsa/pkgfs.c
new file mode 100644
index 0000000..fda7f60
--- /dev/null
+++ b/stand/libsa/pkgfs.c
@@ -0,0 +1,791 @@
+/*-
+ * Copyright (c) 2007-2014, Juniper Networks, Inc.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+#include <sys/stat.h>
+#include <sys/stdint.h>
+#include <string.h>
+#include <zlib.h>
+
+#ifdef PKGFS_DEBUG
+#define DBG(x) printf x
+#else
+#define DBG(x)
+#endif
+
+static int pkg_open(const char *, struct open_file *);
+static int pkg_close(struct open_file *);
+static int pkg_read(struct open_file *, void *, size_t, size_t *);
+static off_t pkg_seek(struct open_file *, off_t, int);
+static int pkg_stat(struct open_file *, struct stat *);
+static int pkg_readdir(struct open_file *, struct dirent *);
+
+struct fs_ops pkgfs_fsops = {
+ "pkg",
+ pkg_open,
+ pkg_close,
+ pkg_read,
+ null_write,
+ pkg_seek,
+ pkg_stat,
+ pkg_readdir
+};
+
+#define PKG_BUFSIZE 512
+#define PKG_MAXCACHESZ 4096
+
+#define PKG_FILEEXT ".tgz"
+
+/*
+ * Layout of POSIX 'ustar' header.
+ */
+struct ustar_hdr {
+ char ut_name[100];
+ char ut_mode[8];
+ char ut_uid[8];
+ char ut_gid[8];
+ char ut_size[12];
+ char ut_mtime[12];
+ char ut_checksum[8];
+ char ut_typeflag[1];
+ char ut_linkname[100];
+ char ut_magic[6]; /* For POSIX: "ustar\0" */
+ char ut_version[2]; /* For POSIX: "00" */
+ char ut_uname[32];
+ char ut_gname[32];
+ char ut_rdevmajor[8];
+ char ut_rdevminor[8];
+ union {
+ struct {
+ char prefix[155];
+ } posix;
+ struct {
+ char atime[12];
+ char ctime[12];
+ char offset[12];
+ char longnames[4];
+ char unused[1];
+ struct gnu_sparse {
+ char offset[12];
+ char numbytes[12];
+ } sparse[4];
+ char isextended[1];
+ char realsize[12];
+ } gnu;
+ } u;
+ u_char __padding[12];
+};
+
+struct package;
+
+struct tarfile
+{
+ struct package *tf_pkg;
+ struct tarfile *tf_next;
+ struct ustar_hdr tf_hdr;
+ off_t tf_ofs;
+ off_t tf_size;
+ off_t tf_fp;
+ size_t tf_cachesz;
+ void *tf_cache;
+};
+
+struct package
+{
+ struct package *pkg_chain;
+ int pkg_fd;
+ off_t pkg_ofs;
+ z_stream pkg_zs;
+ struct tarfile *pkg_first;
+ struct tarfile *pkg_last;
+ u_char pkg_buf[PKG_BUFSIZE];
+};
+
+static struct package *package = NULL;
+
+static int new_package(int, struct package **);
+
+void
+pkgfs_cleanup(void)
+{
+ struct package *chain;
+ struct tarfile *tf, *tfn;
+
+ while (package != NULL) {
+ inflateEnd(&package->pkg_zs);
+ close(package->pkg_fd);
+
+ tf = package->pkg_first;
+ while (tf != NULL) {
+ tfn = tf->tf_next;
+ if (tf->tf_cachesz > 0)
+ free(tf->tf_cache);
+ free(tf);
+ tf = tfn;
+ }
+
+ chain = package->pkg_chain;
+ free(package);
+ package = chain;
+ }
+}
+
+int
+pkgfs_init(const char *pkgname, struct fs_ops *proto)
+{
+ struct package *pkg;
+ int error, fd;
+
+ if (proto != &pkgfs_fsops)
+ pkgfs_cleanup();
+
+ exclusive_file_system = proto;
+
+ fd = open(pkgname, O_RDONLY);
+
+ exclusive_file_system = NULL;
+
+ if (fd == -1)
+ return (errno);
+
+ error = new_package(fd, &pkg);
+ if (error) {
+ close(fd);
+ return (error);
+ }
+
+ if (pkg == NULL)
+ return (EDOOFUS);
+
+ pkg->pkg_chain = package;
+ package = pkg;
+ exclusive_file_system = &pkgfs_fsops;
+ return (0);
+}
+
+static int get_mode(struct tarfile *);
+static int get_zipped(struct package *, void *, size_t);
+static int new_package(int, struct package **);
+static struct tarfile *scan_tarfile(struct package *, struct tarfile *);
+
+static int
+pkg_open(const char *fn, struct open_file *f)
+{
+ struct tarfile *tf;
+
+ if (fn == NULL || f == NULL)
+ return (EINVAL);
+
+ if (package == NULL)
+ return (ENXIO);
+
+ /*
+ * We can only read from a package, so reject request to open
+ * for write-only or read-write.
+ */
+ if (f->f_flags != F_READ)
+ return (EPERM);
+
+ /*
+ * Scan the file headers for the named file. We stop scanning
+ * at the first filename that has the .pkg extension. This is
+ * a package within a package. We assume we have all the files
+ * we need up-front and without having to dig within nested
+ * packages.
+ *
+ * Note that we preserve streaming properties as much as possible.
+ */
+ while (*fn == '/')
+ fn++;
+
+ /*
+ * Allow opening of the root directory for use by readdir()
+ * to support listing files in the package.
+ */
+ if (*fn == '\0') {
+ f->f_fsdata = NULL;
+ return (0);
+ }
+
+ tf = scan_tarfile(package, NULL);
+ while (tf != NULL) {
+ if (strcmp(fn, tf->tf_hdr.ut_name) == 0) {
+ f->f_fsdata = tf;
+ tf->tf_fp = 0; /* Reset the file pointer. */
+ return (0);
+ }
+ tf = scan_tarfile(package, tf);
+ }
+ return (errno);
+}
+
+static int
+pkg_close(struct open_file *f)
+{
+ struct tarfile *tf;
+
+ tf = (struct tarfile *)f->f_fsdata;
+ if (tf == NULL)
+ return (0);
+
+ /*
+ * Free up the cache if we read all of the file.
+ */
+ if (tf->tf_fp == tf->tf_size && tf->tf_cachesz > 0) {
+ free(tf->tf_cache);
+ tf->tf_cachesz = 0;
+ }
+ return (0);
+}
+
+static int
+pkg_read(struct open_file *f, void *buf, size_t size, size_t *res)
+{
+ struct tarfile *tf;
+ char *p;
+ off_t fp;
+ size_t sz;
+
+ tf = (struct tarfile *)f->f_fsdata;
+ if (tf == NULL) {
+ if (res != NULL)
+ *res = size;
+ return (EBADF);
+ }
+
+ fp = tf->tf_fp;
+ p = buf;
+ sz = 0;
+ while (size > 0) {
+ sz = tf->tf_size - fp;
+ if (fp < tf->tf_cachesz && tf->tf_cachesz < tf->tf_size)
+ sz = tf->tf_cachesz - fp;
+ if (size < sz)
+ sz = size;
+ if (sz == 0)
+ break;
+
+ if (fp < tf->tf_cachesz) {
+ /* Satisfy the request from cache. */
+ memcpy(p, tf->tf_cache + fp, sz);
+ fp += sz;
+ p += sz;
+ size -= sz;
+ continue;
+ }
+
+ if (get_zipped(tf->tf_pkg, p, sz) == -1) {
+ sz = -1;
+ break;
+ }
+
+ fp += sz;
+ p += sz;
+ size -= sz;
+
+ if (tf->tf_cachesz != 0)
+ continue;
+
+ tf->tf_cachesz = (sz <= PKG_MAXCACHESZ) ? sz : PKG_MAXCACHESZ;
+ tf->tf_cache = malloc(tf->tf_cachesz);
+ if (tf->tf_cache != NULL)
+ memcpy(tf->tf_cache, buf, tf->tf_cachesz);
+ else
+ tf->tf_cachesz = 0;
+ }
+
+ tf->tf_fp = fp;
+ if (res != NULL)
+ *res = size;
+ return ((sz == -1) ? errno : 0);
+}
+
+static off_t
+pkg_seek(struct open_file *f, off_t ofs, int whence)
+{
+ char buf[512];
+ struct tarfile *tf;
+ off_t delta;
+ size_t sz, res;
+ int error;
+
+ tf = (struct tarfile *)f->f_fsdata;
+ if (tf == NULL) {
+ errno = EBADF;
+ return (-1);
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ delta = ofs - tf->tf_fp;
+ break;
+ case SEEK_CUR:
+ delta = ofs;
+ break;
+ case SEEK_END:
+ delta = tf->tf_size - tf->tf_fp + ofs;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (delta < 0) {
+ DBG(("%s: negative file seek (%jd)\n", __func__,
+ (intmax_t)delta));
+ errno = ESPIPE;
+ return (-1);
+ }
+
+ while (delta > 0 && tf->tf_fp < tf->tf_size) {
+ sz = (delta > sizeof(buf)) ? sizeof(buf) : delta;
+ error = pkg_read(f, buf, sz, &res);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+ delta -= sz - res;
+ }
+
+ return (tf->tf_fp);
+}
+
+static int
+pkg_stat(struct open_file *f, struct stat *sb)
+{
+ struct tarfile *tf;
+
+ tf = (struct tarfile *)f->f_fsdata;
+ if (tf == NULL)
+ return (EBADF);
+ memset(sb, 0, sizeof(*sb));
+ sb->st_mode = get_mode(tf);
+ sb->st_size = tf->tf_size;
+ sb->st_blocks = (tf->tf_size + 511) / 512;
+ return (0);
+}
+
+static int
+pkg_readdir(struct open_file *f, struct dirent *d)
+{
+ struct tarfile *tf;
+
+ tf = (struct tarfile *)f->f_fsdata;
+ if (tf != NULL)
+ return (EBADF);
+
+ tf = scan_tarfile(package, NULL);
+ if (tf == NULL)
+ return (ENOENT);
+
+ d->d_fileno = 0;
+ d->d_reclen = sizeof(*d);
+ d->d_type = DT_REG;
+ memcpy(d->d_name, tf->tf_hdr.ut_name, sizeof(d->d_name));
+ return (0);
+}
+
+/*
+ * Low-level support functions.
+ */
+
+static int
+get_byte(struct package *pkg, off_t *op)
+{
+ int c;
+
+ if (pkg->pkg_zs.avail_in == 0) {
+ c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE);
+ if (c <= 0)
+ return (-1);
+ pkg->pkg_zs.avail_in = c;
+ pkg->pkg_zs.next_in = pkg->pkg_buf;
+ }
+
+ c = *pkg->pkg_zs.next_in;
+ pkg->pkg_zs.next_in++;
+ pkg->pkg_zs.avail_in--;
+ (*op)++;
+ return (c);
+}
+
+static int
+get_zipped(struct package *pkg, void *buf, size_t bufsz)
+{
+ int c;
+
+ pkg->pkg_zs.next_out = buf;
+ pkg->pkg_zs.avail_out = bufsz;
+
+ while (pkg->pkg_zs.avail_out) {
+ if (pkg->pkg_zs.avail_in == 0) {
+ c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE);
+ if (c <= 0) {
+ errno = EIO;
+ return (-1);
+ }
+ pkg->pkg_zs.avail_in = c;
+ pkg->pkg_zs.next_in = pkg->pkg_buf;
+ }
+
+ c = inflate(&pkg->pkg_zs, Z_SYNC_FLUSH);
+ if (c != Z_OK && c != Z_STREAM_END) {
+ errno = EIO;
+ return (-1);
+ }
+ }
+
+ pkg->pkg_ofs += bufsz;
+ return (0);
+}
+
+static int
+cache_data(struct tarfile *tf)
+{
+ struct package *pkg;
+ size_t sz;
+
+ if (tf == NULL) {
+ DBG(("%s: no file to cache data for?\n", __func__));
+ errno = EINVAL;
+ return (-1);
+ }
+
+ pkg = tf->tf_pkg;
+ if (pkg == NULL) {
+ DBG(("%s: no package associated with file?\n", __func__));
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (tf->tf_ofs != pkg->pkg_ofs) {
+ DBG(("%s: caching after partial read of file %s?\n",
+ __func__, tf->tf_hdr.ut_name));
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* We don't cache everything... */
+ if (tf->tf_size > PKG_MAXCACHESZ) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ /* All files are padded to a multiple of 512 bytes. */
+ sz = (tf->tf_size + 0x1ff) & ~0x1ff;
+
+ tf->tf_cache = malloc(sz);
+ if (tf->tf_cache == NULL) {
+ DBG(("%s: could not allocate %d bytes\n", __func__, (int)sz));
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ tf->tf_cachesz = sz;
+ return (get_zipped(pkg, tf->tf_cache, sz));
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static off_t
+pkg_atol8(const char *p, unsigned char_cnt)
+{
+ int64_t l, limit, last_digit_limit;
+ int digit, sign, base;
+
+ base = 8;
+ limit = INT64_MAX / base;
+ last_digit_limit = INT64_MAX % base;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '-') {
+ sign = -1;
+ p++;
+ } else
+ sign = 1;
+
+ l = 0;
+ digit = *p - '0';
+ while (digit >= 0 && digit < base && char_cnt-- > 0) {
+ if (l>limit || (l == limit && digit > last_digit_limit)) {
+ l = UINT64_MAX; /* Truncate on overflow. */
+ break;
+ }
+ l = (l * base) + digit;
+ digit = *++p - '0';
+ }
+ return (sign < 0) ? -l : l;
+}
+
+/*
+ * Parse a base-256 integer. This is just a straight signed binary
+ * value in big-endian order, except that the high-order bit is
+ * ignored. Remember that "int64_t" may or may not be exactly 64
+ * bits; the implementation here tries to avoid making any assumptions
+ * about the actual size of an int64_t. It does assume we're using
+ * twos-complement arithmetic, though.
+ */
+static int64_t
+pkg_atol256(const char *_p, unsigned char_cnt)
+{
+ int64_t l, upper_limit, lower_limit;
+ const unsigned char *p = (const unsigned char *)_p;
+
+ upper_limit = INT64_MAX / 256;
+ lower_limit = INT64_MIN / 256;
+
+ /* Pad with 1 or 0 bits, depending on sign. */
+ if ((0x40 & *p) == 0x40)
+ l = (int64_t)-1;
+ else
+ l = 0;
+ l = (l << 6) | (0x3f & *p++);
+ while (--char_cnt > 0) {
+ if (l > upper_limit) {
+ l = INT64_MAX; /* Truncate on overflow */
+ break;
+ } else if (l < lower_limit) {
+ l = INT64_MIN;
+ break;
+ }
+ l = (l << 8) | (0xff & (int64_t)*p++);
+ }
+ return (l);
+}
+
+static off_t
+pkg_atol(const char *p, unsigned char_cnt)
+{
+ /*
+ * Technically, GNU pkg considers a field to be in base-256
+ * only if the first byte is 0xff or 0x80.
+ */
+ if (*p & 0x80)
+ return (pkg_atol256(p, char_cnt));
+ return (pkg_atol8(p, char_cnt));
+}
+
+static int
+get_mode(struct tarfile *tf)
+{
+ return (pkg_atol(tf->tf_hdr.ut_mode, sizeof(tf->tf_hdr.ut_mode)));
+}
+
+/* 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 */
+
+static int
+new_package(int fd, struct package **pp)
+{
+ struct package *pkg;
+ off_t ofs;
+ int flags, i, error;
+
+ pkg = malloc(sizeof(*pkg));
+ if (pkg == NULL)
+ return (ENOMEM);
+
+ bzero(pkg, sizeof(*pkg));
+ pkg->pkg_fd = fd;
+
+ /*
+ * Parse the header.
+ */
+ error = EFTYPE;
+ ofs = 0;
+
+ /* Check megic. */
+ if (get_byte(pkg, &ofs) != 0x1f || get_byte(pkg, &ofs) != 0x8b)
+ goto fail;
+ /* Check method. */
+ if (get_byte(pkg, &ofs) != Z_DEFLATED)
+ goto fail;
+ /* Check flags. */
+ flags = get_byte(pkg, &ofs);
+ if (flags & RESERVED)
+ goto fail;
+
+ /* Skip time, xflags and OS code. */
+ for (i = 0; i < 6; i++) {
+ if (get_byte(pkg, &ofs) == -1)
+ goto fail;
+ }
+
+ /* Skip extra field. */
+ if (flags & EXTRA_FIELD) {
+ i = (get_byte(pkg, &ofs) & 0xff) |
+ ((get_byte(pkg, &ofs) << 8) & 0xff);
+ while (i-- > 0) {
+ if (get_byte(pkg, &ofs) == -1)
+ goto fail;
+ }
+ }
+
+ /* Skip original file name. */
+ if (flags & ORIG_NAME) {
+ do {
+ i = get_byte(pkg, &ofs);
+ } while (i != 0 && i != -1);
+ if (i == -1)
+ goto fail;
+ }
+
+ /* Print the comment if it's there. */
+ if (flags & COMMENT) {
+ while (1) {
+ i = get_byte(pkg, &ofs);
+ if (i == -1)
+ goto fail;
+ if (i == 0)
+ break;
+ putchar(i);
+ }
+ }
+
+ /* Skip the CRC. */
+ if (flags & HEAD_CRC) {
+ if (get_byte(pkg, &ofs) == -1)
+ goto fail;
+ if (get_byte(pkg, &ofs) == -1)
+ goto fail;
+ }
+
+ /*
+ * Done parsing the ZIP header. Spkgt the inflation engine.
+ */
+ error = inflateInit2(&pkg->pkg_zs, -15);
+ if (error != Z_OK)
+ goto fail;
+
+ *pp = pkg;
+ return (0);
+
+ fail:
+ free(pkg);
+ return (error);
+}
+
+static struct tarfile *
+scan_tarfile(struct package *pkg, struct tarfile *last)
+{
+ char buf[512];
+ struct tarfile *cur;
+ off_t ofs;
+ size_t sz;
+
+ cur = (last != NULL) ? last->tf_next : pkg->pkg_first;
+ if (cur == NULL) {
+ ofs = (last != NULL) ? last->tf_ofs + last->tf_size :
+ pkg->pkg_ofs;
+ ofs = (ofs + 0x1ff) & ~0x1ff;
+
+ /* Check if we've reached EOF. */
+ if (ofs < pkg->pkg_ofs) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+
+ if (ofs != pkg->pkg_ofs) {
+ if (last != NULL && pkg->pkg_ofs == last->tf_ofs) {
+ if (cache_data(last) == -1)
+ return (NULL);
+ } else {
+ sz = ofs - pkg->pkg_ofs;
+ while (sz != 0) {
+ if (sz > sizeof(buf))
+ sz = sizeof(buf);
+ if (get_zipped(pkg, buf, sz) == -1)
+ return (NULL);
+ sz = ofs - pkg->pkg_ofs;
+ }
+ }
+ }
+
+ cur = malloc(sizeof(*cur));
+ if (cur == NULL)
+ return (NULL);
+ memset(cur, 0, sizeof(*cur));
+ cur->tf_pkg = pkg;
+
+ while (1) {
+ if (get_zipped(pkg, &cur->tf_hdr,
+ sizeof(cur->tf_hdr)) == -1) {
+ free(cur);
+ return (NULL);
+ }
+
+ /*
+ * There are always 2 empty blocks appended to
+ * a PKG. It marks the end of the archive.
+ */
+ if (strncmp(cur->tf_hdr.ut_magic, "ustar", 5) != 0) {
+ free(cur);
+ errno = ENOSPC;
+ return (NULL);
+ }
+
+ cur->tf_ofs = pkg->pkg_ofs;
+ cur->tf_size = pkg_atol(cur->tf_hdr.ut_size,
+ sizeof(cur->tf_hdr.ut_size));
+
+ if (cur->tf_hdr.ut_name[0] != '+')
+ break;
+
+ /*
+ * Skip package meta-files.
+ */
+ ofs = cur->tf_ofs + cur->tf_size;
+ ofs = (ofs + 0x1ff) & ~0x1ff;
+ while (pkg->pkg_ofs < ofs) {
+ if (get_zipped(pkg, buf, sizeof(buf)) == -1) {
+ free(cur);
+ return (NULL);
+ }
+ }
+ }
+
+ if (last != NULL)
+ last->tf_next = cur;
+ else
+ pkg->pkg_first = cur;
+ pkg->pkg_last = cur;
+ }
+
+ return (cur);
+}
diff --git a/stand/libsa/powerpc/_setjmp.S b/stand/libsa/powerpc/_setjmp.S
new file mode 100644
index 0000000..7c7c24b
--- /dev/null
+++ b/stand/libsa/powerpc/_setjmp.S
@@ -0,0 +1,115 @@
+/* $FreeBSD$ */
+/* from: NetBSD: setjmp.S,v 1.1 1998/01/27 15:13:12 sakamoto Exp $ */
+/* from: OpenBSD: setjmp.S,v 1.2 1996/12/28 06:22:18 rahnds Exp */
+/* kernel version of this file, does not have signal goop */
+/* int setjmp(jmp_buf env) */
+
+#include <machine/asm.h>
+
+#ifdef __powerpc64__
+#define LD_REG ld
+#define ST_REG std
+#define REGWIDTH 8
+#else
+#define LD_REG lwz
+#define ST_REG stw
+#define REGWIDTH 4
+#endif
+
+#define JMP_r1 1*REGWIDTH
+#define JMP_r2 2*REGWIDTH
+#define JMP_r14 3*REGWIDTH
+#define JMP_r15 4*REGWIDTH
+#define JMP_r16 5*REGWIDTH
+#define JMP_r17 6*REGWIDTH
+#define JMP_r18 7*REGWIDTH
+#define JMP_r19 8*REGWIDTH
+#define JMP_r20 9*REGWIDTH
+#define JMP_r21 10*REGWIDTH
+#define JMP_r22 11*REGWIDTH
+#define JMP_r23 12*REGWIDTH
+#define JMP_r24 13*REGWIDTH
+#define JMP_r25 14*REGWIDTH
+#define JMP_r26 15*REGWIDTH
+#define JMP_r27 16*REGWIDTH
+#define JMP_r28 17*REGWIDTH
+#define JMP_r29 18*REGWIDTH
+#define JMP_r30 19*REGWIDTH
+#define JMP_r31 20*REGWIDTH
+#define JMP_lr 21*REGWIDTH
+#define JMP_cr 22*REGWIDTH
+#define JMP_ctr 23*REGWIDTH
+#define JMP_xer 24*REGWIDTH
+#define JMP_sig 25*REGWIDTH
+
+ASENTRY_NOPROF(_setjmp)
+ ST_REG 31, JMP_r31(3)
+ /* r1, r2, r14-r30 */
+ ST_REG 1, JMP_r1 (3)
+ ST_REG 2, JMP_r2 (3)
+ ST_REG 14, JMP_r14(3)
+ ST_REG 15, JMP_r15(3)
+ ST_REG 16, JMP_r16(3)
+ ST_REG 17, JMP_r17(3)
+ ST_REG 18, JMP_r18(3)
+ ST_REG 19, JMP_r19(3)
+ ST_REG 20, JMP_r20(3)
+ ST_REG 21, JMP_r21(3)
+ ST_REG 22, JMP_r22(3)
+ ST_REG 23, JMP_r23(3)
+ ST_REG 24, JMP_r24(3)
+ ST_REG 25, JMP_r25(3)
+ ST_REG 26, JMP_r26(3)
+ ST_REG 27, JMP_r27(3)
+ ST_REG 28, JMP_r28(3)
+ ST_REG 29, JMP_r29(3)
+ ST_REG 30, JMP_r30(3)
+ /* cr, lr, ctr, xer */
+ mfcr 0
+ ST_REG 0, JMP_cr(3)
+ mflr 0
+ ST_REG 0, JMP_lr(3)
+ mfctr 0
+ ST_REG 0, JMP_ctr(3)
+ mfxer 0
+ ST_REG 0, JMP_xer(3)
+ /* f14-f31, fpscr */
+ li 3, 0
+ blr
+
+
+.extern sigsetmask
+ASENTRY_NOPROF(_longjmp)
+ LD_REG 31, JMP_r31(3)
+ /* r1, r2, r14-r30 */
+ LD_REG 1, JMP_r1 (3)
+ LD_REG 2, JMP_r2 (3)
+ LD_REG 14, JMP_r14(3)
+ LD_REG 15, JMP_r15(3)
+ LD_REG 16, JMP_r16(3)
+ LD_REG 17, JMP_r17(3)
+ LD_REG 18, JMP_r18(3)
+ LD_REG 19, JMP_r19(3)
+ LD_REG 20, JMP_r20(3)
+ LD_REG 21, JMP_r21(3)
+ LD_REG 22, JMP_r22(3)
+ LD_REG 23, JMP_r23(3)
+ LD_REG 24, JMP_r24(3)
+ LD_REG 25, JMP_r25(3)
+ LD_REG 26, JMP_r26(3)
+ LD_REG 27, JMP_r27(3)
+ LD_REG 28, JMP_r28(3)
+ LD_REG 29, JMP_r29(3)
+ LD_REG 30, JMP_r30(3)
+ /* cr, lr, ctr, xer */
+ LD_REG 0, JMP_cr(3)
+ mtcr 0
+ LD_REG 0, JMP_lr(3)
+ mtlr 0
+ LD_REG 0, JMP_ctr(3)
+ mtctr 0
+ LD_REG 0, JMP_xer(3)
+ mtxer 0
+ /* f14-f31, fpscr */
+ mr 3, 4
+ blr
diff --git a/stand/libsa/powerpc/syncicache.c b/stand/libsa/powerpc/syncicache.c
new file mode 100644
index 0000000..434dcec
--- /dev/null
+++ b/stand/libsa/powerpc/syncicache.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (C) 1995-1997, 1999 Wolfgang Solfrank.
+ * Copyright (C) 1995-1997, 1999 TooLs GmbH.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ *
+ * $NetBSD: syncicache.c,v 1.2 1999/05/05 12:36:40 tsubai Exp $
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#if defined(_KERNEL) || defined(_STANDALONE)
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <vm/vm.h>
+#endif
+#include <sys/sysctl.h>
+
+#include <machine/cpu.h>
+#include <machine/md_var.h>
+
+#ifdef _STANDALONE
+int cacheline_size = 32;
+#endif
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#include <stdlib.h>
+
+int cacheline_size = 0;
+
+static void getcachelinesize(void);
+
+static void
+getcachelinesize()
+{
+ static int cachemib[] = { CTL_MACHDEP, CPU_CACHELINE };
+ int clen;
+
+ clen = sizeof(cacheline_size);
+
+ if (sysctl(cachemib, sizeof(cachemib) / sizeof(cachemib[0]),
+ &cacheline_size, &clen, NULL, 0) < 0 || !cacheline_size) {
+ abort();
+ }
+}
+#endif
+
+void
+__syncicache(void *from, int len)
+{
+ int l, off;
+ char *p;
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+ if (!cacheline_size)
+ getcachelinesize();
+#endif
+
+ off = (u_int)from & (cacheline_size - 1);
+ l = len += off;
+ p = (char *)from - off;
+
+ do {
+ __asm __volatile ("dcbst 0,%0" :: "r"(p));
+ p += cacheline_size;
+ } while ((l -= cacheline_size) > 0);
+ __asm __volatile ("sync");
+ p = (char *)from - off;
+ do {
+ __asm __volatile ("icbi 0,%0" :: "r"(p));
+ p += cacheline_size;
+ } while ((len -= cacheline_size) > 0);
+ __asm __volatile ("sync; isync");
+}
+
diff --git a/stand/libsa/printf.c b/stand/libsa/printf.c
new file mode 100644
index 0000000..71e313c
--- /dev/null
+++ b/stand/libsa/printf.c
@@ -0,0 +1,518 @@
+/*-
+ * Copyright (c) 1986, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Standaloneified version of the FreeBSD kernel printf family.
+ */
+
+#include <sys/types.h>
+#include <sys/stddef.h>
+#include <sys/stdint.h>
+#include <limits.h>
+#include <string.h>
+#include "stand.h"
+
+/*
+ * Note that stdarg.h and the ANSI style va_start macro is used for both
+ * ANSI and traditional C compilers.
+ */
+#include <machine/stdarg.h>
+
+#define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
+
+typedef void (kvprintf_fn_t)(int, void *);
+
+static char *ksprintn (char *buf, uintmax_t num, int base, int *len, int upper);
+static int kvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap);
+
+static void
+putchar_wrapper(int cc, void *arg)
+{
+
+ putchar(cc);
+}
+
+int
+printf(const char *fmt, ...)
+{
+ va_list ap;
+ int retval;
+
+ va_start(ap, fmt);
+ retval = kvprintf(fmt, putchar_wrapper, NULL, 10, ap);
+ va_end(ap);
+ return retval;
+}
+
+void
+vprintf(const char *fmt, va_list ap)
+{
+
+ kvprintf(fmt, putchar_wrapper, NULL, 10, ap);
+}
+
+int
+sprintf(char *buf, const char *cfmt, ...)
+{
+ int retval;
+ va_list ap;
+
+ va_start(ap, cfmt);
+ retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
+ buf[retval] = '\0';
+ va_end(ap);
+ return retval;
+}
+
+struct print_buf {
+ char *buf;
+ size_t size;
+};
+
+static void
+snprint_func(int ch, void *arg)
+{
+ struct print_buf *pbuf = arg;
+
+ if (pbuf->size < 2) {
+ /*
+ * Reserve last buffer position for the terminating
+ * character:
+ */
+ return;
+ }
+ *(pbuf->buf)++ = ch;
+ pbuf->size--;
+}
+
+int
+snprintf(char *buf, size_t size, const char *cfmt, ...)
+{
+ int retval;
+ va_list ap;
+ struct print_buf arg;
+
+ arg.buf = buf;
+ arg.size = size;
+
+ va_start(ap, cfmt);
+ retval = kvprintf(cfmt, &snprint_func, &arg, 10, ap);
+ va_end(ap);
+
+ if (arg.size >= 1)
+ *(arg.buf)++ = 0;
+ return retval;
+}
+
+void
+vsprintf(char *buf, const char *cfmt, va_list ap)
+{
+ int retval;
+
+ retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
+ buf[retval] = '\0';
+}
+
+/*
+ * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
+ * order; return an optional length and a pointer to the last character
+ * written in the buffer (i.e., the first character of the string).
+ * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
+ */
+static char *
+ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
+{
+ char *p, c;
+
+ p = nbuf;
+ *p = '\0';
+ do {
+ c = hex2ascii(num % base);
+ *++p = upper ? toupper(c) : c;
+ } while (num /= base);
+ if (lenp)
+ *lenp = p - nbuf;
+ return (p);
+}
+
+/*
+ * Scaled down version of printf(3).
+ *
+ * Two additional formats:
+ *
+ * The format %b is supported to decode error registers.
+ * Its usage is:
+ *
+ * printf("reg=%b\n", regval, "<base><arg>*");
+ *
+ * where <base> is the output base expressed as a control character, e.g.
+ * \10 gives octal; \20 gives hex. Each arg is a sequence of characters,
+ * the first of which gives the bit number to be inspected (origin 1), and
+ * the next characters (up to a control character, i.e. a character <= 32),
+ * give the name of the register. Thus:
+ *
+ * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE");
+ *
+ * would produce output:
+ *
+ * reg=3<BITTWO,BITONE>
+ *
+ * XXX: %D -- Hexdump, takes pointer and separator string:
+ * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX
+ * ("%*D", len, ptr, " " -> XX XX XX XX ...
+ */
+static int
+kvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap)
+{
+#define PCHAR(c) {int cc=(c); if (func) (*func)(cc, arg); else *d++ = cc; retval++; }
+ char nbuf[MAXNBUF];
+ char *d;
+ const char *p, *percent, *q;
+ uint16_t *S;
+ u_char *up;
+ int ch, n;
+ uintmax_t num;
+ int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
+ int cflag, hflag, jflag, tflag, zflag;
+ int dwidth, upper;
+ char padc;
+ int stop = 0, retval = 0;
+
+ num = 0;
+ if (!func)
+ d = (char *) arg;
+ else
+ d = NULL;
+
+ if (fmt == NULL)
+ fmt = "(fmt null)\n";
+
+ if (radix < 2 || radix > 36)
+ radix = 10;
+
+ for (;;) {
+ padc = ' ';
+ width = 0;
+ while ((ch = (u_char)*fmt++) != '%' || stop) {
+ if (ch == '\0')
+ return (retval);
+ PCHAR(ch);
+ }
+ percent = fmt - 1;
+ qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
+ sign = 0; dot = 0; dwidth = 0; upper = 0;
+ cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
+reswitch: switch (ch = (u_char)*fmt++) {
+ case '.':
+ dot = 1;
+ goto reswitch;
+ case '#':
+ sharpflag = 1;
+ goto reswitch;
+ case '+':
+ sign = 1;
+ goto reswitch;
+ case '-':
+ ladjust = 1;
+ goto reswitch;
+ case '%':
+ PCHAR(ch);
+ break;
+ case '*':
+ if (!dot) {
+ width = va_arg(ap, int);
+ if (width < 0) {
+ ladjust = !ladjust;
+ width = -width;
+ }
+ } else {
+ dwidth = va_arg(ap, int);
+ }
+ goto reswitch;
+ case '0':
+ if (!dot) {
+ padc = '0';
+ goto reswitch;
+ }
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ for (n = 0;; ++fmt) {
+ n = n * 10 + ch - '0';
+ ch = *fmt;
+ if (ch < '0' || ch > '9')
+ break;
+ }
+ if (dot)
+ dwidth = n;
+ else
+ width = n;
+ goto reswitch;
+ case 'b':
+ num = (u_int)va_arg(ap, int);
+ p = va_arg(ap, char *);
+ for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
+ PCHAR(*q--);
+
+ if (num == 0)
+ break;
+
+ for (tmp = 0; *p;) {
+ n = *p++;
+ if (num & (1 << (n - 1))) {
+ PCHAR(tmp ? ',' : '<');
+ for (; (n = *p) > ' '; ++p)
+ PCHAR(n);
+ tmp = 1;
+ } else
+ for (; *p > ' '; ++p)
+ continue;
+ }
+ if (tmp)
+ PCHAR('>');
+ break;
+ case 'c':
+ PCHAR(va_arg(ap, int));
+ break;
+ case 'D':
+ up = va_arg(ap, u_char *);
+ p = va_arg(ap, char *);
+ if (!width)
+ width = 16;
+ while(width--) {
+ PCHAR(hex2ascii(*up >> 4));
+ PCHAR(hex2ascii(*up & 0x0f));
+ up++;
+ if (width)
+ for (q=p;*q;q++)
+ PCHAR(*q);
+ }
+ break;
+ case 'd':
+ case 'i':
+ base = 10;
+ sign = 1;
+ goto handle_sign;
+ case 'h':
+ if (hflag) {
+ hflag = 0;
+ cflag = 1;
+ } else
+ hflag = 1;
+ goto reswitch;
+ case 'j':
+ jflag = 1;
+ goto reswitch;
+ case 'l':
+ if (lflag) {
+ lflag = 0;
+ qflag = 1;
+ } else
+ lflag = 1;
+ goto reswitch;
+ case 'n':
+ if (jflag)
+ *(va_arg(ap, intmax_t *)) = retval;
+ else if (qflag)
+ *(va_arg(ap, quad_t *)) = retval;
+ else if (lflag)
+ *(va_arg(ap, long *)) = retval;
+ else if (zflag)
+ *(va_arg(ap, size_t *)) = retval;
+ else if (hflag)
+ *(va_arg(ap, short *)) = retval;
+ else if (cflag)
+ *(va_arg(ap, char *)) = retval;
+ else
+ *(va_arg(ap, int *)) = retval;
+ break;
+ case 'o':
+ base = 8;
+ goto handle_nosign;
+ case 'p':
+ base = 16;
+ sharpflag = (width == 0);
+ sign = 0;
+ num = (uintptr_t)va_arg(ap, void *);
+ goto number;
+ case 'q':
+ qflag = 1;
+ goto reswitch;
+ case 'r':
+ base = radix;
+ if (sign)
+ goto handle_sign;
+ goto handle_nosign;
+ case 's':
+ p = va_arg(ap, char *);
+ if (p == NULL)
+ p = "(null)";
+ if (!dot)
+ n = strlen (p);
+ else
+ for (n = 0; n < dwidth && p[n]; n++)
+ continue;
+
+ width -= n;
+
+ if (!ladjust && width > 0)
+ while (width--)
+ PCHAR(padc);
+ while (n--)
+ PCHAR(*p++);
+ if (ladjust && width > 0)
+ while (width--)
+ PCHAR(padc);
+ break;
+ case 'S': /* Assume console can cope with wide chars */
+ for (S = va_arg(ap, uint16_t *); *S != 0; S++)
+ PCHAR(*S);
+ break;
+ case 't':
+ tflag = 1;
+ goto reswitch;
+ case 'u':
+ base = 10;
+ goto handle_nosign;
+ case 'X':
+ upper = 1;
+ case 'x':
+ base = 16;
+ goto handle_nosign;
+ case 'y':
+ base = 16;
+ sign = 1;
+ goto handle_sign;
+ case 'z':
+ zflag = 1;
+ goto reswitch;
+handle_nosign:
+ sign = 0;
+ if (jflag)
+ num = va_arg(ap, uintmax_t);
+ else if (qflag)
+ num = va_arg(ap, u_quad_t);
+ else if (tflag)
+ num = va_arg(ap, ptrdiff_t);
+ else if (lflag)
+ num = va_arg(ap, u_long);
+ else if (zflag)
+ num = va_arg(ap, size_t);
+ else if (hflag)
+ num = (u_short)va_arg(ap, int);
+ else if (cflag)
+ num = (u_char)va_arg(ap, int);
+ else
+ num = va_arg(ap, u_int);
+ goto number;
+handle_sign:
+ if (jflag)
+ num = va_arg(ap, intmax_t);
+ else if (qflag)
+ num = va_arg(ap, quad_t);
+ else if (tflag)
+ num = va_arg(ap, ptrdiff_t);
+ else if (lflag)
+ num = va_arg(ap, long);
+ else if (zflag)
+ num = va_arg(ap, ssize_t);
+ else if (hflag)
+ num = (short)va_arg(ap, int);
+ else if (cflag)
+ num = (char)va_arg(ap, int);
+ else
+ num = va_arg(ap, int);
+number:
+ if (sign && (intmax_t)num < 0) {
+ neg = 1;
+ num = -(intmax_t)num;
+ }
+ p = ksprintn(nbuf, num, base, &n, upper);
+ tmp = 0;
+ if (sharpflag && num != 0) {
+ if (base == 8)
+ tmp++;
+ else if (base == 16)
+ tmp += 2;
+ }
+ if (neg)
+ tmp++;
+
+ if (!ladjust && padc == '0')
+ dwidth = width - tmp;
+ width -= tmp + imax(dwidth, n);
+ dwidth -= n;
+ if (!ladjust)
+ while (width-- > 0)
+ PCHAR(' ');
+ if (neg)
+ PCHAR('-');
+ if (sharpflag && num != 0) {
+ if (base == 8) {
+ PCHAR('0');
+ } else if (base == 16) {
+ PCHAR('0');
+ PCHAR('x');
+ }
+ }
+ while (dwidth-- > 0)
+ PCHAR('0');
+
+ while (*p)
+ PCHAR(*p--);
+
+ if (ladjust)
+ while (width-- > 0)
+ PCHAR(' ');
+
+ break;
+ default:
+ while (percent < fmt)
+ PCHAR(*percent++);
+ /*
+ * Since we ignore a formatting argument it is no
+ * longer safe to obey the remaining formatting
+ * arguments as the arguments will no longer match
+ * the format specs.
+ */
+ stop = 1;
+ break;
+ }
+ }
+#undef PCHAR
+}
diff --git a/stand/libsa/qdivrem.c b/stand/libsa/qdivrem.c
new file mode 100644
index 0000000..bde3b0d
--- /dev/null
+++ b/stand/libsa/qdivrem.c
@@ -0,0 +1,348 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: Id: qdivrem.c,v 1.7 1997/11/07 09:20:40 phk Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Multiprecision divide. This algorithm is from Knuth vol. 2 (2nd ed),
+ * section 4.3.1, pp. 257--259.
+ */
+
+#include "quad.h"
+
+#define B (1 << HALF_BITS) /* digit base */
+
+/* Combine two `digits' to make a single two-digit number. */
+#define COMBINE(a, b) (((u_int)(a) << HALF_BITS) | (b))
+
+_Static_assert(sizeof(int) / 2 == sizeof(short),
+ "Bitwise functions in libstand are broken on this architecture\n");
+
+/* select a type for digits in base B: use unsigned short if they fit */
+typedef unsigned short digit;
+
+/*
+ * Shift p[0]..p[len] left `sh' bits, ignoring any bits that
+ * `fall out' the left (there never will be any such anyway).
+ * We may assume len >= 0. NOTE THAT THIS WRITES len+1 DIGITS.
+ */
+static void
+shl(digit *p, int len, int sh)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ p[i] = LHALF(p[i] << sh) | (p[i + 1] >> (HALF_BITS - sh));
+ p[i] = LHALF(p[i] << sh);
+}
+
+/*
+ * __qdivrem(u, v, rem) returns u/v and, optionally, sets *rem to u%v.
+ *
+ * We do this in base 2-sup-HALF_BITS, so that all intermediate products
+ * fit within u_int. As a consequence, the maximum length dividend and
+ * divisor are 4 `digits' in this base (they are shorter if they have
+ * leading zeros).
+ */
+u_quad_t
+__qdivrem(uq, vq, arq)
+ u_quad_t uq, vq, *arq;
+{
+ union uu tmp;
+ digit *u, *v, *q;
+ digit v1, v2;
+ u_int qhat, rhat, t;
+ int m, n, d, j, i;
+ digit uspace[5], vspace[5], qspace[5];
+
+ /*
+ * Take care of special cases: divide by zero, and u < v.
+ */
+ if (vq == 0) {
+ /* divide by zero. */
+ static volatile const unsigned int zero = 0;
+
+ tmp.ul[H] = tmp.ul[L] = 1 / zero;
+ if (arq)
+ *arq = uq;
+ return (tmp.q);
+ }
+ if (uq < vq) {
+ if (arq)
+ *arq = uq;
+ return (0);
+ }
+ u = &uspace[0];
+ v = &vspace[0];
+ q = &qspace[0];
+
+ /*
+ * Break dividend and divisor into digits in base B, then
+ * count leading zeros to determine m and n. When done, we
+ * will have:
+ * u = (u[1]u[2]...u[m+n]) sub B
+ * v = (v[1]v[2]...v[n]) sub B
+ * v[1] != 0
+ * 1 < n <= 4 (if n = 1, we use a different division algorithm)
+ * m >= 0 (otherwise u < v, which we already checked)
+ * m + n = 4
+ * and thus
+ * m = 4 - n <= 2
+ */
+ tmp.uq = uq;
+ u[0] = 0;
+ u[1] = HHALF(tmp.ul[H]);
+ u[2] = LHALF(tmp.ul[H]);
+ u[3] = HHALF(tmp.ul[L]);
+ u[4] = LHALF(tmp.ul[L]);
+ tmp.uq = vq;
+ v[1] = HHALF(tmp.ul[H]);
+ v[2] = LHALF(tmp.ul[H]);
+ v[3] = HHALF(tmp.ul[L]);
+ v[4] = LHALF(tmp.ul[L]);
+ for (n = 4; v[1] == 0; v++) {
+ if (--n == 1) {
+ u_int rbj; /* r*B+u[j] (not root boy jim) */
+ digit q1, q2, q3, q4;
+
+ /*
+ * Change of plan, per exercise 16.
+ * r = 0;
+ * for j = 1..4:
+ * q[j] = floor((r*B + u[j]) / v),
+ * r = (r*B + u[j]) % v;
+ * We unroll this completely here.
+ */
+ t = v[2]; /* nonzero, by definition */
+ q1 = u[1] / t;
+ rbj = COMBINE(u[1] % t, u[2]);
+ q2 = rbj / t;
+ rbj = COMBINE(rbj % t, u[3]);
+ q3 = rbj / t;
+ rbj = COMBINE(rbj % t, u[4]);
+ q4 = rbj / t;
+ if (arq)
+ *arq = rbj % t;
+ tmp.ul[H] = COMBINE(q1, q2);
+ tmp.ul[L] = COMBINE(q3, q4);
+ return (tmp.q);
+ }
+ }
+
+ /*
+ * By adjusting q once we determine m, we can guarantee that
+ * there is a complete four-digit quotient at &qspace[1] when
+ * we finally stop.
+ */
+ for (m = 4 - n; u[1] == 0; u++)
+ m--;
+ for (i = 4 - m; --i >= 0;)
+ q[i] = 0;
+ q += 4 - m;
+
+ /*
+ * Here we run Program D, translated from MIX to C and acquiring
+ * a few minor changes.
+ *
+ * D1: choose multiplier 1 << d to ensure v[1] >= B/2.
+ */
+ d = 0;
+ for (t = v[1]; t < B / 2; t <<= 1)
+ d++;
+ if (d > 0) {
+ shl(&u[0], m + n, d); /* u <<= d */
+ shl(&v[1], n - 1, d); /* v <<= d */
+ }
+ /*
+ * D2: j = 0.
+ */
+ j = 0;
+ v1 = v[1]; /* for D3 -- note that v[1..n] are constant */
+ v2 = v[2]; /* for D3 */
+ do {
+ digit uj0, uj1, uj2;
+
+ /*
+ * D3: Calculate qhat (\^q, in TeX notation).
+ * Let qhat = min((u[j]*B + u[j+1])/v[1], B-1), and
+ * let rhat = (u[j]*B + u[j+1]) mod v[1].
+ * While rhat < B and v[2]*qhat > rhat*B+u[j+2],
+ * decrement qhat and increase rhat correspondingly.
+ * Note that if rhat >= B, v[2]*qhat < rhat*B.
+ */
+ uj0 = u[j + 0]; /* for D3 only -- note that u[j+...] change */
+ uj1 = u[j + 1]; /* for D3 only */
+ uj2 = u[j + 2]; /* for D3 only */
+ if (uj0 == v1) {
+ qhat = B;
+ rhat = uj1;
+ goto qhat_too_big;
+ } else {
+ u_int nn = COMBINE(uj0, uj1);
+ qhat = nn / v1;
+ rhat = nn % v1;
+ }
+ while (v2 * qhat > COMBINE(rhat, uj2)) {
+ qhat_too_big:
+ qhat--;
+ if ((rhat += v1) >= B)
+ break;
+ }
+ /*
+ * D4: Multiply and subtract.
+ * The variable `t' holds any borrows across the loop.
+ * We split this up so that we do not require v[0] = 0,
+ * and to eliminate a final special case.
+ */
+ for (t = 0, i = n; i > 0; i--) {
+ t = u[i + j] - v[i] * qhat - t;
+ u[i + j] = LHALF(t);
+ t = (B - HHALF(t)) & (B - 1);
+ }
+ t = u[j] - t;
+ u[j] = LHALF(t);
+ /*
+ * D5: test remainder.
+ * There is a borrow if and only if HHALF(t) is nonzero;
+ * in that (rare) case, qhat was too large (by exactly 1).
+ * Fix it by adding v[1..n] to u[j..j+n].
+ */
+ if (HHALF(t)) {
+ qhat--;
+ for (t = 0, i = n; i > 0; i--) { /* D6: add back. */
+ t += u[i + j] + v[i];
+ u[i + j] = LHALF(t);
+ t = HHALF(t);
+ }
+ u[j] = LHALF(u[j] + t);
+ }
+ q[j] = qhat;
+ } while (++j <= m); /* D7: loop on j. */
+
+ /*
+ * If caller wants the remainder, we have to calculate it as
+ * u[m..m+n] >> d (this is at most n digits and thus fits in
+ * u[m+1..m+n], but we may need more source digits).
+ */
+ if (arq) {
+ if (d) {
+ for (i = m + n; i > m; --i)
+ u[i] = (u[i] >> d) |
+ LHALF(u[i - 1] << (HALF_BITS - d));
+ u[i] = 0;
+ }
+ tmp.ul[H] = COMBINE(uspace[1], uspace[2]);
+ tmp.ul[L] = COMBINE(uspace[3], uspace[4]);
+ *arq = tmp.q;
+ }
+
+ tmp.ul[H] = COMBINE(qspace[1], qspace[2]);
+ tmp.ul[L] = COMBINE(qspace[3], qspace[4]);
+ return (tmp.q);
+}
+
+/*
+ * Divide two unsigned quads.
+ */
+
+u_quad_t
+__udivdi3(a, b)
+ u_quad_t a, b;
+{
+
+ return (__qdivrem(a, b, (u_quad_t *)0));
+}
+
+/*
+ * Return remainder after dividing two unsigned quads.
+ */
+u_quad_t
+__umoddi3(a, b)
+ u_quad_t a, b;
+{
+ u_quad_t r;
+
+ (void)__qdivrem(a, b, &r);
+ return (r);
+}
+
+/*
+ * Divide two signed quads.
+ * ??? if -1/2 should produce -1 on this machine, this code is wrong
+ */
+quad_t
+__divdi3(a, b)
+ quad_t a, b;
+{
+ u_quad_t ua, ub, uq;
+ int neg;
+
+ if (a < 0)
+ ua = -(u_quad_t)a, neg = 1;
+ else
+ ua = a, neg = 0;
+ if (b < 0)
+ ub = -(u_quad_t)b, neg ^= 1;
+ else
+ ub = b;
+ uq = __qdivrem(ua, ub, (u_quad_t *)0);
+ return (neg ? -uq : uq);
+}
+
+/*
+ * Return remainder after dividing two signed quads.
+ *
+ * XXX
+ * If -1/2 should produce -1 on this machine, this code is wrong.
+ */
+quad_t
+__moddi3(a, b)
+ quad_t a, b;
+{
+ u_quad_t ua, ub, ur;
+ int neg;
+
+ if (a < 0)
+ ua = -(u_quad_t)a, neg = 1;
+ else
+ ua = a, neg = 0;
+ if (b < 0)
+ ub = -(u_quad_t)b;
+ else
+ ub = b;
+ (void)__qdivrem(ua, ub, &ur);
+ return (neg ? -ur : ur);
+}
diff --git a/stand/libsa/quad.h b/stand/libsa/quad.h
new file mode 100644
index 0000000..349540a
--- /dev/null
+++ b/stand/libsa/quad.h
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)quad.h 8.1 (Berkeley) 6/4/93
+ * $FreeBSD$
+ */
+
+/*
+ * Quad arithmetic.
+ *
+ * This library makes the following assumptions:
+ *
+ * - The type long long (aka quad_t) exists.
+ *
+ * - A quad variable is exactly twice as long as `long'.
+ *
+ * - The machine's arithmetic is two's complement.
+ *
+ * This library can provide 128-bit arithmetic on a machine with 128-bit
+ * quads and 64-bit longs, for instance, or 96-bit arithmetic on machines
+ * with 48-bit longs.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <limits.h>
+
+_Static_assert(sizeof(quad_t) == sizeof(int) * 2,
+ "Bitwise function in libstand are broken on this architecture\n");
+
+/*
+ * Depending on the desired operation, we view a `long long' (aka quad_t) in
+ * one or more of the following formats.
+ */
+union uu {
+ quad_t q; /* as a (signed) quad */
+ quad_t uq; /* as an unsigned quad */
+ int sl[2]; /* as two signed ints */
+ u_int ul[2]; /* as two unsigned ints */
+};
+
+/*
+ * Define high and low longwords.
+ */
+#define H _QUAD_HIGHWORD
+#define L _QUAD_LOWWORD
+
+/*
+ * Total number of bits in a quad_t and in the pieces that make it up.
+ * These are used for shifting, and also below for halfword extraction
+ * and assembly.
+ */
+#define QUAD_BITS (sizeof(quad_t) * CHAR_BIT)
+#define HALF_BITS (sizeof(int) * CHAR_BIT / 2)
+
+/*
+ * Extract high and low shortwords from longword, and move low shortword of
+ * longword to upper half of long, i.e., produce the upper longword of
+ * ((quad_t)(x) << (number_of_bits_in_long/2)). (`x' must actually be u_long.)
+ *
+ * These are used in the multiply code, to split a longword into upper
+ * and lower halves, and to reassemble a product as a quad_t, shifted left
+ * (sizeof(long)*CHAR_BIT/2).
+ */
+#define HHALF(x) ((x) >> HALF_BITS)
+#define LHALF(x) ((x) & ((1 << HALF_BITS) - 1))
+#define LHUP(x) ((x) << HALF_BITS)
+
+quad_t __divdi3(quad_t a, quad_t b);
+quad_t __moddi3(quad_t a, quad_t b);
+u_quad_t __qdivrem(u_quad_t u, u_quad_t v, u_quad_t *rem);
+u_quad_t __udivdi3(u_quad_t a, u_quad_t b);
+u_quad_t __umoddi3(u_quad_t a, u_quad_t b);
+
+/*
+ * XXX
+ * Compensate for gcc 1 vs gcc 2. Gcc 1 defines ?sh?di3's second argument
+ * as u_quad_t, while gcc 2 correctly uses int. Unfortunately, we still use
+ * both compilers.
+ */
+#if __GNUC__ >= 2
+typedef unsigned int qshift_t;
+#else
+typedef u_quad_t qshift_t;
+#endif
diff --git a/stand/libsa/random.c b/stand/libsa/random.c
new file mode 100644
index 0000000..a5b2ad6
--- /dev/null
+++ b/stand/libsa/random.c
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)random.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+static u_long randseed = 1;
+
+void
+srandom(seed)
+ u_long seed;
+{
+ randseed = seed;
+}
+
+/*
+ * Pseudo-random number generator for randomizing the profiling clock,
+ * and whatever else we might use it for. The result is uniform on
+ * [0, 2^31 - 1].
+ */
+long
+random(void)
+{
+ long x, hi, lo, t;
+
+ /*
+ * Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1).
+ * From "Random number generators: good ones are hard to find",
+ * Park and Miller, Communications of the ACM, vol. 31, no. 10,
+ * October 1988, p. 1195.
+ */
+ x = randseed;
+ hi = x / 127773;
+ lo = x % 127773;
+ t = 16807 * lo - 2836 * hi;
+ if (t <= 0)
+ t += 0x7fffffff;
+ randseed = t;
+ return (t);
+}
diff --git a/stand/libsa/rarp.c b/stand/libsa/rarp.c
new file mode 100644
index 0000000..3b8088e
--- /dev/null
+++ b/stand/libsa/rarp.c
@@ -0,0 +1,218 @@
+/* $NetBSD: rarp.c,v 1.16 1997/07/07 15:52:52 drochner Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: arp.c,v 1.5 93/07/15 05:52:26 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <netinet/in_systm.h>
+
+#include <string.h>
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+
+
+static ssize_t rarpsend(struct iodesc *, void *, size_t);
+static ssize_t rarprecv(struct iodesc *, void **, void **, time_t);
+
+/*
+ * Ethernet (Reverse) Address Resolution Protocol (see RFC 903, and 826).
+ */
+int
+rarp_getipaddress(int sock)
+{
+ struct iodesc *d;
+ struct ether_arp *ap;
+ void *pkt;
+ struct {
+ u_char header[ETHER_SIZE];
+ struct {
+ struct ether_arp arp;
+ u_char pad[18]; /* 60 - sizeof(arp) */
+ } data;
+ } wbuf;
+
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("rarp: socket=%d\n", sock);
+#endif
+ if (!(d = socktodesc(sock))) {
+ printf("rarp: bad socket. %d\n", sock);
+ return (-1);
+ }
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("rarp: d=%x\n", (u_int)d);
+#endif
+
+ bzero((char*)&wbuf.data, sizeof(wbuf.data));
+ ap = &wbuf.data.arp;
+ ap->arp_hrd = htons(ARPHRD_ETHER);
+ ap->arp_pro = htons(ETHERTYPE_IP);
+ ap->arp_hln = sizeof(ap->arp_sha); /* hardware address length */
+ ap->arp_pln = sizeof(ap->arp_spa); /* protocol address length */
+ ap->arp_op = htons(ARPOP_REVREQUEST);
+ bcopy(d->myea, ap->arp_sha, 6);
+ bcopy(d->myea, ap->arp_tha, 6);
+ pkt = NULL;
+
+ if (sendrecv(d,
+ rarpsend, &wbuf.data, sizeof(wbuf.data),
+ rarprecv, &pkt, (void *)&ap) < 0) {
+ printf("No response for RARP request\n");
+ return (-1);
+ }
+
+ bcopy(ap->arp_tpa, (char *)&myip, sizeof(myip));
+#if 0
+ /* XXX - Can NOT assume this is our root server! */
+ bcopy(ap->arp_spa, (char *)&rootip, sizeof(rootip));
+#endif
+ free(pkt);
+
+ /* Compute our "natural" netmask. */
+ if (IN_CLASSA(myip.s_addr))
+ netmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(myip.s_addr))
+ netmask = IN_CLASSB_NET;
+ else
+ netmask = IN_CLASSC_NET;
+
+ d->myip = myip;
+ return (0);
+}
+
+/*
+ * Broadcast a RARP request (i.e. who knows who I am)
+ */
+static ssize_t
+rarpsend(struct iodesc *d, void *pkt, size_t len)
+{
+
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("rarpsend: called\n");
+#endif
+
+ return (sendether(d, pkt, len, bcea, ETHERTYPE_REVARP));
+}
+
+/*
+ * Returns 0 if this is the packet we're waiting for
+ * else -1 (and errno == 0)
+ */
+static ssize_t
+rarprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft)
+{
+ ssize_t n;
+ struct ether_arp *ap;
+ void *ptr = NULL;
+ uint16_t etype; /* host order */
+
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("rarprecv: ");
+#endif
+
+ n = readether(d, &ptr, (void **)&ap, tleft, &etype);
+ errno = 0; /* XXX */
+ if (n == -1 || n < sizeof(struct ether_arp)) {
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("bad len=%d\n", n);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ if (etype != ETHERTYPE_REVARP) {
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("bad type=0x%x\n", etype);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ if (ap->arp_hrd != htons(ARPHRD_ETHER) ||
+ ap->arp_pro != htons(ETHERTYPE_IP) ||
+ ap->arp_hln != sizeof(ap->arp_sha) ||
+ ap->arp_pln != sizeof(ap->arp_spa) )
+ {
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("bad hrd/pro/hln/pln\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ if (ap->arp_op != htons(ARPOP_REVREPLY)) {
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("bad op=0x%x\n", ntohs(ap->arp_op));
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* Is the reply for our Ethernet address? */
+ if (bcmp(ap->arp_tha, d->myea, 6)) {
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("unwanted address\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* We have our answer. */
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("got it\n");
+#endif
+ *pkt = ptr;
+ *payload = ap;
+ return (n);
+}
diff --git a/stand/libsa/read.c b/stand/libsa/read.c
new file mode 100644
index 0000000..c165d68
--- /dev/null
+++ b/stand/libsa/read.c
@@ -0,0 +1,127 @@
+/* $NetBSD: read.c,v 1.8 1997/01/22 00:38:12 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)read.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include "stand.h"
+
+ssize_t
+read(int fd, void *dest, size_t bcount)
+{
+ struct open_file *f = &files[fd];
+ size_t resid;
+
+ if ((unsigned)fd >= SOPEN_MAX || !(f->f_flags & F_READ)) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (f->f_flags & F_RAW) {
+ twiddle(4);
+ errno = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ btodb(f->f_offset), bcount, dest, &resid);
+ if (errno)
+ return (-1);
+ f->f_offset += resid;
+ return (resid);
+ }
+
+ /*
+ * Optimise reads from regular files using a readahead buffer.
+ * If the request can't be satisfied from the current buffer contents,
+ * check to see if it should be bypassed, or refill the buffer and complete
+ * the request.
+ */
+ resid = bcount;
+ for (;;) {
+ size_t ccount, cresid;
+ /* how much can we supply? */
+ ccount = imin(f->f_ralen, resid);
+ if (ccount > 0) {
+ bcopy(f->f_rabuf + f->f_raoffset, dest, ccount);
+ f->f_raoffset += ccount;
+ f->f_ralen -= ccount;
+ resid -= ccount;
+ if (resid == 0)
+ return(bcount);
+ dest = (char *)dest + ccount;
+ }
+
+ /* will filling the readahead buffer again not help? */
+ if (resid >= SOPEN_RASIZE) {
+ /* bypass the rest of the request and leave the buffer empty */
+ if ((errno = (f->f_ops->fo_read)(f, dest, resid, &cresid)))
+ return (-1);
+ return(bcount - cresid);
+ }
+
+ /* fetch more data */
+ if ((errno = (f->f_ops->fo_read)(f, f->f_rabuf, SOPEN_RASIZE, &cresid)))
+ return (-1);
+ f->f_raoffset = 0;
+ f->f_ralen = SOPEN_RASIZE - cresid;
+ /* no more data, return what we had */
+ if (f->f_ralen == 0)
+ return(bcount - resid);
+ }
+}
diff --git a/stand/libsa/readdir.c b/stand/libsa/readdir.c
new file mode 100644
index 0000000..e49d93d
--- /dev/null
+++ b/stand/libsa/readdir.c
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include "stand.h"
+
+struct dirent *
+readdirfd(int fd)
+{
+ static struct dirent dir; /* XXX not thread safe */
+ struct open_file *f = &files[fd];
+
+ if ((unsigned)fd >= SOPEN_MAX || !(f->f_flags & F_READ)) {
+ errno = EBADF;
+ return (NULL);
+ }
+ if (f->f_flags & F_RAW) {
+ errno = EIO;
+ return (NULL);
+ }
+ errno = (f->f_ops->fo_readdir)(f, &dir);
+ if (errno)
+ return (NULL);
+ return (&dir);
+}
diff --git a/stand/libsa/rpc.c b/stand/libsa/rpc.c
new file mode 100644
index 0000000..94e6ce6
--- /dev/null
+++ b/stand/libsa/rpc.c
@@ -0,0 +1,433 @@
+/* $NetBSD: rpc.c,v 1.18 1998/01/23 19:27:45 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * RPC functions used by NFS and bootparams.
+ * Note that bootparams requires the ability to find out the
+ * address of the server from which its response has come.
+ * This is supported by keeping the IP/UDP headers in the
+ * buffer space provided by the caller. (See rpc_fromaddr)
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <string.h>
+
+#include "rpcv2.h"
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+#include "rpc.h"
+
+struct auth_info {
+ int32_t authtype; /* auth type */
+ u_int32_t authlen; /* auth length */
+};
+
+struct auth_unix {
+ int32_t ua_time;
+ int32_t ua_hostname; /* null */
+ int32_t ua_uid;
+ int32_t ua_gid;
+ int32_t ua_gidlist; /* null */
+};
+
+struct rpc_call {
+ u_int32_t rp_xid; /* request transaction id */
+ int32_t rp_direction; /* call direction (0) */
+ u_int32_t rp_rpcvers; /* rpc version (2) */
+ u_int32_t rp_prog; /* program */
+ u_int32_t rp_vers; /* version */
+ u_int32_t rp_proc; /* procedure */
+};
+
+struct rpc_reply {
+ u_int32_t rp_xid; /* request transaction id */
+ int32_t rp_direction; /* call direction (1) */
+ int32_t rp_astatus; /* accept status (0: accepted) */
+ union {
+ u_int32_t rpu_errno;
+ struct {
+ struct auth_info rok_auth;
+ u_int32_t rok_status;
+ } rpu_rok;
+ } rp_u;
+};
+
+/* Local forwards */
+static ssize_t recvrpc(struct iodesc *, void **, void **, time_t);
+static int rpc_getport(struct iodesc *, n_long, n_long);
+
+int rpc_xid;
+int rpc_port = 0x400; /* predecrement */
+
+/*
+ * Make a rpc call; return length of answer
+ * Note: Caller must leave room for headers.
+ */
+ssize_t
+rpc_call(struct iodesc *d, n_long prog, n_long vers, n_long proc,
+ void *sdata, size_t slen, void **rdata, void **pkt)
+{
+ ssize_t cc, rsize;
+ struct auth_info *auth;
+ struct rpc_call *call;
+ struct rpc_reply *reply;
+ char *send_head, *send_tail;
+ void *ptr;
+ n_long x;
+ int port; /* host order */
+
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("rpc_call: prog=0x%x vers=%d proc=%d\n",
+ prog, vers, proc);
+#endif
+
+ port = rpc_getport(d, prog, vers);
+ if (port == -1)
+ return (-1);
+
+ d->destport = htons(port);
+
+ /*
+ * Prepend authorization stuff and headers.
+ * Note, must prepend things in reverse order.
+ */
+ send_head = sdata;
+ send_tail = (char *)sdata + slen;
+
+ /* Auth verifier is always auth_null */
+ send_head -= sizeof(*auth);
+ auth = (struct auth_info *)send_head;
+ auth->authtype = htonl(RPCAUTH_NULL);
+ auth->authlen = 0;
+
+ /* Auth credentials: always auth unix (as root) */
+ send_head -= sizeof(struct auth_unix);
+ bzero(send_head, sizeof(struct auth_unix));
+ send_head -= sizeof(*auth);
+ auth = (struct auth_info *)send_head;
+ auth->authtype = htonl(RPCAUTH_UNIX);
+ auth->authlen = htonl(sizeof(struct auth_unix));
+
+ /* RPC call structure. */
+ send_head -= sizeof(*call);
+ call = (struct rpc_call *)send_head;
+ rpc_xid++;
+ call->rp_xid = htonl(rpc_xid);
+ call->rp_direction = htonl(RPC_CALL);
+ call->rp_rpcvers = htonl(RPC_VER2);
+ call->rp_prog = htonl(prog);
+ call->rp_vers = htonl(vers);
+ call->rp_proc = htonl(proc);
+
+ ptr = NULL;
+ cc = sendrecv(d,
+ sendudp, send_head, send_tail - send_head,
+ recvrpc, &ptr, (void **)&reply);
+
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("callrpc: cc=%zd\n", cc);
+#endif
+ if (cc == -1)
+ return (-1);
+
+ if (cc <= sizeof(*reply)) {
+ errno = EBADRPC;
+ free(ptr);
+ return (-1);
+ }
+
+ /*
+ * Check the RPC reply status.
+ * The xid, dir, astatus were already checked.
+ */
+ auth = &reply->rp_u.rpu_rok.rok_auth;
+ x = ntohl(auth->authlen);
+ if (x != 0) {
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("callrpc: reply auth != NULL\n");
+#endif
+ errno = EBADRPC;
+ free(ptr);
+ return (-1);
+ }
+ x = ntohl(reply->rp_u.rpu_rok.rok_status);
+ if (x != 0) {
+ printf("callrpc: error = %ld\n", (long)x);
+ errno = EBADRPC;
+ free(ptr);
+ return (-1);
+ }
+
+ rsize = cc - sizeof(*reply);
+ *rdata = (void *)((uintptr_t)reply + sizeof(*reply));
+ *pkt = ptr;
+ return (rsize);
+}
+
+/*
+ * Returns true if packet is the one we're waiting for.
+ * This just checks the XID, direction, acceptance.
+ * Remaining checks are done by callrpc
+ */
+static ssize_t
+recvrpc(struct iodesc *d, void **pkt, void **payload, time_t tleft)
+{
+ void *ptr;
+ struct rpc_reply *reply;
+ ssize_t n;
+ int x;
+
+ errno = 0;
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("recvrpc: called\n");
+#endif
+
+ ptr = NULL;
+ n = readudp(d, &ptr, (void **)&reply, tleft);
+ if (n <= (4 * 4)) {
+ free(ptr);
+ return (-1);
+ }
+
+ x = ntohl(reply->rp_xid);
+ if (x != rpc_xid) {
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("recvrpc: rp_xid %d != xid %d\n", x, rpc_xid);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ x = ntohl(reply->rp_direction);
+ if (x != RPC_REPLY) {
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("recvrpc: rp_direction %d != REPLY\n", x);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ x = ntohl(reply->rp_astatus);
+ if (x != RPC_MSGACCEPTED) {
+ errno = ntohl(reply->rp_u.rpu_errno);
+ printf("recvrpc: reject, astat=%d, errno=%d\n", x, errno);
+ free(ptr);
+ return (-1);
+ }
+
+ *pkt = ptr;
+ *payload = reply;
+ /* Return data count (thus indicating success) */
+ return (n);
+}
+
+/*
+ * Given a pointer to a reply just received,
+ * dig out the IP address/port from the headers.
+ */
+void
+rpc_fromaddr(void *pkt, struct in_addr *addr, u_short *port)
+{
+ struct hackhdr {
+ /* Tail of IP header: just IP addresses */
+ n_long ip_src;
+ n_long ip_dst;
+ /* UDP header: */
+ u_int16_t uh_sport; /* source port */
+ u_int16_t uh_dport; /* destination port */
+ int16_t uh_ulen; /* udp length */
+ u_int16_t uh_sum; /* udp checksum */
+ /* RPC reply header: */
+ struct rpc_reply rpc;
+ } *hhdr;
+
+ hhdr = ((struct hackhdr *)pkt) - 1;
+ addr->s_addr = hhdr->ip_src;
+ *port = hhdr->uh_sport;
+}
+
+/*
+ * RPC Portmapper cache
+ */
+#define PMAP_NUM 8 /* need at most 5 pmap entries */
+
+int rpc_pmap_num;
+struct pmap_list {
+ struct in_addr addr; /* server, net order */
+ u_int prog; /* host order */
+ u_int vers; /* host order */
+ int port; /* host order */
+} rpc_pmap_list[PMAP_NUM];
+
+/*
+ * return port number in host order, or -1.
+ * arguments are:
+ * addr .. server, net order.
+ * prog .. host order.
+ * vers .. host order.
+ */
+int
+rpc_pmap_getcache(struct in_addr addr, u_int prog, u_int vers)
+{
+ struct pmap_list *pl;
+
+ for (pl = rpc_pmap_list; pl < &rpc_pmap_list[rpc_pmap_num]; pl++) {
+ if (pl->addr.s_addr == addr.s_addr &&
+ pl->prog == prog && pl->vers == vers )
+ {
+ return (pl->port);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * arguments are:
+ * addr .. server, net order.
+ * prog .. host order.
+ * vers .. host order.
+ * port .. host order.
+ */
+void
+rpc_pmap_putcache(struct in_addr addr, u_int prog, u_int vers, int port)
+{
+ struct pmap_list *pl;
+
+ /* Don't overflow cache... */
+ if (rpc_pmap_num >= PMAP_NUM) {
+ /* ... just re-use the last entry. */
+ rpc_pmap_num = PMAP_NUM - 1;
+#ifdef RPC_DEBUG
+ printf("rpc_pmap_putcache: cache overflow\n");
+#endif
+ }
+
+ pl = &rpc_pmap_list[rpc_pmap_num];
+ rpc_pmap_num++;
+
+ /* Cache answer */
+ pl->addr = addr;
+ pl->prog = prog;
+ pl->vers = vers;
+ pl->port = port;
+}
+
+
+/*
+ * Request a port number from the port mapper.
+ * Returns the port in host order.
+ * prog and vers are host order.
+ */
+int
+rpc_getport(struct iodesc *d, n_long prog, n_long vers)
+{
+ struct args {
+ n_long prog; /* call program */
+ n_long vers; /* call version */
+ n_long proto; /* call protocol */
+ n_long port; /* call port (unused) */
+ } *args;
+ struct res {
+ n_long port;
+ } *res;
+ struct {
+ n_long h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ void *pkt;
+ ssize_t cc;
+ int port;
+
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("%s: prog=0x%x vers=%d\n", __func__, prog, vers);
+#endif
+
+ /* This one is fixed forever. */
+ if (prog == PMAPPROG) {
+ port = PMAPPORT;
+ goto out;
+ }
+
+ /* Try for cached answer first */
+ port = rpc_pmap_getcache(d->destip, prog, vers);
+ if (port != -1)
+ goto out;
+
+ args = &sdata.d;
+ args->prog = htonl(prog);
+ args->vers = htonl(vers);
+ args->proto = htonl(IPPROTO_UDP);
+ args->port = 0;
+ pkt = NULL;
+
+ cc = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT,
+ args, sizeof(*args), (void **)&res, &pkt);
+ if (cc < sizeof(*res)) {
+ printf("getport: %s", strerror(errno));
+ errno = EBADRPC;
+ free(pkt);
+ return (-1);
+ }
+ port = (int)ntohl(res->port);
+ free(pkt);
+
+ rpc_pmap_putcache(d->destip, prog, vers, port);
+
+out:
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("%s: port=%u\n", __func__, port);
+#endif
+ return (port);
+}
diff --git a/stand/libsa/rpc.h b/stand/libsa/rpc.h
new file mode 100644
index 0000000..6dda060
--- /dev/null
+++ b/stand/libsa/rpc.h
@@ -0,0 +1,66 @@
+/* $NetBSD: rpc.h,v 1.8 1996/09/26 23:22:03 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* XXX defines we can't easily get from system includes */
+#define PMAPPORT 111
+#define PMAPPROG 100000
+#define PMAPVERS 2
+#define PMAPPROC_NULL 0
+#define PMAPPROC_SET 1
+#define PMAPPROC_UNSET 2
+#define PMAPPROC_GETPORT 3
+#define PMAPPROC_DUMP 4
+#define PMAPPROC_CALLIT 5
+
+/* RPC functions: */
+ssize_t rpc_call(struct iodesc *, n_long, n_long, n_long,
+ void *, size_t, void **, void **);
+void rpc_fromaddr(void *, struct in_addr *, u_short *);
+int rpc_pmap_getcache(struct in_addr, u_int, u_int);
+void rpc_pmap_putcache(struct in_addr, u_int, u_int, int);
+
+extern int rpc_port; /* decrement before bind */
+
+/*
+ * How much space to leave in front of RPC requests.
+ * In 32-bit words (alignment) we have:
+ * 12: Ether + IP + UDP + padding
+ * 6: RPC call header
+ * 7: Auth UNIX
+ * 2: Auth NULL
+ */
+#define RPC_HEADER_WORDS 28
diff --git a/stand/libsa/rpcv2.h b/stand/libsa/rpcv2.h
new file mode 100644
index 0000000..b59a760
--- /dev/null
+++ b/stand/libsa/rpcv2.h
@@ -0,0 +1,87 @@
+/* $NetBSD: rpcv2.h,v 1.1 1996/02/26 23:05:32 gwr Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)rpcv2.h 8.1 (Berkeley) 6/10/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definitions for Sun RPC Version 2, from
+ * "RPC: Remote Procedure Call Protocol Specification" RFC1057
+ */
+
+/* Version # */
+#define RPC_VER2 2
+
+/* Authentication */
+#define RPCAUTH_NULL 0
+#define RPCAUTH_UNIX 1
+#define RPCAUTH_SHORT 2
+#define RPCAUTH_MAXSIZ 400
+#define RPCAUTH_UNIXGIDS 16
+
+/* Rpc Constants */
+#define RPC_CALL 0
+#define RPC_REPLY 1
+#define RPC_MSGACCEPTED 0
+#define RPC_MSGDENIED 1
+#define RPC_PROGUNAVAIL 1
+#define RPC_PROGMISMATCH 2
+#define RPC_PROCUNAVAIL 3
+#define RPC_GARBAGE 4 /* I like this one */
+#define RPC_MISMATCH 0
+#define RPC_AUTHERR 1
+
+/* Authentication failures */
+#define AUTH_BADCRED 1
+#define AUTH_REJECTCRED 2
+#define AUTH_BADVERF 3
+#define AUTH_REJECTVERF 4
+#define AUTH_TOOWEAK 5 /* Give em wheaties */
+
+/* Sizes of rpc header parts */
+#define RPC_SIZ 24
+#define RPC_REPLYSIZ 28
+
+/* RPC Prog definitions */
+#define RPCPROG_MNT 100005
+#define RPCMNT_VER1 1
+#define RPCMNT_MOUNT 1
+#define RPCMNT_DUMP 2
+#define RPCMNT_UMOUNT 3
+#define RPCMNT_UMNTALL 4
+#define RPCMNT_EXPORT 5
+#define RPCMNT_NAMELEN 255
+#define RPCMNT_PATHLEN 1024
+#define RPCPROG_NFS 100003
diff --git a/stand/libsa/saioctl.h b/stand/libsa/saioctl.h
new file mode 100644
index 0000000..5f888ff
--- /dev/null
+++ b/stand/libsa/saioctl.h
@@ -0,0 +1,50 @@
+/* $NetBSD: saioctl.h,v 1.2 1994/10/26 05:45:04 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)saioctl.h 8.1 (Berkeley) 6/11/93
+ *
+ * $FreeBSD$
+ */
+
+/* ioctl's -- for disks just now */
+#define SAIOHDR (('d'<<8)|1) /* next i/o includes header */
+#define SAIOCHECK (('d'<<8)|2) /* next i/o checks data */
+#define SAIOHCHECK (('d'<<8)|3) /* next i/o checks header & data */
+#define SAIONOBAD (('d'<<8)|4) /* inhibit bad sector forwarding */
+#define SAIODOBAD (('d'<<8)|5) /* enable bad sector forwarding */
+#define SAIOECCLIM (('d'<<8)|6) /* set limit to ecc correction, bits */
+#define SAIOECCUNL (('d'<<8)|7) /* use standard ecc procedures */
+#define SAIORETRIES (('d'<<8)|8) /* set retry count for unit */
+#define SAIODEVDATA (('d'<<8)|9) /* get pointer to pack label */
+#define SAIOSSI (('d'<<8)|10) /* set skip sector inhibit */
+#define SAIONOSSI (('d'<<8)|11) /* inhibit skip sector handling */
+#define SAIOSSDEV (('d'<<8)|12) /* is device skip sector type? */
+#define SAIODEBUG (('d'<<8)|13) /* enable/disable debugging */
+#define SAIOGBADINFO (('d'<<8)|14) /* get bad-sector table */
diff --git a/stand/libsa/sbrk.c b/stand/libsa/sbrk.c
new file mode 100644
index 0000000..471e78e
--- /dev/null
+++ b/stand/libsa/sbrk.c
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1998 Michael Smith
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Minimal sbrk() emulation required for malloc support.
+ */
+
+#include <string.h>
+#include "stand.h"
+#include "zalloc_defs.h"
+
+static size_t maxheap, heapsize = 0;
+static void *heapbase;
+
+void
+setheap(void *base, void *top)
+{
+ /* Align start address for the malloc code. Sigh. */
+ heapbase = (void *)(((uintptr_t)base + MALLOCALIGN_MASK) &
+ ~MALLOCALIGN_MASK);
+ maxheap = (char *)top - (char *)heapbase;
+}
+
+char *
+sbrk(int incr)
+{
+ char *ret;
+
+ if ((heapsize + incr) <= maxheap) {
+ ret = (char *)heapbase + heapsize;
+ bzero(ret, incr);
+ heapsize += incr;
+ return(ret);
+ }
+ errno = ENOMEM;
+ return((char *)-1);
+}
+
diff --git a/stand/libsa/sparc64/_setjmp.S b/stand/libsa/sparc64/_setjmp.S
new file mode 100644
index 0000000..50c924e
--- /dev/null
+++ b/stand/libsa/sparc64/_setjmp.S
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Header: _setjmp.s,v 1.1 91/07/06 16:45:53 torek Exp
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+ .asciz "@(#)_setjmp.s 8.1 (Berkeley) 6/4/93"
+#else
+ RCSID("$NetBSD: _setjmp.S,v 1.4 1998/10/08 02:27:59 eeh Exp $")
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#define _JB_FP 0x0
+#define _JB_PC 0x8
+#define _JB_SP 0x10
+
+ .register %g2,#ignore
+ .register %g3,#ignore
+
+/*
+ * C library -- setjmp, longjmp
+ *
+ * longjmp(a,v)
+ * will generate a "return(v?v:1)" from
+ * the last call to
+ * setjmp(a)
+ * by restoring the previous context.
+ */
+
+ENTRY(_setjmp)
+ stx %sp, [%o0 + _JB_SP]
+ stx %o7, [%o0 + _JB_PC]
+ stx %fp, [%o0 + _JB_FP]
+ retl
+ clr %o0
+END(_setjmp)
+
+ENTRY(_longjmp)
+ mov 1, %g1
+ movrnz %o1, %o1, %g1
+ mov %o0, %g2
+ ldx [%g2 + _JB_FP], %g3
+1: cmp %fp, %g3
+ bl,a 1b
+ restore
+ be,a 2f
+ ldx [%g2 + _JB_SP], %o0
+
+.Lbotch:
+ illtrap
+
+2: cmp %o0, %sp
+ bge,a 3f
+ mov %o0, %sp
+ b,a .Lbotch
+ nop
+3: ldx [%g2 + _JB_PC], %o7
+ retl
+ mov %g1, %o0
+END(_longjmp)
diff --git a/stand/libsa/splitfs.c b/stand/libsa/splitfs.c
new file mode 100644
index 0000000..af28704
--- /dev/null
+++ b/stand/libsa/splitfs.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2002 Maxim Sobolev
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+#define NTRIES (3)
+#define CONF_BUF (512)
+#define SEEK_BUF (512)
+
+struct split_file
+{
+ char **filesv; /* Filenames */
+ char **descsv; /* Descriptions */
+ int filesc; /* Number of parts */
+ int curfile; /* Current file number */
+ int curfd; /* Current file descriptor */
+ off_t tot_pos; /* Offset from the beginning of the sequence */
+ off_t file_pos; /* Offset from the beginning of the slice */
+};
+
+static int split_openfile(struct split_file *sf);
+static int splitfs_open(const char *path, struct open_file *f);
+static int splitfs_close(struct open_file *f);
+static int splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t splitfs_seek(struct open_file *f, off_t offset, int where);
+static int splitfs_stat(struct open_file *f, struct stat *sb);
+
+struct fs_ops splitfs_fsops = {
+ "split",
+ splitfs_open,
+ splitfs_close,
+ splitfs_read,
+ null_write,
+ splitfs_seek,
+ splitfs_stat,
+ null_readdir
+};
+
+static void
+split_file_destroy(struct split_file *sf)
+{
+ int i;
+
+ if (sf->filesc > 0) {
+ for (i = 0; i < sf->filesc; i++) {
+ free(sf->filesv[i]);
+ free(sf->descsv[i]);
+ }
+ free(sf->filesv);
+ free(sf->descsv);
+ }
+ free(sf);
+}
+
+static int
+split_openfile(struct split_file *sf)
+{
+ int i;
+
+ for (i = 0;; i++) {
+ sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY);
+ if (sf->curfd >= 0)
+ break;
+ if ((sf->curfd == -1) && (errno != ENOENT))
+ return (errno);
+ if (i == NTRIES)
+ return (EIO);
+ printf("\nInsert disk labelled %s and press any key...",
+ sf->descsv[sf->curfile]);
+ getchar();
+ putchar('\n');
+ }
+ sf->file_pos = 0;
+ return (0);
+}
+
+static int
+splitfs_open(const char *fname, struct open_file *f)
+{
+ char *buf, *confname, *cp;
+ int conffd;
+ struct split_file *sf;
+ struct stat sb;
+
+ /* Have to be in "just read it" mode */
+ if (f->f_flags != F_READ)
+ return(EPERM);
+
+ /* If the name already ends in `.split', ignore it */
+ if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split")))
+ return(ENOENT);
+
+ /* Construct new name */
+ confname = malloc(strlen(fname) + 7);
+ sprintf(confname, "%s.split", fname);
+
+ /* Try to open the configuration file */
+ conffd = open(confname, O_RDONLY);
+ free(confname);
+ if (conffd == -1)
+ return(ENOENT);
+
+ if (fstat(conffd, &sb) < 0) {
+ printf("splitfs_open: stat failed\n");
+ close(conffd);
+ return(ENOENT);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ printf("splitfs_open: not a file\n");
+ close(conffd);
+ return(EISDIR); /* best guess */
+ }
+
+ /* Allocate a split_file structure, populate it from the config file */
+ sf = malloc(sizeof(struct split_file));
+ bzero(sf, sizeof(struct split_file));
+ buf = malloc(CONF_BUF);
+ while (fgetstr(buf, CONF_BUF, conffd) > 0) {
+ cp = buf;
+ while ((*cp != '\0') && (isspace(*cp) == 0))
+ cp++;
+ if (*cp != '\0') {
+ *cp = '\0';
+ cp++;
+ }
+ while ((*cp != '\0') && (isspace(*cp) != 0))
+ cp++;
+ if (*cp == '\0')
+ cp = buf;
+ sf->filesc++;
+ sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc);
+ sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc);
+ sf->filesv[sf->filesc - 1] = strdup(buf);
+ sf->descsv[sf->filesc - 1] = strdup(cp);
+ }
+ free(buf);
+ close(conffd);
+
+ if (sf->filesc == 0) {
+ split_file_destroy(sf);
+ return(ENOENT);
+ }
+ errno = split_openfile(sf);
+ if (errno != 0) {
+ split_file_destroy(sf);
+ return(ENOENT);
+ }
+
+ /* Looks OK, we'll take it */
+ f->f_fsdata = sf;
+ return (0);
+}
+
+static int
+splitfs_close(struct open_file *f)
+{
+ int fd;
+ struct split_file *sf;
+
+ sf = (struct split_file *)f->f_fsdata;
+ fd = sf->curfd;
+ split_file_destroy(sf);
+ return(close(fd));
+}
+
+static int
+splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ ssize_t nread;
+ size_t totread;
+ struct split_file *sf;
+
+ sf = (struct split_file *)f->f_fsdata;
+ totread = 0;
+ do {
+ nread = read(sf->curfd, buf, size - totread);
+
+ /* Error? */
+ if (nread == -1)
+ return (errno);
+
+ sf->tot_pos += nread;
+ sf->file_pos += nread;
+ totread += nread;
+ buf = (char *)buf + nread;
+
+ if (totread < size) { /* EOF */
+ if (sf->curfile == (sf->filesc - 1)) /* Last slice */
+ break;
+
+ /* Close previous slice */
+ if (close(sf->curfd) != 0)
+ return (errno);
+
+ sf->curfile++;
+ errno = split_openfile(sf);
+ if (errno)
+ return (errno);
+ }
+ } while (totread < size);
+
+ if (resid != NULL)
+ *resid = size - totread;
+
+ return (0);
+}
+
+static off_t
+splitfs_seek(struct open_file *f, off_t offset, int where)
+{
+ int nread;
+ size_t resid;
+ off_t new_pos, seek_by;
+ struct split_file *sf;
+
+ sf = (struct split_file *)f->f_fsdata;
+
+ seek_by = offset;
+ switch (where) {
+ case SEEK_SET:
+ seek_by -= sf->tot_pos;
+ break;
+ case SEEK_CUR:
+ break;
+ case SEEK_END:
+ panic("splitfs_seek: SEEK_END not supported");
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (seek_by > 0) {
+ /*
+ * Seek forward - implemented using splitfs_read(), because otherwise we'll be
+ * unable to detect that we have crossed slice boundary and hence
+ * unable to do a long seek crossing that boundary.
+ */
+ void *tmp;
+
+ tmp = malloc(SEEK_BUF);
+ if (tmp == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ nread = 0;
+ for (; seek_by > 0; seek_by -= nread) {
+ resid = 0;
+ errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid);
+ nread = min(seek_by, SEEK_BUF) - resid;
+ if ((errno != 0) || (nread == 0))
+ /* Error or EOF */
+ break;
+ }
+ free(tmp);
+ if (errno != 0)
+ return (-1);
+ }
+
+ if (seek_by != 0) {
+ /* Seek backward or seek past the boundary of the last slice */
+ if (sf->file_pos + seek_by < 0)
+ panic("splitfs_seek: can't seek past the beginning of the slice");
+ new_pos = lseek(sf->curfd, seek_by, SEEK_CUR);
+ if (new_pos < 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+ sf->tot_pos += new_pos - sf->file_pos;
+ sf->file_pos = new_pos;
+ }
+
+ return (sf->tot_pos);
+}
+
+static int
+splitfs_stat(struct open_file *f, struct stat *sb)
+{
+ int result;
+ struct split_file *sf = (struct split_file *)f->f_fsdata;
+
+ /* stat as normal, but indicate that size is unknown */
+ if ((result = fstat(sf->curfd, sb)) == 0)
+ sb->st_size = -1;
+ return (result);
+}
diff --git a/stand/libsa/stand.h b/stand/libsa/stand.h
new file mode 100644
index 0000000..deb6ad8
--- /dev/null
+++ b/stand/libsa/stand.h
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 1998 Michael Smith.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ * From $NetBSD: stand.h,v 1.22 1997/06/26 19:17:40 drochner Exp $
+ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)stand.h 8.1 (Berkeley) 6/11/93
+ */
+
+#ifndef STAND_H
+#define STAND_H
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <sys/dirent.h>
+
+/* this header intentionally exports NULL from <string.h> */
+#include <string.h>
+
+#define CHK(fmt, args...) printf("%s(%d): " fmt "\n", __func__, __LINE__ , ##args)
+#define PCHK(fmt, args...) {printf("%s(%d): " fmt "\n", __func__, __LINE__ , ##args); getchar();}
+
+/* Avoid unwanted userlandish components */
+#define _KERNEL
+#include <sys/errno.h>
+#undef _KERNEL
+
+/* special stand error codes */
+#define EADAPT (ELAST+1) /* bad adaptor */
+#define ECTLR (ELAST+2) /* bad controller */
+#define EUNIT (ELAST+3) /* bad unit */
+#define ESLICE (ELAST+4) /* bad slice */
+#define EPART (ELAST+5) /* bad partition */
+#define ERDLAB (ELAST+6) /* can't read disk label */
+#define EUNLAB (ELAST+7) /* unlabeled disk */
+#define EOFFSET (ELAST+8) /* relative seek not supported */
+#define ESALAST (ELAST+8) /* */
+
+struct open_file;
+
+/*
+ * This structure is used to define file system operations in a file system
+ * independent way.
+ *
+ * XXX note that filesystem providers should export a pointer to their fs_ops
+ * struct, so that consumers can reference this and thus include the
+ * filesystems that they require.
+ */
+struct fs_ops {
+ const char *fs_name;
+ int (*fo_open)(const char *path, struct open_file *f);
+ int (*fo_close)(struct open_file *f);
+ int (*fo_read)(struct open_file *f, void *buf,
+ size_t size, size_t *resid);
+ int (*fo_write)(struct open_file *f, void *buf,
+ size_t size, size_t *resid);
+ off_t (*fo_seek)(struct open_file *f, off_t offset, int where);
+ int (*fo_stat)(struct open_file *f, struct stat *sb);
+ int (*fo_readdir)(struct open_file *f, struct dirent *d);
+};
+
+/*
+ * libstand-supplied filesystems
+ */
+extern struct fs_ops ufs_fsops;
+extern struct fs_ops tftp_fsops;
+extern struct fs_ops nfs_fsops;
+extern struct fs_ops cd9660_fsops;
+extern struct fs_ops nandfs_fsops;
+extern struct fs_ops gzipfs_fsops;
+extern struct fs_ops bzipfs_fsops;
+extern struct fs_ops dosfs_fsops;
+extern struct fs_ops ext2fs_fsops;
+extern struct fs_ops splitfs_fsops;
+extern struct fs_ops pkgfs_fsops;
+
+/* where values for lseek(2) */
+#define SEEK_SET 0 /* set file offset to offset */
+#define SEEK_CUR 1 /* set file offset to current plus offset */
+#define SEEK_END 2 /* set file offset to EOF plus offset */
+
+/*
+ * Device switch
+ */
+struct devsw {
+ const char dv_name[8];
+ int dv_type; /* opaque type constant, arch-dependant */
+ int (*dv_init)(void); /* early probe call */
+ int (*dv_strategy)(void *devdata, int rw, daddr_t blk,
+ size_t size, char *buf, size_t *rsize);
+ int (*dv_open)(struct open_file *f, ...);
+ int (*dv_close)(struct open_file *f);
+ int (*dv_ioctl)(struct open_file *f, u_long cmd, void *data);
+ int (*dv_print)(int verbose); /* print device information */
+ void (*dv_cleanup)(void);
+};
+
+/*
+ * libstand-supplied device switch
+ */
+extern struct devsw netdev;
+
+extern int errno;
+
+/*
+ * Generic device specifier; architecture-dependent
+ * versions may be larger, but should be allowed to
+ * overlap.
+ */
+struct devdesc
+{
+ struct devsw *d_dev;
+ int d_type;
+#define DEVT_NONE 0
+#define DEVT_DISK 1
+#define DEVT_NET 2
+#define DEVT_CD 3
+#define DEVT_ZFS 4
+#define DEVT_FD 5
+ int d_unit;
+ void *d_opendata;
+};
+
+struct open_file {
+ int f_flags; /* see F_* below */
+ struct devsw *f_dev; /* pointer to device operations */
+ void *f_devdata; /* device specific data */
+ struct fs_ops *f_ops; /* pointer to file system operations */
+ void *f_fsdata; /* file system specific data */
+ off_t f_offset; /* current file offset */
+ char *f_rabuf; /* readahead buffer pointer */
+ size_t f_ralen; /* valid data in readahead buffer */
+ off_t f_raoffset; /* consumer offset in readahead buffer */
+#define SOPEN_RASIZE 512
+};
+
+#define SOPEN_MAX 64
+extern struct open_file files[];
+
+/* f_flags values */
+#define F_READ 0x0001 /* file opened for reading */
+#define F_WRITE 0x0002 /* file opened for writing */
+#define F_RAW 0x0004 /* raw device open - no file system */
+#define F_NODEV 0x0008 /* network open - no device */
+#define F_MASK 0xFFFF
+/* Mode modifier for strategy() */
+#define F_NORA (0x01 << 16) /* Disable Read-Ahead */
+
+#define isascii(c) (((c) & ~0x7F) == 0)
+
+static __inline int isupper(int c)
+{
+ return c >= 'A' && c <= 'Z';
+}
+
+static __inline int islower(int c)
+{
+ return c >= 'a' && c <= 'z';
+}
+
+static __inline int isspace(int c)
+{
+ return c == ' ' || (c >= 0x9 && c <= 0xd);
+}
+
+static __inline int isdigit(int c)
+{
+ return c >= '0' && c <= '9';
+}
+
+static __inline int isxdigit(int c)
+{
+ return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+}
+
+static __inline int isalpha(int c)
+{
+ return isupper(c) || islower(c);
+}
+
+static __inline int isalnum(int c)
+{
+ return isalpha(c) || isdigit(c);
+}
+
+static __inline int toupper(int c)
+{
+ return islower(c) ? c - 'a' + 'A' : c;
+}
+
+static __inline int tolower(int c)
+{
+ return isupper(c) ? c - 'A' + 'a' : c;
+}
+
+/* sbrk emulation */
+extern void setheap(void *base, void *top);
+extern char *sbrk(int incr);
+
+/* Matt Dillon's zalloc/zmalloc */
+extern void *malloc(size_t bytes);
+extern void free(void *ptr);
+/*#define free(p) {CHK("free %p", p); free(p);} */ /* use for catching guard violations */
+extern void *calloc(size_t n1, size_t n2);
+extern void *realloc(void *ptr, size_t size);
+extern void *reallocf(void *ptr, size_t size);
+extern void mallocstats(void);
+
+extern int printf(const char *fmt, ...) __printflike(1, 2);
+extern void vprintf(const char *fmt, __va_list);
+extern int sprintf(char *buf, const char *cfmt, ...) __printflike(2, 3);
+extern int snprintf(char *buf, size_t size, const char *cfmt, ...) __printflike(3, 4);
+extern void vsprintf(char *buf, const char *cfmt, __va_list);
+
+extern void twiddle(u_int callerdiv);
+extern void twiddle_divisor(u_int globaldiv);
+
+extern void ngets(char *, int);
+#define gets(x) ngets((x), 0)
+extern int fgetstr(char *buf, int size, int fd);
+
+extern int open(const char *, int);
+#define O_RDONLY 0x0
+#define O_WRONLY 0x1
+#define O_RDWR 0x2
+extern int close(int);
+extern void closeall(void);
+extern ssize_t read(int, void *, size_t);
+extern ssize_t write(int, void *, size_t);
+extern struct dirent *readdirfd(int);
+
+extern void srandom(u_long seed);
+extern long random(void);
+
+/* imports from stdlib, locally modified */
+extern long strtol(const char *, char **, int);
+extern unsigned long strtoul(const char *, char **, int);
+extern char *optarg; /* getopt(3) external variables */
+extern int optind, opterr, optopt, optreset;
+extern int getopt(int, char * const [], const char *);
+
+/* pager.c */
+extern void pager_open(void);
+extern void pager_close(void);
+extern int pager_output(const char *lines);
+extern int pager_file(const char *fname);
+
+/* No signal state to preserve */
+#define setjmp _setjmp
+#define longjmp _longjmp
+
+/* environment.c */
+#define EV_DYNAMIC (1<<0) /* value was dynamically allocated, free if changed/unset */
+#define EV_VOLATILE (1<<1) /* value is volatile, make a copy of it */
+#define EV_NOHOOK (1<<2) /* don't call hook when setting */
+
+struct env_var;
+typedef char *(ev_format_t)(struct env_var *ev);
+typedef int (ev_sethook_t)(struct env_var *ev, int flags,
+ const void *value);
+typedef int (ev_unsethook_t)(struct env_var *ev);
+
+struct env_var
+{
+ char *ev_name;
+ int ev_flags;
+ void *ev_value;
+ ev_sethook_t *ev_sethook;
+ ev_unsethook_t *ev_unsethook;
+ struct env_var *ev_next, *ev_prev;
+};
+extern struct env_var *environ;
+
+extern struct env_var *env_getenv(const char *name);
+extern int env_setenv(const char *name, int flags,
+ const void *value, ev_sethook_t sethook,
+ ev_unsethook_t unsethook);
+extern char *getenv(const char *name);
+extern int setenv(const char *name, const char *value,
+ int overwrite);
+extern int putenv(char *string);
+extern int unsetenv(const char *name);
+
+extern ev_sethook_t env_noset; /* refuse set operation */
+extern ev_unsethook_t env_nounset; /* refuse unset operation */
+
+/* BCD conversions (undocumented) */
+extern u_char const bcd2bin_data[];
+extern u_char const bin2bcd_data[];
+extern char const hex2ascii_data[];
+
+#define bcd2bin(bcd) (bcd2bin_data[bcd])
+#define bin2bcd(bin) (bin2bcd_data[bin])
+#define hex2ascii(hex) (hex2ascii_data[hex])
+
+/* min/max (undocumented) */
+static __inline int imax(int a, int b) { return (a > b ? a : b); }
+static __inline int imin(int a, int b) { return (a < b ? a : b); }
+static __inline long lmax(long a, long b) { return (a > b ? a : b); }
+static __inline long lmin(long a, long b) { return (a < b ? a : b); }
+static __inline u_int max(u_int a, u_int b) { return (a > b ? a : b); }
+static __inline u_int min(u_int a, u_int b) { return (a < b ? a : b); }
+static __inline quad_t qmax(quad_t a, quad_t b) { return (a > b ? a : b); }
+static __inline quad_t qmin(quad_t a, quad_t b) { return (a < b ? a : b); }
+static __inline u_long ulmax(u_long a, u_long b) { return (a > b ? a : b); }
+static __inline u_long ulmin(u_long a, u_long b) { return (a < b ? a : b); }
+
+
+/* null functions for device/filesystem switches (undocumented) */
+extern int nodev(void);
+extern int noioctl(struct open_file *, u_long, void *);
+extern void nullsys(void);
+
+extern int null_open(const char *path, struct open_file *f);
+extern int null_close(struct open_file *f);
+extern int null_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+extern int null_write(struct open_file *f, void *buf, size_t size, size_t *resid);
+extern off_t null_seek(struct open_file *f, off_t offset, int where);
+extern int null_stat(struct open_file *f, struct stat *sb);
+extern int null_readdir(struct open_file *f, struct dirent *d);
+
+
+/*
+ * Machine dependent functions and data, must be provided or stubbed by
+ * the consumer
+ */
+extern void exit(int);
+extern int getchar(void);
+extern int ischar(void);
+extern void putchar(int);
+extern int devopen(struct open_file *, const char *, const char **);
+extern int devclose(struct open_file *f);
+extern void panic(const char *, ...) __dead2 __printflike(1, 2);
+extern struct fs_ops *file_system[];
+extern struct fs_ops *exclusive_file_system;
+extern struct devsw *devsw[];
+
+/*
+ * Expose byteorder(3) functions.
+ */
+#ifndef _BYTEORDER_PROTOTYPED
+#define _BYTEORDER_PROTOTYPED
+extern uint32_t htonl(uint32_t);
+extern uint16_t htons(uint16_t);
+extern uint32_t ntohl(uint32_t);
+extern uint16_t ntohs(uint16_t);
+#endif
+
+#ifndef _BYTEORDER_FUNC_DEFINED
+#define _BYTEORDER_FUNC_DEFINED
+#define htonl(x) __htonl(x)
+#define htons(x) __htons(x)
+#define ntohl(x) __ntohl(x)
+#define ntohs(x) __ntohs(x)
+#endif
+
+void *Malloc(size_t, const char *, int);
+void *Calloc(size_t, size_t, const char *, int);
+void *Realloc(void *, size_t, const char *, int);
+void Free(void *, const char *, int);
+
+#if 1
+#define malloc(x) Malloc(x, __FILE__, __LINE__)
+#define calloc(x, y) Calloc(x, y, __FILE__, __LINE__)
+#define free(x) Free(x, __FILE__, __LINE__)
+#define realloc(x, y) Realloc(x, y, __FILE__, __LINE__)
+#else
+#define malloc(x) Malloc(x, NULL, 0)
+#define calloc(x, y) Calloc(x, y, NULL, 0)
+#define free(x) Free(x, NULL, 0)
+#define realloc(x, y) Realloc(x, y, NULL, 0)
+#endif
+
+#endif /* STAND_H */
diff --git a/stand/libsa/stat.c b/stand/libsa/stat.c
new file mode 100644
index 0000000..addee5d
--- /dev/null
+++ b/stand/libsa/stat.c
@@ -0,0 +1,52 @@
+/* $NetBSD: stat.c,v 1.4 1996/01/13 22:25:43 leo Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)stat.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+int
+stat(str, sb)
+ const char *str;
+ struct stat *sb;
+{
+ int fd, rv;
+
+ fd = open(str, O_RDONLY);
+ if (fd < 0)
+ return (-1);
+ rv = fstat(fd, sb);
+ (void)close(fd);
+ return (rv);
+}
diff --git a/stand/libsa/strcasecmp.c b/stand/libsa/strcasecmp.c
new file mode 100644
index 0000000..7ef3f59
--- /dev/null
+++ b/stand/libsa/strcasecmp.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/cdefs.h>
+#include <string.h>
+#include "stand.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+int
+strcasecmp(s1, s2)
+ const char *s1, *s2;
+{
+ const u_char
+ *us1 = (const u_char *)s1,
+ *us2 = (const u_char *)s2;
+
+ while (tolower(*us1) == tolower(*us2++))
+ if (*us1++ == '\0')
+ return (0);
+ return (tolower(*us1) - tolower(*--us2));
+}
+
+int
+strncasecmp(s1, s2, n)
+ const char *s1, *s2;
+ size_t n;
+{
+ if (n != 0) {
+ const u_char
+ *us1 = (const u_char *)s1,
+ *us2 = (const u_char *)s2;
+
+ do {
+ if (tolower(*us1) != tolower(*us2++))
+ return (tolower(*us1) - tolower(*--us2));
+ if (*us1++ == '\0')
+ break;
+ } while (--n != 0);
+ }
+ return (0);
+}
diff --git a/stand/libsa/strdup.c b/stand/libsa/strdup.c
new file mode 100644
index 0000000..45a5414
--- /dev/null
+++ b/stand/libsa/strdup.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strdup.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "stand.h"
+#include <stddef.h>
+#include <string.h>
+
+char *
+strdup(str)
+ const char *str;
+{
+ size_t len;
+ char *copy = NULL;
+
+ if (str != NULL) {
+ len = strlen(str) + 1;
+ if ((copy = malloc(len)) == NULL)
+ return (NULL);
+ memcpy(copy, str, len);
+ }
+ return (copy);
+}
diff --git a/stand/libsa/strerror.c b/stand/libsa/strerror.c
new file mode 100644
index 0000000..56571ef
--- /dev/null
+++ b/stand/libsa/strerror.c
@@ -0,0 +1,87 @@
+/* $NetBSD: strerror.c,v 1.12 1997/01/25 00:37:50 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+static struct
+{
+ int err;
+ char *msg;
+} errtab[] = {
+ {0, "no error"},
+ /* standard errors */
+ {EPERM, "operation not permitted"},
+ {ENOENT, "no such file or directory"},
+ {EIO, "input/output error"},
+ {ENXIO, "device not configured"},
+ {ENOEXEC, "exec format error"},
+ {EBADF, "bad file descriptor"},
+ {ENOMEM, "cannot allocate memory"},
+ {ENODEV, "operation not supported by device"},
+ {ENOTDIR, "not a directory"},
+ {EISDIR, "is a directory"},
+ {EINVAL, "invalid argument"},
+ {EMFILE, "too many open files"},
+ {EFBIG, "file too large"},
+ {EROFS, "read-only filesystem"},
+ {EOPNOTSUPP, "operation not supported"},
+ {ETIMEDOUT, "operation timed out"},
+ {ESTALE, "stale NFS file handle"},
+ {EBADRPC, "RPC struct is bad"},
+ {EFTYPE, "inappropriate file type or format"},
+
+ {EADAPT, "bad adaptor number"},
+ {ECTLR, "bad controller number"},
+ {EUNIT, "bad unit number"},
+ {ESLICE, "bad slice number"},
+ {EPART, "bad partition"},
+ {ERDLAB, "can't read disk label"},
+ {EUNLAB, "disk unlabelled"},
+ {EOFFSET, "illegal seek"},
+ {0, NULL}
+};
+
+
+char *
+strerror(int err)
+{
+ static char msg[32];
+ int i;
+
+ for (i = 0; errtab[i].msg != NULL; i++)
+ if (errtab[i].err == err)
+ return(errtab[i].msg);
+ sprintf(msg, "unknown error (%d)", err);
+ return(msg);
+}
diff --git a/stand/libsa/strtol.c b/stand/libsa/strtol.c
new file mode 100644
index 0000000..be82fb1
--- /dev/null
+++ b/stand/libsa/strtol.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "stand.h"
+#include <limits.h>
+
+/*
+ * Convert a string to a long integer.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+long
+strtol(nptr, endptr, base)
+ const char *nptr;
+ char **endptr;
+ int base;
+{
+ const char *s;
+ unsigned long acc;
+ unsigned char c;
+ unsigned long cutoff;
+ int neg = 0, any, cutlim;
+
+ /* Be sensible about NULL strings */
+ if (nptr == NULL)
+ nptr = "";
+ s = nptr;
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ do {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for longs is
+ * [-2147483648..2147483647] and the input base is 10,
+ * cutoff will be set to 214748364 and cutlim to either
+ * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+ * a value > 214748364, or equal but the next digit is > 7 (or 8),
+ * the number is too big, and we will return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
+ cutlim = cutoff % (unsigned long)base;
+ cutoff /= (unsigned long)base;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (!isascii(c))
+ break;
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = neg ? LONG_MIN : LONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != NULL)
+ *endptr = (char *)(any ? s - 1 : nptr);
+ return (acc);
+}
diff --git a/stand/libsa/strtoul.c b/stand/libsa/strtoul.c
new file mode 100644
index 0000000..5735d20
--- /dev/null
+++ b/stand/libsa/strtoul.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * All rights reserved.
+ * Portions of this software were developed by David Chisnall
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strtoul.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+#include <limits.h>
+
+/*
+ * Convert a string to an unsigned long integer.
+ *
+ * Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+unsigned long
+strtoul(const char * __restrict nptr, char ** __restrict endptr, int base)
+{
+ const char *s;
+ unsigned long acc;
+ char c;
+ unsigned long cutoff;
+ int neg, any, cutlim;
+
+ /*
+ * See strtol for comments as to the logic used.
+ */
+ s = nptr;
+ do {
+ c = *s++;
+ } while (isspace((unsigned char)c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else {
+ neg = 0;
+ if (c == '+')
+ c = *s++;
+ }
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X') &&
+ ((s[1] >= '0' && s[1] <= '9') ||
+ (s[1] >= 'A' && s[1] <= 'F') ||
+ (s[1] >= 'a' && s[1] <= 'f'))) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+ acc = any = 0;
+ if (base < 2 || base > 36)
+ goto noconv;
+
+ cutoff = ULONG_MAX / base;
+ cutlim = ULONG_MAX % base;
+ for ( ; ; c = *s++) {
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'A' && c <= 'Z')
+ c -= 'A' - 10;
+ else if (c >= 'a' && c <= 'z')
+ c -= 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = ULONG_MAX;
+ errno = ERANGE;
+ } else if (!any) {
+noconv:
+ errno = EINVAL;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != NULL)
+ *endptr = (char *)(any ? s - 1 : nptr);
+ return (acc);
+}
diff --git a/stand/libsa/tftp.c b/stand/libsa/tftp.c
new file mode 100644
index 0000000..9bef464
--- /dev/null
+++ b/stand/libsa/tftp.c
@@ -0,0 +1,785 @@
+/* $NetBSD: tftp.c,v 1.4 1997/09/17 16:57:07 drochner Exp $ */
+
+/*
+ * Copyright (c) 1996
+ * Matthias Drochner. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by Matthias Drochner.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Simple TFTP implementation for libsa.
+ * Assumes:
+ * - socket descriptor (int) at open_file->f_devdata
+ * - server host IP in global servip
+ * Restrictions:
+ * - read only
+ * - lseek only with SEEK_SET or SEEK_CUR
+ * - no big time differences between transfers (<tftp timeout)
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <netinet/in_systm.h>
+#include <arpa/tftp.h>
+
+#include <string.h>
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+
+#include "tftp.h"
+
+struct tftp_handle;
+
+static int tftp_open(const char *path, struct open_file *f);
+static int tftp_close(struct open_file *f);
+static int tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len);
+static int tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static int tftp_write(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t tftp_seek(struct open_file *f, off_t offset, int where);
+static int tftp_set_blksize(struct tftp_handle *h, const char *str);
+static int tftp_stat(struct open_file *f, struct stat *sb);
+static ssize_t sendrecv_tftp(struct tftp_handle *h,
+ ssize_t (*sproc)(struct iodesc *, void *, size_t),
+ void *sbuf, size_t ssize,
+ ssize_t (*rproc)(struct tftp_handle *h, void **, void **, time_t, unsigned short *),
+ void **, void **, unsigned short *rtype);
+
+struct fs_ops tftp_fsops = {
+ "tftp",
+ tftp_open,
+ tftp_close,
+ tftp_read,
+ tftp_write,
+ tftp_seek,
+ tftp_stat,
+ null_readdir
+};
+
+extern struct in_addr servip;
+
+static int tftpport = 2000;
+static int is_open = 0;
+
+/*
+ * The legacy TFTP_BLKSIZE value was SEGSIZE(512).
+ * TFTP_REQUESTED_BLKSIZE of 1428 is (Ethernet MTU, less the TFTP, UDP and
+ * IP header lengths).
+ */
+#define TFTP_REQUESTED_BLKSIZE 1428
+
+/*
+ * Choose a blksize big enough so we can test with Ethernet
+ * Jumbo frames in the future.
+ */
+#define TFTP_MAX_BLKSIZE 9008
+
+struct tftp_handle {
+ struct iodesc *iodesc;
+ int currblock; /* contents of lastdata */
+ int islastblock; /* flag */
+ int validsize;
+ int off;
+ char *path; /* saved for re-requests */
+ unsigned int tftp_blksize;
+ unsigned long tftp_tsize;
+ void *pkt;
+ struct tftphdr *tftp_hdr;
+};
+
+#define TFTP_MAX_ERRCODE EOPTNEG
+static const int tftperrors[TFTP_MAX_ERRCODE + 1] = {
+ 0, /* ??? */
+ ENOENT,
+ EPERM,
+ ENOSPC,
+ EINVAL, /* ??? */
+ EINVAL, /* ??? */
+ EEXIST,
+ EINVAL, /* ??? */
+ EINVAL, /* Option negotiation failed. */
+};
+
+static int tftp_getnextblock(struct tftp_handle *h);
+
+/* send error message back. */
+static void
+tftp_senderr(struct tftp_handle *h, u_short errcode, const char *msg)
+{
+ struct {
+ u_char header[HEADER_SIZE];
+ struct tftphdr t;
+ u_char space[63]; /* +1 from t */
+ } __packed __aligned(4) wbuf;
+ char *wtail;
+ int len;
+
+ len = strlen(msg);
+ if (len > sizeof(wbuf.space))
+ len = sizeof(wbuf.space);
+
+ wbuf.t.th_opcode = htons((u_short) ERROR);
+ wbuf.t.th_code = htons(errcode);
+
+ wtail = wbuf.t.th_msg;
+ bcopy(msg, wtail, len);
+ wtail[len] = '\0';
+ wtail += len + 1;
+
+ sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
+}
+
+static void
+tftp_sendack(struct tftp_handle *h)
+{
+ struct {
+ u_char header[HEADER_SIZE];
+ struct tftphdr t;
+ } __packed __aligned(4) wbuf;
+ char *wtail;
+
+ wbuf.t.th_opcode = htons((u_short) ACK);
+ wtail = (char *) &wbuf.t.th_block;
+ wbuf.t.th_block = htons((u_short) h->currblock);
+ wtail += 2;
+
+ sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
+}
+
+static ssize_t
+recvtftp(struct tftp_handle *h, void **pkt, void **payload, time_t tleft,
+ unsigned short *rtype)
+{
+ struct iodesc *d = h->iodesc;
+ struct tftphdr *t;
+ void *ptr = NULL;
+ ssize_t len;
+
+ errno = 0;
+
+ len = readudp(d, &ptr, (void **)&t, tleft);
+
+ if (len < 4) {
+ free(ptr);
+ return (-1);
+ }
+
+ *rtype = ntohs(t->th_opcode);
+ switch (ntohs(t->th_opcode)) {
+ case DATA: {
+ int got;
+
+ if (htons(t->th_block) != (u_short) d->xid) {
+ /*
+ * Expected block?
+ */
+ free(ptr);
+ return (-1);
+ }
+ if (d->xid == 1) {
+ /*
+ * First data packet from new port.
+ */
+ struct udphdr *uh;
+ uh = (struct udphdr *) t - 1;
+ d->destport = uh->uh_sport;
+ } /* else check uh_sport has not changed??? */
+ got = len - (t->th_data - (char *)t);
+ *pkt = ptr;
+ *payload = t;
+ return (got);
+ }
+ case ERROR:
+ if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) {
+ printf("illegal tftp error %d\n", ntohs(t->th_code));
+ errno = EIO;
+ } else {
+#ifdef TFTP_DEBUG
+ printf("tftp-error %d\n", ntohs(t->th_code));
+#endif
+ errno = tftperrors[ntohs(t->th_code)];
+ }
+ free(ptr);
+ return (-1);
+ case OACK: {
+ struct udphdr *uh;
+ int tftp_oack_len;
+
+ /*
+ * Unexpected OACK. TFTP transfer already in progress.
+ * Drop the pkt.
+ */
+ if (d->xid != 1) {
+ free(ptr);
+ return (-1);
+ }
+
+ /*
+ * Remember which port this OACK came from, because we need
+ * to send the ACK or errors back to it.
+ */
+ uh = (struct udphdr *) t - 1;
+ d->destport = uh->uh_sport;
+
+ /* Parse options ACK-ed by the server. */
+ tftp_oack_len = len - sizeof(t->th_opcode);
+ if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) {
+ tftp_senderr(h, EOPTNEG, "Malformed OACK");
+ errno = EIO;
+ free(ptr);
+ return (-1);
+ }
+ *pkt = ptr;
+ *payload = t;
+ return (0);
+ }
+ default:
+#ifdef TFTP_DEBUG
+ printf("tftp type %d not handled\n", ntohs(t->th_opcode));
+#endif
+ free(ptr);
+ return (-1);
+ }
+}
+
+/* send request, expect first block (or error) */
+static int
+tftp_makereq(struct tftp_handle *h)
+{
+ struct {
+ u_char header[HEADER_SIZE];
+ struct tftphdr t;
+ u_char space[FNAME_SIZE + 6];
+ } __packed __aligned(4) wbuf;
+ char *wtail;
+ int l;
+ ssize_t res;
+ void *pkt;
+ struct tftphdr *t;
+ char *tftp_blksize = NULL;
+ int blksize_l;
+ unsigned short rtype = 0;
+
+ /*
+ * Allow overriding default TFTP block size by setting
+ * a tftp.blksize environment variable.
+ */
+ if ((tftp_blksize = getenv("tftp.blksize")) != NULL) {
+ tftp_set_blksize(h, tftp_blksize);
+ }
+
+ wbuf.t.th_opcode = htons((u_short) RRQ);
+ wtail = wbuf.t.th_stuff;
+ l = strlen(h->path);
+#ifdef TFTP_PREPEND_PATH
+ if (l > FNAME_SIZE - (sizeof(TFTP_PREPEND_PATH) - 1))
+ return (ENAMETOOLONG);
+ bcopy(TFTP_PREPEND_PATH, wtail, sizeof(TFTP_PREPEND_PATH) - 1);
+ wtail += sizeof(TFTP_PREPEND_PATH) - 1;
+#else
+ if (l > FNAME_SIZE)
+ return (ENAMETOOLONG);
+#endif
+ bcopy(h->path, wtail, l + 1);
+ wtail += l + 1;
+ bcopy("octet", wtail, 6);
+ wtail += 6;
+ bcopy("blksize", wtail, 8);
+ wtail += 8;
+ blksize_l = sprintf(wtail, "%d", h->tftp_blksize);
+ wtail += blksize_l + 1;
+ bcopy("tsize", wtail, 6);
+ wtail += 6;
+ bcopy("0", wtail, 2);
+ wtail += 2;
+
+ /* h->iodesc->myport = htons(--tftpport); */
+ h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
+ h->iodesc->destport = htons(IPPORT_TFTP);
+ h->iodesc->xid = 1; /* expected block */
+
+ h->currblock = 0;
+ h->islastblock = 0;
+ h->validsize = 0;
+
+ pkt = NULL;
+ res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
+ &recvtftp, &pkt, (void **)&t, &rtype);
+ if (res == -1) {
+ free(pkt);
+ return (errno);
+ }
+
+ free(h->pkt);
+ h->pkt = pkt;
+ h->tftp_hdr = t;
+
+ if (rtype == OACK)
+ return (tftp_getnextblock(h));
+
+ /* Server ignored our blksize request, revert to TFTP default. */
+ h->tftp_blksize = SEGSIZE;
+
+ switch (rtype) {
+ case DATA: {
+ h->currblock = 1;
+ h->validsize = res;
+ h->islastblock = 0;
+ if (res < h->tftp_blksize) {
+ h->islastblock = 1; /* very short file */
+ tftp_sendack(h);
+ }
+ return (0);
+ }
+ case ERROR:
+ default:
+ return (errno);
+ }
+
+}
+
+/* ack block, expect next */
+static int
+tftp_getnextblock(struct tftp_handle *h)
+{
+ struct {
+ u_char header[HEADER_SIZE];
+ struct tftphdr t;
+ } __packed __aligned(4) wbuf;
+ char *wtail;
+ int res;
+ void *pkt;
+ struct tftphdr *t;
+ unsigned short rtype = 0;
+ wbuf.t.th_opcode = htons((u_short) ACK);
+ wtail = (char *) &wbuf.t.th_block;
+ wbuf.t.th_block = htons((u_short) h->currblock);
+ wtail += 2;
+
+ h->iodesc->xid = h->currblock + 1; /* expected block */
+
+ pkt = NULL;
+ res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
+ &recvtftp, &pkt, (void **)&t, &rtype);
+
+ if (res == -1) { /* 0 is OK! */
+ free(pkt);
+ return (errno);
+ }
+
+ free(h->pkt);
+ h->pkt = pkt;
+ h->tftp_hdr = t;
+ h->currblock++;
+ h->validsize = res;
+ if (res < h->tftp_blksize)
+ h->islastblock = 1; /* EOF */
+
+ if (h->islastblock == 1) {
+ /* Send an ACK for the last block */
+ wbuf.t.th_block = htons((u_short) h->currblock);
+ sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
+ }
+
+ return (0);
+}
+
+static int
+tftp_open(const char *path, struct open_file *f)
+{
+ struct tftp_handle *tftpfile;
+ struct iodesc *io;
+ int res;
+ size_t pathsize;
+ const char *extraslash;
+
+ if (netproto != NET_TFTP)
+ return (EINVAL);
+
+ if (f->f_dev->dv_type != DEVT_NET)
+ return (EINVAL);
+
+ if (is_open)
+ return (EBUSY);
+
+ tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile));
+ if (!tftpfile)
+ return (ENOMEM);
+
+ memset(tftpfile, 0, sizeof(*tftpfile));
+ tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE;
+ tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
+ if (io == NULL)
+ return (EINVAL);
+
+ io->destip = servip;
+ tftpfile->off = 0;
+ pathsize = (strlen(rootpath) + 1 + strlen(path) + 1) * sizeof(char);
+ tftpfile->path = malloc(pathsize);
+ if (tftpfile->path == NULL) {
+ free(tftpfile);
+ return(ENOMEM);
+ }
+ if (rootpath[strlen(rootpath) - 1] == '/' || path[0] == '/')
+ extraslash = "";
+ else
+ extraslash = "/";
+ res = snprintf(tftpfile->path, pathsize, "%s%s%s",
+ rootpath, extraslash, path);
+ if (res < 0 || res > pathsize) {
+ free(tftpfile->path);
+ free(tftpfile);
+ return(ENOMEM);
+ }
+
+ res = tftp_makereq(tftpfile);
+
+ if (res) {
+ free(tftpfile->path);
+ free(tftpfile->pkt);
+ free(tftpfile);
+ return (res);
+ }
+ f->f_fsdata = (void *) tftpfile;
+ is_open = 1;
+ return (0);
+}
+
+static int
+tftp_read(struct open_file *f, void *addr, size_t size,
+ size_t *resid /* out */)
+{
+ struct tftp_handle *tftpfile;
+ tftpfile = (struct tftp_handle *) f->f_fsdata;
+
+ while (size > 0) {
+ int needblock, count;
+
+ twiddle(32);
+
+ needblock = tftpfile->off / tftpfile->tftp_blksize + 1;
+
+ if (tftpfile->currblock > needblock) { /* seek backwards */
+ tftp_senderr(tftpfile, 0, "No error: read aborted");
+ tftp_makereq(tftpfile); /* no error check, it worked
+ * for open */
+ }
+
+ while (tftpfile->currblock < needblock) {
+ int res;
+
+ res = tftp_getnextblock(tftpfile);
+ if (res) { /* no answer */
+#ifdef TFTP_DEBUG
+ printf("tftp: read error\n");
+#endif
+ return (res);
+ }
+ if (tftpfile->islastblock)
+ break;
+ }
+
+ if (tftpfile->currblock == needblock) {
+ int offinblock, inbuffer;
+
+ offinblock = tftpfile->off % tftpfile->tftp_blksize;
+
+ inbuffer = tftpfile->validsize - offinblock;
+ if (inbuffer < 0) {
+#ifdef TFTP_DEBUG
+ printf("tftp: invalid offset %d\n",
+ tftpfile->off);
+#endif
+ return (EINVAL);
+ }
+ count = (size < inbuffer ? size : inbuffer);
+ bcopy(tftpfile->tftp_hdr->th_data + offinblock,
+ addr, count);
+
+ addr = (char *)addr + count;
+ tftpfile->off += count;
+ size -= count;
+
+ if ((tftpfile->islastblock) && (count == inbuffer))
+ break; /* EOF */
+ } else {
+#ifdef TFTP_DEBUG
+ printf("tftp: block %d not found\n", needblock);
+#endif
+ return (EINVAL);
+ }
+
+ }
+
+ if (resid)
+ *resid = size;
+ return (0);
+}
+
+static int
+tftp_close(struct open_file *f)
+{
+ struct tftp_handle *tftpfile;
+ tftpfile = (struct tftp_handle *) f->f_fsdata;
+
+ /* let it time out ... */
+
+ if (tftpfile) {
+ free(tftpfile->path);
+ free(tftpfile->pkt);
+ free(tftpfile);
+ }
+ is_open = 0;
+ return (0);
+}
+
+static int
+tftp_write(struct open_file *f __unused, void *start __unused, size_t size __unused,
+ size_t *resid __unused /* out */)
+{
+ return (EROFS);
+}
+
+static int
+tftp_stat(struct open_file *f, struct stat *sb)
+{
+ struct tftp_handle *tftpfile;
+ tftpfile = (struct tftp_handle *) f->f_fsdata;
+
+ sb->st_mode = 0444 | S_IFREG;
+ sb->st_nlink = 1;
+ sb->st_uid = 0;
+ sb->st_gid = 0;
+ sb->st_size = (off_t) tftpfile->tftp_tsize;
+ return (0);
+}
+
+static off_t
+tftp_seek(struct open_file *f, off_t offset, int where)
+{
+ struct tftp_handle *tftpfile;
+ tftpfile = (struct tftp_handle *) f->f_fsdata;
+
+ switch (where) {
+ case SEEK_SET:
+ tftpfile->off = offset;
+ break;
+ case SEEK_CUR:
+ tftpfile->off += offset;
+ break;
+ default:
+ errno = EOFFSET;
+ return (-1);
+ }
+ return (tftpfile->off);
+}
+
+static ssize_t
+sendrecv_tftp(struct tftp_handle *h,
+ ssize_t (*sproc)(struct iodesc *, void *, size_t),
+ void *sbuf, size_t ssize,
+ ssize_t (*rproc)(struct tftp_handle *, void **, void **, time_t,
+ unsigned short *),
+ void **pkt, void **payload, unsigned short *rtype)
+{
+ struct iodesc *d = h->iodesc;
+ ssize_t cc;
+ time_t t, t1, tleft;
+
+#ifdef TFTP_DEBUG
+ if (debug)
+ printf("sendrecv: called\n");
+#endif
+
+ tleft = MINTMO;
+ t = t1 = getsecs();
+ for (;;) {
+ if ((getsecs() - t) > MAXTMO) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ cc = (*sproc)(d, sbuf, ssize);
+ if (cc != -1 && cc < ssize)
+ panic("sendrecv: short write! (%zd < %zu)",
+ cc, ssize);
+
+ if (cc == -1) {
+ /* Error on transmit; wait before retrying */
+ while ((getsecs() - t1) < tleft);
+ continue;
+ }
+
+recvnext:
+ /* Try to get a packet and process it. */
+ cc = (*rproc)(h, pkt, payload, tleft, rtype);
+ /* Return on data, EOF or real error. */
+ if (cc != -1 || errno != 0)
+ return (cc);
+ if ((getsecs() - t1) < tleft) {
+ goto recvnext;
+ }
+
+ /* Timed out or didn't get the packet we're waiting for */
+ tleft += MINTMO;
+ if (tleft > (2 * MINTMO)) {
+ tleft = (2 * MINTMO);
+ }
+ t1 = getsecs();
+ }
+}
+
+static int
+tftp_set_blksize(struct tftp_handle *h, const char *str)
+{
+ char *endptr;
+ int new_blksize;
+ int ret = 0;
+
+ if (h == NULL || str == NULL)
+ return (ret);
+
+ new_blksize =
+ (unsigned int)strtol(str, &endptr, 0);
+
+ /*
+ * Only accept blksize value if it is numeric.
+ * RFC2348 specifies that acceptable values are 8-65464.
+ * Let's choose a limit less than MAXRSPACE.
+ */
+ if (*endptr == '\0' && new_blksize >= 8
+ && new_blksize <= TFTP_MAX_BLKSIZE) {
+ h->tftp_blksize = new_blksize;
+ ret = 1;
+ }
+
+ return (ret);
+}
+
+/*
+ * In RFC2347, the TFTP Option Acknowledgement package (OACK)
+ * is used to acknowledge a client's option negotiation request.
+ * The format of an OACK packet is:
+ * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
+ * | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 |
+ * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
+ *
+ * opc
+ * The opcode field contains a 6, for Option Acknowledgment.
+ *
+ * opt1
+ * The first option acknowledgment, copied from the original
+ * request.
+ *
+ * value1
+ * The acknowledged value associated with the first option. If
+ * and how this value may differ from the original request is
+ * detailed in the specification for the option.
+ *
+ * optN, valueN
+ * The final option/value acknowledgment pair.
+ */
+static int
+tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len)
+{
+ /*
+ * We parse the OACK strings into an array
+ * of name-value pairs.
+ */
+ char *tftp_options[128] = { 0 };
+ char *val = buf;
+ int i = 0;
+ int option_idx = 0;
+ int blksize_is_set = 0;
+ int tsize = 0;
+
+ unsigned int orig_blksize;
+
+ while (option_idx < 128 && i < len) {
+ if (buf[i] == '\0') {
+ if (&buf[i] > val) {
+ tftp_options[option_idx] = val;
+ val = &buf[i] + 1;
+ ++option_idx;
+ }
+ }
+ ++i;
+ }
+
+ /* Save the block size we requested for sanity check later. */
+ orig_blksize = h->tftp_blksize;
+
+ /*
+ * Parse individual TFTP options.
+ * * "blksize" is specified in RFC2348.
+ * * "tsize" is specified in RFC2349.
+ */
+ for (i = 0; i < option_idx; i += 2) {
+ if (strcasecmp(tftp_options[i], "blksize") == 0) {
+ if (i + 1 < option_idx)
+ blksize_is_set =
+ tftp_set_blksize(h, tftp_options[i + 1]);
+ } else if (strcasecmp(tftp_options[i], "tsize") == 0) {
+ if (i + 1 < option_idx)
+ tsize = strtol(tftp_options[i + 1], (char **)NULL, 10);
+ if (tsize != 0)
+ h->tftp_tsize = tsize;
+ } else {
+ /* Do not allow any options we did not expect to be ACKed. */
+ printf("unexpected tftp option '%s'\n", tftp_options[i]);
+ return (-1);
+ }
+ }
+
+ if (!blksize_is_set) {
+ /*
+ * If TFTP blksize was not set, try defaulting
+ * to the legacy TFTP blksize of SEGSIZE(512)
+ */
+ h->tftp_blksize = SEGSIZE;
+ } else if (h->tftp_blksize > orig_blksize) {
+ /*
+ * Server should not be proposing block sizes that
+ * exceed what we said we can handle.
+ */
+ printf("unexpected blksize %u\n", h->tftp_blksize);
+ return (-1);
+ }
+
+#ifdef TFTP_DEBUG
+ printf("tftp_blksize: %u\n", h->tftp_blksize);
+ printf("tftp_tsize: %lu\n", h->tftp_tsize);
+#endif
+ return 0;
+}
diff --git a/stand/libsa/tftp.h b/stand/libsa/tftp.h
new file mode 100644
index 0000000..cbbbbd7
--- /dev/null
+++ b/stand/libsa/tftp.h
@@ -0,0 +1,36 @@
+/* $NetBSD: tftp.h,v 1.1.1.1 1997/03/14 02:40:31 perry Exp $ */
+
+/*
+ * Copyright (c) 1996
+ * Matthias Drochner. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by Matthias Drochner.
+ * 4. 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.
+ *
+ */
+
+
+#define IPPORT_TFTP 69
diff --git a/stand/libsa/twiddle.c b/stand/libsa/twiddle.c
new file mode 100644
index 0000000..96ebbbe
--- /dev/null
+++ b/stand/libsa/twiddle.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 1986, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include "stand.h"
+
+/* Extra functions from NetBSD standalone printf.c */
+
+static u_int globaldiv;
+
+void
+twiddle(u_int callerdiv)
+{
+ static u_int callercnt, globalcnt, pos;
+
+ callercnt++;
+ if (callerdiv > 1 && (callercnt % callerdiv) != 0)
+ return;
+
+ globalcnt++;
+ if (globaldiv > 1 && (globalcnt % globaldiv) != 0)
+ return;
+
+ putchar("|/-\\"[pos++ & 3]);
+ putchar('\b');
+}
+
+void
+twiddle_divisor(u_int gdiv)
+{
+
+ globaldiv = gdiv;
+}
diff --git a/stand/libsa/udp.c b/stand/libsa/udp.c
new file mode 100644
index 0000000..8e52f29
--- /dev/null
+++ b/stand/libsa/udp.c
@@ -0,0 +1,180 @@
+/* Taken from $NetBSD: net.c,v 1.20 1997/12/26 22:41:30 scottr Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: net.c,v 1.9 93/08/06 19:32:15 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <string.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+
+#include <netinet/in_pcb.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include "stand.h"
+#include "net.h"
+
+/* Caller must leave room for ethernet, ip and udp headers in front!! */
+ssize_t
+sendudp(struct iodesc *d, void *pkt, size_t len)
+{
+ ssize_t cc;
+ struct udpiphdr *ui;
+ struct udphdr *uh;
+
+#ifdef NET_DEBUG
+ if (debug) {
+ printf("sendudp: d=%lx called.\n", (long)d);
+ if (d) {
+ printf("saddr: %s:%d",
+ inet_ntoa(d->myip), ntohs(d->myport));
+ printf(" daddr: %s:%d\n",
+ inet_ntoa(d->destip), ntohs(d->destport));
+ }
+ }
+#endif
+
+ ui = (struct udpiphdr *)pkt - 1;
+ bzero(ui, sizeof(*ui));
+
+ uh = (struct udphdr *)pkt - 1;
+ len += sizeof(*uh);
+
+ uh->uh_sport = d->myport;
+ uh->uh_dport = d->destport;
+ uh->uh_ulen = htons(len);
+
+ ui->ui_pr = IPPROTO_UDP;
+ ui->ui_len = uh->uh_ulen;
+ ui->ui_src = d->myip;
+ ui->ui_dst = d->destip;
+
+#ifndef UDP_NO_CKSUM
+ uh->uh_sum = in_cksum(ui, len + sizeof (struct ip));
+#endif
+
+ cc = sendip(d, uh, len, IPPROTO_UDP);
+ if (cc == -1)
+ return (-1);
+ if (cc != len)
+ panic("sendudp: bad write (%zd != %zd)", cc, len);
+ return (cc - sizeof(*uh));
+}
+
+/*
+ * Receive a UDP packet and validate it is for us.
+ */
+ssize_t
+readudp(struct iodesc *d, void **pkt, void **payload, time_t tleft)
+{
+ ssize_t n;
+ struct udphdr *uh;
+ void *ptr;
+
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readudp: called\n");
+#endif
+
+ uh = NULL;
+ ptr = NULL;
+ n = readip(d, &ptr, (void **)&uh, tleft, IPPROTO_UDP);
+ if (n == -1 || n < sizeof(*uh) || n != ntohs(uh->uh_ulen)) {
+ free(ptr);
+ return (-1);
+ }
+
+ if (uh->uh_dport != d->myport) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readudp: bad dport %d != %d\n",
+ d->myport, ntohs(uh->uh_dport));
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+#ifndef UDP_NO_CKSUM
+ if (uh->uh_sum) {
+ struct udpiphdr *ui;
+ struct ip *ip;
+ struct ip tip;
+
+ n = ntohs(uh->uh_ulen) + sizeof(*ip);
+
+ /* Check checksum (must save and restore ip header) */
+ ip = (struct ip *)uh - 1;
+ tip = *ip;
+ ui = (struct udpiphdr *)ip;
+ bzero(&ui->ui_x1, sizeof(ui->ui_x1));
+ ui->ui_len = uh->uh_ulen;
+ if (in_cksum(ui, n) != 0) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readudp: bad cksum\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+ *ip = tip;
+ }
+#endif
+ if (ntohs(uh->uh_ulen) < sizeof(*uh)) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readudp: bad udp len %d < %d\n",
+ ntohs(uh->uh_ulen), (int)sizeof(*uh));
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ n = (n > (ntohs(uh->uh_ulen) - sizeof(*uh))) ?
+ ntohs(uh->uh_ulen) - sizeof(*uh) : n;
+ *pkt = ptr;
+ *payload = (void *)((uintptr_t)uh + sizeof(*uh));
+ return (n);
+}
diff --git a/stand/libsa/ufs.c b/stand/libsa/ufs.c
new file mode 100644
index 0000000..928a1d1
--- /dev/null
+++ b/stand/libsa/ufs.c
@@ -0,0 +1,861 @@
+/* $NetBSD: ufs.c,v 1.20 1998/03/01 07:15:39 ross Exp $ */
+
+/*-
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program
+ *
+ * Copyright (c) 1982, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright (c) 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: David Golub
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Stand-alone file reading package.
+ */
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include "stand.h"
+#include "string.h"
+
+static int ufs_open(const char *path, struct open_file *f);
+static int ufs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
+static int ufs_close(struct open_file *f);
+static int ufs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t ufs_seek(struct open_file *f, off_t offset, int where);
+static int ufs_stat(struct open_file *f, struct stat *sb);
+static int ufs_readdir(struct open_file *f, struct dirent *d);
+
+struct fs_ops ufs_fsops = {
+ "ufs",
+ ufs_open,
+ ufs_close,
+ ufs_read,
+ ufs_write,
+ ufs_seek,
+ ufs_stat,
+ ufs_readdir
+};
+
+/*
+ * In-core open file.
+ */
+struct file {
+ off_t f_seekp; /* seek pointer */
+ struct fs *f_fs; /* pointer to super-block */
+ union dinode {
+ struct ufs1_dinode di1;
+ struct ufs2_dinode di2;
+ } f_di; /* copy of on-disk inode */
+ int f_nindir[NIADDR];
+ /* number of blocks mapped by
+ indirect block at level i */
+ char *f_blk[NIADDR]; /* buffer for indirect block at
+ level i */
+ size_t f_blksize[NIADDR];
+ /* size of buffer */
+ ufs2_daddr_t f_blkno[NIADDR];/* disk address of block in buffer */
+ ufs2_daddr_t f_buf_blkno; /* block number of data block */
+ char *f_buf; /* buffer for data block */
+ size_t f_buf_size; /* size of data block */
+};
+#define DIP(fp, field) \
+ ((fp)->f_fs->fs_magic == FS_UFS1_MAGIC ? \
+ (fp)->f_di.di1.field : (fp)->f_di.di2.field)
+
+static int read_inode(ino_t, struct open_file *);
+static int block_map(struct open_file *, ufs2_daddr_t, ufs2_daddr_t *);
+static int buf_read_file(struct open_file *, char **, size_t *);
+static int buf_write_file(struct open_file *, char *, size_t *);
+static int search_directory(char *, struct open_file *, ino_t *);
+
+/*
+ * Read a new inode into a file structure.
+ */
+static int
+read_inode(inumber, f)
+ ino_t inumber;
+ struct open_file *f;
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct fs *fs = fp->f_fs;
+ char *buf;
+ size_t rsize;
+ int rc;
+
+ if (fs == NULL)
+ panic("fs == NULL");
+
+ /*
+ * Read inode and save it.
+ */
+ buf = malloc(fs->fs_bsize);
+ twiddle(1);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsbtodb(fs, ino_to_fsba(fs, inumber)), fs->fs_bsize,
+ buf, &rsize);
+ if (rc)
+ goto out;
+ if (rsize != fs->fs_bsize) {
+ rc = EIO;
+ goto out;
+ }
+
+ if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
+ fp->f_di.di1 = ((struct ufs1_dinode *)buf)
+ [ino_to_fsbo(fs, inumber)];
+ else
+ fp->f_di.di2 = ((struct ufs2_dinode *)buf)
+ [ino_to_fsbo(fs, inumber)];
+
+ /*
+ * Clear out the old buffers
+ */
+ {
+ int level;
+
+ for (level = 0; level < NIADDR; level++)
+ fp->f_blkno[level] = -1;
+ fp->f_buf_blkno = -1;
+ }
+ fp->f_seekp = 0;
+out:
+ free(buf);
+ return (rc);
+}
+
+/*
+ * Given an offset in a file, find the disk block number that
+ * contains that block.
+ */
+static int
+block_map(f, file_block, disk_block_p)
+ struct open_file *f;
+ ufs2_daddr_t file_block;
+ ufs2_daddr_t *disk_block_p; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct fs *fs = fp->f_fs;
+ int level;
+ int idx;
+ ufs2_daddr_t ind_block_num;
+ int rc;
+
+ /*
+ * Index structure of an inode:
+ *
+ * di_db[0..NDADDR-1] hold block numbers for blocks
+ * 0..NDADDR-1
+ *
+ * di_ib[0] index block 0 is the single indirect block
+ * holds block numbers for blocks
+ * NDADDR .. NDADDR + NINDIR(fs)-1
+ *
+ * di_ib[1] index block 1 is the double indirect block
+ * holds block numbers for INDEX blocks for blocks
+ * NDADDR + NINDIR(fs) ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
+ *
+ * di_ib[2] index block 2 is the triple indirect block
+ * holds block numbers for double-indirect
+ * blocks for blocks
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2
+ * + NINDIR(fs)**3 - 1
+ */
+
+ if (file_block < NDADDR) {
+ /* Direct block. */
+ *disk_block_p = DIP(fp, di_db[file_block]);
+ return (0);
+ }
+
+ file_block -= NDADDR;
+
+ /*
+ * nindir[0] = NINDIR
+ * nindir[1] = NINDIR**2
+ * nindir[2] = NINDIR**3
+ * etc
+ */
+ for (level = 0; level < NIADDR; level++) {
+ if (file_block < fp->f_nindir[level])
+ break;
+ file_block -= fp->f_nindir[level];
+ }
+ if (level == NIADDR) {
+ /* Block number too high */
+ return (EFBIG);
+ }
+
+ ind_block_num = DIP(fp, di_ib[level]);
+
+ for (; level >= 0; level--) {
+ if (ind_block_num == 0) {
+ *disk_block_p = 0; /* missing */
+ return (0);
+ }
+
+ if (fp->f_blkno[level] != ind_block_num) {
+ if (fp->f_blk[level] == (char *)0)
+ fp->f_blk[level] =
+ malloc(fs->fs_bsize);
+ twiddle(1);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsbtodb(fp->f_fs, ind_block_num),
+ fs->fs_bsize,
+ fp->f_blk[level],
+ &fp->f_blksize[level]);
+ if (rc)
+ return (rc);
+ if (fp->f_blksize[level] != fs->fs_bsize)
+ return (EIO);
+ fp->f_blkno[level] = ind_block_num;
+ }
+
+ if (level > 0) {
+ idx = file_block / fp->f_nindir[level - 1];
+ file_block %= fp->f_nindir[level - 1];
+ } else
+ idx = file_block;
+
+ if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
+ ind_block_num = ((ufs1_daddr_t *)fp->f_blk[level])[idx];
+ else
+ ind_block_num = ((ufs2_daddr_t *)fp->f_blk[level])[idx];
+ }
+
+ *disk_block_p = ind_block_num;
+
+ return (0);
+}
+
+/*
+ * Write a portion of a file from an internal buffer.
+ */
+static int
+buf_write_file(f, buf_p, size_p)
+ struct open_file *f;
+ char *buf_p;
+ size_t *size_p; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct fs *fs = fp->f_fs;
+ long off;
+ ufs_lbn_t file_block;
+ ufs2_daddr_t disk_block;
+ size_t block_size;
+ int rc;
+
+ /*
+ * Calculate the starting block address and offset.
+ */
+ off = blkoff(fs, fp->f_seekp);
+ file_block = lblkno(fs, fp->f_seekp);
+ block_size = sblksize(fs, DIP(fp, di_size), file_block);
+
+ rc = block_map(f, file_block, &disk_block);
+ if (rc)
+ return (rc);
+
+ if (disk_block == 0)
+ /* Because we can't allocate space on the drive */
+ return (EFBIG);
+
+ /*
+ * Truncate buffer at end of file, and at the end of
+ * this block.
+ */
+ if (*size_p > DIP(fp, di_size) - fp->f_seekp)
+ *size_p = DIP(fp, di_size) - fp->f_seekp;
+ if (*size_p > block_size - off)
+ *size_p = block_size - off;
+
+ /*
+ * If we don't entirely occlude the block and it's not
+ * in memory already, read it in first.
+ */
+ if (((off > 0) || (*size_p + off < block_size)) &&
+ (file_block != fp->f_buf_blkno)) {
+
+ if (fp->f_buf == (char *)0)
+ fp->f_buf = malloc(fs->fs_bsize);
+
+ twiddle(4);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsbtodb(fs, disk_block),
+ block_size, fp->f_buf, &fp->f_buf_size);
+ if (rc)
+ return (rc);
+
+ fp->f_buf_blkno = file_block;
+ }
+
+ /*
+ * Copy the user data into the cached block.
+ */
+ bcopy(buf_p, fp->f_buf + off, *size_p);
+
+ /*
+ * Write the block out to storage.
+ */
+
+ twiddle(4);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_WRITE,
+ fsbtodb(fs, disk_block),
+ block_size, fp->f_buf, &fp->f_buf_size);
+ return (rc);
+}
+
+/*
+ * Read a portion of a file into an internal buffer. Return
+ * the location in the buffer and the amount in the buffer.
+ */
+static int
+buf_read_file(f, buf_p, size_p)
+ struct open_file *f;
+ char **buf_p; /* out */
+ size_t *size_p; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct fs *fs = fp->f_fs;
+ long off;
+ ufs_lbn_t file_block;
+ ufs2_daddr_t disk_block;
+ size_t block_size;
+ int rc;
+
+ off = blkoff(fs, fp->f_seekp);
+ file_block = lblkno(fs, fp->f_seekp);
+ block_size = sblksize(fs, DIP(fp, di_size), file_block);
+
+ if (file_block != fp->f_buf_blkno) {
+ if (fp->f_buf == (char *)0)
+ fp->f_buf = malloc(fs->fs_bsize);
+
+ rc = block_map(f, file_block, &disk_block);
+ if (rc)
+ return (rc);
+
+ if (disk_block == 0) {
+ bzero(fp->f_buf, block_size);
+ fp->f_buf_size = block_size;
+ } else {
+ twiddle(4);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsbtodb(fs, disk_block),
+ block_size, fp->f_buf, &fp->f_buf_size);
+ if (rc)
+ return (rc);
+ }
+
+ fp->f_buf_blkno = file_block;
+ }
+
+ /*
+ * Return address of byte in buffer corresponding to
+ * offset, and size of remainder of buffer after that
+ * byte.
+ */
+ *buf_p = fp->f_buf + off;
+ *size_p = block_size - off;
+
+ /*
+ * But truncate buffer at end of file.
+ */
+ if (*size_p > DIP(fp, di_size) - fp->f_seekp)
+ *size_p = DIP(fp, di_size) - fp->f_seekp;
+
+ return (0);
+}
+
+/*
+ * Search a directory for a name and return its
+ * i_number.
+ */
+static int
+search_directory(name, f, inumber_p)
+ char *name;
+ struct open_file *f;
+ ino_t *inumber_p; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct direct *dp;
+ struct direct *edp;
+ char *buf;
+ size_t buf_size;
+ int namlen, length;
+ int rc;
+
+ length = strlen(name);
+
+ fp->f_seekp = 0;
+ while (fp->f_seekp < DIP(fp, di_size)) {
+ rc = buf_read_file(f, &buf, &buf_size);
+ if (rc)
+ return (rc);
+
+ dp = (struct direct *)buf;
+ edp = (struct direct *)(buf + buf_size);
+ while (dp < edp) {
+ if (dp->d_ino == (ino_t)0)
+ goto next;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ if (fp->f_fs->fs_maxsymlinklen <= 0)
+ namlen = dp->d_type;
+ else
+#endif
+ namlen = dp->d_namlen;
+ if (namlen == length &&
+ !strcmp(name, dp->d_name)) {
+ /* found entry */
+ *inumber_p = dp->d_ino;
+ return (0);
+ }
+ next:
+ dp = (struct direct *)((char *)dp + dp->d_reclen);
+ }
+ fp->f_seekp += buf_size;
+ }
+ return (ENOENT);
+}
+
+static int sblock_try[] = SBLOCKSEARCH;
+
+/*
+ * Open a file.
+ */
+static int
+ufs_open(upath, f)
+ const char *upath;
+ struct open_file *f;
+{
+ char *cp, *ncp;
+ int c;
+ ino_t inumber, parent_inumber;
+ struct file *fp;
+ struct fs *fs;
+ int i, rc;
+ size_t buf_size;
+ int nlinks = 0;
+ char namebuf[MAXPATHLEN+1];
+ char *buf = NULL;
+ char *path = NULL;
+
+ /* allocate file system specific data structure */
+ fp = malloc(sizeof(struct file));
+ bzero(fp, sizeof(struct file));
+ f->f_fsdata = (void *)fp;
+
+ /* allocate space and read super block */
+ fs = malloc(SBLOCKSIZE);
+ fp->f_fs = fs;
+ twiddle(1);
+ /*
+ * Try reading the superblock in each of its possible locations.
+ */
+ for (i = 0; sblock_try[i] != -1; i++) {
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ sblock_try[i] / DEV_BSIZE, SBLOCKSIZE,
+ (char *)fs, &buf_size);
+ if (rc)
+ goto out;
+ if ((fs->fs_magic == FS_UFS1_MAGIC ||
+ (fs->fs_magic == FS_UFS2_MAGIC &&
+ fs->fs_sblockloc == sblock_try[i])) &&
+ buf_size == SBLOCKSIZE &&
+ fs->fs_bsize <= MAXBSIZE &&
+ fs->fs_bsize >= sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[i] == -1) {
+ rc = EINVAL;
+ goto out;
+ }
+ /*
+ * Calculate indirect block levels.
+ */
+ {
+ ufs2_daddr_t mult;
+ int level;
+
+ mult = 1;
+ for (level = 0; level < NIADDR; level++) {
+ mult *= NINDIR(fs);
+ fp->f_nindir[level] = mult;
+ }
+ }
+
+ inumber = ROOTINO;
+ if ((rc = read_inode(inumber, f)) != 0)
+ goto out;
+
+ cp = path = strdup(upath);
+ if (path == NULL) {
+ rc = ENOMEM;
+ goto out;
+ }
+ while (*cp) {
+
+ /*
+ * Remove extra separators
+ */
+ while (*cp == '/')
+ cp++;
+ if (*cp == '\0')
+ break;
+
+ /*
+ * Check that current node is a directory.
+ */
+ if ((DIP(fp, di_mode) & IFMT) != IFDIR) {
+ rc = ENOTDIR;
+ goto out;
+ }
+
+ /*
+ * Get next component of path name.
+ */
+ {
+ int len = 0;
+
+ ncp = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ if (++len > MAXNAMLEN) {
+ rc = ENOENT;
+ goto out;
+ }
+ cp++;
+ }
+ *cp = '\0';
+ }
+
+ /*
+ * Look up component in current directory.
+ * Save directory inumber in case we find a
+ * symbolic link.
+ */
+ parent_inumber = inumber;
+ rc = search_directory(ncp, f, &inumber);
+ *cp = c;
+ if (rc)
+ goto out;
+
+ /*
+ * Open next component.
+ */
+ if ((rc = read_inode(inumber, f)) != 0)
+ goto out;
+
+ /*
+ * Check for symbolic link.
+ */
+ if ((DIP(fp, di_mode) & IFMT) == IFLNK) {
+ int link_len = DIP(fp, di_size);
+ int len;
+
+ len = strlen(cp);
+
+ if (link_len + len > MAXPATHLEN ||
+ ++nlinks > MAXSYMLINKS) {
+ rc = ENOENT;
+ goto out;
+ }
+
+ bcopy(cp, &namebuf[link_len], len + 1);
+
+ if (link_len < fs->fs_maxsymlinklen) {
+ if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
+ cp = (caddr_t)(fp->f_di.di1.di_db);
+ else
+ cp = (caddr_t)(fp->f_di.di2.di_db);
+ bcopy(cp, namebuf, (unsigned) link_len);
+ } else {
+ /*
+ * Read file for symbolic link
+ */
+ size_t buf_size;
+ ufs2_daddr_t disk_block;
+ struct fs *fs = fp->f_fs;
+
+ if (!buf)
+ buf = malloc(fs->fs_bsize);
+ rc = block_map(f, (ufs2_daddr_t)0, &disk_block);
+ if (rc)
+ goto out;
+
+ twiddle(1);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata,
+ F_READ, fsbtodb(fs, disk_block),
+ fs->fs_bsize, buf, &buf_size);
+ if (rc)
+ goto out;
+
+ bcopy((char *)buf, namebuf, (unsigned)link_len);
+ }
+
+ /*
+ * If relative pathname, restart at parent directory.
+ * If absolute pathname, restart at root.
+ */
+ cp = namebuf;
+ if (*cp != '/')
+ inumber = parent_inumber;
+ else
+ inumber = (ino_t)ROOTINO;
+
+ if ((rc = read_inode(inumber, f)) != 0)
+ goto out;
+ }
+ }
+
+ /*
+ * Found terminal component.
+ */
+ rc = 0;
+ fp->f_seekp = 0;
+out:
+ if (buf)
+ free(buf);
+ if (path)
+ free(path);
+ if (rc) {
+ if (fp->f_buf)
+ free(fp->f_buf);
+ free(fp->f_fs);
+ free(fp);
+ }
+ return (rc);
+}
+
+static int
+ufs_close(f)
+ struct open_file *f;
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ int level;
+
+ f->f_fsdata = (void *)0;
+ if (fp == (struct file *)0)
+ return (0);
+
+ for (level = 0; level < NIADDR; level++) {
+ if (fp->f_blk[level])
+ free(fp->f_blk[level]);
+ }
+ if (fp->f_buf)
+ free(fp->f_buf);
+ free(fp->f_fs);
+ free(fp);
+ return (0);
+}
+
+/*
+ * Copy a portion of a file into kernel memory.
+ * Cross block boundaries when necessary.
+ */
+static int
+ufs_read(f, start, size, resid)
+ struct open_file *f;
+ void *start;
+ size_t size;
+ size_t *resid; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ size_t csize;
+ char *buf;
+ size_t buf_size;
+ int rc = 0;
+ char *addr = start;
+
+ while (size != 0) {
+ if (fp->f_seekp >= DIP(fp, di_size))
+ break;
+
+ rc = buf_read_file(f, &buf, &buf_size);
+ if (rc)
+ break;
+
+ csize = size;
+ if (csize > buf_size)
+ csize = buf_size;
+
+ bcopy(buf, addr, csize);
+
+ fp->f_seekp += csize;
+ addr += csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+ return (rc);
+}
+
+/*
+ * Write to a portion of an already allocated file.
+ * Cross block boundaries when necessary. Can not
+ * extend the file.
+ */
+static int
+ufs_write(f, start, size, resid)
+ struct open_file *f;
+ void *start;
+ size_t size;
+ size_t *resid; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ size_t csize;
+ int rc = 0;
+ char *addr = start;
+
+ csize = size;
+ while ((size != 0) && (csize != 0)) {
+ if (fp->f_seekp >= DIP(fp, di_size))
+ break;
+
+ if (csize >= 512) csize = 512; /* XXX */
+
+ rc = buf_write_file(f, addr, &csize);
+ if (rc)
+ break;
+
+ fp->f_seekp += csize;
+ addr += csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+ return (rc);
+}
+
+static off_t
+ufs_seek(f, offset, where)
+ struct open_file *f;
+ off_t offset;
+ int where;
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ switch (where) {
+ case SEEK_SET:
+ fp->f_seekp = offset;
+ break;
+ case SEEK_CUR:
+ fp->f_seekp += offset;
+ break;
+ case SEEK_END:
+ fp->f_seekp = DIP(fp, di_size) - offset;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ return (fp->f_seekp);
+}
+
+static int
+ufs_stat(f, sb)
+ struct open_file *f;
+ struct stat *sb;
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ /* only important stuff */
+ sb->st_mode = DIP(fp, di_mode);
+ sb->st_uid = DIP(fp, di_uid);
+ sb->st_gid = DIP(fp, di_gid);
+ sb->st_size = DIP(fp, di_size);
+ return (0);
+}
+
+static int
+ufs_readdir(struct open_file *f, struct dirent *d)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct direct *dp;
+ char *buf;
+ size_t buf_size;
+ int error;
+
+ /*
+ * assume that a directory entry will not be split across blocks
+ */
+again:
+ if (fp->f_seekp >= DIP(fp, di_size))
+ return (ENOENT);
+ error = buf_read_file(f, &buf, &buf_size);
+ if (error)
+ return (error);
+ dp = (struct direct *)buf;
+ fp->f_seekp += dp->d_reclen;
+ if (dp->d_ino == (ino_t)0)
+ goto again;
+ d->d_type = dp->d_type;
+ strcpy(d->d_name, dp->d_name);
+ return (0);
+}
diff --git a/stand/libsa/ufsread.c b/stand/libsa/ufsread.c
new file mode 100644
index 0000000..5ba65a4
--- /dev/null
+++ b/stand/libsa/ufsread.c
@@ -0,0 +1,326 @@
+/*-
+ * Copyright (c) 2002 McAfee, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and McAfee Research,, the Security Research Division of
+ * McAfee, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as
+ * part of the DARPA CHATS research program
+ *
+ * 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.
+ */
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#ifdef UFS_SMALL_CGBASE
+/* XXX: Revert to old (broken for over 1.5Tb filesystems) version of cgbase
+ (see sys/ufs/ffs/fs.h rev 1.39) so that small boot loaders (e.g. boot2) can
+ support both UFS1 and UFS2. */
+#undef cgbase
+#define cgbase(fs, c) ((ufs2_daddr_t)((fs)->fs_fpg * (c)))
+#endif
+
+typedef uint32_t ufs_ino_t;
+
+/*
+ * We use 4k `virtual' blocks for filesystem data, whatever the actual
+ * filesystem block size. FFS blocks are always a multiple of 4k.
+ */
+#define VBLKSHIFT 12
+#define VBLKSIZE (1 << VBLKSHIFT)
+#define VBLKMASK (VBLKSIZE - 1)
+#define DBPERVBLK (VBLKSIZE / DEV_BSIZE)
+#define INDIRPERVBLK(fs) (NINDIR(fs) / ((fs)->fs_bsize >> VBLKSHIFT))
+#define IPERVBLK(fs) (INOPB(fs) / ((fs)->fs_bsize >> VBLKSHIFT))
+#define INO_TO_VBA(fs, ipervblk, x) \
+ (fsbtodb(fs, cgimin(fs, ino_to_cg(fs, x))) + \
+ (((x) % (fs)->fs_ipg) / (ipervblk) * DBPERVBLK))
+#define INO_TO_VBO(ipervblk, x) ((x) % ipervblk)
+#define FS_TO_VBA(fs, fsb, off) (fsbtodb(fs, fsb) + \
+ ((off) / VBLKSIZE) * DBPERVBLK)
+#define FS_TO_VBO(fs, fsb, off) ((off) & VBLKMASK)
+
+/* Buffers that must not span a 64k boundary. */
+struct dmadat {
+ char blkbuf[VBLKSIZE]; /* filesystem blocks */
+ char indbuf[VBLKSIZE]; /* indir blocks */
+ char sbbuf[SBLOCKSIZE]; /* superblock */
+ char secbuf[DEV_BSIZE]; /* for MBR/disklabel */
+};
+static struct dmadat *dmadat;
+
+static ufs_ino_t lookup(const char *);
+static ssize_t fsread(ufs_ino_t, void *, size_t);
+
+static uint8_t ls, dsk_meta;
+static uint32_t fs_off;
+
+static __inline uint8_t
+fsfind(const char *name, ufs_ino_t * ino)
+{
+ static char buf[DEV_BSIZE];
+ static struct direct d;
+ char *s;
+ ssize_t n;
+
+ fs_off = 0;
+ while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0)
+ for (s = buf; s < buf + DEV_BSIZE;) {
+ memcpy(&d, s, sizeof(struct direct));
+ if (ls)
+ printf("%s ", d.d_name);
+ else if (!strcmp(name, d.d_name)) {
+ *ino = d.d_ino;
+ return d.d_type;
+ }
+ s += d.d_reclen;
+ }
+ if (n != -1 && ls)
+ printf("\n");
+ return 0;
+}
+
+static ufs_ino_t
+lookup(const char *path)
+{
+ static char name[MAXNAMLEN + 1];
+ const char *s;
+ ufs_ino_t ino;
+ ssize_t n;
+ uint8_t dt;
+
+ ino = ROOTINO;
+ dt = DT_DIR;
+ for (;;) {
+ if (*path == '/')
+ path++;
+ if (!*path)
+ break;
+ for (s = path; *s && *s != '/'; s++);
+ if ((n = s - path) > MAXNAMLEN)
+ return 0;
+ ls = *path == '?' && n == 1 && !*s;
+ memcpy(name, path, n);
+ name[n] = 0;
+ if (dt != DT_DIR) {
+ printf("%s: not a directory.\n", name);
+ return (0);
+ }
+ if ((dt = fsfind(name, &ino)) <= 0)
+ break;
+ path = s;
+ }
+ return dt == DT_REG ? ino : 0;
+}
+
+/*
+ * Possible superblock locations ordered from most to least likely.
+ */
+static int sblock_try[] = SBLOCKSEARCH;
+
+#if defined(UFS2_ONLY)
+#define DIP(field) dp2.field
+#elif defined(UFS1_ONLY)
+#define DIP(field) dp1.field
+#else
+#define DIP(field) fs.fs_magic == FS_UFS1_MAGIC ? dp1.field : dp2.field
+#endif
+
+static ssize_t
+fsread_size(ufs_ino_t inode, void *buf, size_t nbyte, size_t *fsizep)
+{
+#ifndef UFS2_ONLY
+ static struct ufs1_dinode dp1;
+ ufs1_daddr_t addr1;
+#endif
+#ifndef UFS1_ONLY
+ static struct ufs2_dinode dp2;
+#endif
+ static struct fs fs;
+ static ufs_ino_t inomap;
+ char *blkbuf;
+ void *indbuf;
+ char *s;
+ size_t n, nb, size, off, vboff;
+ ufs_lbn_t lbn;
+ ufs2_daddr_t addr2, vbaddr;
+ static ufs2_daddr_t blkmap, indmap;
+ u_int u;
+
+ /* Basic parameter validation. */
+ if ((buf == NULL && nbyte != 0) || dmadat == NULL)
+ return (-1);
+
+ blkbuf = dmadat->blkbuf;
+ indbuf = dmadat->indbuf;
+
+ /*
+ * Force probe if inode is zero to ensure we have a valid fs, otherwise
+ * when probing multiple paritions, reads from subsequent parititions
+ * will incorrectly succeed.
+ */
+ if (!dsk_meta || inode == 0) {
+ inomap = 0;
+ dsk_meta = 0;
+ for (n = 0; sblock_try[n] != -1; n++) {
+ if (dskread(dmadat->sbbuf, sblock_try[n] / DEV_BSIZE,
+ SBLOCKSIZE / DEV_BSIZE))
+ return -1;
+ memcpy(&fs, dmadat->sbbuf, sizeof(struct fs));
+ if ((
+#if defined(UFS1_ONLY)
+ fs.fs_magic == FS_UFS1_MAGIC
+#elif defined(UFS2_ONLY)
+ (fs.fs_magic == FS_UFS2_MAGIC &&
+ fs.fs_sblockloc == sblock_try[n])
+#else
+ fs.fs_magic == FS_UFS1_MAGIC ||
+ (fs.fs_magic == FS_UFS2_MAGIC &&
+ fs.fs_sblockloc == sblock_try[n])
+#endif
+ ) &&
+ fs.fs_bsize <= MAXBSIZE &&
+ fs.fs_bsize >= (int32_t)sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[n] == -1) {
+ return -1;
+ }
+ dsk_meta++;
+ } else
+ memcpy(&fs, dmadat->sbbuf, sizeof(struct fs));
+ if (!inode)
+ return 0;
+ if (inomap != inode) {
+ n = IPERVBLK(&fs);
+ if (dskread(blkbuf, INO_TO_VBA(&fs, n, inode), DBPERVBLK))
+ return -1;
+ n = INO_TO_VBO(n, inode);
+#if defined(UFS1_ONLY)
+ memcpy(&dp1, (struct ufs1_dinode *)(void *)blkbuf + n,
+ sizeof(dp1));
+#elif defined(UFS2_ONLY)
+ memcpy(&dp2, (struct ufs2_dinode *)(void *)blkbuf + n,
+ sizeof(dp2));
+#else
+ if (fs.fs_magic == FS_UFS1_MAGIC)
+ memcpy(&dp1, (struct ufs1_dinode *)(void *)blkbuf + n,
+ sizeof(dp1));
+ else
+ memcpy(&dp2, (struct ufs2_dinode *)(void *)blkbuf + n,
+ sizeof(dp2));
+#endif
+ inomap = inode;
+ fs_off = 0;
+ blkmap = indmap = 0;
+ }
+ s = buf;
+ size = DIP(di_size);
+ n = size - fs_off;
+ if (nbyte > n)
+ nbyte = n;
+ nb = nbyte;
+ while (nb) {
+ lbn = lblkno(&fs, fs_off);
+ off = blkoff(&fs, fs_off);
+ if (lbn < NDADDR) {
+ addr2 = DIP(di_db[lbn]);
+ } else if (lbn < NDADDR + NINDIR(&fs)) {
+ n = INDIRPERVBLK(&fs);
+ addr2 = DIP(di_ib[0]);
+ u = (u_int)(lbn - NDADDR) / n * DBPERVBLK;
+ vbaddr = fsbtodb(&fs, addr2) + u;
+ if (indmap != vbaddr) {
+ if (dskread(indbuf, vbaddr, DBPERVBLK))
+ return -1;
+ indmap = vbaddr;
+ }
+ n = (lbn - NDADDR) & (n - 1);
+#if defined(UFS1_ONLY)
+ memcpy(&addr1, (ufs1_daddr_t *)indbuf + n,
+ sizeof(ufs1_daddr_t));
+ addr2 = addr1;
+#elif defined(UFS2_ONLY)
+ memcpy(&addr2, (ufs2_daddr_t *)indbuf + n,
+ sizeof(ufs2_daddr_t));
+#else
+ if (fs.fs_magic == FS_UFS1_MAGIC) {
+ memcpy(&addr1, (ufs1_daddr_t *)indbuf + n,
+ sizeof(ufs1_daddr_t));
+ addr2 = addr1;
+ } else
+ memcpy(&addr2, (ufs2_daddr_t *)indbuf + n,
+ sizeof(ufs2_daddr_t));
+#endif
+ } else
+ return -1;
+ vbaddr = fsbtodb(&fs, addr2) + (off >> VBLKSHIFT) * DBPERVBLK;
+ vboff = off & VBLKMASK;
+ n = sblksize(&fs, (off_t)size, lbn) - (off & ~VBLKMASK);
+ if (n > VBLKSIZE)
+ n = VBLKSIZE;
+ if (blkmap != vbaddr) {
+ if (dskread(blkbuf, vbaddr, n >> DEV_BSHIFT))
+ return -1;
+ blkmap = vbaddr;
+ }
+ n -= vboff;
+ if (n > nb)
+ n = nb;
+ memcpy(s, blkbuf + vboff, n);
+ s += n;
+ fs_off += n;
+ nb -= n;
+ }
+
+ if (fsizep != NULL)
+ *fsizep = size;
+
+ return nbyte;
+}
+
+static ssize_t
+fsread(ufs_ino_t inode, void *buf, size_t nbyte)
+{
+
+ return fsread_size(inode, buf, nbyte, NULL);
+}
+
diff --git a/stand/libsa/util.c b/stand/libsa/util.c
new file mode 100644
index 0000000..49f42eb
--- /dev/null
+++ b/stand/libsa/util.c
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <stdarg.h>
+
+#include "cons.h"
+#include "util.h"
+
+void
+memcpy(void *dst, const void *src, int len)
+{
+ const char *s = src;
+ char *d = dst;
+
+ while (len--)
+ *d++ = *s++;
+}
+
+void
+memset(void *b, int c, size_t len)
+{
+ char *bp = b;
+
+ while (len--)
+ *bp++ = (unsigned char)c;
+}
+
+int
+memcmp(const void *b1, const void *b2, size_t len)
+{
+ const unsigned char *p1, *p2;
+
+ for (p1 = b1, p2 = b2; len > 0; len--, p1++, p2++) {
+ if (*p1 != *p2)
+ return ((*p1) - (*p2));
+ }
+ return (0);
+}
+
+int
+strcmp(const char *s1, const char *s2)
+{
+
+ for (; *s1 == *s2 && *s1 != '\0'; s1++, s2++)
+ ;
+ return ((unsigned char)*s1 - (unsigned char)*s2);
+}
+
+int
+strncmp(const char *s1, const char *s2, size_t len)
+{
+
+ for (; len > 0 && *s1 == *s2 && *s1 != '\0'; len--, s1++, s2++)
+ ;
+ return (len == 0 ? 0 : (unsigned char)*s1 - (unsigned char)*s2);
+}
+
+void
+strcpy(char *dst, const char *src)
+{
+
+ while (*src != '\0')
+ *dst++ = *src++;
+ *dst = '\0';
+}
+
+void
+strcat(char *dst, const char *src)
+{
+
+ while (*dst != '\0')
+ dst++;
+ while (*src != '\0')
+ *dst++ = *src++;
+ *dst = '\0';
+}
+
+char *
+strchr(const char *s, char ch)
+{
+
+ for (; *s != '\0'; s++) {
+ if (*s == ch)
+ return ((char *)(uintptr_t)(const void *)s);
+ }
+ return (NULL);
+}
+
+size_t
+strlen(const char *s)
+{
+ size_t len = 0;
+
+ while (*s++ != '\0')
+ len++;
+ return (len);
+}
+
+int
+printf(const char *fmt, ...)
+{
+ va_list ap;
+ const char *hex = "0123456789abcdef";
+ char buf[32], *s;
+ uint16_t *S;
+ unsigned long long u;
+ int c, l;
+
+ va_start(ap, fmt);
+ while ((c = *fmt++) != '\0') {
+ if (c != '%') {
+ putchar(c);
+ continue;
+ }
+ l = 0;
+nextfmt:
+ c = *fmt++;
+ switch (c) {
+ case 'l':
+ l++;
+ goto nextfmt;
+ case 'c':
+ putchar(va_arg(ap, int));
+ break;
+ case 's':
+ for (s = va_arg(ap, char *); *s != '\0'; s++)
+ putchar(*s);
+ break;
+ case 'S': /* Assume console can cope with wide chars */
+ for (S = va_arg(ap, uint16_t *); *S != 0; S++)
+ putchar(*S);
+ break;
+ case 'd': /* A lie, always prints unsigned */
+ case 'u':
+ case 'x':
+ switch (l) {
+ case 2:
+ u = va_arg(ap, unsigned long long);
+ break;
+ case 1:
+ u = va_arg(ap, unsigned long);
+ break;
+ default:
+ u = va_arg(ap, unsigned int);
+ break;
+ }
+ s = buf;
+ if (c == 'd' || c == 'u') {
+ do
+ *s++ = '0' + (u % 10U);
+ while (u /= 10);
+ } else {
+ do
+ *s++ = hex[u & 0xfu];
+ while (u >>= 4);
+ }
+ while (--s >= buf)
+ putchar(*s);
+ break;
+ }
+ }
+ va_end(ap);
+ return (0);
+}
diff --git a/stand/libsa/util.h b/stand/libsa/util.h
new file mode 100644
index 0000000..88a99f1
--- /dev/null
+++ b/stand/libsa/util.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+
+void memcpy(void *dst, const void *src, int len);
+void memset(void *b, int c, size_t len);
+int memcmp(const void *b1, const void *b2, size_t len);
+
+#define bcopy(src, dst, len) memcpy((dst), (src), (len))
+#define bzero(buf, size) memset((buf), 0, (size))
+#define bcmp(b1, b2, len) (memcmp((b1), (b2), (len)) != 0)
+
+int strcmp(const char *s1, const char *s2);
+int strncmp(const char *s1, const char *s2, size_t len);
+void strcpy(char *dst, const char *src);
+void strcat(char *dst, const char *src);
+char *strchr(const char *s, char ch);
+size_t strlen(const char *s);
+
+int printf(const char *fmt, ...);
+
+#endif /* !_UTIL_H_ */
diff --git a/stand/libsa/uuid_from_string.c b/stand/libsa/uuid_from_string.c
new file mode 100644
index 0000000..7a59b41
--- /dev/null
+++ b/stand/libsa/uuid_from_string.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 2015 M. Warner Losh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Note: some comments taken from lib/libc/uuid/uuid_from_string.c
+ * Copyright (c) 2002 Marcel Moolenaar
+ * Copyright (c) 2002 Hiten Mahesh Pandya
+ */
+
+
+#include <stand.h>
+#include <uuid.h>
+
+static int
+hex2int(int ch)
+{
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ if (ch >= 'a' && ch <= 'f')
+ return 10 + ch - 'a';
+ if (ch >= 'A' && ch <= 'F')
+ return 10 + ch - 'A';
+ return 16;
+}
+
+static uint32_t
+fromhex(const char *s, int len, int *ok)
+{
+ uint32_t v;
+ int i, h;
+
+ if (!*ok)
+ return 0;
+ v = 0;
+ for (i = 0; i < len; i++) {
+ h = hex2int(s[i]);
+ if (h == 16) {
+ *ok = 0;
+ return v;
+ }
+ v = (v << 4) | h;
+ }
+ return v;
+}
+
+/*
+ * uuid_from_string() - convert a string representation of an UUID into
+ * a binary representation.
+ * See also:
+ * http://www.opengroup.org/onlinepubs/009629399/uuid_from_string.htm
+ *
+ * NOTE: The sequence field is in big-endian, while the time fields are in
+ * native byte order.
+ *
+ * 01234567-89ab-cdef-0123-456789abcdef
+ * 000000000011111111112222222222333333
+ * 012345678901234567890123456789012345
+ * - - - -
+ * hhhhhhhh-hhhh-hhhh-bbbb-bbbbbbbbbbbb
+ *
+ */
+void
+uuid_from_string(const char *s, uuid_t *u, uint32_t *status)
+{
+ int ok = 1;
+ int n;
+
+ if (s == NULL || *s == '\0') {
+ uuid_create_nil(u, status);
+ return;
+ }
+
+ if (status != NULL)
+ *status = uuid_s_invalid_string_uuid;
+ if (strlen(s) != 36)
+ return;
+ /* Only support new format, check for all the right dashes */
+ if (s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
+ return;
+ /* native byte order */
+ u->time_low = fromhex(s , 8, &ok);
+ u->time_mid = fromhex(s + 9, 4, &ok);
+ u->time_hi_and_version = fromhex(s + 14, 4, &ok);
+ /* Big endian, but presented as a whole number so decode as such */
+ u->clock_seq_hi_and_reserved = fromhex(s + 19, 2, &ok);
+ u->clock_seq_low = fromhex(s + 21, 2, &ok);
+ u->node[0] = fromhex(s + 24, 2, &ok);
+ u->node[1] = fromhex(s + 26, 2, &ok);
+ u->node[2] = fromhex(s + 28, 2, &ok);
+ u->node[3] = fromhex(s + 30, 2, &ok);
+ u->node[4] = fromhex(s + 32, 2, &ok);
+ u->node[5] = fromhex(s + 34, 2, &ok);
+ if (!ok)
+ return;
+
+ /* We have a successful scan. Check semantics... */
+ n = u->clock_seq_hi_and_reserved;
+ if ((n & 0x80) != 0x00 && /* variant 0? */
+ (n & 0xc0) != 0x80 && /* variant 1? */
+ (n & 0xe0) != 0xc0) { /* variant 2? */
+ if (status != NULL)
+ *status = uuid_s_bad_version;
+ } else {
+ if (status != NULL)
+ *status = uuid_s_ok;
+ }
+}
diff --git a/stand/libsa/uuid_to_string.c b/stand/libsa/uuid_to_string.c
new file mode 100644
index 0000000..d878af4
--- /dev/null
+++ b/stand/libsa/uuid_to_string.c
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2015 M. Warner Losh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+/*
+ * Note: some comments taken from lib/libc/uuid/uuid_to_string.c
+ * Copyright (c) 2002,2005 Marcel Moolenaar
+ * Copyright (c) 2002 Hiten Mahesh Pandya
+ */
+
+#include <stand.h>
+#include <uuid.h>
+
+/*
+ * Dump len characters into *buf from val as hex and update *buf
+ */
+static void
+tohex(char **buf, int len, uint32_t val)
+{
+ static const char *hexstr = "0123456789abcdef";
+ char *walker = *buf;
+ int i;
+
+ for (i = len - 1; i >= 0; i--) {
+ walker[i] = hexstr[val & 0xf];
+ val >>= 4;
+ }
+ *buf = walker + len;
+}
+
+/*
+ * uuid_to_string() - Convert a binary UUID into a string representation.
+ * See also:
+ * http://www.opengroup.org/onlinepubs/009629399/uuid_to_string.htm
+ *
+ * NOTE: The references given above do not have a status code for when
+ * the string could not be allocated. The status code has been
+ * taken from the Hewlett-Packard implementation.
+ *
+ * NOTE: we don't support u == NULL for a nil UUID, sorry.
+ *
+ * NOTE: The sequence field is in big-endian, while the time fields are in
+ * native byte order.
+ *
+ * hhhhhhhh-hhhh-hhhh-bbbb-bbbbbbbbbbbb
+ * 01234567-89ab-cdef-0123-456789abcdef
+ */
+void
+uuid_to_string(const uuid_t *u, char **s, uint32_t *status)
+{
+ uuid_t nil;
+ char *w;
+
+ if (status != NULL)
+ *status = uuid_s_ok;
+ if (s == NULL) /* Regular version does this odd-ball behavior too */
+ return;
+ w = *s = malloc(37);
+ if (*s == NULL) {
+ if (status != NULL)
+ *status = uuid_s_no_memory;
+ return;
+ }
+ if (u == NULL) {
+ u = &nil;
+ uuid_create_nil(&nil, NULL);
+ }
+ /* native */
+ tohex(&w, 8, u->time_low);
+ *w++ = '-';
+ tohex(&w, 4, u->time_mid);
+ *w++ = '-';
+ tohex(&w, 4, u->time_hi_and_version);
+ *w++ = '-';
+ /* Big endian, so do a byte at a time */
+ tohex(&w, 2, u->clock_seq_hi_and_reserved);
+ tohex(&w, 2, u->clock_seq_low);
+ *w++ = '-';
+ tohex(&w, 2, u->node[0]);
+ tohex(&w, 2, u->node[1]);
+ tohex(&w, 2, u->node[2]);
+ tohex(&w, 2, u->node[3]);
+ tohex(&w, 2, u->node[4]);
+ tohex(&w, 2, u->node[5]);
+ *w++ = '\0';
+}
diff --git a/stand/libsa/write.c b/stand/libsa/write.c
new file mode 100644
index 0000000..9e02f08
--- /dev/null
+++ b/stand/libsa/write.c
@@ -0,0 +1,95 @@
+/* $NetBSD: write.c,v 1.7 1996/06/21 20:29:30 pk Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)write.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include "stand.h"
+
+ssize_t
+write(fd, dest, bcount)
+ int fd;
+ void *dest;
+ size_t bcount;
+{
+ struct open_file *f = &files[fd];
+ size_t resid;
+
+ if ((unsigned)fd >= SOPEN_MAX || !(f->f_flags & F_WRITE)) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (f->f_flags & F_RAW) {
+ twiddle(4);
+ errno = (f->f_dev->dv_strategy)(f->f_devdata, F_WRITE,
+ btodb(f->f_offset), bcount, dest, &resid);
+ if (errno)
+ return (-1);
+ f->f_offset += resid;
+ return (resid);
+ }
+ resid = bcount;
+ if ((errno = (f->f_ops->fo_write)(f, dest, bcount, &resid)))
+ return (-1);
+ return (bcount - resid);
+}
diff --git a/stand/libsa/zalloc.c b/stand/libsa/zalloc.c
new file mode 100644
index 0000000..4d1ec62
--- /dev/null
+++ b/stand/libsa/zalloc.c
@@ -0,0 +1,316 @@
+/*
+ * This module derived from code donated to the FreeBSD Project by
+ * Matthew Dillon <dillon@backplane.com>
+ *
+ * Copyright (c) 1998 The FreeBSD Project
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * LIB/MEMORY/ZALLOC.C - self contained low-overhead memory pool/allocation
+ * subsystem
+ *
+ * This subsystem implements memory pools and memory allocation
+ * routines.
+ *
+ * Pools are managed via a linked list of 'free' areas. Allocating
+ * memory creates holes in the freelist, freeing memory fills them.
+ * Since the freelist consists only of free memory areas, it is possible
+ * to allocate the entire pool without incuring any structural overhead.
+ *
+ * The system works best when allocating similarly-sized chunks of
+ * memory. Care must be taken to avoid fragmentation when
+ * allocating/deallocating dissimilar chunks.
+ *
+ * When a memory pool is first allocated, the entire pool is marked as
+ * allocated. This is done mainly because we do not want to modify any
+ * portion of a pool's data area until we are given permission. The
+ * caller must explicitly deallocate portions of the pool to make them
+ * available.
+ *
+ * z[n]xalloc() works like z[n]alloc() but the allocation is made from
+ * within the specified address range. If the segment could not be
+ * allocated, NULL is returned. WARNING! The address range will be
+ * aligned to an 8 or 16 byte boundry depending on the cpu so if you
+ * give an unaligned address range, unexpected results may occur.
+ *
+ * If a standard allocation fails, the reclaim function will be called
+ * to recover some space. This usually causes other portions of the
+ * same pool to be released. Memory allocations at this low level
+ * should not block but you can do that too in your reclaim function
+ * if you want. Reclaim does not function when z[n]xalloc() is used,
+ * only for z[n]alloc().
+ *
+ * Allocation and frees of 0 bytes are valid operations.
+ */
+
+#include "zalloc_defs.h"
+
+/*
+ * Objects in the pool must be aligned to at least the size of struct MemNode.
+ * They must also be aligned to MALLOCALIGN, which should normally be larger
+ * than the struct, so assert that to be so at compile time.
+ */
+typedef char assert_align[(sizeof(struct MemNode) <= MALLOCALIGN) ? 1 : -1];
+
+#define MEMNODE_SIZE_MASK MALLOCALIGN_MASK
+
+/*
+ * znalloc() - allocate memory (without zeroing) from pool. Call reclaim
+ * and retry if appropriate, return NULL if unable to allocate
+ * memory.
+ */
+
+void *
+znalloc(MemPool *mp, uintptr_t bytes)
+{
+ /*
+ * align according to pool object size (can be 0). This is
+ * inclusive of the MEMNODE_SIZE_MASK minimum alignment.
+ *
+ */
+ bytes = (bytes + MEMNODE_SIZE_MASK) & ~MEMNODE_SIZE_MASK;
+
+ if (bytes == 0)
+ return((void *)-1);
+
+ /*
+ * locate freelist entry big enough to hold the object. If all objects
+ * are the same size, this is a constant-time function.
+ */
+
+ if (bytes <= mp->mp_Size - mp->mp_Used) {
+ MemNode **pmn;
+ MemNode *mn;
+
+ for (pmn = &mp->mp_First; (mn=*pmn) != NULL; pmn = &mn->mr_Next) {
+ if (bytes > mn->mr_Bytes)
+ continue;
+
+ /*
+ * Cut a chunk of memory out of the beginning of this
+ * block and fixup the link appropriately.
+ */
+
+ {
+ char *ptr = (char *)mn;
+
+ if (mn->mr_Bytes == bytes) {
+ *pmn = mn->mr_Next;
+ } else {
+ mn = (MemNode *)((char *)mn + bytes);
+ mn->mr_Next = ((MemNode *)ptr)->mr_Next;
+ mn->mr_Bytes = ((MemNode *)ptr)->mr_Bytes - bytes;
+ *pmn = mn;
+ }
+ mp->mp_Used += bytes;
+ return(ptr);
+ }
+ }
+ }
+
+ /*
+ * Memory pool is full, return NULL.
+ */
+
+ return(NULL);
+}
+
+/*
+ * zfree() - free previously allocated memory
+ */
+
+void
+zfree(MemPool *mp, void *ptr, uintptr_t bytes)
+{
+ /*
+ * align according to pool object size (can be 0). This is
+ * inclusive of the MEMNODE_SIZE_MASK minimum alignment.
+ */
+ bytes = (bytes + MEMNODE_SIZE_MASK) & ~MEMNODE_SIZE_MASK;
+
+ if (bytes == 0)
+ return;
+
+ /*
+ * panic if illegal pointer
+ */
+
+ if ((char *)ptr < (char *)mp->mp_Base ||
+ (char *)ptr + bytes > (char *)mp->mp_End ||
+ ((uintptr_t)ptr & MEMNODE_SIZE_MASK) != 0)
+ panic("zfree(%p,%ju): wild pointer", ptr, (uintmax_t)bytes);
+
+ /*
+ * free the segment
+ */
+
+ {
+ MemNode **pmn;
+ MemNode *mn;
+
+ mp->mp_Used -= bytes;
+
+ for (pmn = &mp->mp_First; (mn = *pmn) != NULL; pmn = &mn->mr_Next) {
+ /*
+ * If area between last node and current node
+ * - check range
+ * - check merge with next area
+ * - check merge with previous area
+ */
+ if ((char *)ptr <= (char *)mn) {
+ /*
+ * range check
+ */
+ if ((char *)ptr + bytes > (char *)mn) {
+ panic("zfree(%p,%ju): corrupt memlist1", ptr,
+ (uintmax_t)bytes);
+ }
+
+ /*
+ * merge against next area or create independant area
+ */
+
+ if ((char *)ptr + bytes == (char *)mn) {
+ ((MemNode *)ptr)->mr_Next = mn->mr_Next;
+ ((MemNode *)ptr)->mr_Bytes= bytes + mn->mr_Bytes;
+ } else {
+ ((MemNode *)ptr)->mr_Next = mn;
+ ((MemNode *)ptr)->mr_Bytes= bytes;
+ }
+ *pmn = mn = (MemNode *)ptr;
+
+ /*
+ * merge against previous area (if there is a previous
+ * area).
+ */
+
+ if (pmn != &mp->mp_First) {
+ if ((char*)pmn + ((MemNode*)pmn)->mr_Bytes == (char*)ptr) {
+ ((MemNode *)pmn)->mr_Next = mn->mr_Next;
+ ((MemNode *)pmn)->mr_Bytes += mn->mr_Bytes;
+ mn = (MemNode *)pmn;
+ }
+ }
+ return;
+ /* NOT REACHED */
+ }
+ if ((char *)ptr < (char *)mn + mn->mr_Bytes) {
+ panic("zfree(%p,%ju): corrupt memlist2", ptr,
+ (uintmax_t)bytes);
+ }
+ }
+ /*
+ * We are beyond the last MemNode, append new MemNode. Merge against
+ * previous area if possible.
+ */
+ if (pmn == &mp->mp_First ||
+ (char *)pmn + ((MemNode *)pmn)->mr_Bytes != (char *)ptr
+ ) {
+ ((MemNode *)ptr)->mr_Next = NULL;
+ ((MemNode *)ptr)->mr_Bytes = bytes;
+ *pmn = (MemNode *)ptr;
+ mn = (MemNode *)ptr;
+ } else {
+ ((MemNode *)pmn)->mr_Bytes += bytes;
+ mn = (MemNode *)pmn;
+ }
+ }
+}
+
+/*
+ * zextendPool() - extend memory pool to cover additional space.
+ *
+ * Note: the added memory starts out as allocated, you
+ * must free it to make it available to the memory subsystem.
+ *
+ * Note: mp_Size may not reflect (mp_End - mp_Base) range
+ * due to other parts of the system doing their own sbrk()
+ * calls.
+ */
+
+void
+zextendPool(MemPool *mp, void *base, uintptr_t bytes)
+{
+ if (mp->mp_Size == 0) {
+ mp->mp_Base = base;
+ mp->mp_Used = bytes;
+ mp->mp_End = (char *)base + bytes;
+ mp->mp_Size = bytes;
+ } else {
+ void *pend = (char *)mp->mp_Base + mp->mp_Size;
+
+ if (base < mp->mp_Base) {
+ mp->mp_Size += (char *)mp->mp_Base - (char *)base;
+ mp->mp_Used += (char *)mp->mp_Base - (char *)base;
+ mp->mp_Base = base;
+ }
+ base = (char *)base + bytes;
+ if (base > pend) {
+ mp->mp_Size += (char *)base - (char *)pend;
+ mp->mp_Used += (char *)base - (char *)pend;
+ mp->mp_End = (char *)base;
+ }
+ }
+}
+
+#ifdef ZALLOCDEBUG
+
+void
+zallocstats(MemPool *mp)
+{
+ int abytes = 0;
+ int hbytes = 0;
+ int fcount = 0;
+ MemNode *mn;
+
+ printf("%d bytes reserved", (int) mp->mp_Size);
+
+ mn = mp->mp_First;
+
+ if ((void *)mn != (void *)mp->mp_Base) {
+ abytes += (char *)mn - (char *)mp->mp_Base;
+ }
+
+ while (mn) {
+ if ((char *)mn + mn->mr_Bytes != mp->mp_End) {
+ hbytes += mn->mr_Bytes;
+ ++fcount;
+ }
+ if (mn->mr_Next)
+ abytes += (char *)mn->mr_Next - ((char *)mn + mn->mr_Bytes);
+ mn = mn->mr_Next;
+ }
+ printf(" %d bytes allocated\n%d fragments (%d bytes fragmented)\n",
+ abytes,
+ fcount,
+ hbytes
+ );
+}
+
+#endif
+
diff --git a/stand/libsa/zalloc_defs.h b/stand/libsa/zalloc_defs.h
new file mode 100644
index 0000000..7f2cc12
--- /dev/null
+++ b/stand/libsa/zalloc_defs.h
@@ -0,0 +1,78 @@
+/*
+ * This module derived from code donated to the FreeBSD Project by
+ * Matthew Dillon <dillon@backplane.com>
+ *
+ * Copyright (c) 1998 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * DEFS.H
+ */
+
+#define USEGUARD /* use stard/end guard bytes */
+#define USEENDGUARD
+#define DMALLOCDEBUG /* add debugging code to gather stats */
+#define ZALLOCDEBUG
+
+#include <sys/stdint.h>
+#include "stand.h"
+#include "zalloc_mem.h"
+
+#define Library extern
+
+/*
+ * block extension for sbrk()
+ */
+
+#define BLKEXTEND (4 * 1024)
+#define BLKEXTENDMASK (BLKEXTEND - 1)
+
+/*
+ * Required malloc alignment.
+ *
+ * Embedded platforms using the u-boot API drivers require that all I/O buffers
+ * be on a cache line sized boundary. The worst case size for that is 64 bytes.
+ * For other platforms, 16 bytes works fine. The alignment also must be at
+ * least sizeof(struct MemNode); this is asserted in zalloc.c.
+ */
+
+#if defined(__arm__) || defined(__mips__) || defined(__powerpc__)
+#define MALLOCALIGN 64
+#else
+#define MALLOCALIGN 16
+#endif
+#define MALLOCALIGN_MASK (MALLOCALIGN - 1)
+
+typedef struct Guard {
+ size_t ga_Bytes;
+ size_t ga_Magic; /* must be at least 32 bits */
+} Guard;
+
+#define GAMAGIC 0x55FF44FD
+#define GAFREE 0x5F54F4DF
+
+#include "zalloc_protos.h"
diff --git a/stand/libsa/zalloc_malloc.c b/stand/libsa/zalloc_malloc.c
new file mode 100644
index 0000000..b9a295f
--- /dev/null
+++ b/stand/libsa/zalloc_malloc.c
@@ -0,0 +1,200 @@
+/*
+ * This module derived from code donated to the FreeBSD Project by
+ * Matthew Dillon <dillon@backplane.com>
+ *
+ * Copyright (c) 1998 The FreeBSD Project
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * MALLOC.C - malloc equivalent, runs on top of zalloc and uses sbrk
+ */
+
+#include "zalloc_defs.h"
+
+static MemPool MallocPool;
+
+#ifdef DMALLOCDEBUG
+static int MallocMax;
+static int MallocCount;
+
+void mallocstats(void);
+#endif
+
+#ifdef malloc
+#undef malloc
+#undef free
+#endif
+
+void *
+Malloc(size_t bytes, const char *file, int line)
+{
+ Guard *res;
+
+#ifdef USEENDGUARD
+ bytes += MALLOCALIGN + 1;
+#else
+ bytes += MALLOCALIGN;
+#endif
+
+ while ((res = znalloc(&MallocPool, bytes)) == NULL) {
+ int incr = (bytes + BLKEXTENDMASK) & ~BLKEXTENDMASK;
+ char *base;
+
+ if ((base = sbrk(incr)) == (char *)-1)
+ return(NULL);
+ zextendPool(&MallocPool, base, incr);
+ zfree(&MallocPool, base, incr);
+ }
+#ifdef DMALLOCDEBUG
+ if (++MallocCount > MallocMax)
+ MallocMax = MallocCount;
+#endif
+#ifdef USEGUARD
+ res->ga_Magic = GAMAGIC;
+#endif
+ res->ga_Bytes = bytes;
+#ifdef USEENDGUARD
+ *((signed char *)res + bytes - 1) = -2;
+#endif
+
+ return((char *)res + MALLOCALIGN);
+}
+
+void
+Free(void *ptr, const char *file, int line)
+{
+ size_t bytes;
+
+ if (ptr != NULL) {
+ Guard *res = (void *)((char *)ptr - MALLOCALIGN);
+
+ if (file == NULL)
+ file = "unknown";
+#ifdef USEGUARD
+ if (res->ga_Magic == GAFREE) {
+ printf("free: duplicate free @ %p from %s:%d\n", ptr, file, line);
+ return;
+ }
+ if (res->ga_Magic != GAMAGIC)
+ panic("free: guard1 fail @ %p from %s:%d", ptr, file, line);
+ res->ga_Magic = GAFREE;
+#endif
+#ifdef USEENDGUARD
+ if (*((signed char *)res + res->ga_Bytes - 1) == -1) {
+ printf("free: duplicate2 free @ %p from %s:%d\n", ptr, file, line);
+ return;
+ }
+ if (*((signed char *)res + res->ga_Bytes - 1) != -2)
+ panic("free: guard2 fail @ %p + %zu from %s:%d", ptr, res->ga_Bytes - MALLOCALIGN, file, line);
+ *((signed char *)res + res->ga_Bytes - 1) = -1;
+#endif
+
+ bytes = res->ga_Bytes;
+ zfree(&MallocPool, res, bytes);
+#ifdef DMALLOCDEBUG
+ --MallocCount;
+#endif
+ }
+}
+
+
+void *
+Calloc(size_t n1, size_t n2, const char *file, int line)
+{
+ uintptr_t bytes = (uintptr_t)n1 * (uintptr_t)n2;
+ void *res;
+
+ if ((res = Malloc(bytes, file, line)) != NULL) {
+ bzero(res, bytes);
+#ifdef DMALLOCDEBUG
+ if (++MallocCount > MallocMax)
+ MallocMax = MallocCount;
+#endif
+ }
+ return(res);
+}
+
+/*
+ * realloc() - I could be fancier here and free the old buffer before
+ * allocating the new one (saving potential fragmentation
+ * and potential buffer copies). But I don't bother.
+ */
+
+void *
+Realloc(void *ptr, size_t size, const char *file, int line)
+{
+ void *res;
+ size_t old;
+
+ if ((res = Malloc(size, file, line)) != NULL) {
+ if (ptr) {
+ old = *(size_t *)((char *)ptr - MALLOCALIGN) - MALLOCALIGN;
+ if (old < size)
+ bcopy(ptr, res, old);
+ else
+ bcopy(ptr, res, size);
+ Free(ptr, file, line);
+ } else {
+#ifdef DMALLOCDEBUG
+ if (++MallocCount > MallocMax)
+ MallocMax = MallocCount;
+#ifdef EXITSTATS
+ if (DidAtExit == 0) {
+ DidAtExit = 1;
+ atexit(mallocstats);
+ }
+#endif
+#endif
+ }
+ }
+ return(res);
+}
+
+void *
+Reallocf(void *ptr, size_t size, const char *file, int line)
+{
+ void *res;
+
+ if ((res = Realloc(ptr, size, file, line)) == NULL)
+ Free(ptr, file, line);
+ return(res);
+}
+
+#ifdef DMALLOCDEBUG
+
+void
+mallocstats(void)
+{
+ printf("Active Allocations: %d/%d\n", MallocCount, MallocMax);
+#ifdef ZALLOCDEBUG
+ zallocstats(&MallocPool);
+#endif
+}
+
+#endif
+
diff --git a/stand/libsa/zalloc_mem.h b/stand/libsa/zalloc_mem.h
new file mode 100644
index 0000000..26d388d
--- /dev/null
+++ b/stand/libsa/zalloc_mem.h
@@ -0,0 +1,53 @@
+/*
+ * This module derived from code donated to the FreeBSD Project by
+ * Matthew Dillon <dillon@backplane.com>
+ *
+ * Copyright (c) 1998 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * H/MEM.H
+ *
+ * Basic memory pool / memory node structures.
+ */
+
+typedef struct MemNode {
+ struct MemNode *mr_Next;
+ uintptr_t mr_Bytes;
+} MemNode;
+
+typedef struct MemPool {
+ void *mp_Base;
+ void *mp_End;
+ MemNode *mp_First;
+ uintptr_t mp_Size;
+ uintptr_t mp_Used;
+} MemPool;
+
+#define ZNOTE_FREE 0
+#define ZNOTE_REUSE 1
+
diff --git a/stand/libsa/zalloc_protos.h b/stand/libsa/zalloc_protos.h
new file mode 100644
index 0000000..53a40e4
--- /dev/null
+++ b/stand/libsa/zalloc_protos.h
@@ -0,0 +1,35 @@
+/*
+ * This module derived from code donated to the FreeBSD Project by
+ * Matthew Dillon <dillon@backplane.com>
+ *
+ * Copyright (c) 1998 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+Library void *znalloc(struct MemPool *mpool, uintptr_t bytes);
+Library void zfree(struct MemPool *mpool, void *ptr, uintptr_t bytes);
+Library void zextendPool(MemPool *mp, void *base, uintptr_t bytes);
+Library void zallocstats(struct MemPool *mp);
diff --git a/stand/libsa32/Makefile b/stand/libsa32/Makefile
new file mode 100644
index 0000000..eba6c00
--- /dev/null
+++ b/stand/libsa32/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+DO32=1
+
+.include <bsd.init.mk>
+
+LIB=sa32
+LIBSA_CPUARCH=${MACHINE_CPUARCH:C/amd64/i386/}
+
+.PATH: ${SASRC}
+.include "${SASRC}/Makefile"
diff --git a/stand/libsa32/Makefile.depend b/stand/libsa32/Makefile.depend
new file mode 100644
index 0000000..1d86fce
--- /dev/null
+++ b/stand/libsa32/Makefile.depend
@@ -0,0 +1,15 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/libbz2 \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/loader.mk b/stand/loader.mk
new file mode 100644
index 0000000..66fc2ce
--- /dev/null
+++ b/stand/loader.mk
@@ -0,0 +1,102 @@
+# $FreeBSD$
+
+.include "defs.mk"
+
+.PATH: ${LDRSRC} ${BOOTSRC}/libsa
+
+CFLAGS+=-I${LDRSRC}
+
+SRCS+= boot.c commands.c console.c devopen.c interp.c
+SRCS+= interp_backslash.c interp_parse.c ls.c misc.c
+SRCS+= module.c
+
+.if ${MACHINE} == "i386" || ${MACHINE_CPUARCH} == "amd64"
+SRCS+= load_elf32.c load_elf32_obj.c reloc_elf32.c
+SRCS+= load_elf64.c load_elf64_obj.c reloc_elf64.c
+.elif ${MACHINE} == "pc98"
+SRCS+= load_elf32.c load_elf32_obj.c reloc_elf32.c
+.elif ${MACHINE_CPUARCH} == "aarch64"
+SRCS+= load_elf64.c reloc_elf64.c
+.elif ${MACHINE_CPUARCH} == "arm"
+SRCS+= load_elf32.c reloc_elf32.c
+.elif ${MACHINE_CPUARCH} == "powerpc"
+SRCS+= load_elf32.c reloc_elf32.c
+SRCS+= load_elf64.c reloc_elf64.c
+.elif ${MACHINE_CPUARCH} == "sparc64"
+SRCS+= load_elf64.c reloc_elf64.c
+.elif ${MACHINE_ARCH:Mmips64*} != ""
+SRCS+= load_elf64.c reloc_elf64.c
+.elif ${MACHINE} == "mips"
+SRCS+= load_elf32.c reloc_elf32.c
+.endif
+
+.if ${LOADER_DISK_SUPPORT:Uyes} == "yes"
+SRCS+= disk.c part.c
+.endif
+
+.if ${LOADER_NET_SUPPORT:Uno} == "yes"
+SRCS+= dev_net.c
+.endif
+
+.if defined(HAVE_BCACHE)
+SRCS+= bcache.c
+.endif
+
+.if defined(MD_IMAGE_SIZE)
+CFLAGS+= -DMD_IMAGE_SIZE=${MD_IMAGE_SIZE}
+SRCS+= md.c
+.else
+CLEANFILES+= md.o
+.endif
+
+# Machine-independant ISA PnP
+.if defined(HAVE_ISABUS)
+SRCS+= isapnp.c
+.endif
+.if defined(HAVE_PNP)
+SRCS+= pnp.c
+.endif
+
+# Forth interpreter
+.if ${MK_FORTH} != "no"
+SRCS+= interp_forth.c
+.include "${BOOTSRC}/ficl.mk"
+.endif
+
+.if defined(BOOT_PROMPT_123)
+CFLAGS+= -DBOOT_PROMPT_123
+.endif
+
+.if defined(LOADER_INSTALL_SUPPORT)
+SRCS+= install.c
+.endif
+
+.if defined(HAVE_ZFS)
+CFLAGS+= -DLOADER_ZFS_SUPPORT
+CFLAGS+= -I${ZFSSRC}
+CFLAGS+= -I${SYSDIR}/cddl/boot/zfs
+.if ${MACHINE} == "amd64"
+# Have to override to use 32-bit version of zfs library...
+# kinda lame to select that there XXX
+LIBZFSBOOT= ${BOOTOBJ}/zfs32/libzfsboot.a
+.else
+LIBZFSBOOT= ${BOOTOBJ}/zfs/libzfsboot.a
+.endif
+.endif
+
+CLEANFILES+= vers.c
+VERSION_FILE?= ${.CURDIR}/version
+.if ${MK_REPRODUCIBLE_BUILD} != no
+REPRO_FLAG= -r
+.endif
+vers.c: ${LDRSRC}/newvers.sh ${VERSION_FILE}
+ sh ${LDRSRC}/newvers.sh ${REPRO_FLAG} ${VERSION_FILE} \
+ ${NEWVERSWHAT}
+
+.if !empty(HELP_FILES)
+CLEANFILES+= loader.help
+FILES+= loader.help
+
+loader.help: ${HELP_FILES}
+ cat ${HELP_FILES} | awk -f ${LDRSRC}/merge_help.awk > ${.TARGET}
+.endif
diff --git a/stand/man/Makefile b/stand/man/Makefile
new file mode 100644
index 0000000..15285ac
--- /dev/null
+++ b/stand/man/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+MAN+= loader.8
+.if ${MK_ZFS} != "no"
+MAN+= zfsloader.8
+.endif
+
+.include <bsd.prog.mk>
diff --git a/stand/man/loader.8 b/stand/man/loader.8
new file mode 100644
index 0000000..7af7343
--- /dev/null
+++ b/stand/man/loader.8
@@ -0,0 +1,1101 @@
+.\" Copyright (c) 1999 Daniel C. Sobral
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 18, 2015
+.Dt LOADER 8
+.Os
+.Sh NAME
+.Nm loader
+.Nd kernel bootstrapping final stage
+.Sh DESCRIPTION
+The program called
+.Nm
+is the final stage of
+.Fx Ns 's
+kernel bootstrapping process.
+On IA32 (i386) architectures, it is a
+.Pa BTX
+client.
+It is linked statically to
+.Xr libstand 3
+and usually located in the directory
+.Pa /boot .
+.Pp
+It provides a scripting language that can be used to
+automate tasks, do pre-configuration or assist in recovery
+procedures.
+This scripting language is roughly divided in
+two main components.
+The smaller one is a set of commands
+designed for direct use by the casual user, called "builtin
+commands" for historical reasons.
+The main drive behind these commands is user-friendliness.
+The bigger component is an
+.Tn ANS
+Forth compatible Forth interpreter based on FICL, by
+.An John Sadler .
+.Pp
+During initialization,
+.Nm
+will probe for a console and set the
+.Va console
+variable, or set it to serial console
+.Pq Dq Li comconsole
+if the previous boot stage used that.
+If multiple consoles are selected, they will be listed separated by spaces.
+Then, devices are probed,
+.Va currdev
+and
+.Va loaddev
+are set, and
+.Va LINES
+is set to 24.
+Next,
+.Tn FICL
+is initialized, the builtin words are added to its vocabulary, and
+.Pa /boot/boot.4th
+is processed if it exists.
+No disk switching is possible while that file is being read.
+The inner interpreter
+.Nm
+will use with
+.Tn FICL
+is then set to
+.Ic interpret ,
+which is
+.Tn FICL Ns 's
+default.
+After that,
+.Pa /boot/loader.rc
+is processed if available.
+These files are processed through the
+.Ic include
+command, which reads all of them into memory before processing them,
+making disk changes possible.
+.Pp
+At this point, if an
+.Ic autoboot
+has not been tried, and if
+.Va autoboot_delay
+is not set to
+.Dq Li NO
+(not case sensitive), then an
+.Ic autoboot
+will be tried.
+If the system gets past this point,
+.Va prompt
+will be set and
+.Nm
+will engage interactive mode.
+Please note that historically even when
+.Va autoboot_delay
+is set to
+.Dq Li 0
+user will be able to interrupt autoboot process by pressing some key
+on the console while kernel and modules are being loaded.
+In some
+cases such behaviour may be undesirable, to prevent it set
+.Va autoboot_delay
+to
+.Dq Li -1 ,
+in this case
+.Nm
+will engage interactive mode only if
+.Ic autoboot
+has failed.
+.Sh BUILTIN COMMANDS
+In
+.Nm ,
+builtin commands take parameters from the command line.
+Presently,
+the only way to call them from a script is by using
+.Pa evaluate
+on a string.
+If an error condition occurs, an exception will be generated,
+which can be intercepted using
+.Tn ANS
+Forth exception handling
+words.
+If not intercepted, an error message will be displayed and
+the interpreter's state will be reset, emptying the stack and restoring
+interpreting mode.
+.Pp
+The builtin commands available are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ic autoboot Op Ar seconds Op Ar prompt
+Proceeds to bootstrap the system after a number of seconds, if not
+interrupted by the user.
+Displays a countdown prompt
+warning the user the system is about to be booted,
+unless interrupted by a key press.
+The kernel will be loaded first if necessary.
+Defaults to 10 seconds.
+.Pp
+.It Ic bcachestat
+Displays statistics about disk cache usage.
+For debugging only.
+.Pp
+.It Ic boot
+.It Ic boot Ar kernelname Op Cm ...
+.It Ic boot Fl flag Cm ...
+Immediately proceeds to bootstrap the system, loading the kernel
+if necessary.
+Any flags or arguments are passed to the kernel, but they
+must precede the kernel name, if a kernel name is provided.
+.Pp
+.Em WARNING :
+The behavior of this builtin is changed if
+.Xr loader.4th 8
+is loaded.
+.Pp
+.It Ic echo Xo
+.Op Fl n
+.Op Aq message
+.Xc
+Displays text on the screen.
+A new line will be printed unless
+.Fl n
+is specified.
+.Pp
+.It Ic heap
+Displays memory usage statistics.
+For debugging purposes only.
+.Pp
+.It Ic help Op topic Op subtopic
+Shows help messages read from
+.Pa /boot/loader.help .
+The special topic
+.Em index
+will list the topics available.
+.Pp
+.It Ic include Ar file Op Ar
+Process script files.
+Each file, in turn, is completely read into memory,
+and then each of its lines is passed to the command line interpreter.
+If any error is returned by the interpreter, the include
+command aborts immediately, without reading any other files, and
+returns an error itself (see
+.Sx ERRORS ) .
+.Pp
+.It Ic load Xo
+.Op Fl t Ar type
+.Ar file Cm ...
+.Xc
+Loads a kernel, kernel loadable module (kld), disk image,
+or file of opaque contents tagged as being of the type
+.Ar type .
+Kernel and modules can be either in a.out or ELF format.
+Any arguments passed after the name of the file to be loaded
+will be passed as arguments to that file.
+Use the
+.Li md_image
+type to make the kernel create a file-backed
+.Xr md 4
+disk.
+This is useful for booting from a temporary rootfs.
+Currently, argument passing does not work for the kernel.
+.Pp
+.It Ic load_geli Xo
+.Op Fl n Ar keyno
+.Ar prov Ar file
+.Xc
+Loads a
+.Xr geli 8
+encryption keyfile for the given provider name.
+The key index can be specified via
+.Ar keyno
+or will default to zero.
+.Pp
+.It Ic ls Xo
+.Op Fl l
+.Op Ar path
+.Xc
+Displays a listing of files in the directory
+.Ar path ,
+or the root directory if
+.Ar path
+is not specified.
+If
+.Fl l
+is specified, file sizes will be shown too.
+.Pp
+.It Ic lsdev Op Fl v
+Lists all of the devices from which it may be possible to load modules.
+If
+.Fl v
+is specified, more details are printed.
+.Pp
+.It Ic lsmod Op Fl v
+Displays loaded modules.
+If
+.Fl v
+is specified, more details are shown.
+.Pp
+.It Ic more Ar file Op Ar
+Display the files specified, with a pause at each
+.Va LINES
+displayed.
+.Pp
+.It Ic pnpscan Op Fl v
+Scans for Plug-and-Play devices.
+This is not functional at present.
+.Pp
+.It Ic read Xo
+.Op Fl t Ar seconds
+.Op Fl p Ar prompt
+.Op Va variable
+.Xc
+Reads a line of input from the terminal, storing it in
+.Va variable
+if specified.
+A timeout can be specified with
+.Fl t ,
+though it will be canceled at the first key pressed.
+A prompt may also be displayed through the
+.Fl p
+flag.
+.Pp
+.It Ic reboot
+Immediately reboots the system.
+.Pp
+.It Ic set Ar variable
+.It Ic set Ar variable Ns = Ns Ar value
+Set loader's environment variables.
+.Pp
+.It Ic show Op Va variable
+Displays the specified variable's value, or all variables and their
+values if
+.Va variable
+is not specified.
+.Pp
+.It Ic unload
+Remove all modules from memory.
+.Pp
+.It Ic unset Va variable
+Removes
+.Va variable
+from the environment.
+.Pp
+.It Ic \&?
+Lists available commands.
+.El
+.Ss BUILTIN ENVIRONMENT VARIABLES
+The
+.Nm
+has actually two different kinds of
+.Sq environment
+variables.
+There are ANS Forth's
+.Em environmental queries ,
+and a separate space of environment variables used by builtins, which
+are not directly available to Forth words.
+It is the latter type that this section covers.
+.Pp
+Environment variables can be set and unset through the
+.Ic set
+and
+.Ic unset
+builtins, and can have their values interactively examined through the
+use of the
+.Ic show
+builtin.
+Their values can also be accessed as described in
+.Sx BUILTIN PARSER .
+.Pp
+Notice that these environment variables are not inherited by any shell
+after the system has been booted.
+.Pp
+A few variables are set automatically by
+.Nm .
+Others can affect the behavior of either
+.Nm
+or the kernel at boot.
+Some options may require a value,
+while others define behavior just by being set.
+Both types of builtin variables are described below.
+.Bl -tag -width bootfile
+.It Va autoboot_delay
+Number of seconds
+.Ic autoboot
+will wait before booting.
+If this variable is not defined,
+.Ic autoboot
+will default to 10 seconds.
+.Pp
+If set to
+.Dq Li NO ,
+no
+.Ic autoboot
+will be automatically attempted after processing
+.Pa /boot/loader.rc ,
+though explicit
+.Ic autoboot Ns 's
+will be processed normally, defaulting to 10 seconds delay.
+.Pp
+If set to
+.Dq Li 0 ,
+no delay will be inserted, but user still will be able to interrupt
+.Ic autoboot
+process and escape into the interactive mode by pressing some key
+on the console while kernel and
+modules are being loaded.
+.Pp
+If set to
+.Dq Li -1 ,
+no delay will be inserted and
+.Nm
+will engage interactive mode only if
+.Ic autoboot
+has failed for some reason.
+.It Va boot_askname
+Instructs the kernel to prompt the user for the name of the root device
+when the kernel is booted.
+.It Va boot_cdrom
+Instructs the kernel to try to mount the root file system from CD-ROM.
+.It Va boot_ddb
+Instructs the kernel to start in the DDB debugger, rather than
+proceeding to initialize when booted.
+.It Va boot_dfltroot
+Instructs the kernel to mount the statically compiled-in root file system.
+.It Va boot_gdb
+Selects gdb-remote mode for the kernel debugger by default.
+.It Va boot_multicons
+Enables multiple console support in the kernel early on boot.
+In a running system, console configuration can be manipulated
+by the
+.Xr conscontrol 8
+utility.
+.It Va boot_mute
+All console output is suppressed when console is muted.
+In a running system, the state of console muting can be manipulated by the
+.Xr conscontrol 8
+utility.
+.It Va boot_pause
+During the device probe, pause after each line is printed.
+.It Va boot_serial
+Force the use of a serial console even when an internal console
+is present.
+.It Va boot_single
+Prevents the kernel from initiating a multi-user startup; instead,
+a single-user mode will be entered when the kernel has finished
+device probing.
+.It Va boot_verbose
+Setting this variable causes extra debugging information to be printed
+by the kernel during the boot phase.
+.It Va bootfile
+List of semicolon-separated search path for bootable kernels.
+The default is
+.Dq Li kernel .
+.It Va comconsole_speed
+Defines the speed of the serial console (i386 and amd64 only).
+If the previous boot stage indicated that a serial console is in use
+then this variable is initialized to the current speed of the console
+serial port.
+Otherwise it is set to 9600 unless this was overridden using the
+.Va BOOT_COMCONSOLE_SPEED
+variable when
+.Nm
+was compiled.
+Changes to the
+.Va comconsole_speed
+variable take effect immediately.
+.It Va comconsole_port
+Defines the base i/o port used to access console UART
+(i386 and amd64 only).
+If the variable is not set, its assumed value is 0x3F8, which
+corresponds to PC port COM1, unless overridden by
+.Va BOOT_COMCONSOLE_PORT
+variable during the compilation of
+.Nm .
+Setting the
+.Va comconsole_port
+variable automatically set
+.Va hw.uart.console
+environment variable to provide a hint to kernel for location of the console.
+Loader console is changed immediately after variable
+.Va comconsole_port
+is set.
+.It Va comconsole_pcidev
+Defines the location of a PCI device of the 'simple communication'
+class to be used as the serial console UART (i386 and amd64 only).
+The syntax of the variable is
+.Li 'bus:device:function[:bar]' ,
+where all members must be numeric, with possible
+.Li 0x
+prefix to indicate a hexadecimal value.
+The
+.Va bar
+member is optional and assumed to be 0x10 if omitted.
+The bar must decode i/o space.
+Setting the variable
+.Va comconsole_pcidev
+automatically sets the variable
+.Va comconsole_port
+to the base of the selected bar, and hint
+.Va hw.uart.console .
+Loader console is changed immediately after variable
+.Va comconsole_pcidev
+is set.
+.It Va console
+Defines the current console or consoles.
+Multiple consoles may be specified.
+In that case, the first listed console will become the default console for
+userland output (e.g.\& from
+.Xr init 8 ) .
+.It Va currdev
+Selects the default device.
+Syntax for devices is odd.
+.It Va dumpdev
+Sets the device for kernel dumps.
+This can be used to ensure that a device is configured before the corresponding
+.Va dumpdev
+directive from
+.Xr rc.conf 5
+has been processed, allowing kernel panics that happen during the early stages
+of boot to be captured.
+.It Va init_chroot
+If set to a valid directory in the root file system, it causes
+.Xr init 8
+to perform a
+.Xr chroot 2
+operation on that directory, making it the new root directory.
+That happens before entering single-user mode or multi-user
+mode (but after executing the
+.Va init_script
+if enabled).
+This functionality has generally been eclipsed by rerooting.
+See
+.Xr reboot 8
+.Fl r
+for details.
+.It Va init_path
+Sets the list of binaries which the kernel will try to run as the initial
+process.
+The first matching binary is used.
+The default list is
+.Dq Li /sbin/init:/sbin/oinit:/sbin/init.bak:\:/rescue/init .
+.It Va init_script
+If set to a valid file name in the root file system,
+instructs
+.Xr init 8
+to run that script as the very first action,
+before doing anything else.
+Signal handling and exit code interpretation is similar to
+running the
+.Pa /etc/rc
+script.
+In particular, single-user operation is enforced
+if the script terminates with a non-zero exit code,
+or if a SIGTERM is delivered to the
+.Xr init 8
+process (PID 1).
+This functionality has generally been eclipsed by rerooting.
+See
+.Xr reboot 8
+.Fl r
+for details.
+.It Va init_shell
+Defines the shell binary to be used for executing the various shell scripts.
+The default is
+.Dq Li /bin/sh .
+It is used for running the
+.Va init_script
+if set, as well as for the
+.Pa /etc/rc
+and
+.Pa /etc/rc.shutdown
+scripts.
+The value of the corresponding
+.Xr kenv 2
+variable is evaluated every time
+.Xr init 8
+calls a shell script, so it can be changed later on using the
+.Xr kenv 1
+utility.
+In particular, if a non-default shell is used for running an
+.Va init_script ,
+it might be desirable to have that script reset the value of
+.Va init_shell
+back to the default, so that the
+.Pa /etc/rc
+script is executed with the standard shell
+.Pa /bin/sh .
+.It Va interpret
+Has the value
+.Dq Li OK
+if the Forth's current state is interpreting.
+.It Va LINES
+Define the number of lines on the screen, to be used by the pager.
+.It Va module_path
+Sets the list of directories which will be searched for modules
+named in a load command or implicitly required by a dependency.
+The default value for this variable is
+.Dq Li /boot/kernel;/boot/modules .
+.It Va num_ide_disks
+Sets the number of IDE disks as a workaround for some problems in
+finding the root disk at boot.
+This has been deprecated in favor of
+.Va root_disk_unit .
+.It Va prompt
+Value of
+.Nm Ns 's
+prompt.
+Defaults to
+.Dq Li "${interpret}" .
+If variable
+.Va prompt
+is unset, the default prompt is
+.Ql > .
+.It Va root_disk_unit
+If the code which detects the disk unit number for the root disk is
+confused, e.g.\& by a mix of SCSI and IDE disks, or IDE disks with
+gaps in the sequence (e.g.\& no primary slave), the unit number can
+be forced by setting this variable.
+.It Va rootdev
+By default the value of
+.Va currdev
+is used to set the root file system
+when the kernel is booted.
+This can be overridden by setting
+.Va rootdev
+explicitly.
+.El
+.Pp
+Other variables are used to override kernel tunable parameters.
+The following tunables are available:
+.Bl -tag -width Va
+.It Va hw.physmem
+Limit the amount of physical memory the system will use.
+By default the size is in bytes, but the
+.Cm k , K , m , M , g
+and
+.Cm G
+suffixes
+are also accepted and indicate kilobytes, megabytes and gigabytes
+respectively.
+An invalid suffix will result in the variable being ignored by the
+kernel.
+.It Va hw.pci.host_start_mem , hw.acpi.host_start_mem
+When not otherwise constrained, this limits the memory start
+address.
+The default is 0x80000000 and should be set to at least size of the
+memory and not conflict with other resources.
+Typically, only systems without PCI bridges need to set this variable
+since PCI bridges typically constrain the memory starting address
+(and the variable is only used when bridges do not constrain this
+address).
+.It Va hw.pci.enable_io_modes
+Enable PCI resources which are left off by some BIOSes or are not
+enabled correctly by the device driver.
+Tunable value set to ON (1) by default, but this may cause problems
+with some peripherals.
+.It Va kern.maxusers
+Set the size of a number of statically allocated system tables; see
+.Xr tuning 7
+for a description of how to select an appropriate value for this
+tunable.
+When set, this tunable replaces the value declared in the kernel
+compile-time configuration file.
+.It Va kern.ipc.nmbclusters
+Set the number of mbuf clusters to be allocated.
+The value cannot be set below the default
+determined when the kernel was compiled.
+.It Va kern.ipc.nsfbufs
+Set the number of
+.Xr sendfile 2
+buffers to be allocated.
+Overrides
+.Dv NSFBUFS .
+Not all architectures use such buffers; see
+.Xr sendfile 2
+for details.
+.It Va kern.maxswzone
+Limits the amount of KVM to be used to hold swap
+metadata, which directly governs the
+maximum amount of swap the system can support,
+at the rate of approximately 200 MB of swap space
+per 1 MB of metadata.
+This value is specified in bytes of KVA space.
+If no value is provided, the system allocates
+enough memory to handle an amount of swap
+that corresponds to eight times the amount of
+physical memory present in the system.
+.Pp
+Note that swap metadata can be fragmented,
+which means that the system can run out of
+space before it reaches the theoretical limit.
+Therefore, care should be taken to not configure
+more swap than approximately half of the
+theoretical maximum.
+.Pp
+Running out of space for swap metadata can leave
+the system in an unrecoverable state.
+Therefore, you should only change
+this parameter if you need to greatly extend the
+KVM reservation for other resources such as the
+buffer cache or
+.Va kern.ipc.nmbclusters .
+Modifies kernel option
+.Dv VM_SWZONE_SIZE_MAX .
+.It Va kern.maxbcache
+Limits the amount of KVM reserved for use by the
+buffer cache, specified in bytes.
+The default maximum is 200MB on i386,
+and 400MB on amd64 and sparc64.
+This parameter is used to
+prevent the buffer cache from eating too much
+KVM in large-memory machine configurations.
+Only mess around with this parameter if you need to
+greatly extend the KVM reservation for other resources
+such as the swap zone or
+.Va kern.ipc.nmbclusters .
+Note that
+the NBUF parameter will override this limit.
+Modifies
+.Dv VM_BCACHE_SIZE_MAX .
+.It Va kern.msgbufsize
+Sets the size of the kernel message buffer.
+The default limit of 64KB is usually sufficient unless
+large amounts of trace data need to be collected
+between opportunities to examine the buffer or
+dump it to a file.
+Overrides kernel option
+.Dv MSGBUF_SIZE .
+.It Va machdep.disable_mtrrs
+Disable the use of i686 MTRRs (x86 only).
+.It Va net.inet.tcp.tcbhashsize
+Overrides the compile-time set value of
+.Dv TCBHASHSIZE
+or the preset default of 512.
+Must be a power of 2.
+.It Va twiddle_divisor
+Throttles the output of the
+.Sq twiddle
+I/O progress indicator displayed while loading the kernel and modules.
+This is useful on slow serial consoles where the time spent waiting for
+these characters to be written can add up to many seconds.
+The default is 1 (full speed); a value of 2 spins half as fast, and so on.
+.It Va vm.kmem_size
+Sets the size of kernel memory (bytes).
+This overrides the value determined when the kernel was compiled.
+Modifies
+.Dv VM_KMEM_SIZE .
+.It Va vm.kmem_size_min
+.It Va vm.kmem_size_max
+Sets the minimum and maximum (respectively) amount of kernel memory
+that will be automatically allocated by the kernel.
+These override the values determined when the kernel was compiled.
+Modifies
+.Dv VM_KMEM_SIZE_MIN
+and
+.Dv VM_KMEM_SIZE_MAX .
+.El
+.Ss BUILTIN PARSER
+When a builtin command is executed, the rest of the line is taken
+by it as arguments, and it is processed by a special parser which
+is not used for regular Forth commands.
+.Pp
+This special parser applies the following rules to the parsed text:
+.Bl -enum
+.It
+All backslash characters are preprocessed.
+.Bl -bullet
+.It
+\eb , \ef , \er , \en and \et are processed as in C.
+.It
+\es is converted to a space.
+.It
+\ev is converted to
+.Tn ASCII
+11.
+.It
+\ez is just skipped.
+Useful for things like
+.Dq \e0xf\ez\e0xf .
+.It
+\e0xN and \e0xNN are replaced by the hex N or NN.
+.It
+\eNNN is replaced by the octal NNN
+.Tn ASCII
+character.
+.It
+\e" , \e' and \e$ will escape these characters, preventing them from
+receiving special treatment in Step 2, described below.
+.It
+\e\e will be replaced with a single \e .
+.It
+In any other occurrence, backslash will just be removed.
+.El
+.It
+Every string between non-escaped quotes or double-quotes will be treated
+as a single word for the purposes of the remaining steps.
+.It
+Replace any
+.Li $VARIABLE
+or
+.Li ${VARIABLE}
+with the value of the environment variable
+.Va VARIABLE .
+.It
+Space-delimited arguments are passed to the called builtin command.
+Spaces can also be escaped through the use of \e\e .
+.El
+.Pp
+An exception to this parsing rule exists, and is described in
+.Sx BUILTINS AND FORTH .
+.Ss BUILTINS AND FORTH
+All builtin words are state-smart, immediate words.
+If interpreted, they behave exactly as described previously.
+If they are compiled, though,
+they extract their arguments from the stack instead of the command line.
+.Pp
+If compiled, the builtin words expect to find, at execution time, the
+following parameters on the stack:
+.D1 Ar addrN lenN ... addr2 len2 addr1 len1 N
+where
+.Ar addrX lenX
+are strings which will compose the command line that will be parsed
+into the builtin's arguments.
+Internally, these strings are concatenated in from 1 to N,
+with a space put between each one.
+.Pp
+If no arguments are passed, a 0
+.Em must
+be passed, even if the builtin accepts no arguments.
+.Pp
+While this behavior has benefits, it has its trade-offs.
+If the execution token of a builtin is acquired (through
+.Ic '
+or
+.Ic ['] ) ,
+and then passed to
+.Ic catch
+or
+.Ic execute ,
+the builtin behavior will depend on the system state
+.Bf Em
+at the time
+.Ic catch
+or
+.Ic execute
+is processed!
+.Ef
+This is particularly annoying for programs that want or need to
+handle exceptions.
+In this case, the use of a proxy is recommended.
+For example:
+.Dl : (boot) boot ;
+.Sh FICL
+.Tn FICL
+is a Forth interpreter written in C, in the form of a forth
+virtual machine library that can be called by C functions and vice
+versa.
+.Pp
+In
+.Nm ,
+each line read interactively is then fed to
+.Tn FICL ,
+which may call
+.Nm
+back to execute the builtin words.
+The builtin
+.Ic include
+will also feed
+.Tn FICL ,
+one line at a time.
+.Pp
+The words available to
+.Tn FICL
+can be classified into four groups.
+The
+.Tn ANS
+Forth standard words, extra
+.Tn FICL
+words, extra
+.Fx
+words, and the builtin commands;
+the latter were already described.
+The
+.Tn ANS
+Forth standard words are listed in the
+.Sx STANDARDS
+section.
+The words falling in the two other groups are described in the
+following subsections.
+.Ss FICL EXTRA WORDS
+.Bl -tag -width wid-set-super
+.It Ic .env
+.It Ic .ver
+.It Ic -roll
+.It Ic 2constant
+.It Ic >name
+.It Ic body>
+.It Ic compare
+This is the STRING word set's
+.Ic compare .
+.It Ic compile-only
+.It Ic endif
+.It Ic forget-wid
+.It Ic parse-word
+.It Ic sliteral
+This is the STRING word set's
+.Ic sliteral .
+.It Ic wid-set-super
+.It Ic w@
+.It Ic w!
+.It Ic x.
+.It Ic empty
+.It Ic cell-
+.It Ic -rot
+.El
+.Ss FREEBSD EXTRA WORDS
+.Bl -tag -width XXXXXXXX
+.It Ic \&$ Pq --
+Evaluates the remainder of the input buffer, after having printed it first.
+.It Ic \&% Pq --
+Evaluates the remainder of the input buffer under a
+.Ic catch
+exception guard.
+.It Ic .#
+Works like
+.Ic "."
+but without outputting a trailing space.
+.It Ic fclose Pq Ar fd --
+Closes a file.
+.It Ic fkey Pq Ar fd -- char
+Reads a single character from a file.
+.It Ic fload Pq Ar fd --
+Processes a file
+.Em fd .
+.It Ic fopen Pq Ar addr len mode Li -- Ar fd
+Opens a file.
+Returns a file descriptor, or \-1 in case of failure.
+The
+.Ar mode
+parameter selects whether the file is to be opened for read access, write
+access, or both.
+The constants
+.Dv O_RDONLY , O_WRONLY ,
+and
+.Dv O_RDWR
+are defined in
+.Pa /boot/support.4th ,
+indicating read only, write only, and read-write access, respectively.
+.It Xo
+.Ic fread
+.Pq Ar fd addr len -- len'
+.Xc
+Tries to read
+.Em len
+bytes from file
+.Em fd
+into buffer
+.Em addr .
+Returns the actual number of bytes read, or -1 in case of error or end of
+file.
+.It Ic heap? Pq -- Ar cells
+Return the space remaining in the dictionary heap, in cells.
+This is not related to the heap used by dynamic memory allocation words.
+.It Ic inb Pq Ar port -- char
+Reads a byte from a port.
+.It Ic key Pq -- Ar char
+Reads a single character from the console.
+.It Ic key? Pq -- Ar flag
+Returns
+.Ic true
+if there is a character available to be read from the console.
+.It Ic ms Pq Ar u --
+Waits
+.Em u
+microseconds.
+.It Ic outb Pq Ar port char --
+Writes a byte to a port.
+.It Ic seconds Pq -- Ar u
+Returns the number of seconds since midnight.
+.It Ic tib> Pq -- Ar addr len
+Returns the remainder of the input buffer as a string on the stack.
+.It Ic trace! Pq Ar flag --
+Activates or deactivates tracing.
+Does not work with
+.Ic catch .
+.El
+.Ss FREEBSD DEFINED ENVIRONMENTAL QUERIES
+.Bl -tag -width Ds
+.It arch-i386
+.Ic TRUE
+if the architecture is IA32.
+.It FreeBSD_version
+.Fx
+version at compile time.
+.It loader_version
+.Nm
+version.
+.El
+.Ss SYSTEM DOCUMENTATION
+.Sh FILES
+.Bl -tag -width /boot/defaults/loader.conf -compact
+.It Pa /boot/loader
+.Nm
+itself.
+.It Pa /boot/boot.4th
+Additional
+.Tn FICL
+initialization.
+.It Pa /boot/defaults/loader.conf
+.It Pa /boot/loader.conf
+.It Pa /boot/loader.conf.local
+.Nm
+configuration files, as described in
+.Xr loader.conf 5 .
+.It Pa /boot/loader.rc
+.Nm
+bootstrapping script.
+.It Pa /boot/loader.help
+Loaded by
+.Ic help .
+Contains the help messages.
+.El
+.Sh EXAMPLES
+Boot in single user mode:
+.Pp
+.Dl boot -s
+.Pp
+Load the kernel, a splash screen, and then autoboot in five seconds.
+Notice that a kernel must be loaded before any other
+.Ic load
+command is attempted.
+.Bd -literal -offset indent
+load kernel
+load splash_bmp
+load -t splash_image_data /boot/chuckrulez.bmp
+autoboot 5
+.Ed
+.Pp
+Set the disk unit of the root device to 2, and then boot.
+This would be needed in a system with two IDE disks,
+with the second IDE disk hardwired to ada2 instead of ada1.
+.Bd -literal -offset indent
+set root_disk_unit=2
+boot /boot/kernel/kernel
+.Ed
+.Pp
+See also:
+.Bl -tag -width /usr/share/examples/bootforth/X
+.It Pa /boot/loader.4th
+Extra builtin-like words.
+.It Pa /boot/support.4th
+.Pa loader.conf
+processing words.
+.It Pa /usr/share/examples/bootforth/
+Assorted examples.
+.El
+.Sh ERRORS
+The following values are thrown by
+.Nm :
+.Bl -tag -width XXXXX -offset indent
+.It 100
+Any type of error in the processing of a builtin.
+.It -1
+.Ic Abort
+executed.
+.It -2
+.Ic Abort"
+executed.
+.It -56
+.Ic Quit
+executed.
+.It -256
+Out of interpreting text.
+.It -257
+Need more text to succeed -- will finish on next run.
+.It -258
+.Ic Bye
+executed.
+.It -259
+Unspecified error.
+.El
+.Sh SEE ALSO
+.Xr libstand 3 ,
+.Xr loader.conf 5 ,
+.Xr tuning 7 ,
+.Xr boot 8 ,
+.Xr btxld 8
+.Sh STANDARDS
+For the purposes of ANS Forth compliance, loader is an
+.Bf Em
+ANS Forth System with Environmental Restrictions, Providing
+.Ef
+.Bf Li
+.No .( ,
+.No :noname ,
+.No ?do ,
+parse, pick, roll, refill, to, value, \e, false, true,
+.No <> ,
+.No 0<> ,
+compile\&, , erase, nip, tuck
+.Ef
+.Em and
+.Li marker
+.Bf Em
+from the Core Extensions word set, Providing the Exception Extensions
+word set, Providing the Locals Extensions word set, Providing the
+Memory-Allocation Extensions word set, Providing
+.Ef
+.Bf Li
+\&.s,
+bye, forget, see, words,
+\&[if],
+\&[else]
+.Ef
+.Em and
+.Li [then]
+.Bf Em
+from the Programming-Tools extension word set, Providing the
+Search-Order extensions word set.
+.Ef
+.Sh HISTORY
+The
+.Nm
+first appeared in
+.Fx 3.1 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+was written by
+.An Michael Smith Aq msmith@FreeBSD.org .
+.Pp
+.Tn FICL
+was written by
+.An John Sadler Aq john_sadler@alum.mit.edu .
+.Sh BUGS
+The
+.Ic expect
+and
+.Ic accept
+words will read from the input buffer instead of the console.
+The latter will be fixed, but the former will not.
diff --git a/stand/man/zfsloader.8 b/stand/man/zfsloader.8
new file mode 100644
index 0000000..fd437d9
--- /dev/null
+++ b/stand/man/zfsloader.8
@@ -0,0 +1,106 @@
+.\" Copyright (c) 2014 Andriy Gapon <avg@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 15, 2014
+.Dt ZFSLOADER 8
+.Os
+.Sh NAME
+.Nm zfsloader
+.Nd kernel bootstrapping final stage
+.Sh DESCRIPTION
+.Nm
+is an extended variant of
+.Xr loader 8
+with added support for booting from ZFS.
+This document describes only differences from
+.Xr loader 8 .
+.Sh ZFS FEATURES
+.Nm
+supports the following format for specifying ZFS filesystems which
+can be used wherever
+.Xr loader 8
+refers to a device specification:
+.Pp
+.Ar zfs:pool/filesystem:
+.Pp
+where
+.Pa pool/filesystem
+is a ZFS filesystem name as described in
+.Xr zfs 8 .
+.Pp
+If
+.Pa /etc/fstab
+does not have an entry for the root filesystem and
+.Va vfs.root.mountfrom
+is not set, but
+.Va currdev
+refers to a ZFS filesystem, then
+.Nm
+will instruct kernel to use that filesystem as the root filesystem.
+.Sh ZFS COMMAND EXTENSIONS
+.Bl -tag -width Ds -compact
+.It Ic lsdev Op Fl v
+Lists ZFS pools in addition to disks and partitions.
+Adding
+.Fl v
+shows more ZFS pool details in a format that resembles
+.Nm zpool Cm status
+output.
+.Pp
+.It Ic lszfs Ar filesystem
+A ZFS extended command that can be used to explore the ZFS filesystem
+hierarchy in a pool.
+Lists the immediate children of the
+.Ar filesystem .
+The filesystem hierarchy is rooted at a filesystem with the same name
+as the pool.
+.El
+.Sh FILES
+.Bl -tag -width /boot/zfsloader -compact
+.It Pa /boot/zfsloader
+.Nm
+itself.
+.El
+.Sh EXAMPLES
+Set the default device used for loading a kernel from a ZFS filesystem:
+.Bd -literal -offset indent
+set currdev=zfs:tank/ROOT/knowngood:
+.Ed
+.Sh SEE ALSO
+.Xr gptzfsboot 8 ,
+.Xr loader 8 ,
+.Xr zfs 8 ,
+.Xr zfsboot 8 ,
+.Xr zfsloader 8 ,
+.Xr zpool 8
+.Sh HISTORY
+The
+.Nm
+first appeared in
+.Fx 7.3 .
+.Sh AUTHORS
+This manual page was written by
+.An Andriy Gapon Aq avg@FreeBSD.org .
diff --git a/stand/mips/Makefile b/stand/mips/Makefile
new file mode 100644
index 0000000..760c557
--- /dev/null
+++ b/stand/mips/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+SUBDIR= uboot
+
+#
+# The BERI boot loader port works only on 64-bit MIPS; not a hard port to
+# 32-bit if someone is interested. Build on all 64-bit MIPS platforms to
+# ensure it gets adequate build-test coverage.
+#
+.if ${MACHINE_ARCH} == "mips64"
+SUBDIR+= beri
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/stand/mips/Makefile.inc b/stand/mips/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/stand/mips/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/stand/mips/beri/Makefile b/stand/mips/beri/Makefile
new file mode 100644
index 0000000..afcb538
--- /dev/null
+++ b/stand/mips/beri/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= boot2 loader
+
+.include <bsd.subdir.mk>
diff --git a/stand/mips/beri/Makefile.inc b/stand/mips/beri/Makefile.inc
new file mode 100644
index 0000000..a12699c
--- /dev/null
+++ b/stand/mips/beri/Makefile.inc
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+CFLAGS+= -ffreestanding
+LDFLAGS+= -nostdlib
+
+.include "../Makefile.inc"
diff --git a/stand/mips/beri/boot2/Makefile b/stand/mips/beri/boot2/Makefile
new file mode 100644
index 0000000..d369340
--- /dev/null
+++ b/stand/mips/beri/boot2/Makefile
@@ -0,0 +1,86 @@
+#-
+# Copyright (c) 2013-2014 Robert N. M. Watson
+# All rights reserved.
+#
+# This software was developed by SRI International and the University of
+# Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+# ("CTSRD"), as part of the DARPA CRASH research programme.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+INSTALLFLAGS= -b
+
+LOADERS= flashboot jtagboot
+FILES= ${LOADERS} ${LOADERS:S/$/.md5/}
+
+SRCS= relocate.S \
+ start.S \
+ boot2.c \
+ altera_jtag_uart.c \
+ cfi.c \
+ sdcard.c
+
+MAN=
+
+AFLAGS= -G0
+
+CFLAGS= -ffreestanding \
+ -I${.CURDIR} \
+ -I${SASRC} \
+ -I${LDRSRC} \
+ -D_KERNEL \
+ -Wall \
+ -G0 \
+ -fno-pic -mno-abicalls \
+ -msoft-float \
+ -g
+
+LDFLAGS= -nostdlib \
+ -static \
+ -Wl,-N \
+ -G0 \
+ -L${.CURDIR}
+
+.PATH: ${BOOTSRC}/mips/beri/common
+CFLAGS+= -I${BOOTSRC}/mips/beri/common
+
+flashboot.elf: relocate.o start.o boot2.o altera_jtag_uart.o cfi.o sdcard.o
+ ${CC} ${LDFLAGS} -T ${.CURDIR}/flashboot.ldscript -o ${.TARGET} \
+ ${.ALLSRC} ${LIBSA}
+flashboot: flashboot.elf
+ ${OBJCOPY} -S -O binary ${.TARGET}.elf ${.TARGET}
+flashboot.md5: flashboot
+ md5 flashboot > flashboot.md5
+
+jtagboot: start.o boot2.o altera_jtag_uart.o cfi.o sdcard.o
+ ${CC} ${LDFLAGS} -T ${.CURDIR}/jtagboot.ldscript -o ${.TARGET} \
+ ${.ALLSRC} ${LIBSA}
+jtagboot.md5: jtagboot
+ md5 jtagboot > jtagboot.md5
+
+CLEANFILES+= flashboot.elf
+
+.include <bsd.prog.mk>
diff --git a/stand/mips/beri/boot2/boot2.c b/stand/mips/beri/boot2/boot2.c
new file mode 100644
index 0000000..3db33c4
--- /dev/null
+++ b/stand/mips/beri/boot2/boot2.c
@@ -0,0 +1,661 @@
+/*-
+ * Copyright (c) 2013-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/diskmbr.h>
+#include <sys/dirent.h>
+#include <sys/endian.h>
+#include <sys/reboot.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+
+#include <stand.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <beri.h>
+#include <cfi.h>
+#include <cons.h>
+#include <mips.h>
+#include <sdcard.h>
+
+#include "paths.h"
+#include "rbx.h"
+
+static int beri_argc;
+static const char **beri_argv, **beri_envv;
+static uint64_t beri_memsize;
+
+#define IO_KEYBOARD 1
+#define IO_SERIAL 2
+
+#define SECOND 1 /* Circa that many ticks in a second. */
+
+#define ARGS 0x900
+#define NOPT 14
+#define MEM_BASE 0x12
+#define MEM_EXT 0x15
+
+/*
+ * XXXRW: I think this has to do with whether boot2 expects a partition
+ * table?
+ */
+#define DRV_HARD 0x80
+#define DRV_MASK 0x7f
+
+/* Default to using CFI flash. */
+#define TYPE_DEFAULT BOOTINFO_DEV_TYPE_SDCARD
+
+/* Hard-coded assumption about location of JTAG-loaded kernel. */
+#define DRAM_KERNEL_ADDR ((void *)mips_phys_to_cached(0x20000))
+
+extern uint32_t _end;
+
+static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
+static const unsigned char flags[NOPT] = {
+ RBX_DUAL,
+ RBX_SERIAL,
+ RBX_ASKNAME,
+ RBX_CDROM,
+ RBX_CONFIG,
+ RBX_KDB,
+ RBX_GDB,
+ RBX_MUTE,
+ RBX_NOINTR,
+ RBX_PAUSE,
+ RBX_QUIET,
+ RBX_DFLTROOT,
+ RBX_SINGLE,
+ RBX_VERBOSE
+};
+
+/* These must match BOOTINFO_DEV_TYPE constants. */
+static const char *const dev_nm[] = {"dram", "cfi", "sdcard"};
+static const u_int dev_nm_count = nitems(dev_nm);
+
+static struct dmadat __dmadat;
+
+static struct dsk {
+ unsigned type; /* BOOTINFO_DEV_TYPE_x object type. */
+ uintptr_t unitptr; /* Unit number or pointer to object. */
+ uint8_t slice;
+ uint8_t part;
+#if 0
+ unsigned start;
+ int init;
+#endif
+} dsk;
+static char cmd[512], cmddup[512], knamebuf[1024];
+static const char *kname;
+uint32_t opts;
+#if 0
+static int comspeed = SIOSPD;
+#endif
+struct bootinfo bootinfo;
+static uint8_t ioctrl = IO_KEYBOARD;
+
+void exit(int);
+void putchar(int);
+static void boot_fromdram(void);
+static void boot_fromfs(void);
+static void load(void);
+static int parse(void);
+static int dskread(void *, unsigned, unsigned);
+static int xputc(int);
+static int xgetc(int);
+
+
+#define UFS_SMALL_CGBASE
+#include "ufsread.c"
+
+static inline int
+xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
+{
+ if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
+ printf("Invalid %s\n", "format");
+ return -1;
+ }
+ return 0;
+}
+
+static inline void
+getstr(void)
+{
+ char *s;
+ int c;
+
+ s = cmd;
+ for (;;) {
+ switch (c = xgetc(0)) {
+ case 0:
+ break;
+ case '\177':
+ case '\b':
+ if (s > cmd) {
+ s--;
+ printf("\b \b");
+ }
+ break;
+ case '\n':
+ case '\r':
+ putchar('\n');
+ *s = 0;
+ return;
+ default:
+ if (s - cmd < sizeof(cmd) - 1)
+ *s++ = c;
+ putchar(c);
+ }
+ }
+}
+
+int
+main(u_int argc, const char *argv[], const char *envv[], uint64_t memsize)
+{
+ uint8_t autoboot;
+ ufs_ino_t ino;
+ size_t nbyte;
+
+ /* Arguments from Miniboot. */
+ beri_argc = argc;
+ beri_argv = argv;
+ beri_envv = envv;
+ beri_memsize = memsize;
+
+ dmadat = &__dmadat;
+#if 0
+ /* XXXRW: more here. */
+ v86.ctl = V86_FLAGS;
+ v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
+ dsk.drive = *(uint8_t *)PTOV(ARGS);
+#endif
+ dsk.type = TYPE_DEFAULT;
+#if 0
+ dsk.unit = dsk.drive & DRV_MASK;
+ dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
+#endif
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+
+ /* Process configuration file */
+
+ autoboot = 1;
+
+ if ((ino = lookup(PATH_CONFIG)) ||
+ (ino = lookup(PATH_DOTCONFIG))) {
+ nbyte = fsread(ino, cmd, sizeof(cmd) - 1);
+ cmd[nbyte] = '\0';
+ }
+
+ if (*cmd) {
+ memcpy(cmddup, cmd, sizeof(cmd));
+ if (parse())
+ autoboot = 0;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%s: %s", PATH_CONFIG, cmddup);
+ /* Do not process this command twice */
+ *cmd = 0;
+ }
+
+ /*
+ * Try to exec stage 3 boot loader. If interrupted by a keypress,
+ * or in case of failure, try to load a kernel directly instead.
+ */
+
+ if (!kname) {
+ kname = PATH_LOADER;
+ if (autoboot && !keyhit(3*SECOND)) {
+ boot_fromfs();
+ kname = PATH_KERNEL;
+ }
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ printf("\nFreeBSD/mips boot\n"
+ "Default: %s%ju:%s\n"
+ "boot: ",
+ dev_nm[dsk.type], dsk.unitptr, kname);
+#if 0
+ if (ioctrl & IO_SERIAL)
+ sio_flush();
+#endif
+ if (!autoboot || keyhit(3*SECOND))
+ getstr();
+ else if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ putchar('\n');
+ autoboot = 0;
+ if (parse())
+ putchar('\a');
+ else
+ load();
+ }
+}
+
+/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
+void
+exit(int x)
+{
+}
+
+static void
+boot(void *entryp, int argc, const char *argv[], const char *envv[])
+{
+
+ bootinfo.bi_kernelname = (bi_ptr_t)kname;
+ bootinfo.bi_boot2opts = opts & RBX_MASK;
+ bootinfo.bi_boot_dev_type = dsk.type;
+ bootinfo.bi_boot_dev_unitptr = dsk.unitptr;
+ bootinfo.bi_memsize = beri_memsize;
+#if 0
+ /*
+ * XXXRW: A possible future way to distinguish Miniboot passing a memory
+ * size vs DTB..?
+ */
+ if (beri_memsize <= BERI_MEMVSDTB)
+ bootinfo.bi_memsize = beri_memsize;
+ else
+ bootinfo.bi_dtb = beri_memsize;
+#endif
+ ((void(*)(int, const char **, const char **, void *))entryp)(argc, argv,
+ envv, &bootinfo);
+}
+
+/*
+ * Boot a kernel that has mysteriously (i.e., by JTAG) appeared in DRAM;
+ * assume that it is already properly relocated, etc, and invoke its entry
+ * address without question or concern.
+ */
+static void
+boot_fromdram(void)
+{
+ void *kaddr = DRAM_KERNEL_ADDR; /* XXXRW: Something better here. */
+ Elf64_Ehdr *ehp = kaddr;
+
+ if (!IS_ELF(*ehp)) {
+ printf("Invalid %s\n", "format");
+ return;
+ }
+ boot((void *)ehp->e_entry, beri_argc, beri_argv, beri_envv);
+}
+
+static void
+boot_fromfs(void)
+{
+ union {
+ Elf64_Ehdr eh;
+ } hdr;
+ static Elf64_Phdr ep[2];
+#if 0
+ static Elf64_Shdr es[2];
+#endif
+ caddr_t p;
+ ufs_ino_t ino;
+ uint64_t addr;
+ int i, j;
+
+ if (!(ino = lookup(kname))) {
+ if (!ls)
+ printf("No %s\n", kname);
+ return;
+ }
+ if (xfsread(ino, &hdr, sizeof(hdr)))
+ return;
+
+ if (IS_ELF(hdr.eh)) {
+ fs_off = hdr.eh.e_phoff;
+ for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
+ if (xfsread(ino, ep + j, sizeof(ep[0])))
+ return;
+ if (ep[j].p_type == PT_LOAD)
+ j++;
+ }
+ for (i = 0; i < 2; i++) {
+ p = (caddr_t)ep[i].p_paddr;
+ fs_off = ep[i].p_offset;
+ if (xfsread(ino, p, ep[i].p_filesz))
+ return;
+ }
+ p += roundup2(ep[1].p_memsz, PAGE_SIZE);
+#if 0
+ bootinfo.bi_symtab = VTOP(p);
+ if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
+ fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
+ (hdr.eh.e_shstrndx + 1);
+ if (xfsread(ino, &es, sizeof(es)))
+ return;
+ for (i = 0; i < 2; i++) {
+ *(Elf32_Word *)p = es[i].sh_size;
+ p += sizeof(es[i].sh_size);
+ fs_off = es[i].sh_offset;
+ if (xfsread(ino, p, es[i].sh_size))
+ return;
+ p += es[i].sh_size;
+ }
+ }
+#endif
+ addr = hdr.eh.e_entry;
+#if 0
+ bootinfo.bi_esymtab = VTOP(p);
+#endif
+ } else {
+ printf("Invalid %s\n", "format");
+ return;
+ }
+ boot((void *)addr, beri_argc, beri_argv, beri_envv);
+}
+
+static void
+load(void)
+{
+
+ switch (dsk.type) {
+ case BOOTINFO_DEV_TYPE_DRAM:
+ boot_fromdram();
+ break;
+
+ default:
+ boot_fromfs();
+ break;
+ }
+}
+
+static int
+parse()
+{
+ char *arg = cmd;
+ char *ep, *p, *q;
+ char unit;
+ size_t len;
+ const char *cp;
+#if 0
+ int c, i, j;
+#else
+ int c, i;
+#endif
+
+ while ((c = *arg++)) {
+ if (c == ' ' || c == '\t' || c == '\n')
+ continue;
+ for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+ ep = p;
+ if (*p)
+ *p++ = 0;
+ if (c == '-') {
+ while ((c = *arg++)) {
+ if (c == 'P') {
+ cp = "yes";
+#if 0
+ } else {
+ opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
+ cp = "no";
+ }
+#endif
+ printf("Keyboard: %s\n", cp);
+ continue;
+#if 0
+ } else if (c == 'S') {
+ j = 0;
+ while ((unsigned int)(i = *arg++ - '0') <= 9)
+ j = j * 10 + i;
+ if (j > 0 && i == -'0') {
+ comspeed = j;
+ break;
+ }
+ /* Fall through to error below ('S' not in optstr[]). */
+#endif
+ }
+ for (i = 0; c != optstr[i]; i++)
+ if (i == NOPT - 1)
+ return -1;
+ opts ^= OPT_SET(flags[i]);
+ }
+ ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
+ OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
+#if 0
+ if (ioctrl & IO_SERIAL) {
+ if (sio_init(115200 / comspeed) != 0)
+ ioctrl &= ~IO_SERIAL;
+ }
+#endif
+ } else {
+ /*-
+ * Parse a device/kernel name. Format(s):
+ *
+ * path
+ * deviceX:path
+ *
+ * NB: Utterly incomprehensible but space-efficient ARM/i386
+ * parsing removed in favour of larger but easier-to-read C. This
+ * is still not great, however -- e.g., relating to unit handling.
+ *
+ * TODO: it would be nice if a DRAM pointer could be specified
+ * here.
+ *
+ * XXXRW: Pick up pieces here.
+ */
+
+ /*
+ * Search for a parens; if none, then it's just a path.
+ * Otherwise, it's a devicename.
+ */
+ arg--;
+ q = strsep(&arg, ":");
+ if (arg != NULL) {
+ len = strlen(q);
+ if (len < 2) {
+ printf("Invalid device: name too short\n");
+ return (-1);
+ }
+
+ /*
+ * First, handle one-digit unit.
+ */
+ unit = q[len-1];
+ if (unit < '0' || unit > '9') {
+ printf("Invalid device: invalid unit\n", q,
+ unit);
+ return (-1);
+ }
+ unit -= '0';
+ q[len-1] = '\0';
+
+ /*
+ * Next, find matching device.
+ */
+ for (i = 0; i < dev_nm_count; i++) {
+ if (strcmp(q, dev_nm[i]) == 0)
+ break;
+ }
+ if (i == dev_nm_count) {
+ printf("Invalid device: no driver match\n");
+ return (-1);
+ }
+ dsk.type = i;
+ dsk.unitptr = unit; /* Someday: also a DRAM pointer? */
+ } else
+ arg = q;
+ if ((i = ep - arg)) {
+ if ((size_t)i >= sizeof(knamebuf))
+ return -1;
+ memcpy(knamebuf, arg, i + 1);
+ kname = knamebuf;
+ }
+ }
+ arg = p;
+ }
+ return 0;
+}
+
+static int
+drvread(void *buf, unsigned lba, unsigned nblk)
+{
+
+ /* XXXRW: eventually, we may want to pass 'drive' and 'unit' here. */
+ switch (dsk.type) {
+ case BOOTINFO_DEV_TYPE_CFI:
+ return (cfi_read(buf, lba, nblk));
+
+ case BOOTINFO_DEV_TYPE_SDCARD:
+ return (altera_sdcard_read(buf, lba, nblk));
+
+ default:
+ return (-1);
+ }
+}
+
+static int
+dskread(void *buf, unsigned lba, unsigned nblk)
+{
+#if 0
+ /*
+ * XXXRW: For now, assume no partition table around the file system; it's
+ * just in raw flash.
+ */
+ struct dos_partition *dp;
+ struct disklabel *d;
+ char *sec;
+ unsigned i;
+ uint8_t sl;
+
+ if (!dsk_meta) {
+ sec = dmadat->secbuf;
+ dsk.start = 0;
+ if (drvread(sec, DOSBBSECTOR, 1))
+ return -1;
+ dp = (void *)(sec + DOSPARTOFF);
+ sl = dsk.slice;
+ if (sl < BASE_SLICE) {
+ for (i = 0; i < NDOSPART; i++)
+ if (dp[i].dp_typ == DOSPTYP_386BSD &&
+ (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
+ sl = BASE_SLICE + i;
+ if (dp[i].dp_flag & 0x80 ||
+ dsk.slice == COMPATIBILITY_SLICE)
+ break;
+ }
+ if (dsk.slice == WHOLE_DISK_SLICE)
+ dsk.slice = sl;
+ }
+ if (sl != WHOLE_DISK_SLICE) {
+ if (sl != COMPATIBILITY_SLICE)
+ dp += sl - BASE_SLICE;
+ if (dp->dp_typ != DOSPTYP_386BSD) {
+ printf("Invalid %s\n", "slice");
+ return -1;
+ }
+ dsk.start = le32toh(dp->dp_start);
+ }
+ if (drvread(sec, dsk.start + LABELSECTOR, 1))
+ return -1;
+ d = (void *)(sec + LABELOFFSET);
+ if (le32toh(d->d_magic) != DISKMAGIC ||
+ le32toh(d->d_magic2) != DISKMAGIC) {
+ if (dsk.part != RAW_PART) {
+ printf("Invalid %s\n", "label");
+ return -1;
+ }
+ } else {
+ if (!dsk.init) {
+ if (le16toh(d->d_type) == DTYPE_SCSI)
+ dsk.type = TYPE_DA;
+ dsk.init++;
+ }
+ if (dsk.part >= le16toh(d->d_npartitions) ||
+ !(le32toh(d->d_partitions[dsk.part].p_size))) {
+ printf("Invalid %s\n", "partition");
+ return -1;
+ }
+ dsk.start += le32toh(d->d_partitions[dsk.part].p_offset);
+ dsk.start -= le32toh(d->d_partitions[RAW_PART].p_offset);
+ }
+ }
+ return drvread(buf, dsk.start + lba, nblk);
+#else
+ return drvread(buf, lba, nblk);
+#endif
+}
+
+void
+putchar(int c)
+{
+ if (c == '\n')
+ xputc('\r');
+ xputc(c);
+}
+
+static int
+xputc(int c)
+{
+ if (ioctrl & IO_KEYBOARD)
+ putc(c);
+#if 0
+ if (ioctrl & IO_SERIAL)
+ sio_putc(c);
+#endif
+ return c;
+}
+
+static int
+xgetc(int fn)
+{
+ if (OPT_CHECK(RBX_NOINTR))
+ return 0;
+ for (;;) {
+ if (ioctrl & IO_KEYBOARD && keyhit(0))
+ return fn ? 1 : getc();
+#if 0
+ if (ioctrl & IO_SERIAL && sio_ischar())
+ return fn ? 1 : sio_getc();
+#endif
+ if (fn)
+ return 0;
+ }
+}
diff --git a/stand/mips/beri/boot2/flashboot.ldscript b/stand/mips/beri/boot2/flashboot.ldscript
new file mode 100644
index 0000000..4d61438
--- /dev/null
+++ b/stand/mips/beri/boot2/flashboot.ldscript
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2011-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+INCLUDE ../common/common.ldscript
+
+/*
+ * When boot2 is loaded via JTAG, it's dropped at 0x10000, and will not need
+ * to self-relocate, since it will be in DRAM.
+ */
+__boot2_base__ = 0x100000;
+__boot2_base_vaddr__ = __mips64_xkphys_cached__ + __boot2_base__;
+
+/*
+ * XXXRW: Currently, miniboot interprets the ELF header rather than jumping
+ * straight into the loader. For now, give the location where we know it will
+ * be.
+ */
+ENTRY(prerelocate_start)
+SECTIONS
+{
+ . = __boot2_base_vaddr__;
+ . += SIZEOF_HEADERS;
+ .text ALIGN(0x10): {
+ relocate.o(.text)
+ start.o(.text)
+ *(EXCLUDE_FILE (relocate.o start.o) .text)
+ }
+ .data ALIGN(0x10): { *(.data)}
+ .bss ALIGN(0x10): { *(.bss) }
+
+ __heap = ALIGN(0x8); /* 64-bit aligned heap pointer */
+ __data_end = .;
+ __boot_loader_len__ = . - __boot2_base_vaddr__;
+ __bss_start = ADDR(.bss);
+ __bss_end = ALIGN(__bss_start + SIZEOF(.bss), 0x8);
+}
diff --git a/stand/mips/beri/boot2/jtagboot.ldscript b/stand/mips/beri/boot2/jtagboot.ldscript
new file mode 100644
index 0000000..064c6e1
--- /dev/null
+++ b/stand/mips/beri/boot2/jtagboot.ldscript
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2011-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+INCLUDE ../common/common.ldscript
+
+/*
+ * When boot2 is loaded via JTAG, it's dropped at 0x10000, and will not need
+ * to self-relocate, since it will be in DRAM.
+ */
+__boot2_base__ = 0x100000;
+__boot2_base_vaddr__ = __mips64_xkphys_cached__ + __boot2_base__;
+
+/*
+ * XXXRW: Currently, miniboot interprets the ELF header rather than jumping
+ * straight into the loader. For now, give the location where we know it will
+ * be.
+ */
+ENTRY(start)
+SECTIONS
+{
+ . = __boot2_base_vaddr__;
+ . += SIZEOF_HEADERS;
+ .text ALIGN(0x10): {
+ start.o(.text)
+ *(EXCLUDE_FILE (start.o) .text)
+ }
+ .data ALIGN(0x10): { *(.data)}
+ .bss ALIGN(0x10): { *(.bss) }
+
+ __heap = ALIGN(0x8); /* 64-bit aligned heap pointer */
+ __data_end = .;
+ __boot_loader_len__ = . - __boot2_base_vaddr__;
+ __bss_start = ADDR(.bss);
+ __bss_end = ALIGN(__bss_start + SIZEOF(.bss), 0x8);
+}
diff --git a/stand/mips/beri/boot2/relocate.S b/stand/mips/beri/boot2/relocate.S
new file mode 100644
index 0000000..d704eb5
--- /dev/null
+++ b/stand/mips/beri/boot2/relocate.S
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 2013-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+.set mips64
+.set noreorder
+.set nobopt
+.set noat
+
+/*
+ * Save arguments from the BERI firmware for use in C-land, and jump into
+ * main. Assume that registers/stack/etc are sufficiently initialised to get
+ * going. Notice that we use only temporaries while relocating, as we want to
+ * retain argument registers to pass in to main().
+ *
+ * Note slightly surprising structure: boot2 is linked for a specific address,
+ * but we may start running the code somewhere else (e.g., in DRAM as inserted
+ * with JTAG, or in flash). The starting assembly is therefore PIC, but the
+ * main body of the code is not PIC.
+ */
+
+
+ .text
+ .global prerelocate_start
+ .ent prerelocate_start
+prerelocate_start:
+
+ /*
+ * Calculate the actual run-time, pre-relocated value of
+ * 'start', which we will use as the source address for
+ * memcpy(). Note that although a symbol is used here, this
+ * should generate code for a short relative branch, leaving
+ * the previous $pc in $ra.
+ */
+ bal baltarget
+ nop
+baltarget:
+ dsub $t1, $ra, 8 /* Src. */
+
+ /*
+ * Relocate boot2 to DRAM where we can write back global
+ * variable values; jump to it. Assume all values are 32-bit
+ * aligned. Use an inline PIC version of memcpy()
+ * pre-relocation; strong alignment assumptions.
+ */
+ dla $t0, __boot2_base_vaddr__ /* Dst. */
+ dla $t2, __boot_loader_len__ /* Len. */
+
+memcpy_loop:
+ beq $t2, 0, memcopy_done
+ nop
+ lw $at, 0($t1)
+ sw $at, 0($t0)
+ daddiu $t0, 4
+ daddiu $t1, 4
+ daddi $t2, -4
+ b memcpy_loop
+ nop
+
+memcopy_done:
+ /*
+ * We can now jump into the relocated code, running from
+ * cached DRAM rather than uncached flash. Note that a
+ * relative branch instruction cannot be used.
+ */
+ dla $at, relocated_start
+ jr $at
+ nop
+
+relocated_start:
+ dla $at, start
+ jr $at
+ nop
+
+ .end prerelocate_start
diff --git a/stand/mips/beri/boot2/start.S b/stand/mips/beri/boot2/start.S
new file mode 100644
index 0000000..c1fcce5
--- /dev/null
+++ b/stand/mips/beri/boot2/start.S
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 2013-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+.set mips64
+.set noreorder
+.set nobopt
+.set noat
+
+/*
+ * Save arguments from the BERI firmware for use in C-land, and jump into
+ * main. Assume that registers/stack/etc are sufficiently initialised to get
+ * going. Notice that we use only temporaries while relocating, as we want to
+ * retain argument registers to pass in to main().
+ *
+ * Note slightly surprising structure: boot2 is linked for a specific address,
+ * but we may start running the code somewhere else (e.g., in DRAM as inserted
+ * with JTAG, or in flash). The starting assembly is therefore PIC, but the
+ * main body of the code is not PIC.
+ */
+
+
+ .text
+ .global start
+ .ent start
+start:
+
+ /*
+ * Zero BSS. Run from cached memory as this will speed up
+ * code execution noticeably. Assuming 64-bit alignment of
+ * everything here.
+ */
+ dla $t0, __bss_start
+ dla $t1, __bss_end
+
+bss_loop:
+ beq $t0, $t1, bss_done
+ nop
+ sd $zero, 0($t0)
+ daddiu $t0, 8
+ b bss_loop
+ nop
+
+bss_done:
+ jal main
+ nop
+
+ /*
+ * Ideally we wouldn't get here, but just in case.
+ */
+loop:
+ b loop
+ nop
+ .end start
diff --git a/stand/mips/beri/common/altera_jtag_uart.c b/stand/mips/beri/common/altera_jtag_uart.c
new file mode 100644
index 0000000..7edb451
--- /dev/null
+++ b/stand/mips/beri/common/altera_jtag_uart.c
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 2011, 2013 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "util.h"
+#include "mips.h"
+
+/*-
+ * Routines for interacting with the CHERI console UART. Programming details
+ * from the June 2011 "Embedded Peripherals User Guide" by Altera
+ * Corporation, tables 6-2 (JTAG UART Core Register Map), 6-3 (Data Register
+ * Bits), and 6-4 (Control Register Bits).
+ *
+ * Hard-coded physical address for the first JTAG UART -- true on all BERI and
+ * CHERI boards.
+ */
+#define CHERI_UART_BASE 0x7f000000 /* JTAG UART */
+
+/*
+ *
+ * Offsets of data and control registers relative to the base. Altera
+ * conventions are maintained in CHERI.
+ */
+#define ALTERA_JTAG_UART_DATA_OFF 0x00000000
+#define ALTERA_JTAG_UART_CONTROL_OFF 0x00000004
+
+/*
+ * Offset 0: 'data' register -- bits 31-16 (RAVAIL), 15 (RVALID),
+ * 14-8 (Reserved), 7-0 (DATA).
+ *
+ * DATA - One byte read or written.
+ * RAVAIL - Bytes available to read (excluding the current byte).
+ * RVALID - Whether the byte in DATA is valid.
+ */
+#define ALTERA_JTAG_UART_DATA_DATA 0x000000ff
+#define ALTERA_JTAG_UART_DATA_RESERVED 0x00007f00
+#define ALTERA_JTAG_UART_DATA_RVALID 0x00008000
+#define ALTERA_JTAG_UART_DATA_RAVAIL 0xffff0000
+#define ALTERA_JTAG_UART_DATA_RAVAIL_SHIFT 16
+
+/*-
+ * Offset 1: 'control' register -- bits 31-16 (WSPACE), 15-11 (Reserved),
+ * 10 (AC), 9 (WI), 8 (RI), 7..2 (Reserved), 1 (WE), 0 (RE).
+ *
+ * RE - Enable read interrupts.
+ * WE - Enable write interrupts.
+ * RI - Read interrupt pending.
+ * WI - Write interrupt pending.
+ * AC - Activity bit; set to '1' to clear to '0'.
+ * WSPACE - Space available in the write FIFO.
+ */
+#define ALTERA_JTAG_UART_CONTROL_RE 0x00000001
+#define ALTERA_JTAG_UART_CONTROL_WE 0x00000002
+#define ALTERA_JTAG_UART_CONTROL_RESERVED0 0x000000fc
+#define ALTERA_JTAG_UART_CONTROL_RI 0x00000100
+#define ALTERA_JTAG_UART_CONTROL_WI 0x00000200
+#define ALTERA_JTAG_UART_CONTROL_AC 0x00000400
+#define ALTERA_JTAG_UART_CONTROL_RESERVED1 0x0000f800
+#define ALTERA_JTAG_UART_CONTROL_WSPACE 0xffff0000
+#define ALTERA_JTAG_UART_CONTROL_WSPACE_SHIFT 16
+
+/*
+ * One-byte buffer as we can't check whether the UART is readable without
+ * actually reading from it.
+ */
+static char buffer_data;
+static int buffer_valid;
+
+/*
+ * Low-level read and write register routines; the Altera UART is little
+ * endian, so we byte swap 32-bit reads and writes.
+ */
+static inline uint32_t
+uart_data_read(void)
+{
+
+ return (mips_ioread_uint32le(mips_phys_to_uncached(CHERI_UART_BASE +
+ ALTERA_JTAG_UART_DATA_OFF)));
+}
+
+static inline void
+uart_data_write(uint32_t v)
+{
+
+ mips_iowrite_uint32le(mips_phys_to_uncached(CHERI_UART_BASE +
+ ALTERA_JTAG_UART_DATA_OFF), v);
+}
+
+static inline uint32_t
+uart_control_read(void)
+{
+
+ return (mips_ioread_uint32le(mips_phys_to_uncached(CHERI_UART_BASE +
+ ALTERA_JTAG_UART_CONTROL_OFF)));
+}
+
+static inline void
+uart_control_write(uint32_t v)
+{
+
+ mips_iowrite_uint32le(mips_phys_to_uncached(CHERI_UART_BASE +
+ ALTERA_JTAG_UART_DATA_OFF), v);
+}
+
+static int
+uart_writable(void)
+{
+
+ return ((uart_control_read() & ALTERA_JTAG_UART_CONTROL_WSPACE) != 0);
+}
+
+static int
+uart_readable(void)
+{
+ uint32_t v;
+
+ if (buffer_valid)
+ return (1);
+ v = uart_data_read();
+ if ((v & ALTERA_JTAG_UART_DATA_RVALID) != 0) {
+ buffer_valid = 1;
+ buffer_data = (v & ALTERA_JTAG_UART_DATA_DATA);
+ }
+ return (0);
+}
+
+int
+keyhit(int seconds)
+{
+ register_t stoptime;
+
+ stoptime = cp0_count_get() + seconds * 100000000; /* 100 MHz. */
+ do {
+ if (uart_readable())
+ return (1);
+ } while (cp0_count_get() < stoptime);
+ return (0);
+}
+
+int
+getc(void)
+{
+
+ while (!(uart_readable()));
+ buffer_valid = 0;
+ return (buffer_data);
+}
+
+void
+putc(int ch)
+{
+
+ uart_data_write(ch);
+}
diff --git a/stand/mips/beri/common/beri.h b/stand/mips/beri/common/beri.h
new file mode 100644
index 0000000..e6ccae8
--- /dev/null
+++ b/stand/mips/beri/common/beri.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2013-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOT_BERI_H_
+#define _BOOT_BERI_H_
+
+/*
+ * Older BERI boot loaders pass in physical memory size as $a3; newer ones
+ * pass in FDT DTB data. This constant helps us tell the difference.
+ */
+#define BERI_MEMVSDTB (256*1024*1024*1024ULL)
+
+#endif
diff --git a/stand/mips/beri/common/cfi.c b/stand/mips/beri/common/cfi.c
new file mode 100644
index 0000000..a6798f4
--- /dev/null
+++ b/stand/mips/beri/common/cfi.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 2013 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "util.h"
+#include "mips.h"
+#include "cfi.h"
+
+/*
+ * Memory-mapped Intel StrataFlash mini-driver. Very mini. Nothing fancy --
+ * and few seatbelts.
+ *
+ * XXXRW: Should we be making some effort to reset isf to a known-good state
+ * before starting, in case there was a soft reset mid-transaction.
+ *
+ * XXXRW: Would be nice to support multiple devices and also handle SD cards
+ * here ... and probably not too hard.
+ */
+extern void *__cheri_flash_bootfs_vaddr__;
+extern void *__cheri_flash_bootfs_len__;
+
+#define CHERI_BOOTFS_BASE ((uintptr_t)&__cheri_flash_bootfs_vaddr__)
+#define CHERI_BOOTFS_LENGTH ((uintptr_t)&__cheri_flash_bootfs_len__)
+
+int
+cfi_read(void *buf, unsigned lba, unsigned nblk)
+{
+
+ if ((lba << 9) + (nblk << 9) > CHERI_BOOTFS_LENGTH)
+ return (-1);
+ memcpy(buf, (void *)(CHERI_BOOTFS_BASE + (lba << 9)), nblk << 9);
+ return (0);
+}
+
+uint64_t
+cfi_get_mediasize(void)
+{
+
+ return (CHERI_BOOTFS_LENGTH);
+}
+
+uint64_t
+cfi_get_sectorsize(void)
+{
+
+ return (512); /* Always a good sector size. */
+}
diff --git a/stand/mips/beri/common/cfi.h b/stand/mips/beri/common/cfi.h
new file mode 100644
index 0000000..3dfe48c
--- /dev/null
+++ b/stand/mips/beri/common/cfi.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2013 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CFI_H_
+#define _CFI_H_
+
+int cfi_read(void *buf, unsigned lba, unsigned blk);
+uint64_t cfi_get_mediasize(void);
+uint64_t cfi_get_sectorsize(void);
+
+#endif
diff --git a/stand/mips/beri/common/common.ldscript b/stand/mips/beri/common/common.ldscript
new file mode 100644
index 0000000..6266646
--- /dev/null
+++ b/stand/mips/beri/common/common.ldscript
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 2011-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * MIPS segment definitions.
+ */
+__mips_ckseg_cached__ = 0xffffffff80000000; /* BSD kernel here. */
+__mips64_xkphys_cached__ = 0x9800000000000000; /* Device memory here. */
+__mips64_xkphys_uncached__ = 0x9000000000000000; /* Device I/O here. */
+
+/*
+ * Physical addresses of various peripherals.
+ */
+__cheri_flash_base__ = 0x74000000;
+__cheri_sdcard_base__ = 0x7f008000;
+
+/*
+ * Location of boot2 in flash.
+ */
+__cheri_flash_boot_loader_base_ = 0x03fe0000;
+__cheri_flash_boot_loader_vaddr__ = __mips64_xkphys_cached__ +
+ __cheri_flash_base__ + __cheri_flash_boot_loader_base_;
+
+/*
+ * Location of boot file system in flash.
+ */
+__cheri_flash_bootfs_base__ = 0x1820000;
+__cheri_flash_bootfs_len__ = 0x27c0000;
+__cheri_flash_bootfs_vaddr__ = __mips64_xkphys_cached__ +
+ __cheri_flash_base__ + __cheri_flash_bootfs_base__;
+
+/*
+ * Location of SD card controller.
+ */
+__cheri_sdcard_vaddr__ = __mips64_xkphys_uncached__ + __cheri_sdcard_base__;
+
+/*
+ * Location where the production kernel gets put. This must agree with other
+ * definitions, such as in the kernel's own linker script.
+ *
+ * (As it happens, in the short run, we also place boot2 here, as Miniboot
+ * expects to find an ELF binary there -- but that will change.)
+ */
+__kernel_base__ = 0x100000;
+__kernel_vaddr__ = __mips64_xkphys_cached__ + __kernel_base__;
+
+OUTPUT_ARCH(mips)
diff --git a/stand/mips/beri/common/cons.h b/stand/mips/beri/common/cons.h
new file mode 100644
index 0000000..18a466a
--- /dev/null
+++ b/stand/mips/beri/common/cons.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2013 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CONS_H_
+#define _CONS_H_
+
+int getc(void);
+int keyhit(int);
+void putc(int);
+
+#endif
diff --git a/stand/mips/beri/common/mips.h b/stand/mips/beri/common/mips.h
new file mode 100644
index 0000000..c63d475
--- /dev/null
+++ b/stand/mips/beri/common/mips.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2011 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MIPS_H_
+#define _MIPS_H_
+
+/*
+ * 64-bit MIPS types.
+ */
+#if 0
+typedef unsigned long register_t; /* 64-bit MIPS register */
+#endif
+typedef unsigned long paddr_t; /* Physical address */
+typedef unsigned long vaddr_t; /* Virtual address */
+
+#if 0
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long uint64_t;
+#endif
+
+/*
+ * MIPS address space layout.
+ */
+#define MIPS_XKPHYS_UNCACHED_BASE 0x9000000000000000
+#define MIPS_XKPHYS_CACHED_NC_BASE 0x9800000000000000
+
+static inline vaddr_t
+mips_phys_to_cached(paddr_t phys)
+{
+
+ return (phys | MIPS_XKPHYS_CACHED_NC_BASE);
+}
+
+static inline vaddr_t
+mips_phys_to_uncached(paddr_t phys)
+{
+
+ return (phys | MIPS_XKPHYS_UNCACHED_BASE);
+}
+
+/*
+ * Endian conversion routines for use in I/O -- most Altera devices are little
+ * endian, but our processor is big endian.
+ */
+static inline uint16_t
+byteswap16(uint16_t v)
+{
+
+ return ((v & 0xff00) >> 8 | (v & 0xff) << 8);
+}
+
+static inline uint32_t
+byteswap32(uint32_t v)
+{
+
+ return ((v & 0xff000000) >> 24 | (v & 0x00ff0000) >> 8 |
+ (v & 0x0000ff00) << 8 | (v & 0x000000ff) << 24);
+}
+
+/*
+ * MIPS simple I/O routines -- arguments are virtual addresses so that the
+ * caller can determine required caching properties.
+ */
+static inline uint8_t
+mips_ioread_uint8(vaddr_t vaddr)
+{
+ uint8_t v;
+
+ __asm__ __volatile__ ("lb %0, 0(%1)" : "=r" (v) : "r" (vaddr));
+ return (v);
+}
+
+static inline void
+mips_iowrite_uint8(vaddr_t vaddr, uint8_t v)
+{
+
+ __asm__ __volatile__ ("sb %0, 0(%1)" : : "r" (v), "r" (vaddr));
+}
+
+static inline uint32_t
+mips_ioread_uint32(vaddr_t vaddr)
+{
+ uint32_t v;
+
+ __asm__ __volatile__ ("lw %0, 0(%1)" : "=r" (v) : "r" (vaddr));
+ return (v);
+}
+
+static inline void
+mips_iowrite_uint32(vaddr_t vaddr, uint32_t v)
+{
+
+ __asm__ __volatile__ ("sw %0, 0(%1)" : : "r" (v), "r" (vaddr));
+}
+
+/*
+ * Little-endian versions of 32-bit I/O routines.
+ */
+static inline uint32_t
+mips_ioread_uint32le(vaddr_t vaddr)
+{
+
+ return (byteswap32(mips_ioread_uint32(vaddr)));
+}
+
+static inline void
+mips_iowrite_uint32le(vaddr_t vaddr, uint32_t v)
+{
+
+ mips_iowrite_uint32(vaddr, byteswap32(v));
+}
+
+/*
+ * Coprocessor 0 interfaces.
+ */
+static inline register_t
+cp0_count_get(void)
+{
+ register_t count;
+
+ __asm__ __volatile__ ("dmfc0 %0, $9" : "=r" (count));
+ return (count);
+}
+
+#endif
diff --git a/stand/mips/beri/common/sdcard.c b/stand/mips/beri/common/sdcard.c
new file mode 100644
index 0000000..e50f4e7
--- /dev/null
+++ b/stand/mips/beri/common/sdcard.c
@@ -0,0 +1,334 @@
+/*-
+ * Copyright (c) 2012-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+
+
+/*
+ * Altera University Program SD Card micro-driver for boot2 and loader.
+ *
+ * XXXRW: It might be nice to add 'unit' arguments to all APIs to allow
+ * multiple instances to be addressed.
+ */
+
+/* Constants lifted from altera_sdcard.h -- possibly we should share headers? */
+#define ALTERA_SDCARD_OFF_RXTX_BUFFER 0 /* 512-byte I/O buffer */
+#define ALTERA_SDCARD_OFF_CID 512 /* 16-byte Card ID number */
+#define ALTERA_SDCARD_OFF_CSD 528 /* 16-byte Card Specific Data */
+#define ALTERA_SDCARD_OFF_OCR 544 /* Operating Conditions Reg */
+#define ALTERA_SDCARD_OFF_SR 548 /* SD Card Status Register */
+#define ALTERA_SDCARD_OFF_RCA 552 /* Relative Card Address Reg */
+#define ALTERA_SDCARD_OFF_CMD_ARG 556 /* Command Argument Register */
+#define ALTERA_SDCARD_OFF_CMD 560 /* Command Register */
+#define ALTERA_SDCARD_OFF_ASR 564 /* Auxiliary Status Register */
+#define ALTERA_SDCARD_OFF_RR1 568 /* Response R1 */
+
+#define ALTERA_SDCARD_SECTORSIZE 512
+
+#define ALTERA_SDCARD_CMD_SEND_RCA 0x03 /* Retrieve card RCA. */
+#define ALTERA_SDCARD_CMD_SEND_CSD 0x09 /* Retrieve CSD register. */
+#define ALTERA_SDCARD_CMD_SEND_CID 0x0A /* Retrieve CID register. */
+#define ALTERA_SDCARD_CMD_READ_BLOCK 0x11 /* Read block from disk. */
+#define ALTERA_SDCARD_CMD_WRITE_BLOCK 0x18 /* Write block to disk. */
+
+#define ALTERA_SDCARD_ASR_CMDVALID 0x0001
+#define ALTERA_SDCARD_ASR_CARDPRESENT 0x0002
+#define ALTERA_SDCARD_ASR_CMDINPROGRESS 0x0004
+#define ALTERA_SDCARD_ASR_SRVALID 0x0008
+#define ALTERA_SDCARD_ASR_CMDTIMEOUT 0x0010
+#define ALTERA_SDCARD_ASR_CMDDATAERROR 0x0020
+
+#define ALTERA_SDCARD_RR1_INITPROCRUNNING 0x0100
+#define ALTERA_SDCARD_RR1_ERASEINTERRUPTED 0x0200
+#define ALTERA_SDCARD_RR1_ILLEGALCOMMAND 0x0400
+#define ALTERA_SDCARD_RR1_COMMANDCRCFAILED 0x0800
+#define ALTERA_SDCARD_RR1_ADDRESSMISALIGNED 0x1000
+#define ALTERA_SDCARD_RR1_ADDRBLOCKRANGE 0x2000
+
+#define ALTERA_SDCARD_CSD_STRUCTURE_BYTE 15
+#define ALTERA_SDCARD_CSD_STRUCTURE_MASK 0xc0 /* 2 bits */
+#define ALTERA_SDCARD_CSD_STRUCTURE_RSHIFT 6
+#define ALTERA_SDCARD_CSD_SIZE 16
+#define ALTERA_SDCARD_CSD_READ_BL_LEN_BYTE 10
+#define ALTERA_SDCARD_CSD_READ_BL_LEN_MASK 0x0f /* 4 bits */
+#define ALTERA_SDCARD_CSD_C_SIZE_BYTE0 7
+#define ALTERA_SDCARD_CSD_C_SIZE_MASK0 0xc0 /* top 2 bits */
+#define ALTERA_SDCARD_CSD_C_SIZE_RSHIFT0 6
+#define ALTERA_SDCARD_CSD_C_SIZE_BYTE1 8
+#define ALTERA_SDCARD_CSD_C_SIZE_MASK1 0xff /* 8 bits */
+#define ALTERA_SDCARD_CSD_C_SIZE_LSHIFT1 2
+#define ALTERA_SDCARD_CSD_C_SIZE_BYTE2 9
+#define ALTERA_SDCARD_CSD_C_SIZE_MASK2 0x03 /* bottom 2 bits */
+#define ALTERA_SDCARD_CSD_C_SIZE_LSHIFT2 10
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE0 5
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK0 0x80 /* top 1 bit */
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_RSHIFT0 7
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE1 6
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK1 0x03 /* bottom 2 bits */
+#define ALTERA_SDCARD_CSD_C_SIZE_MULT_LSHIFT1 1
+
+/*
+ * Not all RR1 values are "errors" per se -- check only for the ones that are
+ * when performing error handling.
+ */
+#define ALTERA_SDCARD_RR1_ERRORMASK \
+ (ALTERA_SDCARD_RR1_ERASEINTERRUPTED | ALTERA_SDCARD_RR1_ILLEGALCOMMAND | \
+ ALTERA_SDCARD_RR1_COMMANDCRCFAILED | ALTERA_SDCARD_RR1_ADDRESSMISALIGNED |\
+ ALTERA_SDCARD_RR1_ADDRBLOCKRANGE)
+
+extern void __cheri_sdcard_vaddr__;
+
+#define ALTERA_SDCARD_PTR(type, offset) \
+ (volatile type *)((uint8_t *)&__cheri_sdcard_vaddr__ + (offset))
+
+static __inline uint16_t
+altera_sdcard_read_uint16(u_int offset)
+{
+ volatile uint16_t *p;
+
+ p = ALTERA_SDCARD_PTR(uint16_t, offset);
+ return (le16toh(*p));
+}
+
+static __inline void
+altera_sdcard_write_uint16(u_int offset, uint16_t v)
+{
+ volatile uint16_t *p;
+
+ p = ALTERA_SDCARD_PTR(uint16_t, offset);
+ *p = htole16(v);
+}
+
+static __inline void
+altera_sdcard_write_uint32(u_int offset, uint32_t v)
+{
+ volatile uint32_t *p;
+
+ p = ALTERA_SDCARD_PTR(uint32_t, offset);
+ *p = htole32(v);
+}
+
+static __inline uint16_t
+altera_sdcard_read_asr(void)
+{
+
+ return (altera_sdcard_read_uint16(ALTERA_SDCARD_OFF_ASR));
+}
+
+static __inline uint16_t
+altera_sdcard_read_rr1(void)
+{
+
+ return (altera_sdcard_read_uint16(ALTERA_SDCARD_OFF_RR1));
+}
+
+static __inline void
+altera_sdcard_write_cmd(uint16_t cmd)
+{
+
+ altera_sdcard_write_uint16(ALTERA_SDCARD_OFF_CMD, cmd);
+}
+
+static __inline void
+altera_sdcard_write_cmd_arg(uint32_t cmd_arg)
+{
+
+ altera_sdcard_write_uint32(ALTERA_SDCARD_OFF_CMD_ARG, cmd_arg);
+}
+
+/* NB: Use 16-bit aligned buffer due to hardware features, so 16-bit type. */
+static __inline void
+altera_sdcard_read_csd(uint16_t *csdp)
+{
+ volatile uint16_t *hw_csdp;
+ u_int i;
+
+ hw_csdp = ALTERA_SDCARD_PTR(uint16_t, ALTERA_SDCARD_OFF_CSD);
+ for (i = 0; i < ALTERA_SDCARD_CSD_SIZE / sizeof(uint16_t); i++)
+ csdp[i] = hw_csdp[i];
+}
+
+/*
+ * Private interface: load exactly one block of size ALTERA_SDCARD_SECTORSIZE
+ * from block #lba.
+ */
+static int
+altera_sdcard_read_block(void *buf, unsigned lba)
+{
+ volatile uint32_t *rxtxp;
+ uint32_t *bufp;
+ uint16_t asr, rr1;
+ int i;
+
+ if (!(altera_sdcard_read_asr() & ALTERA_SDCARD_ASR_CARDPRESENT)) {
+ printf("SD Card: card not present\n");
+ return (-1);
+ }
+
+ bufp = (uint32_t *)buf;
+ rxtxp = ALTERA_SDCARD_PTR(uint32_t, ALTERA_SDCARD_OFF_RXTX_BUFFER);
+
+ /*
+ * Issue read block command.
+ */
+ altera_sdcard_write_cmd_arg(lba * ALTERA_SDCARD_SECTORSIZE);
+ altera_sdcard_write_cmd(ALTERA_SDCARD_CMD_READ_BLOCK);
+
+ /*
+ * Wait for device to signal completion of command.
+ */
+ while ((asr = altera_sdcard_read_asr()) &
+ ALTERA_SDCARD_ASR_CMDINPROGRESS);
+
+ /*
+ * Due to hardware bugs/features, interpretting this field is messy.
+ */
+ rr1 = altera_sdcard_read_rr1();
+ rr1 &= ~ALTERA_SDCARD_RR1_COMMANDCRCFAILED; /* HW bug. */
+ if (asr & ALTERA_SDCARD_ASR_CMDTIMEOUT) {
+ printf("SD Card: timeout\n");
+ return (-1);
+ }
+ if ((asr & ALTERA_SDCARD_ASR_CMDDATAERROR) &&
+ (rr1 & ALTERA_SDCARD_RR1_ERRORMASK)) {
+ printf("SD Card: asr %u rr1 %u\n", asr, rr1);
+ return (-1);
+ }
+
+ /*
+ * We can't use a regular memcpy() due to byte-enable bugs in the
+ * Altera IP core: instead copy in 32-bit units.
+ */
+ for (i = 0; i < ALTERA_SDCARD_SECTORSIZE/sizeof(uint32_t); i++)
+ bufp[i] = rxtxp[i];
+ return (0);
+}
+
+/*
+ * Public interface: load 'nblk' blocks from block #lba into *buf.
+ */
+int
+altera_sdcard_read(void *buf, unsigned lba, unsigned nblk)
+{
+ uint8_t *bufp = buf;
+ int i;
+
+ for (i = 0; i < nblk; i++) {
+ if (altera_sdcard_read_block(bufp + i *
+ ALTERA_SDCARD_SECTORSIZE, lba + i) < 0) {
+ printf("SD Card: block read %u failed\n", i);
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Public interface: query (current) media size.
+ */
+uint64_t
+altera_sdcard_get_mediasize(void)
+{
+ uint64_t mediasize;
+ uint64_t c_size, c_size_mult, read_bl_len;
+ uint16_t csd16[ALTERA_SDCARD_CSD_SIZE/sizeof(uint16_t)];
+ uint8_t *csd8p = (uint8_t *)&csd16;
+ uint8_t byte0, byte1, byte2;
+
+ altera_sdcard_read_csd(csd16); /* Provide 16-bit alignment. */
+
+ read_bl_len = csd8p[ALTERA_SDCARD_CSD_READ_BL_LEN_BYTE];
+ read_bl_len &= ALTERA_SDCARD_CSD_READ_BL_LEN_MASK;
+
+ byte0 = csd8p[ALTERA_SDCARD_CSD_C_SIZE_BYTE0];
+ byte0 &= ALTERA_SDCARD_CSD_C_SIZE_MASK0;
+ byte1 = csd8p[ALTERA_SDCARD_CSD_C_SIZE_BYTE1];
+ byte2 = csd8p[ALTERA_SDCARD_CSD_C_SIZE_BYTE2];
+ byte2 &= ALTERA_SDCARD_CSD_C_SIZE_MASK2;
+ c_size = (byte0 >> ALTERA_SDCARD_CSD_C_SIZE_RSHIFT0) |
+ (byte1 << ALTERA_SDCARD_CSD_C_SIZE_LSHIFT1) |
+ (byte2 << ALTERA_SDCARD_CSD_C_SIZE_LSHIFT2);
+
+ byte0 = csd8p[ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE0];
+ byte0 &= ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK0;
+ byte1 = csd8p[ALTERA_SDCARD_CSD_C_SIZE_MULT_BYTE1];
+ byte1 &= ALTERA_SDCARD_CSD_C_SIZE_MULT_MASK1;
+ c_size_mult = (byte0 >> ALTERA_SDCARD_CSD_C_SIZE_MULT_RSHIFT0) |
+ (byte1 << ALTERA_SDCARD_CSD_C_SIZE_MULT_LSHIFT1);
+
+ mediasize = (c_size + 1) * (1 << (c_size_mult + 2)) *
+ (1 << read_bl_len);
+ return (mediasize);
+}
+
+/*
+ * Public interface: is media present / supported?
+ */
+int
+altera_sdcard_get_present(void)
+{
+ uint16_t csd16[ALTERA_SDCARD_CSD_SIZE/sizeof(uint16_t)];
+ uint8_t *csd8p = (uint8_t *)&csd16;
+ uint8_t csd_structure;
+
+ /* First: does status bit think it is there? */
+ if (!(altera_sdcard_read_asr() & ALTERA_SDCARD_ASR_CARDPRESENT)) {
+ printf("SD Card: not present\n");
+ return (0);
+ }
+
+ /* Second: do we understand the CSD structure version? */
+ altera_sdcard_read_csd(csd16); /* Provide 16-bit alignment. */
+ csd_structure = csd8p[ALTERA_SDCARD_CSD_STRUCTURE_BYTE];
+ csd_structure &= ALTERA_SDCARD_CSD_STRUCTURE_MASK;
+ csd_structure >>= ALTERA_SDCARD_CSD_STRUCTURE_RSHIFT;
+ if (csd_structure != 0) {
+ printf("SD Card: unrecognised csd %u\n", csd_structure);
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * Public interface: query sector size.
+ */
+uint64_t
+altera_sdcard_get_sectorsize(void)
+{
+
+ return (ALTERA_SDCARD_SECTORSIZE);
+}
diff --git a/stand/mips/beri/common/sdcard.h b/stand/mips/beri/common/sdcard.h
new file mode 100644
index 0000000..0eb7760
--- /dev/null
+++ b/stand/mips/beri/common/sdcard.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SDCARD_H_
+#define _SDCARD_H_
+
+int altera_sdcard_read(void *buf, unsigned lba, unsigned nblk);
+uint64_t altera_sdcard_get_mediasize(void);
+int altera_sdcard_get_present(void);
+uint64_t altera_sdcard_get_sectorsize(void);
+
+#endif /* !_SDCARD_H_ */
diff --git a/stand/mips/beri/loader/Makefile b/stand/mips/beri/loader/Makefile
new file mode 100644
index 0000000..763b3b3
--- /dev/null
+++ b/stand/mips/beri/loader/Makefile
@@ -0,0 +1,111 @@
+#-
+# Copyright (c) 2013-2014 Robert N. M. Watson
+# All rights reserved.
+#
+# This software was developed by SRI International and the University of
+# Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+# ("CTSRD"), as part of the DARPA CRASH research programme.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+LOADER_MSDOS_SUPPORT?= yes
+LOADER_UFS_SUPPORT?= yes
+LOADER_CD9660_SUPPORT?= no
+LOADER_EXT2FS_SUPPORT?= no
+LOADER_GZIP_SUPPORT?= yes
+LOADER_BZIP2_SUPPORT?= yes
+
+.include <bsd.init.mk>
+
+MK_SSP= no
+MAN=
+
+PROG?= loader
+NEWVERSWHAT= "BERI loader" ${MACHINE_CPUARCH}
+INSTALLFLAGS= -b
+
+# Architecture-specific loader code
+SRCS= start.S \
+ main.c \
+ devicename.c \
+ exec.c \
+ metadata.c \
+ vers.c \
+ arch.c
+
+# libstand front-ends for shared driver code
+SRCS+= beri_console.c \
+ beri_disk_cfi.c \
+ beri_disk_sdcard.c
+
+# Common code with boot2
+SRCS+= altera_jtag_uart.c \
+ cfi.c \
+ sdcard.c
+
+# Since we don't have a backward compatibility issue, default to this on BERI.
+CFLAGS+= -DBOOT_PROMPT_123
+
+HELP_FILES+= help.mips
+
+# Always add MI sources
+.include "${BOOTSRC}/loader.mk"
+
+# BERI files common to boot2 and loader
+.PATH: ${BOOTSRC}/mips/beri/common
+CFLAGS+= -I${BOOTSRC}/mips/beri/common
+
+# Loader-specific MD headers
+CFLAGS+= -I${.CURDIR}
+
+# Generate code appropriate for the loader environment
+CFLAGS+= -G0 \
+ -fno-pic \
+ -mno-abicalls \
+ -msoft-float \
+ -g
+
+LDFLAGS= -nostdlib \
+ -static \
+ -T ${.CURDIR}/loader.ldscript \
+ -L${.CURDIR} \
+ -e __start
+
+DPADD= ${LIBFICL} ${LIBSA}
+LDADD= ${LIBFICL} ${LIBSA}
+
+.if defined(LOADER_USB_SUPPORT)
+# Do garbage collection
+CFLAGS+= -ffunction-sections -fdata-sections
+CFLAGS+= -Wl,--gc-sections
+# Link USB BOOT library
+LDADD+= ${BOOTOBJ}/usb/libusbboot.a
+CFLAGS+= -I${BOOTSRC}/usb
+# Define USB SUPPORT
+CFLAGS+= -DLOADER_USB_SUPPORT
+.endif
+
+all: loader
+
+.include <bsd.prog.mk>
diff --git a/stand/mips/beri/loader/arch.c b/stand/mips/beri/loader/arch.c
new file mode 100644
index 0000000..5ce8ede
--- /dev/null
+++ b/stand/mips/beri/loader/arch.c
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 2013 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <machine/elf.h>
+
+#include <stand.h>
+#include <bootstrap.h>
+#include <loader.h>
+#include <mips.h>
+
+static int beri_arch_autoload(void);
+static ssize_t beri_arch_copyin(const void *src, vm_offset_t va, size_t len);
+static ssize_t beri_arch_copyout(vm_offset_t va, void *dst, size_t len);
+static uint64_t beri_arch_loadaddr(u_int type, void *data, uint64_t addr);
+static ssize_t beri_arch_readin(int fd, vm_offset_t va, size_t len);
+
+struct arch_switch archsw = {
+ .arch_autoload = beri_arch_autoload,
+ .arch_getdev = beri_arch_getdev,
+ .arch_copyin = beri_arch_copyin,
+ .arch_copyout = beri_arch_copyout,
+ .arch_loadaddr = beri_arch_loadaddr,
+ .arch_readin = beri_arch_readin,
+
+};
+
+static int
+beri_arch_autoload(void)
+{
+
+ return (0);
+}
+
+static ssize_t
+beri_arch_copyin(const void *src, vm_offset_t va, size_t len)
+{
+
+ memcpy((void *)va, src, len);
+ return (len);
+}
+
+static ssize_t
+beri_arch_copyout(vm_offset_t va, void *dst, size_t len)
+{
+
+ memcpy(dst, (void *)va, len);
+ return (len);
+}
+
+static uint64_t
+beri_arch_loadaddr(u_int type, void *data, uint64_t addr)
+{
+ uint64_t align;
+
+ /* Align ELF objects at page boundaries; others at cache lines. */
+ align = (type == LOAD_ELF) ? PAGE_SIZE : CACHE_LINE_SIZE;
+ return (roundup2(addr, align));
+}
+
+static ssize_t
+beri_arch_readin(int fd, vm_offset_t va, size_t len)
+{
+
+ return (read(fd, (void *)va, len));
+}
diff --git a/stand/mips/beri/loader/beri_console.c b/stand/mips/beri/loader/beri_console.c
new file mode 100644
index 0000000..9a4ae19
--- /dev/null
+++ b/stand/mips/beri/loader/beri_console.c
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 2013 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <bootstrap.h>
+
+#include <cons.h>
+
+static void c_probe(struct console *);
+static int c_init(int);
+static void c_out(int);
+static int c_in(void);
+static int c_ready(void);
+
+struct console altera_jtag_uart_console = {
+ .c_name = "comconsole",
+ .c_desc = "altera jtag uart",
+ .c_flags = 0,
+ .c_probe = c_probe,
+ .c_init = c_init,
+ .c_out = c_out,
+ .c_in = c_in,
+ .c_ready = c_ready,
+};
+
+static void
+c_probe(struct console *cp)
+{
+
+ cp->c_flags |= C_PRESENTIN|C_PRESENTOUT;
+}
+
+static int
+c_init(int arg)
+{
+
+ return (0);
+}
+
+static void
+c_out(int c)
+{
+
+ putc(c);
+}
+
+static int
+c_in(void)
+{
+
+ return (getc());
+}
+
+static int
+c_ready(void)
+{
+
+ return (keyhit(0));
+}
diff --git a/stand/mips/beri/loader/beri_disk_cfi.c b/stand/mips/beri/loader/beri_disk_cfi.c
new file mode 100644
index 0000000..a21947f
--- /dev/null
+++ b/stand/mips/beri/loader/beri_disk_cfi.c
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 2013-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <bootstrap.h>
+#include <stdarg.h>
+
+#include <stand.h>
+#include <disk.h>
+
+#include <cfi.h>
+
+static int beri_cfi_disk_init(void);
+static int beri_cfi_disk_open(struct open_file *, ...);
+static int beri_cfi_disk_close(struct open_file *);
+static int beri_cfi_disk_strategy(void *, int, daddr_t, size_t,
+ char *, size_t *);
+static int beri_cfi_disk_print(int);
+
+struct devsw beri_cfi_disk = {
+ .dv_name = "cfi",
+ .dv_type = DEVT_DISK,
+ .dv_init = beri_cfi_disk_init,
+ .dv_strategy = beri_cfi_disk_strategy,
+ .dv_open = beri_cfi_disk_open,
+ .dv_close = beri_cfi_disk_close,
+ .dv_ioctl = noioctl,
+ .dv_print = beri_cfi_disk_print,
+ .dv_cleanup = NULL,
+};
+
+static int
+beri_cfi_disk_init(void)
+{
+
+ return (0);
+}
+
+static int
+beri_cfi_disk_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
+ char *buf, size_t *rsizep)
+{
+ int error;
+
+ flag &= F_MASK;
+ if (flag == F_WRITE)
+ return (EROFS);
+ if (flag != F_READ)
+ return (EINVAL);
+ if (rsizep != NULL)
+ *rsizep = 0;
+ error = cfi_read(buf, dblk, size >> 9);
+ if (error == 0 && rsizep != NULL)
+ *rsizep = size;
+ else if (error != 0)
+ printf("%s: error %d\n", __func__, error);
+ return (error);
+}
+
+static int
+beri_cfi_disk_open(struct open_file *f, ...)
+{
+ va_list ap;
+ struct disk_devdesc *dev;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct disk_devdesc *);
+ va_end(ap);
+
+ if (dev->d_unit != 0)
+ return (EIO);
+ return (disk_open(dev, cfi_get_mediasize(), cfi_get_sectorsize()));
+}
+
+static int
+beri_cfi_disk_close(struct open_file *f)
+{
+ struct disk_devdesc *dev;
+
+ dev = (struct disk_devdesc *)f->f_devdata;
+ return (disk_close(dev));
+}
+
+static int
+beri_cfi_disk_print(int verbose)
+{
+ struct disk_devdesc dev;
+ char line[80];
+ int ret;
+
+ printf("%s devices:", beri_cfi_disk.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ snprintf(line, sizeof(line), " cfi%d CFI flash device\n", 0);
+ ret = pager_output(line);
+ if (ret != 0)
+ return (ret);
+ dev.d_dev = &beri_cfi_disk;
+ dev.d_unit = 0;
+ dev.d_slice = -1;
+ dev.d_partition = -1;
+ if (disk_open(&dev, cfi_get_mediasize(), cfi_get_sectorsize()) == 0) {
+ snprintf(line, sizeof(line), " cfi%d", 0);
+ ret = disk_print(&dev, line, verbose);
+ disk_close(&dev);
+ }
+
+ return (ret);
+}
diff --git a/stand/mips/beri/loader/beri_disk_sdcard.c b/stand/mips/beri/loader/beri_disk_sdcard.c
new file mode 100644
index 0000000..266fb4a
--- /dev/null
+++ b/stand/mips/beri/loader/beri_disk_sdcard.c
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 2013-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <bootstrap.h>
+#include <stdarg.h>
+
+#include <stand.h>
+#include <disk.h>
+
+#include <sdcard.h>
+
+static int beri_sdcard_disk_init(void);
+static int beri_sdcard_disk_open(struct open_file *, ...);
+static int beri_sdcard_disk_close(struct open_file *);
+static int beri_sdcard_disk_strategy(void *, int, daddr_t, size_t,
+ char *, size_t *);
+static int beri_sdcard_disk_print(int);
+
+struct devsw beri_sdcard_disk = {
+ .dv_name = "sdcard",
+ .dv_type = DEVT_DISK,
+ .dv_init = beri_sdcard_disk_init,
+ .dv_strategy = beri_sdcard_disk_strategy,
+ .dv_open = beri_sdcard_disk_open,
+ .dv_close = beri_sdcard_disk_close,
+ .dv_ioctl = noioctl,
+ .dv_print = beri_sdcard_disk_print,
+ .dv_cleanup = NULL,
+};
+
+static int
+beri_sdcard_disk_init(void)
+{
+
+ return (0);
+}
+
+static int
+beri_sdcard_disk_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
+ char *buf, size_t *rsizep)
+{
+ int error;
+
+ flag &= F_MASK;
+ if (flag == F_WRITE)
+ return (EROFS);
+ if (flag != F_READ)
+ return (EINVAL);
+ if (rsizep != NULL)
+ *rsizep = 0;
+ error = altera_sdcard_read(buf, dblk, size >> 9);
+ if (error == 0 && rsizep != NULL)
+ *rsizep = size;
+ else if (error != 0)
+ printf("%s: error %d\n", __func__, error);
+ return (error);
+}
+
+static int
+beri_sdcard_disk_open(struct open_file *f, ...)
+{
+ va_list ap;
+ struct disk_devdesc *dev;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct disk_devdesc *);
+ va_end(ap);
+
+ if (!(altera_sdcard_get_present())) {
+ printf("SD card not present or not supported\n");
+ return (ENXIO);
+ }
+
+ if (dev->d_unit != 0)
+ return (EIO);
+ return (disk_open(dev, altera_sdcard_get_mediasize(),
+ altera_sdcard_get_sectorsize()));
+}
+
+static int
+beri_sdcard_disk_close(struct open_file *f)
+{
+ struct disk_devdesc *dev;
+
+ dev = (struct disk_devdesc *)f->f_devdata;
+ return (disk_close(dev));
+}
+
+static int
+beri_sdcard_disk_print(int verbose)
+{
+ struct disk_devdesc dev;
+ char line[80];
+ int ret;
+
+ printf("%s devices:", beri_sdcard_disk.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ snprintf(line, sizeof(line), " sdcard%d Altera SD card drive\n", 0);
+ ret = pager_output(line);
+ if (ret != 0)
+ return (ret);
+ dev.d_dev = &beri_sdcard_disk;
+ dev.d_unit = 0;
+ dev.d_slice = -1;
+ dev.d_partition = -1;
+ if (disk_open(&dev, altera_sdcard_get_mediasize(),
+ altera_sdcard_get_sectorsize()) == 0) {
+ snprintf(line, sizeof(line), " sdcard%d", 0);
+ ret = disk_print(&dev, line, verbose);
+ disk_close(&dev);
+ }
+ return (ret);
+}
diff --git a/stand/mips/beri/loader/devicename.c b/stand/mips/beri/loader/devicename.c
new file mode 100644
index 0000000..4a2c273
--- /dev/null
+++ b/stand/mips/beri/loader/devicename.c
@@ -0,0 +1,205 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+#include "disk.h"
+
+static int beri_arch_parsedev(struct disk_devdesc **dev,
+ const char *devspec, const char **path);
+
+/*
+ * Point (dev) at an allocated device specifier for the device matching the
+ * path in (devspec). If it contains an explicit device specification,
+ * use that. If not, use the default device.
+ */
+int
+beri_arch_getdev(void **vdev, const char *devspec, const char **path)
+{
+ struct disk_devdesc **dev = (struct disk_devdesc **)vdev;
+ int rv;
+
+ /*
+ * If it looks like this is just a path and no
+ * device, go with the current device.
+ */
+ if ((devspec == NULL) ||
+ (devspec[0] == '/') ||
+ (strchr(devspec, ':') == NULL)) {
+
+ if (((rv = beri_arch_parsedev(dev, getenv("currdev"), NULL)) == 0) &&
+ (path != NULL))
+ *path = devspec;
+ return(rv);
+ }
+
+ /*
+ * Try to parse the device name off the beginning of the devspec
+ */
+ return(beri_arch_parsedev(dev, devspec, path));
+}
+
+/*
+ * Point (dev) at an allocated device specifier matching the string version
+ * at the beginning of (devspec). Return a pointer to the remaining
+ * text in (path).
+ *
+ * In all cases, the beginning of (devspec) is compared to the names
+ * of known devices in the device switch, and then any following text
+ * is parsed according to the rules applied to the device type.
+ *
+ * For disk-type devices, the syntax is:
+ *
+ * disk<unit>[s<slice>][<partition>]:
+ *
+ */
+static int
+beri_arch_parsedev(struct disk_devdesc **dev, const char *devspec,
+ const char **path)
+{
+ struct disk_devdesc *idev;
+ struct devsw *dv;
+ int i, unit, err;
+ const char *cp;
+ const char *np;
+
+ /* minimum length check */
+ if (strlen(devspec) < 2)
+ return(EINVAL);
+
+ /* look for a device that matches */
+ for (i = 0, dv = NULL; devsw[i] != NULL; i++) {
+ if (!strncmp(devspec, devsw[i]->dv_name, strlen(devsw[i]->dv_name))) {
+ dv = devsw[i];
+ break;
+ }
+ }
+ if (dv == NULL)
+ return(ENOENT);
+ idev = malloc(sizeof(struct disk_devdesc));
+ err = 0;
+ np = (devspec + strlen(dv->dv_name));
+
+ switch(dv->dv_type) {
+ case DEVT_NONE: /* XXX what to do here? Do we care? */
+ break;
+
+ case DEVT_DISK:
+ err = disk_parsedev(idev, np, path);
+ if (err != 0)
+ goto fail;
+ break;
+
+ case DEVT_CD:
+ case DEVT_NET:
+ case DEVT_ZFS:
+ unit = 0;
+
+ if (*np && (*np != ':')) {
+ unit = strtol(np, &cp, 0); /* get unit number if present */
+ if (cp == np) {
+ err = EUNIT;
+ goto fail;
+ }
+ } else {
+ cp = np;
+ }
+ if (*cp && (*cp != ':')) {
+ err = EINVAL;
+ goto fail;
+ }
+
+ idev->d_unit = unit;
+ if (path != NULL)
+ *path = (*cp == 0) ? cp : cp + 1;
+ break;
+
+ default:
+ err = EINVAL;
+ goto fail;
+ }
+ idev->d_dev = dv;
+ idev->d_type = dv->dv_type;
+ if (dev == NULL) {
+ free(idev);
+ } else {
+ *dev = idev;
+ }
+ return(0);
+
+ fail:
+ free(idev);
+ return(err);
+}
+
+
+char *
+beri_arch_fmtdev(void *vdev)
+{
+ struct disk_devdesc *dev = (struct disk_devdesc *)vdev;
+ static char buf[128]; /* XXX device length constant? */
+
+ switch(dev->d_type) {
+ case DEVT_NONE:
+ strcpy(buf, "(no device)");
+ break;
+
+ case DEVT_CD:
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
+ break;
+
+ case DEVT_DISK:
+ return (disk_fmtdev(vdev));
+
+ case DEVT_NET:
+ case DEVT_ZFS:
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
+ break;
+ }
+ return(buf);
+}
+
+
+/*
+ * Set currdev to suit the value being supplied in (value)
+ */
+int
+beri_arch_setcurrdev(struct env_var *ev, int flags, const void *value)
+{
+ struct disk_devdesc *ncurr;
+ int rv;
+
+ if ((rv = beri_arch_parsedev(&ncurr, value, NULL)) != 0)
+ return(rv);
+ free(ncurr);
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return(0);
+}
diff --git a/stand/mips/beri/loader/exec.c b/stand/mips/beri/loader/exec.c
new file mode 100644
index 0000000..382b6a1
--- /dev/null
+++ b/stand/mips/beri/loader/exec.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2013-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/linker.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+
+#include <bootstrap.h>
+#include <loader.h>
+#include <mips.h>
+#include <stand.h>
+
+static int beri_elf64_loadfile(char *, uint64_t,
+ struct preloaded_file **);
+static int beri_elf64_exec(struct preloaded_file *fp);
+
+struct file_format beri_elf = {
+ .l_load = beri_elf64_loadfile,
+ .l_exec = beri_elf64_exec,
+};
+
+/*
+ * bootinfo that we will pass onto the kernel; some fields derived from
+ * *boot2_bootinfop, others filled in by loader.
+ */
+struct bootinfo bootinfo;
+
+static int
+beri_elf64_loadfile(char *filename, uint64_t dest,
+ struct preloaded_file **result)
+{
+
+ /*
+ * Some platforms require invalidation of instruction caches here; we
+ * don't need that currently.
+ */
+ return (__elfN(loadfile)(filename, dest, result));
+}
+
+static int
+beri_elf64_exec(struct preloaded_file *fp)
+{
+ void (*entry)(register_t, register_t, register_t, register_t);
+ struct file_metadata *md;
+ vm_offset_t mdp;
+ Elf_Ehdr *ehdr;
+ int error;
+
+ md = file_findmetadata(fp, MODINFOMD_ELFHDR);
+ if (md == NULL) {
+ printf("%s: file_findmetadata failed\n");
+ return (EFTYPE);
+ }
+ ehdr = (Elf_Ehdr *)md->md_data;
+
+ error = md_load64(fp->f_args, &mdp);
+ if (error) {
+ printf("%s: md_load64 failed\n");
+ return (error);
+ }
+
+ entry = (void *)ehdr->e_entry;
+ printf("Kernel entry at %p\n", entry);
+
+ dev_cleanup(); /* XXXRW: Required? */
+ printf("Kernel args: %s\n", fp->f_args);
+
+ /*
+ * Configure bootinfo for the loaded kernel. Some values are
+ * inherited from the bootinfo passed to us by boot2 (e.g., DTB
+ * pointer); others are local to the loader (e.g., kernel boot flags).
+ */
+ bzero(&bootinfo, sizeof(bootinfo));
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+ bootinfo.bi_boot2opts = boot2_bootinfo.bi_boot2opts;
+ /* NB: bi_kernelname used only by boot2. */
+ /* NB: bi_nfs_diskless not yet. */
+ bootinfo.bi_dtb = boot2_bootinfo.bi_dtb;
+ bootinfo.bi_memsize = boot2_bootinfo.bi_memsize;
+ bootinfo.bi_modulep = mdp;
+
+ /*
+ * XXXRW: For now, pass 'memsize' rather than dtb or bootinfo. This
+ * is the 'old' ABI spoken by Miniboot and the kernel. To pass in
+ * boot2opts, modules, etc, we will need to fix this to pass in at
+ * least bootinfop.
+ */
+ (*entry)(boot2_argc, (register_t)boot2_argv, (register_t)boot2_envv,
+ &bootinfo);
+
+ panic("exec returned");
+}
diff --git a/stand/mips/beri/loader/help.mips b/stand/mips/beri/loader/help.mips
new file mode 100644
index 0000000..5873eb0
--- /dev/null
+++ b/stand/mips/beri/loader/help.mips
@@ -0,0 +1 @@
+$FreeBSD$
diff --git a/stand/mips/beri/loader/loader.h b/stand/mips/beri/loader/loader.h
new file mode 100644
index 0000000..e4152e7
--- /dev/null
+++ b/stand/mips/beri/loader/loader.h
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2013-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOT_LOADER_H_
+#define _BOOT_LOADER_H_
+
+/* beri_console.c */
+extern struct console altera_jtag_uart_console;
+
+/* beri_disk.c */
+extern struct devsw beri_cfi_disk;
+extern struct devsw beri_sdcard_disk;
+
+/* devicename.c */
+int beri_arch_setcurrdev(struct env_var *, int, const void *);
+char *beri_arch_fmtdev(void *);
+int beri_arch_getdev(void **, const char *, const char **);
+
+/* exec.c */
+extern struct file_format beri_elf;
+
+/* main.c */
+extern int boot2_argc;
+extern char **boot2_argv;
+extern char **boot2_envv;
+extern struct bootinfo boot2_bootinfo;
+
+/* metadata.c */
+int md_load64(char *args, vm_offset_t *modulep);
+
+/* vers.c */
+extern char bootprog_info[];
+
+#endif /* !_BOOT_LOADER_H_ */
diff --git a/stand/mips/beri/loader/loader.ldscript b/stand/mips/beri/loader/loader.ldscript
new file mode 100644
index 0000000..deb4865
--- /dev/null
+++ b/stand/mips/beri/loader/loader.ldscript
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 2011-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+INCLUDE ../common/common.ldscript
+
+/*
+ * Location where loader will execute.
+ */
+__loader_base__ = 0x20000;
+__loader_base_vaddr__ = __mips64_xkphys_cached__ + __loader_base__;
+
+/*
+ * Highest address the loader is allowed to use below the kernel.
+ */
+__loader_end__ = 0x100000;
+__loader_end_vaddr__ = __mips64_xkphys_cached__ + __loader_end__;
+
+OUTPUT_ARCH(mips)
+ENTRY(start)
+SECTIONS
+{
+ /*
+ * We rely on boot2 having (a) configured a stack, and (b) loaded us
+ * to an appropriate bit of physical/virtual memory such that no
+ * self-relocating code is required here.
+ */
+ . = __loader_base_vaddr__;
+ . += SIZEOF_HEADERS;
+
+ .text ALIGN(0x10): {
+ start.o(.text*)
+ *(EXCLUDE_FILE (start.o) .text*)
+ *(.rodata*)
+
+ __start_set_Xcommand_set = .;
+ KEEP(*(set_Xcommand_set))
+ __stop_set_Xcommand_set = .;
+
+ __start_set_Xficl_compile_set = .;
+ KEEP(*(set_Xficl_compile_set))
+ __stop_set_Xficl_compile_set = .;
+ }
+ .data ALIGN(0x10): { *(.data*)}
+ .bss ALIGN(0x10): { *(.bss*) }
+
+ __heap = ALIGN(0x8); /* 64-bit aligned heap pointer */
+ __data_end = .;
+ __boot_loader_len__ = . - __loader_base_vaddr__;
+ __bss_start = ADDR(.bss);
+ __bss_end = ALIGN(__bss_start + SIZEOF(.bss), 0x8);
+
+ __heap_start = .;
+ __heap_end = __loader_end_vaddr__;
+ __heap_len = __heap_end - __heap_start;
+}
diff --git a/stand/mips/beri/loader/main.c b/stand/mips/beri/loader/main.c
new file mode 100644
index 0000000..2d201d8
--- /dev/null
+++ b/stand/mips/beri/loader/main.c
@@ -0,0 +1,244 @@
+/*-
+ * Copyright (c) 2013-2014 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/reboot.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+
+#include <stand.h>
+#include <bootstrap.h>
+#include <loader.h>
+#include <mips.h>
+
+#ifdef LOADER_USB_SUPPORT
+#include <storage/umass_common.h>
+#endif
+
+static int __elfN(exec)(struct preloaded_file *);
+static void extract_currdev(struct bootinfo *);
+
+struct devsw *devsw[] = {
+ &beri_cfi_disk,
+ &beri_sdcard_disk,
+#ifdef LOADER_USB_SUPPORT
+ &umass_disk,
+#endif
+ NULL
+};
+
+struct arch_switch archsw;
+
+struct file_format *file_formats[] = {
+ &beri_elf,
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#ifdef LOADER_UFS_SUPPORT
+ &ufs_fsops,
+#endif
+ NULL
+};
+
+struct console *consoles[] = {
+ &altera_jtag_uart_console,
+ NULL
+};
+
+extern void __bss_start, __bss_end;
+extern void __heap_start, __heap_end;
+
+static int
+__elfN(exec)(struct preloaded_file *fp)
+{
+
+ return (EFTYPE);
+}
+
+/*
+ * Capture arguments from boot2 for later reuse when launching the kernel.
+ * Note that we choose not to maintain a pointer to boo2_bootinfop after
+ * initial argument processing: this is because we might load the kernel over
+ * the spot where boot2 was running, so we can't pass that pointer on to the
+ * kernel. To be on the safe side, never reference it outside of the body of
+ * main(), instead preserving a copy.
+ */
+int boot2_argc;
+char **boot2_argv;
+char **boot2_envv;
+
+struct bootinfo boot2_bootinfo;
+
+int
+main(int argc, char *argv[], char *envv[], struct bootinfo *bootinfop)
+{
+ struct devsw **dp;
+
+ /* NB: Must be sure to bzero() before using any globals. */
+ bzero(&__bss_start, (uintptr_t)&__bss_end - (uintptr_t)&__bss_start);
+
+ boot2_argc = argc;
+ boot2_argv = argv;
+ boot2_envv = envv;
+ boot2_bootinfo = *bootinfop; /* Copy rather than by reference. */
+
+ setheap((void *)&__heap_start, (void *)&__heap_end);
+
+ /*
+ * Pick up console settings from boot2; probe console.
+ */
+ if (bootinfop->bi_boot2opts & RB_MULTIPLE) {
+ if (bootinfop->bi_boot2opts & RB_SERIAL)
+ setenv("console", "comconsole vidconsole", 1);
+ else
+ setenv("console", "vidconsole comconsole", 1);
+ } else if (bootinfop->bi_boot2opts & RB_SERIAL)
+ setenv("console", "comconsole", 1);
+ else if (bootinfop->bi_boot2opts & RB_MUTE)
+ setenv("console", "nullconsole", 1);
+ cons_probe();
+ setenv("LINES", "24", 1);
+
+ printf("%s(%d, %p, %p, %p (%p))\n", __func__, argc, argv, envv,
+ bootinfop, (void *)bootinfop->bi_memsize);
+
+ /*
+ * Initialise devices.
+ */
+ for (dp = devsw; *dp != NULL; dp++) {
+ if ((*dp)->dv_init != NULL)
+ (*dp)->dv_init();
+ }
+ extract_currdev(bootinfop);
+
+ printf("\n%s", bootprog_info);
+#if 0
+ printf("bootpath=\"%s\"\n", bootpath);
+#endif
+
+ interact(NULL);
+ return (0);
+}
+
+static void
+extract_currdev(struct bootinfo *bootinfop)
+{
+ const char *bootdev;
+
+ /*
+ * Pick up boot device information from boot2.
+ *
+ * XXXRW: Someday: device units.
+ */
+ switch(bootinfop->bi_boot_dev_type) {
+ case BOOTINFO_DEV_TYPE_DRAM:
+ bootdev = "dram0";
+ break;
+
+ case BOOTINFO_DEV_TYPE_CFI:
+ bootdev = "cfi0";
+ break;
+
+ case BOOTINFO_DEV_TYPE_SDCARD:
+ bootdev = "sdcard0";
+ break;
+
+ default:
+ bootdev = NULL;
+ }
+
+ if (bootdev != NULL) {
+ env_setenv("currdev", EV_VOLATILE, bootdev, NULL, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, bootdev, env_noset,
+ env_nounset);
+ }
+}
+
+void
+abort(void)
+{
+
+ printf("error: loader abort\n");
+ while (1);
+}
+
+void
+exit(int code)
+{
+
+ printf("error: loader exit\n");
+ while (1);
+}
+
+void
+longjmperror(void)
+{
+
+ printf("error: loader longjmp error\n");
+ while (1);
+}
+
+time_t
+time(time_t *tloc)
+{
+
+ /* We can't provide time since UTC, so just provide time since boot. */
+ return (cp0_count_get() / 100000000);
+}
+
+/*
+ * Delay - in usecs
+ *
+ * NOTE: We are assuming that the CPU is running at 100MHz.
+ */
+void
+delay(int usecs)
+{
+ uint32_t delta;
+ uint32_t curr;
+ uint32_t last;
+
+ last = cp0_count_get();
+ while (usecs > 0) {
+ curr = cp0_count_get();
+ delta = curr - last;
+ while (usecs > 0 && delta >= 100) {
+ usecs--;
+ last += 100;
+ delta -= 100;
+ }
+ }
+}
diff --git a/stand/mips/beri/loader/metadata.c b/stand/mips/beri/loader/metadata.c
new file mode 100644
index 0000000..0698cd1
--- /dev/null
+++ b/stand/mips/beri/loader/metadata.c
@@ -0,0 +1,355 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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.
+ *
+ * from: FreeBSD: src/sys/boot/sparc64/loader/metadata.c,v 1.6
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+
+#include <machine/metadata.h>
+
+#include "bootstrap.h"
+
+/*
+ * Return a 'boothowto' value corresponding to the kernel arguments in
+ * (kargs) and any relevant environment variables.
+ */
+static struct
+{
+ const char *ev;
+ int mask;
+} howto_names[] = {
+ {"boot_askname", RB_ASKNAME},
+ {"boot_cdrom", RB_CDROM},
+ {"boot_ddb", RB_KDB},
+ {"boot_dfltroot", RB_DFLTROOT},
+ {"boot_gdb", RB_GDB},
+ {"boot_multicons", RB_MULTIPLE},
+ {"boot_mute", RB_MUTE},
+ {"boot_pause", RB_PAUSE},
+ {"boot_serial", RB_SERIAL},
+ {"boot_single", RB_SINGLE},
+ {"boot_verbose", RB_VERBOSE},
+ {NULL, 0}
+};
+
+int
+md_getboothowto(char *kargs)
+{
+ char *cp;
+ int howto;
+ int active;
+ int i;
+
+ /* Parse kargs */
+ howto = 0;
+ if (kargs != NULL) {
+ cp = kargs;
+ active = 0;
+ while (*cp != 0) {
+ if (!active && (*cp == '-')) {
+ active = 1;
+ } else if (active)
+ switch (*cp) {
+ case 'a':
+ howto |= RB_ASKNAME;
+ break;
+ case 'C':
+ howto |= RB_CDROM;
+ break;
+ case 'd':
+ howto |= RB_KDB;
+ break;
+ case 'D':
+ howto |= RB_MULTIPLE;
+ break;
+ case 'm':
+ howto |= RB_MUTE;
+ break;
+ case 'g':
+ howto |= RB_GDB;
+ break;
+ case 'h':
+ howto |= RB_SERIAL;
+ break;
+ case 'p':
+ howto |= RB_PAUSE;
+ break;
+ case 'r':
+ howto |= RB_DFLTROOT;
+ break;
+ case 's':
+ howto |= RB_SINGLE;
+ break;
+ case 'v':
+ howto |= RB_VERBOSE;
+ break;
+ default:
+ active = 0;
+ break;
+ }
+ cp++;
+ }
+ }
+ /* get equivalents from the environment */
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+ if (!strcmp(getenv("console"), "comconsole"))
+ howto |= RB_SERIAL;
+ if (!strcmp(getenv("console"), "nullconsole"))
+ howto |= RB_MUTE;
+ return(howto);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+vm_offset_t
+md_copyenv(vm_offset_t addr)
+{
+ struct env_var *ep;
+
+ /* traverse the environment */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ archsw.arch_copyin(ep->ev_name, addr, strlen(ep->ev_name));
+ addr += strlen(ep->ev_name);
+ archsw.arch_copyin("=", addr, 1);
+ addr++;
+ if (ep->ev_value != NULL) {
+ archsw.arch_copyin(ep->ev_value, addr, strlen(ep->ev_value));
+ addr += strlen(ep->ev_value);
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ return(addr);
+}
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+
+static int align;
+
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ archsw.arch_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c) \
+ if (c) \
+ archsw.arch_copyin(s, a, strlen(s) + 1);\
+ a += roundup(strlen(s) + 1, align); \
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ archsw.arch_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), align); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c);\
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ archsw.arch_copyin(mm->md_data, a, mm->md_size);\
+ a += roundup(mm->md_size, align); \
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+vm_offset_t
+md_copymodules(vm_offset_t addr, int kern64)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ uint64_t scratch64;
+ int c;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must come first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ if (kern64) {
+ scratch64 = fp->f_addr;
+ MOD_ADDR(addr, scratch64, c);
+ scratch64 = fp->f_size;
+ MOD_SIZE(addr, scratch64, c);
+ } else {
+ MOD_ADDR(addr, fp->f_addr, c);
+ MOD_SIZE(addr, fp->f_size, c);
+ }
+ for (md = fp->f_metadata; md != NULL; md = md->md_next) {
+ if (!(md->md_type & MODINFOMD_NOCOPY)) {
+ MOD_METADATA(addr, md, c);
+ }
+ }
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Load the information expected by a powerpc kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+md_load_dual(char *args, vm_offset_t *modulep, int kern64)
+{
+ struct preloaded_file *kfp;
+ struct preloaded_file *xp;
+ struct file_metadata *md;
+ vm_offset_t kernend;
+ vm_offset_t addr;
+ vm_offset_t envp;
+ vm_offset_t size;
+ uint64_t scratch64;
+ char *rootdevname;
+ int howto;
+
+ align = kern64 ? 8 : 4;
+ howto = md_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied device
+ * This should perhaps go to MI code and/or have $rootdev tested/set by
+ * MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ if (rootdevname == NULL)
+ rootdevname = getenv("currdev");
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(rootdevname);
+
+ /* find the last module in the chain */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* copy our environment */
+ envp = addr;
+ addr = md_copyenv(addr);
+
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ kernend = 0;
+ kfp = file_findfile(NULL, kern64 ? "elf64 kernel" : "elf32 kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ if (kern64) {
+ scratch64 = envp;
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof scratch64, &scratch64);
+ scratch64 = kernend;
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof scratch64, &scratch64);
+ } else {
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ }
+
+ *modulep = addr;
+ size = md_copymodules(0, kern64);
+ kernend = roundup(addr + size, PAGE_SIZE);
+
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ if (kern64) {
+ scratch64 = kernend;
+ bcopy(&scratch64, md->md_data, sizeof scratch64);
+ } else {
+ bcopy(&kernend, md->md_data, sizeof kernend);
+ }
+
+ (void)md_copymodules(addr, kern64);
+
+ return(0);
+}
+
+int
+md_load(char *args, vm_offset_t *modulep)
+{
+ return (md_load_dual(args, modulep, 0));
+}
+
+int
+md_load64(char *args, vm_offset_t *modulep)
+{
+ return (md_load_dual(args, modulep, 1));
+}
+
diff --git a/stand/mips/beri/loader/start.S b/stand/mips/beri/loader/start.S
new file mode 100644
index 0000000..d679caa
--- /dev/null
+++ b/stand/mips/beri/loader/start.S
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2013 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+.set mips64
+.set noreorder
+.set nobopt
+.set noat
+
+ASM_ENTRY(__start)
+VECTOR(_loader_start, unknown)
+ /* Not much here yet. */
+ jal main
+ nop
+
+ /* If main() returns, spin. */
+loop:
+ b loop
+ nop
+VECTOR_END(_loader_start)
diff --git a/stand/mips/beri/loader/version b/stand/mips/beri/loader/version
new file mode 100644
index 0000000..ce8e187
--- /dev/null
+++ b/stand/mips/beri/loader/version
@@ -0,0 +1,6 @@
+$FreeBSD$
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+0.1: Initial MIPS version
diff --git a/stand/mips/uboot/Makefile b/stand/mips/uboot/Makefile
new file mode 100644
index 0000000..b941ba3
--- /dev/null
+++ b/stand/mips/uboot/Makefile
@@ -0,0 +1,57 @@
+# $FreeBSD$
+
+LOADER_CD9660_SUPPORT?= no
+LOADER_EXT2FS_SUPPORT?= no
+LOADER_MSDOS_SUPPORT?= yes
+LOADER_UFS_SUPPORT?= yes
+LOADER_NET_SUPPORT?= yes
+LOADER_NFS_SUPPORT?= yes
+LOADER_TFTP_SUPPORT?= no
+LOADER_GZIP_SUPPORT?= no
+LOADER_BZIP2_SUPPORT?= no
+
+.include <bsd.init.mk>
+
+FILES+= ubldr
+
+NEWVERSWHAT= "U-Boot loader" ${MACHINE_ARCH}
+INSTALLFLAGS= -b
+WARNS?= 1
+# Address at which ubldr will be loaded.
+# This varies for different boards and SOCs.
+UBLDR_LOADADDR?= 0xffffffff80800000
+
+# Architecture-specific loader code
+SRCS= start.S conf.c vers.c
+
+HELP_FILES+= help.uboot ${BOOTSRC}/fdt/help.fdt
+
+# Always add MI sources
+.include "${BOOTSRC}/loader.mk"
+
+CFLAGS+= -ffreestanding -msoft-float -g
+
+LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.${MACHINE_CPUARCH}
+
+.include "${BOOTSRC}/uboot.mk"
+
+DPADD= ${LIBFICL} ${LIBUBOOT} ${LIBFDT} ${LIBUBOOT_FDT} ${LIBSA}
+LDADD= ${LIBFICL} ${LIBUBOOT} ${LIBFDT} ${LIBUBOOT_FDT} ${LIBSA}
+
+OBJS+= ${SRCS:N*.h:R:S/$/.o/g}
+
+ldscript.abs:
+ echo "UBLDR_LOADADDR = ${UBLDR_LOADADDR};" >${.TARGET}
+
+ldscript.pie:
+ echo "UBLDR_LOADADDR = 0;" >${.TARGET}
+
+ubldr: ${OBJS} ldscript.abs ${.CURDIR}/ldscript.${MACHINE_CPUARCH} ${DPADD}
+ ${CC} ${CFLAGS} -T ldscript.abs ${LDFLAGS} \
+ -o ${.TARGET} ${OBJS} ${LDADD}
+ ${OBJCOPY} -S -O binary ubldr ubldr.bin
+
+CLEANFILES+= ldscript.abs ldscript.pie ubldr ubldr.pie ubldr.bin
+
+.include <bsd.stand.mk>
+.include <bsd.prog.mk>
diff --git a/stand/mips/uboot/conf.c b/stand/mips/uboot/conf.c
new file mode 100644
index 0000000..3579b6a
--- /dev/null
+++ b/stand/mips/uboot/conf.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 2008 Semihalf, Rafal Jaworowski
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "bootstrap.h"
+#include "libuboot.h"
+
+#if defined(LOADER_NET_SUPPORT)
+#include "dev_net.h"
+#endif
+
+/* Make sure we have an explicit reference to exit so libsa's panic pulls in the MD exit */
+void (*exitfn)(int) = exit;
+
+struct devsw *devsw[] = {
+#if defined(LOADER_DISK_SUPPORT) || defined(LOADER_CD9660_SUPPORT)
+ &uboot_storage,
+#endif
+#if defined(LOADER_NET_SUPPORT)
+ &netdev,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#if defined(LOADER_MSDOS_SUPPORT)
+ &dosfs_fsops,
+#endif
+#if defined(LOADER_UFS_SUPPORT)
+ &ufs_fsops,
+#endif
+#if defined(LOADER_CD9660_SUPPORT)
+ &cd9660_fsops,
+#endif
+#if defined(LOADER_EXT2FS_SUPPORT)
+ &ext2fs_fsops,
+#endif
+#if defined(LOADER_NANDFS_SUPPORT)
+ &nandfs_fsops,
+#endif
+#if defined(LOADER_NFS_SUPPORT)
+ &nfs_fsops,
+#endif
+#if defined(LOADER_TFTP_SUPPORT)
+ &tftp_fsops,
+#endif
+#if defined(LOADER_GZIP_SUPPORT)
+ &gzipfs_fsops,
+#endif
+#if defined(LOADER_BZIP2_SUPPORT)
+ &bzipfs_fsops,
+#endif
+ NULL
+};
+
+struct netif_driver *netif_drivers[] = {
+#if defined(LOADER_NET_SUPPORT)
+ &uboot_net,
+#endif
+ NULL,
+};
+
+struct file_format *file_formats[] = {
+ &uboot_elf,
+ NULL
+};
+
+extern struct console uboot_console;
+
+struct console *consoles[] = {
+ &uboot_console,
+ NULL
+};
+
+void
+abort(void)
+{
+
+ printf("error: loader abort\n");
+ while (1);
+}
+
+void
+longjmperror(void)
+{
+
+ printf("error: loader longjmp error\n");
+ while (1);
+}
+
+int debug = 1;
diff --git a/stand/mips/uboot/help.uboot b/stand/mips/uboot/help.uboot
new file mode 100644
index 0000000..c1574af
--- /dev/null
+++ b/stand/mips/uboot/help.uboot
@@ -0,0 +1,27 @@
+$FreeBSD$
+
+###############################################################################
+# Tubenv DShow or import U-Boot environment variables
+
+ ubenv <import | show> [varname ...]
+
+ Display U-Boot environment variables, or import them into the
+ loader environment (which makes them available in the kernel).
+
+###############################################################################
+# Tubenv Simport DImport U-Boot env vars
+
+ ubenv import [varname ...]
+
+ If no variable names are specified, all U-Boot environment
+ variables are imported. Each variable is prefixed with "uboot."
+ to avoid any possible conflicts with loader or kernel variables.
+
+###############################################################################
+# Tubenv Sshow DShow U-Boot env vars
+
+ ubenv show [varname ...]
+
+ If no variable names are specified, all U-Boot environment
+ variables are shown.
+
diff --git a/stand/mips/uboot/ldscript.mips b/stand/mips/uboot/ldscript.mips
new file mode 100644
index 0000000..815dabc
--- /dev/null
+++ b/stand/mips/uboot/ldscript.mips
@@ -0,0 +1,134 @@
+/* $FreeBSD$ */
+
+OUTPUT_ARCH(mips)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ /*. = UBLDR_LOADADDR + SIZEOF_HEADERS;*/
+ . = UBLDR_LOADADDR;
+ .text :
+ {
+ start.o(.text*)
+ *(EXCLUDE_FILE (start.o) .text*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ PROVIDE (etext = .);
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rela.got : { *(.rela.got) }
+ .rela.got1 : { *(.rela.got1) }
+ .rela.got2 : { *(.rela.got2) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rela.init : { *(.rela.init) }
+ .rela.fini : { *(.rela.fini) }
+ .rela.bss : { *(.rela.bss) }
+ .rela.plt : { *(.rela.plt) }
+ .rela.sdata : { *(.rela.sdata) }
+ .rela.sbss : { *(.rela.sbss) }
+ .rela.sdata2 : { *(.rela.sdata2) }
+ .rela.sbss2 : { *(.rela.sbss2) }
+ .init : { *(.init) } =0
+ .fini : { *(.fini) } =0
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 : { *(.sdata2) }
+ .sbss2 : { *(.sbss2) }
+ /* Adjust the address for the data segment to the next page up. */
+ . = ((. + 0x1000) & ~(0x1000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ }
+ .data1 : { *(.data1) }
+ .got1 : { *(.got1) }
+ .dynamic : { *(.dynamic) }
+ /* Put .ctors and .dtors next to the .got2 section, so that the pointers
+ get relocated with -mrelocatable. Also put in the .fixup pointers.
+ The current compiler no longer needs this, but keep it around for 2.7.2 */
+ PROVIDE (_GOT2_START_ = .);
+ .got2 : { *(.got2) }
+ PROVIDE (__CTOR_LIST__ = .);
+ .ctors : { *(.ctors) }
+ PROVIDE (__CTOR_END__ = .);
+ PROVIDE (__DTOR_LIST__ = .);
+ .dtors : { *(.dtors) }
+ PROVIDE (__DTOR_END__ = .);
+ PROVIDE (_FIXUP_START_ = .);
+ .fixup : { *(.fixup) }
+ PROVIDE (_FIXUP_END_ = .);
+ PROVIDE (_GOT2_END_ = .);
+ PROVIDE (_GOT_START_ = .);
+ .got : { *(.got) }
+ .got.plt : { *(.got.plt) }
+ PROVIDE (_GOT_END_ = .);
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ *(.sbss)
+ *(.scommon)
+ *(.dynsbss)
+ PROVIDE (__sbss_end = .);
+ }
+ .plt : { *(.plt) }
+ .bss :
+ {
+ PROVIDE (__bss_start = .);
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ /* 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) }
+ .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) }
+ /* These must appear regardless of . */
+}
diff --git a/stand/mips/uboot/loader.conf b/stand/mips/uboot/loader.conf
new file mode 100644
index 0000000..dd2a23a
--- /dev/null
+++ b/stand/mips/uboot/loader.conf
@@ -0,0 +1,13 @@
+# This is defaults/loader.conf for ARM, containing defaults for loader(8).
+# Do not modify the contents of this file, instead put your customizations
+# into /boot/loader.conf or /boot/loader.conf.local
+# $FreeBSD$
+
+autoboot_delay=10
+bootfile="kernel" # Kernel name (possibly absolute path)
+kernel="kernel" # /boot sub-directory containing kernel and modules
+loader_conf_files="/boot/loader.conf /boot/loader.conf.local"
+module_path="/boot/kernel;/boot/modules;/boot/dtb;/boot/overlays"
+nextboot_conf="/boot/nextboot.conf"
+nextboot_enable="NO"
+verbose_loading="NO"
diff --git a/stand/mips/uboot/start.S b/stand/mips/uboot/start.S
new file mode 100644
index 0000000..2ca68b2
--- /dev/null
+++ b/stand/mips/uboot/start.S
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+ .text
+ .extern _C_LABEL(main)
+ .weak _DYNAMIC
+
+/*
+ * Entry point to the loader that U-Boot passes control to.
+ */
+ENTRY(_start)
+ PTR_S sp, uboot_address
+ j main
+ nop
+END(_start)
+
+/*
+ * syscall()
+ */
+ENTRY(syscall)
+ PTR_S ra, ret_address
+ PTR_L t9, syscall_ptr
+ jalr t9
+ nop
+ PTR_L ra, ret_address
+ jr ra
+ nop
+END(syscall)
+
+/*
+ * Data section
+ */
+ .data
+ .align 8
+ .globl syscall_ptr
+syscall_ptr:
+ .dword 0
+
+ .globl uboot_address
+uboot_address:
+ .dword 0
+
+ret_address:
+ .dword 0
diff --git a/stand/mips/uboot/version b/stand/mips/uboot/version
new file mode 100644
index 0000000..486c412
--- /dev/null
+++ b/stand/mips/uboot/version
@@ -0,0 +1,9 @@
+$FreeBSD$
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+1.2: Extended with NAND FS support.
+1.1: Flattened Device Tree blob support.
+1.0: Added storage support. Booting from HDD, USB, etc. is now possible.
+0.5: Initial U-Boot/arm version (netbooting only).
diff --git a/stand/ofw/Makefile b/stand/ofw/Makefile
new file mode 100644
index 0000000..3b881b7
--- /dev/null
+++ b/stand/ofw/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= libofw
+
+.include <bsd.subdir.mk>
diff --git a/stand/ofw/Makefile.inc b/stand/ofw/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/stand/ofw/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/stand/ofw/common/Makefile.inc b/stand/ofw/common/Makefile.inc
new file mode 100644
index 0000000..5d20372
--- /dev/null
+++ b/stand/ofw/common/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+SRCS+= main.c
diff --git a/stand/ofw/common/main.c b/stand/ofw/common/main.c
new file mode 100644
index 0000000..3c0bbdf
--- /dev/null
+++ b/stand/ofw/common/main.c
@@ -0,0 +1,185 @@
+/*-
+ * Copyright (c) 2000 Benno Rice <benno@jeamland.net>
+ * Copyright (c) 2000 Stephane Potvin <sepotvin@videotron.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. 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 AUTHORS 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "openfirm.h"
+#include "libofw.h"
+#include "bootstrap.h"
+
+struct arch_switch archsw; /* MI/MD interface boundary */
+
+extern char end[];
+extern char bootprog_info[];
+
+u_int32_t acells, scells;
+
+static char bootargs[128];
+
+#define HEAP_SIZE 0x100000
+
+#define OF_puts(fd, text) OF_write(fd, text, strlen(text))
+
+void
+init_heap(void)
+{
+ void *base;
+ ihandle_t stdout;
+
+ if ((base = ofw_alloc_heap(HEAP_SIZE)) == (void *)0xffffffff) {
+ OF_getprop(chosen, "stdout", &stdout, sizeof(stdout));
+ OF_puts(stdout, "Heap memory claim failed!\n");
+ OF_enter();
+ }
+
+ setheap(base, (void *)((int)base + HEAP_SIZE));
+}
+
+uint64_t
+memsize(void)
+{
+ phandle_t memoryp;
+ cell_t reg[24];
+ int i, sz;
+ u_int64_t memsz;
+
+ memsz = 0;
+ memoryp = OF_instance_to_package(memory);
+
+ sz = OF_getprop(memoryp, "reg", &reg, sizeof(reg));
+ sz /= sizeof(reg[0]);
+
+ for (i = 0; i < sz; i += (acells + scells)) {
+ if (scells > 1)
+ memsz += (uint64_t)reg[i + acells] << 32;
+ memsz += reg[i + acells + scells - 1];
+ }
+
+ return (memsz);
+}
+
+int
+main(int (*openfirm)(void *))
+{
+ phandle_t root;
+ int i;
+ char bootpath[64];
+ char *ch;
+ int bargc;
+ char **bargv;
+
+ /*
+ * Initialise the Open Firmware routines by giving them the entry point.
+ */
+ OF_init(openfirm);
+
+ root = OF_finddevice("/");
+
+ scells = acells = 1;
+ OF_getprop(root, "#address-cells", &acells, sizeof(acells));
+ OF_getprop(root, "#size-cells", &scells, sizeof(scells));
+
+ /*
+ * Initialise the heap as early as possible. Once this is done,
+ * alloc() is usable. The stack is buried inside us, so this is
+ * safe.
+ */
+ init_heap();
+
+ /*
+ * Set up console.
+ */
+ cons_probe();
+
+ /*
+ * March through the device switch probing for things.
+ */
+ for (i = 0; devsw[i] != NULL; i++)
+ if (devsw[i]->dv_init != NULL)
+ (devsw[i]->dv_init)();
+
+ printf("\n%s", bootprog_info);
+ printf("Memory: %lldKB\n", memsize() / 1024);
+
+ OF_getprop(chosen, "bootpath", bootpath, 64);
+ ch = strchr(bootpath, ':');
+ *ch = '\0';
+ printf("Booted from: %s\n", bootpath);
+
+ printf("\n");
+
+ /*
+ * Only parse the first bootarg if present. It should
+ * be simple to handle extra arguments
+ */
+ OF_getprop(chosen, "bootargs", bootargs, sizeof(bootargs));
+ bargc = 0;
+ parse(&bargc, &bargv, bootargs);
+ if (bargc == 1)
+ env_setenv("currdev", EV_VOLATILE, bargv[0], ofw_setcurrdev,
+ env_nounset);
+ else
+ env_setenv("currdev", EV_VOLATILE, bootpath,
+ ofw_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, bootpath, env_noset,
+ env_nounset);
+ setenv("LINES", "24", 1); /* optional */
+
+ archsw.arch_getdev = ofw_getdev;
+ archsw.arch_copyin = ofw_copyin;
+ archsw.arch_copyout = ofw_copyout;
+ archsw.arch_readin = ofw_readin;
+ archsw.arch_autoload = ofw_autoload;
+
+ interact(NULL); /* doesn't return */
+
+ OF_exit();
+
+ return 0;
+}
+
+COMMAND_SET(halt, "halt", "halt the system", command_halt);
+
+static int
+command_halt(int argc, char *argv[])
+{
+
+ OF_exit();
+ return (CMD_OK);
+}
+
+COMMAND_SET(memmap, "memmap", "print memory map", command_memmap);
+
+int
+command_memmap(int argc, char **argv)
+{
+
+ ofw_memmap(acells);
+ return (CMD_OK);
+}
diff --git a/stand/ofw/libofw/Makefile b/stand/ofw/libofw/Makefile
new file mode 100644
index 0000000..8e9ac80
--- /dev/null
+++ b/stand/ofw/libofw/Makefile
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+LIB= ofw
+INTERNALLIB=
+
+SRCS= devicename.c elf_freebsd.c ofw_console.c ofw_copy.c ofw_disk.c \
+ ofw_memory.c ofw_module.c ofw_net.c ofw_reboot.c \
+ ofw_time.c openfirm.c
+.PATH: ${ZFSSRC}
+SRCS+= devicename_stubs.c
+
+# Pick up the bootstrap header for some interface items
+CFLAGS+= -I${LDRSRC}
+
+CFLAGS+= -ffreestanding
+.if ${MACHINE_CPUARCH} == "powerpc"
+CFLAGS+= -msoft-float
+SRCS+= ppc64_elf_freebsd.c
+.endif
+
+.ifdef(BOOT_DISK_DEBUG)
+# Make the disk code more talkative
+CFLAGS+= -DDISK_DEBUG
+.endif
+
+.include <bsd.lib.mk>
diff --git a/stand/ofw/libofw/devicename.c b/stand/ofw/libofw/devicename.c
new file mode 100644
index 0000000..c9814b7
--- /dev/null
+++ b/stand/ofw/libofw/devicename.c
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "libofw.h"
+#include "../zfs/libzfs.h"
+
+static int ofw_parsedev(struct ofw_devdesc **, const char *, const char **);
+
+/*
+ * Point (dev) at an allocated device specifier for the device matching the
+ * path in (devspec). If it contains an explicit device specification,
+ * use that. If not, use the default device.
+ */
+int
+ofw_getdev(void **vdev, const char *devspec, const char **path)
+{
+ struct ofw_devdesc **dev = (struct ofw_devdesc **)vdev;
+ int rv;
+
+ /*
+ * If it looks like this is just a path and no
+ * device, go with the current device.
+ */
+ if ((devspec == NULL) ||
+ ((strchr(devspec, '@') == NULL) &&
+ (strchr(devspec, ':') == NULL))) {
+
+ if (((rv = ofw_parsedev(dev, getenv("currdev"), NULL)) == 0) &&
+ (path != NULL))
+ *path = devspec;
+ return(rv);
+ }
+
+ /*
+ * Try to parse the device name off the beginning of the devspec
+ */
+ return(ofw_parsedev(dev, devspec, path));
+}
+
+/*
+ * Point (dev) at an allocated device specifier matching the string version
+ * at the beginning of (devspec). Return a pointer to the remaining
+ * text in (path).
+ */
+static int
+ofw_parsedev(struct ofw_devdesc **dev, const char *devspec, const char **path)
+{
+ struct ofw_devdesc *idev;
+ struct devsw *dv;
+ phandle_t handle;
+ const char *p;
+ const char *s;
+ char *ep;
+ char name[256];
+ char type[64];
+ int err;
+ int len;
+ int i;
+
+ for (p = s = devspec; *s != '\0'; p = s) {
+ if ((s = strchr(p + 1, '/')) == NULL)
+ s = strchr(p, '\0');
+ len = s - devspec;
+ bcopy(devspec, name, len);
+ name[len] = '\0';
+ if ((handle = OF_finddevice(name)) == -1) {
+ bcopy(name, type, len);
+ type[len] = '\0';
+ } else if (OF_getprop(handle, "device_type", type, sizeof(type)) == -1)
+ continue;
+ for (i = 0; (dv = devsw[i]) != NULL; i++) {
+ if (strncmp(dv->dv_name, type, strlen(dv->dv_name)) == 0)
+ goto found;
+ }
+ }
+ return(ENOENT);
+
+found:
+ if (path != NULL)
+ *path = s;
+ idev = malloc(sizeof(struct ofw_devdesc));
+ if (idev == NULL) {
+ printf("ofw_parsedev: malloc failed\n");
+ return ENOMEM;
+ }
+ strcpy(idev->d_path, name);
+ idev->d_dev = dv;
+ idev->d_type = dv->dv_type;
+ if (idev->d_type == DEVT_ZFS) {
+ p = devspec + strlen(dv->dv_name);
+ err = zfs_parsedev((struct zfs_devdesc *)idev, p, path);
+ if (err != 0) {
+ free(idev);
+ return (err);
+ }
+ }
+
+ if (dev == NULL) {
+ free(idev);
+ } else {
+ *dev = idev;
+ }
+ return(0);
+}
+
+int
+ofw_setcurrdev(struct env_var *ev, int flags, const void *value)
+{
+ struct ofw_devdesc *ncurr;
+ int rv;
+
+ if ((rv = ofw_parsedev(&ncurr, value, NULL)) != 0)
+ return rv;
+
+ free(ncurr);
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return 0;
+}
diff --git a/stand/ofw/libofw/elf_freebsd.c b/stand/ofw/libofw/elf_freebsd.c
new file mode 100644
index 0000000..80ece7e
--- /dev/null
+++ b/stand/ofw/libofw/elf_freebsd.c
@@ -0,0 +1,104 @@
+/*-
+ * Copyright (c) 2001 Benno Rice <benno@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/linker.h>
+
+#include <machine/metadata.h>
+#include <machine/elf.h>
+
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "libofw.h"
+#include "openfirm.h"
+
+extern char end[];
+extern vm_offset_t reloc; /* From <arch>/conf.c */
+
+int
+__elfN(ofw_loadfile)(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ int r;
+
+ r = __elfN(loadfile)(filename, dest, result);
+ if (r != 0)
+ return (r);
+
+#if defined(__powerpc__)
+ /*
+ * No need to sync the icache for modules: this will
+ * be done by the kernel after relocation.
+ */
+ if (!strcmp((*result)->f_type, "elf kernel"))
+ __syncicache((void *) (*result)->f_addr, (*result)->f_size);
+#endif
+ return (0);
+}
+
+int
+__elfN(ofw_exec)(struct preloaded_file *fp)
+{
+ struct file_metadata *fmp;
+ vm_offset_t mdp, dtbp;
+ Elf_Ehdr *e;
+ int error;
+ intptr_t entry;
+
+ if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) {
+ return(EFTYPE);
+ }
+ e = (Elf_Ehdr *)&fmp->md_data;
+ entry = e->e_entry;
+
+ if ((error = md_load(fp->f_args, &mdp, &dtbp)) != 0)
+ return (error);
+
+ printf("Kernel entry at 0x%lx ...\n", e->e_entry);
+
+ dev_cleanup();
+ ofw_release_heap();
+ if (dtbp != 0) {
+ OF_quiesce();
+ ((int (*)(u_long, u_long, u_long, void *, u_long))entry)(dtbp, 0, 0,
+ mdp, sizeof(mdp));
+ } else {
+ OF_chain((void *)reloc, end - (char *)reloc, (void *)entry,
+ (void *)mdp, sizeof(mdp));
+ }
+
+ panic("exec returned");
+}
+
+struct file_format ofw_elf =
+{
+ __elfN(ofw_loadfile),
+ __elfN(ofw_exec)
+};
diff --git a/stand/ofw/libofw/libofw.h b/stand/ofw/libofw/libofw.h
new file mode 100644
index 0000000..87e9095
--- /dev/null
+++ b/stand/ofw/libofw/libofw.h
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (C) 2000 Benno Rice.
+ * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "openfirm.h"
+
+/* Note: Must match the 'struct devdesc' in bootstrap.h */
+struct ofw_devdesc {
+ struct devsw *d_dev;
+ int d_type;
+ int d_unit;
+ ihandle_t d_handle;
+ union {
+ char d_path[256];
+ struct {
+ uint64_t pool_guid;
+ uint64_t root_guid;
+ };
+ };
+};
+
+extern int ofw_getdev(void **vdev, const char *devspec, const char **path);
+extern ev_sethook_t ofw_setcurrdev;
+
+extern struct devsw ofwdisk;
+extern struct netif_driver ofwnet;
+
+int ofwn_getunit(const char *);
+
+ssize_t ofw_copyin(const void *src, vm_offset_t dest, const size_t len);
+ssize_t ofw_copyout(const vm_offset_t src, void *dest, const size_t len);
+ssize_t ofw_readin(const int fd, vm_offset_t dest, const size_t len);
+
+extern int ofw_boot(void);
+extern int ofw_autoload(void);
+
+void ofw_memmap(int);
+void *ofw_alloc_heap(unsigned int);
+void ofw_release_heap(void);
+
+struct preloaded_file;
+struct file_format;
+
+int ofw_elf_loadfile(char *, vm_offset_t, struct preloaded_file **);
+int ofw_elf_exec(struct preloaded_file *);
+
+extern struct file_format ofw_elf;
+#ifdef __powerpc__
+extern struct file_format ofw_elf64;
+#endif
+
+extern void reboot(void);
+
+struct ofw_reg
+{
+ cell_t base;
+ cell_t size;
+};
+
+struct ofw_reg2
+{
+ cell_t base_hi;
+ cell_t base_lo;
+ cell_t size;
+};
+
+extern int (*openfirmware)(void *);
diff --git a/stand/ofw/libofw/ofw_console.c b/stand/ofw/libofw/ofw_console.c
new file mode 100644
index 0000000..59ce9a5
--- /dev/null
+++ b/stand/ofw/libofw/ofw_console.c
@@ -0,0 +1,120 @@
+/* $NetBSD: prom.c,v 1.3 1997/09/06 14:03:58 drochner Exp $ */
+
+/*-
+ * Mach Operating System
+ * Copyright (c) 1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include "bootstrap.h"
+#include "openfirm.h"
+
+static void ofw_cons_probe(struct console *cp);
+static int ofw_cons_init(int);
+void ofw_cons_putchar(int);
+int ofw_cons_getchar(void);
+int ofw_cons_poll(void);
+
+static ihandle_t stdin;
+static ihandle_t stdout;
+
+struct console ofwconsole = {
+ "ofw",
+ "Open Firmware console",
+ 0,
+ ofw_cons_probe,
+ ofw_cons_init,
+ ofw_cons_putchar,
+ ofw_cons_getchar,
+ ofw_cons_poll,
+};
+
+static void
+ofw_cons_probe(struct console *cp)
+{
+
+ OF_getprop(chosen, "stdin", &stdin, sizeof(stdin));
+ OF_getprop(chosen, "stdout", &stdout, sizeof(stdout));
+ cp->c_flags |= C_PRESENTIN|C_PRESENTOUT;
+}
+
+static int
+ofw_cons_init(int arg)
+{
+ return 0;
+}
+
+void
+ofw_cons_putchar(int c)
+{
+ char cbuf;
+
+ if (c == '\n') {
+ cbuf = '\r';
+ OF_write(stdout, &cbuf, 1);
+ }
+
+ cbuf = c;
+ OF_write(stdout, &cbuf, 1);
+}
+
+static int saved_char = -1;
+
+int
+ofw_cons_getchar()
+{
+ unsigned char ch = '\0';
+ int l;
+
+ if (saved_char != -1) {
+ l = saved_char;
+ saved_char = -1;
+ return l;
+ }
+
+ if (OF_read(stdin, &ch, 1) > 0)
+ return (ch);
+
+ return (-1);
+}
+
+int
+ofw_cons_poll()
+{
+ unsigned char ch;
+
+ if (saved_char != -1)
+ return 1;
+
+ if (OF_read(stdin, &ch, 1) > 0) {
+ saved_char = ch;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/stand/ofw/libofw/ofw_copy.c b/stand/ofw/libofw/ofw_copy.c
new file mode 100644
index 0000000..ffd6987
--- /dev/null
+++ b/stand/ofw/libofw/ofw_copy.c
@@ -0,0 +1,173 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * MD primitives supporting placement of module data
+ *
+ * XXX should check load address/size against memory top.
+ */
+#include <stand.h>
+
+#include "libofw.h"
+
+#define READIN_BUF (4 * 1024)
+#define PAGE_SIZE 0x1000
+#define PAGE_MASK 0x0fff
+#define MAPMEM_PAGE_INC 16
+
+
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+
+static int
+ofw_mapmem(vm_offset_t dest, const size_t len)
+{
+ void *destp, *addr;
+ size_t dlen;
+ size_t resid;
+ size_t nlen;
+ static vm_offset_t last_dest = 0;
+ static size_t last_len = 0;
+
+ nlen = len;
+ /*
+ * Check to see if this region fits in a prior mapping.
+ * Allocations are generally sequential, so only check
+ * the last one.
+ */
+ if (dest >= last_dest &&
+ (dest + len) <= (last_dest + last_len)) {
+ return (0);
+ }
+
+ /*
+ * Trim area covered by existing mapping, if any
+ */
+ if (dest < (last_dest + last_len) && dest >= last_dest) {
+ nlen -= (last_dest + last_len) - dest;
+ dest = last_dest + last_len;
+ }
+
+ destp = (void *)(dest & ~PAGE_MASK);
+ resid = dest & PAGE_MASK;
+
+ /*
+ * To avoid repeated mappings on small allocations,
+ * never map anything less than MAPMEM_PAGE_INC pages at a time
+ */
+ if ((nlen + resid) < PAGE_SIZE*MAPMEM_PAGE_INC) {
+ dlen = PAGE_SIZE*MAPMEM_PAGE_INC;
+ } else
+ dlen = roundup(nlen + resid, PAGE_SIZE);
+
+ if (OF_call_method("claim", memory, 3, 1, destp, dlen, 0, &addr)
+ == -1) {
+ printf("ofw_mapmem: physical claim failed\n");
+ return (ENOMEM);
+ }
+
+ /*
+ * We only do virtual memory management when real_mode is false.
+ */
+ if (real_mode == 0) {
+ if (OF_call_method("claim", mmu, 3, 1, destp, dlen, 0, &addr)
+ == -1) {
+ printf("ofw_mapmem: virtual claim failed\n");
+ return (ENOMEM);
+ }
+
+ if (OF_call_method("map", mmu, 4, 0, destp, destp, dlen, 0)
+ == -1) {
+ printf("ofw_mapmem: map failed\n");
+ return (ENOMEM);
+ }
+ }
+ last_dest = (vm_offset_t) destp;
+ last_len = dlen;
+
+ return (0);
+}
+
+ssize_t
+ofw_copyin(const void *src, vm_offset_t dest, const size_t len)
+{
+ if (ofw_mapmem(dest, len)) {
+ printf("ofw_copyin: map error\n");
+ return (0);
+ }
+
+ bcopy(src, (void *)dest, len);
+ return(len);
+}
+
+ssize_t
+ofw_copyout(const vm_offset_t src, void *dest, const size_t len)
+{
+ bcopy((void *)src, dest, len);
+ return(len);
+}
+
+ssize_t
+ofw_readin(const int fd, vm_offset_t dest, const size_t len)
+{
+ void *buf;
+ size_t resid, chunk, get;
+ ssize_t got;
+ vm_offset_t p;
+
+ p = dest;
+
+ chunk = min(READIN_BUF, len);
+ buf = malloc(chunk);
+ if (buf == NULL) {
+ printf("ofw_readin: buf malloc failed\n");
+ return(0);
+ }
+
+ if (ofw_mapmem(dest, len)) {
+ printf("ofw_readin: map error\n");
+ free(buf);
+ return (0);
+ }
+
+ for (resid = len; resid > 0; resid -= got, p += got) {
+ get = min(chunk, resid);
+ got = read(fd, buf, get);
+
+ if (got <= 0) {
+ if (got < 0)
+ printf("ofw_readin: read failed\n");
+ break;
+ }
+
+ bcopy(buf, (void *)p, got);
+ }
+
+ free(buf);
+ return(len - resid);
+}
diff --git a/stand/ofw/libofw/ofw_disk.c b/stand/ofw/libofw/ofw_disk.c
new file mode 100644
index 0000000..2c3bd56
--- /dev/null
+++ b/stand/ofw/libofw/ofw_disk.c
@@ -0,0 +1,184 @@
+/*-
+ * Copyright (C) 2000 Benno Rice.
+ * 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 Benno Rice ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Disk I/O routines using Open Firmware
+ */
+
+#include <sys/param.h>
+
+#include <netinet/in.h>
+
+#include <machine/stdarg.h>
+
+#include <stand.h>
+#include <sys/disk.h>
+
+#include "bootstrap.h"
+#include "libofw.h"
+
+static int ofwd_init(void);
+static int ofwd_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int ofwd_open(struct open_file *f, ...);
+static int ofwd_close(struct open_file *f);
+static int ofwd_ioctl(struct open_file *f, u_long cmd, void *data);
+static int ofwd_print(int verbose);
+
+struct devsw ofwdisk = {
+ "block",
+ DEVT_DISK,
+ ofwd_init,
+ ofwd_strategy,
+ ofwd_open,
+ ofwd_close,
+ ofwd_ioctl,
+ ofwd_print
+};
+
+/*
+ * We're not guaranteed to be able to open a device more than once and there
+ * is no OFW standard method to determine whether a device is already opened.
+ * Opening a device multiple times simultaneously happens to work with most
+ * OFW block device drivers but triggers a trap with at least the driver for
+ * the on-board controllers of Sun Fire V100 and Ultra 1. Upper layers and MI
+ * code expect to be able to open a device more than once however. Given that
+ * different partitions of the same device might be opened at the same time as
+ * done by ZFS, we can't generally just keep track of the opened devices and
+ * reuse the instance handle when asked to open an already opened device. So
+ * the best we can do is to cache the lastly used device path and close and
+ * open devices in ofwd_strategy() as needed.
+ */
+static struct ofw_devdesc *kdp;
+
+static int
+ofwd_init(void)
+{
+
+ return (0);
+}
+
+static int
+ofwd_strategy(void *devdata, int flag __unused, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct ofw_devdesc *dp = (struct ofw_devdesc *)devdata;
+ daddr_t pos;
+ int n;
+
+ if (dp != kdp) {
+ if (kdp != NULL) {
+#if !defined(__powerpc__)
+ OF_close(kdp->d_handle);
+#endif
+ kdp = NULL;
+ }
+ if ((dp->d_handle = OF_open(dp->d_path)) == -1)
+ return (ENOENT);
+ kdp = dp;
+ }
+
+ pos = dblk * 512;
+ do {
+ if (OF_seek(dp->d_handle, pos) < 0)
+ return (EIO);
+ n = OF_read(dp->d_handle, buf, size);
+ if (n < 0 && n != -2)
+ return (EIO);
+ } while (n == -2);
+ *rsize = size;
+ return (0);
+}
+
+static int
+ofwd_open(struct open_file *f, ...)
+{
+ struct ofw_devdesc *dp;
+ va_list vl;
+
+ va_start(vl, f);
+ dp = va_arg(vl, struct ofw_devdesc *);
+ va_end(vl);
+
+ if (dp != kdp) {
+ if (kdp != NULL) {
+ OF_close(kdp->d_handle);
+ kdp = NULL;
+ }
+ if ((dp->d_handle = OF_open(dp->d_path)) == -1) {
+ printf("%s: Could not open %s\n", __func__,
+ dp->d_path);
+ return (ENOENT);
+ }
+ kdp = dp;
+ }
+ return (0);
+}
+
+static int
+ofwd_close(struct open_file *f)
+{
+ struct ofw_devdesc *dev = f->f_devdata;
+
+ if (dev == kdp) {
+#if !defined(__powerpc__)
+ OF_close(dev->d_handle);
+#endif
+ kdp = NULL;
+ }
+ return (0);
+}
+
+static int
+ofwd_ioctl(struct open_file *f, u_long cmd, void *data)
+{
+ struct ofw_devdesc *dev = f->f_devdata;
+ int block_size;
+ unsigned int n;
+
+ switch (cmd) {
+ case DIOCGSECTORSIZE:
+ block_size = OF_block_size(dev->d_handle);
+ *(u_int *)data = block_size;
+ break;
+ case DIOCGMEDIASIZE:
+ block_size = OF_block_size(dev->d_handle);
+ n = OF_blocks(dev->d_handle);
+ *(uint64_t *)data = (uint64_t)(n * block_size);
+ break;
+ default:
+ return (ENOTTY);
+ }
+ return (0);
+}
+
+static int
+ofwd_print(int verbose __unused)
+{
+ return (0);
+}
diff --git a/stand/ofw/libofw/ofw_memory.c b/stand/ofw/libofw/ofw_memory.c
new file mode 100644
index 0000000..5616184
--- /dev/null
+++ b/stand/ofw/libofw/ofw_memory.c
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 2001 Benno Rice
+ * 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 Benno Rice ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <stand.h>
+
+#include "libofw.h"
+#include "openfirm.h"
+
+static void *heap_base = NULL;
+static unsigned int heap_size = 0;
+
+struct ofw_mapping {
+ vm_offset_t va;
+ int len;
+ vm_offset_t pa;
+ int mode;
+};
+
+struct ofw_mapping2 {
+ vm_offset_t va;
+ int len;
+ vm_offset_t pa_hi;
+ vm_offset_t pa_lo;
+ int mode;
+};
+
+void
+ofw_memmap(int acells)
+{
+ struct ofw_mapping *mapptr;
+ struct ofw_mapping2 *mapptr2;
+ phandle_t mmup;
+ int nmapping, i;
+ u_char mappings[256 * sizeof(struct ofw_mapping2)];
+ char lbuf[80];
+
+ mmup = OF_instance_to_package(mmu);
+
+ bzero(mappings, sizeof(mappings));
+
+ nmapping = OF_getprop(mmup, "translations", mappings, sizeof(mappings));
+ if (nmapping == -1) {
+ printf("Could not get memory map (%d)\n",
+ nmapping);
+ return;
+ }
+
+ pager_open();
+ if (acells == 1) {
+ nmapping /= sizeof(struct ofw_mapping);
+ mapptr = (struct ofw_mapping *) mappings;
+
+ printf("%17s\t%17s\t%8s\t%6s\n", "Virtual Range",
+ "Physical Range", "#Pages", "Mode");
+
+ for (i = 0; i < nmapping; i++) {
+ sprintf(lbuf, "%08x-%08x\t%08x-%08x\t%8d\t%6x\n",
+ mapptr[i].va,
+ mapptr[i].va + mapptr[i].len,
+ mapptr[i].pa,
+ mapptr[i].pa + mapptr[i].len,
+ mapptr[i].len / 0x1000,
+ mapptr[i].mode);
+ if (pager_output(lbuf))
+ break;
+ }
+ } else {
+ nmapping /= sizeof(struct ofw_mapping2);
+ mapptr2 = (struct ofw_mapping2 *) mappings;
+
+ printf("%17s\t%17s\t%8s\t%6s\n", "Virtual Range",
+ "Physical Range", "#Pages", "Mode");
+
+ for (i = 0; i < nmapping; i++) {
+ sprintf(lbuf, "%08x-%08x\t%08x-%08x\t%8d\t%6x\n",
+ mapptr2[i].va,
+ mapptr2[i].va + mapptr2[i].len,
+ mapptr2[i].pa_lo,
+ mapptr2[i].pa_lo + mapptr2[i].len,
+ mapptr2[i].len / 0x1000,
+ mapptr2[i].mode);
+ if (pager_output(lbuf))
+ break;
+ }
+ }
+ pager_close();
+}
+
+void *
+ofw_alloc_heap(unsigned int size)
+{
+ phandle_t memoryp, root;
+ cell_t available[4];
+ cell_t acells;
+
+ root = OF_finddevice("/");
+ acells = 1;
+ OF_getprop(root, "#address-cells", &acells, sizeof(acells));
+
+ memoryp = OF_instance_to_package(memory);
+ OF_getprop(memoryp, "available", available, sizeof(available));
+
+ heap_base = OF_claim((void *)available[acells-1], size,
+ sizeof(register_t));
+
+ if (heap_base != (void *)-1) {
+ heap_size = size;
+ }
+
+ return (heap_base);
+}
+
+void
+ofw_release_heap(void)
+{
+ OF_release(heap_base, heap_size);
+}
diff --git a/stand/ofw/libofw/ofw_module.c b/stand/ofw/libofw/ofw_module.c
new file mode 100644
index 0000000..d39a343
--- /dev/null
+++ b/stand/ofw/libofw/ofw_module.c
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ofw-specific module functionality.
+ *
+ */
+
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+#include "libofw.h"
+
+/*
+ * Use voodoo to load modules required by current hardware.
+ */
+int
+ofw_autoload(void)
+{
+ /* XXX Call some machdep autoload routine? */
+ return(0);
+}
diff --git a/stand/ofw/libofw/ofw_net.c b/stand/ofw/libofw/ofw_net.c
new file mode 100644
index 0000000..ea5e47e
--- /dev/null
+++ b/stand/ofw/libofw/ofw_net.c
@@ -0,0 +1,275 @@
+/*-
+ * Copyright (c) 2000-2001 Benno Rice
+ * 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 Benno Rice ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+
+#include <stand.h>
+#include <net.h>
+#include <netif.h>
+
+#include "openfirm.h"
+
+static int ofwn_probe(struct netif *, void *);
+static int ofwn_match(struct netif *, void *);
+static void ofwn_init(struct iodesc *, void *);
+static ssize_t ofwn_get(struct iodesc *, void **, time_t);
+static ssize_t ofwn_put(struct iodesc *, void *, size_t);
+static void ofwn_end(struct netif *);
+
+extern struct netif_stats ofwn_stats[];
+
+struct netif_dif ofwn_ifs[] = {
+ /* dif_unit dif_nsel dif_stats dif_private */
+ { 0, 1, &ofwn_stats[0], 0, },
+};
+
+struct netif_stats ofwn_stats[nitems(ofwn_ifs)];
+
+struct netif_driver ofwnet = {
+ "net", /* netif_bname */
+ ofwn_match, /* netif_match */
+ ofwn_probe, /* netif_probe */
+ ofwn_init, /* netif_init */
+ ofwn_get, /* netif_get */
+ ofwn_put, /* netif_put */
+ ofwn_end, /* netif_end */
+ ofwn_ifs, /* netif_ifs */
+ nitems(ofwn_ifs) /* netif_nifs */
+};
+
+static ihandle_t netinstance;
+
+static void *dmabuf;
+
+static int
+ofwn_match(struct netif *nif, void *machdep_hint)
+{
+ return 1;
+}
+
+static int
+ofwn_probe(struct netif *nif, void *machdep_hint)
+{
+ return 0;
+}
+
+static ssize_t
+ofwn_put(struct iodesc *desc, void *pkt, size_t len)
+{
+ size_t sendlen;
+ ssize_t rv;
+
+#if defined(NETIF_DEBUG)
+ struct ether_header *eh;
+ printf("netif_put: desc=0x%x pkt=0x%x len=%d\n", desc, pkt, len);
+ eh = pkt;
+ printf("dst: %s ", ether_sprintf(eh->ether_dhost));
+ printf("src: %s ", ether_sprintf(eh->ether_shost));
+ printf("type: 0x%x\n", eh->ether_type & 0xffff);
+#endif
+
+ sendlen = len;
+ if (sendlen < 60) {
+ sendlen = 60;
+#if defined(NETIF_DEBUG)
+ printf("netif_put: length padded to %d\n", sendlen);
+#endif
+ }
+
+ if (dmabuf) {
+ bcopy(pkt, dmabuf, sendlen);
+ pkt = dmabuf;
+ }
+
+ rv = OF_write(netinstance, pkt, len);
+
+#if defined(NETIF_DEBUG)
+ printf("netif_put: OF_write returned %d\n", rv);
+#endif
+
+ return rv;
+}
+
+static ssize_t
+ofwn_get(struct iodesc *desc, void **pkt, time_t timeout)
+{
+ time_t t;
+ ssize_t length;
+ size_t len;
+ char *buf, *ptr;
+
+#if defined(NETIF_DEBUG)
+ printf("netif_get: pkt=%p, timeout=%d\n", pkt, timeout);
+#endif
+
+ /*
+ * We should read the "max-frame-size" int property instead,
+ * but at this time the iodesc does not have mtu, so we will take
+ * a small shortcut here.
+ */
+ len = ETHER_MAX_LEN;
+ buf = malloc(len + ETHER_ALIGN);
+ if (buf == NULL)
+ return (-1);
+ ptr = buf + ETHER_ALIGN;
+
+ t = getsecs();
+ do {
+ length = OF_read(netinstance, ptr, len);
+ } while ((length == -2 || length == 0) &&
+ (getsecs() - t < timeout));
+
+#if defined(NETIF_DEBUG)
+ printf("netif_get: received length=%d (%x)\n", length, length);
+#endif
+
+ if (length < 12) {
+ free(buf);
+ return (-1);
+ }
+
+#if defined(NETIF_VERBOSE_DEBUG)
+ {
+ char *ch = ptr;
+ int i;
+
+ for(i = 0; i < 96; i += 4) {
+ printf("%02x%02x%02x%02x ", ch[i], ch[i+1],
+ ch[i+2], ch[i+3]);
+ }
+ printf("\n");
+ }
+#endif
+
+#if defined(NETIF_DEBUG)
+ {
+ struct ether_header *eh = ptr;
+
+ printf("dst: %s ", ether_sprintf(eh->ether_dhost));
+ printf("src: %s ", ether_sprintf(eh->ether_shost));
+ printf("type: 0x%x\n", eh->ether_type & 0xffff);
+ }
+#endif
+
+ *pkt = buf;
+ return (length);
+}
+
+extern char *strchr();
+
+static void
+ofwn_init(struct iodesc *desc, void *machdep_hint)
+{
+ phandle_t netdev;
+ char path[64];
+ char *ch;
+ int pathlen;
+
+ pathlen = OF_getprop(chosen, "bootpath", path, 64);
+ if ((ch = strchr(path, ':')) != NULL)
+ *ch = '\0';
+ netdev = OF_finddevice(path);
+#ifdef __sparc64__
+ if (OF_getprop(netdev, "mac-address", desc->myea, 6) == -1)
+#else
+ if (OF_getprop(netdev, "local-mac-address", desc->myea, 6) == -1)
+#endif
+ goto punt;
+
+ printf("boot: ethernet address: %s\n", ether_sprintf(desc->myea));
+
+ if ((netinstance = OF_open(path)) == -1) {
+ printf("Could not open network device.\n");
+ goto punt;
+ }
+
+#if defined(NETIF_DEBUG)
+ printf("ofwn_init: Open Firmware instance handle: %08x\n", netinstance);
+#endif
+
+#ifndef __sparc64__
+ dmabuf = NULL;
+ if (OF_call_method("dma-alloc", netinstance, 1, 1, (64 * 1024), &dmabuf)
+ < 0) {
+ printf("Failed to allocate DMA buffer (got %08x).\n", dmabuf);
+ goto punt;
+ }
+
+#if defined(NETIF_DEBUG)
+ printf("ofwn_init: allocated DMA buffer: %08x\n", dmabuf);
+#endif
+#endif
+
+ return;
+
+punt:
+ printf("\n");
+ printf("Could not boot from %s.\n", path);
+ OF_enter();
+}
+
+static void
+ofwn_end(struct netif *nif)
+{
+#ifdef BROKEN
+ /* dma-free freezes at least some Apple ethernet controllers */
+ OF_call_method("dma-free", netinstance, 2, 0, dmabuf, MAXPHYS);
+#endif
+ OF_close(netinstance);
+}
+
+#if 0
+int
+ofwn_getunit(const char *path)
+{
+ int i;
+ char newpath[255];
+
+ OF_canon(path, newpath, 254);
+
+ for (i = 0; i < nofwninfo; i++) {
+ printf(">>> test =\t%s\n", ofwninfo[i].ofwn_path);
+ if (strcmp(path, ofwninfo[i].ofwn_path) == 0)
+ return i;
+
+ if (strcmp(newpath, ofwninfo[i].ofwn_path) == 0)
+ return i;
+ }
+
+ return -1;
+}
+#endif
diff --git a/stand/ofw/libofw/ofw_reboot.c b/stand/ofw/libofw/ofw_reboot.c
new file mode 100644
index 0000000..bbb420a
--- /dev/null
+++ b/stand/ofw/libofw/ofw_reboot.c
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 2000 Benno Rice
+ * 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 Benno Rice ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "openfirm.h"
+
+void
+exit(int code)
+{
+ OF_exit();
+}
diff --git a/stand/ofw/libofw/ofw_time.c b/stand/ofw/libofw/ofw_time.c
new file mode 100644
index 0000000..d5ca271
--- /dev/null
+++ b/stand/ofw/libofw/ofw_time.c
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 2000 Benno Rice
+ * 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 Benno Rice ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "openfirm.h"
+
+time_t
+time(time_t *tloc)
+{
+ int secs;
+
+ secs = OF_milliseconds() / 1000;
+ if (tloc)
+ *tloc = secs;
+ return secs;
+}
+
+time_t
+getsecs(void)
+{
+ time_t n = 0;
+ time(&n);
+ return n;
+}
+
+void
+delay(int usecs)
+{
+ int msecs, start;
+
+ msecs = usecs / 1000;
+ start = OF_milliseconds();
+
+ while (OF_milliseconds() - start < msecs);
+}
diff --git a/stand/ofw/libofw/openfirm.c b/stand/ofw/libofw/openfirm.c
new file mode 100644
index 0000000..1ccae60
--- /dev/null
+++ b/stand/ofw/libofw/openfirm.c
@@ -0,0 +1,832 @@
+/* $NetBSD: Locore.c,v 1.7 2000/08/20 07:04:59 tsubai Exp $ */
+
+/*-
+ * Copyright (C) 1995, 1996 Wolfgang Solfrank.
+ * Copyright (C) 1995, 1996 TooLs GmbH.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*-
+ * Copyright (C) 2000 Benno Rice.
+ * 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 Benno Rice ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <machine/stdarg.h>
+
+#include <stand.h>
+
+#include "openfirm.h"
+
+int (*openfirmware)(void *);
+
+phandle_t chosen;
+ihandle_t mmu;
+ihandle_t memory;
+int real_mode = 0;
+
+/* Initialiser */
+
+void
+OF_init(int (*openfirm)(void *))
+{
+ phandle_t options;
+ char mode[sizeof("true")];
+
+ openfirmware = openfirm;
+
+ if ((chosen = OF_finddevice("/chosen")) == -1)
+ OF_exit();
+ if (OF_getprop(chosen, "memory", &memory, sizeof(memory)) == -1) {
+ memory = OF_open("/memory");
+ if (memory == -1)
+ memory = OF_open("/memory@0");
+ if (memory == -1)
+ OF_exit();
+ }
+ if (OF_getprop(chosen, "mmu", &mmu, sizeof(mmu)) == -1)
+ OF_exit();
+
+ /*
+ * Check if we run in real mode. If so, we do not need to map
+ * memory later on.
+ */
+ options = OF_finddevice("/options");
+ if (OF_getprop(options, "real-mode?", mode, sizeof(mode)) > 0 &&
+ strcmp(mode, "true") == 0)
+ real_mode = 1;
+}
+
+/*
+ * Generic functions
+ */
+
+/* Test to see if a service exists. */
+int
+OF_test(char *name)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t service;
+ cell_t missing;
+ } args = {
+ (cell_t)"test",
+ 1,
+ 1,
+ };
+
+ args.service = (cell_t)name;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.missing);
+}
+
+/* Return firmware millisecond count. */
+int
+OF_milliseconds()
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t ms;
+ } args = {
+ (cell_t)"milliseconds",
+ 0,
+ 1,
+ };
+
+ openfirmware(&args);
+ return (args.ms);
+}
+
+/*
+ * Device tree functions
+ */
+
+/* Return the next sibling of this node or 0. */
+phandle_t
+OF_peer(phandle_t node)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t node;
+ cell_t next;
+ } args = {
+ (cell_t)"peer",
+ 1,
+ 1,
+ };
+
+ args.node = node;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.next);
+}
+
+/* Return the first child of this node or 0. */
+phandle_t
+OF_child(phandle_t node)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t node;
+ cell_t child;
+ } args = {
+ (cell_t)"child",
+ 1,
+ 1,
+ };
+
+ args.node = node;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.child);
+}
+
+/* Return the parent of this node or 0. */
+phandle_t
+OF_parent(phandle_t node)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t node;
+ cell_t parent;
+ } args = {
+ (cell_t)"parent",
+ 1,
+ 1,
+ };
+
+ args.node = node;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.parent);
+}
+
+/* Return the package handle that corresponds to an instance handle. */
+phandle_t
+OF_instance_to_package(ihandle_t instance)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t instance;
+ cell_t package;
+ } args = {
+ (cell_t)"instance-to-package",
+ 1,
+ 1,
+ };
+
+ args.instance = instance;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.package);
+}
+
+/* Get the length of a property of a package. */
+int
+OF_getproplen(phandle_t package, char *propname)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t package;
+ cell_t propname;
+ cell_t proplen;
+ } args = {
+ (cell_t)"getproplen",
+ 2,
+ 1,
+ };
+
+ args.package = package;
+ args.propname = (cell_t)propname;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.proplen);
+}
+
+/* Get the value of a property of a package. */
+int
+OF_getprop(phandle_t package, char *propname, void *buf, int buflen)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t package;
+ cell_t propname;
+ cell_t buf;
+ cell_t buflen;
+ cell_t size;
+ } args = {
+ (cell_t)"getprop",
+ 4,
+ 1,
+ };
+
+ args.package = package;
+ args.propname = (cell_t)propname;
+ args.buf = (cell_t)buf;
+ args.buflen = buflen;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.size);
+}
+
+/* Get the next property of a package. */
+int
+OF_nextprop(phandle_t package, char *previous, char *buf)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t package;
+ cell_t previous;
+ cell_t buf;
+ cell_t flag;
+ } args = {
+ (cell_t)"nextprop",
+ 3,
+ 1,
+ };
+
+ args.package = package;
+ args.previous = (cell_t)previous;
+ args.buf = (cell_t)buf;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.flag);
+}
+
+/* Set the value of a property of a package. */
+/* XXX Has a bug on FirePower */
+int
+OF_setprop(phandle_t package, char *propname, void *buf, int len)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t package;
+ cell_t propname;
+ cell_t buf;
+ cell_t len;
+ cell_t size;
+ } args = {
+ (cell_t)"setprop",
+ 4,
+ 1,
+ };
+
+ args.package = package;
+ args.propname = (cell_t)propname;
+ args.buf = (cell_t)buf;
+ args.len = len;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.size);
+}
+
+/* Convert a device specifier to a fully qualified pathname. */
+int
+OF_canon(const char *device, char *buf, int len)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t device;
+ cell_t buf;
+ cell_t len;
+ cell_t size;
+ } args = {
+ (cell_t)"canon",
+ 3,
+ 1,
+ };
+
+ args.device = (cell_t)device;
+ args.buf = (cell_t)buf;
+ args.len = len;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.size);
+}
+
+/* Return a package handle for the specified device. */
+phandle_t
+OF_finddevice(const char *device)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t device;
+ cell_t package;
+ } args = {
+ (cell_t)"finddevice",
+ 1,
+ 1,
+ };
+
+ args.device = (cell_t)device;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.package);
+}
+
+/* Return the fully qualified pathname corresponding to an instance. */
+int
+OF_instance_to_path(ihandle_t instance, char *buf, int len)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t instance;
+ cell_t buf;
+ cell_t len;
+ cell_t size;
+ } args = {
+ (cell_t)"instance-to-path",
+ 3,
+ 1,
+ };
+
+ args.instance = instance;
+ args.buf = (cell_t)buf;
+ args.len = len;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.size);
+}
+
+/* Return the fully qualified pathname corresponding to a package. */
+int
+OF_package_to_path(phandle_t package, char *buf, int len)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t package;
+ cell_t buf;
+ cell_t len;
+ cell_t size;
+ } args = {
+ (cell_t)"package-to-path",
+ 3,
+ 1,
+ };
+
+ args.package = package;
+ args.buf = (cell_t)buf;
+ args.len = len;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.size);
+}
+
+/* Call the method in the scope of a given instance. */
+int
+OF_call_method(char *method, ihandle_t instance, int nargs, int nreturns, ...)
+{
+ va_list ap;
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t method;
+ cell_t instance;
+ cell_t args_n_results[12];
+ } args = {
+ (cell_t)"call-method",
+ 2,
+ 1,
+ };
+ cell_t *cp;
+ int n;
+
+ if (nargs > 6)
+ return (-1);
+ args.nargs = nargs + 2;
+ args.nreturns = nreturns + 1;
+ args.method = (cell_t)method;
+ args.instance = instance;
+ va_start(ap, nreturns);
+ for (cp = (cell_t *)(args.args_n_results + (n = nargs)); --n >= 0;)
+ *--cp = va_arg(ap, cell_t);
+ if (openfirmware(&args) == -1)
+ return (-1);
+ if (args.args_n_results[nargs])
+ return (args.args_n_results[nargs]);
+ for (cp = (cell_t *)(args.args_n_results + nargs + (n = args.nreturns));
+ --n > 0;)
+ *va_arg(ap, cell_t *) = *--cp;
+ va_end(ap);
+ return (0);
+}
+
+/*
+ * Device I/O functions
+ */
+
+/* Open an instance for a device. */
+ihandle_t
+OF_open(char *device)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t device;
+ cell_t instance;
+ } args = {
+ (cell_t)"open",
+ 1,
+ 1,
+ };
+
+ args.device = (cell_t)device;
+ if (openfirmware(&args) == -1 || args.instance == 0) {
+ return (-1);
+ }
+ return (args.instance);
+}
+
+/* Close an instance. */
+void
+OF_close(ihandle_t instance)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t instance;
+ } args = {
+ (cell_t)"close",
+ 1,
+ };
+
+ args.instance = instance;
+ openfirmware(&args);
+}
+
+/* Read from an instance. */
+int
+OF_read(ihandle_t instance, void *addr, int len)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t instance;
+ cell_t addr;
+ cell_t len;
+ cell_t actual;
+ } args = {
+ (cell_t)"read",
+ 3,
+ 1,
+ };
+
+ args.instance = instance;
+ args.addr = (cell_t)addr;
+ args.len = len;
+
+#if defined(OPENFIRM_DEBUG)
+ printf("OF_read: called with instance=%08x, addr=%p, len=%d\n",
+ args.instance, args.addr, args.len);
+#endif
+
+ if (openfirmware(&args) == -1)
+ return (-1);
+
+#if defined(OPENFIRM_DEBUG)
+ printf("OF_read: returning instance=%d, addr=%p, len=%d, actual=%d\n",
+ args.instance, args.addr, args.len, args.actual);
+#endif
+
+ return (args.actual);
+}
+
+/* Write to an instance. */
+int
+OF_write(ihandle_t instance, void *addr, int len)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t instance;
+ cell_t addr;
+ cell_t len;
+ cell_t actual;
+ } args = {
+ (cell_t)"write",
+ 3,
+ 1,
+ };
+
+ args.instance = instance;
+ args.addr = (cell_t)addr;
+ args.len = len;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.actual);
+}
+
+/* Seek to a position. */
+int
+OF_seek(ihandle_t instance, u_int64_t pos)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t instance;
+ cell_t poshi;
+ cell_t poslo;
+ cell_t status;
+ } args = {
+ (cell_t)"seek",
+ 3,
+ 1,
+ };
+
+ args.instance = instance;
+ args.poshi = pos >> 32;
+ args.poslo = pos;
+ if (openfirmware(&args) == -1)
+ return (-1);
+ return (args.status);
+}
+
+/* Blocks. */
+unsigned int
+OF_blocks(ihandle_t instance)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t instance;
+ cell_t result;
+ cell_t blocks;
+ } args = {
+ (cell_t)"#blocks",
+ 2,
+ 1,
+ };
+
+ args.instance = instance;
+ if (openfirmware(&args) == -1)
+ return ((unsigned int)-1);
+ return (args.blocks);
+}
+
+/* Block size. */
+int
+OF_block_size(ihandle_t instance)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t instance;
+ cell_t result;
+ cell_t size;
+ } args = {
+ (cell_t)"block-size",
+ 2,
+ 1,
+ };
+
+ args.instance = instance;
+ if (openfirmware(&args) == -1)
+ return (512);
+ return (args.size);
+}
+
+/*
+/*
+ * Memory functions
+ */
+
+/* Claim an area of memory. */
+void *
+OF_claim(void *virt, u_int size, u_int align)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t virt;
+ cell_t size;
+ cell_t align;
+ cell_t baseaddr;
+ } args = {
+ (cell_t)"claim",
+ 3,
+ 1,
+ };
+
+ args.virt = (cell_t)virt;
+ args.size = size;
+ args.align = align;
+ if (openfirmware(&args) == -1)
+ return ((void *)-1);
+ return ((void *)args.baseaddr);
+}
+
+/* Release an area of memory. */
+void
+OF_release(void *virt, u_int size)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t virt;
+ cell_t size;
+ } args = {
+ (cell_t)"release",
+ 2,
+ };
+
+ args.virt = (cell_t)virt;
+ args.size = size;
+ openfirmware(&args);
+}
+
+/*
+ * Control transfer functions
+ */
+
+/* Reset the system and call "boot <bootspec>". */
+void
+OF_boot(char *bootspec)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t bootspec;
+ } args = {
+ (cell_t)"boot",
+ 1,
+ };
+
+ args.bootspec = (cell_t)bootspec;
+ openfirmware(&args);
+ for (;;) /* just in case */
+ ;
+}
+
+/* Suspend and drop back to the Open Firmware interface. */
+void
+OF_enter()
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ } args = {
+ (cell_t)"enter",
+ };
+
+ openfirmware(&args);
+ /* We may come back. */
+}
+
+/* Shut down and drop back to the Open Firmware interface. */
+void
+OF_exit()
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ } args = {
+ (cell_t)"exit",
+ };
+
+ openfirmware(&args);
+ for (;;) /* just in case */
+ ;
+}
+
+void
+OF_quiesce()
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ } args = {
+ (cell_t)"quiesce",
+ };
+
+ openfirmware(&args);
+}
+
+/* Free <size> bytes starting at <virt>, then call <entry> with <arg>. */
+#if 0
+void
+OF_chain(void *virt, u_int size, void (*entry)(), void *arg, u_int len)
+{
+ static struct {
+ cell_t name;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t virt;
+ cell_t size;
+ cell_t entry;
+ cell_t arg;
+ cell_t len;
+ } args = {
+ (cell_t)"chain",
+ 5,
+ };
+
+ args.virt = (cell_t)virt;
+ args.size = size;
+ args.entry = (cell_t)entry;
+ args.arg = (cell_t)arg;
+ args.len = len;
+ openfirmware(&args);
+}
+#else
+void
+OF_chain(void *virt, u_int size, void (*entry)(), void *arg, u_int len)
+{
+ /*
+ * This is a REALLY dirty hack till the firmware gets this going
+ */
+#if 0
+ if (size > 0)
+ OF_release(virt, size);
+#endif
+ entry(0, 0, openfirmware, arg, len);
+}
+#endif
diff --git a/stand/ofw/libofw/openfirm.h b/stand/ofw/libofw/openfirm.h
new file mode 100644
index 0000000..9fe77c6
--- /dev/null
+++ b/stand/ofw/libofw/openfirm.h
@@ -0,0 +1,124 @@
+/* $NetBSD: openfirm.h,v 1.1 1998/05/15 10:16:00 tsubai Exp $ */
+
+/*-
+ * Copyright (C) 1995, 1996 Wolfgang Solfrank.
+ * Copyright (C) 1995, 1996 TooLs GmbH.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*-
+ * Copyright (C) 2000 Benno Rice.
+ * 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 Benno Rice ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _OPENFIRM_H_
+#define _OPENFIRM_H_
+/*
+ * Prototypes for Open Firmware Interface Routines
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+typedef unsigned int ihandle_t;
+typedef unsigned int phandle_t;
+typedef unsigned long int cell_t;
+
+extern int (*openfirmware)(void *);
+extern phandle_t chosen;
+extern ihandle_t memory, mmu;
+extern int real_mode;
+
+/*
+ * This isn't actually an Open Firmware function, but it seemed like the right
+ * place for it to go.
+ */
+void OF_init(int (*openfirm)(void *));
+
+/* Generic functions */
+int OF_test(char *);
+void OF_quiesce(); /* Disable firmware */
+
+/* Device tree functions */
+phandle_t OF_peer(phandle_t);
+phandle_t OF_child(phandle_t);
+phandle_t OF_parent(phandle_t);
+phandle_t OF_instance_to_package(ihandle_t);
+int OF_getproplen(phandle_t, char *);
+int OF_getprop(phandle_t, char *, void *, int);
+int OF_nextprop(phandle_t, char *, char *);
+int OF_setprop(phandle_t, char *, void *, int);
+int OF_canon(const char *, char *, int);
+phandle_t OF_finddevice(const char *);
+int OF_instance_to_path(ihandle_t, char *, int);
+int OF_package_to_path(phandle_t, char *, int);
+int OF_call_method(char *, ihandle_t, int, int, ...);
+
+/* Device I/O functions */
+ihandle_t OF_open(char *);
+void OF_close(ihandle_t);
+int OF_read(ihandle_t, void *, int);
+int OF_write(ihandle_t, void *, int);
+int OF_seek(ihandle_t, u_quad_t);
+unsigned int OF_blocks(ihandle_t);
+int OF_block_size(ihandle_t);
+
+/* Memory functions */
+void *OF_claim(void *, u_int, u_int);
+void OF_release(void *, u_int);
+
+/* Control transfer functions */
+void OF_boot(char *);
+void OF_enter(void);
+void OF_exit(void) __attribute__((noreturn));
+void OF_chain(void *, u_int, void (*)(), void *, u_int);
+
+/* Time function */
+int OF_milliseconds(void);
+
+#endif /* _OPENFIRM_H_ */
diff --git a/stand/ofw/libofw/ppc64_elf_freebsd.c b/stand/ofw/libofw/ppc64_elf_freebsd.c
new file mode 100644
index 0000000..b411da4
--- /dev/null
+++ b/stand/ofw/libofw/ppc64_elf_freebsd.c
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 2001 Benno Rice <benno@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+
+#include <sys/param.h>
+#include <sys/linker.h>
+
+#include <machine/metadata.h>
+#include <machine/elf.h>
+
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "libofw.h"
+#include "openfirm.h"
+
+extern char end[];
+extern vm_offset_t reloc; /* From <arch>/conf.c */
+
+int
+ppc64_ofw_elf_loadfile(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ int r;
+
+ r = __elfN(loadfile)(filename, dest, result);
+ if (r != 0)
+ return (r);
+
+ /*
+ * No need to sync the icache for modules: this will
+ * be done by the kernel after relocation.
+ */
+ if (!strcmp((*result)->f_type, "elf kernel"))
+ __syncicache((void *) (*result)->f_addr, (*result)->f_size);
+ return (0);
+}
+
+int
+ppc64_ofw_elf_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *fmp;
+ vm_offset_t mdp, dtbp;
+ Elf_Ehdr *e;
+ int error;
+ intptr_t entry;
+
+ if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) {
+ return(EFTYPE);
+ }
+ e = (Elf_Ehdr *)&fmp->md_data;
+
+ /* Handle function descriptor for ELFv1 kernels */
+ if ((e->e_flags & 3) == 2)
+ entry = e->e_entry;
+ else
+ entry = *(uint64_t *)e->e_entry;
+
+ if ((error = md_load64(fp->f_args, &mdp, &dtbp)) != 0)
+ return (error);
+
+ printf("Kernel entry at 0x%lx ...\n", entry);
+
+ dev_cleanup();
+ ofw_release_heap();
+
+ if (dtbp != 0) {
+ OF_quiesce();
+ ((int (*)(u_long, u_long, u_long, void *, u_long))entry)(dtbp, 0, 0,
+ mdp, sizeof(mdp));
+ } else {
+ OF_chain((void *)reloc, end - (char *)reloc, (void *)entry,
+ (void *)mdp, sizeof(mdp));
+ }
+
+ panic("exec returned");
+}
+
+struct file_format ofw_elf64 =
+{
+ ppc64_ofw_elf_loadfile,
+ ppc64_ofw_elf_exec
+};
diff --git a/stand/pc98/Makefile b/stand/pc98/Makefile
new file mode 100644
index 0000000..e8f9dbf
--- /dev/null
+++ b/stand/pc98/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= boot0 boot0.5 pc98boot btx boot2 cdboot kgzldr libpc98 loader
+
+.include <bsd.subdir.mk>
diff --git a/stand/pc98/Makefile.inc b/stand/pc98/Makefile.inc
new file mode 100644
index 0000000..d766303
--- /dev/null
+++ b/stand/pc98/Makefile.inc
@@ -0,0 +1,29 @@
+# Common defines for all of /stand/pc98/
+#
+# $FreeBSD$
+
+BINDIR?= /boot
+
+LOADER_ADDRESS?=0x200000
+CFLAGS+= -march=i386 -ffreestanding
+CFLAGS.gcc+= -mpreferred-stack-boundary=2
+CFLAGS+= ${CFLAGS_NO_SIMD} -msoft-float
+CFLAGS+= -Os -DPC98
+LDFLAGS+= -nostdlib
+
+# BTX components
+.if exists(${.OBJDIR}/../btx)
+BTXDIR= ${.OBJDIR}/../btx
+.else
+BTXDIR= ${.CURDIR}/../btx
+.endif
+BTXLDR= ${BTXDIR}/btxldr/btxldr
+BTXKERN= ${BTXDIR}/btx/btx
+BTXCRT= ${BTXDIR}/lib/crt0.o
+
+# compact binary with no padding between text, data, bss
+LDSCRIPT= ${SRCTOP}/stand/i386/boot.ldscript
+LDFLAGS_BIN=-e start -Ttext ${ORG} -Wl,-T,${LDSCRIPT},-S,--oformat,binary
+LD_FLAGS_BIN=-static -T ${LDSCRIPT} --gc-sections
+
+.include "../Makefile.inc"
diff --git a/stand/pc98/boot0.5/Makefile b/stand/pc98/boot0.5/Makefile
new file mode 100644
index 0000000..ec40fe5
--- /dev/null
+++ b/stand/pc98/boot0.5/Makefile
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+PROG= ${BOOT}.out
+INTERNALPROG=
+FILES= ${BOOT}
+MAN=
+SRCS= start.s boot.s boot0.5.s disk.s selector.s support.s syscons.s \
+ putssjis.s
+CLEANFILES= ${BOOT} ${BOOT}.bin
+
+BOOT= boot0.5
+
+# The base address that we the boot0 code to to run it. Don't change this
+# unless you are glutton for punishment.
+BOOT_BOOT0_ORG?= 0x0000
+
+LDFLAGS=-e start -Ttext ${BOOT_BOOT0_ORG} -Wl,-N,-T,${.CURDIR}/ldscript
+
+# The size of boot0.5 must be 7168 bytes
+${BOOT}: ${BOOT}.bin
+ cat ${BOOT}.bin /dev/zero | ${DD} of=${BOOT} bs=1 count=7168
+
+${BOOT}.bin: ${BOOT}.out
+ ${OBJCOPY} -S -O binary ${BOOT}.out ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/stand/pc98/boot0.5/boot.s b/stand/pc98/boot0.5/boot.s
new file mode 100644
index 0000000..9d11206
--- /dev/null
+++ b/stand/pc98/boot0.5/boot.s
@@ -0,0 +1,174 @@
+# Copyright (c) KATO Takenori, 1999, 2000.
+#
+# All rights reserved. Unpublished rights reserved under the copyright
+# laws of Japan.
+#
+# 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 as
+# the first lines of this file unmodified.
+# 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 ``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.
+#
+# $FreeBSD$
+#
+
+ .code16
+
+ .text
+ .global boot
+#
+# Read bootstrap program and jump to it.
+#
+boot:
+ # Step 1: Save parameters
+ movw curdevice, %si
+ movb daua(%si), %al
+ movb %al, b_daua
+ shlw %si
+ movw secsize(%si), %ax
+ movw %ax, b_secsize
+
+ movw curpartition, %si
+ movb partnum(%si), %al # %al = real partition number
+ xorb %ah, %ah
+ movw %ax, b_partn # save real parttion number
+ movb $5, %cl
+ shlw %cl, %si # %si = offset to parttable
+ addw $4, %si
+ movb parttable(%si), %al # IPLS
+ movb %al, b_sector
+ incw %si
+ movb parttable(%si), %al # IPLH
+ movb %al, b_head
+ incw %si # IPLC
+ movw parttable(%si), %ax
+ movw %ax, b_cylinder
+
+ # Step 2: Calculate the segment address of the bootstrap routine
+ movw $0x1d00, %ax
+ movw b_secsize, %cx
+ shrw %cx
+ shrw %cx
+ subw %cx, %ax
+ subw $0x100, %ax
+ movw %ax, b_bootseg
+
+ # Step 3: Read bootstrap code
+ movb $6, %ah
+ movb b_daua, %al
+ movw b_secsize, %bx
+ shlw %bx # 2 sectors
+ movw b_cylinder, %cx
+ movb b_head, %dh
+ movb b_sector, %dl
+ movw b_bootseg, %es
+ xorw %bp, %bp
+ int $0x1b
+ jc boot_error
+
+ # Step 4: Set DA/UA into BIOS work area
+ xorw %ax, %ax
+ movw %ax, %es
+ movw $0x584, %bx # DISK_BOOT
+ movb b_daua, %dl
+ call write_biosparam
+
+ call sc_clean
+ # Step 5: Set registers
+ # %ah: 00
+ # %al: DA/UA
+ # %bx: Sector size * 2
+ # %cx: cylinder number of boot partition
+ # %si: pointer to partition table
+ movw b_partn, %ax
+ movb $5, %cl
+ shl %cl, %ax # %ax = partition number * 32
+ addw b_secsize, %ax
+ movw %ax, %si # %si = pointer to partition table
+ movw b_cylinder, %cx # %cx = cylinder
+ movb b_head, %dh # %dh = head
+ movb b_sector, %dl # %dl = sector
+ movw b_bootseg, %es # %es = boot segment
+ movb b_daua, %al # %al = DA/UA
+ movw b_secsize, %bx
+ shlw %bx # %bx = sector size * 2
+ cli
+ movw %cs:iniss, %ss # Restore stack pointer
+ movw %cs:inisp, %sp
+ push %es # Boot segment
+ xorw %bp, %bp
+ push %bp # 0
+ movw %ax, %di # Save %ax
+ xorw %ax, %ax
+ movw %ax, %ds # %ds = 0
+ movw %di, %ax # Restore %ax
+ xorb %ah, %ah # %ah = 0
+ xorw %di, %di # %di = 0
+ sti
+
+ # Jump to bootstrap code
+ lret
+ # NOTREACHED
+
+boot_error:
+ ret
+
+#
+# Try to boot from default partition.
+#
+ .global trydefault
+trydefault:
+ movw ndevice, %cx
+ xorw %si, %si
+trydefault_loop:
+ movw %si, curdevice
+ push %cx
+ push %si
+ call read_ipl
+ pop %si
+ pop %cx
+ cmpb $0x80, defpartflag
+ jne nodefpart
+ # Default partition is defined.
+ push %cx
+ movw npartition, %cx
+srch_part:
+ movw %cx, %bx
+ decw %bx
+ movb defpartnum, %al # %al = real partition number
+ cmpb partnum(%bx), %al
+ jne not_match
+ movw %bx, curpartition # Store partition number
+ call boot
+not_match:
+ loop srch_part
+ pop %cx
+nodefpart:
+ incw %si
+ loop trydefault_loop
+ ret
+
+ .data
+b_daua: .byte 0 # DA/UA
+b_head: .byte 0 # SYSH
+b_sector: .byte 0 # SYSS
+b_cylinder: .word 0 # SYSC
+b_bootseg: .word 0
+b_secsize: .word 0
+b_partn: .word 0 # Real partition number
diff --git a/stand/pc98/boot0.5/boot0.5.s b/stand/pc98/boot0.5/boot0.5.s
new file mode 100644
index 0000000..f878006
--- /dev/null
+++ b/stand/pc98/boot0.5/boot0.5.s
@@ -0,0 +1,293 @@
+# Copyright (c) KATO Takenori, 1999, 2000, 2007.
+#
+# All rights reserved. Unpublished rights reserved under the copyright
+# laws of Japan.
+#
+# 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 as
+# the first lines of this file unmodified.
+# 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 ``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.
+#
+# $FreeBSD$
+#
+ .global main
+ .code16
+
+ .text
+main:
+ # Check hireso mode
+ movw $0x501, %bx # BIOS_FLAG
+ call read_biosparam
+ testb $0x08, %dl
+ jz normalmode
+ movb $1, ishireso
+normalmode:
+ call sc_init
+
+ # Display title and copyright.
+ movw $title, %di
+ call sc_puts
+ xorw %cx, %cx
+ movw $1, %dx
+ call sc_goto
+ movw $copyright, %di
+ call sc_puts
+
+ # Scan hard drives
+ xorw %si, %si # number of partition
+ call scan_sasi # SASI/IDE
+ call scan_scsi # SCSI
+ movw %si, ndevice
+ orw %si, %si
+ jnz drives_found
+ jmp exit # No hard drives
+
+drives_found:
+ # Setup sector size dependent parameters
+ movw %si, %cx # %cx = number of devices
+setup_loop:
+ movw %cx, %di
+ decw %di
+ shlw %di
+ movw secsize(%di), %ax
+ cmpw $1024, %ax
+ je setup_1024
+ cmpw $512, %ax
+ je setup_512
+ # 256 bytes/sector
+ movw $0x100, partoff(%di)
+ movw $0x0fa, defflagoff(%di)
+ movw $0x0fb, defpartoff(%di)
+ movw $8, maxpart(%di)
+ jmp setup_secsize_end
+ # 1024 bytes/sector
+setup_1024:
+ # XXX Fix me!
+ movw $0x400, partoff(%di)
+ movw $0x3fa, defflagoff(%di)
+ movw $0x3fb, defpartoff(%di)
+ movb $32, maxpart(%di)
+ jmp setup_secsize_end
+ # 512 bytes/sector
+setup_512:
+ movw $0x200, partoff(%di)
+ movw $0x1fa, defflagoff(%di)
+ movw $0x1fb, defpartoff(%di)
+ movb $16, maxpart(%di)
+setup_secsize_end:
+ loop setup_loop
+
+ # For debug with floppy, fake the parameter.
+ movw $0x584, %bx # DISK_BOOT
+ call read_biosparam
+ andb $0xf0, %dl
+ cmpb $0x90, %ah
+ jne boot_from_hdd
+ movb daua, %dl
+ call write_biosparam
+
+boot_from_hdd:
+ movw $500, %cx
+wait_0_5:
+ call wait1ms
+ loop wait_0_5
+
+ # If the TAB is pressed, don't try to boot from default partition
+ xorw %di, %di # flag
+wait_key_release:
+ call sc_iskeypress
+ orw %ax, %ax
+ jz key_release # KBD buffer empty.
+ call sc_getc
+ cmpb $0x0f, %ah # TAB
+ jne wait_key_release
+ # TAB pressed
+ movw $1, %di
+ jmp wait_key_release
+key_release:
+ orw %di, %di
+ jnz dont_try_default # TAB pressed.
+ call trydefault
+ # Default partition not found.
+dont_try_default:
+ call show_usage
+ call showdevices
+ call selector
+exit:
+ ret
+#
+# Display usage
+#
+show_usage:
+ movw $44, %cx
+ movw $3, %dx
+ call sc_goto
+ movw $msg_usage1, %di
+ call sc_puts
+ movw $44, %cx
+ movw $4, %dx
+ call sc_goto
+ movw $msg_usage2, %di
+ call sc_puts
+ movw $44, %cx
+ movw $5, %dx
+ call sc_goto
+ movw $msg_usage3, %di
+ call sc_puts
+ movw $44, %cx
+ movw $7, %dx
+ call sc_goto
+ movw $msg_usage4, %di
+ call sc_puts
+ movw $44, %cx
+ movw $8, %dx
+ call sc_goto
+ movw $msg_usage5, %di
+ call sc_puts
+ movw $44, %cx
+ movw $9, %dx
+ call sc_goto
+ movw $msg_usage6, %di
+ call sc_puts
+ movw $44, %cx
+ movw $10, %dx
+ call sc_goto
+ movw $msg_usage7, %di
+ call sc_puts
+ movw $44, %cx
+ movw $11, %dx
+ call sc_goto
+ movw $msg_usage8, %di
+ call sc_puts
+ movw $44, %cx
+ movw $16, %dx
+ call sc_goto
+ movw $msg_usage9, %di
+ call sc_puts
+ movw $44, %cx
+ movw $17, %dx
+ call sc_goto
+ movw $msg_usage10, %di
+ call sc_puts
+ movw $44, %cx
+ movw $18, %dx
+ call sc_goto
+ movw $msg_usage11, %di
+ call sc_puts
+ movw $44, %cx
+ movw $19, %dx
+ call sc_goto
+ movw $msg_usage12, %di
+ call sc_puts
+ ret
+
+#
+# Display device list
+#
+showdevices:
+ movw $2, %cx
+ movw $4, %dx
+ call sc_goto
+ movw $msg_device, %di
+ call sc_puts
+ xorw %si, %si # %si = device number
+ movw ndevice, %cx # %cx = number of devices
+showdev_loop:
+ push %cx
+ movw $2, %cx
+ movw $5, %dx
+ addw %si, %dx
+ call sc_goto
+ # Check DA
+ movb daua(%si), %al
+ push %ax
+ andb $0xf0, %al
+ cmpb $0x80, %al
+ je show_sasi
+ cmpb $0xa0, %al
+ je show_scsi
+ # unknown device
+ movw $msg_unknown, %di
+ call sc_puts
+ jmp showunit
+ # SASI
+show_sasi:
+ movw $msg_sasi, %di
+ call sc_puts
+ jmp showunit
+ # SCSI
+show_scsi:
+ movw $msg_scsi, %di
+ call sc_puts
+ # Display unit number.
+showunit:
+ pop %ax
+ andb $0x0f, %al
+ addb $'0', %al
+ call sc_putc
+ incw %si
+ pop %cx
+ loop showdev_loop
+ movw ndevice, %dx
+ addw $5, %dx
+ movw $2, %cx
+ call sc_goto
+ movw $msg_exitmenu, %di
+ call sc_puts
+ ret
+
+ .data
+ .global curdevice, ndevice
+ndevice: .word 0 # number of device
+curdevice: .word 0 # current device
+
+ .global ishireso
+ishireso: .byte 0
+
+title: .asciz "PC98 Boot Selector Version 1.2"
+copyright: .ascii "(C)Copyright 1999-2007 KATO Takenori. "
+ .asciz "All rights reserved."
+msg_device: .asciz "Device"
+msg_sasi: .asciz "SASI/IDE unit "
+msg_scsi: .asciz "SCSI ID "
+msg_unknown: .asciz "unknown unit "
+msg_exitmenu: .asciz "Exit this menu"
+msg_usage1: .asciz "Device list"
+msg_usage2: .asciz "UP, DOWN: select boot device"
+msg_usage3: .asciz "RETURN: move to slice list"
+msg_usage4: .asciz "Slice list"
+msg_usage5: .asciz "UP, DOWN: select boot slice"
+msg_usage6: .asciz "RETURN: boot"
+msg_usage7: .asciz "SPACE: toggle default"
+msg_usage8: .asciz "ESC: move to device list"
+msg_usage9: .asciz "LEGEND"
+msg_usage10: .asciz ">>: selected device/slice"
+msg_usage11: .asciz "*: default slice to boot"
+msg_usage12: .asciz "!: unbootable slice"
+
+ .bss
+ .global daua, secsize, defflagoff, defpartoff
+ .global maxpart, partoff
+daua: .space 12 # DA/DU list
+secsize: .space 12 * 2 # Sector soize
+defflagoff: .space 12 * 2
+defpartoff: .space 12 * 2
+maxpart: .space 12 * 2
+partoff: .space 12 * 2
diff --git a/stand/pc98/boot0.5/disk.s b/stand/pc98/boot0.5/disk.s
new file mode 100644
index 0000000..6e80348
--- /dev/null
+++ b/stand/pc98/boot0.5/disk.s
@@ -0,0 +1,296 @@
+# Copyright (c) KATO Takenori, 1999, 2000.
+#
+# All rights reserved. Unpublished rights reserved under the copyright
+# laws of Japan.
+#
+# 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 as
+# the first lines of this file unmodified.
+# 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 ``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.
+#
+# $FreeBSD$
+#
+
+ .code16
+ .text
+#
+# Check magic number at the end of the sector 0
+#
+check_magic:
+ movw curdevice, %si
+ shlw %si
+ movw secsize(%si), %bx
+ decw %bx
+ decw %bx
+ movw iplbuf(%bx), %ax
+ cmpw $0xaa55, %ax
+ je magic_ok
+ movw $1, %ax
+ ret
+magic_ok:
+ xorw %ax, %ax
+ ret
+
+#
+# Copy partition table from buffer to parttable.
+#
+setup_partition:
+ push %cs
+ pop %es
+ movw curdevice, %bx
+ shlw %bx
+ movw maxpart(%bx), %cx # %cx = max num of partitions
+ movw partoff(%bx), %di
+ movw %di, %bx # %bx = offset to partition table
+ xorw %dx, %dx # %dx = partition number
+setup_partition_loop:
+ push %cx
+ movw %dx, %si
+ movb $5, %cl
+ shlw %cl, %si
+ addw %bx, %si
+ movb iplbuf(%si), %al
+ orb %al, %al
+ jz unused_partition
+ addw $iplbuf, %si
+ movw npartition, %ax
+ movw %ax, %di
+ movb $5, %cl
+ shlw %cl, %di
+ addw $parttable, %di
+ movw $32, %cx
+ rep
+ movsb
+ movw %ax, %di
+ addw $partnum, %di
+ movb %dl, (%di)
+ incw npartition
+unused_partition:
+ incw %dx
+ pop %cx
+ loop setup_partition_loop
+ ret
+
+#
+# Read IPL and partition table in the current device.
+#
+ .global read_ipl
+read_ipl:
+ movw curdevice, %ax
+ movw %ax, %si # %si = device number
+ movw %ax, %di
+ shlw %di
+
+ movw %cs, %ax
+ movw %ax, %es
+ movb $6, %ah
+ movb daua(%si), %al
+ movw $0x400, %bx
+ xorw %cx, %cx
+ xorw %dx, %dx
+ movw $iplbuf, %bp
+ int $0x1b
+ jc read_ipl_error
+ movw defflagoff(%di), %bx
+ movb iplbuf(%bx), %al
+ movb %al, defpartflag
+ incw %bx
+ movb iplbuf(%bx), %al
+ movb %al, defpartnum
+ movw $0, npartition
+ call check_magic
+ orw %ax, %ax
+ jnz no_magic
+ call setup_partition
+no_magic:
+ xorw %ax, %ax
+read_ipl_error:
+ xorw %bx, %bx
+ movw %bx, %es
+ ret
+
+#
+# Restore IPL from the buffer
+#
+ .global write_ipl
+write_ipl:
+ movw curdevice, %ax
+ movw %ax, %si
+ movw %ax, %di
+ shlw %di
+
+ # Restore default boot partition info.
+ movw defflagoff(%di), %bx
+ movb defpartflag, %al
+ movb %al, iplbuf(%bx)
+ incw %bx
+ movb defpartnum, %al
+ movb %al, iplbuf(%bx)
+
+ movw %cs, %ax
+ movw %ax, %es
+ movb $5, %ah
+ movb daua(%si), %al
+ movw secsize(%di), %bx
+ xorw %cx, %cx
+ xorw %dx, %dx
+ movw $iplbuf, %bp
+ int $0x1b
+ jc write_ipl_error
+ xorw %ax, %ax
+write_ipl_error:
+ xorw %bx, %bx
+ movw %bx, %es
+ ret
+
+#
+# Scan HDD devices
+#
+ .global scan_sasi, scan_scsi
+ # Scan SASI disk
+scan_sasi:
+ # SASI Disk
+ movw $4, %cx
+ movw $0x0001, %ax # %ah = unit number, %al = for bit operation
+
+sasi_loop:
+ movw %si, %di
+ shlw %di
+ movw $0x55d, %bx # DISK_EQUIP
+ call read_biosparam
+ testb %al, %dl
+ jz no_sasi_unit
+ movb $0x80, %dh
+ addb %ah, %dh # %dh = DA/UA
+ movb %dh, daua(%si) # Store DA/UA
+
+ # Try new sense command
+ push %ax
+ push %cx
+ movb %dh, %al
+ movb $0x84, %ah
+ int $0x1b
+ pop %cx
+ pop %ax
+ jc err_newsense
+ movw %bx, %dx
+ jmp found_sasi_unit
+
+err_newsense:
+ movw $0x457, %bx # capacity & sector size of IDE HDD
+ call read_biosparam
+ orb %ah, %ah
+ jz sasi_1
+ cmpb $1, %ah
+ jz sasi_2
+
+ # SASI #3/#4
+ movw $512, %dx # XXX
+ jmp found_sasi_unit
+
+sasi_1:
+ # SASI #1
+ testb $0x80, %dl
+ jz sasi_256
+ jmp sasi_512
+sasi_2:
+ # SASI #2
+ testb $0x40, %dl
+ jz sasi_256
+ jmp sasi_512
+
+sasi_256:
+ movw $256, %dx
+ jmp found_sasi_unit
+sasi_512:
+ movw $512, %dx
+found_sasi_unit:
+ movw %dx, secsize(%di)
+ incw %si
+no_sasi_unit:
+ incb %ah
+ shlb %al
+ loop sasi_loop
+ ret
+
+#
+# Scan SCSI disk
+# SI number of disks
+# destroyed: %ax, %bx, %cx, %dx
+scan_scsi:
+ movw $8, %cx
+ movw $0x0001, %ax # %ah = ID number, %al = for bit operation
+scsi_loop:
+ # Check whether drive exist.
+ movw %si, %di
+ shlw %di
+ movw $0x482, %bx # DISK_EQUIPS
+ call read_biosparam
+ testb %al, %dl
+ jz no_scsi_unit
+ xorw %bx, %bx
+ movb %ah, %bl
+ shlw %bx
+ shlw %bx
+ addw $0x460, %bx # SCSI parameter block
+ call read_biosparam
+ orb %dl, %dl
+ jz no_scsi_unit
+
+ # SCSI harddrive found.
+ movb $0xa0, %dh
+ addb %ah, %dh
+ movb %dh, daua(%si)
+
+ # Check sector size.
+ addw $3, %bx
+ call read_biosparam
+ andb $0x30, %dl
+ cmpb $0x20, %dl
+ je scsi_1024
+ cmpb $0x10, %dl
+ je scsi_512
+ movw $256, %dx
+ jmp found_scsi
+scsi_1024:
+ movw $1024, %dx
+ jmp found_scsi
+scsi_512:
+ movw $512, %dx
+found_scsi:
+ movw %dx, secsize(%di)
+ incw %si
+no_scsi_unit:
+ incb %ah
+ shlb %al
+ loop scsi_loop
+ ret
+
+ .data
+ .global defpartflag, defpartnum, npartition
+defpartflag: .byte 0
+defpartnum: .byte 0
+npartition: .word 0 # number of partitions
+
+ .bss
+ .global partnum, parttable
+iplbuf: .space 0x400 # Read buffer for IPL
+partnum: .space 32 # Index of parttable
+parttable: .space 1024 # Copy of valid partition table
diff --git a/stand/pc98/boot0.5/ldscript b/stand/pc98/boot0.5/ldscript
new file mode 100644
index 0000000..49044ab
--- /dev/null
+++ b/stand/pc98/boot0.5/ldscript
@@ -0,0 +1,12 @@
+/*
+ * $FreeBSD$
+ */
+
+SECTIONS
+{
+ .text : { *(.text) }
+ .data : { *(.data) }
+ . = 0x1243;
+ .putssjis : { *(.putssjis) }
+ .bss : { *(.bss) }
+}
diff --git a/stand/pc98/boot0.5/putssjis.s b/stand/pc98/boot0.5/putssjis.s
new file mode 100644
index 0000000..221b9e6
--- /dev/null
+++ b/stand/pc98/boot0.5/putssjis.s
@@ -0,0 +1,137 @@
+# Copyright (c) KATO Takenori, 2007.
+#
+# All rights reserved. Unpublished rights reserved under the copyright
+# laws of Japan.
+#
+# 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 as
+# the first lines of this file unmodified.
+# 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 ``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.
+#
+# $FreeBSD$
+#
+
+ .code16
+ .section .putssjis, "awx", @progbits
+
+ #
+ # Display string with Shift-JIS support
+ # %si: address of string, %di: T-VRAM address, %cx: count
+ #
+
+ # Absolute address of putssjis_entry must be 0x1243.
+putssjis_entry:
+ push %es
+ push %ax
+ # Setup the T-VRAM segement address.
+ xorw %ax, %ax
+ movw %ax, %es
+ movw $0xa000, %ax
+ testb $0x08, %es:0x501
+ jz normalmode
+ movw $0xe000, %ax
+normalmode:
+ movw %ax, %es
+
+putssjis_loop:
+ lodsw
+ call check_sjis
+ jc put_2byte_char
+
+ # 1 byte character
+ xorb %ah, %ah
+ testb $0xe0, %al # Check control code.
+ jnz put_1byte_char
+ movb $0x20, %al # Convert control code into the space.
+put_1byte_char:
+ stosw
+ decw %si
+ jmp putssjis_loop_end
+
+put_2byte_char:
+ subb $0x20, %al
+
+ # Check 2byte "hankaku"
+ cmp $0x09, %al
+ je put_2byte_hankaku
+ cmp $0x0a, %al
+ je put_2byte_hankaku
+ cmp $0x0b, %al
+ je put_2byte_hankaku
+ jmp put_2byte_zenkaku
+
+put_2byte_hankaku:
+ stosw
+ jmp putssjis_loop_end
+
+put_2byte_zenkaku:
+ stosw
+ orb $0x80, %ah
+ stosw
+ decw %cx
+
+putssjis_loop_end:
+ loop putssjis_loop
+
+ pop %ax
+ pop %es
+ ret
+
+ # Check 2-byte code.
+check_sjis:
+ cmpb $0x80, %al
+ jbe found_ank_kana
+ cmpb $0xa0, %al
+ jb found_2byte_char
+ cmpb $0xe0, %al
+ jb found_ank_kana
+ cmpb $0xf0, %al
+ jae found_ank_kana
+ jmp found_2byte_char
+found_ank_kana:
+ clc
+ ret
+
+found_2byte_char:
+ # Convert Shift-JIS into JIS.
+ cmpb $0x9f, %al
+ ja sjis_h_2 # Upper > 0x9f
+ subb $0x71, %al # Upper -= 0x71
+ jmp sjis_lower
+sjis_h_2:
+ subb $0xb1, %al # Upper -= 0xb1
+sjis_lower:
+ salb %al # Upper *= 2
+ incb %al # Upper += 1
+
+ cmpb $0x7f, %ah
+ jbe sjis_l_2
+ decb %ah # Lower -= 1 if lower > 0x7f
+sjis_l_2:
+ cmpb $0x9e, %ah
+ jb sjis_l_3
+ subb $0x7d, %ah # Lower -= 0x7d
+ incb %al # Upper += 1
+ jmp check_2byte_end
+sjis_l_3:
+ subb $0x1f, %ah # Lower -= 0x1f
+check_2byte_end:
+ stc
+ ret
diff --git a/stand/pc98/boot0.5/selector.s b/stand/pc98/boot0.5/selector.s
new file mode 100644
index 0000000..9d98ef8
--- /dev/null
+++ b/stand/pc98/boot0.5/selector.s
@@ -0,0 +1,450 @@
+# Copyright (c) KATO Takenori, 1999, 2000, 2007.
+#
+# All rights reserved. Unpublished rights reserved under the copyright
+# laws of Japan.
+#
+# 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 as
+# the first lines of this file unmodified.
+# 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 ``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.
+#
+# $FreeBSD$
+#
+
+ .code16
+
+ .text
+#
+# Display partition table.
+#
+showpartitions:
+ # Clear partition table area
+ movw $16, %cx
+clear_part:
+ push %cx
+ movw %cx, %dx
+ decw %dx
+ addw $5, %dx
+ movw $20, %cx
+ call sc_goto
+ movw $msg_spc, %di
+ call sc_puts
+ pop %cx
+ loop clear_part
+
+ # Check `Exit' menu
+ movw curdevice, %ax
+ cmpw ndevice, %ax
+ je no_slice
+
+ # XXX Move this to a suitable place!
+ movw $22, %cx
+ movw $4, %dx
+ call sc_goto
+ movw $msg_slice, %di
+ call sc_puts
+
+ # Check the number of partitions
+ movw npartition, %cx
+ orw %cx, %cx
+ jnz partitionexist
+no_slice:
+ # Just show the `no slice' message.
+ movw $22, %cx
+ movw $5, %dx
+ call sc_goto
+ movw $msg_noslice, %di
+ call sc_puts
+ ret
+partitionexist:
+ xorw %si, %si # %si = partition number
+showpart_loop:
+ push %cx # %cx = number of partitions
+ movw $22, %cx
+ movw %si, %dx
+ addw $5, %dx
+ call sc_goto
+ movw %si, %di
+ movb $5, %cl
+ shlw %cl, %di
+ addw $0x10, %di # SYSM field
+ # SYSM: space filled string. Don't use sc_puts.
+ movw $16, %cx
+showpart_name:
+ push %cx
+ movb parttable(%di), %al
+ call sc_putc
+ incw %di
+ pop %cx
+ loop showpart_name
+ incw %si
+ pop %cx
+ loop showpart_loop
+ ret
+
+#
+# Show default slice indicator
+# If the default boot slice exists, `*' indicator will be showed.
+#
+showdefaultslicemark:
+ cmpb $0x80, defpartflag
+ je defpartexist
+ ret
+defpartexist:
+ movw npartition, %cx
+defslice_loop:
+ movw %cx, %bx
+ decw %bx
+ push %cx
+ push %bx
+ movw $40, %cx
+ movw %bx, %dx
+ addw $5, %dx
+ call sc_goto
+
+ pop %bx
+ pop %cx
+ movb defpartnum, %al
+ cmpb partnum(%bx), %al
+ jne nomatch
+ movb $'*', %al
+ call sc_putc
+ jmp defslice_done
+nomatch:
+ movb $' ', %al
+ call sc_putc
+defslice_done:
+ loop defslice_loop
+ ret
+
+#
+# Hide default slice indicator
+#
+hidedefaultslicemark:
+ movw $16, %cx
+hidedefslice_loop:
+ push %cx
+ movw %cx, %dx
+ addw $4, %dx
+ movw $40, %cx
+ call sc_goto
+ movb $' ', %al
+ call sc_putc
+ pop %cx
+ loop hidedefslice_loop
+ ret
+
+#
+# Toggle default slice
+#
+toggle_default:
+ cmpb $0x80, defpartflag
+ jne set_default
+ # Clear default
+ movb $0, defpartflag
+ call write_ipl # Restore
+ call hidedefaultslicemark
+ ret
+ # Set default slice
+set_default:
+ movw curpartition, %si
+ movb partnum(%si), %al # %al = real partition number
+ movb $5, %cl
+ shlw %cl, %si
+ # Default slice must be bootable
+ testb $0x80, parttable(%si)
+ jnz curpart_bootable
+ # Current partition is not bootable.
+ ret
+curpart_bootable:
+ movb $0x80, defpartflag
+ movb %al, defpartnum
+ call write_ipl # Restore
+ call showdefaultslicemark
+ ret
+
+#
+# Show/hide cursor
+#
+show_devcurs:
+ xorw %cx, %cx
+ movw curdevice, %dx
+ addw $5, %dx
+ call sc_goto
+ movb $'>', %al
+ call sc_putc
+ movb $'>', %al
+ call sc_putc
+ ret
+
+hide_devcurs:
+ xorw %cx, %cx
+ movw curdevice, %dx
+ addw $5, %dx
+ call sc_goto
+ movb $' ', %al
+ call sc_putc
+ movb $' ', %al
+ call sc_putc
+ ret
+
+show_slicecurs:
+ movw $20, %cx
+ movw curpartition, %dx
+ addw $5, %dx
+ call sc_goto
+ movb $'>', %al
+ call sc_putc
+ movb $'>', %al
+ call sc_putc
+ ret
+
+hide_slicecurs:
+ movw $20, %cx
+ movw curpartition, %dx
+ addw $5, %dx
+ call sc_goto
+ movb $' ', %al
+ call sc_putc
+ movb $' ', %al
+ call sc_putc
+ ret
+
+isforceboot:
+ xorw %cx, %cx
+ movw $20, %dx
+ call sc_goto
+ movw $msg_force, %di
+ call sc_puts
+ call sc_getc
+ push %ax
+ xorw %cx, %cx
+ movw $20, %dx
+ call sc_goto
+ movw $msg_forceclr, %di
+ call sc_puts
+ pop %ax
+ cmpb $0x15, %ah
+ je force_yes
+ xorw %ax, %ax
+ ret
+force_yes:
+ movw $1, %ax
+ ret
+
+#
+# Main loop for device mode
+#
+devmode:
+ call read_ipl
+ call hidedefaultslicemark
+ call showpartitions
+ call showdefaultslicemark
+ call show_devcurs
+
+ movw $2, %cx
+ movw $4, %dx
+ call sc_goto
+ movb $0xe5, %al
+ movw $6, %cx
+ call sc_setattr
+ movw $22, %cx
+ movw $4, %dx
+ call sc_goto
+ movb $0xe1, %al
+ movw $5, %cx
+ call sc_setattr
+ movw $44, %cx
+ movw $3, %dx
+ call sc_goto
+ movb $0xe5, %al
+ movw $11, %cx
+ call sc_setattr
+ movw $44, %cx
+ movw $7, %dx
+ call sc_goto
+ movb $0xe1, %al
+ movw $10, %cx
+ call sc_setattr
+
+devmode_loop:
+ call sc_getc
+ movw ndevice, %bx
+ cmpb $0x3a, %ah # UP
+ je dev_up
+ cmpb $0x3d, %ah # DOWN
+ je dev_down
+ cmpb $0x3c, %ah # RIGHT
+ je dev_right
+ cmpb $0x1c, %ah # RETURN
+ jne devmode_loop
+ cmpw curdevice, %bx
+ jne dev_right
+ movw $3, mode # N88-BASIC
+ ret
+
+ # XXX
+ .space 5, 0x90
+ ret # Dummy ret @0x9ab
+
+dev_up:
+ cmpw $0, curdevice
+ je devmode_loop
+ call hide_devcurs
+ decw curdevice
+ call read_ipl
+ call hidedefaultslicemark
+ call showpartitions
+ call showdefaultslicemark
+ call show_devcurs
+ jmp devmode_loop
+dev_down:
+ cmpw curdevice, %bx
+ je devmode_loop
+ call hide_devcurs
+ incw curdevice
+ call read_ipl
+ call hidedefaultslicemark
+ call showpartitions
+ call showdefaultslicemark
+ call show_devcurs
+ jmp devmode_loop
+dev_right:
+ cmpw curdevice, %bx
+ je devmode_loop
+ movw $1, mode # Slice mode
+ ret
+
+#
+# main loop for slice mode
+#
+slicemode:
+ movw $0, curpartition
+ call show_slicecurs
+ movw $2, %cx
+ movw $4, %dx
+ call sc_goto
+ movb $0xe1, %al
+ movw $6, %cx
+ call sc_setattr
+ movw $22, %cx
+ movw $4, %dx
+ call sc_goto
+ movb $0xe5, %al
+ movw $5, %cx
+ call sc_setattr
+ movw $44, %cx
+ movw $3, %dx
+ call sc_goto
+ movb $0xe1, %al
+ movw $11, %cx
+ call sc_setattr
+ movw $44, %cx
+ movw $7, %dx
+ call sc_goto
+ movb $0xe5, %al
+ movw $10, %cx
+ call sc_setattr
+
+slicemode_loop:
+ call sc_getc
+ cmpb $0x3a, %ah # UP
+ je slice_up
+ cmpb $0x3d, %ah # DOWN
+ je slice_down
+ cmpb $0x3b, %ah # LEFT
+ je slice_esc
+ cmpb $0x00, %ah # ESC
+ je slice_esc
+ cmpb $0x1c, %ah # RETURN
+ je slice_ret
+ cmpb $0x34, %ah # SPC
+ je slice_spc
+ cmpb $0x62, %ah # f1
+ je slice_spc
+ jmp slicemode_loop
+slice_up:
+ cmpw $0, curpartition
+ je slicemode_loop
+ call hide_slicecurs
+ decw curpartition
+ call show_slicecurs
+ jmp slicemode_loop
+slice_down:
+ movw curpartition, %bx
+ movw npartition, %ax
+ decw %ax
+ cmpw %bx, %ax
+ je slicemode_loop
+ call hide_slicecurs
+ incw curpartition
+ call show_slicecurs
+ jmp slicemode_loop
+slice_esc:
+ movw $0, mode # Device mode
+ ret
+slice_spc:
+ call toggle_default
+ jmp slicemode_loop
+slice_ret:
+ # Test bit 7 of mid
+ movw curpartition, %si
+ movb $5, %cl
+ shlw %cl, %si
+ testb $0x80, parttable(%si)
+ jnz bootable_slice
+ call isforceboot
+ orw %ax, %ax
+ jz slicemode_loop
+bootable_slice:
+ call boot
+ jmp slicemode_loop
+
+#
+# Main loop
+#
+ .global selector
+selector:
+ movw $0, curdevice # trydefault may change the curdevice.
+ movw $0, mode
+
+selector_loop:
+ cmpw $0, mode
+ je status_dev
+ cmpw $1, mode
+ je status_slice
+ ret
+status_dev:
+ call devmode
+ jmp selector_loop
+status_slice:
+ call slicemode
+ jmp selector_loop
+
+ .data
+ .global curpartition
+curpartition: .word 0 # current patition
+mode: .word 0
+
+msg_spc: .asciz " "
+msg_slice: .asciz "Slice"
+msg_noslice: .asciz "no slice"
+msg_force: .asciz "This slice is not bootable. Continue? (Y / [N])"
+msg_forceclr: .asciz " "
diff --git a/stand/pc98/boot0.5/start.s b/stand/pc98/boot0.5/start.s
new file mode 100644
index 0000000..008ae66
--- /dev/null
+++ b/stand/pc98/boot0.5/start.s
@@ -0,0 +1,73 @@
+# Copyright (c) KATO Takenori, 1999, 2000, 2007.
+#
+# All rights reserved. Unpublished rights reserved under the copyright
+# laws of Japan.
+#
+# 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 as
+# the first lines of this file unmodified.
+# 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 ``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.
+#
+# $FreeBSD$
+#
+ .global start
+ .code16
+
+ .text
+start:
+ jmp start1
+
+ # Magic
+ .org 0x053, 0x20
+ .byte 0x4e, 0x45, 0x43
+
+ .org 0x8f
+ .byte 0x32, 0x2e, 0x37, 0x30
+
+ .org 0x2d4
+start1:
+ # The instruction 'call 0x9ab' can be here. See also selector.s.
+ nop
+ nop
+ nop
+ cli
+ movw %cs, %ax
+ movw %ax, %ds
+ movw %ss, iniss
+ movw %sp, inisp
+ movw %ax, %ss
+ movw $0xfffe, %sp
+ sti
+ xorw %ax, %ax
+ movw %ax, %es
+ call main
+
+ cli
+ movw %cs:iniss, %ss
+ movw %cs:inisp, %sp
+ sti
+ int $0x1e
+ # NOTREACHED
+ lret
+
+ .data
+ .global iniss, inisp
+iniss: .word 0
+inisp: .word 0
diff --git a/stand/pc98/boot0.5/support.s b/stand/pc98/boot0.5/support.s
new file mode 100644
index 0000000..df1115b
--- /dev/null
+++ b/stand/pc98/boot0.5/support.s
@@ -0,0 +1,94 @@
+# Copyright (c) KATO Takenori, 1999, 2000.
+#
+# All rights reserved. Unpublished rights reserved under the copyright
+# laws of Japan.
+#
+# 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 as
+# the first lines of this file unmodified.
+# 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 ``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.
+#
+# $FreeBSD$
+#
+
+ .code16
+
+ .text
+#
+# Wait 1ms
+#
+ .global wait1ms
+wait1ms:
+ push %cx
+ movw $800, %cx
+wait_loop:
+ outb %al, $0x5f
+ loop wait_loop
+ pop %cx
+ ret
+
+#
+# Read one byte from BIOS parameter block
+# %bx offset
+# %dl value
+#
+ .global read_biosparam
+read_biosparam:
+ movb %es:(%bx), %dl
+ ret
+
+#
+# Write one byte to BIOS parameter block
+# %bx offset
+# %dl value
+#
+ .global write_biosparam
+write_biosparam:
+ movb %dl, %es:(%bx)
+ ret
+
+#
+# beep
+#
+ .global beep_on, beep_off, beep
+beep_on:
+ movb $0x17, %ah
+ int $0x18
+ ret
+
+beep_off:
+ movb $0x18, %ah
+ int $0x18
+ ret
+
+beep:
+ push %cx
+ call beep_on
+ movw $100, %cx
+beep_loop1:
+ call wait1ms
+ loop beep_loop1
+ call beep_off
+ movw $50, %cx
+beep_loop2:
+ call wait1ms
+ loop beep_loop2
+ pop %cx
+ ret
diff --git a/stand/pc98/boot0.5/syscons.s b/stand/pc98/boot0.5/syscons.s
new file mode 100644
index 0000000..150b835
--- /dev/null
+++ b/stand/pc98/boot0.5/syscons.s
@@ -0,0 +1,253 @@
+# Copyright (c) KATO Takenori, 1999, 2000.
+#
+# All rights reserved. Unpublished rights reserved under the copyright
+# laws of Japan.
+#
+# 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 as
+# the first lines of this file unmodified.
+# 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 ``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.
+#
+# $FreeBSD$
+#
+
+ .code16
+
+ .text
+#
+# %al character code
+# destroyed: %al, %bx
+#
+put_character:
+ movw $0xe000, %bx
+ movb ishireso, %ah
+ orb %ah, %ah
+ jne hireso_ch
+ movw $0xa000, %bx
+hireso_ch:
+ movw %bx, %es
+ xorb %ah, %ah
+ movw curpos, %bx
+ movw %ax, %es:(%bx)
+ xorw %ax, %ax
+ movw %ax, %es
+ ret
+
+#
+# %al attribute
+# destroyed: %ah, %cx
+#
+set_attribute:
+ movw $0xe200, %bx
+ movb ishireso, %ah
+ orb %ah, %ah
+ jne hireso_ch
+ movw $0xa200, %bx
+hireso_attr:
+ movw %bx, %es
+ xorb %ah, %ah
+ movw curpos, %bx
+ movw %ax, %es:(%bx)
+ xorw %bx, %bx
+ movw %bx, %es
+ ret
+
+#
+# Put a character
+# %al: character code
+# destroyed: %ah, %bx, %cx
+#
+ .global sc_putc
+sc_putc:
+ call put_character
+ incw curpos
+ incw curpos
+ cmpw $4000, curpos
+ jng putc_end
+ movw $0, curpos
+putc_end:
+ ret
+
+#
+# Put a null terminated string
+# %di: pointer to string
+# destroyed: %ah, %cx, %di
+#
+ .global sc_puts
+sc_puts:
+ movb (%di), %al
+ orb %al, %al
+ jz puts_end
+ call sc_putc
+ incw %di
+ jmp sc_puts
+puts_end:
+ ret
+
+#
+# Change the current cursor position
+# %cx: X
+# %dx: Y
+# destroyed: %ax, %bx
+#
+ .global sc_goto
+sc_goto:
+ movw %dx, %ax # AX=Y
+ shlw %ax # AX=Y*64
+ shlw %ax
+ shlw %ax
+ shlw %ax
+ shlw %ax
+ shlw %ax
+ movw %dx, %bx # BX=Y
+ shlw %bx # BX=Y*16
+ shlw %bx
+ shlw %bx
+ shlw %bx
+ addw %bx, %ax # AX=Y*64+Y*16=Y*80
+ addw %cx, %ax
+ shlw %ax
+ movw %ax, curpos
+ ret
+
+#
+# Clear screen
+# destroyed: %ax, %bx
+#
+ .global sc_clean
+sc_clean:
+ movb $0x16, %ah
+ movw $0xe120, %dx
+ int $0x18 # KBD/CRT BIOS
+ movw $0, curpos
+ ret
+
+#
+# Set sttribute code
+# %al: attribute
+# %cx: count
+# destroyed: %ax, %bx, %cx
+#
+ .global sc_setattr
+sc_setattr:
+ call set_attribute
+ incw curpos
+ incw curpos
+ loop sc_setattr
+
+#
+# Sense the state of shift key
+# destroyed: %ax
+#
+ .global sc_getshiftkey
+sc_getshiftkey:
+ movb $2, %ah # Sense KB_SHIFT_COD
+ int $0x18 # KBD/CRT BIOS
+ xorb %ah, %ah
+ ret
+
+#
+# Check KBD buffer
+#
+ .global sc_iskeypress
+sc_iskeypress:
+ mov $1, %ah
+ int $0x18 # KBD/CRT BIOS
+ testb $1, %bh
+ jz no_key
+ movw $1, %ax
+ ret
+no_key:
+ xorw %ax, %ax
+ ret
+
+#
+# Read from KBD
+#
+ .global sc_getc
+sc_getc:
+ xorb %ah, %ah
+ int $0x18
+ ret
+
+#
+# Initialize CRT (normal mode)
+#
+init_screen_normal:
+ # Disable graphic screen
+ movb $0x41, %ah
+ int $0x18
+ # Init graphic screen
+ movb $0x42, %al
+ movb $0xc0, %ch
+ int $0x18
+ # 80x25 mode
+ movw $0x0a00, %ax
+ int $0x18
+ ret
+
+#
+# Initialize CRT (hireso mode)
+#
+init_screen_hireso:
+ # Init RAM window
+ movb $8, %al
+ outb %al, $0x91
+ movb $0x0a, %al
+ outb %al, $0x93
+ # 80x31 mode
+ movw $0x0a00, %ax
+ int $0x18
+ ret
+
+#
+# Initialize screen (internal)
+#
+init_screen:
+ movb ishireso, %ah
+ orb %ah, %ah
+ jne hireso_ini
+ call init_screen_normal
+ jmp init_next
+hireso_ini:
+ call init_screen_hireso
+init_next:
+ movb $0x0c, %ah
+ int $0x18
+ # cursor home and off
+ xorw %dx, %dx
+ movb $0x13, %ah
+ int $0x18
+ movb $0x12, %ah
+ int $0x18
+ ret
+
+#
+# Initialize screeen
+#
+ .global sc_init
+sc_init:
+ call init_screen
+ call sc_clean
+ movw $0, curpos
+ ret
+
+ .data
+curpos: .word 0 # Current cursor position
diff --git a/stand/pc98/boot0/Makefile b/stand/pc98/boot0/Makefile
new file mode 100644
index 0000000..d348f60
--- /dev/null
+++ b/stand/pc98/boot0/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+PROG= ${BOOT}
+INTERNALPROG=
+FILES= ${BOOT}
+MAN=
+SRCS= ${BOOT}.s
+CLEANFILES= ${BOOT}
+
+BOOT= boot0
+
+# The base address that we the boot0 code to to run it. Don't change this
+# unless you are glutton for punishment.
+BOOT_BOOT0_ORG?= 0x0000
+ORG=${BOOT_BOOT0_ORG}
+
+LDFLAGS=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
diff --git a/stand/pc98/boot0/boot0.s b/stand/pc98/boot0/boot0.s
new file mode 100644
index 0000000..508e252
--- /dev/null
+++ b/stand/pc98/boot0/boot0.s
@@ -0,0 +1,108 @@
+# Copyright (c) KATO Takenori, 1999, 2000.
+#
+# All rights reserved. Unpublished rights reserved under the copyright
+# laws of Japan.
+#
+# 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 as
+# the first lines of this file unmodified.
+# 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. 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.
+#
+# $FreeBSD$
+
+ .globl start
+ .code16
+start:
+ jmp main
+
+ .org 4
+ .ascii "IPL1"
+ .byte 0, 0, 0
+
+ .globl start
+main:
+ xor %ax, %ax
+ mov %ax, %ds
+ mov (0x584), %al # DA/UA
+ mov %al, %ah
+ and $0xf0, %ah
+ cmp $0x90, %ah
+ je fdd
+
+ # hdd
+ mov $6, %ah
+ mov $0x3000, %bx
+ mov %bx, %es
+ mov $0x2000, %bx
+ xor %cx, %cx
+ xor %dx, %dx
+ xor %bp, %bp
+ int $0x1b
+ jc error_hdd
+
+ push %ax
+ mov %es, %ax
+ add $0x40, %ax
+ mov %ax, %es
+ pop %ax
+ push %es
+ push %bp
+ lret
+
+ # fdd
+fdd:
+ xor %di, %di
+fdd_retry:
+ mov $0xd6, %ah
+ mov $0x3000, %bx
+ mov %bx, %es
+ mov $0x2000, %bx
+ mov $0x0200, %cx
+ mov $0x0001, %dx
+ xor %bp, %bp
+ int $0x1b
+ jc error
+ push %ax
+ mov %es, %ax
+ add $0x40, %ax
+ mov %ax, %es
+ pop %ax
+ push %es
+ push %bp
+ lret
+
+error:
+ or %di, %di
+ jnz error_hdd
+ and $0x0f, %al
+ or $0x30, %al
+ jmp fdd_retry
+
+error_hdd:
+ jmp error
+
+ .org 0x1fa
+ .byte 0 # defflag_off
+ .byte 0 # defpart_off
+ .byte 1 # menu version
+ .byte 0
+ .word 0xaa55
diff --git a/stand/pc98/boot2/Makefile b/stand/pc98/boot2/Makefile
new file mode 100644
index 0000000..2db0590
--- /dev/null
+++ b/stand/pc98/boot2/Makefile
@@ -0,0 +1,116 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+FILES= boot boot1 boot2
+
+NM?= nm
+
+BOOT_COMCONSOLE_PORT?= 0x238
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+REL1= 0x700
+ORG1= 0
+ORG2= 0x2000
+
+# Decide level of UFS support.
+BOOT2_UFS?= UFS1_AND_UFS2
+#BOOT2_UFS?= UFS2_ONLY
+#BOOT2_UFS?= UFS1_ONLY
+
+CFLAGS= -fomit-frame-pointer \
+ -mrtd \
+ -mregparm=3 \
+ -D${BOOT2_UFS} \
+ -DFLAGS=${BOOT_BOOT1_FLAGS} \
+ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} \
+ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ -I${.CURDIR}/../../.. \
+ -I${.CURDIR}/../../i386/boot2 \
+ -I${.CURDIR}/../../common \
+ -I${.CURDIR}/../btx/lib -I. \
+ -Wall -Waggregate-return -Wbad-function-cast -Wcast-align \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+ -Winline
+
+CFLAGS.gcc+= -Os \
+ -fno-guess-branch-probability \
+ -fno-unit-at-a-time \
+ --param max-inline-insns-single=100
+.if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} <= 40201
+CFLAGS.gcc+= -mno-align-long-strings
+.endif
+
+# Set machine type to PC98_SYSTEM_PARAMETER
+#CFLAGS+= -DSET_MACHINE_TYPE
+
+# Initialize the bi_bios_geom using the BIOS geometry
+#CFLAGS+= -DGET_BIOSGEOM
+
+CFLAGS.clang+= -Oz ${CLANG_OPT_SMALL}
+
+LD_FLAGS=${LD_FLAGS_BIN}
+
+# Pick up ../Makefile.inc early.
+.include <bsd.init.mk>
+
+.PATH: ${.CURDIR}/../../i386/boot2
+
+CLEANFILES= boot
+
+boot: boot1 boot2
+ cat boot1 boot2 > boot
+
+CLEANFILES+= boot1 boot1.out boot1.o
+
+boot1: boot1.out
+ ${OBJCOPY} -S -O binary boot1.out ${.TARGET}
+
+boot1.out: boot1.o
+ ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} boot1.o
+
+CLEANFILES+= boot2 boot2.ld boot2.ldr boot2.bin boot2.out boot2.o \
+ boot2.s boot2.s.tmp boot2.h sio.o
+
+boot2: boot2.ld
+ @set -- `ls -l boot2.ld`; x=$$((7680-$$5)); \
+ echo "$$x bytes available"; test $$x -ge 0
+ ${DD} if=boot2.ld of=${.TARGET} obs=7680 conv=osync
+
+boot2.ld: boot2.ldr boot2.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \
+ -o ${.TARGET} -P 1 boot2.bin
+
+boot2.ldr:
+ ${DD} if=/dev/zero of=${.TARGET} bs=276 count=1
+
+boot2.bin: boot2.out
+ ${OBJCOPY} -S -O binary boot2.out ${.TARGET}
+
+boot2.out: ${BTXCRT} boot2.o sio.o
+ ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC}
+
+boot2.o: boot2.s
+ ${CC} ${ACFLAGS} -c boot2.s
+
+SRCS= boot2.c boot2.h
+
+boot2.s: boot2.c boot2.h ${.CURDIR}/../../common/ufsread.c
+ ${CC} ${CFLAGS} -S -o boot2.s.tmp ${.CURDIR}/boot2.c
+ sed -e '/align/d' -e '/nop/d' < boot2.s.tmp > boot2.s
+ rm -f boot2.s.tmp
+
+boot2.h: boot1.out
+ ${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T (read|putc)/ \
+ { x = $$1 - ORG1; \
+ printf("#define %sORG %#x\n", toupper($$3), REL1 + x) }' \
+ ORG1=`printf "%d" ${ORG1}` \
+ REL1=`printf "%d" ${REL1}` > ${.TARGET}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.boot1.S= ${CLANG_NO_IAS}
diff --git a/stand/pc98/boot2/boot1.S b/stand/pc98/boot2/boot1.S
new file mode 100644
index 0000000..5c97206
--- /dev/null
+++ b/stand/pc98/boot2/boot1.S
@@ -0,0 +1,395 @@
+/*-
+ * Copyright (c) 2008-2009 TAKAHASHI Yoshihiro
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Memory Locations */
+ .set STACK_OFF,0x6000 # Stack offset
+ .set LOAD_SIZE,8192 # Load size
+ .set DAUA,0x0584 # DA/UA
+ .set MEM_REL,0x700 # Relocation address
+ .set MEM_ARG,0x900 # Arguments
+ .set MEM_BUF,0x8cec # Load area
+ .set MEM_BTX,0x9000 # BTX start
+ .set MEM_JMP,0x9010 # BTX entry point
+ .set MEM_USR,0xa000 # Client start
+
+/* PC98 machine type from sys/pc98/pc98/pc98_machdep.h */
+ .set MEM_SYS, 0xa100 # System common area segment
+ .set PC98_MACHINE_TYPE, 0x0620 # PC98 machine type
+ .set EPSON_ID, 0x0624 # EPSON machine id
+
+ .set M_NEC_PC98, 0x0001
+ .set M_EPSON_PC98, 0x0002
+ .set M_NOT_H98, 0x0010
+ .set M_H98, 0x0020
+ .set M_NOTE, 0x0040
+ .set M_NORMAL, 0x1000
+ .set M_8M, 0x8000
+
+/* Partition Constants */
+ .set PRT_OFF,0x1be # Partition offset
+
+/* Misc. Constants */
+ .set SIZ_PAG,0x1000 # Page size
+ .set SIZ_SEC,0x200 # Sector size
+
+ .set NSECT,0x10
+
+ .globl start
+ .globl read
+ .globl putc
+ .code16
+
+start: jmp main
+
+boot_cyl: .org 4
+ .ascii "IPL1 "
+
+main: cld
+
+ /* Setup the stack */
+ xor %si,%si
+ mov %si,%ss
+ mov $STACK_OFF,%sp
+
+ push %cx
+
+ /* Relocate ourself to MEM_REL */
+ push %cs
+ pop %ds
+ mov %si,%es
+ mov $MEM_REL,%di
+ mov $SIZ_SEC,%cx
+ rep
+ movsb
+
+ /* Transfer PC-9801 system common area */
+ xor %ax,%ax
+ mov %ax,%si
+ mov %ax,%ds
+ mov %ax,%di
+ mov $MEM_SYS,%ax
+ mov %ax,%es
+ mov $0x0600,%cx
+ rep
+ movsb
+
+ /* Transfer EPSON machine type */
+ mov $0xfd00,%ax
+ mov %ax,%ds
+ mov (0x804),%eax
+ and $0x00ffffff,%eax
+ mov %eax,%es:(EPSON_ID)
+
+ /* Set machine type to PC98_SYSTEM_PARAMETER */
+#ifdef SET_MACHINE_TYPE
+ call set_machine_type
+#else
+ mov $M_NEC_PC98+M_NOT_H98,%eax
+ mov %eax,%es:(PC98_MACHINE_TYPE)
+#endif
+
+ /* Setup graphic screen */
+ mov $0x42,%ah /* 640x400 */
+ mov $0xc0,%ch
+ int $0x18
+ mov $0x40,%ah /* graph on */
+ int $0x18
+
+ /* Setup text screen */
+ mov $0x0a00,%ax /* 80x25 */
+ int $0x18
+ mov $0x0c,%ah /* text on */
+ int $0x18
+ mov $0x13,%ah /* cursor home */
+ xor %dx,%dx
+ int $0x18
+ mov $0x11,%ah /* cursor on */
+ int $0x18
+
+ /* Setup keyboard */
+ mov $0x03,%ah
+ int $0x18
+
+ pop %cx
+
+ /* bootstrap passes */
+ xor %edi,%edi
+ mov %di,%ds
+ mov %di,%es
+ mov %cs,%bx
+ cmp $0x1fe0,%bx
+ jz boot_fd
+ cmp $0x1fc0,%bx
+ jnz boot_hd
+ xor %cx,%cx
+ mov (DAUA),%al
+ and $0xf0,%al
+ cmp $0x30,%al
+ jz boot_fd
+ cmp $0x90,%al
+ jnz boot_hd
+boot_fd: xor %cx,%cx
+ jmp boot_load
+boot_hd: test %cx,%cx
+ jnz boot_load
+ mov %cs:(boot_cyl),%cx
+boot_load: mov %cx,MEM_ARG /* Save cylinder number */
+ mov %cx,%di
+ xor %dx,%dx
+ mov $LOAD_SIZE,%bx
+ mov $MEM_BUF,%bp
+ push %cs
+ callw read
+ jc error
+
+ /* Transfer boot2.bin */
+ mov $MEM_BTX,%bx
+ mov 0xa(%bx),%si /* BTX size */
+ add %bx,%si /* start of boot2.bin */
+ mov $MEM_USR+SIZ_PAG*2,%di
+ mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx
+ sub %si,%cx
+ rep
+ movsb
+
+ /* Enable A20 */
+ xor %ax,%ax
+ outb %al,$0xf2
+ mov $0x02,%al
+ outb %al,$0xf6
+
+ /* Start BTX */
+ ljmp $0x0000,$MEM_JMP
+
+/*
+ * Reads sectors from the disk.
+ * Call with:
+ *
+ * %bx - bytes to read
+ * %cx - cylinder
+ * %dh - head
+ * %dl - sector
+ * %edi - lba
+ * %es:(%bp) - buffer to read data into
+ */
+read: xor %ax,%ax
+ mov %ax,%ds
+ mov $0x06,%ah
+ mov (DAUA),%al
+ mov %ax,%si
+ and $0xf0,%al
+ cmp $0x30,%al /* 1.44MB FDD */
+ jz read_fd
+ cmp $0x90,%al /* 1MB FDD */
+ jz read_fd
+ cmp $0xa0,%al /* Is SCSI device? */
+ jnz read_load
+ push %cx
+ mov %si,%cx
+ and $0x0f,%cl
+ inc %cl
+ mov (0x482),%ah
+ shr %cl,%ah /* Is SCSI HDD? */
+ pop %cx
+ jc read_load
+ and $0xff7f,%si /* SCSI MO */
+ mov %di,%cx
+ shr $16,%edi
+ mov %di,%dx
+ jmp read_load
+read_fd: or $0xd000,%si
+ or $0x0200,%cx
+ inc %dx
+read_load: mov %si,%ax
+ int $0x1b
+ lret
+
+/*
+ * Print out the error message, wait for a keypress, and then reboot
+ * the machine.
+ */
+error: push %cs
+ pop %ds
+ mov $msg_eread,%si
+ call putstr
+ xor %ax,%ax /* Get keypress */
+ int $0x18
+ xor %ax,%ax /* CPU reset */
+ outb %al,$0xf0
+halt: hlt
+ jmp halt /* Spin */
+
+/*
+ * Display a null-terminated string.
+ */
+putstr.0: push %cs
+ callw putc
+putstr: lodsb
+ test %al,%al
+ jne putstr.0
+ ret
+
+/*
+ * Display a single char.
+ */
+putc: pusha
+ xor %dx,%dx
+ mov %dx,%ds
+ mov MEM_REL+cursor-start,%di
+ mov $0xa000,%bx
+ mov %bx,%es
+ mov $(80*2),%cx
+
+ cmp $0x08,%al
+ je putc.bs
+ cmp $0x0d,%al
+ je putc.cr
+ cmp $0x0a,%al
+ je putc.lf
+ cmp $0x5c,%al /* \ */
+ jne 1f
+ mov $0xfc,%al
+1: movb $0xe1,%es:0x2000(%di)
+ stosw
+ jmp putc.scr
+putc.bs: test %di,%di
+ jz putc.move
+ dec %di
+ dec %di
+ movb $0xe1,%es:0x2000(%di)
+ movw $0x20,%es:(%di)
+ jmp putc.move
+putc.cr: mov %di,%ax
+ div %cx
+ sub %dx,%di
+ jmp putc.move
+putc.lf: add %cx,%di
+putc.scr: cmp $(80*2*25),%di /* Scroll screen */
+ jb putc.move
+ push %ds
+ mov %bx,%ds
+ mov $(80*2),%si
+ xor %di,%di
+ mov $(80*24/2),%cx
+ rep
+ movsl
+ xor %ax,%ax
+ mov $0x20,%al
+ mov $80,%cl
+ rep
+ stosw
+ pop %ds
+ mov $(80*24*2),%di
+putc.move: mov %di,MEM_REL+cursor-start /* Move cursor */
+ mov $0x13,%ah
+ mov %di,%dx
+ int $0x18
+ popa
+ lret
+
+cursor: .word 0
+
+#ifdef SET_MACHINE_TYPE
+/*
+ * Set machine type to PC98_SYSTEM_PARAMETER.
+ */
+set_machine_type:
+ xor %edx,%edx
+ mov %dx,%ds
+// mov $MEM_SYS,%ax
+// mov %ax,%es
+
+ /* Wait V-SYNC */
+vsync.1: inb $0x60,%al
+ test $0x20,%al
+ jnz vsync.1
+vsync.2: inb $0x60,%al
+ test $0x20,%al
+ jz vsync.2
+
+ /* ANK 'A' font */
+ xor %al,%al
+ outb %al,$0xa1
+ mov $0x41,%al
+ outb %al,$0xa3
+
+ /* Get 'A' font from CG window */
+ push %ds
+ mov $0xa400,%ax
+ mov %ax,%ds
+ xor %eax,%eax
+ xor %bx,%bx
+ mov $4,%cx
+font.1: add (%bx),%eax
+ add $4,%bx
+ loop font.1
+ pop %ds
+ cmp $0x6efc58fc,%eax
+ jnz m_epson
+
+m_pc98: or $M_NEC_PC98,%edx
+ mov $0x0458,%bx
+ mov (%bx),%al
+ test $0x80,%al
+ jz m_not_h98
+ or $M_H98,%edx
+ jmp 1f
+m_epson: or $M_EPSON_PC98,%edx
+m_not_h98: or $M_NOT_H98,%edx
+
+1: inb $0x42,%al
+ test $0x20,%al
+ jz 1f
+ or $M_8M,%edx
+
+1: mov $0x0400,%bx
+ mov (%bx),%al
+ test $0x80,%al
+ jz 1f
+ or $M_NOTE,%edx
+
+1: mov $PC98_MACHINE_TYPE,%bx
+ mov %edx,%es:(%bx)
+ ret
+#endif
+
+/* Messages */
+
+msg_eread: .asciz "Error\r\n"
+
+ .org PRT_OFF,0x90
+
+/* Partition table */
+
+ .fill 0x30,0x1,0x0
+ .byte 0x80, 0x00, 0x01, 0x00
+ .byte 0xa5, 0xff, 0xff, 0xff
+ .byte 0x00, 0x00, 0x00, 0x00
+ .byte 0x50, 0xc3, 0x00, 0x00
+
+ .word 0xaa55 # Magic number
diff --git a/stand/pc98/boot2/boot2.c b/stand/pc98/boot2/boot2.c
new file mode 100644
index 0000000..b582516
--- /dev/null
+++ b/stand/pc98/boot2/boot2.c
@@ -0,0 +1,803 @@
+/*-
+ * Copyright (c) 2008-2009 TAKAHASHI Yoshihiro
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/diskpc98.h>
+#include <sys/dirent.h>
+#include <sys/reboot.h>
+
+#include <machine/bootinfo.h>
+#include <machine/cpufunc.h>
+#include <machine/elf.h>
+
+#include <stdarg.h>
+
+#include <a.out.h>
+
+#include <btxv86.h>
+
+#include "boot2.h"
+#include "lib.h"
+#include "paths.h"
+#include "rbx.h"
+
+/* Define to 0 to omit serial support */
+#ifndef SERIAL
+#define SERIAL 0
+#endif
+
+#define IO_KEYBOARD 1
+#define IO_SERIAL 2
+
+#if SERIAL
+#define DO_KBD (ioctrl & IO_KEYBOARD)
+#define DO_SIO (ioctrl & IO_SERIAL)
+#else
+#define DO_KBD (1)
+#define DO_SIO (0)
+#endif
+
+#define SECOND 1 /* Circa that many ticks in a second. */
+
+#define ARGS 0x900
+#define NOPT 14
+#define NDEV 3
+
+#define DRV_DISK 0xf0
+#define DRV_UNIT 0x0f
+
+#define TYPE_AD 0
+#define TYPE_DA 1
+#define TYPE_FD 2
+
+extern uint32_t _end;
+
+static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
+static const unsigned char flags[NOPT] = {
+ RBX_DUAL,
+ RBX_SERIAL,
+ RBX_ASKNAME,
+ RBX_CDROM,
+ RBX_CONFIG,
+ RBX_KDB,
+ RBX_GDB,
+ RBX_MUTE,
+ RBX_NOINTR,
+ RBX_PAUSE,
+ RBX_QUIET,
+ RBX_DFLTROOT,
+ RBX_SINGLE,
+ RBX_VERBOSE
+};
+
+static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
+static const unsigned char dev_maj[NDEV] = {30, 4, 2};
+static const unsigned char dev_daua[NDEV] = {0x80, 0xa0, 0x90};
+
+static struct dsk {
+ unsigned daua;
+ unsigned type;
+ unsigned disk;
+ unsigned unit;
+ unsigned head;
+ unsigned sec;
+ uint8_t slice;
+ uint8_t part;
+ unsigned start;
+} dsk;
+static char cmd[512], cmddup[512], knamebuf[1024];
+static const char *kname;
+uint32_t opts;
+static struct bootinfo bootinfo;
+#if SERIAL
+static int comspeed = SIOSPD;
+static uint8_t ioctrl = IO_KEYBOARD;
+#endif
+
+int main(void);
+void exit(int);
+static void load(void);
+static int parse(void);
+static int dskread(void *, unsigned, unsigned);
+static void printf(const char *,...);
+static void putchar(int);
+static int drvread(void *, unsigned);
+static int keyhit(unsigned);
+static int xputc(int);
+static int xgetc(int);
+static inline int getc(int);
+
+static void memcpy(void *, const void *, int);
+static void
+memcpy(void *dst, const void *src, int len)
+{
+ const char *s = src;
+ char *d = dst;
+
+ while (len--)
+ *d++ = *s++;
+}
+
+static inline int
+strcmp(const char *s1, const char *s2)
+{
+ for (; *s1 == *s2 && *s1; s1++, s2++);
+ return (unsigned char)*s1 - (unsigned char)*s2;
+}
+
+#define UFS_SMALL_CGBASE
+#include "ufsread.c"
+
+static inline int
+xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
+{
+ if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
+ printf("Invalid %s\n", "format");
+ return -1;
+ }
+ return 0;
+}
+
+static inline void
+getstr(void)
+{
+ char *s;
+ int c;
+
+ s = cmd;
+ for (;;) {
+ switch (c = xgetc(0)) {
+ case 0:
+ break;
+ case '\177':
+ case '\b':
+ if (s > cmd) {
+ s--;
+ printf("\b \b");
+ }
+ break;
+ case '\n':
+ case '\r':
+ *s = 0;
+ return;
+ default:
+ if (s - cmd < sizeof(cmd) - 1)
+ *s++ = c;
+ putchar(c);
+ }
+ }
+}
+
+static inline void
+putc(int c)
+{
+
+ v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
+ v86.addr = PUTCORG; /* call to putc in boot1 */
+ v86.eax = c;
+ v86int();
+ v86.ctl = V86_FLAGS;
+}
+
+static inline int
+is_scsi_hd(void)
+{
+
+ if ((*(u_char *)PTOV(0x482) >> dsk.unit) & 0x01)
+ return 1;
+
+ return 0;
+}
+
+static inline void
+fix_sector_size(void)
+{
+ u_char *p;
+
+ p = (u_char *)PTOV(0x460 + dsk.unit * 4); /* SCSI equipment parameter */
+
+ if ((p[0] & 0x1f) == 7) { /* SCSI MO */
+ if (!(p[3] & 0x30)) { /* 256B / sector */
+ p[3] |= 0x10; /* forced set 512B / sector */
+ p[3 + 0xa1000] |= 0x10;
+ }
+ }
+}
+
+static inline uint32_t
+get_diskinfo(void)
+{
+
+ if (dsk.disk == 0x30) { /* 1440KB FD */
+ /* 80 cylinders, 2 heads, 18 sectors */
+ return (80 << 16) | (2 << 8) | 18;
+ } else if (dsk.disk == 0x90) { /* 1200KB FD */
+ /* 80 cylinders, 2 heads, 15 sectors */
+ return (80 << 16) | (2 << 8) | 15;
+ } else if (dsk.disk == 0x80 || is_scsi_hd()) { /* IDE or SCSI HDD */
+ v86.addr = 0x1b;
+ v86.eax = 0x8400 | dsk.daua;
+ v86int();
+ return (v86.ecx << 16) | v86.edx;
+ }
+
+ /* SCSI MO or CD */
+ fix_sector_size(); /* SCSI MO */
+
+ /* other SCSI devices */
+ return (65535 << 16) | (8 << 8) | 32;
+}
+
+static void
+set_dsk(void)
+{
+ uint32_t di;
+
+ di = get_diskinfo();
+
+ dsk.head = (di >> 8) & 0xff;
+ dsk.sec = di & 0xff;
+ dsk.start = 0;
+}
+
+#ifdef GET_BIOSGEOM
+static uint32_t
+bd_getbigeom(int bunit)
+{
+ int hds = 0;
+ int unit = 0x80; /* IDE HDD */
+ u_int addr = 0x55d;
+
+ while (unit < 0xa7) {
+ if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f)))
+ if (hds++ == bunit)
+ break;
+
+ if (unit >= 0xA0) {
+ int media = ((unsigned *)PTOV(0x460))[unit & 0x0F] & 0x1F;
+
+ if (media == 7 && hds++ == bunit) /* SCSI MO */
+ return(0xFFFE0820); /* C:65535 H:8 S:32 */
+ }
+ if (++unit == 0x84) {
+ unit = 0xA0; /* SCSI HDD */
+ addr = 0x482;
+ }
+ }
+ if (unit == 0xa7)
+ return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */
+ v86.addr = 0x1b;
+ v86.eax = 0x8400 | unit;
+ v86int();
+ if (V86_CY(v86.efl))
+ return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */
+ return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff);
+}
+#endif
+
+static int
+check_slice(void)
+{
+ struct pc98_partition *dp;
+ char *sec;
+ unsigned i, cyl;
+
+ sec = dmadat->secbuf;
+ cyl = *(uint16_t *)PTOV(ARGS);
+ set_dsk();
+
+ if (dsk.type == TYPE_FD)
+ return (WHOLE_DISK_SLICE);
+ if (drvread(sec, PC98_BBSECTOR))
+ return (WHOLE_DISK_SLICE); /* Read error */
+ dp = (void *)(sec + PC98_PARTOFF);
+ for (i = 0; i < PC98_NPARTS; i++) {
+ if (dp[i].dp_mid == DOSMID_386BSD) {
+ if (dp[i].dp_scyl <= cyl && cyl <= dp[i].dp_ecyl)
+ return (BASE_SLICE + i);
+ }
+ }
+
+ return (WHOLE_DISK_SLICE);
+}
+
+int
+main(void)
+{
+#ifdef GET_BIOSGEOM
+ int i;
+#endif
+ uint8_t autoboot;
+ ufs_ino_t ino;
+ size_t nbyte;
+
+ dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+ v86.ctl = V86_FLAGS;
+ v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
+ dsk.daua = *(uint8_t *)PTOV(0x584);
+ dsk.disk = dsk.daua & DRV_DISK;
+ dsk.unit = dsk.daua & DRV_UNIT;
+ if (dsk.disk == 0x80)
+ dsk.type = TYPE_AD;
+ else if (dsk.disk == 0xa0)
+ dsk.type = TYPE_DA;
+ else /* if (dsk.disk == 0x30 || dsk.disk == 0x90) */
+ dsk.type = TYPE_FD;
+ dsk.slice = check_slice();
+#ifdef GET_BIOSGEOM
+ for (i = 0; i < N_BIOS_GEOM; i++)
+ bootinfo.bi_bios_geom[i] = bd_getbigeom(i);
+#endif
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+
+ /* Process configuration file */
+
+ autoboot = 1;
+
+ if ((ino = lookup(PATH_CONFIG)) ||
+ (ino = lookup(PATH_DOTCONFIG))) {
+ nbyte = fsread(ino, cmd, sizeof(cmd) - 1);
+ cmd[nbyte] = '\0';
+ }
+
+ if (*cmd) {
+ memcpy(cmddup, cmd, sizeof(cmd));
+ if (parse())
+ autoboot = 0;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%s: %s", PATH_CONFIG, cmddup);
+ /* Do not process this command twice */
+ *cmd = 0;
+ }
+
+ /*
+ * Try to exec stage 3 boot loader. If interrupted by a keypress,
+ * or in case of failure, try to load a kernel directly instead.
+ */
+
+ if (!kname) {
+ kname = PATH_LOADER;
+ if (autoboot && !keyhit(3*SECOND)) {
+ load();
+ kname = PATH_KERNEL;
+ }
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ printf("\nFreeBSD/pc98 boot\n"
+ "Default: %u:%s(%u,%c)%s\n"
+ "boot: ",
+ dsk.unit, dev_nm[dsk.type], dsk.unit,
+ 'a' + dsk.part, kname);
+ if (DO_SIO)
+ sio_flush();
+ if (!autoboot || keyhit(3*SECOND))
+ getstr();
+ else if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ putchar('\n');
+ autoboot = 0;
+ if (parse())
+ putchar('\a');
+ else
+ load();
+ }
+}
+
+/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
+void
+exit(int x)
+{
+}
+
+static void
+load(void)
+{
+ union {
+ struct exec ex;
+ Elf32_Ehdr eh;
+ } hdr;
+ static Elf32_Phdr ep[2];
+ static Elf32_Shdr es[2];
+ caddr_t p;
+ ufs_ino_t ino;
+ uint32_t addr;
+ int k;
+ uint8_t i, j;
+
+ if (!(ino = lookup(kname))) {
+ if (!ls)
+ printf("No %s\n", kname);
+ return;
+ }
+ if (xfsread(ino, &hdr, sizeof(hdr)))
+ return;
+
+ if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
+ addr = hdr.ex.a_entry & 0xffffff;
+ p = PTOV(addr);
+ fs_off = PAGE_SIZE;
+ if (xfsread(ino, p, hdr.ex.a_text))
+ return;
+ p += roundup2(hdr.ex.a_text, PAGE_SIZE);
+ if (xfsread(ino, p, hdr.ex.a_data))
+ return;
+ } else if (IS_ELF(hdr.eh)) {
+ fs_off = hdr.eh.e_phoff;
+ for (j = k = 0; k < hdr.eh.e_phnum && j < 2; k++) {
+ if (xfsread(ino, ep + j, sizeof(ep[0])))
+ return;
+ if (ep[j].p_type == PT_LOAD)
+ j++;
+ }
+ for (i = 0; i < 2; i++) {
+ p = PTOV(ep[i].p_paddr & 0xffffff);
+ fs_off = ep[i].p_offset;
+ if (xfsread(ino, p, ep[i].p_filesz))
+ return;
+ }
+ p += roundup2(ep[1].p_memsz, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
+ fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
+ (hdr.eh.e_shstrndx + 1);
+ if (xfsread(ino, &es, sizeof(es)))
+ return;
+ for (i = 0; i < 2; i++) {
+ *(Elf32_Word *)p = es[i].sh_size;
+ p += sizeof(es[i].sh_size);
+ fs_off = es[i].sh_offset;
+ if (xfsread(ino, p, es[i].sh_size))
+ return;
+ p += es[i].sh_size;
+ }
+ }
+ addr = hdr.eh.e_entry & 0xffffff;
+ bootinfo.bi_esymtab = VTOP(p);
+ } else {
+ printf("Invalid %s\n", "format");
+ return;
+ }
+
+ bootinfo.bi_kernelname = VTOP(kname);
+ bootinfo.bi_bios_dev = dsk.daua;
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
+ MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),
+ 0, 0, 0, VTOP(&bootinfo));
+}
+
+static int
+parse()
+{
+ char *arg = cmd;
+ char *ep, *p, *q;
+ const char *cp;
+ unsigned int drv;
+ int c, i, j;
+ size_t k;
+
+ while ((c = *arg++)) {
+ if (c == ' ' || c == '\t' || c == '\n')
+ continue;
+ for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+ ep = p;
+ if (*p)
+ *p++ = 0;
+ if (c == '-') {
+ while ((c = *arg++)) {
+ if (c == 'P') {
+ if (*(uint8_t *)PTOV(0x481) & 0x48) {
+ cp = "yes";
+ } else {
+ opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
+ cp = "no";
+ }
+ printf("Keyboard: %s\n", cp);
+ continue;
+#if SERIAL
+ } else if (c == 'S') {
+ j = 0;
+ while ((unsigned int)(i = *arg++ - '0') <= 9)
+ j = j * 10 + i;
+ if (j > 0 && i == -'0') {
+ comspeed = j;
+ break;
+ }
+ /* Fall through to error below ('S' not in optstr[]). */
+#endif
+ }
+ for (i = 0; c != optstr[i]; i++)
+ if (i == NOPT - 1)
+ return -1;
+ opts ^= OPT_SET(flags[i]);
+ }
+#if SERIAL
+ ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
+ OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
+ if (DO_SIO) {
+ if (sio_init(115200 / comspeed) != 0)
+ ioctrl &= ~IO_SERIAL;
+ }
+#endif
+ } else {
+ for (q = arg--; *q && *q != '('; q++);
+ if (*q) {
+ drv = -1;
+ if (arg[1] == ':') {
+ drv = *arg - '0';
+ if (drv > 9)
+ return (-1);
+ arg += 2;
+ }
+ if (q - arg != 2)
+ return -1;
+ for (i = 0; arg[0] != dev_nm[i][0] ||
+ arg[1] != dev_nm[i][1]; i++)
+ if (i == NDEV - 1)
+ return -1;
+ dsk.type = i;
+ arg += 3;
+ dsk.unit = *arg - '0';
+ if (arg[1] != ',' || dsk.unit > 9)
+ return -1;
+ arg += 2;
+ dsk.slice = WHOLE_DISK_SLICE;
+ if (arg[1] == ',') {
+ dsk.slice = *arg - '0' + 1;
+ if (dsk.slice > PC98_NPARTS + 1)
+ return -1;
+ arg += 2;
+ }
+ if (arg[1] != ')')
+ return -1;
+ dsk.part = *arg - 'a';
+ if (dsk.part > 7)
+ return (-1);
+ arg += 2;
+ if (drv == -1)
+ drv = dsk.unit;
+ dsk.disk = dev_daua[dsk.type];
+ dsk.daua = dsk.disk | dsk.unit;
+ dsk_meta = 0;
+ }
+ k = ep - arg;
+ if (k > 0) {
+ if (k >= sizeof(knamebuf))
+ return -1;
+ memcpy(knamebuf, arg, k + 1);
+ kname = knamebuf;
+ }
+ }
+ arg = p;
+ }
+ return 0;
+}
+
+static int
+dskread(void *buf, unsigned lba, unsigned nblk)
+{
+ struct pc98_partition *dp;
+ struct disklabel *d;
+ char *sec;
+ unsigned i;
+ uint8_t sl;
+ u_char *p;
+ const char *reason;
+
+ if (!dsk_meta) {
+ sec = dmadat->secbuf;
+ set_dsk();
+ if (dsk.type == TYPE_FD)
+ goto unsliced;
+ if (drvread(sec, PC98_BBSECTOR))
+ return -1;
+ dp = (void *)(sec + PC98_PARTOFF);
+ sl = dsk.slice;
+ if (sl < BASE_SLICE) {
+ for (i = 0; i < PC98_NPARTS; i++)
+ if (dp[i].dp_mid == DOSMID_386BSD) {
+ sl = BASE_SLICE + i;
+ break;
+ }
+ dsk.slice = sl;
+ }
+ if (sl != WHOLE_DISK_SLICE) {
+ dp += sl - BASE_SLICE;
+ if (dp->dp_mid != DOSMID_386BSD) {
+ reason = "slice";
+ goto error;
+ }
+ dsk.start = dp->dp_scyl * dsk.head * dsk.sec +
+ dp->dp_shd * dsk.sec + dp->dp_ssect;
+ }
+ if (drvread(sec, dsk.start + LABELSECTOR))
+ return -1;
+ d = (void *)(sec + LABELOFFSET);
+ if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
+ if (dsk.part != RAW_PART) {
+ reason = "label";
+ goto error;
+ }
+ } else {
+ if (dsk.part >= d->d_npartitions ||
+ !d->d_partitions[dsk.part].p_size) {
+ reason = "partition";
+ goto error;
+ }
+ dsk.start += d->d_partitions[dsk.part].p_offset;
+ dsk.start -= d->d_partitions[RAW_PART].p_offset;
+ }
+ unsliced: ;
+ }
+ for (p = buf; nblk; p += 512, lba++, nblk--) {
+ if ((i = drvread(p, dsk.start + lba)))
+ return i;
+ }
+ return 0;
+error:
+ printf("Invalid %s\n", reason);
+ return -1;
+}
+
+static void
+printf(const char *fmt,...)
+{
+ va_list ap;
+ static char buf[10];
+ char *s;
+ unsigned u;
+ int c;
+
+ va_start(ap, fmt);
+ while ((c = *fmt++)) {
+ if (c == '%') {
+ c = *fmt++;
+ switch (c) {
+ case 'c':
+ putchar(va_arg(ap, int));
+ continue;
+ case 's':
+ for (s = va_arg(ap, char *); *s; s++)
+ putchar(*s);
+ continue;
+ case 'u':
+ u = va_arg(ap, unsigned);
+ s = buf;
+ do
+ *s++ = '0' + u % 10U;
+ while (u /= 10U);
+ while (--s >= buf)
+ putchar(*s);
+ continue;
+ }
+ }
+ putchar(c);
+ }
+ va_end(ap);
+ return;
+}
+
+static void
+putchar(int c)
+{
+ if (c == '\n')
+ xputc('\r');
+ xputc(c);
+}
+
+static int
+drvread(void *buf, unsigned lba)
+{
+ static unsigned c = 0x2d5c7c2f;
+ unsigned bpc, x, cyl, head, sec;
+
+ bpc = dsk.sec * dsk.head;
+ cyl = lba / bpc;
+ x = lba % bpc;
+ head = x / dsk.sec;
+ sec = x % dsk.sec;
+
+ if (!OPT_CHECK(RBX_QUIET)) {
+ xputc(c = c << 8 | c >> 24);
+ xputc('\b');
+ }
+ v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
+ v86.addr = READORG; /* call to read in boot1 */
+ v86.ecx = cyl;
+ v86.edx = (head << 8) | sec;
+ v86.edi = lba;
+ v86.ebx = 512;
+ v86.es = VTOPSEG(buf);
+ v86.ebp = VTOPOFF(buf);
+ v86int();
+ v86.ctl = V86_FLAGS;
+ if (V86_CY(v86.efl)) {
+ printf("error %u c/h/s %u/%u/%u lba %u\n", v86.eax >> 8 & 0xff,
+ cyl, head, sec, lba);
+ return -1;
+ }
+ return 0;
+}
+
+static inline void
+delay(void)
+{
+ int i;
+
+ i = 800;
+ do {
+ outb(0x5f, 0); /* about 600ns */
+ } while (--i >= 0);
+}
+
+static int
+keyhit(unsigned sec)
+{
+ unsigned i;
+
+ if (OPT_CHECK(RBX_NOINTR))
+ return 0;
+ for (i = 0; i < sec * 1000; i++) {
+ if (xgetc(1))
+ return 1;
+ delay();
+ }
+ return 0;
+}
+
+static int
+xputc(int c)
+{
+ if (DO_KBD)
+ putc(c);
+ if (DO_SIO)
+ sio_putc(c);
+ return c;
+}
+
+static int
+getc(int fn)
+{
+ v86.addr = 0x18;
+ v86.eax = fn << 8;
+ v86int();
+ if (fn)
+ return (v86.ebx >> 8) & 0x01;
+ else
+ return v86.eax & 0xff;
+}
+
+static int
+xgetc(int fn)
+{
+ if (OPT_CHECK(RBX_NOINTR))
+ return 0;
+ for (;;) {
+ if (DO_KBD && getc(1))
+ return fn ? 1 : getc(0);
+ if (DO_SIO && sio_ischar())
+ return fn ? 1 : sio_getc();
+ if (fn)
+ return 0;
+ }
+}
diff --git a/stand/pc98/btx/Makefile b/stand/pc98/btx/Makefile
new file mode 100644
index 0000000..39f78ed
--- /dev/null
+++ b/stand/pc98/btx/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= btx btxldr lib
+
+.include <bsd.subdir.mk>
diff --git a/stand/pc98/btx/Makefile.inc b/stand/pc98/btx/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/stand/pc98/btx/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/stand/pc98/btx/btx/Makefile b/stand/pc98/btx/btx/Makefile
new file mode 100644
index 0000000..2755460
--- /dev/null
+++ b/stand/pc98/btx/btx/Makefile
@@ -0,0 +1,33 @@
+# $FreeBSD$
+
+PROG= btx
+INTERNALPROG=
+MAN=
+SRCS= btx.S
+
+.if defined(BOOT_BTX_NOHANG)
+BOOT_BTX_FLAGS=0x1
+.else
+BOOT_BTX_FLAGS=0x0
+.endif
+
+CFLAGS+=-DBTX_FLAGS=${BOOT_BTX_FLAGS}
+CFLAGS+=-I${.CURDIR}/../../../i386/common
+
+.if defined(BTX_SERIAL)
+BOOT_COMCONSOLE_PORT?= 0x238
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+CFLAGS+=-DBTX_SERIAL -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} -DSIOSPD=${BOOT_COMCONSOLE_SPEED}
+.endif
+
+ORG= 0x9000
+
+LDFLAGS=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.btx.S= ${CLANG_NO_IAS}
diff --git a/stand/pc98/btx/btx/btx.S b/stand/pc98/btx/btx/btx.S
new file mode 100644
index 0000000..e8710d3
--- /dev/null
+++ b/stand/pc98/btx/btx/btx.S
@@ -0,0 +1,1104 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+#include <bootargs.h>
+
+/*
+ * Memory layout.
+ */
+ .set MEM_BTX,0x1000 # Start of BTX memory
+ .set MEM_ESP0,0x1800 # Supervisor stack
+ .set MEM_BUF,0x1800 # Scratch buffer
+ .set MEM_ESPR,0x5e00 # Real mode stack
+ .set MEM_IDT,0x5e00 # IDT
+ .set MEM_TSS,0x5f98 # TSS
+ .set MEM_MAP,0x6000 # I/O bit map
+ .set MEM_TSS_END,0x7fff # End of TSS
+ .set MEM_ORG,0x9000 # BTX code
+ .set MEM_USR,0xa000 # Start of user memory
+/*
+ * Paging control.
+ */
+ .set PAG_SIZ,0x1000 # Page size
+ .set PAG_CNT,0x1000 # Pages to map
+/*
+ * Fields in %eflags.
+ */
+ .set PSL_RESERVED_DEFAULT,0x00000002
+ .set PSL_T,0x00000100 # Trap flag
+ .set PSL_I,0x00000200 # Interrupt enable flag
+ .set PSL_VM,0x00020000 # Virtual 8086 mode flag
+ .set PSL_AC,0x00040000 # Alignment check flag
+/*
+ * Segment selectors.
+ */
+ .set SEL_SCODE,0x8 # Supervisor code
+ .set SEL_SDATA,0x10 # Supervisor data
+ .set SEL_RCODE,0x18 # Real mode code
+ .set SEL_RDATA,0x20 # Real mode data
+ .set SEL_UCODE,0x28|3 # User code
+ .set SEL_UDATA,0x30|3 # User data
+ .set SEL_TSS,0x38 # TSS
+/*
+ * Task state segment fields.
+ */
+ .set TSS_ESP0,0x4 # PL 0 ESP
+ .set TSS_SS0,0x8 # PL 0 SS
+ .set TSS_MAP,0x66 # I/O bit map base
+/*
+ * System calls.
+ */
+ .set SYS_EXIT,0x0 # Exit
+ .set SYS_EXEC,0x1 # Exec
+/*
+ * Fields in V86 interface structure.
+ */
+ .set V86_CTL,0x0 # Control flags
+ .set V86_ADDR,0x4 # Int number/address
+ .set V86_ES,0x8 # V86 ES
+ .set V86_DS,0xc # V86 DS
+ .set V86_FS,0x10 # V86 FS
+ .set V86_GS,0x14 # V86 GS
+/*
+ * V86 control flags.
+ */
+ .set V86F_ADDR,0x10000 # Segment:offset address
+ .set V86F_CALLF,0x20000 # Emulate far call
+ .set V86F_FLAGS,0x40000 # Return flags
+/*
+ * Dump format control bytes.
+ */
+ .set DMP_X16,0x1 # Word
+ .set DMP_X32,0x2 # Long
+ .set DMP_MEM,0x4 # Memory
+ .set DMP_EOL,0x8 # End of line
+/*
+ * Screen defaults and assumptions.
+ */
+ .set SCR_MAT,0xe1 # Mode/attribute
+ .set SCR_COL,0x50 # Columns per row
+ .set SCR_ROW,0x19 # Rows per screen
+/*
+ * BIOS Data Area locations.
+ */
+ .set BDA_MEM,0x501 # Free memory
+ .set BDA_POS,0x53e # Cursor position
+/*
+ * Derivations, for brevity.
+ */
+ .set _ESP0H,MEM_ESP0>>0x8 # Byte 1 of ESP0
+ .set _TSSIO,MEM_MAP-MEM_TSS # TSS I/O base
+ .set _TSSLM,MEM_TSS_END-MEM_TSS # TSS limit
+ .set _IDTLM,MEM_TSS-MEM_IDT-1 # IDT limit
+/*
+ * Code segment.
+ */
+ .globl start
+ .code16
+start: # Start of code
+/*
+ * BTX header.
+ */
+btx_hdr: .byte 0xeb # Machine ID
+ .byte 0xe # Header size
+ .ascii "BTX" # Magic
+ .byte 0x1 # Major version
+ .byte 0x2 # Minor version
+ .byte BTX_FLAGS # Flags
+ .word PAG_CNT-MEM_ORG>>0xc # Paging control
+ .word break-start # Text size
+ .long 0x0 # Entry address
+/*
+ * Initialization routine.
+ */
+init: cli # Disable interrupts
+ xor %ax,%ax # Zero/segment
+ mov %ax,%ss # Set up
+ mov $MEM_ESP0,%sp # stack
+ mov %ax,%es # Address
+ mov %ax,%ds # data
+ pushl $0x2 # Clear
+ popfl # flags
+/*
+ * Initialize memory.
+ */
+ mov $MEM_IDT,%di # Memory to initialize
+ mov $(MEM_ORG-MEM_IDT)/2,%cx # Words to zero
+ rep # Zero-fill
+ stosw # memory
+/*
+ * Update real mode IDT for reflecting hardware interrupts.
+ */
+ mov $intr20,%bx # Address first handler
+ mov $0x10,%cx # Number of handlers
+ mov $0x20*4,%di # First real mode IDT entry
+init.0: mov %bx,(%di) # Store IP
+ inc %di # Address next
+ inc %di # entry
+ stosw # Store CS
+ add $4,%bx # Next handler
+ loop init.0 # Next IRQ
+/*
+ * Create IDT.
+ */
+ mov $MEM_IDT,%di
+ mov $idtctl,%si # Control string
+init.1: lodsb # Get entry
+ cbw # count
+ xchg %ax,%cx # as word
+ jcxz init.4 # If done
+ lodsb # Get segment
+ xchg %ax,%dx # P:DPL:type
+ lodsw # Get control
+ xchg %ax,%bx # set
+ lodsw # Get handler offset
+ mov $SEL_SCODE,%dh # Segment selector
+init.2: shr %bx # Handle this int?
+ jnc init.3 # No
+ mov %ax,(%di) # Set handler offset
+ mov %dh,0x2(%di) # and selector
+ mov %dl,0x5(%di) # Set P:DPL:type
+ add $0x4,%ax # Next handler
+init.3: lea 0x8(%di),%di # Next entry
+ loop init.2 # Till set done
+ jmp init.1 # Continue
+/*
+ * Initialize TSS.
+ */
+init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0
+ movb $SEL_SDATA,TSS_SS0(%di) # Set SS0
+ movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base
+/*
+ * Bring up the system.
+ */
+ mov $0x2820,%bx # Set protected mode
+ callw setpic # IRQ offsets
+ lidt idtdesc # Set IDT
+ lgdt gdtdesc # Set GDT
+ mov %cr0,%eax # Switch to protected
+ inc %ax # mode
+ mov %eax,%cr0 #
+ ljmp $SEL_SCODE,$init.8 # To 32-bit code
+ .code32
+init.8: xorl %ecx,%ecx # Zero
+ movb $SEL_SDATA,%cl # To 32-bit
+ movw %cx,%ss # stack
+/*
+ * Launch user task.
+ */
+ movb $SEL_TSS,%cl # Set task
+ ltr %cx # register
+ movl $MEM_USR,%edx # User base address
+ movzwl %ss:BDA_MEM,%eax # Get free memory
+ andl $0x7,%eax
+ incl %eax
+ shll $0x11,%eax # To bytes
+ subl $ARGSPACE,%eax # Less arg space
+ subl %edx,%eax # Less base
+ movb $SEL_UDATA,%cl # User data selector
+ pushl %ecx # Set SS
+ pushl %eax # Set ESP
+ push $0x202 # Set flags (IF set)
+ push $SEL_UCODE # Set CS
+ pushl btx_hdr+0xc # Set EIP
+ pushl %ecx # Set GS
+ pushl %ecx # Set FS
+ pushl %ecx # Set DS
+ pushl %ecx # Set ES
+ pushl %edx # Set EAX
+ movb $0x7,%cl # Set remaining
+init.9: push $0x0 # general
+ loop init.9 # registers
+#ifdef BTX_SERIAL
+ call sio_init # setup the serial console
+#endif
+ popa # and initialize
+ popl %es # Initialize
+ popl %ds # user
+ popl %fs # segment
+ popl %gs # registers
+ iret # To user mode
+/*
+ * Exit routine.
+ */
+exit: cli # Disable interrupts
+ movl $MEM_ESP0,%esp # Clear stack
+/*
+ * Turn off paging.
+ */
+ movl %cr0,%eax # Get CR0
+ andl $~0x80000000,%eax # Disable
+ movl %eax,%cr0 # paging
+ xorl %ecx,%ecx # Zero
+ movl %ecx,%cr3 # Flush TLB
+/*
+ * Restore the GDT in case we caught a kernel trap.
+ */
+ lgdt %cs:gdtdesc # Set GDT
+/*
+ * To 16 bits.
+ */
+ ljmpw $SEL_RCODE,$exit.1 # Reload CS
+ .code16
+exit.1: mov $SEL_RDATA,%cl # 16-bit selector
+ mov %cx,%ss # Reload SS
+ mov %cx,%ds # Load
+ mov %cx,%es # remaining
+ mov %cx,%fs # segment
+ mov %cx,%gs # registers
+/*
+ * To real-address mode.
+ */
+ dec %ax # Switch to
+ mov %eax,%cr0 # real mode
+ ljmp $0x0,$exit.2 # Reload CS
+exit.2: xor %ax,%ax # Real mode segment
+ mov %ax,%ss # Reload SS
+ mov %ax,%ds # Address data
+ mov $0x1008,%bx # Set real mode
+ callw setpic # IRQ offsets
+ lidt ivtdesc # Set IVT
+/*
+ * Reboot or await reset.
+ */
+ sti # Enable interrupts
+ testb $0x1,btx_hdr+0x7 # Reboot?
+exit.3: jz exit.3 # No
+ movb $0xa0,%al
+ outb %al,$0x35
+ movb $0x00,%al
+ outb %al,$0xf0 # reboot the machine
+exit.4: jmp exit.4
+/*
+ * Set IRQ offsets by reprogramming 8259A PICs.
+ */
+setpic: in $0x02,%al # Save master
+ push %ax # IMR
+ in $0x0a,%al # Save slave
+ push %ax # IMR
+ movb $0x11,%al # ICW1 to
+ outb %al,$0x00 # master,
+ outb %al,$0x08 # slave
+ movb %bl,%al # ICW2 to
+ outb %al,$0x02 # master
+ movb %bh,%al # ICW2 to
+ outb %al,$0x0a # slave
+ movb $0x80,%al # ICW3 to
+ outb %al,$0x02 # master
+ movb $0x7,%al # ICW3 to
+ outb %al,$0x0a # slave
+ movb $0x1d,%al # ICW4 to
+ outb %al,$0x02 # master,
+ movb $0x9,%al # ICW4 to
+ outb %al,$0x0a # slave
+ pop %ax # Restore slave
+ outb %al,$0x0a # IMR
+ pop %ax # Restore master
+ outb %al,$0x02 # IMR
+ retw # To caller
+ .code32
+/*
+ * Exception jump table.
+ */
+intx00: push $0x0 # Int 0x0: #DE
+ jmp ex_noc # Divide error
+ push $0x1 # Int 0x1: #DB
+ jmp ex_noc # Debug
+ push $0x3 # Int 0x3: #BP
+ jmp ex_noc # Breakpoint
+ push $0x4 # Int 0x4: #OF
+ jmp ex_noc # Overflow
+ push $0x5 # Int 0x5: #BR
+ jmp ex_noc # BOUND range exceeded
+ push $0x6 # Int 0x6: #UD
+ jmp ex_noc # Invalid opcode
+ push $0x7 # Int 0x7: #NM
+ jmp ex_noc # Device not available
+ push $0x8 # Int 0x8: #DF
+ jmp except # Double fault
+ push $0xa # Int 0xa: #TS
+ jmp except # Invalid TSS
+ push $0xb # Int 0xb: #NP
+ jmp except # Segment not present
+ push $0xc # Int 0xc: #SS
+ jmp except # Stack segment fault
+ push $0xd # Int 0xd: #GP
+ jmp except # General protection
+ push $0xe # Int 0xe: #PF
+ jmp except # Page fault
+intx10: push $0x10 # Int 0x10: #MF
+ jmp ex_noc # Floating-point error
+/*
+ * Save a zero error code.
+ */
+ex_noc: pushl (%esp,1) # Duplicate int no
+ movb $0x0,0x4(%esp,1) # Fake error code
+/*
+ * Handle exception.
+ */
+except: cld # String ops inc
+ pushl %ds # Save
+ pushl %es # most
+ pusha # registers
+ pushl %gs # Set GS
+ pushl %fs # Set FS
+ pushl %ds # Set DS
+ pushl %es # Set ES
+ cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode?
+ jne except.1 # No
+ pushl %ss # Set SS
+ jmp except.2 # Join common code
+except.1: pushl 0x50(%esp,1) # Set SS
+except.2: pushl 0x50(%esp,1) # Set ESP
+ push $SEL_SDATA # Set up
+ popl %ds # to
+ pushl %ds # address
+ popl %es # data
+ movl %esp,%ebx # Stack frame
+ movl $dmpfmt,%esi # Dump format string
+ movl $MEM_BUF,%edi # Buffer
+ pushl %eax
+ pushl %edx
+wait.1: inb $0x60,%al
+ testb $0x04,%al
+ jz wait.1
+ movb $0xe0,%al
+ outb %al,$0x62
+wait.2: inb $0x60,%al
+ testb $0x01,%al
+ jz wait.2
+ xorl %edx,%edx
+ inb $0x62,%al
+ movb %al,%dl
+ inb $0x62,%al
+ movb %al,%dh
+ inb $0x62,%al
+ inb $0x62,%al
+ inb $0x62,%al
+ movl %edx,%eax
+ shlw $1,%ax
+ movl $BDA_POS,%edx
+ movw %ax,(%edx)
+ popl %edx
+ popl %eax
+ pushl %edi # Dump to
+ call dump # buffer
+ popl %esi # and
+ call putstr # display
+ leal 0x18(%esp,1),%esp # Discard frame
+ popa # Restore
+ popl %es # registers
+ popl %ds # saved
+ cmpb $0x3,(%esp,1) # Breakpoint?
+ je except.3 # Yes
+ cmpb $0x1,(%esp,1) # Debug?
+ jne except.2a # No
+ testl $PSL_T,0x10(%esp,1) # Trap flag set?
+ jnz except.3 # Yes
+except.2a: jmp exit # Exit
+except.3: leal 0x8(%esp,1),%esp # Discard err, int no
+ iret # From interrupt
+
+/*
+ * Reboot the machine by setting the reboot flag and exiting
+ */
+reboot: orb $0x1,btx_hdr+0x7 # Set the reboot flag
+ jmp exit # Terminate BTX and reboot
+
+/*
+ * Protected Mode Hardware interrupt jump table.
+ */
+intx20: push $0x8 # Int 0x20: IRQ0
+ jmp int_hw # V86 int 0x8
+ push $0x9 # Int 0x21: IRQ1
+ jmp int_hw # V86 int 0x9
+ push $0xa # Int 0x22: IRQ2
+ jmp int_hw # V86 int 0xa
+ push $0xb # Int 0x23: IRQ3
+ jmp int_hw # V86 int 0xb
+ push $0xc # Int 0x24: IRQ4
+ jmp int_hw # V86 int 0xc
+ push $0xd # Int 0x25: IRQ5
+ jmp int_hw # V86 int 0xd
+ push $0xe # Int 0x26: IRQ6
+ jmp int_hw # V86 int 0xe
+ push $0xf # Int 0x27: IRQ7
+ jmp int_hw # V86 int 0xf
+ push $0x10 # Int 0x28: IRQ8
+ jmp int_hw # V86 int 0x10
+ push $0x11 # Int 0x29: IRQ9
+ jmp int_hw # V86 int 0x11
+ push $0x12 # Int 0x2a: IRQ10
+ jmp int_hw # V86 int 0x12
+ push $0x13 # Int 0x2b: IRQ11
+ jmp int_hw # V86 int 0x13
+ push $0x14 # Int 0x2c: IRQ12
+ jmp int_hw # V86 int 0x14
+ push $0x15 # Int 0x2d: IRQ13
+ jmp int_hw # V86 int 0x15
+ push $0x16 # Int 0x2e: IRQ14
+ jmp int_hw # V86 int 0x16
+ push $0x17 # Int 0x2f: IRQ15
+ jmp int_hw # V86 int 0x17
+
+/*
+ * Invoke real mode interrupt/function call from user mode with arguments.
+ */
+intx31: pushl $-1 # Dummy int no for btx_v86
+/*
+ * Invoke real mode interrupt/function call from protected mode.
+ *
+ * We place a trampoline on the user stack that will return to rret_tramp
+ * which will reenter protected mode and then finally return to the user
+ * client.
+ *
+ * Kernel frame %esi points to: Real mode stack frame at MEM_ESPR:
+ *
+ * -0x00 user %ss -0x04 kernel %esp (with full frame)
+ * -0x04 user %esp -0x08 btx_v86 pointer
+ * -0x08 user %eflags -0x0c flags (only used if interrupt)
+ * -0x0c user %cs -0x10 real mode CS:IP return trampoline
+ * -0x10 user %eip -0x12 real mode flags
+ * -0x14 int no -0x16 real mode CS:IP (target)
+ * -0x18 %eax
+ * -0x1c %ecx
+ * -0x20 %edx
+ * -0x24 %ebx
+ * -0x28 %esp
+ * -0x2c %ebp
+ * -0x30 %esi
+ * -0x34 %edi
+ * -0x38 %gs
+ * -0x3c %fs
+ * -0x40 %ds
+ * -0x44 %es
+ * -0x48 zero %eax (hardware int only)
+ * -0x4c zero %ecx (hardware int only)
+ * -0x50 zero %edx (hardware int only)
+ * -0x54 zero %ebx (hardware int only)
+ * -0x58 zero %esp (hardware int only)
+ * -0x5c zero %ebp (hardware int only)
+ * -0x60 zero %esi (hardware int only)
+ * -0x64 zero %edi (hardware int only)
+ * -0x68 zero %gs (hardware int only)
+ * -0x6c zero %fs (hardware int only)
+ * -0x70 zero %ds (hardware int only)
+ * -0x74 zero %es (hardware int only)
+ */
+int_hw: cld # String ops inc
+ pusha # Save gp regs
+ pushl %gs # Save
+ pushl %fs # seg
+ pushl %ds # regs
+ pushl %es
+ push $SEL_SDATA # Set up
+ popl %ds # to
+ pushl %ds # address
+ popl %es # data
+ leal 0x44(%esp,1),%esi # Base of frame
+ movl %esp,MEM_ESPR-0x04 # Save kernel stack pointer
+ movl -0x14(%esi),%eax # Get Int no
+ cmpl $-1,%eax # Hardware interrupt?
+ jne intusr.1 # Yes
+/*
+ * v86 calls save the btx_v86 pointer on the real mode stack and read
+ * the address and flags from the btx_v86 structure. For interrupt
+ * handler invocations (VM86 INTx requests), disable interrupts,
+ * tracing, and alignment checking while the handler runs.
+ */
+ movl $MEM_USR,%ebx # User base
+ movl %ebx,%edx # address
+ addl -0x4(%esi),%ebx # User ESP
+ movl (%ebx),%ebp # btx_v86 pointer
+ addl %ebp,%edx # Flatten btx_v86 ptr
+ movl %edx,MEM_ESPR-0x08 # Save btx_v86 ptr
+ movl V86_ADDR(%edx),%eax # Get int no/address
+ movl V86_CTL(%edx),%edx # Get control flags
+ movl -0x08(%esi),%ebx # Save user flags in %ebx
+ testl $V86F_ADDR,%edx # Segment:offset?
+ jnz intusr.4 # Yes
+ andl $~(PSL_I|PSL_T|PSL_AC),%ebx # Disable interrupts, tracing,
+ # and alignment checking for
+ # interrupt handler
+ jmp intusr.3 # Skip hardware interrupt
+/*
+ * Hardware interrupts store a NULL btx_v86 pointer and use the
+ * address (interrupt number) from the stack with empty flags. Also,
+ * push a dummy frame of zeros onto the stack for all the general
+ * purpose and segment registers and clear %eflags. This gives the
+ * hardware interrupt handler a clean slate.
+ */
+intusr.1: xorl %edx,%edx # Control flags
+ movl %edx,MEM_ESPR-0x08 # NULL btx_v86 ptr
+ movl $12,%ecx # Frame is 12 dwords
+intusr.2: pushl $0x0 # Fill frame
+ loop intusr.2 # with zeros
+ movl $PSL_RESERVED_DEFAULT,%ebx # Set clean %eflags
+/*
+ * Look up real mode IDT entry for hardware interrupts and VM86 INTx
+ * requests.
+ */
+intusr.3: shll $0x2,%eax # Scale
+ movl (%eax),%eax # Load int vector
+ jmp intusr.5 # Skip CALLF test
+/*
+ * Panic if V86F_CALLF isn't set with V86F_ADDR.
+ */
+intusr.4: testl $V86F_CALLF,%edx # Far call?
+ jnz intusr.5 # Ok
+ movl %edx,0x30(%esp,1) # Place VM86 flags in int no
+ movl $badvm86,%esi # Display bad
+ call putstr # VM86 call
+ popl %es # Restore
+ popl %ds # seg
+ popl %fs # regs
+ popl %gs
+ popal # Restore gp regs
+ jmp ex_noc # Panic
+/*
+ * %eax now holds the segment:offset of the function.
+ * %ebx now holds the %eflags to pass to real mode.
+ * %edx now holds the V86F_* flags.
+ */
+intusr.5: movw %bx,MEM_ESPR-0x12 # Pass user flags to real mode
+ # target
+/*
+ * If this is a v86 call, copy the seg regs out of the btx_v86 structure.
+ */
+ movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr
+ jecxz intusr.6 # Skip for hardware ints
+ leal -0x44(%esi),%edi # %edi => kernel stack seg regs
+ pushl %esi # Save
+ leal V86_ES(%ecx),%esi # %esi => btx_v86 seg regs
+ movl $4,%ecx # Copy seg regs
+ rep # from btx_v86
+ movsl # to kernel stack
+ popl %esi # Restore
+intusr.6: movl -0x08(%esi),%ebx # Copy user flags to real
+ movl %ebx,MEM_ESPR-0x0c # mode return trampoline
+ movl $rret_tramp,%ebx # Set return trampoline
+ movl %ebx,MEM_ESPR-0x10 # CS:IP
+ movl %eax,MEM_ESPR-0x16 # Real mode target CS:IP
+ ljmpw $SEL_RCODE,$intusr.7 # Change to 16-bit segment
+ .code16
+intusr.7: movl %cr0,%eax # Leave
+ dec %al # protected
+ movl %eax,%cr0 # mode
+ ljmpw $0x0,$intusr.8
+intusr.8: xorw %ax,%ax # Reset %ds
+ movw %ax,%ds # and
+ movw %ax,%ss # %ss
+ lidt ivtdesc # Set IVT
+ popl %es # Restore
+ popl %ds # seg
+ popl %fs # regs
+ popl %gs
+ popal # Restore gp regs
+ movw $MEM_ESPR-0x16,%sp # Switch to real mode stack
+ iret # Call target routine
+/*
+ * For the return to real mode we setup a stack frame like this on the real
+ * mode stack. Note that callf calls won't pop off the flags, but we just
+ * ignore that by repositioning %sp to be just above the btx_v86 pointer
+ * so it is aligned. The stack is relative to MEM_ESPR.
+ *
+ * -0x04 kernel %esp
+ * -0x08 btx_v86
+ * -0x0c %eax
+ * -0x10 %ecx
+ * -0x14 %edx
+ * -0x18 %ebx
+ * -0x1c %esp
+ * -0x20 %ebp
+ * -0x24 %esi
+ * -0x28 %edi
+ * -0x2c %gs
+ * -0x30 %fs
+ * -0x34 %ds
+ * -0x38 %es
+ * -0x3c %eflags
+ */
+rret_tramp: movw $MEM_ESPR-0x08,%sp # Reset stack pointer
+ pushal # Save gp regs
+ pushl %gs # Save
+ pushl %fs # seg
+ pushl %ds # regs
+ pushl %es
+ pushfl # Save %eflags
+ cli # Disable interrupts
+ std # String ops dec
+ xorw %ax,%ax # Reset seg
+ movw %ax,%ds # regs
+ movw %ax,%es # (%ss is already 0)
+ lidt idtdesc # Set IDT
+ lgdt gdtdesc # Set GDT
+ mov %cr0,%eax # Switch to protected
+ inc %ax # mode
+ mov %eax,%cr0 #
+ ljmp $SEL_SCODE,$rret_tramp.1 # To 32-bit code
+ .code32
+rret_tramp.1: xorl %ecx,%ecx # Zero
+ movb $SEL_SDATA,%cl # Setup
+ movw %cx,%ss # 32-bit
+ movw %cx,%ds # seg
+ movw %cx,%es # regs
+ movl MEM_ESPR-0x04,%esp # Switch to kernel stack
+ leal 0x44(%esp,1),%esi # Base of frame
+ andb $~0x2,tss_desc+0x5 # Clear TSS busy
+ movb $SEL_TSS,%cl # Set task
+ ltr %cx # register
+/*
+ * Now we are back in protected mode. The kernel stack frame set up
+ * before entering real mode is still intact. For hardware interrupts,
+ * leave the frame unchanged.
+ */
+ cmpl $0,MEM_ESPR-0x08 # Leave saved regs unchanged
+ jz rret_tramp.3 # for hardware ints
+/*
+ * For V86 calls, copy the registers off of the real mode stack onto
+ * the kernel stack as we want their updated values. Also, initialize
+ * the segment registers on the kernel stack.
+ *
+ * Note that the %esp in the kernel stack after this is garbage, but popa
+ * ignores it, so we don't have to fix it up.
+ */
+ leal -0x18(%esi),%edi # Kernel stack GP regs
+ pushl %esi # Save
+ movl $MEM_ESPR-0x0c,%esi # Real mode stack GP regs
+ movl $8,%ecx # Copy GP regs from
+ rep # real mode stack
+ movsl # to kernel stack
+ movl $SEL_UDATA,%eax # Selector for data seg regs
+ movl $4,%ecx # Initialize %ds,
+ rep # %es, %fs, and
+ stosl # %gs
+/*
+ * For V86 calls, copy the saved seg regs on the real mode stack back
+ * over to the btx_v86 structure. Also, conditionally update the
+ * saved eflags on the kernel stack based on the flags from the user.
+ */
+ movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr
+ leal V86_GS(%ecx),%edi # %edi => btx_v86 seg regs
+ leal MEM_ESPR-0x2c,%esi # %esi => real mode seg regs
+ xchgl %ecx,%edx # Save btx_v86 ptr
+ movl $4,%ecx # Copy seg regs
+ rep # from real mode stack
+ movsl # to btx_v86
+ popl %esi # Restore
+ movl V86_CTL(%edx),%edx # Read V86 control flags
+ testl $V86F_FLAGS,%edx # User wants flags?
+ jz rret_tramp.3 # No
+ movl MEM_ESPR-0x3c,%eax # Read real mode flags
+ movw %ax,-0x08(%esi) # Update user flags (low 16)
+/*
+ * Return to the user task
+ */
+rret_tramp.3: popl %es # Restore
+ popl %ds # seg
+ popl %fs # regs
+ popl %gs
+ popal # Restore gp regs
+ addl $4,%esp # Discard int no
+ iret # Return to user mode
+
+/*
+ * System Call.
+ */
+intx30: cmpl $SYS_EXEC,%eax # Exec system call?
+ jne intx30.1 # No
+ pushl %ss # Set up
+ popl %es # all
+ pushl %es # segment
+ popl %ds # registers
+ pushl %ds # for the
+ popl %fs # program
+ pushl %fs # we're
+ popl %gs # invoking
+ movl $MEM_USR,%eax # User base address
+ addl 0xc(%esp,1),%eax # Change to user
+ leal 0x4(%eax),%esp # stack
+ popl %eax # Call
+ call *%eax # program
+intx30.1: orb $0x1,%ss:btx_hdr+0x7 # Flag reboot
+ jmp exit # Exit
+/*
+ * Dump structure [EBX] to [EDI], using format string [ESI].
+ */
+dump.0: stosb # Save char
+dump: lodsb # Load char
+ testb %al,%al # End of string?
+ jz dump.10 # Yes
+ testb $0x80,%al # Control?
+ jz dump.0 # No
+ movb %al,%ch # Save control
+ movb $'=',%al # Append
+ stosb # '='
+ lodsb # Get offset
+ pushl %esi # Save
+ movsbl %al,%esi # To
+ addl %ebx,%esi # pointer
+ testb $DMP_X16,%ch # Dump word?
+ jz dump.1 # No
+ lodsw # Get and
+ call hex16 # dump it
+dump.1: testb $DMP_X32,%ch # Dump long?
+ jz dump.2 # No
+ lodsl # Get and
+ call hex32 # dump it
+dump.2: testb $DMP_MEM,%ch # Dump memory?
+ jz dump.8 # No
+ pushl %ds # Save
+ testl $PSL_VM,0x50(%ebx) # V86 mode?
+ jnz dump.3 # Yes
+ verr 0x4(%esi) # Readable selector?
+ jnz dump.3 # No
+ ldsl (%esi),%esi # Load pointer
+ jmp dump.4 # Join common code
+dump.3: lodsl # Set offset
+ xchgl %eax,%edx # Save
+ lodsl # Get segment
+ shll $0x4,%eax # * 0x10
+ addl %edx,%eax # + offset
+ xchgl %eax,%esi # Set pointer
+dump.4: movb $2,%dl # Num lines
+dump.4a: movb $0x10,%cl # Bytes to dump
+dump.5: lodsb # Get byte and
+ call hex8 # dump it
+ decb %cl # Keep count
+ jz dump.6a # If done
+ movb $'-',%al # Separator
+ cmpb $0x8,%cl # Half way?
+ je dump.6 # Yes
+ movb $' ',%al # Use space
+dump.6: stosb # Save separator
+ jmp dump.5 # Continue
+dump.6a: decb %dl # Keep count
+ jz dump.7 # If done
+ movb $0xa,%al # Line feed
+ stosb # Save one
+ movb $7,%cl # Leading
+ movb $' ',%al # spaces
+dump.6b: stosb # Dump
+ decb %cl # spaces
+ jnz dump.6b
+ jmp dump.4a # Next line
+dump.7: popl %ds # Restore
+dump.8: popl %esi # Restore
+ movb $0xa,%al # Line feed
+ testb $DMP_EOL,%ch # End of line?
+ jnz dump.9 # Yes
+ movb $' ',%al # Use spaces
+ stosb # Save one
+dump.9: jmp dump.0 # Continue
+dump.10: stosb # Terminate string
+ ret # To caller
+/*
+ * Convert EAX, AX, or AL to hex, saving the result to [EDI].
+ */
+hex32: pushl %eax # Save
+ shrl $0x10,%eax # Do upper
+ call hex16 # 16
+ popl %eax # Restore
+hex16: call hex16.1 # Do upper 8
+hex16.1: xchgb %ah,%al # Save/restore
+hex8: pushl %eax # Save
+ shrb $0x4,%al # Do upper
+ call hex8.1 # 4
+ popl %eax # Restore
+hex8.1: andb $0xf,%al # Get lower 4
+ cmpb $0xa,%al # Convert
+ sbbb $0x69,%al # to hex
+ das # digit
+ orb $0x20,%al # To lower case
+ stosb # Save char
+ ret # (Recursive)
+/*
+ * Output zero-terminated string [ESI] to the console.
+ */
+putstr.0: call putchr # Output char
+putstr: lodsb # Load char
+ testb %al,%al # End of string?
+ jnz putstr.0 # No
+ ret # To caller
+#ifdef BTX_SERIAL
+ .set SIO_PRT,SIOPRT # Base port
+ .set SIO_FMT,SIOFMT # 8N1
+ .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD
+
+/*
+ * int sio_init(void)
+ */
+sio_init: movw $SIO_PRT+0x3,%dx # Data format reg
+ movb $SIO_FMT|0x80,%al # Set format
+ outb %al,(%dx) # and DLAB
+ pushl %edx # Save
+ subb $0x3,%dl # Divisor latch reg
+ movw $SIO_DIV,%ax # Set
+ outw %ax,(%dx) # BPS
+ popl %edx # Restore
+ movb $SIO_FMT,%al # Clear
+ outb %al,(%dx) # DLAB
+ incl %edx # Modem control reg
+ movb $0x3,%al # Set RTS,
+ outb %al,(%dx) # DTR
+ incl %edx # Line status reg
+ call sio_getc.1 # Get character
+
+/*
+ * int sio_flush(void)
+ */
+sio_flush: xorl %eax,%eax # Return value
+ xorl %ecx,%ecx # Timeout
+ movb $0x80,%ch # counter
+sio_flush.1: call sio_ischar # Check for character
+ jz sio_flush.2 # Till none
+ loop sio_flush.1 # or counter is zero
+ movb $1, %al # Exhausted all tries
+sio_flush.2: ret # To caller
+
+/*
+ * void sio_putc(int c)
+ */
+sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg
+ xor %ecx,%ecx # Timeout
+ movb $0x40,%ch # counter
+sio_putc.1: inb (%dx),%al # Transmitter
+ testb $0x20,%al # buffer empty?
+ loopz sio_putc.1 # No
+ jz sio_putc.2 # If timeout
+ movb 0x4(%esp,1),%al # Get character
+ subb $0x5,%dl # Transmitter hold reg
+ outb %al,(%dx) # Write character
+sio_putc.2: ret $0x4 # To caller
+
+/*
+ * int sio_getc(void)
+ */
+sio_getc: call sio_ischar # Character available?
+ jz sio_getc # No
+sio_getc.1: subb $0x5,%dl # Receiver buffer reg
+ inb (%dx),%al # Read character
+ ret # To caller
+
+/*
+ * int sio_ischar(void)
+ */
+sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register
+ xorl %eax,%eax # Zero
+ inb (%dx),%al # Received data
+ andb $0x1,%al # ready?
+ ret # To caller
+
+/*
+ * Output character AL to the serial console.
+ */
+putchr: pusha # Save
+ cmpb $10, %al # is it a newline?
+ jne putchr.1 # no?, then leave
+ push $13 # output a carriage
+ call sio_putc # return first
+ movb $10, %al # restore %al
+putchr.1: pushl %eax # Push the character
+ # onto the stack
+ call sio_putc # Output the character
+ popa # Restore
+ ret # To caller
+#else
+/*
+ * Output character AL to the console.
+ */
+putchr: pusha # Save
+ xorl %ecx,%ecx # Zero for loops
+ movb $SCR_MAT,%ah # Mode/attribute
+ movl $BDA_POS,%ebx # BDA pointer
+ movw (%ebx),%dx # Cursor position
+ movl $0xa0000,%edi
+putchr.1: cmpb $0xa,%al # New line?
+ je putchr.2 # Yes
+ movw %dx,%cx
+ movb %al,(%edi,%ecx,1) # Write char
+ addl $0x2000,%ecx
+ movb %ah,(%edi,%ecx,1) # Write attr
+ addw $0x02,%dx
+ jmp putchr.3
+putchr.2: movw %dx,%ax
+ movb $SCR_COL*2,%dl
+ div %dl
+ incb %al
+ mul %dl
+ movw %ax,%dx
+putchr.3: cmpw $SCR_ROW*SCR_COL*2,%dx
+ jb putchr.4 # No
+ leal 2*SCR_COL(%edi),%esi # New top line
+ movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
+ rep # Scroll
+ movsl # screen
+ movb $0x20,%al # Space
+ xorb %ah,%ah
+ movb $SCR_COL,%cl # Columns to clear
+ rep # Clear
+ stosw # line
+ movw $(SCR_ROW-1)*SCR_COL*2,%dx
+putchr.4: movw %dx,(%ebx) # Update position
+ popa # Restore
+ ret # To caller
+#endif
+
+ .code16
+/*
+ * Real Mode Hardware interrupt jump table.
+ */
+intr20: push $0x8 # Int 0x20: IRQ0
+ jmp int_hwr # V86 int 0x8
+ push $0x9 # Int 0x21: IRQ1
+ jmp int_hwr # V86 int 0x9
+ push $0xa # Int 0x22: IRQ2
+ jmp int_hwr # V86 int 0xa
+ push $0xb # Int 0x23: IRQ3
+ jmp int_hwr # V86 int 0xb
+ push $0xc # Int 0x24: IRQ4
+ jmp int_hwr # V86 int 0xc
+ push $0xd # Int 0x25: IRQ5
+ jmp int_hwr # V86 int 0xd
+ push $0xe # Int 0x26: IRQ6
+ jmp int_hwr # V86 int 0xe
+ push $0xf # Int 0x27: IRQ7
+ jmp int_hwr # V86 int 0xf
+ push $0x10 # Int 0x28: IRQ8
+ jmp int_hwr # V86 int 0x10
+ push $0x11 # Int 0x29: IRQ9
+ jmp int_hwr # V86 int 0x11
+ push $0x12 # Int 0x2a: IRQ10
+ jmp int_hwr # V86 int 0x12
+ push $0x13 # Int 0x2b: IRQ11
+ jmp int_hwr # V86 int 0x13
+ push $0x14 # Int 0x2c: IRQ12
+ jmp int_hwr # V86 int 0x14
+ push $0x15 # Int 0x2d: IRQ13
+ jmp int_hwr # V86 int 0x15
+ push $0x16 # Int 0x2e: IRQ14
+ jmp int_hwr # V86 int 0x16
+ push $0x17 # Int 0x2f: IRQ15
+ jmp int_hwr # V86 int 0x17
+/*
+ * Reflect hardware interrupts in real mode.
+ */
+int_hwr: push %ax # Save
+ push %ds # Save
+ push %bp # Save
+ mov %sp,%bp # Address stack frame
+ xchg %bx,6(%bp) # Swap BX, int no
+ xor %ax,%ax # Set %ds:%bx to
+ shl $2,%bx # point to
+ mov %ax,%ds # IDT entry
+ mov (%bx),%ax # Load IP
+ mov 2(%bx),%bx # Load CS
+ xchg %ax,4(%bp) # Swap saved %ax,%bx with
+ xchg %bx,6(%bp) # CS:IP of handler
+ pop %bp # Restore
+ pop %ds # Restore
+ lret # Jump to handler
+
+ .p2align 4
+/*
+ * Global descriptor table.
+ */
+gdt: .word 0x0,0x0,0x0,0x0 # Null entry
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
+ .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+ .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE
+ .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA
+tss_desc: .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS
+gdt.1:
+/*
+ * Pseudo-descriptors.
+ */
+gdtdesc: .word gdt.1-gdt-1,gdt,0x0 # GDT
+idtdesc: .word _IDTLM,MEM_IDT,0x0 # IDT
+ivtdesc: .word 0x400-0x0-1,0x0,0x0 # IVT
+/*
+ * IDT construction control string.
+ */
+idtctl: .byte 0x10, 0x8e # Int 0x0-0xf
+ .word 0x7dfb,intx00 # (exceptions)
+ .byte 0x10, 0x8e # Int 0x10
+ .word 0x1, intx10 # (exception)
+ .byte 0x10, 0x8e # Int 0x20-0x2f
+ .word 0xffff,intx20 # (hardware)
+ .byte 0x1, 0xee # int 0x30
+ .word 0x1, intx30 # (system call)
+ .byte 0x2, 0xee # Int 0x31-0x32
+ .word 0x1, intx31 # (V86, null)
+ .byte 0x0 # End of string
+/*
+ * Dump format string.
+ */
+dmpfmt: .byte '\n' # "\n"
+ .ascii "int" # "int="
+ .byte 0x80|DMP_X32, 0x40 # "00000000 "
+ .ascii "err" # "err="
+ .byte 0x80|DMP_X32, 0x44 # "00000000 "
+ .ascii "efl" # "efl="
+ .byte 0x80|DMP_X32, 0x50 # "00000000 "
+ .ascii "eip" # "eip="
+ .byte 0x80|DMP_X32|DMP_EOL,0x48 # "00000000\n"
+ .ascii "eax" # "eax="
+ .byte 0x80|DMP_X32, 0x34 # "00000000 "
+ .ascii "ebx" # "ebx="
+ .byte 0x80|DMP_X32, 0x28 # "00000000 "
+ .ascii "ecx" # "ecx="
+ .byte 0x80|DMP_X32, 0x30 # "00000000 "
+ .ascii "edx" # "edx="
+ .byte 0x80|DMP_X32|DMP_EOL,0x2c # "00000000\n"
+ .ascii "esi" # "esi="
+ .byte 0x80|DMP_X32, 0x1c # "00000000 "
+ .ascii "edi" # "edi="
+ .byte 0x80|DMP_X32, 0x18 # "00000000 "
+ .ascii "ebp" # "ebp="
+ .byte 0x80|DMP_X32, 0x20 # "00000000 "
+ .ascii "esp" # "esp="
+ .byte 0x80|DMP_X32|DMP_EOL,0x0 # "00000000\n"
+ .ascii "cs" # "cs="
+ .byte 0x80|DMP_X16, 0x4c # "0000 "
+ .ascii "ds" # "ds="
+ .byte 0x80|DMP_X16, 0xc # "0000 "
+ .ascii "es" # "es="
+ .byte 0x80|DMP_X16, 0x8 # "0000 "
+ .ascii " " # " "
+ .ascii "fs" # "fs="
+ .byte 0x80|DMP_X16, 0x10 # "0000 "
+ .ascii "gs" # "gs="
+ .byte 0x80|DMP_X16, 0x14 # "0000 "
+ .ascii "ss" # "ss="
+ .byte 0x80|DMP_X16|DMP_EOL,0x4 # "0000\n"
+ .ascii "cs:eip" # "cs:eip="
+ .byte 0x80|DMP_MEM|DMP_EOL,0x48 # "00 00 ... 00 00\n"
+ .ascii "ss:esp" # "ss:esp="
+ .byte 0x80|DMP_MEM|DMP_EOL,0x0 # "00 00 ... 00 00\n"
+ .asciz "BTX halted\n" # End
+/*
+ * Bad VM86 call panic
+ */
+badvm86: .asciz "Invalid VM86 Request\n"
+
+/*
+ * End of BTX memory.
+ */
+ .p2align 4
+break:
diff --git a/stand/pc98/btx/btxldr/Makefile b/stand/pc98/btx/btxldr/Makefile
new file mode 100644
index 0000000..47e83a0
--- /dev/null
+++ b/stand/pc98/btx/btxldr/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+PROG= btxldr
+INTERNALPROG=
+MAN=
+SRCS= btxldr.S
+
+CFLAGS+=-DLOADER_ADDRESS=${LOADER_ADDRESS}
+CFLAGS+=-I${.CURDIR}/../../../i386/common
+
+.if defined(BTXLDR_VERBOSE)
+CFLAGS+=-DBTXLDR_VERBOSE
+.endif
+
+ORG=${LOADER_ADDRESS}
+LDFLAGS=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.btxldr.S= ${CLANG_NO_IAS}
diff --git a/stand/pc98/btx/btxldr/btxldr.S b/stand/pc98/btx/btxldr/btxldr.S
new file mode 100644
index 0000000..9a6483f
--- /dev/null
+++ b/stand/pc98/btx/btxldr/btxldr.S
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+#include <bootargs.h>
+
+/*
+ * Prototype BTX loader program, written in a couple of hours. The
+ * real thing should probably be more flexible, and in C.
+ */
+
+/*
+ * Memory locations.
+ */
+ .set MEM_STUB,0x600 # Real mode stub
+ .set MEM_ESP,0x1000 # New stack pointer
+ .set MEM_TBL,0x5000 # BTX page tables
+ .set MEM_ENTRY,0x9010 # BTX entry point
+ .set MEM_DATA,start+0x1000 # Data segment
+/*
+ * Segment selectors.
+ */
+ .set SEL_SCODE,0x8 # 4GB code
+ .set SEL_SDATA,0x10 # 4GB data
+ .set SEL_RCODE,0x18 # 64K code
+ .set SEL_RDATA,0x20 # 64K data
+/*
+ * Paging constants.
+ */
+ .set PAG_SIZ,0x1000 # Page size
+ .set PAG_ENT,0x4 # Page entry size
+/*
+ * Screen constants.
+ */
+ .set SCR_MAT,0xe1 # Mode/attribute
+ .set SCR_COL,0x50 # Columns per row
+ .set SCR_ROW,0x19 # Rows per screen
+/*
+ * BIOS Data Area locations.
+ */
+ .set BDA_MEM,0xa1501 # Free memory
+ .set BDA_POS,0xa153e # Cursor position
+/*
+ * Required by aout gas inadequacy.
+ */
+ .set SIZ_STUB,0x1a # Size of stub
+/*
+ * We expect to be loaded by boot2 at the origin defined in ./Makefile.
+ */
+ .globl start
+/*
+ * BTX program loader for ELF clients.
+ */
+start: cld # String ops inc
+ cli
+gdcwait.1: inb $0x60,%al
+ testb $0x04,%al
+ jz gdcwait.1
+ movb $0xe0,%al
+ outb %al,$0x62
+ nop
+gdcwait.2: inb $0x60,%al
+ testb $0x01,%al
+ jz gdcwait.2
+ inb $0x62,%al
+ movb %al,%dl
+ inb $0x62,%al
+ movb %al,%dh
+ inb $0x62,%al
+ inb $0x62,%al
+ inb $0x62,%al
+ shlw $1,%dx
+ movl $BDA_POS,%ebx
+ movw %dx,(%ebx)
+ movl $m_logo,%esi # Identify
+ call putstr # ourselves
+ movzwl BDA_MEM,%eax # Get base memory
+ andl $0x7,%eax
+ incl %eax
+ shll $0x11,%eax # in bytes
+ movl %eax,%ebp # Base of user stack
+#ifdef BTXLDR_VERBOSE
+ movl $m_mem,%esi # Display
+ call hexout # amount of
+ call putstr # base memory
+#endif
+ lgdt gdtdesc # Load new GDT
+/*
+ * Relocate caller's arguments.
+ */
+#ifdef BTXLDR_VERBOSE
+ movl $m_esp,%esi # Display
+ movl %esp,%eax # caller
+ call hexout # stack
+ call putstr # pointer
+ movl $m_args,%esi # Format string
+ leal 0x4(%esp),%ebx # First argument
+ movl $0x6,%ecx # Count
+start.1: movl (%ebx),%eax # Get argument and
+ addl $0x4,%ebx # bump pointer
+ call hexout # Display it
+ loop start.1 # Till done
+ call putstr # End message
+#endif
+ movl BA_BOOTINFO+4(%esp),%esi # Source: bootinfo
+ cmpl $0x0, %esi # If the bootinfo pointer
+ je start_null_bi # is null, don't copy it
+ movl BI_SIZE(%esi),%ecx # Allocate space
+ subl %ecx,%ebp # for bootinfo
+ movl %ebp,%edi # Destination
+ rep # Copy
+ movsb # it
+ movl %ebp,BA_BOOTINFO+4(%esp) # Update pointer
+ movl %edi,%ebp # Restore base pointer
+#ifdef BTXLDR_VERBOSE
+ movl $m_rel_bi,%esi # Display
+ movl %ebp,%eax # bootinfo
+ call hexout # relocation
+ call putstr # message
+#endif
+start_null_bi: movl $BOOTARGS_SIZE,%ecx # Fixed size of arguments
+ testl $KARGS_FLAGS_EXTARG, BA_BOOTFLAGS+4(%esp) # Check for extra data
+ jz start_fixed # Skip if the flag is not set
+ addl BOOTARGS_SIZE+4(%esp),%ecx # Add size of variable args
+start_fixed: subl $ARGOFF,%ebp # Place args at fixed offset
+ leal 0x4(%esp),%esi # Source
+ movl %ebp,%edi # Destination
+ rep # Copy
+ movsb # them
+#ifdef BTXLDR_VERBOSE
+ movl $m_rel_args,%esi # Display
+ movl %ebp,%eax # argument
+ call hexout # relocation
+ call putstr # message
+#endif
+/*
+ * Set up BTX kernel.
+ */
+ movl $MEM_ESP,%esp # Set up new stack
+ movl $MEM_DATA,%ebx # Data segment
+ movl $m_vers,%esi # Display BTX
+ call putstr # version message
+ movb 0x5(%ebx),%al # Get major version
+ addb $'0',%al # Display
+ call putchr # it
+ movb $'.',%al # And a
+ call putchr # dot
+ movb 0x6(%ebx),%al # Get minor
+ xorb %ah,%ah # version
+ movb $0xa,%dl # Divide
+ divb %dl,%al # by 10
+ addb $'0',%al # Display
+ call putchr # tens
+ movb %ah,%al # Get units
+ addb $'0',%al # Display
+ call putchr # units
+ call putstr # End message
+ movl %ebx,%esi # BTX image
+ movzwl 0x8(%ebx),%edi # Compute
+ orl $PAG_SIZ/PAG_ENT-1,%edi # the
+ incl %edi # BTX
+ shll $0x2,%edi # load
+ addl $MEM_TBL,%edi # address
+ pushl %edi # Save load address
+ movzwl 0xa(%ebx),%ecx # Image size
+#ifdef BTXLDR_VERBOSE
+ pushl %ecx # Save image size
+#endif
+ rep # Relocate
+ movsb # BTX
+ movl %esi,%ebx # Keep place
+#ifdef BTXLDR_VERBOSE
+ movl $m_rel_btx,%esi # Restore
+ popl %eax # parameters
+ call hexout # and
+#endif
+ popl %ebp # display
+#ifdef BTXLDR_VERBOSE
+ movl %ebp,%eax # the
+ call hexout # relocation
+ call putstr # message
+#endif
+ addl $PAG_SIZ,%ebp # Display
+#ifdef BTXLDR_VERBOSE
+ movl $m_base,%esi # the
+ movl %ebp,%eax # user
+ call hexout # base
+ call putstr # address
+#endif
+/*
+ * Set up ELF-format client program.
+ */
+ cmpl $0x464c457f,(%ebx) # ELF magic number?
+ je start.3 # Yes
+ movl $e_fmt,%esi # Display error
+ call putstr # message
+start.2: jmp start.2 # Hang
+start.3:
+#ifdef BTXLDR_VERBOSE
+ movl $m_elf,%esi # Display ELF
+ call putstr # message
+ movl $m_segs,%esi # Format string
+#endif
+ movl $0x2,%edi # Segment count
+ movl 0x1c(%ebx),%edx # Get e_phoff
+ addl %ebx,%edx # To pointer
+ movzwl 0x2c(%ebx),%ecx # Get e_phnum
+start.4: cmpl $0x1,(%edx) # Is p_type PT_LOAD?
+ jne start.6 # No
+#ifdef BTXLDR_VERBOSE
+ movl 0x4(%edx),%eax # Display
+ call hexout # p_offset
+ movl 0x8(%edx),%eax # Display
+ call hexout # p_vaddr
+ movl 0x10(%edx),%eax # Display
+ call hexout # p_filesz
+ movl 0x14(%edx),%eax # Display
+ call hexout # p_memsz
+ call putstr # End message
+#endif
+ pushl %esi # Save
+ pushl %edi # working
+ pushl %ecx # registers
+ movl 0x4(%edx),%esi # Get p_offset
+ addl %ebx,%esi # as pointer
+ movl 0x8(%edx),%edi # Get p_vaddr
+ addl %ebp,%edi # as pointer
+ movl 0x10(%edx),%ecx # Get p_filesz
+ rep # Set up
+ movsb # segment
+ movl 0x14(%edx),%ecx # Any bytes
+ subl 0x10(%edx),%ecx # to zero?
+ jz start.5 # No
+ xorb %al,%al # Then
+ rep # zero
+ stosb # them
+start.5: popl %ecx # Restore
+ popl %edi # working
+ popl %esi # registers
+ decl %edi # Segments to do
+ je start.7 # If none
+start.6: addl $0x20,%edx # To next entry
+ loop start.4 # Till done
+start.7:
+#ifdef BTXLDR_VERBOSE
+ movl $m_done,%esi # Display done
+ call putstr # message
+#endif
+ movl $start.8,%esi # Real mode stub
+ movl $MEM_STUB,%edi # Destination
+ movl $start.9-start.8,%ecx # Size
+ rep # Relocate
+ movsb # it
+ ljmp $SEL_RCODE,$MEM_STUB # To 16-bit code
+ .code16
+start.8: xorw %ax,%ax # Data
+ movb $SEL_RDATA,%al # selector
+ movw %ax,%ss # Reload SS
+ movw %ax,%ds # Reset
+ movw %ax,%es # other
+ movw %ax,%fs # segment
+ movw %ax,%gs # limits
+ movl %cr0,%eax # Switch to
+ decw %ax # real
+ movl %eax,%cr0 # mode
+ ljmp $0,$MEM_ENTRY # Jump to BTX entry point
+start.9:
+ .code32
+/*
+ * Output message [ESI] followed by EAX in hex.
+ */
+hexout: pushl %eax # Save
+ call putstr # Display message
+ popl %eax # Restore
+ pushl %esi # Save
+ pushl %edi # caller's
+ movl $buf,%edi # Buffer
+ pushl %edi # Save
+ call hex32 # To hex
+ xorb %al,%al # Terminate
+ stosb # string
+ popl %esi # Restore
+hexout.1: lodsb # Get a char
+ cmpb $'0',%al # Leading zero?
+ je hexout.1 # Yes
+ testb %al,%al # End of string?
+ jne hexout.2 # No
+ decl %esi # Undo
+hexout.2: decl %esi # Adjust for inc
+ call putstr # Display hex
+ popl %edi # Restore
+ popl %esi # caller's
+ ret # To caller
+/*
+ * Output zero-terminated string [ESI] to the console.
+ */
+putstr.0: call putchr # Output char
+putstr: lodsb # Load char
+ testb %al,%al # End of string?
+ jne putstr.0 # No
+ ret # To caller
+/*
+ * Output character AL to the console.
+ */
+putchr: pusha # Save
+ xorl %ecx,%ecx # Zero for loops
+ movb $SCR_MAT,%ah # Mode/attribute
+ movl $BDA_POS,%ebx # BDA pointer
+ movw (%ebx),%dx # Cursor position
+ movl $0xa0000,%edi # Regen buffer (color)
+putchr.1: cmpb $0xa,%al # New line?
+ je putchr.2 # Yes
+ movw %dx,%cx
+ movb %al,(%edi,%ecx,1) # Write char
+ addl $0x2000,%ecx
+ movb %ah,(%edi,%ecx,1) # Write attr
+ addw $0x2,%dx
+ jmp putchr.3
+putchr.2: movw %dx,%ax
+ movb $SCR_COL*2,%dl
+ div %dl
+ incb %al
+ mul %dl
+ movw %ax,%dx
+putchr.3: cmpw $SCR_COL*SCR_ROW*2,%dx
+ jb putchr.4 # No
+ leal 2*SCR_COL(%edi),%esi # New top line
+ movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
+ rep # Scroll
+ movsl # screen
+ movb $' ',%al # Space
+ xorb %ah,%ah
+ movb $SCR_COL,%cl # Columns to clear
+ rep # Clear
+ stosw # line
+ movw $(SCR_ROW-1)*SCR_COL*2,%dx
+putchr.4: movw %dx,(%ebx) # Update position
+ shrw $1,%dx
+gdcwait.3: inb $0x60,%al
+ testb $0x04,%al
+ jz gdcwait.3
+ movb $0x49,%al
+ outb %al,$0x62
+ movb %dl,%al
+ outb %al,$0x60
+ movb %dh,%al
+ outb %al,$0x60
+ popa # Restore
+ ret # To caller
+/*
+ * Convert EAX, AX, or AL to hex, saving the result to [EDI].
+ */
+hex32: pushl %eax # Save
+ shrl $0x10,%eax # Do upper
+ call hex16 # 16
+ popl %eax # Restore
+hex16: call hex16.1 # Do upper 8
+hex16.1: xchgb %ah,%al # Save/restore
+hex8: pushl %eax # Save
+ shrb $0x4,%al # Do upper
+ call hex8.1 # 4
+ popl %eax # Restore
+hex8.1: andb $0xf,%al # Get lower 4
+ cmpb $0xa,%al # Convert
+ sbbb $0x69,%al # to hex
+ das # digit
+ orb $0x20,%al # To lower case
+ stosb # Save char
+ ret # (Recursive)
+
+ .data
+ .p2align 4
+/*
+ * Global descriptor table.
+ */
+gdt: .word 0x0,0x0,0x0,0x0 # Null entry
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
+ .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+gdt.1:
+gdtdesc: .word gdt.1-gdt-1 # Limit
+ .long gdt # Base
+/*
+ * Messages.
+ */
+m_logo: .asciz " \nBTX loader 1.00 "
+m_vers: .asciz "BTX version is \0\n"
+e_fmt: .asciz "Error: Client format not supported\n"
+#ifdef BTXLDR_VERBOSE
+m_mem: .asciz "Starting in protected mode (base mem=\0)\n"
+m_esp: .asciz "Arguments passed (esp=\0):\n"
+m_args: .asciz"<howto="
+ .asciz" bootdev="
+ .asciz" junk="
+ .asciz" "
+ .asciz" "
+ .asciz" bootinfo=\0>\n"
+m_rel_bi: .asciz "Relocated bootinfo (size=48) to \0\n"
+m_rel_args: .asciz "Relocated arguments (size=18) to \0\n"
+m_rel_btx: .asciz "Relocated kernel (size=\0) to \0\n"
+m_base: .asciz "Client base address is \0\n"
+m_elf: .asciz "Client format is ELF\n"
+m_segs: .asciz "text segment: offset="
+ .asciz " vaddr="
+ .asciz " filesz="
+ .asciz " memsz=\0\n"
+ .asciz "data segment: offset="
+ .asciz " vaddr="
+ .asciz " filesz="
+ .asciz " memsz=\0\n"
+m_done: .asciz "Loading complete\n"
+#endif
+/*
+ * Uninitialized data area.
+ */
+buf: # Scratch buffer
diff --git a/stand/pc98/btx/lib/Makefile b/stand/pc98/btx/lib/Makefile
new file mode 100644
index 0000000..e5876bc
--- /dev/null
+++ b/stand/pc98/btx/lib/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= crt0.o
+INTERNALPROG=
+MAN=
+SRCS= btxcsu.S btxsys.s btxv86.s
+CFLAGS+=-I${.CURDIR}/../../../i386/common
+LDFLAGS=-Wl,-r
+
+.include <bsd.prog.mk>
diff --git a/stand/pc98/btx/lib/btxcsu.S b/stand/pc98/btx/lib/btxcsu.S
new file mode 100644
index 0000000..c46f809
--- /dev/null
+++ b/stand/pc98/btx/lib/btxcsu.S
@@ -0,0 +1,49 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#include <bootargs.h>
+
+#
+# BTX C startup code (ELF).
+#
+
+#
+# Globals.
+#
+ .global _start
+#
+# Client entry point.
+#
+_start: cld
+ pushl %eax
+ movl $_edata,%edi
+ movl $_end,%ecx
+ subl %edi, %ecx
+ xorb %al, %al
+ rep
+ stosb
+ popl __base
+ movl %esp,%eax # Set
+ addl $ARGADJ,%eax # argument
+ movl %eax,__args # pointer
+ call main # Invoke client main()
+ call exit # Invoke client exit()
+#
+# Data.
+#
+ .comm __base,4 # Client base address
+ .comm __args,4 # Client arguments
diff --git a/stand/pc98/btx/lib/btxsys.s b/stand/pc98/btx/lib/btxsys.s
new file mode 100644
index 0000000..9c77b42
--- /dev/null
+++ b/stand/pc98/btx/lib/btxsys.s
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# BTX system calls.
+#
+
+#
+# Globals.
+#
+ .global __exit
+ .global __exec
+#
+# Constants.
+#
+ .set INT_SYS,0x30 # Interrupt number
+#
+# System call: exit
+#
+__exit: xorl %eax,%eax # BTX system
+ int $INT_SYS # call 0x0
+#
+# System call: exec
+#
+__exec: movl $0x1,%eax # BTX system
+ int $INT_SYS # call 0x1
diff --git a/stand/pc98/btx/lib/btxv86.h b/stand/pc98/btx/lib/btxv86.h
new file mode 100644
index 0000000..27f6b34
--- /dev/null
+++ b/stand/pc98/btx/lib/btxv86.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+#ifndef _BTXV86_H_
+#define _BTXV86_H_
+
+#include <sys/types.h>
+#include <machine/psl.h>
+
+#define V86_ADDR 0x10000 /* Segment:offset address */
+#define V86_CALLF 0x20000 /* Emulate far call */
+#define V86_FLAGS 0x40000 /* Return flags */
+
+struct __v86 {
+ uint32_t ctl; /* Control flags */
+ uint32_t addr; /* Interrupt number or address */
+ uint32_t es; /* V86 ES register */
+ uint32_t ds; /* V86 DS register */
+ uint32_t fs; /* V86 FS register */
+ uint32_t gs; /* V86 GS register */
+ uint32_t eax; /* V86 EAX register */
+ uint32_t ecx; /* V86 ECX register */
+ uint32_t edx; /* V86 EDX register */
+ uint32_t ebx; /* V86 EBX register */
+ uint32_t efl; /* V86 eflags register */
+ uint32_t ebp; /* V86 EBP register */
+ uint32_t esi; /* V86 ESI register */
+ uint32_t edi; /* V86 EDI register */
+};
+
+extern struct __v86 __v86; /* V86 interface structure */
+void __v86int(void);
+
+#define v86 __v86
+#define v86int __v86int
+
+extern u_int32_t __base;
+extern u_int32_t __args;
+
+#define PTOV(pa) ((caddr_t)(pa) - __base)
+#define VTOP(va) ((vm_offset_t)(va) + __base)
+#define VTOPSEG(va) (u_int16_t)(VTOP((caddr_t)va) >> 4)
+#define VTOPOFF(va) (u_int16_t)(VTOP((caddr_t)va) & 0xf)
+
+#define V86_CY(x) ((x) & PSL_C)
+#define V86_ZR(x) ((x) & PSL_Z)
+
+void __exit(int) __attribute__((__noreturn__));
+void __exec(caddr_t, ...);
+
+#endif /* !_BTXV86_H_ */
diff --git a/stand/pc98/btx/lib/btxv86.s b/stand/pc98/btx/lib/btxv86.s
new file mode 100644
index 0000000..0d7d111
--- /dev/null
+++ b/stand/pc98/btx/lib/btxv86.s
@@ -0,0 +1,85 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# BTX V86 interface.
+#
+
+#
+# Globals.
+#
+ .global __v86int
+#
+# Fields in V86 interface structure.
+#
+ .set V86_CTL,0x0 # Control flags
+ .set V86_ADDR,0x4 # Int number/address
+ .set V86_ES,0x8 # V86 ES
+ .set V86_DS,0xc # V86 DS
+ .set V86_FS,0x10 # V86 FS
+ .set V86_GS,0x14 # V86 GS
+ .set V86_EAX,0x18 # V86 EAX
+ .set V86_ECX,0x1c # V86 ECX
+ .set V86_EDX,0x20 # V86 EDX
+ .set V86_EBX,0x24 # V86 EBX
+ .set V86_EFL,0x28 # V86 eflags
+ .set V86_EBP,0x2c # V86 EBP
+ .set V86_ESI,0x30 # V86 ESI
+ .set V86_EDI,0x34 # V86 EDI
+#
+# Other constants.
+#
+ .set INT_V86,0x31 # Interrupt number
+ .set SIZ_V86,0x38 # Size of V86 structure
+#
+# V86 interface function.
+#
+__v86int: popl __v86ret # Save return address
+ pushl $__v86 # Push pointer
+ call __v86_swap # Load V86 registers
+ int $INT_V86 # To BTX
+ call __v86_swap # Load user registers
+ addl $0x4,%esp # Discard pointer
+ pushl __v86ret # Restore return address
+ ret # To user
+#
+# Swap V86 and user registers.
+#
+__v86_swap: xchgl %ebp,0x4(%esp,1) # Swap pointer, EBP
+ xchgl %eax,V86_EAX(%ebp) # Swap EAX
+ xchgl %ecx,V86_ECX(%ebp) # Swap ECX
+ xchgl %edx,V86_EDX(%ebp) # Swap EDX
+ xchgl %ebx,V86_EBX(%ebp) # Swap EBX
+ pushl %eax # Save
+ pushf # Put eflags
+ popl %eax # in EAX
+ xchgl %eax,V86_EFL(%ebp) # Swap
+ pushl %eax # Put EAX
+ popf # in eflags
+ movl 0x8(%esp,1),%eax # Load EBP
+ xchgl %eax,V86_EBP(%ebp) # Swap
+ movl %eax,0x8(%esp,1) # Save EBP
+ popl %eax # Restore
+ xchgl %esi,V86_ESI(%ebp) # Swap ESI
+ xchgl %edi,V86_EDI(%ebp) # Swap EDI
+ xchgl %ebp,0x4(%esp,1) # Swap pointer, EBP
+ ret # To caller
+#
+# V86 interface structure.
+#
+ .comm __v86,SIZ_V86
+ .comm __v86ret,4
diff --git a/stand/pc98/cdboot/Makefile b/stand/pc98/cdboot/Makefile
new file mode 100644
index 0000000..ba94111
--- /dev/null
+++ b/stand/pc98/cdboot/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+PROG= cdboot
+STRIP=
+BINMODE=${NOBINMODE}
+MAN=
+SRCS= ${PROG}.S
+
+CFLAGS+=-I${.CURDIR}/../../i386/common
+
+ORG= 0x0000
+
+LDFLAGS=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.cdboot.S= ${CLANG_NO_IAS}
diff --git a/stand/pc98/cdboot/cdboot.S b/stand/pc98/cdboot/cdboot.S
new file mode 100644
index 0000000..c97c02b
--- /dev/null
+++ b/stand/pc98/cdboot/cdboot.S
@@ -0,0 +1,805 @@
+#
+# Copyright (c) 2006 TAKAHASHI Yoshihiro <nyan@FreeBSD.org>
+# Copyright (c) 2001 John Baldwin <jhb@FreeBSD.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+# $FreeBSD$
+
+#
+# Basically, we first create a set of boot arguments to pass to the loaded
+# binary. Then we attempt to load /boot/loader from the CD we were booted
+# off of.
+#
+
+#include <bootargs.h>
+
+#
+# Memory locations.
+#
+ .set STACK_OFF,0x6000 # Stack offset
+ .set LOAD_SEG,0x0700 # Load segment
+ .set LOAD_SIZE,2048 # Load size
+ .set DAUA,0x0584 # DA/UA
+
+ .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k
+ .set MEM_ARG,0x900 # Arguments at start
+ .set MEM_ARG_BTX,0xa100 # Where we move them to so the
+ # BTX client can see them
+ .set MEM_ARG_SIZE,0x18 # Size of the arguments
+ .set MEM_BTX_ADDRESS,0x9000 # where BTX lives
+ .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute
+ .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader
+ .set MEM_BTX_CLIENT,0xa000 # where BTX clients live
+#
+# PC98 machine type from sys/pc98/pc98/pc98_machdep.h
+#
+ .set MEM_SYS, 0xa100 # System common area segment
+ .set PC98_MACHINE_TYPE, 0x0620 # PC98 machine type
+ .set EPSON_ID, 0x0624 # EPSON machine id
+
+ .set M_NEC_PC98, 0x0001
+ .set M_EPSON_PC98, 0x0002
+ .set M_NOT_H98, 0x0010
+ .set M_H98, 0x0020
+ .set M_NOTE, 0x0040
+ .set M_NORMAL, 0x1000
+ .set M_8M, 0x8000
+#
+# Signature Constants
+#
+ .set SIG1_OFF,0x1fe # Signature offset
+ .set SIG2_OFF,0x7fe # Signature offset
+#
+# a.out header fields
+#
+ .set AOUT_TEXT,0x04 # text segment size
+ .set AOUT_DATA,0x08 # data segment size
+ .set AOUT_BSS,0x0c # zero'd BSS size
+ .set AOUT_SYMBOLS,0x10 # symbol table
+ .set AOUT_ENTRY,0x14 # entry point
+ .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header
+#
+# Segment selectors.
+#
+ .set SEL_SDATA,0x8 # Supervisor data
+ .set SEL_RDATA,0x10 # Real mode data
+ .set SEL_SCODE,0x18 # PM-32 code
+ .set SEL_SCODE16,0x20 # PM-16 code
+#
+# BTX constants
+#
+ .set INT_SYS,0x30 # BTX syscall interrupt
+#
+# Constants for reading from the CD.
+#
+ .set ERROR_TIMEOUT,0x90 # BIOS timeout on read
+ .set NUM_RETRIES,3 # Num times to retry
+ .set SECTOR_SIZE,0x800 # size of a sector
+ .set SECTOR_SHIFT,11 # number of place to shift
+ .set BUFFER_LEN,0x100 # number of sectors in buffer
+ .set MAX_READ,0xf800 # max we can read at a time
+ .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT
+ .set MEM_READ_BUFFER,0x9000 # buffer to read from CD
+ .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor
+ .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer
+ .set VOLDESC_LBA,0x10 # LBA of vol descriptor
+ .set VD_PRIMARY,1 # Primary VD
+ .set VD_END,255 # VD Terminator
+ .set VD_ROOTDIR,156 # Offset of Root Dir Record
+ .set DIR_LEN,0 # Offset of Dir Record length
+ .set DIR_EA_LEN,1 # Offset of EA length
+ .set DIR_EXTENT,2 # Offset of 64-bit LBA
+ .set DIR_SIZE,10 # Offset of 64-bit length
+ .set DIR_NAMELEN,32 # Offset of 8-bit name len
+ .set DIR_NAME,33 # Offset of dir name
+
+#
+# Program start.
+#
+ .code16
+ .globl start
+
+start: jmp main
+
+ .org 4
+ .ascii "IPL1 "
+
+main: cld
+
+ /* Setup the stack */
+ xor %ax,%ax
+ mov %ax,%ss
+ mov $STACK_OFF,%sp
+
+ push %ecx
+
+ /* Setup graphic screen */
+ mov $0x42,%ah # 640x400
+ mov $0xc0,%ch
+ int $0x18
+ mov $0x40,%ah # graph on
+ int $0x18
+
+ /* Setup text screen */
+ mov $0x0a00,%ax # 80x25
+ int $0x18
+ mov $0x0c,%ah # text on
+ int $0x18
+ mov $0x13,%ah # cursor home
+ xor %dx,%dx
+ int $0x18
+ mov $0x11,%ah # cursor on
+ int $0x18
+
+ /* Setup keyboard */
+ mov $0x03,%ah
+ int $0x18
+
+ /* Transfer PC-9801 system common area */
+ xor %ax,%ax
+ mov %ax,%si
+ mov %ax,%ds
+ mov %ax,%di
+ mov $MEM_SYS,%ax
+ mov %ax,%es
+ mov $0x0600,%cx
+ rep
+ movsb
+
+ /* Transfer EPSON machine type */
+ mov $0xfd00,%ax
+ mov %ax,%ds
+ mov (0x804),%eax
+ and $0x00ffffff,%eax
+ mov %eax,%es:(EPSON_ID)
+
+ /* Set machine type to PC98_SYSTEM_PARAMETER */
+ call machine_check
+
+ /* Load cdboot */
+ xor %ax,%ax
+ mov %ax,%ds
+ mov $0x06,%ah /* Read data */
+ mov (DAUA),%al /* Read drive */
+ pop %ecx /* cylinder */
+ xor %dx,%dx /* head / sector */
+ mov $LOAD_SEG,%bx /* Load address */
+ mov %bx,%es
+ xor %bp,%bp
+ mov $LOAD_SIZE,%bx /* Load size */
+ int $0x1b
+ mov $msg_readerr,%si
+ jc error
+
+ /* Jump to cdboot */
+ ljmp $LOAD_SEG,$cdboot
+
+#
+# Set machine type to PC98_SYSTEM_PARAMETER.
+#
+machine_check: xor %edx,%edx
+ mov %dx,%ds
+ mov $MEM_SYS,%ax
+ mov %ax,%es
+
+ /* Wait V-SYNC */
+vsync.1: inb $0x60,%al
+ test $0x20,%al
+ jnz vsync.1
+vsync.2: inb $0x60,%al
+ test $0x20,%al
+ jz vsync.2
+
+ /* ANK 'A' font */
+ xor %al,%al
+ outb %al,$0xa1
+ mov $0x41,%al
+ outb %al,$0xa3
+
+ /* Get 'A' font from CG window */
+ push %ds
+ mov $0xa400,%ax
+ mov %ax,%ds
+ xor %eax,%eax
+ xor %bx,%bx
+ mov $4,%cx
+font.1: add (%bx),%eax
+ add $4,%bx
+ loop font.1
+ pop %ds
+ cmp $0x6efc58fc,%eax
+ jnz m_epson
+
+m_pc98: or $M_NEC_PC98,%edx
+ mov $0x0458,%bx
+ mov (%bx),%al
+ test $0x80,%al
+ jz m_not_h98
+ or $M_H98,%edx
+ jmp 1f
+m_epson: or $M_EPSON_PC98,%edx
+m_not_h98: or $M_NOT_H98,%edx
+
+1: inb $0x42,%al
+ test $0x20,%al
+ jz 1f
+ or $M_8M,%edx
+
+1: mov $0x0400,%bx
+ mov (%bx),%al
+ test $0x80,%al
+ jz 1f
+ or $M_NOTE,%edx
+
+1: mov $PC98_MACHINE_TYPE,%bx
+ mov %edx,%es:(%bx)
+ ret
+
+#
+# Print out the error message at [SI], wait for a keypress, and then
+# reboot the machine.
+#
+error: call putstr
+ mov $msg_keypress,%si
+ call putstr
+ xor %ax,%ax # Get keypress
+ int $0x18
+ xor %ax,%ax # CPU reset
+ outb %al,$0xf0
+halt: hlt
+ jmp halt # Spin
+
+#
+# Display a null-terminated string at [SI].
+#
+# Trashes: AX, BX, CX, DX, SI, DI
+#
+putstr: push %ds
+ push %es
+ mov %cs,%ax
+ mov %ax,%ds
+ mov $0xa000,%ax
+ mov %ax,%es
+ mov cursor,%di
+ mov $0x00e1,%bx # Attribute
+ mov $160,%cx
+putstr.0: lodsb
+ testb %al,%al
+ jz putstr.done
+ cmp $0x0d,%al
+ jz putstr.cr
+ cmp $0x0a,%al
+ jz putstr.lf
+ mov %bl,%es:0x2000(%di)
+ stosb
+ inc %di
+ jmp putstr.move
+putstr.cr: xor %dx,%dx
+ mov %di,%ax
+ div %cx
+ sub %dx,%di
+ jmp putstr.move
+putstr.lf: add %cx,%di
+putstr.move: mov %di,%dx
+ mov $0x13,%ah # Move cursor
+ int $0x18
+ jmp putstr.0
+putstr.done: mov %di,cursor
+ pop %es
+ pop %ds
+ ret
+
+#
+# Display a single char at [AL], but don't move a cursor.
+#
+putc: push %es
+ push %di
+ push %bx
+ mov $0xa000,%bx
+ mov %bx,%es
+ mov cursor,%di
+ mov $0xe1,%bl # Attribute
+ mov %bl,%es:0x2000(%di)
+ stosb
+ pop %bx
+ pop %di
+ pop %es
+ ret
+
+msg_readerr: .asciz "Read Error\r\n"
+msg_keypress: .asciz "\r\nPress any key to reboot\r\n"
+
+/* Boot signature */
+
+ .org SIG1_OFF,0x90
+
+ .word 0xaa55 # Magic number
+
+#
+# cdboot
+#
+cdboot: mov %cs,%ax
+ mov %ax,%ds
+ xor %ax,%ax
+ mov %ax,%es
+ mov %es:(DAUA),%al # Save BIOS boot device
+ mov %al,drive
+ mov %cx,cylinder # Save BIOS boot cylinder
+
+ mov $msg_welcome,%si # %ds:(%si) -> welcome message
+ call putstr # display the welcome message
+#
+# Setup the arguments that the loader is expecting from boot[12]
+#
+ mov $msg_bootinfo,%si # %ds:(%si) -> boot args message
+ call putstr # display the message
+ mov $MEM_ARG,%bx # %ds:(%bx) -> boot args
+ mov %bx,%di # %es:(%di) -> boot args
+ xor %eax,%eax # zero %eax
+ mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit
+ # dwords
+ rep # Clear the arguments
+ stosl # to zero
+ mov drive,%dl # Store BIOS boot device
+ mov %dl,%es:0x4(%bx) # in kargs->bootdev
+ or $KARGS_FLAGS_CD,%es:0x8(%bx) # kargs->bootflags |=
+ # KARGS_FLAGS_CD
+#
+# Load Volume Descriptor
+#
+ mov $VOLDESC_LBA,%eax # Set LBA of first VD
+load_vd: push %eax # Save %eax
+ mov $1,%dh # One sector
+ mov $MEM_VOLDESC,%ebx # Destination
+ call read # Read it in
+ cmpb $VD_PRIMARY,%es:(%bx) # Primary VD?
+ je have_vd # Yes
+ pop %eax # Prepare to
+ inc %eax # try next
+ cmpb $VD_END,%es:(%bx) # Last VD?
+ jne load_vd # No, read next
+ mov $msg_novd,%si # No VD
+ jmp error # Halt
+have_vd: # Have Primary VD
+#
+# Try to look up the loader binary using the paths in the loader_paths
+# array.
+#
+ mov $loader_paths,%si # Point to start of array
+lookup_path: push %si # Save file name pointer
+ call lookup # Try to find file
+ pop %di # Restore file name pointer
+ jnc lookup_found # Found this file
+ push %es
+ mov %cs,%ax
+ mov %ax,%es
+ xor %al,%al # Look for next
+ mov $0xffff,%cx # path name by
+ repnz # scanning for
+ scasb # nul char
+ pop %es
+ mov %di,%si # Point %si at next path
+ mov (%si),%al # Get first char of next path
+ or %al,%al # Is it double nul?
+ jnz lookup_path # No, try it.
+ mov $msg_failed,%si # Failed message
+ jmp error # Halt
+lookup_found: # Found a loader file
+#
+# Load the binary into the buffer. Due to real mode addressing limitations
+# we have to read it in 64k chunks.
+#
+ mov %es:DIR_SIZE(%bx),%eax # Read file length
+ add $SECTOR_SIZE-1,%eax # Convert length to sectors
+ shr $SECTOR_SHIFT,%eax
+ cmp $BUFFER_LEN,%eax
+ jbe load_sizeok
+ mov $msg_load2big,%si # Error message
+ jmp error
+load_sizeok: movzbw %al,%cx # Num sectors to read
+ mov %es:DIR_EXTENT(%bx),%eax # Load extent
+ xor %edx,%edx
+ mov %es:DIR_EA_LEN(%bx),%dl
+ add %edx,%eax # Skip extended
+ mov $MEM_READ_BUFFER,%ebx # Read into the buffer
+load_loop: mov %cl,%dh
+ cmp $MAX_READ_SEC,%cl # Truncate to max read size
+ jbe load_notrunc
+ mov $MAX_READ_SEC,%dh
+load_notrunc: sub %dh,%cl # Update count
+ push %eax # Save
+ call read # Read it in
+ pop %eax # Restore
+ add $MAX_READ_SEC,%eax # Update LBA
+ add $MAX_READ,%ebx # Update dest addr
+ jcxz load_done # Done?
+ jmp load_loop # Keep going
+load_done:
+#
+# Turn on the A20 address line
+#
+ xor %ax,%ax # Turn A20 on
+ outb %al,$0xf2
+ mov $0x02,%al
+ outb %al,$0xf6
+#
+# Relocate the loader and BTX using a very lazy protected mode
+#
+ mov $msg_relocate,%si # Display the
+ call putstr # relocation message
+ mov %es:(MEM_READ_BUFFER+AOUT_ENTRY),%edi # %edi is the destination
+ mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is
+ # the start of the text
+ # segment
+ mov %es:(MEM_READ_BUFFER+AOUT_TEXT),%ecx # %ecx = length of the text
+ # segment
+ push %edi # Save entry point for later
+ lgdt gdtdesc # setup our own gdt
+ cli # turn off interrupts
+ mov %cr0,%eax # Turn on
+ or $0x1,%al # protected
+ mov %eax,%cr0 # mode
+ ljmp $SEL_SCODE,$pm_start # long jump to clear the
+ # instruction pre-fetch queue
+ .code32
+pm_start: mov $SEL_SDATA,%ax # Initialize
+ mov %ax,%ds # %ds and
+ mov %ax,%es # %es to a flat selector
+ rep # Relocate the
+ movsb # text segment
+ add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page
+ and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment
+ mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment
+ rep # Relocate the
+ movsb # data segment
+ mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss
+ xor %eax,%eax # zero %eax
+ add $3,%cl # round %ecx up to
+ shr $2,%ecx # a multiple of 4
+ rep # zero the
+ stosl # bss
+ mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader
+ add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader
+ mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go
+ movzwl 0xa(%esi),%ecx # %ecx -> length of BTX
+ rep # Relocate
+ movsb # BTX
+ ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM
+ .code16
+pm_16: mov $SEL_RDATA,%ax # Initialize
+ mov %ax,%ds # %ds and
+ mov %ax,%es # %es to a real mode selector
+ mov %cr0,%eax # Turn off
+ and $~0x1,%al # protected
+ mov %eax,%cr0 # mode
+ ljmp $LOAD_SEG,$pm_end # Long jump to clear the
+ # instruction pre-fetch queue
+pm_end: sti # Turn interrupts back on now
+#
+# Copy the BTX client to MEM_BTX_CLIENT
+#
+ mov %cs,%ax
+ mov %ax,%ds
+ xor %ax,%ax
+ mov %ax,%es
+ mov $MEM_BTX_CLIENT,%di # Prepare to relocate
+ mov $btx_client,%si # the simple btx client
+ mov $(btx_client_end-btx_client),%cx # length of btx client
+ rep # Relocate the
+ movsb # simple BTX client
+#
+# Copy the boot[12] args to where the BTX client can see them
+#
+ xor %ax,%ax
+ mov %ax,%ds
+ mov $MEM_ARG,%si # where the args are at now
+ mov $MEM_ARG_BTX,%di # where the args are moving to
+ mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs
+ rep # Relocate
+ movsl # the words
+#
+# Save the entry point so the client can get to it later on
+#
+ pop %eax # Restore saved entry point
+ stosl # and add it to the end of
+ # the arguments
+#
+# Now we just start up BTX and let it do the rest
+#
+ mov $msg_jump,%si # Display the
+ call putstr # jump message
+ ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point
+
+#
+# Lookup the file in the path at [SI] from the root directory.
+#
+# Trashes: All but BX
+# Returns: CF = 0 (success), BX = pointer to record
+# CF = 1 (not found)
+#
+lookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record
+ push %bx
+ push %si
+ mov $msg_lookup,%si # Display lookup message
+ call putstr
+ pop %si
+ push %si
+ call putstr
+ mov $msg_lookup2,%si
+ call putstr
+ pop %si
+ pop %bx
+lookup_dir: lodsb # Get first char of path
+ cmp $0,%al # Are we done?
+ je lookup_done # Yes
+ cmp $'/',%al # Skip path separator.
+ je lookup_dir
+ dec %si # Undo lodsb side effect
+ call find_file # Lookup first path item
+ jnc lookup_dir # Try next component
+ mov $msg_lookupfail,%si # Not found message
+ push %bx
+ call putstr
+ pop %bx
+ stc # Set carry
+ ret
+lookup_done: mov $msg_lookupok,%si # Success message
+ push %bx
+ call putstr
+ pop %bx
+ clc # Clear carry
+ ret
+
+#
+# Lookup file at [SI] in directory whose record is at [BX].
+#
+# Trashes: All but returns
+# Returns: CF = 0 (success), BX = pointer to record, SI = next path item
+# CF = 1 (not found), SI = preserved
+#
+find_file: mov %es:DIR_EXTENT(%bx),%eax # Load extent
+ xor %edx,%edx
+ mov %es:DIR_EA_LEN(%bx),%dl
+ add %edx,%eax # Skip extended attributes
+ mov %eax,rec_lba # Save LBA
+ mov %es:DIR_SIZE(%bx),%eax # Save size
+ mov %eax,rec_size
+ xor %cl,%cl # Zero length
+ push %si # Save
+ff.namelen: inc %cl # Update length
+ lodsb # Read char
+ cmp $0,%al # Nul?
+ je ff.namedone # Yes
+ cmp $'/',%al # Path separator?
+ jnz ff.namelen # No, keep going
+ff.namedone: dec %cl # Adjust length and save
+ mov %cl,name_len
+ pop %si # Restore
+ff.load: mov rec_lba,%eax # Load LBA
+ mov $MEM_DIR,%ebx # Address buffer
+ mov $1,%dh # One sector
+ call read # Read directory block
+ incl rec_lba # Update LBA to next block
+ff.scan: mov %ebx,%edx # Check for EOF
+ sub $MEM_DIR,%edx
+ cmp %edx,rec_size
+ ja ff.scan.1
+ stc # EOF reached
+ ret
+ff.scan.1: cmpb $0,%es:DIR_LEN(%bx) # Last record in block?
+ je ff.nextblock
+ push %si # Save
+ movzbw %es:DIR_NAMELEN(%bx),%si # Find end of string
+ff.checkver: cmpb $'0',%es:DIR_NAME-1(%bx,%si) # Less than '0'?
+ jb ff.checkver.1
+ cmpb $'9',%es:DIR_NAME-1(%bx,%si) # Greater than '9'?
+ ja ff.checkver.1
+ dec %si # Next char
+ jnz ff.checkver
+ jmp ff.checklen # All numbers in name, so
+ # no version
+ff.checkver.1: movzbw %es:DIR_NAMELEN(%bx),%cx
+ cmp %cx,%si # Did we find any digits?
+ je ff.checkdot # No
+ cmpb $';',%es:DIR_NAME-1(%bx,%si) # Check for semicolon
+ jne ff.checkver.2
+ dec %si # Skip semicolon
+ mov %si,%cx
+ mov %cl,%es:DIR_NAMELEN(%bx) # Adjust length
+ jmp ff.checkdot
+ff.checkver.2: mov %cx,%si # Restore %si to end of string
+ff.checkdot: cmpb $'.',%es:DIR_NAME-1(%bx,%si) # Trailing dot?
+ jne ff.checklen # No
+ decb %es:DIR_NAMELEN(%bx) # Adjust length
+ff.checklen: pop %si # Restore
+ movzbw name_len,%cx # Load length of name
+ cmp %cl,%es:DIR_NAMELEN(%bx) # Does length match?
+ je ff.checkname # Yes, check name
+ff.nextrec: add %es:DIR_LEN(%bx),%bl # Next record
+ adc $0,%bh
+ jmp ff.scan
+ff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size
+ jnc ff.load # If subtract ok, keep going
+ ret # End of file, so not found
+ff.checkname: lea DIR_NAME(%bx),%di # Address name in record
+ push %si # Save
+ repe cmpsb # Compare name
+ je ff.match # We have a winner!
+ pop %si # Restore
+ jmp ff.nextrec # Keep looking.
+ff.match: add $2,%sp # Discard saved %si
+ clc # Clear carry
+ ret
+
+#
+# Load DH sectors starting at LBA EAX into [EBX].
+#
+# Trashes: EAX
+#
+read: push %es # Save
+ push %bp
+ push %dx
+ push %cx
+ push %ebx
+ mov %bx,%bp # Set destination address
+ and $0x000f,%bp
+ shr $4,%ebx
+ mov %bx,%es
+ xor %bx,%bx # Set read bytes
+ mov %dh,%bl
+ shl $SECTOR_SHIFT,%bx # 2048 bytes/sec
+ mov %ax,%cx # Set LBA
+ shr $16,%eax
+ mov %ax,%dx
+read.retry: mov $0x06,%ah # BIOS device read
+ mov drive,%al
+ and $0x7f,%al
+ call twiddle # Entertain the user
+ int $0x1b # Call BIOS
+ jc read.fail # Worked?
+ pop %ebx # Restore
+ pop %cx
+ pop %dx
+ pop %bp
+ pop %es
+ ret # Return
+read.fail: cmp $ERROR_TIMEOUT,%ah # Timeout?
+ je read.retry # Yes, Retry.
+read.error: mov %ah,%al # Save error
+ mov $hex_error,%di # Format it
+ call hex8 # as hex
+ mov $msg_badread,%si # Display Read error message
+ jmp error
+
+#
+# Output the "twiddle"
+#
+twiddle: push %ax # Save
+ push %bx # Save
+ mov twiddle_index,%al # Load index
+ mov $twiddle_chars,%bx # Address table
+ inc %al # Next
+ and $3,%al # char
+ mov %al,twiddle_index # Save index for next call
+ xlat # Get char
+ call putc # Output it
+ pop %bx # Restore
+ pop %ax # Restore
+ ret
+
+#
+# Convert AL to hex, saving the result to [EDI].
+#
+hex8: pushl %eax # Save
+ shrb $0x4,%al # Do upper
+ call hex8.1 # 4
+ popl %eax # Restore
+hex8.1: andb $0xf,%al # Get lower 4
+ cmpb $0xa,%al # Convert
+ sbbb $0x69,%al # to hex
+ das # digit
+ orb $0x20,%al # To lower case
+ mov %al,(%di) # Save char
+ inc %di
+ ret # (Recursive)
+
+#
+# BTX client to start btxldr
+#
+ .code32
+btx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi
+ # %ds:(%esi) -> end
+ # of boot[12] args
+ mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push
+ std # Go backwards
+push_arg: lodsl # Read argument
+ push %eax # Push it onto the stack
+ loop push_arg # Push all of the arguments
+ cld # In case anyone depends on this
+ pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of
+ # the loader
+ push %eax # Emulate a near call
+ mov $0x1,%eax # 'exec' system call
+ int $INT_SYS # BTX system call
+btx_client_end:
+ .code16
+
+ .p2align 4
+#
+# Global descriptor table.
+#
+gdt: .word 0x0,0x0,0x0,0x0 # Null entry
+ .word 0xffff,0x0000,0x9200,0x00cf # SEL_SDATA
+ .word 0xffff,0x0000,0x9200,0x0000 # SEL_RDATA
+ .word 0xffff,LOAD_SEG<<4,0x9a00,0x00cf # SEL_SCODE (32-bit)
+ .word 0xffff,LOAD_SEG<<4,0x9a00,0x008f # SEL_SCODE16 (16-bit)
+gdt.1:
+#
+# Pseudo-descriptors.
+#
+gdtdesc: .word gdt.1-gdt-1 # Limit
+ .long LOAD_SEG<<4 + gdt # Base
+
+#
+# BOOT device
+#
+drive: .byte 0
+cylinder: .word 0
+
+#
+# State for searching dir
+#
+rec_lba: .long 0x0 # LBA (adjusted for EA)
+rec_size: .long 0x0 # File size
+name_len: .byte 0x0 # Length of current name
+
+cursor: .word 0
+twiddle_index: .byte 0x0
+
+msg_welcome: .asciz "CD Loader 1.2\r\n\n"
+msg_bootinfo: .asciz "Building the boot loader arguments\r\n"
+msg_relocate: .asciz "Relocating the loader and the BTX\r\n"
+msg_jump: .asciz "Starting the BTX loader\r\n"
+msg_badread: .ascii "Read Error: 0x"
+hex_error: .asciz "00\r\n"
+msg_novd: .asciz "Could not find Primary Volume Descriptor\r\n"
+msg_lookup: .asciz "Looking up "
+msg_lookup2: .asciz "... "
+msg_lookupok: .asciz "Found\r\n"
+msg_lookupfail: .asciz "File not found\r\n"
+msg_load2big: .asciz "File too big\r\n"
+msg_failed: .asciz "Boot failed\r\n"
+twiddle_chars: .ascii "|/-\\"
+loader_paths: .asciz "/BOOT.PC98/LOADER"
+ .asciz "/boot.pc98/loader"
+ .asciz "/BOOT/LOADER"
+ .asciz "/boot/loader"
+ .byte 0
+
+/* Boot signature */
+
+ .org SIG2_OFF,0x90
+
+ .word 0xaa55 # Magic number
diff --git a/stand/pc98/kgzldr/Makefile b/stand/pc98/kgzldr/Makefile
new file mode 100644
index 0000000..0070d70
--- /dev/null
+++ b/stand/pc98/kgzldr/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+PROG= kgzldr.o
+STRIP=
+BINMODE=${LIBMODE}
+BINDIR= ${LIBDIR}
+MAN=
+
+SRCS= start.s boot.c inflate.c lib.c crt.s sio.s
+CFLAGS= -Os
+CFLAGS+=-DKZIP
+NO_SHARED=
+LDFLAGS=-Wl,-r
+.PATH: ${.CURDIR}/../../../kern
+.PATH: ${.CURDIR}/../../i386/kgzldr
+
+BOOT_COMCONSOLE_PORT?= 0x238
+AFLAGS+=--defsym SIO_PRT=${BOOT_COMCONSOLE_PORT}
+
+.include <bsd.prog.mk>
diff --git a/stand/pc98/kgzldr/crt.s b/stand/pc98/kgzldr/crt.s
new file mode 100644
index 0000000..35c1fc2
--- /dev/null
+++ b/stand/pc98/kgzldr/crt.s
@@ -0,0 +1,89 @@
+#
+# Copyright (c) 1999 Global Technology Associates, Inc.
+# 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.
+#
+# From: btx.s 1.10 1999/02/25 16:27:41 rnordier
+# $FreeBSD$
+#
+
+# Screen defaults and assumptions.
+
+ .set SCR_MAT,0xe1 # Mode/attribute
+ .set SCR_COL,0x50 # Columns per row
+ .set SCR_ROW,0x19 # Rows per screen
+
+# BIOS Data Area locations.
+
+ .set BDA_POS,0x53e # Cursor position
+
+ .globl crt_putchr
+
+# void crt_putchr(int c)
+
+crt_putchr: movb 0x4(%esp,1),%al # Get character
+ pusha # Save
+ xorl %ecx,%ecx # Zero for loops
+ movb $SCR_MAT,%ah # Mode/attribute
+ movl $BDA_POS,%ebx # BDA pointer
+ movw (%ebx),%dx # Cursor position
+ movl $0xa0000,%edi
+crt_putchr.1: cmpb $0xa,%al # New line?
+ je crt_putchr.2 # Yes
+ movw %dx,%cx
+ movb %al,(%edi,%ecx,1) # Write char
+ addl $0x2000,%ecx
+ movb %ah,(%edi,%ecx,1) # Write attr
+ addw $0x02,%dx
+ jmp crt_putchr.3
+crt_putchr.2: movw %dx,%ax
+ movb $SCR_COL*2,%dl
+ div %dl
+ incb %al
+ mul %dl
+ movw %ax,%dx
+crt_putchr.3: cmpw $SCR_ROW*SCR_COL*2,%dx
+ jb crt_putchr.4 # No
+ leal 2*SCR_COL(%edi),%esi # New top line
+ movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
+ rep # Scroll
+ movsl # screen
+ movb $' ',%al # Space
+ xorb %ah,%ah
+ movb $SCR_COL,%cl # Columns to clear
+ rep # Clear
+ stosw # line
+ movw $(SCR_ROW-1)*SCR_COL*2,%dx
+crt_putchr.4: movw %dx,(%ebx) # Update position
+ shrw $1,%dx
+crt_putchr.5: inb $0x60,%al # Move cursor
+ testb $0x04,%al
+ jz crt_putchr.5
+ movb $0x49,%al
+ outb %al,$0x62
+ movb %dl,%al
+ outb %al,$0x60
+ movb %dh,%al
+ outb %al,$0x60
+ popa # Restore
+ ret # To caller
diff --git a/stand/pc98/libpc98/Makefile b/stand/pc98/libpc98/Makefile
new file mode 100644
index 0000000..f3e27a4
--- /dev/null
+++ b/stand/pc98/libpc98/Makefile
@@ -0,0 +1,51 @@
+# $FreeBSD$
+#
+LIB= pc98
+INTERNALLIB=
+
+.PATH: ${.CURDIR}/../../i386/libi386
+
+SRCS= bioscd.c biosdisk.c biosmem.c biospnp.c \
+ biospci.c biossmap.c bootinfo.c bootinfo32.c \
+ comconsole.c devicename.c elf32_freebsd.c \
+ i386_copy.c i386_module.c nullconsole.c pc98_sys.c pxe.c pxetramp.s \
+ time.c vidconsole.c
+.PATH: ${.CURDIR}/../../zfs
+SRCS+= devicename_stubs.c
+
+# Enable PXE TFTP or NFS support, not both.
+.if defined(LOADER_TFTP_SUPPORT)
+CFLAGS+= -DLOADER_TFTP_SUPPORT
+.else
+CFLAGS+= -DLOADER_NFS_SUPPORT
+.endif
+
+BOOT_COMCONSOLE_PORT?= 0x238
+CFLAGS+= -DCOMPORT=${BOOT_COMCONSOLE_PORT}
+
+BOOT_COMCONSOLE_SPEED?= 9600
+CFLAGS+= -DCOMSPEED=${BOOT_COMCONSOLE_SPEED}
+
+.ifdef(BOOT_BIOSDISK_DEBUG)
+# Make the disk code more talkative
+CFLAGS+= -DDISK_DEBUG
+.endif
+
+# Include simple terminal emulation (cons25-compatible)
+CFLAGS+= -DTERM_EMU
+
+# XXX: make alloca() useable
+CFLAGS+= -Dalloca=__builtin_alloca
+
+CFLAGS+= -I${.CURDIR}/../../ficl -I${.CURDIR}/../../ficl/i386 \
+ -I${.CURDIR}/../../common \
+ -I${.CURDIR}/../btx/lib \
+ -I${.CURDIR}/../../i386/libi386 \
+ -I${.CURDIR}/../../.. -I.
+# the location of libstand
+CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/
+
+# Handle FreeBSD specific %b and %D printf format specifiers
+CFLAGS+= ${FORMAT_EXTENSIONS}
+
+.include <bsd.lib.mk>
diff --git a/stand/pc98/libpc98/bioscd.c b/stand/pc98/libpc98/bioscd.c
new file mode 100644
index 0000000..f259701
--- /dev/null
+++ b/stand/pc98/libpc98/bioscd.c
@@ -0,0 +1,420 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * BIOS CD device handling for CD's that have been booted off of via no
+ * emulation booting as defined in the El Torito standard.
+ *
+ * Ideas and algorithms from:
+ *
+ * - FreeBSD libi386/biosdisk.c
+ *
+ */
+
+#include <stand.h>
+
+#include <sys/param.h>
+#include <machine/bootinfo.h>
+
+#include <stdarg.h>
+
+#include <bootstrap.h>
+#include <btxv86.h>
+#include "libi386.h"
+
+#define BIOSCD_SECSIZE 2048
+#define BUFSIZE (1 * BIOSCD_SECSIZE)
+#define MAXBCDEV 1
+
+/* Major numbers for devices we frontend for. */
+#define ACDMAJOR 117
+#define CDMAJOR 15
+
+#ifdef DISK_DEBUG
+# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
+#else
+# define DEBUG(fmt, args...)
+#endif
+
+struct specification_packet {
+ u_char sp_size;
+ u_char sp_bootmedia;
+ u_char sp_drive;
+ u_char sp_controller;
+ u_int sp_lba;
+ u_short sp_devicespec;
+ u_short sp_buffersegment;
+ u_short sp_loadsegment;
+ u_short sp_sectorcount;
+ u_short sp_cylsec;
+ u_char sp_head;
+};
+
+/*
+ * List of BIOS devices, translation from disk unit number to
+ * BIOS unit number.
+ */
+static struct bcinfo {
+ int bc_unit; /* BIOS unit number */
+ struct specification_packet bc_sp;
+ int bc_open; /* reference counter */
+ void *bc_bcache; /* buffer cache data */
+} bcinfo [MAXBCDEV];
+static int nbcinfo = 0;
+
+#define BC(dev) (bcinfo[(dev)->d_unit])
+
+static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
+static int bc_init(void);
+static int bc_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int bc_realstrategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int bc_open(struct open_file *f, ...);
+static int bc_close(struct open_file *f);
+static int bc_print(int verbose);
+
+struct devsw bioscd = {
+ "cd",
+ DEVT_CD,
+ bc_init,
+ bc_strategy,
+ bc_open,
+ bc_close,
+ noioctl,
+ bc_print,
+ NULL
+};
+
+/*
+ * Translate between BIOS device numbers and our private unit numbers.
+ */
+int
+bc_bios2unit(int biosdev)
+{
+ int i;
+
+ DEBUG("looking for bios device 0x%x", biosdev);
+ for (i = 0; i < nbcinfo; i++) {
+ DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
+ if (bcinfo[i].bc_unit == biosdev)
+ return(i);
+ }
+ return(-1);
+}
+
+int
+bc_unit2bios(int unit)
+{
+ if ((unit >= 0) && (unit < nbcinfo))
+ return(bcinfo[unit].bc_unit);
+ return(-1);
+}
+
+/*
+ * We can't quiz, we have to be told what device to use, so this functoin
+ * doesn't do anything. Instead, the loader calls bc_add() with the BIOS
+ * device number to add.
+ */
+static int
+bc_init(void)
+{
+
+ return (0);
+}
+
+int
+bc_add(int biosdev)
+{
+
+ if (nbcinfo >= MAXBCDEV)
+ return (-1);
+ bcinfo[nbcinfo].bc_unit = biosdev;
+
+ /* SCSI CD-ROM only */
+ if ((biosdev & 0xf0) != 0xa0)
+ return (-1);
+ if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5)
+ return (-1);
+
+ printf("BIOS CD is cd%d\n", nbcinfo);
+ nbcinfo++;
+ bcache_add_dev(nbcinfo); /* register cd device in bcache */
+ return(0);
+}
+
+/*
+ * Print information about disks
+ */
+static int
+bc_print(int verbose)
+{
+ char line[80];
+ int i, ret = 0;
+
+ if (nbcinfo == 0)
+ return (0);
+
+ printf("%s devices:", bioscd.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ for (i = 0; i < nbcinfo; i++) {
+ sprintf(line, " cd%d: Device 0x%x\n", i,
+ bcinfo[i].bc_sp.sp_devicespec);
+ if ((ret = pager_output(line)) != 0)
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * Attempt to open the disk described by (dev) for use by (f).
+ */
+static int
+bc_open(struct open_file *f, ...)
+{
+ va_list ap;
+ struct i386_devdesc *dev;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct i386_devdesc *);
+ va_end(ap);
+ if (dev->d_unit >= nbcinfo) {
+ DEBUG("attempt to open nonexistent disk");
+ return(ENXIO);
+ }
+
+ BC(dev).bc_open++;
+ if (BC(dev).bc_bcache == NULL)
+ BC(dev).bc_bcache = bcache_allocate();
+ return(0);
+}
+
+static int
+bc_close(struct open_file *f)
+{
+ struct i386_devdesc *dev;
+
+ dev = (struct i386_devdesc *)f->f_devdata;
+ BC(dev).bc_open--;
+ if (BC(dev).bc_open == 0) {
+ bcache_free(BC(dev).bc_bcache);
+ BC(dev).bc_bcache = NULL;
+ }
+ return(0);
+}
+
+static int
+bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct bcache_devdata bcd;
+ struct i386_devdesc *dev;
+
+ dev = (struct i386_devdesc *)devdata;
+ bcd.dv_strategy = bc_realstrategy;
+ bcd.dv_devdata = devdata;
+ bcd.dv_cache = BC(dev).bc_bcache;
+
+ return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize));
+}
+
+static int
+bc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct i386_devdesc *dev;
+ int unit;
+ int blks;
+#ifdef BD_SUPPORT_FRAGS
+ char fragbuf[BIOSCD_SECSIZE];
+ size_t fragsize;
+
+ fragsize = size % BIOSCD_SECSIZE;
+#else
+ if (size % BIOSCD_SECSIZE)
+ return (EINVAL);
+#endif
+
+ if (rw != F_READ)
+ return(EROFS);
+ dev = (struct i386_devdesc *)devdata;
+ unit = dev->d_unit;
+ blks = size / BIOSCD_SECSIZE;
+ if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
+ return (EINVAL);
+ dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
+ DEBUG("read %d from %lld to %p", blks, dblk, buf);
+
+ if (rsize)
+ *rsize = 0;
+ if (blks && bc_read(unit, dblk, blks, buf)) {
+ DEBUG("read error");
+ return (EIO);
+ }
+#ifdef BD_SUPPORT_FRAGS
+ DEBUG("frag read %d from %lld+%d to %p",
+ fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
+ if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
+ DEBUG("frag read error");
+ return(EIO);
+ }
+ bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
+#endif
+ if (rsize)
+ *rsize = size;
+ return (0);
+}
+
+/* Max number of sectors to bounce-buffer at a time. */
+#define CD_BOUNCEBUF 8
+
+static int
+bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
+{
+ u_int maxfer, resid, result, retry, x;
+ caddr_t bbuf, p, xp;
+ int biosdev;
+#ifdef DISK_DEBUG
+ int error;
+#endif
+
+ /* Just in case some idiot actually tries to read -1 blocks... */
+ if (blks < 0)
+ return (-1);
+
+ /* If nothing to do, just return succcess. */
+ if (blks == 0)
+ return (0);
+
+ /* Decide whether we have to bounce */
+ if (VTOP(dest) >> 20 != 0) {
+ /*
+ * The destination buffer is above first 1MB of
+ * physical memory so we have to arrange a suitable
+ * bounce buffer.
+ */
+ x = min(CD_BOUNCEBUF, (unsigned)blks);
+ bbuf = alloca(x * BIOSCD_SECSIZE);
+ maxfer = x;
+ } else {
+ bbuf = NULL;
+ maxfer = 0;
+ }
+
+ biosdev = bc_unit2bios(unit);
+ resid = blks;
+ p = dest;
+
+ while (resid > 0) {
+ if (bbuf)
+ xp = bbuf;
+ else
+ xp = p;
+ x = resid;
+ if (maxfer > 0)
+ x = min(x, maxfer);
+
+ /*
+ * Loop retrying the operation a couple of times. The BIOS
+ * may also retry.
+ */
+ for (retry = 0; retry < 3; retry++) {
+ /* If retrying, reset the drive */
+ if (retry > 0) {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x1b;
+ v86.eax = 0x0300 | biosdev;
+ v86int();
+ }
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x1b;
+ v86.eax = 0x0600 | (biosdev & 0x7f);
+ v86.ebx = x * BIOSCD_SECSIZE;
+ v86.ecx = dblk & 0xffff;
+ v86.edx = (dblk >> 16) & 0xffff;
+ v86.ebp = VTOPOFF(xp);
+ v86.es = VTOPSEG(xp);
+ v86int();
+ result = V86_CY(v86.efl);
+ if (result == 0)
+ break;
+ }
+
+#ifdef DISK_DEBUG
+ error = (v86.eax >> 8) & 0xff;
+#endif
+ DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
+ VTOP(p), result ? "failed" : "ok");
+ DEBUG("unit %d status 0x%x", unit, error);
+ if (bbuf != NULL)
+ bcopy(bbuf, p, x * BIOSCD_SECSIZE);
+ p += (x * BIOSCD_SECSIZE);
+ dblk += x;
+ resid -= x;
+ }
+
+/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */
+ return(0);
+}
+
+/*
+ * Return a suitable dev_t value for (dev).
+ */
+int
+bc_getdev(struct i386_devdesc *dev)
+{
+ int biosdev, unit, device;
+ int major;
+ int rootdev;
+
+ unit = dev->d_unit;
+ biosdev = bc_unit2bios(unit);
+ DEBUG("unit %d BIOS device %d", unit, biosdev);
+ if (biosdev == -1) /* not a BIOS device */
+ return(-1);
+
+ device = biosdev & 0xf0;
+ if (device == 0x80)
+ major = ACDMAJOR;
+ else if (device == 0xa0)
+ major = CDMAJOR;
+ else
+ return (-1);
+
+ unit = 0; /* XXX */
+
+ /* XXX: Assume partition 'a'. */
+ rootdev = MAKEBOOTDEV(major, 0, unit, 0);
+ DEBUG("dev is 0x%x\n", rootdev);
+ return(rootdev);
+}
diff --git a/stand/pc98/libpc98/biosdisk.c b/stand/pc98/libpc98/biosdisk.c
new file mode 100644
index 0000000..86a550d
--- /dev/null
+++ b/stand/pc98/libpc98/biosdisk.c
@@ -0,0 +1,1120 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * BIOS disk device handling.
+ *
+ * Ideas and algorithms from:
+ *
+ * - NetBSD libi386/biosdisk.c
+ * - FreeBSD biosboot/disk.c
+ *
+ */
+
+#include <stand.h>
+
+#include <sys/disklabel.h>
+#include <sys/diskpc98.h>
+#include <machine/bootinfo.h>
+
+#include <stdarg.h>
+
+#include <bootstrap.h>
+#include <btxv86.h>
+#include "libi386.h"
+
+#define BIOS_NUMDRIVES 0x475
+#define BIOSDISK_SECSIZE 512
+#define BUFSIZE (1 * BIOSDISK_SECSIZE)
+
+#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */
+#define WDMAJOR 0 /* major numbers for devices we frontend for */
+#define WFDMAJOR 1
+#define FDMAJOR 2
+#define DAMAJOR 4
+
+#ifdef DISK_DEBUG
+# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
+#else
+# define DEBUG(fmt, args...)
+#endif
+
+struct open_disk {
+ int od_dkunit; /* disk unit number */
+ int od_unit; /* BIOS unit number */
+ int od_cyl; /* BIOS geometry */
+ int od_hds;
+ int od_sec;
+ int od_boff; /* block offset from beginning of BIOS disk */
+ int od_flags;
+#define BD_MODEINT13 0x0000
+#define BD_MODEEDD1 0x0001
+#define BD_MODEEDD3 0x0002
+#define BD_MODEMASK 0x0003
+#define BD_FLOPPY 0x0004
+#define BD_LABELOK 0x0008
+#define BD_PARTTABOK 0x0010
+#define BD_OPTICAL 0x0020
+ struct disklabel od_disklabel;
+ int od_nslices; /* slice count */
+ struct pc98_partition od_slicetab[PC98_NPARTS];
+};
+
+/*
+ * List of BIOS devices, translation from disk unit number to
+ * BIOS unit number.
+ */
+static struct bdinfo
+{
+ int bd_unit; /* BIOS unit number */
+ int bd_flags;
+ int bd_type; /* BIOS 'drive type' (floppy only) */
+ int bd_da_unit; /* kernel unit number for da */
+ int bd_open; /* reference counter */
+ void *bd_bcache; /* buffer cache data */
+} bdinfo [MAXBDDEV];
+static int nbdinfo = 0;
+
+#define BD(dev) (bdinfo[(dev)->d_unit])
+
+static int bd_getgeom(struct open_disk *od);
+static int bd_read(struct open_disk *od, daddr_t dblk, int blks,
+ caddr_t dest);
+static int bd_write(struct open_disk *od, daddr_t dblk, int blks,
+ caddr_t dest);
+
+static int bd_int13probe(struct bdinfo *bd);
+
+static int bd_printslice(struct open_disk *od, struct pc98_partition *dp,
+ char *prefix, int verbose);
+static int bd_printbsdslice(struct open_disk *od, daddr_t offset,
+ char *prefix, int verbose);
+
+static int bd_init(void);
+static int bd_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int bd_realstrategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int bd_open(struct open_file *f, ...);
+static int bd_close(struct open_file *f);
+static int bd_print(int verbose);
+
+struct devsw biosdisk = {
+ "disk",
+ DEVT_DISK,
+ bd_init,
+ bd_strategy,
+ bd_open,
+ bd_close,
+ noioctl,
+ bd_print,
+ NULL
+};
+
+static int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev);
+static void bd_closedisk(struct open_disk *od);
+static int bd_open_pc98(struct open_disk *od, struct i386_devdesc *dev);
+static int bd_bestslice(struct open_disk *od);
+static void bd_checkextended(struct open_disk *od, int slicenum);
+
+/*
+ * Translate between BIOS device numbers and our private unit numbers.
+ */
+int
+bd_bios2unit(int biosdev)
+{
+ int i;
+
+ DEBUG("looking for bios device 0x%x", biosdev);
+ for (i = 0; i < nbdinfo; i++) {
+ DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
+ if (bdinfo[i].bd_unit == biosdev)
+ return(i);
+ }
+ return(-1);
+}
+
+int
+bd_unit2bios(int unit)
+{
+ if ((unit >= 0) && (unit < nbdinfo))
+ return(bdinfo[unit].bd_unit);
+ return(-1);
+}
+
+/*
+ * Quiz the BIOS for disk devices, save a little info about them.
+ */
+static int
+bd_init(void)
+{
+ int base, unit;
+ int da_drive=0, n=-0x10;
+
+ /* sequence 0x90, 0x80, 0xa0 */
+ for (base = 0x90; base <= 0xa0; base += n, n += 0x30) {
+ for (unit = base; (nbdinfo < MAXBDDEV) || ((unit & 0x0f) < 4); unit++) {
+ bdinfo[nbdinfo].bd_open = 0;
+ bdinfo[nbdinfo].bd_bcache = NULL;
+ bdinfo[nbdinfo].bd_unit = unit;
+ bdinfo[nbdinfo].bd_flags = (unit & 0xf0) == 0x90 ? BD_FLOPPY : 0;
+
+ if (!bd_int13probe(&bdinfo[nbdinfo])){
+ if (((unit & 0xf0) == 0x90 && (unit & 0x0f) < 4) ||
+ ((unit & 0xf0) == 0xa0 && (unit & 0x0f) < 6))
+ continue; /* Target IDs are not contiguous. */
+ else
+ break;
+ }
+
+ if (bdinfo[nbdinfo].bd_flags & BD_FLOPPY){
+ /* available 1.44MB access? */
+ if (*(u_char *)PTOV(0xA15AE) & (1<<(unit & 0xf))) {
+ /* boot media 1.2MB FD? */
+ if ((*(u_char *)PTOV(0xA1584) & 0xf0) != 0x90)
+ bdinfo[nbdinfo].bd_unit = 0x30 + (unit & 0xf);
+ }
+ }
+ else {
+ if ((unit & 0xF0) == 0xA0) /* SCSI HD or MO */
+ bdinfo[nbdinfo].bd_da_unit = da_drive++;
+ }
+ /* XXX we need "disk aliases" to make this simpler */
+ printf("BIOS drive %c: is disk%d\n",
+ 'A' + nbdinfo, nbdinfo);
+ nbdinfo++;
+ }
+ }
+ bcache_add_dev(nbdinfo);
+ return(0);
+}
+
+/*
+ * Try to detect a device supported by the legacy int13 BIOS
+ */
+static int
+bd_int13probe(struct bdinfo *bd)
+{
+ int addr;
+
+ if (bd->bd_flags & BD_FLOPPY) {
+ addr = 0xa155c;
+ } else {
+ if ((bd->bd_unit & 0xf0) == 0x80)
+ addr = 0xa155d;
+ else
+ addr = 0xa1482;
+ }
+ if ( *(u_char *)PTOV(addr) & (1<<(bd->bd_unit & 0x0f))) {
+ bd->bd_flags |= BD_MODEINT13;
+ return(1);
+ }
+ if ((bd->bd_unit & 0xF0) == 0xA0) {
+ int media = ((unsigned *)PTOV(0xA1460))[bd->bd_unit & 0x0F] & 0x1F;
+
+ if (media == 7) { /* MO */
+ bd->bd_flags |= BD_MODEINT13 | BD_OPTICAL;
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/*
+ * Print information about disks
+ */
+static int
+bd_print(int verbose)
+{
+ int i, j, ret = 0;
+ char line[80];
+ struct i386_devdesc dev;
+ struct open_disk *od;
+ struct pc98_partition *dptr;
+
+ if (nbdinfo == 0)
+ return (0);
+
+ printf("%s devices:", biosdisk.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ for (i = 0; i < nbdinfo; i++) {
+ snprintf(line, sizeof(line), " disk%d: BIOS drive %c:\n",
+ i, 'A' + i);
+ if ((ret = pager_output(line)) != 0)
+ break;
+
+ /* try to open the whole disk */
+ dev.d_unit = i;
+ dev.d_kind.biosdisk.slice = -1;
+ dev.d_kind.biosdisk.partition = -1;
+
+ if (!bd_opendisk(&od, &dev)) {
+
+ /* Do we have a partition table? */
+ if (od->od_flags & BD_PARTTABOK) {
+ dptr = &od->od_slicetab[0];
+
+ /* Check for a "dedicated" disk */
+ for (j = 0; j < od->od_nslices; j++) {
+ snprintf(line, sizeof(line), " disk%ds%d", i, j + 1);
+ if ((ret = bd_printslice(od, &dptr[j], line, verbose)) != 0)
+ break;
+ }
+ }
+ bd_closedisk(od);
+ if (ret != 0)
+ break;
+ }
+ }
+ return (ret);
+}
+
+/* Given a size in 512 byte sectors, convert it to a human-readable number. */
+static char *
+display_size(uint64_t size)
+{
+ static char buf[80];
+ char unit;
+
+ size /= 2;
+ unit = 'K';
+ if (size >= 10485760000LL) {
+ size /= 1073741824;
+ unit = 'T';
+ } else if (size >= 10240000) {
+ size /= 1048576;
+ unit = 'G';
+ } else if (size >= 10000) {
+ size /= 1024;
+ unit = 'M';
+ }
+ sprintf(buf, "%6ld%cB", (long)size, unit);
+ return (buf);
+}
+
+/*
+ * Print information about slices on a disk. For the size calculations we
+ * assume a 512 byte sector.
+ */
+static int
+bd_printslice(struct open_disk *od, struct pc98_partition *dp, char *prefix,
+ int verbose)
+{
+ int cylsecs, start, size;
+ char stats[80];
+ char line[80];
+
+ cylsecs = od->od_hds * od->od_sec;
+ start = dp->dp_scyl * cylsecs + dp->dp_shd * od->od_sec + dp->dp_ssect;
+ size = (dp->dp_ecyl - dp->dp_scyl + 1) * cylsecs;
+
+ if (verbose)
+ sprintf(stats, " %s (%d - %d)", display_size(size),
+ start, start + size);
+ else
+ stats[0] = '\0';
+
+ switch(dp->dp_mid & PC98_MID_MASK) {
+ case PC98_MID_386BSD:
+ return (bd_printbsdslice(od, start, prefix, verbose));
+ case 0x00: /* unused partition */
+ return (0);
+ case 0x01:
+ sprintf(line, "%s: FAT-12%s\n", prefix, stats);
+ break;
+ case 0x11:
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ sprintf(line, "%s: FAT-16%s\n", prefix, stats);
+ break;
+ default:
+ sprintf(line, "%s: Unknown fs: 0x%x %s\n", prefix, dp->dp_mid,
+ stats);
+ }
+ return (pager_output(line));
+}
+
+/*
+ * Print out each valid partition in the disklabel of a FreeBSD slice.
+ * For size calculations, we assume a 512 byte sector size.
+ */
+static int
+bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix,
+ int verbose)
+{
+ char line[80];
+ char buf[BIOSDISK_SECSIZE];
+ struct disklabel *lp;
+ int i;
+
+ /* read disklabel */
+ if (bd_read(od, offset + LABELSECTOR, 1, buf))
+ return (0);
+ lp =(struct disklabel *)(&buf[0]);
+ if (lp->d_magic != DISKMAGIC) {
+ sprintf(line, "%s: FFS bad disklabel\n", prefix);
+ return (pager_output(line));
+ }
+
+ /* Print partitions */
+ for (i = 0; i < lp->d_npartitions; i++) {
+ /*
+ * For each partition, make sure we know what type of fs it is. If
+ * not, then skip it. However, since floppies often have bogus
+ * fstypes, print the 'a' partition on a floppy even if it is marked
+ * unused.
+ */
+ if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) ||
+ (lp->d_partitions[i].p_fstype == FS_SWAP) ||
+ (lp->d_partitions[i].p_fstype == FS_VINUM) ||
+ ((lp->d_partitions[i].p_fstype == FS_UNUSED) &&
+ (od->od_flags & BD_FLOPPY) && (i == 0))) {
+
+ /* Only print out statistics in verbose mode */
+ if (verbose)
+ sprintf(line, " %s%c: %s %s (%d - %d)\n", prefix, 'a' + i,
+ (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap " :
+ (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" :
+ "FFS ",
+ display_size(lp->d_partitions[i].p_size),
+ lp->d_partitions[i].p_offset,
+ lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size);
+ else
+ sprintf(line, " %s%c: %s\n", prefix, 'a' + i,
+ (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" :
+ (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" :
+ "FFS");
+ if (pager_output(line))
+ return (1);
+ }
+ }
+ return (0);
+}
+
+
+/*
+ * Attempt to open the disk described by (dev) for use by (f).
+ *
+ * Note that the philosophy here is "give them exactly what
+ * they ask for". This is necessary because being too "smart"
+ * about what the user might want leads to complications.
+ * (eg. given no slice or partition value, with a disk that is
+ * sliced - are they after the first BSD slice, or the DOS
+ * slice before it?)
+ */
+static int
+bd_open(struct open_file *f, ...)
+{
+ va_list ap;
+ struct i386_devdesc *dev;
+ struct open_disk *od;
+ int error;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct i386_devdesc *);
+ va_end(ap);
+ if ((error = bd_opendisk(&od, dev)))
+ return(error);
+
+ BD(dev).bd_open++;
+ if (BD(dev).bd_bcache == NULL)
+ BD(dev).bd_bcache = bcache_allocate();
+
+ /*
+ * Save our context
+ */
+ ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od;
+ DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff);
+ return(0);
+}
+
+static int
+bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
+{
+ struct open_disk *od;
+ int error;
+
+ if (dev->d_unit >= nbdinfo) {
+ DEBUG("attempt to open nonexistent disk");
+ return(ENXIO);
+ }
+
+ od = (struct open_disk *)malloc(sizeof(struct open_disk));
+ if (!od) {
+ DEBUG("no memory");
+ return (ENOMEM);
+ }
+
+ /* Look up BIOS unit number, intialise open_disk structure */
+ od->od_dkunit = dev->d_unit;
+ od->od_unit = bdinfo[od->od_dkunit].bd_unit;
+ od->od_flags = bdinfo[od->od_dkunit].bd_flags;
+ od->od_boff = 0;
+ error = 0;
+ DEBUG("open '%s', unit 0x%x slice %d partition %d",
+ i386_fmtdev(dev), dev->d_unit,
+ dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition);
+
+ /* Get geometry for this open (removable device may have changed) */
+ if (bd_getgeom(od)) {
+ DEBUG("can't get geometry");
+ error = ENXIO;
+ goto out;
+ }
+
+ /* Determine disk layout. */
+ error = bd_open_pc98(od, dev);
+
+ out:
+ if (error) {
+ free(od);
+ } else {
+ *odp = od; /* return the open disk */
+ }
+ return(error);
+}
+
+static int
+bd_open_pc98(struct open_disk *od, struct i386_devdesc *dev)
+{
+ struct pc98_partition *dptr;
+ struct disklabel *lp;
+ int sector, slice, i;
+ char buf[BUFSIZE];
+
+ /*
+ * Following calculations attempt to determine the correct value
+ * for d->od_boff by looking for the slice and partition specified,
+ * or searching for reasonable defaults.
+ */
+
+ /*
+ * Find the slice in the DOS slice table.
+ */
+ od->od_nslices = 0;
+ if (od->od_flags & BD_FLOPPY) {
+ sector = 0;
+ goto unsliced;
+ }
+ if (bd_read(od, 0, 1, buf)) {
+ DEBUG("error reading MBR");
+ return (EIO);
+ }
+
+ /*
+ * Check the slice table magic.
+ */
+ if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) {
+ /* If a slice number was explicitly supplied, this is an error */
+ if (dev->d_kind.biosdisk.slice > 0) {
+ DEBUG("no slice table/MBR (no magic)");
+ return (ENOENT);
+ }
+ sector = 0;
+ goto unsliced; /* may be a floppy */
+ }
+ if (bd_read(od, 1, 1, buf)) {
+ DEBUG("error reading MBR");
+ return (EIO);
+ }
+
+ /*
+ * copy the partition table, then pick up any extended partitions.
+ */
+ bcopy(buf + PC98_PARTOFF, &od->od_slicetab,
+ sizeof(struct pc98_partition) * PC98_NPARTS);
+ od->od_nslices = PC98_NPARTS; /* extended slices start here */
+ od->od_flags |= BD_PARTTABOK;
+ dptr = &od->od_slicetab[0];
+
+ /* Is this a request for the whole disk? */
+ if (dev->d_kind.biosdisk.slice == -1) {
+ sector = 0;
+ goto unsliced;
+ }
+
+ /*
+ * if a slice number was supplied but not found, this is an error.
+ */
+ if (dev->d_kind.biosdisk.slice > 0) {
+ slice = dev->d_kind.biosdisk.slice - 1;
+ if (slice >= od->od_nslices) {
+ DEBUG("slice %d not found", slice);
+ return (ENOENT);
+ }
+ }
+
+ /* Try to auto-detect the best slice; this should always give a slice number */
+ if (dev->d_kind.biosdisk.slice == 0) {
+ slice = bd_bestslice(od);
+ if (slice == -1) {
+ return (ENOENT);
+ }
+ dev->d_kind.biosdisk.slice = slice;
+ }
+
+ dptr = &od->od_slicetab[0];
+ /*
+ * Accept the supplied slice number unequivocally (we may be looking
+ * at a DOS partition).
+ */
+ dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */
+ sector = dptr->dp_scyl * od->od_hds * od->od_sec +
+ dptr->dp_shd * od->od_sec + dptr->dp_ssect;
+ {
+ int end = dptr->dp_ecyl * od->od_hds * od->od_sec +
+ dptr->dp_ehd * od->od_sec + dptr->dp_esect;
+ DEBUG("slice entry %d at %d, %d sectors",
+ dev->d_kind.biosdisk.slice - 1, sector, end-sector);
+ }
+
+ /*
+ * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition
+ */
+ if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0))
+ dev->d_kind.biosdisk.partition = 0;
+
+ unsliced:
+ /*
+ * Now we have the slice offset, look for the partition in the disklabel if we have
+ * a partition to start with.
+ *
+ * XXX we might want to check the label checksum.
+ */
+ if (dev->d_kind.biosdisk.partition < 0) {
+ od->od_boff = sector; /* no partition, must be after the slice */
+ DEBUG("opening raw slice");
+ } else {
+
+ if (bd_read(od, sector + LABELSECTOR, 1, buf)) {
+ DEBUG("error reading disklabel");
+ return (EIO);
+ }
+ DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel);
+ bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel));
+ lp = &od->od_disklabel;
+ od->od_flags |= BD_LABELOK;
+
+ if (lp->d_magic != DISKMAGIC) {
+ DEBUG("no disklabel");
+ return (ENOENT);
+ }
+ if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
+ DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
+ 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
+ return (EPART);
+ }
+
+#ifdef DISK_DEBUG
+ /* Complain if the partition is unused unless this is a floppy. */
+ if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) &&
+ !(od->od_flags & BD_FLOPPY))
+ DEBUG("warning, partition marked as unused");
+#endif
+
+ od->od_boff =
+ lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset -
+ lp->d_partitions[RAW_PART].p_offset +
+ sector;
+ }
+ return (0);
+}
+
+/*
+ * Search for a slice with the following preferences:
+ *
+ * 1: Active FreeBSD slice
+ * 2: Non-active FreeBSD slice
+ * 3: Active Linux slice
+ * 4: non-active Linux slice
+ * 5: Active FAT/FAT32 slice
+ * 6: non-active FAT/FAT32 slice
+ */
+#define PREF_RAWDISK 0
+#define PREF_FBSD_ACT 1
+#define PREF_FBSD 2
+#define PREF_LINUX_ACT 3
+#define PREF_LINUX 4
+#define PREF_DOS_ACT 5
+#define PREF_DOS 6
+#define PREF_NONE 7
+
+/*
+ * slicelimit is in the range 0 .. PC98_NPARTS
+ */
+static int
+bd_bestslice(struct open_disk *od)
+{
+ struct pc98_partition *dp;
+ int pref, preflevel;
+ int i, prefslice;
+
+ prefslice = 0;
+ preflevel = PREF_NONE;
+
+ dp = &od->od_slicetab[0];
+ for (i = 0; i < od->od_nslices; i++, dp++) {
+ switch(dp->dp_mid & PC98_MID_MASK) {
+ case PC98_MID_386BSD: /* FreeBSD */
+ if ((dp->dp_mid & PC98_MID_BOOTABLE) &&
+ (preflevel > PREF_FBSD_ACT)) {
+ pref = i;
+ preflevel = PREF_FBSD_ACT;
+ } else if (preflevel > PREF_FBSD) {
+ pref = i;
+ preflevel = PREF_FBSD;
+ }
+ break;
+
+ case 0x11: /* DOS/Windows */
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x63:
+ if ((dp->dp_mid & PC98_MID_BOOTABLE) &&
+ (preflevel > PREF_DOS_ACT)) {
+ pref = i;
+ preflevel = PREF_DOS_ACT;
+ } else if (preflevel > PREF_DOS) {
+ pref = i;
+ preflevel = PREF_DOS;
+ }
+ break;
+ }
+ }
+ return (prefslice);
+}
+
+static int
+bd_close(struct open_file *f)
+{
+ struct i386_devdesc *dev = f->f_devdata;
+ struct open_disk *od = (struct open_disk *)(dev->d_kind.biosdisk.data);
+
+ BD(dev).bd_open--;
+ if (BD(dev).bd_open == 0) {
+ bcache_free(BD(dev).bd_bcache);
+ BD(dev).bd_bcache = NULL;
+ }
+
+ bd_closedisk(od);
+ return(0);
+}
+
+static void
+bd_closedisk(struct open_disk *od)
+{
+ DEBUG("open_disk %p", od);
+#if 0
+ /* XXX is this required? (especially if disk already open...) */
+ if (od->od_flags & BD_FLOPPY)
+ delay(3000000);
+#endif
+ free(od);
+}
+
+static int
+bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct bcache_devdata bcd;
+ struct i386_devdesc *dev = devdata;
+ struct open_disk *od = (struct open_disk *)(dev->d_kind.biosdisk.data);
+
+ bcd.dv_strategy = bd_realstrategy;
+ bcd.dv_devdata = devdata;
+ bcd.dv_cache = BD(dev).bd_bcache;
+ return(bcache_strategy(&bcd, rw, dblk+od->od_boff, size, buf, rsize));
+}
+
+static int
+bd_realstrategy(void *devdata, int rw, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize)
+{
+ struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
+ int blks;
+#ifdef BD_SUPPORT_FRAGS
+ char fragbuf[BIOSDISK_SECSIZE];
+ size_t fragsize;
+
+ fragsize = size % BIOSDISK_SECSIZE;
+#else
+ if (size % BIOSDISK_SECSIZE)
+ panic("bd_strategy: %d bytes I/O not multiple of block size", size);
+#endif
+
+ DEBUG("open_disk %p", od);
+ blks = size / BIOSDISK_SECSIZE;
+ if (rsize)
+ *rsize = 0;
+
+ switch(rw){
+ case F_READ:
+ DEBUG("read %d from %d to %p", blks, dblk, buf);
+
+ if (blks && bd_read(od, dblk, blks, buf)) {
+ DEBUG("read error");
+ return (EIO);
+ }
+#ifdef BD_SUPPORT_FRAGS
+ DEBUG("bd_strategy: frag read %d from %d+%d to %p",
+ fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
+ if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
+ DEBUG("frag read error");
+ return(EIO);
+ }
+ bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
+#endif
+ break;
+ case F_WRITE :
+ DEBUG("write %d from %d to %p", blks, dblk, buf);
+
+ if (blks && bd_write(od, dblk, blks, buf)) {
+ DEBUG("write error");
+ return (EIO);
+ }
+#ifdef BD_SUPPORT_FRAGS
+ if(fragsize) {
+ DEBUG("Attempted to write a frag");
+ return (EIO);
+ }
+#endif
+ break;
+ default:
+ /* DO NOTHING */
+ return (EROFS);
+ }
+
+ if (rsize)
+ *rsize = size;
+ return (0);
+}
+
+/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
+#define FLOPPY_BOUNCEBUF 18
+
+static int
+bd_chs_io(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest, int write)
+{
+ u_int x, bpc, cyl, hd, sec;
+
+ bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */
+ x = dblk;
+ cyl = x / bpc; /* block # / blocks per cylinder */
+ x %= bpc; /* block offset into cylinder */
+ hd = x / od->od_sec; /* offset / blocks per track */
+ sec = x % od->od_sec; /* offset into track */
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x1b;
+ if (write)
+ v86.eax = 0x0500 | od->od_unit;
+ else
+ v86.eax = 0x0600 | od->od_unit;
+ if (od->od_flags & BD_FLOPPY) {
+ v86.eax |= 0xd000;
+ v86.ecx = 0x0200 | (cyl & 0xff);
+ v86.edx = (hd << 8) | (sec + 1);
+ } else if (od->od_flags & BD_OPTICAL) {
+ v86.eax &= 0xFF7F;
+ v86.ecx = dblk & 0xFFFF;
+ v86.edx = dblk >> 16;
+ } else {
+ v86.ecx = cyl;
+ v86.edx = (hd << 8) | sec;
+ }
+ v86.ebx = blks * BIOSDISK_SECSIZE;
+ v86.es = VTOPSEG(dest);
+ v86.ebp = VTOPOFF(dest);
+ v86int();
+ return (V86_CY(v86.efl));
+}
+
+static int
+bd_io(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest, int write)
+{
+ u_int x, sec, result, resid, retry, maxfer;
+ caddr_t p, xp, bbuf, breg;
+
+ /* Just in case some idiot actually tries to read/write -1 blocks... */
+ if (blks < 0)
+ return (-1);
+
+ resid = blks;
+ p = dest;
+
+ /* Decide whether we have to bounce */
+ if (VTOP(dest) >> 20 != 0 ||
+ ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) {
+
+ /*
+ * There is a 64k physical boundary somewhere in the
+ * destination buffer, or the destination buffer is above
+ * first 1MB of physical memory so we have to arrange a
+ * suitable bounce buffer. Allocate a buffer twice as large
+ * as we need to. Use the bottom half unless there is a break
+ * there, in which case we use the top half.
+ */
+ x = min(od->od_sec, (unsigned)blks);
+ bbuf = alloca(x * 2 * BIOSDISK_SECSIZE);
+ if (((u_int32_t)VTOP(bbuf) & 0xffff0000) ==
+ ((u_int32_t)VTOP(bbuf + x * BIOSDISK_SECSIZE) & 0xffff0000)) {
+ breg = bbuf;
+ } else {
+ breg = bbuf + x * BIOSDISK_SECSIZE;
+ }
+ maxfer = x; /* limit transfers to bounce region size */
+ } else {
+ breg = bbuf = NULL;
+ maxfer = 0;
+ }
+
+ while (resid > 0) {
+ /*
+ * Play it safe and don't cross track boundaries.
+ * (XXX this is probably unnecessary)
+ */
+ sec = dblk % od->od_sec; /* offset into track */
+ x = min(od->od_sec - sec, resid);
+ if (maxfer > 0)
+ x = min(x, maxfer); /* fit bounce buffer */
+
+ /* where do we transfer to? */
+ xp = bbuf == NULL ? p : breg;
+
+ /*
+ * Put your Data In, Put your Data out,
+ * Put your Data In, and shake it all about
+ */
+ if (write && bbuf != NULL)
+ bcopy(p, breg, x * BIOSDISK_SECSIZE);
+
+ /*
+ * Loop retrying the operation a couple of times. The BIOS
+ * may also retry.
+ */
+ for (retry = 0; retry < 3; retry++) {
+ /* if retrying, reset the drive */
+ if (retry > 0) {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x1b;
+ v86.eax = 0x0300 | od->od_unit;
+ v86int();
+ }
+
+ result = bd_chs_io(od, dblk, x, xp, write);
+ if (result == 0)
+ break;
+ }
+
+ if (write)
+ DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
+ p, VTOP(p), dblk, result ? "failed" : "ok");
+ else
+ DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
+ dblk, p, VTOP(p), result ? "failed" : "ok");
+ if (result) {
+ return(-1);
+ }
+ if (!write && bbuf != NULL)
+ bcopy(breg, p, x * BIOSDISK_SECSIZE);
+ p += (x * BIOSDISK_SECSIZE);
+ dblk += x;
+ resid -= x;
+ }
+
+/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */
+ return(0);
+}
+
+static int
+bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
+{
+
+ return (bd_io(od, dblk, blks, dest, 0));
+}
+
+static int
+bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
+{
+
+ return (bd_io(od, dblk, blks, dest, 1));
+}
+
+static int
+bd_getgeom(struct open_disk *od)
+{
+
+ if (od->od_flags & BD_FLOPPY) {
+ od->od_cyl = 79;
+ od->od_hds = 2;
+ od->od_sec = (od->od_unit & 0xf0) == 0x30 ? 18 : 15;
+ } else if (od->od_flags & BD_OPTICAL) {
+ od->od_cyl = 0xFFFE;
+ od->od_hds = 8;
+ od->od_sec = 32;
+ } else {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x1b;
+ v86.eax = 0x8400 | od->od_unit;
+ v86int();
+
+ od->od_cyl = v86.ecx;
+ od->od_hds = (v86.edx >> 8) & 0xff;
+ od->od_sec = v86.edx & 0xff;
+ if (V86_CY(v86.efl))
+ return(1);
+ }
+
+ DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
+ return(0);
+}
+
+/*
+ * Return the BIOS geometry of a given "fixed drive" in a format
+ * suitable for the legacy bootinfo structure. Since the kernel is
+ * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
+ * prefer to get the information directly, rather than rely on being
+ * able to put it together from information already maintained for
+ * different purposes and for a probably different number of drives.
+ *
+ * For valid drives, the geometry is expected in the format (31..0)
+ * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
+ * indicated by returning the geometry of a "1.2M" PC-format floppy
+ * disk. And, incidentally, what is returned is not the geometry as
+ * such but the highest valid cylinder, head, and sector numbers.
+ */
+u_int32_t
+bd_getbigeom(int bunit)
+{
+ int hds = 0;
+ int unit = 0x80; /* IDE HDD */
+ u_int addr = 0xA155d;
+
+ while (unit < 0xa7) {
+ if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f)))
+ if (hds++ == bunit)
+ break;
+
+ if (unit >= 0xA0) {
+ int media = ((unsigned *)PTOV(0xA1460))[unit & 0x0F] & 0x1F;
+
+ if (media == 7 && hds++ == bunit) /* SCSI MO */
+ return(0xFFFE0820); /* C:65535 H:8 S:32 */
+ }
+ if (++unit == 0x84) {
+ unit = 0xA0; /* SCSI HDD */
+ addr = 0xA1482;
+ }
+ }
+ if (unit == 0xa7)
+ return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x1b;
+ v86.eax = 0x8400 | unit;
+ v86int();
+ if (V86_CY(v86.efl))
+ return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */
+ return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff);
+}
+
+/*
+ * Return a suitable dev_t value for (dev).
+ *
+ * In the case where it looks like (dev) is a SCSI disk, we allow the number of
+ * IDE disks to be specified in $num_ide_disks. There should be a Better Way.
+ */
+int
+bd_getdev(struct i386_devdesc *dev)
+{
+ struct open_disk *od;
+ int biosdev;
+ int major;
+ int rootdev;
+ char *nip, *cp;
+ int unitofs = 0, i, unit;
+
+ biosdev = bd_unit2bios(dev->d_unit);
+ DEBUG("unit %d BIOS device %d", dev->d_unit, biosdev);
+ if (biosdev == -1) /* not a BIOS device */
+ return(-1);
+ if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */
+ return(-1);
+
+ if ((biosdev & 0xf0) == 0x90 || (biosdev & 0xf0) == 0x30) {
+ /* floppy (or emulated floppy) or ATAPI device */
+ if (bdinfo[dev->d_unit].bd_type == DT_ATAPI) {
+ /* is an ATAPI disk */
+ major = WFDMAJOR;
+ } else {
+ /* is a floppy disk */
+ major = FDMAJOR;
+ }
+ } else {
+ /* harddisk */
+ if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) {
+ /* label OK, disk labelled as SCSI */
+ major = DAMAJOR;
+ /* check for unit number correction hint, now deprecated */
+ if ((nip = getenv("num_ide_disks")) != NULL) {
+ i = strtol(nip, &cp, 0);
+ /* check for parse error */
+ if ((cp != nip) && (*cp == 0))
+ unitofs = i;
+ }
+ } else {
+ /* assume an IDE disk */
+ major = WDMAJOR;
+ }
+ }
+ /* default root disk unit number */
+ if ((biosdev & 0xf0) == 0xa0)
+ unit = bdinfo[dev->d_unit].bd_da_unit;
+ else
+ unit = biosdev & 0xf;
+
+ /* XXX a better kludge to set the root disk unit number */
+ if ((nip = getenv("root_disk_unit")) != NULL) {
+ i = strtol(nip, &cp, 0);
+ /* check for parse error */
+ if ((cp != nip) && (*cp == 0))
+ unit = i;
+ }
+
+ rootdev = MAKEBOOTDEV(major, dev->d_kind.biosdisk.slice + 1, unit,
+ dev->d_kind.biosdisk.partition);
+ DEBUG("dev is 0x%x\n", rootdev);
+ return(rootdev);
+}
diff --git a/stand/pc98/libpc98/biosmem.c b/stand/pc98/libpc98/biosmem.c
new file mode 100644
index 0000000..c5a9b30
--- /dev/null
+++ b/stand/pc98/libpc98/biosmem.c
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Obtain memory configuration information from the BIOS
+ */
+#include <stand.h>
+#include "libi386.h"
+#include "btxv86.h"
+
+vm_offset_t memtop, memtop_copyin, high_heap_base;
+uint32_t bios_basemem, bios_extmem, high_heap_size;
+
+/*
+ * The minimum amount of memory to reserve in bios_extmem for the heap.
+ */
+#define HEAP_MIN (64 * 1024 * 1024)
+
+void
+bios_getmem(void)
+{
+
+ bios_basemem = ((*(u_char *)PTOV(0xA1501) & 0x07) + 1) * 128 * 1024;
+ bios_extmem = *(u_char *)PTOV(0xA1401) * 128 * 1024 +
+ *(u_int16_t *)PTOV(0xA1594) * 1024 * 1024;
+
+ /* Set memtop to actual top of memory */
+ memtop = memtop_copyin = 0x100000 + bios_extmem;
+
+ /*
+ * If we have extended memory, use the last 3MB of 'extended' memory
+ * as a high heap candidate.
+ */
+ if (bios_extmem >= HEAP_MIN) {
+ high_heap_size = HEAP_MIN;
+ high_heap_base = memtop - HEAP_MIN;
+ }
+}
diff --git a/stand/pc98/libpc98/biossmap.c b/stand/pc98/libpc98/biossmap.c
new file mode 100644
index 0000000..5a7a89f
--- /dev/null
+++ b/stand/pc98/libpc98/biossmap.c
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2006 TAKAHASHI Yoshihiro <nyan@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include "libi386.h"
+
+void
+bios_addsmapdata(struct preloaded_file *kfp)
+{
+
+}
diff --git a/stand/pc98/libpc98/comconsole.c b/stand/pc98/libpc98/comconsole.c
new file mode 100644
index 0000000..1bf2d6a
--- /dev/null
+++ b/stand/pc98/libpc98/comconsole.c
@@ -0,0 +1,367 @@
+/*-
+ * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include <machine/cpufunc.h>
+#include <dev/ic/ns16550.h>
+#include <dev/pci/pcireg.h>
+#include "libi386.h"
+
+#define COMC_FMT 0x3 /* 8N1 */
+#define COMC_TXWAIT 0x40000 /* transmit timeout */
+#define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */
+#define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */
+
+#ifndef COMPORT
+#define COMPORT 0x238
+#endif
+#ifndef COMSPEED
+#define COMSPEED 9600
+#endif
+
+static void comc_probe(struct console *cp);
+static int comc_init(int arg);
+static void comc_putchar(int c);
+static int comc_getchar(void);
+static int comc_getspeed(void);
+static int comc_ischar(void);
+static int comc_parseint(const char *string);
+static uint32_t comc_parse_pcidev(const char *string);
+static int comc_pcidev_set(struct env_var *ev, int flags,
+ const void *value);
+static int comc_pcidev_handle(uint32_t locator);
+static int comc_port_set(struct env_var *ev, int flags,
+ const void *value);
+static void comc_setup(int speed, int port);
+static int comc_speed_set(struct env_var *ev, int flags,
+ const void *value);
+
+static int comc_curspeed;
+static int comc_port = COMPORT;
+static uint32_t comc_locator;
+
+struct console comconsole = {
+ "comconsole",
+ "serial port",
+ 0,
+ comc_probe,
+ comc_init,
+ comc_putchar,
+ comc_getchar,
+ comc_ischar
+};
+
+static void
+comc_probe(struct console *cp)
+{
+ char intbuf[16];
+ char *cons, *env;
+ int speed, port;
+ uint32_t locator;
+
+ if (comc_curspeed == 0) {
+ comc_curspeed = COMSPEED;
+ /*
+ * Assume that the speed was set by an earlier boot loader if
+ * comconsole is already the preferred console.
+ */
+ cons = getenv("console");
+ if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) ||
+ getenv("boot_multicons") != NULL) {
+ comc_curspeed = comc_getspeed();
+ }
+
+ env = getenv("comconsole_speed");
+ if (env != NULL) {
+ speed = comc_parseint(env);
+ if (speed > 0)
+ comc_curspeed = speed;
+ }
+
+ sprintf(intbuf, "%d", comc_curspeed);
+ unsetenv("comconsole_speed");
+ env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set,
+ env_nounset);
+
+ env = getenv("comconsole_port");
+ if (env != NULL) {
+ port = comc_parseint(env);
+ if (port > 0)
+ comc_port = port;
+ }
+
+ sprintf(intbuf, "%d", comc_port);
+ unsetenv("comconsole_port");
+ env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set,
+ env_nounset);
+
+ env = getenv("comconsole_pcidev");
+ if (env != NULL) {
+ locator = comc_parse_pcidev(env);
+ if (locator != 0)
+ comc_pcidev_handle(locator);
+ }
+
+ unsetenv("comconsole_pcidev");
+ env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set,
+ env_nounset);
+ }
+ comc_setup(comc_curspeed, comc_port);
+}
+
+static int
+comc_init(int arg)
+{
+
+ comc_setup(comc_curspeed, comc_port);
+
+ if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) ==
+ (C_PRESENTIN | C_PRESENTOUT))
+ return (CMD_OK);
+ return (CMD_ERROR);
+}
+
+static void
+comc_putchar(int c)
+{
+ int wait;
+
+ for (wait = COMC_TXWAIT; wait > 0; wait--)
+ if (inb(comc_port + com_lsr) & LSR_TXRDY) {
+ outb(comc_port + com_data, (u_char)c);
+ break;
+ }
+}
+
+static int
+comc_getchar(void)
+{
+ return (comc_ischar() ? inb(comc_port + com_data) : -1);
+}
+
+static int
+comc_ischar(void)
+{
+ return (inb(comc_port + com_lsr) & LSR_RXRDY);
+}
+
+static int
+comc_speed_set(struct env_var *ev, int flags, const void *value)
+{
+ int speed;
+
+ if (value == NULL || (speed = comc_parseint(value)) <= 0) {
+ printf("Invalid speed\n");
+ return (CMD_ERROR);
+ }
+
+ if (comc_curspeed != speed)
+ comc_setup(speed, comc_port);
+
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+
+ return (CMD_OK);
+}
+
+static int
+comc_port_set(struct env_var *ev, int flags, const void *value)
+{
+ int port;
+
+ if (value == NULL || (port = comc_parseint(value)) <= 0) {
+ printf("Invalid port\n");
+ return (CMD_ERROR);
+ }
+
+ if (comc_port != port)
+ comc_setup(comc_curspeed, port);
+
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+
+ return (CMD_OK);
+}
+
+/*
+ * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10.
+ * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0]
+ */
+static uint32_t
+comc_parse_pcidev(const char *string)
+{
+ char *p, *p1;
+ uint8_t bus, dev, func, bar;
+ uint32_t locator;
+ int pres;
+
+ pres = strtol(string, &p, 0);
+ if (p == string || *p != ':' || pres < 0 )
+ return (0);
+ bus = pres;
+ p1 = ++p;
+
+ pres = strtol(p1, &p, 0);
+ if (p == string || *p != ':' || pres < 0 )
+ return (0);
+ dev = pres;
+ p1 = ++p;
+
+ pres = strtol(p1, &p, 0);
+ if (p == string || (*p != ':' && *p != '\0') || pres < 0 )
+ return (0);
+ func = pres;
+
+ if (*p == ':') {
+ p1 = ++p;
+ pres = strtol(p1, &p, 0);
+ if (p == string || *p != '\0' || pres <= 0 )
+ return (0);
+ bar = pres;
+ } else
+ bar = 0x10;
+
+ locator = (bar << 16) | biospci_locator(bus, dev, func);
+ return (locator);
+}
+
+static int
+comc_pcidev_handle(uint32_t locator)
+{
+ char intbuf[64];
+ uint32_t port;
+
+ if (biospci_read_config(locator & 0xffff,
+ (locator & 0xff0000) >> 16, 2, &port) == -1) {
+ printf("Cannot read bar at 0x%x\n", locator);
+ return (CMD_ERROR);
+ }
+ if (!PCI_BAR_IO(port)) {
+ printf("Memory bar at 0x%x\n", locator);
+ return (CMD_ERROR);
+ }
+ port &= PCIM_BAR_IO_BASE;
+
+ sprintf(intbuf, "%d", port);
+ unsetenv("comconsole_port");
+ env_setenv("comconsole_port", EV_VOLATILE, intbuf,
+ comc_port_set, env_nounset);
+
+ comc_setup(comc_curspeed, port);
+ comc_locator = locator;
+
+ return (CMD_OK);
+}
+
+static int
+comc_pcidev_set(struct env_var *ev, int flags, const void *value)
+{
+ uint32_t locator;
+ int error;
+
+ if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) {
+ printf("Invalid pcidev\n");
+ return (CMD_ERROR);
+ }
+ if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 &&
+ comc_locator != locator) {
+ error = comc_pcidev_handle(locator);
+ if (error != CMD_OK)
+ return (error);
+ }
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return (CMD_OK);
+}
+
+static void
+comc_setup(int speed, int port)
+{
+ static int TRY_COUNT = 1000000;
+ char intbuf[64];
+ int tries;
+
+ unsetenv("hw.uart.console");
+ comc_curspeed = speed;
+ comc_port = port;
+ if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0)
+ return;
+
+ outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT);
+ outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff);
+ outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8);
+ outb(comc_port + com_cfcr, COMC_FMT);
+ outb(comc_port + com_mcr, MCR_RTS | MCR_DTR);
+
+ tries = 0;
+ do
+ inb(comc_port + com_data);
+ while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT);
+
+ if (tries < TRY_COUNT) {
+ comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+ sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed);
+ env_setenv("hw.uart.console", EV_VOLATILE, intbuf, NULL, NULL);
+ } else
+ comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
+}
+
+static int
+comc_parseint(const char *speedstr)
+{
+ char *p;
+ int speed;
+
+ speed = strtol(speedstr, &p, 0);
+ if (p == speedstr || *p != '\0' || speed <= 0)
+ return (-1);
+
+ return (speed);
+}
+
+static int
+comc_getspeed(void)
+{
+ u_int divisor;
+ u_char dlbh;
+ u_char dlbl;
+ u_char cfcr;
+
+ cfcr = inb(comc_port + com_cfcr);
+ outb(comc_port + com_cfcr, CFCR_DLAB | cfcr);
+
+ dlbl = inb(comc_port + com_dlbl);
+ dlbh = inb(comc_port + com_dlbh);
+
+ outb(comc_port + com_cfcr, cfcr);
+
+ divisor = dlbh << 8 | dlbl;
+
+ /* XXX there should be more sanity checking. */
+ if (divisor == 0)
+ return (COMSPEED);
+ return (COMC_DIV2BPS(divisor));
+}
diff --git a/stand/pc98/libpc98/libpc98.h b/stand/pc98/libpc98/libpc98.h
new file mode 100644
index 0000000..78b07a1
--- /dev/null
+++ b/stand/pc98/libpc98/libpc98.h
@@ -0,0 +1,29 @@
+/*-
+ * Copyright (c) 2009 TAKAHASHI Yoshihiro <nyan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+void set_machine_type(void);
diff --git a/stand/pc98/libpc98/pc98_sys.c b/stand/pc98/libpc98/pc98_sys.c
new file mode 100644
index 0000000..7f66d02
--- /dev/null
+++ b/stand/pc98/libpc98/pc98_sys.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 2009 TAKAHASHI Yoshihiro <nyan@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <btxv86.h>
+#include <machine/cpufunc.h>
+#define _KERNEL
+#include <pc98/pc98/pc98_machdep.h>
+
+/*
+ * Set machine type to PC98_SYSTEM_PARAMETER.
+ */
+void
+set_machine_type(void)
+{
+ int i;
+ u_long ret, data;
+
+ /* PC98_SYSTEM_PARAMETER (0x501) */
+ ret = ((*(u_char *)PTOV(0xA1501)) & 0x08) >> 3;
+
+ /* Wait V-SYNC */
+ while (inb(0x60) & 0x20) {}
+ while (!(inb(0x60) & 0x20)) {}
+
+ /* ANK 'A' font */
+ outb(0xa1, 0x00);
+ outb(0xa3, 0x41);
+
+ /* M_NORMAL, use CG window (all NEC OK) */
+ for (i = data = 0; i < 4; i++)
+ data += *((u_long *)PTOV(0xA4000) + i); /* 0xa4000 */
+ if (data == 0x6efc58fc) /* DA data */
+ ret |= M_NEC_PC98;
+ else
+ ret |= M_EPSON_PC98;
+ ret |= (inb(0x42) & 0x20) ? M_8M : 0;
+
+ /* PC98_SYSTEM_PARAMETER(0x400) */
+ if ((*(u_char *)PTOV(0xA1400)) & 0x80)
+ ret |= M_NOTE;
+ if (ret & M_NEC_PC98) {
+ /* PC98_SYSTEM_PARAMETER(0x458) */
+ if ((*(u_char *)PTOV(0xA1458)) & 0x80)
+ ret |= M_H98;
+ else
+ ret |= M_NOT_H98;
+ } else
+ ret |= M_NOT_H98;
+
+ (*(u_long *)PTOV(0xA1620)) = ret;
+}
diff --git a/stand/pc98/libpc98/time.c b/stand/pc98/libpc98/time.c
new file mode 100644
index 0000000..5d832bb
--- /dev/null
+++ b/stand/pc98/libpc98/time.c
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <btxv86.h>
+#include <machine/cpufunc.h>
+#include "bootstrap.h"
+#include "libi386.h"
+
+static int bios_seconds(void);
+
+/*
+ * Return the BIOS time-of-day value.
+ *
+ * XXX uses undocumented BCD support from libstand.
+ */
+static int
+bios_seconds(void)
+{
+ int hr, minute, sec;
+ unsigned char bios_time[6];
+
+ v86.ctl = 0;
+ v86.addr = 0x1c; /* int 0x1c, function 0 */
+ v86.eax = 0x0000;
+ v86.es = VTOPSEG(bios_time);
+ v86.ebx = VTOPOFF(bios_time);
+ v86int();
+
+ hr = bcd2bin(bios_time[3]);
+ minute = bcd2bin(bios_time[4]);
+ sec = bcd2bin(bios_time[5]);
+
+ return (hr * 3600 + minute * 60 + sec);
+}
+
+/*
+ * Return the time in seconds since the beginning of the day.
+ */
+time_t
+time(time_t *t)
+{
+ static time_t lasttime;
+ time_t now;
+
+ now = bios_seconds();
+
+ if (now < lasttime)
+ now += 24 * 3600;
+ lasttime = now;
+
+ if (t != NULL)
+ *t = now;
+ return(now);
+}
+
+/*
+ * Use the BIOS Wait function to pause for (period) microseconds.
+ *
+ * Resolution of this function is variable, but typically around
+ * 1ms.
+ */
+void
+delay(int period)
+{
+ int i;
+
+ period = (period + 500) / 1000;
+ for( ; period != 0 ; period--)
+ for(i=800;i != 0; i--)
+ outb(0x5f,0); /* wait 600ns */
+}
diff --git a/stand/pc98/libpc98/vidconsole.c b/stand/pc98/libpc98/vidconsole.c
new file mode 100644
index 0000000..7cf81e8
--- /dev/null
+++ b/stand/pc98/libpc98/vidconsole.c
@@ -0,0 +1,596 @@
+/*-
+ * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
+ * Copyright (c) 1997 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp)
+ * 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.
+ *
+ * Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <machine/cpufunc.h>
+#include "libi386.h"
+
+#if KEYBOARD_PROBE
+#include <machine/cpufunc.h>
+
+static int probe_keyboard(void);
+#endif
+static void vidc_probe(struct console *cp);
+static int vidc_init(int arg);
+static void vidc_putchar(int c);
+static int vidc_getchar(void);
+static int vidc_ischar(void);
+
+static int vidc_started;
+
+#ifdef TERM_EMU
+#define MAXARGS 8
+#define DEFAULT_FGCOLOR 7
+#define DEFAULT_BGCOLOR 0
+
+void end_term(void);
+void bail_out(int c);
+void vidc_term_emu(int c);
+void get_pos(void);
+void curs_move(int x, int y);
+void write_char(int c, int fg, int bg);
+void scroll_up(int rows, int fg, int bg);
+void CD(void);
+void CM(void);
+void HO(void);
+
+static int args[MAXARGS], argc;
+static int fg_c, bg_c, curx, cury;
+static int esc;
+#endif
+
+static unsigned short *crtat, *Crtat;
+static int row = 25, col = 80;
+#ifdef TERM_EMU
+static u_int8_t ibmpc_to_pc98[256] = {
+ 0x01, 0x21, 0x81, 0xa1, 0x41, 0x61, 0xc1, 0xe1,
+ 0x09, 0x29, 0x89, 0xa9, 0x49, 0x69, 0xc9, 0xe9,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
+ 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
+ 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
+ 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+
+ 0x03, 0x23, 0x83, 0xa3, 0x43, 0x63, 0xc3, 0xe3,
+ 0x0b, 0x2b, 0x8b, 0xab, 0x4b, 0x6b, 0xcb, 0xeb,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
+ 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef,
+};
+#define at2pc98(fg_at, bg_at) ibmpc_to_pc98[((bg_at) << 4) | (fg_at)]
+#endif /* TERM_EMU */
+
+struct console vidconsole = {
+ "vidconsole",
+ "internal video/keyboard",
+ 0,
+ vidc_probe,
+ vidc_init,
+ vidc_putchar,
+ vidc_getchar,
+ vidc_ischar
+};
+
+static void
+vidc_probe(struct console *cp)
+{
+
+ /* look for a keyboard */
+#if KEYBOARD_PROBE
+ if (probe_keyboard())
+#endif
+ {
+
+ cp->c_flags |= C_PRESENTIN;
+ }
+
+ /* XXX for now, always assume we can do BIOS screen output */
+ cp->c_flags |= C_PRESENTOUT;
+}
+
+static int
+vidc_init(int arg)
+{
+ int i, hw_cursor;
+
+ if (vidc_started && arg == 0)
+ return (0);
+ vidc_started = 1;
+ Crtat = (unsigned short *)PTOV(0xA0000);
+ while ((inb(0x60) & 0x04) == 0)
+ ;
+ outb(0x62, 0xe0);
+ while ((inb(0x60) & 0x01) == 0)
+ ;
+ hw_cursor = inb(0x62);
+ hw_cursor |= (inb(0x62) << 8);
+ inb(0x62);
+ inb(0x62);
+ inb(0x62);
+ crtat = Crtat + hw_cursor;
+#ifdef TERM_EMU
+ /* Init terminal emulator */
+ end_term();
+ get_pos();
+ curs_move(curx, cury);
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+#endif
+ for (i = 0; i < 10 && vidc_ischar(); i++)
+ (void)vidc_getchar();
+ return (0); /* XXX reinit? */
+}
+
+static void
+beep(void)
+{
+
+ outb(0x37, 6);
+ delay(40000);
+ outb(0x37, 7);
+}
+
+#if 0
+static void
+vidc_biosputchar(int c)
+{
+ unsigned short *cp;
+ int i, pos;
+
+#ifdef TERM_EMU
+ *crtat = (c == 0x5c ? 0xfc : c);
+ *(crtat + 0x1000) = at2pc98(fg, bg);
+#else
+ switch(c) {
+ case '\b':
+ crtat--;
+ break;
+ case '\r':
+ crtat -= (crtat - Crtat) % col;
+ break;
+ case '\n':
+ crtat += col;
+ break;
+ default:
+ *crtat = (c == 0x5c ? 0xfc : c);
+ *(crtat++ + 0x1000) = 0xe1;
+ break;
+ }
+
+ if (crtat >= Crtat + col * row) {
+ cp = Crtat;
+ for (i = 1; i < row; i++) {
+ bcopy((void *)(cp + col), (void *)cp, col * 2);
+ cp += col;
+ }
+ for (i = 0; i < col; i++) {
+ *cp++ = ' ';
+ }
+ crtat -= col;
+ }
+ pos = crtat - Crtat;
+ while ((inb(0x60) & 0x04) == 0) {}
+ outb(0x62, 0x49);
+ outb(0x60, pos & 0xff);
+ outb(0x60, pos >> 8);
+#endif
+}
+#endif
+
+static void
+vidc_rawputchar(int c)
+{
+ int i;
+
+ if (c == '\t')
+ /* lame tab expansion */
+ for (i = 0; i < 8; i++)
+ vidc_rawputchar(' ');
+ else {
+ /* Emulate AH=0eh (teletype output) */
+ switch(c) {
+ case '\a':
+ beep();
+ return;
+ case '\r':
+ curx = 0;
+ curs_move(curx, cury);
+ return;
+ case '\n':
+ cury++;
+ if (cury > 24) {
+ scroll_up(1, fg_c, bg_c);
+ cury--;
+ } else {
+ curs_move(curx, cury);
+ }
+ return;
+ case '\b':
+ if (curx > 0) {
+ curx--;
+ curs_move(curx, cury);
+ /* write_char(' ', fg_c, bg_c); XXX destructive(!) */
+ return;
+ }
+ return;
+ default:
+ write_char(c, fg_c, bg_c);
+ curx++;
+ if (curx > 79) {
+ curx = 0;
+ cury++;
+ }
+ if (cury > 24) {
+ curx = 0;
+ scroll_up(1, fg_c, bg_c);
+ cury--;
+ }
+ }
+ curs_move(curx, cury);
+ }
+}
+
+#ifdef TERM_EMU
+
+/* Get cursor position on the screen. Result is in edx. Sets
+ * curx and cury appropriately.
+ */
+void
+get_pos(void)
+{
+ int pos = crtat - Crtat;
+
+ curx = pos % col;
+ cury = pos / col;
+}
+
+/* Move cursor to x rows and y cols (0-based). */
+void
+curs_move(int x, int y)
+{
+ int pos;
+
+ pos = x + y * col;
+ crtat = Crtat + pos;
+ pos = crtat - Crtat;
+ while((inb(0x60) & 0x04) == 0) {}
+ outb(0x62, 0x49);
+ outb(0x60, pos & 0xff);
+ outb(0x60, pos >> 8);
+ curx = x;
+ cury = y;
+#define isvisible(c) (((c) >= 32) && ((c) < 255))
+ if (!isvisible(*crtat & 0x00ff)) {
+ write_char(' ', fg_c, bg_c);
+ }
+}
+
+/* Scroll up the whole window by a number of rows. If rows==0,
+ * clear the window. fg and bg are attributes for the new lines
+ * inserted in the window.
+ */
+void
+scroll_up(int rows, int fgcol, int bgcol)
+{
+ unsigned short *cp;
+ int i;
+
+ if (rows == 0)
+ rows = 25;
+ cp = Crtat;
+ for (i = rows; i < row; i++) {
+ bcopy((void *)(cp + col), (void *)cp, col * 2);
+ cp += col;
+ }
+ for (i = 0; i < col; i++) {
+ *(cp + 0x1000) = at2pc98(fgcol, bgcol);
+ *cp++ = ' ';
+ }
+}
+
+/* Write character and attribute at cursor position. */
+void
+write_char(int c, int fgcol, int bgcol)
+{
+
+ *crtat = (c == 0x5c ? 0xfc : (c & 0xff));
+ *(crtat + 0x1000) = at2pc98(fgcol, bgcol);
+}
+
+/**************************************************************/
+/*
+ * Screen manipulation functions. They use accumulated data in
+ * args[] and argc variables.
+ *
+ */
+
+/* Clear display from current position to end of screen */
+void
+CD(void)
+{
+ int pos;
+
+ get_pos();
+ for (pos = 0; crtat + pos <= Crtat + col * row; pos++) {
+ *(crtat + pos) = ' ';
+ *(crtat + pos + 0x1000) = at2pc98(fg_c, bg_c);
+ }
+ end_term();
+}
+
+/* Absolute cursor move to args[0] rows and args[1] columns
+ * (the coordinates are 1-based).
+ */
+void
+CM(void)
+{
+
+ if (args[0] > 0)
+ args[0]--;
+ if (args[1] > 0)
+ args[1]--;
+ curs_move(args[1], args[0]);
+ end_term();
+}
+
+/* Home cursor (left top corner) */
+void
+HO(void)
+{
+
+ argc = 1;
+ args[0] = args[1] = 1;
+ CM();
+}
+
+/* Clear internal state of the terminal emulation code */
+void
+end_term(void)
+{
+
+ esc = 0;
+ argc = -1;
+}
+
+/* Gracefully exit ESC-sequence processing in case of misunderstanding */
+void
+bail_out(int c)
+{
+ char buf[16], *ch;
+ int i;
+
+ if (esc) {
+ vidc_rawputchar('\033');
+ if (esc != '\033')
+ vidc_rawputchar(esc);
+ for (i = 0; i <= argc; ++i) {
+ sprintf(buf, "%d", args[i]);
+ ch = buf;
+ while (*ch)
+ vidc_rawputchar(*ch++);
+ }
+ }
+ vidc_rawputchar(c);
+ end_term();
+}
+
+static void
+get_arg(int c)
+{
+
+ if (argc < 0)
+ argc = 0;
+ args[argc] *= 10;
+ args[argc] += c - '0';
+}
+
+/* Emulate basic capabilities of cons25 terminal */
+void
+vidc_term_emu(int c)
+{
+ static int ansi_col[] = {
+ 0, 4, 2, 6, 1, 5, 3, 7,
+ };
+ int t;
+ int i;
+
+ switch (esc) {
+ case 0:
+ switch (c) {
+ case '\033':
+ esc = c;
+ break;
+ default:
+ vidc_rawputchar(c);
+ break;
+ }
+ break;
+
+ case '\033':
+ switch (c) {
+ case '[':
+ esc = c;
+ args[0] = 0;
+ argc = -1;
+ break;
+ default:
+ bail_out(c);
+ break;
+ }
+ break;
+
+ case '[':
+ switch (c) {
+ case ';':
+ if (argc < 0) /* XXX */
+ argc = 0;
+ else if (argc + 1 >= MAXARGS)
+ bail_out(c);
+ else
+ args[++argc] = 0;
+ break;
+ case 'H':
+ if (argc < 0)
+ HO();
+ else if (argc == 1)
+ CM();
+ else
+ bail_out(c);
+ break;
+ case 'J':
+ if (argc < 0)
+ CD();
+ else
+ bail_out(c);
+ break;
+ case 'm':
+ if (argc < 0) {
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+ }
+ for (i = 0; i <= argc; ++i) {
+ switch (args[i]) {
+ case 0: /* back to normal */
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+ break;
+ case 1: /* bold */
+ fg_c |= 0x8;
+ break;
+ case 4: /* underline */
+ case 5: /* blink */
+ bg_c |= 0x8;
+ break;
+ case 7: /* reverse */
+ t = fg_c;
+ fg_c = bg_c;
+ bg_c = t;
+ break;
+ case 30: case 31: case 32: case 33:
+ case 34: case 35: case 36: case 37:
+ fg_c = ansi_col[args[i] - 30];
+ break;
+ case 39: /* normal */
+ fg_c = DEFAULT_FGCOLOR;
+ break;
+ case 40: case 41: case 42: case 43:
+ case 44: case 45: case 46: case 47:
+ bg_c = ansi_col[args[i] - 40];
+ break;
+ case 49: /* normal */
+ bg_c = DEFAULT_BGCOLOR;
+ break;
+ }
+ }
+ end_term();
+ break;
+ default:
+ if (isdigit(c))
+ get_arg(c);
+ else
+ bail_out(c);
+ break;
+ }
+ break;
+
+ default:
+ bail_out(c);
+ break;
+ }
+}
+#endif
+
+static void
+vidc_putchar(int c)
+{
+#ifdef TERM_EMU
+ vidc_term_emu(c);
+#else
+ vidc_rawputchar(c);
+#endif
+}
+
+static int
+vidc_getchar(void)
+{
+
+ if (vidc_ischar()) {
+ v86.ctl = 0;
+ v86.addr = 0x18;
+ v86.eax = 0x0;
+ v86int();
+ return (v86.eax & 0xff);
+ } else {
+ return (-1);
+ }
+}
+
+static int
+vidc_ischar(void)
+{
+
+ v86.ctl = 0;
+ v86.addr = 0x18;
+ v86.eax = 0x100;
+ v86int();
+ return ((v86.ebx >> 8) & 0x1);
+}
+
+#if KEYBOARD_PROBE
+static int
+probe_keyboard(void)
+{
+ return (*(u_char *)PTOV(0xA1481) & 0x48);
+}
+#endif /* KEYBOARD_PROBE */
diff --git a/stand/pc98/loader/Makefile b/stand/pc98/loader/Makefile
new file mode 100644
index 0000000..d75e8d0
--- /dev/null
+++ b/stand/pc98/loader/Makefile
@@ -0,0 +1,99 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+MK_SSP= no
+MAN=
+
+LOADER?= loader
+PROG= ${LOADER}.sym
+INTERNALPROG=
+NEWVERSWHAT= "bootstrap loader" pc98
+VERSION_FILE= ${.CURDIR}/../../i386/loader/version
+
+# architecture-specific loader code
+SRCS= main.c conf.c vers.c
+.PATH: ${.CURDIR}/../../i386/loader
+
+# Enable PXE TFTP or NFS support, not both.
+.if defined(LOADER_TFTP_SUPPORT)
+CFLAGS+= -DLOADER_TFTP_SUPPORT
+.else
+CFLAGS+= -DLOADER_NFS_SUPPORT
+.endif
+
+# Include bcache code.
+HAVE_BCACHE= yes
+
+# Enable PnP and ISA-PnP code.
+HAVE_PNP= yes
+HAVE_ISABUS= yes
+
+.if ${MK_FORTH} != "no"
+# Enable BootForth
+BOOT_FORTH= yes
+CFLAGS+= -DBOOT_FORTH -I${.CURDIR}/../../ficl -I${.CURDIR}/../../ficl/i386
+LIBFICL= ${.OBJDIR}/../../ficl/libficl.a
+.endif
+
+.if defined(LOADER_BZIP2_SUPPORT)
+CFLAGS+= -DLOADER_BZIP2_SUPPORT
+.endif
+.if !defined(LOADER_NO_GZIP_SUPPORT)
+CFLAGS+= -DLOADER_GZIP_SUPPORT
+.endif
+
+# Always add MI sources
+.PATH: ${.CURDIR}/../../common
+.include "${.CURDIR}/../../common/Makefile.inc"
+CFLAGS+= -I${.CURDIR}/../../common
+CFLAGS+= -I${.CURDIR}/../../i386
+CFLAGS+= -I.
+
+CLEANFILES= ${LOADER} ${LOADER}.bin loader.help
+
+CFLAGS+= -Wall
+LDFLAGS= -static -Ttext 0x0
+
+# pc98 standalone support library
+LIBPC98= ${.OBJDIR}/../libpc98/libpc98.a
+CFLAGS+= -I${.CURDIR}/..
+
+LIBSTAND= ${.OBJDIR}/../../libstand32/libstand.a
+
+# BTX components
+CFLAGS+= -I${.CURDIR}/../btx/lib
+
+# Debug me!
+#CFLAGS+= -g
+#LDFLAGS+= -g
+
+# Pick up ../Makefile.inc early.
+.include <bsd.init.mk>
+
+${LOADER}: ${LOADER}.bin ${BTXLDR} ${BTXKERN}
+ btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \
+ -b ${BTXKERN} ${LOADER}.bin
+
+${LOADER}.bin: ${LOADER}.sym
+ cp ${.ALLSRC} ${.TARGET}
+ strip -R .comment -R .note ${.TARGET}
+
+loader.help: help.common help.pc98
+ cat ${.ALLSRC} | awk -f ${.CURDIR}/../../common/merge_help.awk > ${.TARGET}
+
+FILES= ${LOADER}
+# XXX INSTALLFLAGS_loader= -b
+FILESMODE_${LOADER}= ${BINMODE} -b
+
+.PATH: ${.CURDIR}/../../forth
+.include "${.CURDIR}/../../forth/Makefile.inc"
+
+FILES+= ${.CURDIR}/../../i386/loader/loader.rc menu.rc
+
+# XXX crt0.o needs to be first for pxeboot(8) to work
+OBJS= ${BTXCRT}
+
+DPADD= ${LIBFICL} ${LIBPC98} ${LIBSTAND}
+LDADD= ${LIBFICL} ${LIBPC98} ${LIBSTAND}
+
+.include <bsd.prog.mk>
diff --git a/stand/pc98/loader/conf.c b/stand/pc98/loader/conf.c
new file mode 100644
index 0000000..695c260
--- /dev/null
+++ b/stand/pc98/loader/conf.c
@@ -0,0 +1,116 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include "libi386/libi386.h"
+
+/*
+ * We could use linker sets for some or all of these, but
+ * then we would have to control what ended up linked into
+ * the bootstrap. So it's easier to conditionalise things
+ * here.
+ *
+ * XXX rename these arrays to be consistent and less namespace-hostile
+ *
+ * XXX as libi386 and biosboot merge, some of these can become linker sets.
+ */
+
+/* Exported for libstand */
+struct devsw *devsw[] = {
+ &bioscd,
+ &biosdisk,
+#if defined(LOADER_NFS_SUPPORT) || defined(LOADER_TFTP_SUPPORT)
+ &pxedisk,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+ &ufs_fsops,
+ &ext2fs_fsops,
+ &dosfs_fsops,
+ &cd9660_fsops,
+#ifdef LOADER_NFS_SUPPORT
+ &nfs_fsops,
+#endif
+#ifdef LOADER_TFTP_SUPPORT
+ &tftp_fsops,
+#endif
+#ifdef LOADER_GZIP_SUPPORT
+ &gzipfs_fsops,
+#endif
+#ifdef LOADER_BZIP2_SUPPORT
+ &bzipfs_fsops,
+#endif
+ &splitfs_fsops,
+ NULL
+};
+
+/* Exported for i386 only */
+/*
+ * Sort formats so that those that can detect based on arguments
+ * rather than reading the file go first.
+ */
+extern struct file_format i386_elf;
+extern struct file_format i386_elf_obj;
+
+struct file_format *file_formats[] = {
+ &i386_elf,
+ &i386_elf_obj,
+ NULL
+};
+
+/*
+ * Consoles
+ *
+ * We don't prototype these in libi386.h because they require
+ * data structures from bootstrap.h as well.
+ */
+extern struct console vidconsole;
+extern struct console comconsole;
+extern struct console nullconsole;
+
+struct console *consoles[] = {
+ &vidconsole,
+ &comconsole,
+ &nullconsole,
+ NULL
+};
+
+extern struct pnphandler isapnphandler;
+extern struct pnphandler biospnphandler;
+extern struct pnphandler biospcihandler;
+
+struct pnphandler *pnphandlers[] = {
+ &biospnphandler, /* should go first, as it may set isapnp_readport */
+ &isapnphandler,
+ &biospcihandler,
+ NULL
+};
diff --git a/stand/pc98/loader/help.pc98 b/stand/pc98/loader/help.pc98
new file mode 100644
index 0000000..4b9197c
--- /dev/null
+++ b/stand/pc98/loader/help.pc98
@@ -0,0 +1,38 @@
+################################################################################
+# Treboot DReboot the system
+
+ reboot
+
+ Causes the system to immediately reboot.
+
+################################################################################
+# Theap DDisplay memory management statistics
+
+ heap
+
+ Requests debugging output from the heap manager. For debugging use
+ only.
+
+################################################################################
+# Tset Snum_ide_disks DSet the number of IDE disks
+
+ NOTE: this variable is deprecated, use root_disk_unit instead.
+
+ set num_ide_disks=<value>
+
+ When booting from a SCSI disk on a system with one or more IDE disks,
+ and where the IDE disks are the default boot device, it is necessary
+ to tell the kernel how many IDE disks there are in order to have it
+ correctly locate the SCSI disk you are booting from.
+
+################################################################################
+# Tset Sroot_disk_unit DForce the root disk unit number.
+
+ set root_disk_unit=<value>
+
+ If the code which detects the disk unit number for the root disk is
+ confused, eg. by a mix of SCSI and IDE disks, or IDE disks with
+ gaps in the sequence (eg. no primary slave), the unit number can be
+ forced by setting this variable.
+
+################################################################################
diff --git a/stand/pc98/loader/main.c b/stand/pc98/loader/main.c
new file mode 100644
index 0000000..c31cc84
--- /dev/null
+++ b/stand/pc98/loader/main.c
@@ -0,0 +1,322 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * MD bootstrap main() and assorted miscellaneous
+ * commands.
+ */
+
+#include <stand.h>
+#include <stddef.h>
+#include <string.h>
+#include <machine/bootinfo.h>
+#include <machine/cpufunc.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+
+#include "bootstrap.h"
+#include "common/bootargs.h"
+#include "libi386/libi386.h"
+#include "libpc98/libpc98.h"
+#include "btxv86.h"
+
+CTASSERT(sizeof(struct bootargs) == BOOTARGS_SIZE);
+CTASSERT(offsetof(struct bootargs, bootinfo) == BA_BOOTINFO);
+CTASSERT(offsetof(struct bootargs, bootflags) == BA_BOOTFLAGS);
+CTASSERT(offsetof(struct bootinfo, bi_size) == BI_SIZE);
+
+/* Arguments passed in from the boot1/boot2 loader */
+static struct bootargs *kargs;
+
+static u_int32_t initial_howto;
+static u_int32_t initial_bootdev;
+static struct bootinfo *initial_bootinfo;
+
+struct arch_switch archsw; /* MI/MD interface boundary */
+
+static void extract_currdev(void);
+static int isa_inb(int port);
+static void isa_outb(int port, int value);
+void exit(int code);
+
+/* from vers.c */
+extern char bootprog_info[];
+
+/* XXX debugging */
+extern char end[];
+
+static void *heap_top;
+static void *heap_bottom;
+
+static uint64_t
+pc98_loadaddr(u_int type, void *data, uint64_t addr)
+{
+ struct stat st;
+
+ if (type == LOAD_ELF)
+ return (roundup(addr, PAGE_SIZE));
+
+ /* We cannot use 15M-16M area on pc98. */
+ if (type == LOAD_RAW && addr < 0x1000000 && stat(data, &st) == 0 &&
+ (st.st_size == -1 || addr + st.st_size > 0xf00000))
+ addr = 0x1000000;
+ return (addr);
+}
+
+int
+main(void)
+{
+ int i;
+
+ /* Set machine type to PC98_SYSTEM_PARAMETER. */
+ set_machine_type();
+
+ /* Pick up arguments */
+ kargs = (void *)__args;
+ initial_howto = kargs->howto;
+ initial_bootdev = kargs->bootdev;
+ initial_bootinfo = kargs->bootinfo ? (struct bootinfo *)PTOV(kargs->bootinfo) : NULL;
+
+ /* Initialize the v86 register set to a known-good state. */
+ bzero(&v86, sizeof(v86));
+ v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
+
+ /*
+ * Initialise the heap as early as possible. Once this is done, malloc() is usable.
+ */
+ bios_getmem();
+
+#if defined(LOADER_BZIP2_SUPPORT)
+ if (high_heap_size > 0) {
+ heap_top = PTOV(high_heap_base + high_heap_size);
+ heap_bottom = PTOV(high_heap_base);
+ if (high_heap_base < memtop_copyin)
+ memtop_copyin = high_heap_base;
+ } else
+#endif
+ {
+ heap_top = (void *)PTOV(bios_basemem);
+ heap_bottom = (void *)end;
+ }
+ setheap(heap_bottom, heap_top);
+
+ /*
+ * XXX Chicken-and-egg problem; we want to have console output early, but some
+ * console attributes may depend on reading from eg. the boot device, which we
+ * can't do yet.
+ *
+ * We can use printf() etc. once this is done.
+ * If the previous boot stage has requested a serial console, prefer that.
+ */
+ bi_setboothowto(initial_howto);
+ if (initial_howto & RB_MULTIPLE) {
+ if (initial_howto & RB_SERIAL)
+ setenv("console", "comconsole vidconsole", 1);
+ else
+ setenv("console", "vidconsole comconsole", 1);
+ } else if (initial_howto & RB_SERIAL)
+ setenv("console", "comconsole", 1);
+ else if (initial_howto & RB_MUTE)
+ setenv("console", "nullconsole", 1);
+ cons_probe();
+
+ /*
+ * Initialise the block cache. Set the upper limit.
+ */
+ bcache_init(32768, 512);
+
+ /*
+ * Special handling for PXE and CD booting.
+ */
+ if (kargs->bootinfo == 0) {
+ /*
+ * We only want the PXE disk to try to init itself in the below
+ * walk through devsw if we actually booted off of PXE.
+ */
+ if (kargs->bootflags & KARGS_FLAGS_PXE)
+ pxe_enable(kargs->pxeinfo ? PTOV(kargs->pxeinfo) : NULL);
+ else if (kargs->bootflags & KARGS_FLAGS_CD)
+ bc_add(initial_bootdev);
+ }
+
+ archsw.arch_autoload = i386_autoload;
+ archsw.arch_getdev = i386_getdev;
+ archsw.arch_copyin = i386_copyin;
+ archsw.arch_copyout = i386_copyout;
+ archsw.arch_readin = i386_readin;
+ archsw.arch_isainb = isa_inb;
+ archsw.arch_isaoutb = isa_outb;
+ archsw.arch_loadaddr = pc98_loadaddr;
+
+ /*
+ * March through the device switch probing for things.
+ */
+ for (i = 0; devsw[i] != NULL; i++)
+ if (devsw[i]->dv_init != NULL)
+ (devsw[i]->dv_init)();
+ printf("BIOS %dkB/%dkB available memory\n", bios_basemem / 1024, bios_extmem / 1024);
+ if (initial_bootinfo != NULL) {
+ initial_bootinfo->bi_basemem = bios_basemem / 1024;
+ initial_bootinfo->bi_extmem = bios_extmem / 1024;
+ }
+
+ printf("\n%s", bootprog_info);
+
+ extract_currdev(); /* set $currdev and $loaddev */
+ setenv("LINES", "24", 1); /* optional */
+
+ interact(NULL); /* doesn't return */
+
+ /* if we ever get here, it is an error */
+ return (1);
+}
+
+/*
+ * Set the 'current device' by (if possible) recovering the boot device as
+ * supplied by the initial bootstrap.
+ *
+ * XXX should be extended for netbooting.
+ */
+static void
+extract_currdev(void)
+{
+ struct i386_devdesc new_currdev;
+ int major;
+ int biosdev = -1;
+
+ /* Assume we are booting from a BIOS disk by default */
+ new_currdev.d_dev = &biosdisk;
+
+ /* new-style boot loaders such as pxeldr and cdldr */
+ if (kargs->bootinfo == 0) {
+ if ((kargs->bootflags & KARGS_FLAGS_CD) != 0) {
+ /* we are booting from a CD with cdboot */
+ new_currdev.d_dev = &bioscd;
+ new_currdev.d_unit = bc_bios2unit(initial_bootdev);
+ } else if ((kargs->bootflags & KARGS_FLAGS_PXE) != 0) {
+ /* we are booting from pxeldr */
+ new_currdev.d_dev = &pxedisk;
+ new_currdev.d_unit = 0;
+ } else {
+ /* we don't know what our boot device is */
+ new_currdev.d_kind.biosdisk.slice = -1;
+ new_currdev.d_kind.biosdisk.partition = 0;
+ biosdev = -1;
+ }
+ } else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) {
+ /* The passed-in boot device is bad */
+ new_currdev.d_kind.biosdisk.slice = -1;
+ new_currdev.d_kind.biosdisk.partition = 0;
+ biosdev = -1;
+ } else {
+ new_currdev.d_kind.biosdisk.slice = B_SLICE(initial_bootdev) - 1;
+ new_currdev.d_kind.biosdisk.partition = B_PARTITION(initial_bootdev);
+ biosdev = initial_bootinfo->bi_bios_dev;
+ major = B_TYPE(initial_bootdev);
+
+ /*
+ * If we are booted by an old bootstrap, we have to guess at the BIOS
+ * unit number. We will lose if there is more than one disk type
+ * and we are not booting from the lowest-numbered disk type
+ * (ie. SCSI when IDE also exists).
+ */
+ if ((biosdev == 0) && (B_TYPE(initial_bootdev) != 2)) { /* biosdev doesn't match major */
+ if (B_TYPE(initial_bootdev) == 6)
+ biosdev = 0x30 + B_UNIT(initial_bootdev);
+ else
+ biosdev = (major << 3) + 0x80 + B_UNIT(initial_bootdev);
+ }
+ }
+ new_currdev.d_type = new_currdev.d_dev->dv_type;
+
+ /*
+ * If we are booting off of a BIOS disk and we didn't succeed in determining
+ * which one we booted off of, just use disk0: as a reasonable default.
+ */
+ if ((new_currdev.d_type == biosdisk.dv_type) &&
+ ((new_currdev.d_unit = bd_bios2unit(biosdev)) == -1)) {
+ printf("Can't work out which disk we are booting from.\n"
+ "Guessed BIOS device 0x%x not found by probes, defaulting to disk0:\n", biosdev);
+ new_currdev.d_unit = 0;
+ }
+
+ env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev),
+ i386_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset,
+ env_nounset);
+}
+
+COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
+
+static int
+command_reboot(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; devsw[i] != NULL; ++i)
+ if (devsw[i]->dv_cleanup != NULL)
+ (devsw[i]->dv_cleanup)();
+
+ printf("Rebooting...\n");
+ delay(1000000);
+ __exit(0);
+}
+
+/* provide this for panic, as it's not in the startup code */
+void
+exit(int code)
+{
+ __exit(code);
+}
+
+COMMAND_SET(heap, "heap", "show heap usage", command_heap);
+
+static int
+command_heap(int argc, char *argv[])
+{
+ mallocstats();
+ printf("heap base at %p, top at %p, upper limit at %p\n", heap_bottom,
+ sbrk(0), heap_top);
+ return(CMD_OK);
+}
+
+/* ISA bus access functions for PnP. */
+static int
+isa_inb(int port)
+{
+
+ return (inb(port));
+}
+
+static void
+isa_outb(int port, int value)
+{
+
+ outb(port, value);
+}
diff --git a/stand/pc98/pc98boot/Makefile b/stand/pc98/pc98boot/Makefile
new file mode 100644
index 0000000..f33b15f
--- /dev/null
+++ b/stand/pc98/pc98boot/Makefile
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+FILES= ${BOOT}
+CLEANFILES= ${BOOT} ${BOOT}.part
+
+BOOT= pc98boot
+
+.if exists(${.OBJDIR}/../boot0)
+BOOT0= ${.OBJDIR}/../boot0/boot0
+.else
+BOOT0= ${.CURDIR}/../boot0/boot0
+.endif
+.if exists(${.OBJDIR}/../boot0.5)
+BOOT05= ${.OBJDIR}/../boot0.5/boot0.5
+.else
+BOOT05= ${.CURDIR}/../boot0.5/boot0.5
+.endif
+
+${BOOT}: ${BOOT0} ${BOOT05} ${BOOT}.part
+ cat ${BOOT0} ${BOOT}.part ${BOOT05} > ${.TARGET}
+
+${BOOT}.part:
+ ${DD} if=/dev/zero of=${.TARGET} bs=512 count=1
+
+.include <bsd.prog.mk>
diff --git a/stand/powerpc/Makefile b/stand/powerpc/Makefile
new file mode 100644
index 0000000..969eee8
--- /dev/null
+++ b/stand/powerpc/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+SUBDIR= boot1.chrp ofw uboot
+.if ${MACHINE_ARCH} != "powerpcspe"
+SUBDIR+= ps3
+.endif
+.if ${MK_FDT} == "yes"
+SUBDIR+= kboot
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/stand/powerpc/Makefile.inc b/stand/powerpc/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/stand/powerpc/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/stand/powerpc/boot1.chrp/Makefile b/stand/powerpc/boot1.chrp/Makefile
new file mode 100644
index 0000000..384d0ab
--- /dev/null
+++ b/stand/powerpc/boot1.chrp/Makefile
@@ -0,0 +1,42 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+SSP_CFLAGS=
+
+PROG= boot1.elf
+NEWVERSWHAT= "Open Firmware boot block" ${MACHINE_ARCH}
+INSTALLFLAGS= -b
+
+FILES= boot1.hfs
+SRCS= boot1.c ashldi3.c syncicache.c
+
+MAN=
+
+CFLAGS= -ffreestanding -msoft-float \
+ -I${LDRSRC} -I${SYSDIR} -I${SASRC} \
+ -D_STANDALONE
+LDFLAGS=-nostdlib -static -Wl,-N
+
+.PATH: ${SYSDIR}/libkern ${SRCTOP}/lib/libc/powerpc/gen ${.CURDIR}
+
+# The following inserts out objects into a template HFS
+# created by generate-hfs.sh
+
+.include "${.CURDIR}/Makefile.hfs"
+
+boot1.hfs: boot1.elf bootinfo.txt
+ echo ${.OBJDIR}
+ uudecode ${.CURDIR}/hfs.tmpl.bz2.uu
+ mv hfs.tmpl.bz2 ${.TARGET}.bz2
+ bzip2 -f -d ${.TARGET}.bz2
+ ${DD} if=boot1.elf of=${.TARGET} seek=${BOOT1_OFFSET} conv=notrunc
+ ${DD} if=${.CURDIR}/bootinfo.txt of=${.TARGET} seek=${BOOTINFO_OFFSET} \
+ conv=notrunc
+
+CLEANFILES+= boot1.hfs
+
+boot1.o: ${SASRC}/ufsread.c
+
+.include <bsd.prog.mk>
+
diff --git a/stand/powerpc/boot1.chrp/Makefile.hfs b/stand/powerpc/boot1.chrp/Makefile.hfs
new file mode 100644
index 0000000..c6c2d86
--- /dev/null
+++ b/stand/powerpc/boot1.chrp/Makefile.hfs
@@ -0,0 +1,4 @@
+# This file autogenerated by generate-hfs.sh - DO NOT EDIT
+# $FreeBSD$
+BOOTINFO_OFFSET=0x9c
+BOOT1_OFFSET=0x1c
diff --git a/stand/powerpc/boot1.chrp/boot1.c b/stand/powerpc/boot1.chrp/boot1.c
new file mode 100644
index 0000000..8de8e9f
--- /dev/null
+++ b/stand/powerpc/boot1.chrp/boot1.c
@@ -0,0 +1,777 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ * Copyright (c) 2001 Robert Drehmel
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/dirent.h>
+#include <machine/elf.h>
+#include <machine/stdarg.h>
+
+#include "paths.h"
+
+#define BSIZEMAX 16384
+
+typedef int putc_func_t(char c, void *arg);
+typedef int32_t ofwh_t;
+
+struct sp_data {
+ char *sp_buf;
+ u_int sp_len;
+ u_int sp_size;
+};
+
+static const char digits[] = "0123456789abcdef";
+
+static char bootpath[128];
+static char bootargs[128];
+
+static ofwh_t bootdev;
+
+static struct fs fs;
+static char blkbuf[BSIZEMAX];
+static unsigned int fsblks;
+
+static uint32_t fs_off;
+
+int main(int ac, char **av);
+
+static void exit(int) __dead2;
+static void load(const char *);
+static int dskread(void *, u_int64_t, int);
+
+static void usage(void);
+
+static void bcopy(const void *src, void *dst, size_t len);
+static void bzero(void *b, size_t len);
+
+static int domount(const char *device, int quiet);
+
+static void panic(const char *fmt, ...) __dead2;
+static int printf(const char *fmt, ...);
+static int putchar(char c, void *arg);
+static int vprintf(const char *fmt, va_list ap);
+static int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap);
+
+static int __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap);
+static int __putc(char c, void *arg);
+static int __puts(const char *s, putc_func_t *putc, void *arg);
+static int __sputc(char c, void *arg);
+static char *__uitoa(char *buf, u_int val, int base);
+static char *__ultoa(char *buf, u_long val, int base);
+
+void __syncicache(void *, int);
+
+/*
+ * Open Firmware interface functions
+ */
+typedef u_int32_t ofwcell_t;
+typedef u_int32_t u_ofwh_t;
+typedef int (*ofwfp_t)(void *);
+ofwfp_t ofw; /* the prom Open Firmware entry */
+ofwh_t chosenh;
+
+void ofw_init(void *, int, int (*)(void *), char *, int);
+static ofwh_t ofw_finddevice(const char *);
+static ofwh_t ofw_open(const char *);
+static int ofw_close(ofwh_t);
+static int ofw_getprop(ofwh_t, const char *, void *, size_t);
+static int ofw_setprop(ofwh_t, const char *, void *, size_t);
+static int ofw_read(ofwh_t, void *, size_t);
+static int ofw_write(ofwh_t, const void *, size_t);
+static int ofw_claim(void *virt, size_t len, u_int align);
+static int ofw_seek(ofwh_t, u_int64_t);
+static void ofw_exit(void) __dead2;
+
+ofwh_t bootdevh;
+ofwh_t stdinh, stdouth;
+
+__asm(" \n\
+ .data \n\
+ .align 4 \n\
+stack: \n\
+ .space 16384 \n\
+ \n\
+ .text \n\
+ .globl _start \n\
+_start: \n\
+ lis %r1,stack@ha \n\
+ addi %r1,%r1,stack@l \n\
+ addi %r1,%r1,8192 \n\
+ \n\
+ b ofw_init \n\
+");
+
+void
+ofw_init(void *vpd, int res, int (*openfirm)(void *), char *arg, int argl)
+{
+ char *av[16];
+ char *p;
+ int ac;
+
+ ofw = openfirm;
+
+ chosenh = ofw_finddevice("/chosen");
+ ofw_getprop(chosenh, "stdin", &stdinh, sizeof(stdinh));
+ ofw_getprop(chosenh, "stdout", &stdouth, sizeof(stdouth));
+ ofw_getprop(chosenh, "bootargs", bootargs, sizeof(bootargs));
+ ofw_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath));
+
+ bootargs[sizeof(bootargs) - 1] = '\0';
+ bootpath[sizeof(bootpath) - 1] = '\0';
+
+ p = bootpath;
+ while (*p != '\0') {
+ /* Truncate partition ID */
+ if (*p == ':') {
+ ofw_close(bootdev);
+ *(++p) = '\0';
+ break;
+ }
+ p++;
+ }
+
+ ac = 0;
+ p = bootargs;
+ for (;;) {
+ while (*p == ' ' && *p != '\0')
+ p++;
+ if (*p == '\0' || ac >= 16)
+ break;
+ av[ac++] = p;
+ while (*p != ' ' && *p != '\0')
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+ }
+
+ exit(main(ac, av));
+}
+
+static ofwh_t
+ofw_finddevice(const char *name)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"finddevice",
+ 1,
+ 1,
+ (ofwcell_t)name,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_finddevice: name=\"%s\"\n", name);
+ return (1);
+ }
+ return (args[4]);
+}
+
+static int
+ofw_getprop(ofwh_t ofwh, const char *name, void *buf, size_t len)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"getprop",
+ 4,
+ 1,
+ (u_ofwh_t)ofwh,
+ (ofwcell_t)name,
+ (ofwcell_t)buf,
+ len,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_getprop: ofwh=0x%x buf=%p len=%u\n",
+ ofwh, buf, len);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+ofw_setprop(ofwh_t ofwh, const char *name, void *buf, size_t len)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"setprop",
+ 4,
+ 1,
+ (u_ofwh_t)ofwh,
+ (ofwcell_t)name,
+ (ofwcell_t)buf,
+ len,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_setprop: ofwh=0x%x buf=%p len=%u\n",
+ ofwh, buf, len);
+ return (1);
+ }
+ return (0);
+}
+
+static ofwh_t
+ofw_open(const char *path)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"open",
+ 1,
+ 1,
+ (ofwcell_t)path,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_open: path=\"%s\"\n", path);
+ return (-1);
+ }
+ return (args[4]);
+}
+
+static int
+ofw_close(ofwh_t devh)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"close",
+ 1,
+ 0,
+ (u_ofwh_t)devh
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_close: devh=0x%x\n", devh);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+ofw_claim(void *virt, size_t len, u_int align)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"claim",
+ 3,
+ 1,
+ (ofwcell_t)virt,
+ len,
+ align,
+ 0,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_claim: virt=%p len=%u\n", virt, len);
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+ofw_read(ofwh_t devh, void *buf, size_t len)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"read",
+ 3,
+ 1,
+ (u_ofwh_t)devh,
+ (ofwcell_t)buf,
+ len,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_read: devh=0x%x buf=%p len=%u\n", devh, buf, len);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+ofw_write(ofwh_t devh, const void *buf, size_t len)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"write",
+ 3,
+ 1,
+ (u_ofwh_t)devh,
+ (ofwcell_t)buf,
+ len,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_write: devh=0x%x buf=%p len=%u\n", devh, buf, len);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+ofw_seek(ofwh_t devh, u_int64_t off)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"seek",
+ 3,
+ 1,
+ (u_ofwh_t)devh,
+ off >> 32,
+ off,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_seek: devh=0x%x off=0x%lx\n", devh, off);
+ return (1);
+ }
+ return (0);
+}
+
+static void
+ofw_exit(void)
+{
+ ofwcell_t args[3];
+
+ args[0] = (ofwcell_t)"exit";
+ args[1] = 0;
+ args[2] = 0;
+
+ for (;;)
+ (*ofw)(args);
+}
+
+static void
+bcopy(const void *src, void *dst, size_t len)
+{
+ const char *s = src;
+ char *d = dst;
+
+ while (len-- != 0)
+ *d++ = *s++;
+}
+
+static void
+memcpy(void *dst, const void *src, size_t len)
+{
+ bcopy(src, dst, len);
+}
+
+static void
+bzero(void *b, size_t len)
+{
+ char *p = b;
+
+ while (len-- != 0)
+ *p++ = 0;
+}
+
+static int
+strcmp(const char *s1, const char *s2)
+{
+ for (; *s1 == *s2 && *s1; s1++, s2++)
+ ;
+ return ((u_char)*s1 - (u_char)*s2);
+}
+
+#include "ufsread.c"
+
+int
+main(int ac, char **av)
+{
+ const char *path;
+ char bootpath_full[255];
+ int i, len;
+
+ path = PATH_LOADER;
+ for (i = 0; i < ac; i++) {
+ switch (av[i][0]) {
+ case '-':
+ switch (av[i][1]) {
+ default:
+ usage();
+ }
+ break;
+ default:
+ path = av[i];
+ break;
+ }
+ }
+
+ printf(" \n>> FreeBSD/powerpc Open Firmware boot block\n"
+ " Boot path: %s\n"
+ " Boot loader: %s\n", bootpath, path);
+
+ len = 0;
+ while (bootpath[len] != '\0') len++;
+
+ memcpy(bootpath_full,bootpath,len+1);
+
+ if (bootpath_full[len-1] != ':') {
+ /* First try full volume */
+ if (domount(bootpath_full,1) == 0)
+ goto out;
+
+ /* Add a : so that we try partitions if that fails */
+ if (bootdev > 0)
+ ofw_close(bootdev);
+ bootpath_full[len] = ':';
+ len += 1;
+ }
+
+ /* Loop through first 16 partitions to find a UFS one */
+ for (i = 0; i < 16; i++) {
+ if (i < 10) {
+ bootpath_full[len] = i + '0';
+ bootpath_full[len+1] = '\0';
+ } else {
+ bootpath_full[len] = '1';
+ bootpath_full[len+1] = i - 10 + '0';
+ bootpath_full[len+2] = '\0';
+ }
+
+ if (domount(bootpath_full,1) >= 0)
+ break;
+
+ if (bootdev > 0)
+ ofw_close(bootdev);
+ }
+
+ if (i >= 16)
+ panic("domount");
+
+out:
+ printf(" Boot volume: %s\n",bootpath_full);
+ ofw_setprop(chosenh, "bootargs", bootpath_full, len+2);
+ load(path);
+ return (1);
+}
+
+static void
+usage(void)
+{
+
+ printf("usage: boot device [/path/to/loader]\n");
+ exit(1);
+}
+
+static void
+exit(int code)
+{
+
+ ofw_exit();
+}
+
+static struct dmadat __dmadat;
+
+static int
+domount(const char *device, int quiet)
+{
+
+ dmadat = &__dmadat;
+ if ((bootdev = ofw_open(device)) == -1) {
+ printf("domount: can't open device\n");
+ return (-1);
+ }
+ if (fsread(0, NULL, 0)) {
+ if (!quiet)
+ printf("domount: can't read superblock\n");
+ return (-1);
+ }
+ return (0);
+}
+
+static void
+load(const char *fname)
+{
+ Elf32_Ehdr eh;
+ Elf32_Phdr ph;
+ caddr_t p;
+ ufs_ino_t ino;
+ int i;
+
+ if ((ino = lookup(fname)) == 0) {
+ printf("File %s not found\n", fname);
+ return;
+ }
+ if (fsread(ino, &eh, sizeof(eh)) != sizeof(eh)) {
+ printf("Can't read elf header\n");
+ return;
+ }
+ if (!IS_ELF(eh)) {
+ printf("Not an ELF file\n");
+ return;
+ }
+ for (i = 0; i < eh.e_phnum; i++) {
+ fs_off = eh.e_phoff + i * eh.e_phentsize;
+ if (fsread(ino, &ph, sizeof(ph)) != sizeof(ph)) {
+ printf("Can't read program header %d\n", i);
+ return;
+ }
+ if (ph.p_type != PT_LOAD)
+ continue;
+ fs_off = ph.p_offset;
+ p = (caddr_t)ph.p_vaddr;
+ ofw_claim(p,(ph.p_filesz > ph.p_memsz) ?
+ ph.p_filesz : ph.p_memsz,0);
+ if (fsread(ino, p, ph.p_filesz) != ph.p_filesz) {
+ printf("Can't read content of section %d\n", i);
+ return;
+ }
+ if (ph.p_filesz != ph.p_memsz)
+ bzero(p + ph.p_filesz, ph.p_memsz - ph.p_filesz);
+ __syncicache(p, ph.p_memsz);
+ }
+ ofw_close(bootdev);
+ (*(void (*)(void *, int, ofwfp_t, char *, int))eh.e_entry)(NULL, 0,
+ ofw,NULL,0);
+}
+
+static int
+dskread(void *buf, u_int64_t lba, int nblk)
+{
+ /*
+ * The Open Firmware should open the correct partition for us.
+ * That means, if we read from offset zero on an open instance handle,
+ * we should read from offset zero of that partition.
+ */
+ ofw_seek(bootdev, lba * DEV_BSIZE);
+ ofw_read(bootdev, buf, nblk * DEV_BSIZE);
+ return (0);
+}
+
+static void
+panic(const char *fmt, ...)
+{
+ char buf[128];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof buf, fmt, ap);
+ printf("panic: %s\n", buf);
+ va_end(ap);
+
+ exit(1);
+}
+
+static int
+printf(const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vprintf(fmt, ap);
+ va_end(ap);
+ return (ret);
+}
+
+static int
+putchar(char c, void *arg)
+{
+ char buf;
+
+ if (c == '\n') {
+ buf = '\r';
+ ofw_write(stdouth, &buf, 1);
+ }
+ buf = c;
+ ofw_write(stdouth, &buf, 1);
+ return (1);
+}
+
+static int
+vprintf(const char *fmt, va_list ap)
+{
+ int ret;
+
+ ret = __printf(fmt, putchar, 0, ap);
+ return (ret);
+}
+
+static int
+vsnprintf(char *str, size_t sz, const char *fmt, va_list ap)
+{
+ struct sp_data sp;
+ int ret;
+
+ sp.sp_buf = str;
+ sp.sp_len = 0;
+ sp.sp_size = sz;
+ ret = __printf(fmt, __sputc, &sp, ap);
+ return (ret);
+}
+
+static int
+__printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap)
+{
+ char buf[(sizeof(long) * 8) + 1];
+ char *nbuf;
+ u_long ul;
+ u_int ui;
+ int lflag;
+ int sflag;
+ char *s;
+ int pad;
+ int ret;
+ int c;
+
+ nbuf = &buf[sizeof buf - 1];
+ ret = 0;
+ while ((c = *fmt++) != 0) {
+ if (c != '%') {
+ ret += putc(c, arg);
+ continue;
+ }
+ lflag = 0;
+ sflag = 0;
+ pad = 0;
+reswitch: c = *fmt++;
+ switch (c) {
+ case '#':
+ sflag = 1;
+ goto reswitch;
+ case '%':
+ ret += putc('%', arg);
+ break;
+ case 'c':
+ c = va_arg(ap, int);
+ ret += putc(c, arg);
+ break;
+ case 'd':
+ if (lflag == 0) {
+ ui = (u_int)va_arg(ap, int);
+ if (ui < (int)ui) {
+ ui = -ui;
+ ret += putc('-', arg);
+ }
+ s = __uitoa(nbuf, ui, 10);
+ } else {
+ ul = (u_long)va_arg(ap, long);
+ if (ul < (long)ul) {
+ ul = -ul;
+ ret += putc('-', arg);
+ }
+ s = __ultoa(nbuf, ul, 10);
+ }
+ ret += __puts(s, putc, arg);
+ break;
+ case 'l':
+ lflag = 1;
+ goto reswitch;
+ case 'o':
+ if (lflag == 0) {
+ ui = (u_int)va_arg(ap, u_int);
+ s = __uitoa(nbuf, ui, 8);
+ } else {
+ ul = (u_long)va_arg(ap, u_long);
+ s = __ultoa(nbuf, ul, 8);
+ }
+ ret += __puts(s, putc, arg);
+ break;
+ case 'p':
+ ul = (u_long)va_arg(ap, void *);
+ s = __ultoa(nbuf, ul, 16);
+ ret += __puts("0x", putc, arg);
+ ret += __puts(s, putc, arg);
+ break;
+ case 's':
+ s = va_arg(ap, char *);
+ ret += __puts(s, putc, arg);
+ break;
+ case 'u':
+ if (lflag == 0) {
+ ui = va_arg(ap, u_int);
+ s = __uitoa(nbuf, ui, 10);
+ } else {
+ ul = va_arg(ap, u_long);
+ s = __ultoa(nbuf, ul, 10);
+ }
+ ret += __puts(s, putc, arg);
+ break;
+ case 'x':
+ if (lflag == 0) {
+ ui = va_arg(ap, u_int);
+ s = __uitoa(nbuf, ui, 16);
+ } else {
+ ul = va_arg(ap, u_long);
+ s = __ultoa(nbuf, ul, 16);
+ }
+ if (sflag)
+ ret += __puts("0x", putc, arg);
+ ret += __puts(s, putc, arg);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ pad = pad * 10 + c - '0';
+ goto reswitch;
+ default:
+ break;
+ }
+ }
+ return (ret);
+}
+
+static int
+__sputc(char c, void *arg)
+{
+ struct sp_data *sp;
+
+ sp = arg;
+ if (sp->sp_len < sp->sp_size)
+ sp->sp_buf[sp->sp_len++] = c;
+ sp->sp_buf[sp->sp_len] = '\0';
+ return (1);
+}
+
+static int
+__puts(const char *s, putc_func_t *putc, void *arg)
+{
+ const char *p;
+ int ret;
+
+ ret = 0;
+ for (p = s; *p != '\0'; p++)
+ ret += putc(*p, arg);
+ return (ret);
+}
+
+static char *
+__uitoa(char *buf, u_int ui, int base)
+{
+ char *p;
+
+ p = buf;
+ *p = '\0';
+ do
+ *--p = digits[ui % base];
+ while ((ui /= base) != 0);
+ return (p);
+}
+
+static char *
+__ultoa(char *buf, u_long ul, int base)
+{
+ char *p;
+
+ p = buf;
+ *p = '\0';
+ do
+ *--p = digits[ul % base];
+ while ((ul /= base) != 0);
+ return (p);
+}
diff --git a/stand/powerpc/boot1.chrp/bootinfo.txt b/stand/powerpc/boot1.chrp/bootinfo.txt
new file mode 100644
index 0000000..61cf007
--- /dev/null
+++ b/stand/powerpc/boot1.chrp/bootinfo.txt
@@ -0,0 +1,14 @@
+<CHRP-BOOT>
+<DESCRIPTION>FreeBSD/powerpc bootloader</DESCRIPTION>
+<OS-NAME>FreeBSD</OS-NAME>
+<VERSION> $FreeBSD$ </VERSION>
+
+<COMPATIBLE>
+MacRISC MacRISC3 MacRISC4
+</COMPATIBLE>
+<BOOT-SCRIPT>
+" screen" output
+boot &device;:&partition;,\ppc\boot1.elf
+</BOOT-SCRIPT>
+</CHRP-BOOT>
+
diff --git a/stand/powerpc/boot1.chrp/generate-hfs.sh b/stand/powerpc/boot1.chrp/generate-hfs.sh
new file mode 100755
index 0000000..ef368d1
--- /dev/null
+++ b/stand/powerpc/boot1.chrp/generate-hfs.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+# This script generates the dummy HFS filesystem used for the PowerPC boot
+# blocks. It uses hfsutils (emulators/hfsutils) to generate a template
+# filesystem with the relevant interesting files. These are then found by
+# grep, and the offsets written to a Makefile snippet.
+#
+# Because of licensing concerns, and because it is overkill, we do not
+# distribute hfsutils as a build tool. If you need to regenerate the HFS
+# template (e.g. because the boot block or the CHRP script have grown),
+# you must install it from ports.
+
+# $FreeBSD$
+
+HFS_SIZE=1600 #Size in 512-byte blocks of the produced image
+
+CHRPBOOT_SIZE=2k
+BOOT1_SIZE=64k
+
+# Generate 800K HFS image
+OUTPUT_FILE=hfs.tmpl
+
+dd if=/dev/zero of=$OUTPUT_FILE bs=512 count=$HFS_SIZE
+hformat -l "FreeBSD Bootstrap" $OUTPUT_FILE
+hmount $OUTPUT_FILE
+
+# Create and bless a directory for the boot loader
+hmkdir ppc
+hattrib -b ppc
+hcd ppc
+
+# Make two dummy files for the CHRP boot script and boot1
+echo 'Bootinfo START' | dd of=bootinfo.txt.tmp cbs=$CHRPBOOT_SIZE count=1 conv=block
+echo 'Boot1 START' | dd of=boot1.elf.tmp cbs=$BOOT1_SIZE count=1 conv=block
+
+hcopy boot1.elf.tmp :boot1.elf
+hcopy bootinfo.txt.tmp :bootinfo.txt
+hattrib -c chrp -t tbxi bootinfo.txt
+humount
+
+rm bootinfo.txt.tmp
+rm boot1.elf.tmp
+
+# Locate the offsets of the two fake files
+BOOTINFO_OFFSET=$(hd $OUTPUT_FILE | grep 'Bootinfo START' | cut -f 1 -d ' ')
+BOOT1_OFFSET=$(hd $OUTPUT_FILE | grep 'Boot1 START' | cut -f 1 -d ' ')
+
+# Convert to numbers of blocks
+BOOTINFO_OFFSET=$(echo 0x$BOOTINFO_OFFSET | awk '{printf("%x\n",$1/512);}')
+BOOT1_OFFSET=$(echo 0x$BOOT1_OFFSET | awk '{printf("%x\n",$1/512);}')
+
+echo '# This file autogenerated by generate-hfs.sh - DO NOT EDIT' > Makefile.hfs
+echo '# $FreeBSD$' >> Makefile.hfs
+echo "BOOTINFO_OFFSET=0x$BOOTINFO_OFFSET" >> Makefile.hfs
+echo "BOOT1_OFFSET=0x$BOOT1_OFFSET" >> Makefile.hfs
+
+bzip2 $OUTPUT_FILE
+echo 'HFS template boot filesystem created by generate-hfs.sh' > $OUTPUT_FILE.bz2.uu
+echo 'DO NOT EDIT' >> $OUTPUT_FILE.bz2.uu
+echo '$FreeBSD$' >> $OUTPUT_FILE.bz2.uu
+
+uuencode $OUTPUT_FILE.bz2 $OUTPUT_FILE.bz2 >> $OUTPUT_FILE.bz2.uu
+rm $OUTPUT_FILE.bz2
+
diff --git a/stand/powerpc/boot1.chrp/hfs.tmpl.bz2.uu b/stand/powerpc/boot1.chrp/hfs.tmpl.bz2.uu
new file mode 100644
index 0000000..20b7695
--- /dev/null
+++ b/stand/powerpc/boot1.chrp/hfs.tmpl.bz2.uu
@@ -0,0 +1,18 @@
+HFS template boot filesystem created by generate-hfs.sh
+DO NOT EDIT
+$FreeBSD$
+begin 644 hfs.tmpl.bz2
+M0EIH.3%!629365^MV6L``"]__O___M)20>!0O2$>0#MUW$!$``%%$``@``!`
+M!`!R2<`![@H2"2A2-Z)-HC]4`:`-``T:&@&@#0/TTH<`PC":8A@$`R`&$:9,
+MF$8"&AP#",)IB&`0#(`81IDR81@(:")0HT*>4,:0TT`:!ZC0/*/4/1&@;4>H
+M&@RYG-FL-,\=C91FTSS@99`O+OA;*$ZN3-&UF`W@#SP:;MVF_EN,-]P$ZN2B
+M=";7YHA7VT!#<@,B`H*?S#?Q;CUJ8H8+9:E)+4"L7'CL5&D.IO4;H98%^4@[
+M9`*L&1``5*0#A(E<M\J&(@6BZXC<0K!#!)!H05`5*L!^DNM12_"L`<9!Y.7T
+M<I94[[MT$^$30P-DYBVI.X7R<KY7&H$DA(<S;NW1PB7-47*Q>B8BH`S'1O^N
+M)Y6'!#N:*:<E!+,#/,\;M_FXRP![+P44D6],L'G1X7Q&[JI6WN@&KCIW8:".
+MTP=Y77`%67""`=I/UR500#!=DD+!>JYTEX\:%'"?.G$FCP8TM_0/ND`^IG3[
+M.QUEDP&P>3+Z442Q!:S.A1>00]'X'D%)UX9QZ>$Q?(808QB-4%9BBTL"L(BB
+MEJ*'HH820Q$'%G':JF.:>$U4H`0S`:GSZM-C5BNX(2&$B"J*45`"4FEZ__%W
+))%.%"07ZW9:P
+`
+end
diff --git a/stand/powerpc/kboot/Makefile b/stand/powerpc/kboot/Makefile
new file mode 100644
index 0000000..bed586e
--- /dev/null
+++ b/stand/powerpc/kboot/Makefile
@@ -0,0 +1,50 @@
+# $FreeBSD$
+
+LOADER_CD9660_SUPPORT?= yes
+LOADER_MSDOS_SUPPORT?= no
+LOADER_EXT2FS_SUPPORT?= yes
+LOADER_UFS_SUPPORT?= yes
+LOADER_NET_SUPPORT?= yes
+LOADER_NFS_SUPPORT?= yes
+LOADER_TFTP_SUPPORT?= no
+LOADER_GZIP_SUPPORT?= yes
+LOADER_BZIP2_SUPPORT?= no
+
+.include <bsd.init.mk>
+MK_SSP= no
+MAN=
+
+PROG= loader.kboot
+NEWVERSWHAT= "kboot loader" ${MACHINE_ARCH}
+INSTALLFLAGS= -b
+
+# Architecture-specific loader code
+SRCS= conf.c metadata.c vers.c main.c ppc64_elf_freebsd.c
+SRCS+= host_syscall.S hostcons.c hostdisk.c kerneltramp.S kbootfdt.c
+SRCS+= ucmpdi2.c
+
+.include "${BOOTSRC}/fdt.mk"
+
+CFLAGS+= -mcpu=powerpc64
+
+# Always add MI sources
+HELP_FILES= # Disable
+.include "${BOOTSRC}/loader.mk"
+.PATH: ${SYSDIR}/libkern
+
+CFLAGS+= -Wall -ffreestanding -msoft-float -DAIM
+# load address. set in linker script
+RELOC?= 0x0
+CFLAGS+= -DRELOC=${RELOC}
+
+LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.powerpc
+
+# 64-bit bridge extensions
+CFLAGS+= -Wa,-mppc64bridge
+
+DPADD= ${LIBFICL} ${LIBOFW} ${LIBFDT} ${LIBSA}
+LDADD= ${LIBFICL} ${LIBOFW} ${LIBFDT} ${LIBSA}
+
+HELP_FILES+= ${FDTSRC}/help.fdt
+
+.include <bsd.prog.mk>
diff --git a/stand/powerpc/kboot/conf.c b/stand/powerpc/kboot/conf.c
new file mode 100644
index 0000000..104dd93
--- /dev/null
+++ b/stand/powerpc/kboot/conf.c
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (C) 1999 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "bootstrap.h"
+
+#if defined(LOADER_NET_SUPPORT)
+#include "dev_net.h"
+#endif
+
+extern struct devsw hostdisk;
+
+/*
+ * We could use linker sets for some or all of these, but
+ * then we would have to control what ended up linked into
+ * the bootstrap. So it's easier to conditionalise things
+ * here.
+ *
+ * XXX rename these arrays to be consistent and less namespace-hostile
+ */
+
+/* Exported for libstand */
+struct devsw *devsw[] = {
+#if defined(LOADER_DISK_SUPPORT) || defined(LOADER_CD9660_SUPPORT)
+ &hostdisk,
+#endif
+#if defined(LOADER_NET_SUPPORT)
+ &netdev,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#if defined(LOADER_UFS_SUPPORT)
+ &ufs_fsops,
+#endif
+#if defined(LOADER_CD9660_SUPPORT)
+ &cd9660_fsops,
+#endif
+#if defined(LOADER_EXT2FS_SUPPORT)
+ &ext2fs_fsops,
+#endif
+#if defined(LOADER_NFS_SUPPORT)
+ &nfs_fsops,
+#endif
+#if defined(LOADER_TFTP_SUPPORT)
+ &tftp_fsops,
+#endif
+#if defined(LOADER_GZIP_SUPPORT)
+ &gzipfs_fsops,
+#endif
+#if defined(LOADER_BZIP2_SUPPORT)
+ &bzipfs_fsops,
+#endif
+ NULL
+};
+
+extern struct netif_driver kbootnet;
+
+struct netif_driver *netif_drivers[] = {
+#if 0 /* XXX */
+#if defined(LOADER_NET_SUPPORT)
+ &kbootnet,
+#endif
+#endif
+ NULL,
+};
+
+/* Exported for PowerPC only */
+/*
+ * Sort formats so that those that can detect based on arguments
+ * rather than reading the file go first.
+ */
+
+extern struct file_format ppc_elf64;
+
+struct file_format *file_formats[] = {
+ &ppc_elf64,
+ NULL
+};
+
+/*
+ * Consoles
+ */
+extern struct console hostconsole;
+
+struct console *consoles[] = {
+ &hostconsole,
+ NULL
+};
+
diff --git a/stand/powerpc/kboot/host_syscall.S b/stand/powerpc/kboot/host_syscall.S
new file mode 100644
index 0000000..3607fdb
--- /dev/null
+++ b/stand/powerpc/kboot/host_syscall.S
@@ -0,0 +1,75 @@
+/*
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+ENTRY(host_read)
+ li %r0, 3 # SYS_read
+ sc
+ bso 1f
+ blr
+1:
+ li %r3, 0
+ blr
+
+
+ENTRY(host_write)
+ li %r0, 4 # SYS_write
+ sc
+ blr
+
+ENTRY(host_seek)
+ mr %r4,%r5
+ mr %r5,%r6
+ mr %r6,%r7
+ li %r0, 140 # SYS_llseek
+ sc
+ blr
+
+ENTRY(host_open)
+ li %r0, 5 # SYS_open
+ sc
+ bso 1f
+ blr
+1:
+ li %r3, 0
+ blr
+
+ENTRY(host_close)
+ li %r0, 6 # SYS_close
+ sc
+ blr
+
+ENTRY(host_mmap)
+ li %r0, 90 # SYS_mmap
+ sc
+ blr
+
+ENTRY(host_gettimeofday)
+ li %r0, 78 # SYS_gettimeofday
+ sc
+ blr
+
+ENTRY(host_select)
+ li %r0, 142 # SYS_select
+ sc
+ blr
+
+ENTRY(kexec_load)
+ lis %r6,21 # KEXEC_ARCH_PPC64
+ li %r0,268 # __NR_kexec_load
+ sc
+ blr
+
+ENTRY(host_reboot)
+ li %r0,88 # SYS_reboot
+ sc
+ blr
+
+ENTRY(host_getdents)
+ li %r0,141 # SYS_getdents
+ sc
+ blr
+
diff --git a/stand/powerpc/kboot/host_syscall.h b/stand/powerpc/kboot/host_syscall.h
new file mode 100644
index 0000000..0d47bd5
--- /dev/null
+++ b/stand/powerpc/kboot/host_syscall.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (C) 2014 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _HOST_SYSCALL_H
+#define _HOST_SYSCALL_H
+
+#include <stand.h>
+
+ssize_t host_read(int fd, void *buf, size_t nbyte);
+ssize_t host_write(int fd, const void *buf, size_t nbyte);
+ssize_t host_seek(int fd, int64_t offset, int whence);
+int host_open(char *path, int flags, int mode);
+int host_close(int fd);
+void *host_mmap(void *addr, size_t len, int prot, int flags, int fd, int);
+#define host_getmem(size) host_mmap(0, size, 3 /* RW */, 0x22 /* ANON */, -1, 0);
+struct host_timeval {
+ int tv_sec;
+ int tv_usec;
+};
+int host_gettimeofday(struct host_timeval *a, void *b);
+int host_select(int nfds, long *readfds, long *writefds, long *exceptfds,
+ struct host_timeval *timeout);
+int kexec_load(vm_offset_t start, int nsegs, void *segs);
+int host_reboot(int, int, int, void *);
+int host_getdents(int fd, void *dirp, int count);
+
+#endif
diff --git a/stand/powerpc/kboot/hostcons.c b/stand/powerpc/kboot/hostcons.c
new file mode 100644
index 0000000..31dceb0
--- /dev/null
+++ b/stand/powerpc/kboot/hostcons.c
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (C) 2014 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include "bootstrap.h"
+#include "host_syscall.h"
+
+static void hostcons_probe(struct console *cp);
+static int hostcons_init(int arg);
+static void hostcons_putchar(int c);
+static int hostcons_getchar();
+static int hostcons_poll();
+
+struct console hostconsole = {
+ "host",
+ "Host Console",
+ 0,
+ hostcons_probe,
+ hostcons_init,
+ hostcons_putchar,
+ hostcons_getchar,
+ hostcons_poll,
+};
+
+static void
+hostcons_probe(struct console *cp)
+{
+
+ cp->c_flags |= C_PRESENTIN|C_PRESENTOUT;
+}
+
+static int
+hostcons_init(int arg)
+{
+
+ /* XXX: set nonblocking */
+ /* tcsetattr(~(ICANON | ECHO)) */
+
+ return (0);
+}
+
+static void
+hostcons_putchar(int c)
+{
+ uint8_t ch = c;
+
+ host_write(1, &ch, 1);
+}
+
+static int
+hostcons_getchar()
+{
+ uint8_t ch;
+ int rv;
+
+ rv = host_read(0, &ch, 1);
+ if (rv == 1)
+ return (ch);
+ return (-1);
+}
+
+static int
+hostcons_poll()
+{
+ struct host_timeval tv = {0,0};
+ long fds = 1 << 0;
+ int ret;
+
+ ret = host_select(32, &fds, NULL, NULL, &tv);
+ return (ret > 0);
+}
+
diff --git a/stand/powerpc/kboot/hostdisk.c b/stand/powerpc/kboot/hostdisk.c
new file mode 100644
index 0000000..22f5406
--- /dev/null
+++ b/stand/powerpc/kboot/hostdisk.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (C) 2014 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include "bootstrap.h"
+#include "host_syscall.h"
+
+static int hostdisk_init(void);
+static int hostdisk_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int hostdisk_open(struct open_file *f, ...);
+static int hostdisk_close(struct open_file *f);
+static int hostdisk_ioctl(struct open_file *f, u_long cmd, void *data);
+static int hostdisk_print(int verbose);
+
+struct devsw hostdisk = {
+ "/dev",
+ DEVT_DISK,
+ hostdisk_init,
+ hostdisk_strategy,
+ hostdisk_open,
+ hostdisk_close,
+ hostdisk_ioctl,
+ hostdisk_print,
+};
+
+static int
+hostdisk_init(void)
+{
+
+ return (0);
+}
+
+static int
+hostdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct devdesc *desc = devdata;
+ daddr_t pos;
+ int n;
+
+ pos = dblk * 512;
+
+ if (host_seek(desc->d_unit, pos, 0) < 0) {
+ printf("Seek error\n");
+ return (EIO);
+ }
+ n = host_read(desc->d_unit, buf, size);
+
+ if (n < 0)
+ return (EIO);
+
+ *rsize = n;
+ return (0);
+}
+
+static int
+hostdisk_open(struct open_file *f, ...)
+{
+ struct devdesc *desc;
+ va_list vl;
+
+ va_start(vl, f);
+ desc = va_arg(vl, struct devdesc *);
+ va_end(vl);
+
+ desc->d_unit = host_open(desc->d_opendata, O_RDONLY, 0);
+
+ if (desc->d_unit <= 0) {
+ printf("hostdisk_open: couldn't open %s: %d\n",
+ desc->d_opendata, desc->d_unit);
+ return (ENOENT);
+ }
+
+ return (0);
+}
+
+static int
+hostdisk_close(struct open_file *f)
+{
+ struct devdesc *desc = f->f_devdata;
+
+ host_close(desc->d_unit);
+ return (0);
+}
+
+static int
+hostdisk_ioctl(struct open_file *f, u_long cmd, void *data)
+{
+
+ return (EINVAL);
+}
+
+static int
+hostdisk_print(int verbose)
+{
+ return (0);
+}
+
diff --git a/stand/powerpc/kboot/kbootfdt.c b/stand/powerpc/kboot/kbootfdt.c
new file mode 100644
index 0000000..5ab3c3d
--- /dev/null
+++ b/stand/powerpc/kboot/kbootfdt.c
@@ -0,0 +1,184 @@
+/*-
+ * Copyright (C) 2014 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <fdt_platform.h>
+#include <libfdt.h>
+#include "bootstrap.h"
+#include "host_syscall.h"
+
+static void
+add_node_to_fdt(void *buffer, const char *path, int fdt_offset)
+{
+ int child_offset, fd, pfd, error, dentsize;
+ char subpath[512];
+ void *propbuf;
+ ssize_t proplen;
+
+ struct host_dent {
+ unsigned long d_fileno;
+ unsigned long d_off;
+ unsigned short d_reclen;
+ char d_name[];
+ /* uint8_t d_type; */
+ };
+ char dents[2048];
+ struct host_dent *dent;
+ int d_type;
+
+ fd = host_open(path, O_RDONLY, 0);
+ while (1) {
+ dentsize = host_getdents(fd, dents, sizeof(dents));
+ if (dentsize <= 0)
+ break;
+ for (dent = (struct host_dent *)dents;
+ (char *)dent < dents + dentsize;
+ dent = (struct host_dent *)((void *)dent + dent->d_reclen)) {
+ sprintf(subpath, "%s/%s", path, dent->d_name);
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+ d_type = *((char *)(dent) + dent->d_reclen - 1);
+ if (d_type == 4 /* DT_DIR */) {
+ child_offset = fdt_add_subnode(buffer, fdt_offset,
+ dent->d_name);
+ if (child_offset < 0) {
+ printf("Error %d adding node %s/%s, skipping\n",
+ child_offset, path, dent->d_name);
+ continue;
+ }
+
+ add_node_to_fdt(buffer, subpath, child_offset);
+ } else {
+ propbuf = malloc(1024);
+ proplen = 0;
+ pfd = host_open(subpath, O_RDONLY, 0);
+ if (pfd > 0) {
+ proplen = host_read(pfd, propbuf, 1024);
+ host_close(pfd);
+ }
+ error = fdt_setprop(buffer, fdt_offset, dent->d_name,
+ propbuf, proplen);
+ free(propbuf);
+ if (error)
+ printf("Error %d adding property %s to "
+ "node %d\n", error, dent->d_name,
+ fdt_offset);
+ }
+ }
+ }
+
+ host_close(fd);
+}
+
+/* Fix up wrong values added to the device tree by prom_init() in Linux */
+
+static void
+fdt_linux_fixups(void *fdtp)
+{
+ int offset, len;
+ const void *prop;
+
+ /*
+ * Remove /memory/available properties, which reflect long-gone OF
+ * state
+ */
+
+ offset = fdt_path_offset(fdtp, "/memory@0");
+ if (offset > 0)
+ fdt_delprop(fdtp, offset, "available");
+
+ /*
+ * Add reservations for OPAL and RTAS state if present
+ */
+
+ offset = fdt_path_offset(fdtp, "/ibm,opal");
+ if (offset > 0) {
+ uint64_t *base, *size;
+ base = fdt_getprop(fdtp, offset, "opal-base-address",
+ &len);
+ size = fdt_getprop(fdtp, offset, "opal-runtime-size",
+ &len);
+ if (base != NULL && size != NULL)
+ fdt_add_mem_rsv(fdtp, fdt64_to_cpu(*base),
+ fdt64_to_cpu(*size));
+ }
+ offset = fdt_path_offset(fdtp, "/rtas");
+ if (offset > 0) {
+ uint32_t *base, *size;
+ base = fdt_getprop(fdtp, offset, "linux,rtas-base", &len);
+ size = fdt_getprop(fdtp, offset, "rtas-size", &len);
+ if (base != NULL && size != NULL)
+ fdt_add_mem_rsv(fdtp, fdt32_to_cpu(*base),
+ fdt32_to_cpu(*size));
+ }
+
+ /*
+ * Patch up /chosen nodes so that the stored handles mean something,
+ * where possible.
+ */
+ offset = fdt_path_offset(fdtp, "/chosen");
+ if (offset > 0) {
+ fdt_delprop(fdtp, offset, "cpu"); /* This node not meaningful */
+
+ offset = fdt_path_offset(fdtp, "/chosen");
+ prop = fdt_getprop(fdtp, offset, "linux,stdout-package", &len);
+ if (prop != NULL) {
+ fdt_setprop(fdtp, offset, "stdout", prop, len);
+ offset = fdt_path_offset(fdtp, "/chosen");
+ fdt_setprop(fdtp, offset, "stdin", prop, len);
+ }
+ }
+}
+
+int
+fdt_platform_load_dtb(void)
+{
+ void *buffer;
+ size_t buflen = 409600;
+
+ buffer = malloc(buflen);
+ fdt_create_empty_tree(buffer, buflen);
+ add_node_to_fdt(buffer, "/proc/device-tree",
+ fdt_path_offset(buffer, "/"));
+ fdt_linux_fixups(buffer);
+
+ fdt_pack(buffer);
+
+ fdt_load_dtb_addr(buffer);
+ free(buffer);
+
+ return (0);
+}
+
+void
+fdt_platform_fixups(void)
+{
+
+}
+
diff --git a/stand/powerpc/kboot/kerneltramp.S b/stand/powerpc/kboot/kerneltramp.S
new file mode 100644
index 0000000..a394c95
--- /dev/null
+++ b/stand/powerpc/kboot/kerneltramp.S
@@ -0,0 +1,55 @@
+/*
+ * This is the analog to the kexec "purgatory" code
+ *
+ * The goal here is to call the actual kernel entry point with the arguments it
+ * expects when kexec calls into it with no arguments. The value of the kernel
+ * entry point and arguments r3-r7 are copied into the trampoline text (which
+ * can be executed from any address) at bytes 8-32. kexec begins execution
+ * of APs at 0x60 bytes past the entry point, executing in a copy relocated
+ * to the absolute address 0x60. Here we implement a loop waiting on the release
+ * of a lock by the kernel at 0x40.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+ .globl CNAME(kerneltramp),CNAME(szkerneltramp)
+CNAME(kerneltramp):
+ mflr %r9
+ bl 2f
+ .space 24 /* branch address, r3-r7 */
+
+. = kerneltramp + 0x40 /* AP spinlock */
+ .long 0
+
+. = kerneltramp + 0x60 /* AP entry point */
+ li %r3,0x40
+1: lwz %r1,0(%r3)
+ cmpwi %r1,0
+ beq 1b
+
+ /* Jump into CPU reset */
+ li %r0,0x100
+ icbi 0,%r0
+ isync
+ sync
+ ba 0x100
+
+2: /* Continuation of kerneltramp */
+ mflr %r8
+ mtlr %r9
+ lwz %r3,0(%r8)
+ mtctr %r3
+ lwz %r3,4(%r8)
+ lwz %r4,8(%r8)
+ lwz %r5,12(%r8)
+ lwz %r6,16(%r8)
+ lwz %r7,20(%r8)
+ bctr
+
+endkerneltramp:
+
+ .data
+CNAME(szkerneltramp):
+ .long endkerneltramp - CNAME(kerneltramp)
diff --git a/stand/powerpc/kboot/ldscript.powerpc b/stand/powerpc/kboot/ldscript.powerpc
new file mode 100644
index 0000000..7291136
--- /dev/null
+++ b/stand/powerpc/kboot/ldscript.powerpc
@@ -0,0 +1,111 @@
+/* $FreeBSD: user/nwhitehorn/kboot/powerpc/kboot/ldscript.powerpc 272888 2014-10-10 06:24:09Z bapt $ */
+
+OUTPUT_FORMAT("elf32-powerpc-freebsd", "elf32-powerpc-freebsd",
+ "elf32-powerpc-freebsd")
+OUTPUT_ARCH(powerpc:common)
+ENTRY(_start)
+SEARCH_DIR(/usr/lib);
+PROVIDE (__stack = 0);
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x100000;
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rela.got : { *(.rela.got) }
+ .rela.got1 : { *(.rela.got1) }
+ .rela.got2 : { *(.rela.got2) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rela.init : { *(.rela.init) }
+ .rela.fini : { *(.rela.fini) }
+ .rela.bss : { *(.rela.bss) }
+ .rela.plt : { *(.rela.plt) }
+ .rela.sbss : { *(.rela.sbss) }
+ .rela.sbss2 : { *(.rela.sbss2) }
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ PROVIDE (etext = .);
+ .init : { *(.init) } =0
+ .fini : { *(.fini) } =0
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .sbss2 : { *(.sbss2) }
+ /* Adjust the address for the data segment to the next page up. */
+ . = ((. + 0x1000) & ~(0x1000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .got1 : { *(.got1) }
+ .dynamic : { *(.dynamic) }
+ /* Put .ctors and .dtors next to the .got2 section, so that the pointers
+ get relocated with -mrelocatable. Also put in the .fixup pointers.
+ The current compiler no longer needs this, but keep it around for 2.7.2 */
+ PROVIDE (_GOT2_START_ = .);
+ .got2 : { *(.got2) }
+ PROVIDE (__CTOR_LIST__ = .);
+ .ctors : { *(.ctors) }
+ PROVIDE (__CTOR_END__ = .);
+ PROVIDE (__DTOR_LIST__ = .);
+ .dtors : { *(.dtors) }
+ PROVIDE (__DTOR_END__ = .);
+ PROVIDE (_FIXUP_START_ = .);
+ .fixup : { *(.fixup) }
+ PROVIDE (_FIXUP_END_ = .);
+ PROVIDE (_GOT2_END_ = .);
+ PROVIDE (_GOT_START_ = .);
+ .got : { *(.got) }
+ .got.plt : { *(.got.plt) }
+ PROVIDE (_GOT_END_ = .);
+ _edata = .;
+ PROVIDE (edata = .);
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ *(.sbss)
+ *(.scommon)
+ *(.dynsbss)
+ PROVIDE (__sbss_end = .);
+ }
+ .plt : { *(.plt) }
+ .bss :
+ {
+ PROVIDE (__bss_start = .);
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ . = ALIGN(4096);
+ _end = . ;
+ PROVIDE (end = .);
+}
+
diff --git a/stand/powerpc/kboot/main.c b/stand/powerpc/kboot/main.c
new file mode 100644
index 0000000..7a24c16
--- /dev/null
+++ b/stand/powerpc/kboot/main.c
@@ -0,0 +1,319 @@
+/*-
+ * Copyright (C) 2010-2014 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <fdt_platform.h>
+
+#define _KERNEL
+#include <machine/cpufunc.h>
+#include "bootstrap.h"
+#include "host_syscall.h"
+
+struct arch_switch archsw;
+extern void *_end;
+
+extern char bootprog_info[];
+
+int kboot_getdev(void **vdev, const char *devspec, const char **path);
+ssize_t kboot_copyin(const void *src, vm_offset_t dest, const size_t len);
+ssize_t kboot_copyout(vm_offset_t src, void *dest, const size_t len);
+ssize_t kboot_readin(const int fd, vm_offset_t dest, const size_t len);
+int kboot_autoload(void);
+uint64_t kboot_loadaddr(u_int type, void *data, uint64_t addr);
+int kboot_setcurrdev(struct env_var *ev, int flags, const void *value);
+
+extern int command_fdt_internal(int argc, char *argv[]);
+
+int
+kboot_getdev(void **vdev, const char *devspec, const char **path)
+{
+ int i;
+ const char *devpath, *filepath;
+ struct devsw *dv;
+ struct devdesc *desc;
+
+ if (strchr(devspec, ':') != NULL) {
+ devpath = devspec;
+ filepath = strchr(devspec, ':') + 1;
+ } else {
+ devpath = getenv("currdev");
+ filepath = devspec;
+ }
+
+ for (i = 0; (dv = devsw[i]) != NULL; i++) {
+ if (strncmp(dv->dv_name, devpath, strlen(dv->dv_name)) == 0)
+ goto found;
+ }
+ return (ENOENT);
+
+found:
+ if (path != NULL && filepath != NULL)
+ *path = filepath;
+ else if (path != NULL)
+ *path = strchr(devspec, ':') + 1;
+
+ if (vdev != NULL) {
+ desc = malloc(sizeof(*desc));
+ desc->d_dev = dv;
+ desc->d_unit = 0;
+ desc->d_opendata = strdup(devpath);
+ *vdev = desc;
+ }
+
+ return (0);
+}
+
+int
+main(int argc, const char **argv)
+{
+ void *heapbase;
+ const size_t heapsize = 15*1024*1024;
+ const char *bootdev = argv[1];
+
+ /*
+ * Set the heap to one page after the end of the loader.
+ */
+ heapbase = host_getmem(heapsize);
+ setheap(heapbase, heapbase + heapsize);
+
+ /*
+ * Set up console.
+ */
+ cons_probe();
+
+ printf("Boot device: %s\n", bootdev);
+
+ archsw.arch_getdev = kboot_getdev;
+ archsw.arch_copyin = kboot_copyin;
+ archsw.arch_copyout = kboot_copyout;
+ archsw.arch_readin = kboot_readin;
+ archsw.arch_autoload = kboot_autoload;
+ archsw.arch_loadaddr = kboot_loadaddr;
+
+ printf("\n%s", bootprog_info);
+
+ setenv("currdev", bootdev, 1);
+ setenv("loaddev", bootdev, 1);
+ setenv("LINES", "24", 1);
+
+ interact(NULL); /* doesn't return */
+
+ return (0);
+}
+
+void
+exit(int code)
+{
+ /* XXX: host_exit */
+}
+
+void
+delay(int usecs)
+{
+ struct host_timeval tvi, tv;
+ uint64_t ti, t;
+ host_gettimeofday(&tvi, NULL);
+ ti = tvi.tv_sec*1000000 + tvi.tv_usec;
+ do {
+ host_gettimeofday(&tv, NULL);
+ t = tv.tv_sec*1000000 + tv.tv_usec;
+ } while (t < ti + usecs);
+}
+
+time_t
+getsecs(void)
+{
+ struct host_timeval tv;
+ host_gettimeofday(&tv, NULL);
+ return (tv.tv_sec);
+}
+
+time_t
+time(time_t *tloc)
+{
+ time_t rv;
+
+ rv = getsecs();
+ if (tloc != NULL)
+ *tloc = rv;
+
+ return (rv);
+}
+
+struct kexec_segment {
+ void *buf;
+ int bufsz;
+ void *mem;
+ int memsz;
+};
+
+struct kexec_segment loaded_segments[128];
+int nkexec_segments = 0;
+
+static ssize_t
+get_phys_buffer(vm_offset_t dest, const size_t len, void **buf)
+{
+ int i = 0;
+ const size_t segsize = 2*1024*1024;
+
+ for (i = 0; i < nkexec_segments; i++) {
+ if (dest >= (vm_offset_t)loaded_segments[i].mem &&
+ dest < (vm_offset_t)loaded_segments[i].mem +
+ loaded_segments[i].memsz)
+ goto out;
+ }
+
+ loaded_segments[nkexec_segments].buf = host_getmem(segsize);
+ loaded_segments[nkexec_segments].bufsz = segsize;
+ loaded_segments[nkexec_segments].mem = (void *)rounddown2(dest,segsize);
+ loaded_segments[nkexec_segments].memsz = segsize;
+ i = nkexec_segments;
+ nkexec_segments++;
+
+out:
+ *buf = loaded_segments[i].buf + (dest -
+ (vm_offset_t)loaded_segments[i].mem);
+ return (min(len,loaded_segments[i].bufsz - (dest -
+ (vm_offset_t)loaded_segments[i].mem)));
+}
+
+ssize_t
+kboot_copyin(const void *src, vm_offset_t dest, const size_t len)
+{
+ ssize_t segsize, remainder;
+ void *destbuf;
+
+ remainder = len;
+ do {
+ segsize = get_phys_buffer(dest, remainder, &destbuf);
+ bcopy(src, destbuf, segsize);
+ remainder -= segsize;
+ src += segsize;
+ dest += segsize;
+ } while (remainder > 0);
+
+ return (len);
+}
+
+ssize_t
+kboot_copyout(vm_offset_t src, void *dest, const size_t len)
+{
+ ssize_t segsize, remainder;
+ void *srcbuf;
+
+ remainder = len;
+ do {
+ segsize = get_phys_buffer(src, remainder, &srcbuf);
+ bcopy(srcbuf, dest, segsize);
+ remainder -= segsize;
+ src += segsize;
+ dest += segsize;
+ } while (remainder > 0);
+
+ return (len);
+}
+
+ssize_t
+kboot_readin(const int fd, vm_offset_t dest, const size_t len)
+{
+ void *buf;
+ size_t resid, chunk, get;
+ ssize_t got;
+ vm_offset_t p;
+
+ p = dest;
+
+ chunk = min(PAGE_SIZE, len);
+ buf = malloc(chunk);
+ if (buf == NULL) {
+ printf("kboot_readin: buf malloc failed\n");
+ return (0);
+ }
+
+ for (resid = len; resid > 0; resid -= got, p += got) {
+ get = min(chunk, resid);
+ got = read(fd, buf, get);
+ if (got <= 0) {
+ if (got < 0)
+ printf("kboot_readin: read failed\n");
+ break;
+ }
+
+ kboot_copyin(buf, p, got);
+ }
+
+ free (buf);
+ return (len - resid);
+}
+
+int
+kboot_autoload(void)
+{
+
+ return (0);
+}
+
+uint64_t
+kboot_loadaddr(u_int type, void *data, uint64_t addr)
+{
+ /*
+ * Need to stay out of the way of Linux. /chosen/linux,kernel-end does
+ * a better job here, but use a fixed offset for now.
+ */
+
+ if (type == LOAD_ELF)
+ addr = roundup(addr, PAGE_SIZE);
+ else
+ addr += 64*1024*1024; /* Stay out of the way of Linux */
+
+ return (addr);
+}
+
+void
+_start(int argc, const char **argv, char **env)
+{
+ register volatile void **sp asm("r1");
+ main((int)sp[0], (const char **)&sp[1]);
+}
+
+/*
+ * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
+ * and declaring it as extern is in contradiction with COMMAND_SET() macro
+ * (which uses static pointer), we're defining wrapper function, which
+ * calls the proper fdt handling routine.
+ */
+static int
+command_fdt(int argc, char *argv[])
+{
+
+ return (command_fdt_internal(argc, argv));
+}
+
+COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
+
diff --git a/stand/powerpc/kboot/metadata.c b/stand/powerpc/kboot/metadata.c
new file mode 100644
index 0000000..1e8c314
--- /dev/null
+++ b/stand/powerpc/kboot/metadata.c
@@ -0,0 +1,343 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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.
+ *
+ * from: FreeBSD: src/sys/boot/sparc64/loader/metadata.c,v 1.6
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/boot.h>
+#include <fdt_platform.h>
+
+#include <machine/metadata.h>
+
+#include "bootstrap.h"
+
+int
+md_getboothowto(char *kargs)
+{
+ char *cp;
+ int howto;
+ int active;
+ int i;
+
+ /* Parse kargs */
+ howto = 0;
+ if (kargs != NULL) {
+ cp = kargs;
+ active = 0;
+ while (*cp != 0) {
+ if (!active && (*cp == '-')) {
+ active = 1;
+ } else if (active)
+ switch (*cp) {
+ case 'a':
+ howto |= RB_ASKNAME;
+ break;
+ case 'C':
+ howto |= RB_CDROM;
+ break;
+ case 'd':
+ howto |= RB_KDB;
+ break;
+ case 'D':
+ howto |= RB_MULTIPLE;
+ break;
+ case 'm':
+ howto |= RB_MUTE;
+ break;
+ case 'g':
+ howto |= RB_GDB;
+ break;
+ case 'h':
+ howto |= RB_SERIAL;
+ break;
+ case 'p':
+ howto |= RB_PAUSE;
+ break;
+ case 'r':
+ howto |= RB_DFLTROOT;
+ break;
+ case 's':
+ howto |= RB_SINGLE;
+ break;
+ case 'v':
+ howto |= RB_VERBOSE;
+ break;
+ default:
+ active = 0;
+ break;
+ }
+ cp++;
+ }
+ }
+ /* get equivalents from the environment */
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+ if (!strcmp(getenv("console"), "comconsole"))
+ howto |= RB_SERIAL;
+ if (!strcmp(getenv("console"), "nullconsole"))
+ howto |= RB_MUTE;
+ return(howto);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+vm_offset_t
+md_copyenv(vm_offset_t addr)
+{
+ struct env_var *ep;
+
+ /* traverse the environment */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ archsw.arch_copyin(ep->ev_name, addr, strlen(ep->ev_name));
+ addr += strlen(ep->ev_name);
+ archsw.arch_copyin("=", addr, 1);
+ addr++;
+ if (ep->ev_value != NULL) {
+ archsw.arch_copyin(ep->ev_value, addr, strlen(ep->ev_value));
+ addr += strlen(ep->ev_value);
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ return(addr);
+}
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+
+static int align;
+
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ archsw.arch_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c) \
+ if (c) \
+ archsw.arch_copyin(s, a, strlen(s) + 1);\
+ a += roundup(strlen(s) + 1, align); \
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ archsw.arch_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), align); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c);\
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ archsw.arch_copyin(mm->md_data, a, mm->md_size);\
+ a += roundup(mm->md_size, align); \
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+vm_offset_t
+md_copymodules(vm_offset_t addr, int kern64)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ uint64_t scratch64;
+ int c;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must come first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ if (kern64) {
+ scratch64 = fp->f_addr;
+ MOD_ADDR(addr, scratch64, c);
+ scratch64 = fp->f_size;
+ MOD_SIZE(addr, scratch64, c);
+ } else {
+ MOD_ADDR(addr, fp->f_addr, c);
+ MOD_SIZE(addr, fp->f_size, c);
+ }
+ for (md = fp->f_metadata; md != NULL; md = md->md_next) {
+ if (!(md->md_type & MODINFOMD_NOCOPY)) {
+ MOD_METADATA(addr, md, c);
+ }
+ }
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Load the information expected by a powerpc kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+md_load_dual(char *args, vm_offset_t *modulep, vm_offset_t *dtb, int kern64)
+{
+ struct preloaded_file *kfp;
+ struct preloaded_file *xp;
+ struct file_metadata *md;
+ vm_offset_t kernend;
+ vm_offset_t addr;
+ vm_offset_t envp;
+ vm_offset_t fdtp;
+ vm_offset_t size;
+ uint64_t scratch64;
+ char *rootdevname;
+ int howto;
+
+ align = kern64 ? 8 : 4;
+ howto = md_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied device
+ * This should perhaps go to MI code and/or have $rootdev tested/set by
+ * MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ if (rootdevname == NULL)
+ rootdevname = getenv("currdev");
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(rootdevname);
+
+ /* find the last module in the chain */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* copy our environment */
+ envp = addr;
+ addr = md_copyenv(addr);
+
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* Copy out FDT */
+ size = fdt_copy(addr);
+ *dtb = fdtp = addr;
+ addr = roundup(addr + size, PAGE_SIZE);
+
+ kernend = 0;
+ kfp = file_findfile(NULL, kern64 ? "elf64 kernel" : "elf32 kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ if (kern64) {
+ scratch64 = envp;
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof scratch64, &scratch64);
+ scratch64 = fdtp;
+ file_addmetadata(kfp, MODINFOMD_DTBP, sizeof scratch64, &scratch64);
+ scratch64 = kernend;
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof scratch64, &scratch64);
+ } else {
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_DTBP, sizeof fdtp, &fdtp);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ }
+
+ *modulep = addr;
+ size = md_copymodules(0, kern64);
+ kernend = roundup(addr + size, PAGE_SIZE);
+
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ if (kern64) {
+ scratch64 = kernend;
+ bcopy(&scratch64, md->md_data, sizeof scratch64);
+ } else {
+ bcopy(&kernend, md->md_data, sizeof kernend);
+ }
+
+ (void)md_copymodules(addr, kern64);
+
+ return(0);
+}
+
+int
+md_load(char *args, vm_offset_t *modulep, vm_offset_t *dtb)
+{
+ return (md_load_dual(args, modulep, dtb, 0));
+}
+
+int
+md_load64(char *args, vm_offset_t *modulep, vm_offset_t *dtb)
+{
+ return (md_load_dual(args, modulep, dtb, 1));
+}
+
diff --git a/stand/powerpc/kboot/ppc64_elf_freebsd.c b/stand/powerpc/kboot/ppc64_elf_freebsd.c
new file mode 100644
index 0000000..22fa7b5
--- /dev/null
+++ b/stand/powerpc/kboot/ppc64_elf_freebsd.c
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 2001 Benno Rice <benno@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+
+#include <sys/param.h>
+#include <sys/linker.h>
+
+#include <machine/metadata.h>
+#include <machine/elf.h>
+
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "host_syscall.h"
+
+extern char end[];
+extern void *kerneltramp;
+extern size_t szkerneltramp;
+extern int nkexec_segments;
+extern void * loaded_segments;
+
+int
+ppc64_elf_loadfile(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ int r;
+
+ r = __elfN(loadfile)(filename, dest, result);
+ if (r != 0)
+ return (r);
+
+ return (0);
+}
+
+int
+ppc64_elf_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *fmp;
+ vm_offset_t mdp, dtb;
+ Elf_Ehdr *e;
+ int error;
+ uint32_t *trampoline;
+ uint64_t entry;
+ vm_offset_t trampolinebase;
+
+ if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) {
+ return(EFTYPE);
+ }
+ e = (Elf_Ehdr *)&fmp->md_data;
+
+ /* Figure out where to put it */
+ trampolinebase = archsw.arch_loadaddr(LOAD_RAW, NULL, 0);
+
+ /* Set up loader trampoline */
+ trampoline = malloc(szkerneltramp);
+ memcpy(trampoline, &kerneltramp, szkerneltramp);
+ /* Parse function descriptor for ELFv1 kernels */
+ if ((e->e_flags & 3) == 2)
+ entry = e->e_entry;
+ else
+ archsw.arch_copyout(e->e_entry + elf64_relocation_offset,
+ &entry, 8);
+ trampoline[2] = entry + elf64_relocation_offset;
+ trampoline[4] = 0; /* Phys. mem offset */
+ trampoline[5] = 0; /* OF entry point */
+
+ if ((error = md_load64(fp->f_args, &mdp, &dtb)) != 0)
+ return (error);
+
+ trampoline[3] = dtb;
+ trampoline[6] = mdp;
+ trampoline[7] = sizeof(mdp);
+ printf("Kernel entry at %#jx (%#x) ...\n", e->e_entry, trampoline[2]);
+ printf("DTB at %#x, mdp at %#x\n", dtb, mdp);
+
+ dev_cleanup();
+
+ archsw.arch_copyin(trampoline, trampolinebase, szkerneltramp);
+ free(trampoline);
+
+ error = kexec_load(trampolinebase, nkexec_segments, &loaded_segments);
+ if (error != 0)
+ panic("kexec_load returned error: %d", error);
+ error = host_reboot(0xfee1dead, 672274793,
+ 0x45584543 /* LINUX_REBOOT_CMD_KEXEC */, NULL);
+ if (error != 0)
+ panic("reboot returned error: %d", error);
+ while (1) {}
+
+ panic("exec returned");
+}
+
+struct file_format ppc_elf64 =
+{
+ ppc64_elf_loadfile,
+ ppc64_elf_exec
+};
diff --git a/stand/powerpc/kboot/version b/stand/powerpc/kboot/version
new file mode 100644
index 0000000..b24d6ea8
--- /dev/null
+++ b/stand/powerpc/kboot/version
@@ -0,0 +1,6 @@
+$FreeBSD: user/nwhitehorn/kboot/powerpc/kboot/version 224106 2011-07-16 19:01:09Z nwhitehorn $
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+0.1: Initial kboot/PowerPC version.
diff --git a/stand/powerpc/ofw/Makefile b/stand/powerpc/ofw/Makefile
new file mode 100644
index 0000000..1853b25
--- /dev/null
+++ b/stand/powerpc/ofw/Makefile
@@ -0,0 +1,55 @@
+# $FreeBSD$
+
+LOADER_CD9660_SUPPORT?= yes
+LOADER_EXT2FS_SUPPORT?= no
+LOADER_MSDOS_SUPPORT?= no
+LOADER_UFS_SUPPORT?= yes
+LOADER_NET_SUPPORT?= yes
+LOADER_NFS_SUPPORT?= yes
+LOADER_TFTP_SUPPORT?= no
+LOADER_GZIP_SUPPORT?= yes
+LOADER_BZIP2_SUPPORT?= no
+
+.include <bsd.init.mk>
+MK_SSP= no
+MAN=
+
+PROG= loader
+NEWVERSWHAT= "Open Firmware loader" ${MACHINE_ARCH}
+INSTALLFLAGS= -b
+
+# Architecture-specific loader code
+SRCS= conf.c metadata.c vers.c start.c
+SRCS+= ucmpdi2.c
+
+.include "${BOOTSRC}/fdt.mk"
+.if ${MK_FDT} == "yes"
+SRCS+= ofwfdt.c
+.endif
+
+HELP_FILES+= ${FDTSRC}/help.fdt
+
+# Always add MI sources
+.include "${BOOTSRC}/loader.mk"
+
+.PATH: ${SYSDIR}/libkern
+
+CFLAGS+= -ffreestanding -msoft-float
+# load address. set in linker script
+RELOC?= 0x1C00000
+CFLAGS+= -DRELOC=${RELOC}
+
+LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.powerpc
+
+# Pull in common loader code
+.PATH: ${BOOTSRC}/ofw/common
+.include "${BOOTSRC}/ofw/common/Makefile.inc"
+
+# Open Firmware standalone support library
+LIBOFW= ${BOOTOBJ}/ofw/libofw/libofw.a
+CFLAGS+= -I${BOOTSRC}/ofw/libofw
+
+DPADD= ${LIBFICL} ${LIBOFW} ${LIBFDT} ${LIBSA}
+LDADD= ${LIBFICL} ${LIBOFW} ${LIBFDT} ${LIBSA}
+
+.include <bsd.prog.mk>
diff --git a/stand/powerpc/ofw/conf.c b/stand/powerpc/ofw/conf.c
new file mode 100644
index 0000000..7cad372
--- /dev/null
+++ b/stand/powerpc/ofw/conf.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "bootstrap.h"
+#include "libofw.h"
+#include "openfirm.h"
+
+#if defined(LOADER_NET_SUPPORT)
+#include "dev_net.h"
+#endif
+
+/* Make sure we have an explicit reference to exit so libsa's panic pulls in the MD exit */
+void (*exitfn)(int) = exit;
+
+/*
+ * We could use linker sets for some or all of these, but
+ * then we would have to control what ended up linked into
+ * the bootstrap. So it's easier to conditionalise things
+ * here.
+ *
+ * XXX rename these arrays to be consistent and less namespace-hostile
+ */
+
+/* Exported for libstand */
+struct devsw *devsw[] = {
+#if defined(LOADER_DISK_SUPPORT) || defined(LOADER_CD9660_SUPPORT)
+ &ofwdisk,
+#endif
+#if defined(LOADER_NET_SUPPORT)
+ &netdev,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#if defined(LOADER_UFS_SUPPORT)
+ &ufs_fsops,
+#endif
+#if defined(LOADER_CD9660_SUPPORT)
+ &cd9660_fsops,
+#endif
+#if defined(LOADER_EXT2FS_SUPPORT)
+ &ext2fs_fsops,
+#endif
+#if defined(LOADER_NFS_SUPPORT)
+ &nfs_fsops,
+#endif
+#if defined(LOADER_TFTP_SUPPORT)
+ &tftp_fsops,
+#endif
+#if defined(LOADER_GZIP_SUPPORT)
+ &gzipfs_fsops,
+#endif
+#if defined(LOADER_BZIP2_SUPPORT)
+ &bzipfs_fsops,
+#endif
+ NULL
+};
+
+struct netif_driver *netif_drivers[] = {
+#if defined(LOADER_NET_SUPPORT)
+ &ofwnet,
+#endif
+ NULL,
+};
+
+/* Exported for PowerPC only */
+/*
+ * Sort formats so that those that can detect based on arguments
+ * rather than reading the file go first.
+ */
+
+struct file_format *file_formats[] = {
+ &ofw_elf,
+ &ofw_elf64,
+ NULL
+};
+
+/*
+ * Consoles
+ *
+ * We don't prototype these in libofw.h because they require
+ * data structures from bootstrap.h as well.
+ */
+extern struct console ofwconsole;
+
+struct console *consoles[] = {
+ &ofwconsole,
+ NULL
+};
+
+/*
+ * reloc - our load address
+ */
+vm_offset_t reloc = RELOC;
diff --git a/stand/powerpc/ofw/ldscript.powerpc b/stand/powerpc/ofw/ldscript.powerpc
new file mode 100644
index 0000000..3a27650
--- /dev/null
+++ b/stand/powerpc/ofw/ldscript.powerpc
@@ -0,0 +1,138 @@
+/* $FreeBSD$ */
+
+OUTPUT_FORMAT("elf32-powerpc-freebsd", "elf32-powerpc-freebsd",
+ "elf32-powerpc-freebsd")
+OUTPUT_ARCH(powerpc:common)
+ENTRY(_start)
+SEARCH_DIR(/usr/lib);
+PROVIDE (__stack = 0);
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x01c00000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rela.got : { *(.rela.got) }
+ .rela.got1 : { *(.rela.got1) }
+ .rela.got2 : { *(.rela.got2) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rela.init : { *(.rela.init) }
+ .rela.fini : { *(.rela.fini) }
+ .rela.bss : { *(.rela.bss) }
+ .rela.plt : { *(.rela.plt) }
+ .rela.sdata : { *(.rela.sdata) }
+ .rela.sbss : { *(.rela.sbss) }
+ .rela.sdata2 : { *(.rela.sdata2) }
+ .rela.sbss2 : { *(.rela.sbss2) }
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ PROVIDE (etext = .);
+ .init : { *(.init) } =0
+ .fini : { *(.fini) } =0
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 : { *(.sdata2) }
+ .sbss2 : { *(.sbss2) }
+ /* Adjust the address for the data segment to the next page up. */
+ . = ((. + 0x1000) & ~(0x1000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .got1 : { *(.got1) }
+ .dynamic : { *(.dynamic) }
+ /* Put .ctors and .dtors next to the .got2 section, so that the pointers
+ get relocated with -mrelocatable. Also put in the .fixup pointers.
+ The current compiler no longer needs this, but keep it around for 2.7.2 */
+ PROVIDE (_GOT2_START_ = .);
+ .got2 : { *(.got2) }
+ PROVIDE (__CTOR_LIST__ = .);
+ .ctors : { *(.ctors) }
+ PROVIDE (__CTOR_END__ = .);
+ PROVIDE (__DTOR_LIST__ = .);
+ .dtors : { *(.dtors) }
+ PROVIDE (__DTOR_END__ = .);
+ PROVIDE (_FIXUP_START_ = .);
+ .fixup : { *(.fixup) }
+ PROVIDE (_FIXUP_END_ = .);
+ PROVIDE (_GOT2_END_ = .);
+ PROVIDE (_GOT_START_ = .);
+ .got : { *(.got) }
+ .got.plt : { *(.got.plt) }
+ PROVIDE (_GOT_END_ = .);
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ *(.sbss)
+ *(.scommon)
+ *(.dynsbss)
+ PROVIDE (__sbss_end = .);
+ }
+ .plt : { *(.plt) }
+ .bss :
+ {
+ PROVIDE (__bss_start = .);
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ /* 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) }
+ .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) }
+ /* These must appear regardless of . */
+}
+
diff --git a/stand/powerpc/ofw/metadata.c b/stand/powerpc/ofw/metadata.c
new file mode 100644
index 0000000..25d51f8
--- /dev/null
+++ b/stand/powerpc/ofw/metadata.c
@@ -0,0 +1,349 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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.
+ *
+ * from: FreeBSD: src/sys/boot/sparc64/loader/metadata.c,v 1.6
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/boot.h>
+#include <fdt_platform.h>
+
+#include <machine/metadata.h>
+
+#include "bootstrap.h"
+
+int
+md_getboothowto(char *kargs)
+{
+ char *cp;
+ int howto;
+ int active;
+ int i;
+
+ /* Parse kargs */
+ howto = 0;
+ if (kargs != NULL) {
+ cp = kargs;
+ active = 0;
+ while (*cp != 0) {
+ if (!active && (*cp == '-')) {
+ active = 1;
+ } else if (active)
+ switch (*cp) {
+ case 'a':
+ howto |= RB_ASKNAME;
+ break;
+ case 'C':
+ howto |= RB_CDROM;
+ break;
+ case 'd':
+ howto |= RB_KDB;
+ break;
+ case 'D':
+ howto |= RB_MULTIPLE;
+ break;
+ case 'm':
+ howto |= RB_MUTE;
+ break;
+ case 'g':
+ howto |= RB_GDB;
+ break;
+ case 'h':
+ howto |= RB_SERIAL;
+ break;
+ case 'p':
+ howto |= RB_PAUSE;
+ break;
+ case 'r':
+ howto |= RB_DFLTROOT;
+ break;
+ case 's':
+ howto |= RB_SINGLE;
+ break;
+ case 'v':
+ howto |= RB_VERBOSE;
+ break;
+ default:
+ active = 0;
+ break;
+ }
+ cp++;
+ }
+ }
+ /* get equivalents from the environment */
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+ if (!strcmp(getenv("console"), "comconsole"))
+ howto |= RB_SERIAL;
+ if (!strcmp(getenv("console"), "nullconsole"))
+ howto |= RB_MUTE;
+ return(howto);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+vm_offset_t
+md_copyenv(vm_offset_t addr)
+{
+ struct env_var *ep;
+
+ /* traverse the environment */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ archsw.arch_copyin(ep->ev_name, addr, strlen(ep->ev_name));
+ addr += strlen(ep->ev_name);
+ archsw.arch_copyin("=", addr, 1);
+ addr++;
+ if (ep->ev_value != NULL) {
+ archsw.arch_copyin(ep->ev_value, addr, strlen(ep->ev_value));
+ addr += strlen(ep->ev_value);
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ return(addr);
+}
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+
+static int align;
+
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ archsw.arch_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c) \
+ if (c) \
+ archsw.arch_copyin(s, a, strlen(s) + 1);\
+ a += roundup(strlen(s) + 1, align); \
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ archsw.arch_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), align); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c);\
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ archsw.arch_copyin(mm->md_data, a, mm->md_size);\
+ a += roundup(mm->md_size, align); \
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+vm_offset_t
+md_copymodules(vm_offset_t addr, int kern64)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ uint64_t scratch64;
+ int c;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must come first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ if (kern64) {
+ scratch64 = fp->f_addr;
+ MOD_ADDR(addr, scratch64, c);
+ scratch64 = fp->f_size;
+ MOD_SIZE(addr, scratch64, c);
+ } else {
+ MOD_ADDR(addr, fp->f_addr, c);
+ MOD_SIZE(addr, fp->f_size, c);
+ }
+ for (md = fp->f_metadata; md != NULL; md = md->md_next) {
+ if (!(md->md_type & MODINFOMD_NOCOPY)) {
+ MOD_METADATA(addr, md, c);
+ }
+ }
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Load the information expected by a powerpc kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+md_load_dual(char *args, vm_offset_t *modulep, vm_offset_t *dtb, int kern64)
+{
+ struct preloaded_file *kfp;
+ struct preloaded_file *xp;
+ struct file_metadata *md;
+ vm_offset_t kernend;
+ vm_offset_t addr;
+ vm_offset_t envp;
+ vm_offset_t fdtp;
+ vm_offset_t size;
+ uint64_t scratch64;
+ char *rootdevname;
+ int howto;
+
+ align = kern64 ? 8 : 4;
+ howto = md_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied device
+ * This should perhaps go to MI code and/or have $rootdev tested/set by
+ * MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ if (rootdevname == NULL)
+ rootdevname = getenv("currdev");
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(rootdevname);
+
+ /* find the last module in the chain */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* copy our environment */
+ envp = addr;
+ addr = md_copyenv(addr);
+
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* Copy out FDT */
+ *dtb = fdtp = 0;
+ if (getenv("usefdt") != NULL) {
+ size = fdt_copy(addr);
+ *dtb = fdtp = addr;
+ addr = roundup(addr + size, PAGE_SIZE);
+ }
+
+ kernend = 0;
+ kfp = file_findfile(NULL, kern64 ? "elf64 kernel" : "elf32 kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ if (kern64) {
+ scratch64 = envp;
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof scratch64, &scratch64);
+ if (fdtp != 0) {
+ scratch64 = fdtp;
+ file_addmetadata(kfp, MODINFOMD_DTBP, sizeof scratch64, &scratch64);
+ }
+ scratch64 = kernend;
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof scratch64, &scratch64);
+ } else {
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ if (fdtp != 0)
+ file_addmetadata(kfp, MODINFOMD_DTBP, sizeof fdtp, &fdtp);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ }
+
+ *modulep = addr;
+ size = md_copymodules(0, kern64);
+ kernend = roundup(addr + size, PAGE_SIZE);
+
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ if (kern64) {
+ scratch64 = kernend;
+ bcopy(&scratch64, md->md_data, sizeof scratch64);
+ } else {
+ bcopy(&kernend, md->md_data, sizeof kernend);
+ }
+
+ (void)md_copymodules(addr, kern64);
+
+ return(0);
+}
+
+int
+md_load(char *args, vm_offset_t *modulep, vm_offset_t *dtb)
+{
+ return (md_load_dual(args, modulep, dtb, 0));
+}
+
+int
+md_load64(char *args, vm_offset_t *modulep, vm_offset_t *dtb)
+{
+ return (md_load_dual(args, modulep, dtb, 1));
+}
+
diff --git a/stand/powerpc/ofw/ofwfdt.c b/stand/powerpc/ofw/ofwfdt.c
new file mode 100644
index 0000000..02f9939
--- /dev/null
+++ b/stand/powerpc/ofw/ofwfdt.c
@@ -0,0 +1,219 @@
+/*-
+ * Copyright (C) 2014-2015 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Include stdlib.h because of DEBUG_MALLOC shenanigans in stand.h for now.
+ * This will be removed in the future, when libsa silently replaces stdlib.h.
+ */
+#include <stdlib.h>
+#include <stand.h>
+#include <sys/param.h>
+#include <fdt_platform.h>
+#include <openfirm.h>
+#include <libfdt.h>
+#include "bootstrap.h"
+
+extern int command_fdt_internal(int argc, char *argv[]);
+
+static int
+OF_hasprop(phandle_t node, const char *prop)
+{
+ return (OF_getproplen(node, (char *)prop) > 0);
+}
+
+static void
+add_node_to_fdt(void *buffer, phandle_t node, int fdt_offset)
+{
+ int i, child_offset, error;
+ char name[255], *lastprop, *subname;
+ void *propbuf;
+ ssize_t proplen;
+
+ lastprop = NULL;
+ while (OF_nextprop(node, lastprop, name) > 0) {
+ proplen = OF_getproplen(node, name);
+
+ /* Detect and correct for errors and strangeness */
+ if (proplen < 0)
+ proplen = 0;
+ if (proplen > 1024)
+ proplen = 1024;
+
+ propbuf = malloc(proplen);
+ if (propbuf == NULL) {
+ printf("Cannot allocate memory for prop %s\n", name);
+ return;
+ }
+ OF_getprop(node, name, propbuf, proplen);
+ error = fdt_setprop(buffer, fdt_offset, name, propbuf, proplen);
+ free(propbuf);
+ lastprop = name;
+ if (error)
+ printf("Error %d adding property %s to "
+ "node %d\n", error, name, fdt_offset);
+ }
+
+ if (!OF_hasprop(node, "phandle") && !OF_hasprop(node, "linux,phandle")
+ && !OF_hasprop(node, "ibm,phandle"))
+ fdt_setprop(buffer, fdt_offset, "phandle", &node, sizeof(node));
+
+ for (node = OF_child(node); node > 0; node = OF_peer(node)) {
+ OF_package_to_path(node, name, sizeof(name));
+ subname = strrchr(name, '/');
+ subname++;
+ child_offset = fdt_add_subnode(buffer, fdt_offset, subname);
+ if (child_offset < 0) {
+ printf("Error %d adding node %s (%s), skipping\n",
+ child_offset, name, subname);
+ continue;
+ }
+
+ add_node_to_fdt(buffer, node, child_offset);
+ }
+}
+
+static void
+ofwfdt_fixups(void *fdtp)
+{
+ int offset, len, i;
+ phandle_t node;
+ ihandle_t rtas;
+ const void *prop;
+
+ /*
+ * Instantiate and add reservations for RTAS state if present
+ */
+
+ offset = fdt_path_offset(fdtp, "/rtas");
+ if (offset > 0) {
+ uint32_t base;
+ void *rtasmem;
+ char path[255];
+
+ node = OF_finddevice("/rtas");
+ OF_package_to_path(node, path, sizeof(path));
+ OF_getprop(node, "rtas-size", &len, sizeof(len));
+
+ /* Allocate memory */
+ rtasmem = OF_claim(0, len, 4096);
+
+ /* Instantiate RTAS */
+ rtas = OF_open(path);
+ base = 0;
+ OF_call_method("instantiate-rtas", rtas, 1, 1, (cell_t)rtas,
+ &base);
+
+ /* Store info to FDT using Linux convention */
+ base = cpu_to_fdt32(base);
+ fdt_setprop(fdtp, offset, "linux,rtas-entry", &base,
+ sizeof(base));
+ base = cpu_to_fdt32((uint32_t)rtasmem);
+ offset = fdt_path_offset(fdtp, "/rtas");
+ fdt_setprop(fdtp, offset, "linux,rtas-base", &base,
+ sizeof(base));
+
+ /* Mark RTAS private data area reserved */
+ fdt_add_mem_rsv(fdtp, base, len);
+ } else {
+ /*
+ * Remove /memory/available properties, which reflect long-gone
+ * OF state. Note that this doesn't work if we need RTAS still,
+ * since that's part of the firmware.
+ */
+ offset = fdt_path_offset(fdtp, "/memory@0");
+ if (offset > 0)
+ fdt_delprop(fdtp, offset, "available");
+ }
+
+
+ /*
+ * Convert stored ihandles under /chosen to xref phandles
+ */
+ offset = fdt_path_offset(fdtp, "/chosen");
+ if (offset > 0) {
+ const char *chosenprops[] = {"stdout", "stdin", "mmu", "cpu",
+ NULL};
+ const uint32_t *ihand;
+ for (i = 0; chosenprops[i] != NULL; i++) {
+ ihand = fdt_getprop(fdtp, offset, chosenprops[i], &len);
+ if (ihand != NULL && len == sizeof(*ihand)) {
+ node = OF_instance_to_package(
+ fdt32_to_cpu(*ihand));
+ if (OF_hasprop(node, "phandle"))
+ OF_getprop(node, "phandle", &node,
+ sizeof(node));
+ else if (OF_hasprop(node, "linux,phandle"))
+ OF_getprop(node, "linux,phandle", &node,
+ sizeof(node));
+ else if (OF_hasprop(node, "ibm,phandle"))
+ OF_getprop(node, "ibm,phandle", &node,
+ sizeof(node));
+ node = cpu_to_fdt32(node);
+ fdt_setprop(fdtp, offset, chosenprops[i], &node,
+ sizeof(node));
+ }
+
+ /* Refind node in case it moved */
+ offset = fdt_path_offset(fdtp, "/chosen");
+ }
+ }
+}
+
+int
+fdt_platform_load_dtb(void)
+{
+ void *buffer;
+ size_t buflen = 409600;
+
+ buffer = malloc(buflen);
+ fdt_create_empty_tree(buffer, buflen);
+ add_node_to_fdt(buffer, OF_peer(0), fdt_path_offset(buffer, "/"));
+ ofwfdt_fixups(buffer);
+ fdt_pack(buffer);
+
+ fdt_load_dtb_addr(buffer);
+ free(buffer);
+
+ return (0);
+}
+
+void
+fdt_platform_fixups(void)
+{
+
+}
+
+static int
+command_fdt(int argc, char *argv[])
+{
+
+ return (command_fdt_internal(argc, argv));
+}
+
+COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
+
diff --git a/stand/powerpc/ofw/start.c b/stand/powerpc/ofw/start.c
new file mode 100644
index 0000000..911a2ec
--- /dev/null
+++ b/stand/powerpc/ofw/start.c
@@ -0,0 +1,74 @@
+/* $NetBSD: Locore.c,v 1.7 2000/08/20 07:04:59 tsubai Exp $ */
+/*-
+ * Copyright (C) 1995, 1996 Wolfgang Solfrank.
+ * Copyright (C) 1995, 1996 TooLs GmbH.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "libofw.h"
+
+void startup(void *, int, int (*)(void *), char *, int);
+
+__asm(" \n\
+ .data \n\
+ .align 4 \n\
+stack: \n\
+ .space 16388 \n\
+ \n\
+ .text \n\
+ .globl _start \n\
+_start: \n\
+ lis %r1,stack@ha \n\
+ addi %r1,%r1,stack@l \n\
+ addi %r1,%r1,8192 \n\
+ \n\
+ /* Clear the .bss!!! */ \n\
+ li %r0,0 \n\
+ lis %r8,_edata@ha \n\
+ addi %r8,%r8,_edata@l\n\
+ lis %r9,_end@ha \n\
+ addi %r9,%r9,_end@l \n\
+ \n\
+1: cmpw 0,%r8,%r9 \n\
+ bge 2f \n\
+ stw %r0,0(%r8) \n\
+ addi %r8,%r8,4 \n\
+ b 1b \n\
+ \n\
+2: b startup \n\
+");
+
+void
+startup(void *vpd, int res, int (*openfirm)(void *), char *arg, int argl)
+{
+ main(openfirm);
+}
diff --git a/stand/powerpc/ofw/version b/stand/powerpc/ofw/version
new file mode 100644
index 0000000..cb0f693
--- /dev/null
+++ b/stand/powerpc/ofw/version
@@ -0,0 +1,6 @@
+$FreeBSD$
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+0.1: Initial OFW/PowerPC version.
diff --git a/stand/powerpc/ps3/Makefile b/stand/powerpc/ps3/Makefile
new file mode 100644
index 0000000..07ad135
--- /dev/null
+++ b/stand/powerpc/ps3/Makefile
@@ -0,0 +1,48 @@
+# $FreeBSD$
+
+LOADER_UFS_SUPPORT?= yes
+LOADER_CD9660_SUPPORT?= yes
+LOADER_EXT2FS_SUPPORT?= yes
+LOADER_NET_SUPPORT?= yes
+LOADER_NFS_SUPPORT?= yes
+LOADER_TFTP_SUPPORT?= no
+LOADER_GZIP_SUPPORT?= yes
+LOADER_BZIP2_SUPPORT?= no
+
+.include <bsd.init.mk>
+MK_SSP= no
+MAN=
+
+PROG= loader.ps3
+NEWVERSWHAT= "Playstation 3 loader" ${MACHINE_ARCH}
+INSTALLFLAGS= -b
+
+# Architecture-specific loader code
+SRCS= start.S conf.c metadata.c vers.c main.c devicename.c ppc64_elf_freebsd.c
+SRCS+= lv1call.S ps3cons.c font.h ps3mmu.c ps3net.c ps3repo.c \
+ ps3stor.c ps3disk.c ps3cdrom.c
+SRCS+= ucmpdi2.c
+
+CFLAGS+= -mcpu=powerpc64
+
+# Always add MI sources
+HELP_FILES= # Disable
+.include "${BOOTSRC}/loader.mk"
+.PATH: ${SYSDIR}/libkern
+
+CFLAGS+= -Wall -ffreestanding -msoft-float -DAIM
+# load address. set in linker script
+RELOC?= 0x0
+CFLAGS+= -DRELOC=${RELOC}
+
+LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.powerpc
+
+DPADD= ${LIBFICL} ${LIBOFW} ${LIBSA}
+LDADD= ${LIBFICL} ${LIBOFW} ${LIBSA}
+
+SC_DFLT_FONT=cp437
+
+font.h:
+ uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x16.fnt && file2c 'u_char dflt_font_16[16*256] = {' '};' < ${SC_DFLT_FONT}-8x16 > font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x14.fnt && file2c 'u_char dflt_font_14[14*256] = {' '};' < ${SC_DFLT_FONT}-8x14 >> font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x8.fnt && file2c 'u_char dflt_font_8[8*256] = {' '};' < ${SC_DFLT_FONT}-8x8 >> font.h
+
+.include <bsd.prog.mk>
diff --git a/stand/powerpc/ps3/conf.c b/stand/powerpc/ps3/conf.c
new file mode 100644
index 0000000..3a5ae4c
--- /dev/null
+++ b/stand/powerpc/ps3/conf.c
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (C) 1999 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "bootstrap.h"
+
+#if defined(LOADER_NET_SUPPORT)
+#include "dev_net.h"
+#endif
+
+extern struct devsw ps3disk;
+extern struct devsw ps3cdrom;
+
+/*
+ * We could use linker sets for some or all of these, but
+ * then we would have to control what ended up linked into
+ * the bootstrap. So it's easier to conditionalise things
+ * here.
+ *
+ * XXX rename these arrays to be consistent and less namespace-hostile
+ */
+
+/* Exported for libstand */
+struct devsw *devsw[] = {
+#if defined(LOADER_CD9660_SUPPORT)
+ &ps3cdrom,
+#endif
+#if defined(LOADER_DISK_SUPPORT)
+ &ps3disk,
+#endif
+#if defined(LOADER_NET_SUPPORT)
+ &netdev,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#if defined(LOADER_UFS_SUPPORT)
+ &ufs_fsops,
+#endif
+#if defined(LOADER_CD9660_SUPPORT)
+ &cd9660_fsops,
+#endif
+#if defined(LOADER_EXT2FS_SUPPORT)
+ &ext2fs_fsops,
+#endif
+#if defined(LOADER_NFS_SUPPORT)
+ &nfs_fsops,
+#endif
+#if defined(LOADER_TFTP_SUPPORT)
+ &tftp_fsops,
+#endif
+#if defined(LOADER_GZIP_SUPPORT)
+ &gzipfs_fsops,
+#endif
+#if defined(LOADER_BZIP2_SUPPORT)
+ &bzipfs_fsops,
+#endif
+ NULL
+};
+
+extern struct netif_driver ps3net;
+
+struct netif_driver *netif_drivers[] = {
+#if defined(LOADER_NET_SUPPORT)
+ &ps3net,
+#endif
+ NULL,
+};
+
+/* Exported for PowerPC only */
+/*
+ * Sort formats so that those that can detect based on arguments
+ * rather than reading the file go first.
+ */
+
+extern struct file_format ppc_elf64;
+
+struct file_format *file_formats[] = {
+ &ppc_elf64,
+ NULL
+};
+
+/*
+ * Consoles
+ */
+extern struct console ps3console;
+
+struct console *consoles[] = {
+ &ps3console,
+ NULL
+};
+
+/*
+ * reloc - our load address
+ */
+vm_offset_t reloc = RELOC;
diff --git a/stand/powerpc/ps3/devicename.c b/stand/powerpc/ps3/devicename.c
new file mode 100644
index 0000000..041f853
--- /dev/null
+++ b/stand/powerpc/ps3/devicename.c
@@ -0,0 +1,238 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/disklabel.h>
+
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+#include "ps3.h"
+#include "ps3devdesc.h"
+
+static int ps3_parsedev(struct ps3_devdesc **dev, const char *devspec,
+ const char **path);
+
+/*
+ * Point (dev) at an allocated device specifier for the device matching the
+ * path in (devspec). If it contains an explicit device specification,
+ * use that. If not, use the default device.
+ */
+int
+ps3_getdev(void **vdev, const char *devspec, const char **path)
+{
+ struct ps3_devdesc **dev = (struct ps3_devdesc **)vdev;
+ int rv = 0;
+
+ /*
+ * If it looks like this is just a path and no
+ * device, go with the current device.
+ */
+ if ((devspec == NULL) || (devspec[0] == '/') ||
+ (strchr(devspec, ':') == NULL)) {
+ rv = ps3_parsedev(dev, getenv("currdev"), NULL);
+
+ if (rv == 0 && path != NULL)
+ *path = devspec;
+ return(rv);
+ }
+
+ /*
+ * Try to parse the device name off the beginning of the devspec.
+ */
+ return (ps3_parsedev(dev, devspec, path));
+}
+
+/*
+ * Point (dev) at an allocated device specifier matching the string version
+ * at the beginning of (devspec). Return a pointer to the remaining
+ * text in (path).
+ *
+ * In all cases, the beginning of (devspec) is compared to the names
+ * of known devices in the device switch, and then any following text
+ * is parsed according to the rules applied to the device type.
+ *
+ * For disk-type devices, the syntax is:
+ *
+ * disk<unit>[<partition>]:
+ *
+ */
+static int
+ps3_parsedev(struct ps3_devdesc **dev, const char *devspec, const char **path)
+{
+ struct ps3_devdesc *idev;
+ struct devsw *dv;
+ char *cp;
+ const char *np;
+ int i, unit, pnum, ptype, err;
+
+ /* minimum length check */
+ if (strlen(devspec) < 2)
+ return(EINVAL);
+
+ /* look for a device that matches */
+ for (i = 0, dv = NULL; devsw[i] != NULL; i++) {
+ if (!strncmp(devspec, devsw[i]->dv_name,
+ strlen(devsw[i]->dv_name))) {
+ dv = devsw[i];
+ break;
+ }
+ }
+ if (dv == NULL)
+ return(ENOENT);
+ idev = malloc(sizeof(struct ps3_devdesc));
+ err = 0;
+ np = (devspec + strlen(dv->dv_name));
+
+ switch(dv->dv_type) {
+ case DEVT_NONE:
+ break;
+
+ case DEVT_DISK:
+ unit = -1;
+ pnum = -1;
+ ptype = -1;
+ if (*np && (*np != ':')) {
+ /* next comes the unit number */
+ unit = strtol(np, &cp, 10);
+ if (cp == np) {
+ err = EUNIT;
+ goto fail;
+ }
+ if (*cp && (*cp != ':')) {
+ /* get partition */
+ if (*cp == 'p' && *(cp + 1) &&
+ *(cp + 1) != ':') {
+ pnum = strtol(cp + 1, &cp, 10);
+ ptype = PTYPE_GPT;
+ } else {
+ pnum = *cp - 'a';
+ ptype = PTYPE_BSDLABEL;
+ if ((pnum < 0) ||
+ (pnum >= MAXPARTITIONS)) {
+ err = EPART;
+ goto fail;
+ }
+ cp++;
+ }
+ }
+ }
+ if (*cp && (*cp != ':')) {
+ err = EINVAL;
+ goto fail;
+ }
+
+ idev->d_unit = unit;
+ idev->d_disk.pnum = pnum;
+ idev->d_disk.ptype = ptype;
+ idev->d_disk.data = NULL;
+ if (path != NULL)
+ *path = (*cp == 0) ? cp : cp + 1;
+ break;
+
+ case DEVT_NET:
+ case DEVT_CD:
+ /*
+ * PS3 only has one network interface (well, two, but
+ * netbooting over wireless is not something I'm going
+ * to worry about.
+ */
+
+ idev->d_unit = 0;
+ break;
+
+ default:
+ err = EINVAL;
+ goto fail;
+ }
+ idev->d_dev = dv;
+ idev->d_type = dv->dv_type;
+ if (dev == NULL) {
+ free(idev);
+ } else {
+ *dev = idev;
+ }
+ return (0);
+
+fail:
+ free(idev);
+ return (err);
+}
+
+
+char *
+ps3_fmtdev(void *vdev)
+{
+ struct ps3_devdesc *dev = (struct ps3_devdesc *)vdev;
+ char *cp;
+ static char buf[128];
+
+ switch(dev->d_type) {
+ case DEVT_NONE:
+ strcpy(buf, "(no device)");
+ break;
+
+ case DEVT_DISK:
+ cp = buf;
+ cp += sprintf(cp, "%s%d", dev->d_dev->dv_name, dev->d_unit);
+ if (dev->d_kind.disk.pnum >= 0) {
+ if (dev->d_kind.disk.ptype == PTYPE_BSDLABEL)
+ cp += sprintf(cp, "%c",
+ dev->d_kind.disk.pnum + 'a');
+ else if (dev->d_kind.disk.ptype == PTYPE_GPT)
+ cp += sprintf(cp, "p%i",
+ dev->d_kind.disk.pnum);
+ }
+
+ strcat(cp, ":");
+ break;
+
+ case DEVT_NET:
+ case DEVT_CD:
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
+ break;
+ }
+ return(buf);
+}
+
+/*
+ * Set currdev to suit the value being supplied in (value).
+ */
+int
+ps3_setcurrdev(struct env_var *ev, int flags, const void *value)
+{
+ struct ps3_devdesc *ncurr;
+ int rv;
+
+ if ((rv = ps3_parsedev(&ncurr, value, NULL)) != 0)
+ return (rv);
+ free(ncurr);
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return (0);
+}
diff --git a/stand/powerpc/ps3/ldscript.powerpc b/stand/powerpc/ps3/ldscript.powerpc
new file mode 100644
index 0000000..12f3e75
--- /dev/null
+++ b/stand/powerpc/ps3/ldscript.powerpc
@@ -0,0 +1,111 @@
+/* $FreeBSD$ */
+
+OUTPUT_FORMAT("elf32-powerpc-freebsd", "elf32-powerpc-freebsd",
+ "elf32-powerpc-freebsd")
+OUTPUT_ARCH(powerpc:common)
+ENTRY(_start)
+SEARCH_DIR(/usr/lib);
+PROVIDE (__stack = 0);
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x0;
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rela.got : { *(.rela.got) }
+ .rela.got1 : { *(.rela.got1) }
+ .rela.got2 : { *(.rela.got2) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rela.init : { *(.rela.init) }
+ .rela.fini : { *(.rela.fini) }
+ .rela.bss : { *(.rela.bss) }
+ .rela.plt : { *(.rela.plt) }
+ .rela.sbss : { *(.rela.sbss) }
+ .rela.sbss2 : { *(.rela.sbss2) }
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ PROVIDE (etext = .);
+ .init : { *(.init) } =0
+ .fini : { *(.fini) } =0
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .sbss2 : { *(.sbss2) }
+ /* Adjust the address for the data segment to the next page up. */
+ . = ((. + 0x1000) & ~(0x1000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .got1 : { *(.got1) }
+ .dynamic : { *(.dynamic) }
+ /* Put .ctors and .dtors next to the .got2 section, so that the pointers
+ get relocated with -mrelocatable. Also put in the .fixup pointers.
+ The current compiler no longer needs this, but keep it around for 2.7.2 */
+ PROVIDE (_GOT2_START_ = .);
+ .got2 : { *(.got2) }
+ PROVIDE (__CTOR_LIST__ = .);
+ .ctors : { *(.ctors) }
+ PROVIDE (__CTOR_END__ = .);
+ PROVIDE (__DTOR_LIST__ = .);
+ .dtors : { *(.dtors) }
+ PROVIDE (__DTOR_END__ = .);
+ PROVIDE (_FIXUP_START_ = .);
+ .fixup : { *(.fixup) }
+ PROVIDE (_FIXUP_END_ = .);
+ PROVIDE (_GOT2_END_ = .);
+ PROVIDE (_GOT_START_ = .);
+ .got : { *(.got) }
+ .got.plt : { *(.got.plt) }
+ PROVIDE (_GOT_END_ = .);
+ _edata = .;
+ PROVIDE (edata = .);
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ *(.sbss)
+ *(.scommon)
+ *(.dynsbss)
+ PROVIDE (__sbss_end = .);
+ }
+ .plt : { *(.plt) }
+ .bss :
+ {
+ PROVIDE (__bss_start = .);
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ . = ALIGN(4096);
+ _end = . ;
+ PROVIDE (end = .);
+}
+
diff --git a/stand/powerpc/ps3/lv1call.S b/stand/powerpc/ps3/lv1call.S
new file mode 100644
index 0000000..a399a9c
--- /dev/null
+++ b/stand/powerpc/ps3/lv1call.S
@@ -0,0 +1,346 @@
+/*-
+ * Copyright (C) 2010 Nathan Whitehorn
+ * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Hypercall stubs. Note: this is all a hack and should die. */
+
+#define hc .long 0x44000022
+
+#define LD64_IM(r, highest, higher, high, low) \
+ lis r,highest; \
+ addi r,r,higher; \
+ sldi r,r,32; \
+ addis r,r,high; \
+ addi r,r,low;
+
+#define SIMPLE_HVCALL(x, c) \
+.global x; \
+x: \
+ mflr %r0; \
+ stw %r0,4(%r1); \
+ clrldi %r3,%r3,32; \
+ clrldi %r4,%r4,32; \
+ clrldi %r5,%r5,32; \
+ clrldi %r6,%r6,32; \
+ clrldi %r7,%r7,32; \
+ clrldi %r8,%r8,32; \
+ clrldi %r9,%r9,32; \
+ clrldi %r10,%r10,32; \
+ li %r11,c; \
+ hc; \
+ extsw %r3,%r3; \
+ lwz %r0,4(%r1); \
+ mtlr %r0; \
+ blr
+
+SIMPLE_HVCALL(lv1_open_device, 170)
+SIMPLE_HVCALL(lv1_close_device, 171)
+SIMPLE_HVCALL(lv1_gpu_open, 210)
+SIMPLE_HVCALL(lv1_gpu_context_attribute, 225)
+SIMPLE_HVCALL(lv1_panic, 255)
+SIMPLE_HVCALL(lv1_net_start_tx_dma, 187)
+SIMPLE_HVCALL(lv1_net_stop_tx_dma, 188)
+SIMPLE_HVCALL(lv1_net_start_rx_dma, 189)
+SIMPLE_HVCALL(lv1_net_stop_rx_dma, 190)
+
+.global lv1_get_physmem
+lv1_get_physmem:
+ mflr %r0
+ stw %r0,4(%r1)
+ stw %r3,-8(%r1) /* Address for maxmem */
+
+ li %r11,69 /* Get PU ID */
+ hc
+ std %r4,-16(%r1)
+
+ li %r11,74 /* Get LPAR ID */
+ hc
+ std %r4,-24(%r1)
+
+ ld %r3,-24(%r1)
+ LD64_IM(%r4,0x0000,0x0000,0x6269,0x0000 /* "bi" */)
+ LD64_IM(%r5,0x7075,0x0000,0x0000,0x0000 /* "pu" */)
+ ld %r6,-16(%r1)
+ LD64_IM(%r7,0x726d,0x5f73,0x697a,0x6500 /* "rm_size" */)
+ li %r11,91
+ hc
+ extsw %r3,%r3
+
+ lwz %r5,-8(%r1)
+ std %r4,0(%r5)
+
+ lwz %r0,4(%r1)
+ mtlr %r0
+ blr
+
+.global lv1_setup_address_space
+lv1_setup_address_space:
+ mflr %r0
+ stw %r0,4(%r1)
+
+ stw %r3,-4(%r1)
+ stw %r4,-8(%r1)
+
+ li %r3,18 /* PT size: log2(256 KB) */
+ li %r4,2 /* Two page sizes */
+ li %r5,24 /* Page sizes: (24 << 56) | (16 << 48) */
+ sldi %r5,%r5,24
+ li %r6,16
+ sldi %r6,%r6,16
+ or %r5,%r5,%r6
+ sldi %r5,%r5,32
+
+ li %r11,2 /* lv1_construct_virtual_address_space */
+ hc
+
+ lwz %r6,-4(%r1)
+ lwz %r7,-8(%r1)
+ std %r4,0(%r6)
+ std %r5,0(%r7)
+
+ /* AS_ID in r4 */
+ mr %r3,%r4
+ li %r11,7 /* lv1_select_virtual_address_space */
+ hc
+ extsw %r3,%r3
+
+ lwz %r0,4(%r1)
+ mtlr %r0
+ blr
+
+.global lv1_insert_pte
+lv1_insert_pte:
+ mflr %r0
+ stw %r0,4(%r1)
+
+ mr %r11,%r4 /* Save R4 */
+
+ clrldi %r3,%r3,32
+ clrldi %r7,%r5,32
+
+ sldi %r4,%r3,3 /* Convert ptegidx into base PTE slot */
+ li %r3,0 /* Current address space */
+ ld %r5,0(%r11)
+ ld %r6,8(%r11)
+ li %r8,0 /* No other flags */
+
+ li %r11,158
+ hc
+ extsw %r3,%r3
+
+ lwz %r0,4(%r1)
+ mtlr %r0
+ blr
+
+.global lv1_gpu_context_allocate
+lv1_gpu_context_allocate:
+ mflr %r0
+ stw %r0,4(%r1)
+ stw %r7,-4(%r1)
+
+ sldi %r3,%r3,32
+ clrldi %r4,%r4,32
+ or %r3,%r3,%r4
+ clrldi %r4,%r5,32
+ clrldi %r5,%r6,32
+
+ li %r11,217
+ hc
+ extsw %r3,%r3
+
+ lwz %r7,-4(%r1)
+ std %r4,0(%r7)
+
+ lwz %r0,4(%r1)
+ mtlr %r0
+ blr
+
+.global lv1_gpu_memory_allocate
+lv1_gpu_memory_allocate:
+ mflr %r0
+ stw %r0,4(%r1)
+ stw %r8,-4(%r1)
+ stw %r9,-8(%r1)
+
+ li %r11,214
+ hc
+ extsw %r3,%r3
+
+ lwz %r8,-4(%r1)
+ lwz %r9,-8(%r1)
+ std %r4,0(%r8)
+ std %r5,0(%r9)
+
+ lwz %r0,4(%r1)
+ mtlr %r0
+ blr
+
+.global lv1_net_control
+lv1_net_control:
+ mflr %r0
+ stw %r0,4(%r1)
+ stw %r9,-4(%r1)
+
+ li %r11,194
+ hc
+ extsw %r3,%r3
+
+ lwz %r8,-4(%r1)
+ std %r4,0(%r8)
+
+ lwz %r0,4(%r1)
+ mtlr %r0
+ blr
+
+.global lv1_setup_dma
+lv1_setup_dma:
+ mflr %r0
+ stw %r0,4(%r1)
+ stw %r3,-4(%r1)
+ stw %r4,-8(%r1)
+ stw %r5,-12(%r1)
+
+ lwz %r3,-4(%r1)
+ lwz %r4,-8(%r1)
+ lis %r5,0x0800 /* 128 MB */
+ li %r6,24 /* log2(IO_PAGESIZE) */
+ li %r7,0 /* flags */
+ li %r11,174 /* lv1_allocate_device_dma_region */
+ hc
+ extsw %r3,%r3
+ cmpdi %r3,0
+ bne 1f
+ std %r4,-24(%r1)
+
+ lwz %r3,-4(%r1)
+ lwz %r4,-8(%r1)
+ li %r5,0
+ ld %r6,-24(%r1)
+ lis %r7,0x0800 /* 128 MB */
+ lis %r8,0xf800 /* flags */
+ sldi %r8,%r8,32
+ li %r11,176 /* lv1_map_device_dma_region */
+ hc
+ extsw %r3,%r3
+
+ lwz %r9,-12(%r1)
+ ld %r6,-24(%r1)
+ std %r6,0(%r9)
+
+1: lwz %r0,4(%r1)
+ mtlr %r0
+ blr
+
+.global lv1_get_repository_node_value
+lv1_get_repository_node_value:
+ mflr %r0
+ stw %r0,4(%r1)
+
+ sldi %r3,%r3,32
+ clrldi %r4,%r4,32
+ or %r3,%r3,%r4
+ sldi %r4,%r5,32
+ clrldi %r5,%r6,32
+ or %r4,%r4,%r5
+ sldi %r5,%r7,32
+ clrldi %r6,%r8,32
+ or %r5,%r5,%r6
+ sldi %r6,%r9,32
+ clrldi %r7,%r10,32
+ or %r6,%r6,%r7
+ lwz %r7,8(%r1)
+ lwz %r8,12(%r1)
+ sldi %r7,%r7,32
+ or %r7,%r7,%r8
+
+ li %r11,91
+ hc
+ extsw %r3,%r3
+
+ lwz %r6,16(%r1)
+ std %r4,0(%r6)
+ lwz %r6,20(%r1)
+ std %r5,0(%r6)
+
+ lwz %r0,4(%r1)
+ mtlr %r0
+ blr
+
+.global lv1_storage_read
+lv1_storage_read:
+ mflr %r0
+ stw %r0,4(%r1)
+
+ sldi %r3,%r3,32
+ clrldi %r4,%r4,32
+ or %r3,%r3,%r4
+ sldi %r4,%r5,32
+ clrldi %r5,%r6,32
+ or %r4,%r4,%r5
+ sldi %r5,%r7,32
+ clrldi %r6,%r8,32
+ or %r5,%r5,%r6
+ sldi %r6,%r9,32
+ clrldi %r7,%r10,32
+ or %r6,%r6,%r7
+ ld %r7,8(%r1)
+ ld %r8,16(%r1)
+
+ li %r11,245
+ hc
+ extsw %r3,%r3
+
+ lwz %r5,24(%r1)
+ std %r4,0(%r5)
+
+ lwz %r0,4(%r1)
+ mtlr %r0
+ blr
+
+.global lv1_storage_check_async_status
+lv1_storage_check_async_status:
+ mflr %r0
+ stw %r0,4(%r1)
+ stw %r7,-4(%r1)
+
+ sldi %r3,%r3,32
+ clrldi %r4,%r4,32
+ or %r3,%r3,%r4
+ sldi %r4,%r5,32
+ clrldi %r5,%r6,32
+ or %r4,%r4,%r5
+
+ li %r11,254
+ hc
+ extsw %r3,%r3
+
+ lwz %r5,-4(%r1)
+ std %r4,0(%r5)
+
+ lwz %r0,4(%r1)
+ mtlr %r0
+ blr
diff --git a/stand/powerpc/ps3/lv1call.h b/stand/powerpc/ps3/lv1call.h
new file mode 100644
index 0000000..fb80448
--- /dev/null
+++ b/stand/powerpc/ps3/lv1call.h
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PS3_LV1CALL_H
+#define _PS3_LV1CALL_H
+
+#include <machine/pte.h>
+
+int lv1_get_physmem(uint64_t *maxmem);
+int lv1_setup_address_space(uint64_t *as_id, uint64_t *ptsize);
+int lv1_insert_pte(u_int ptegidx, struct lpte *pte, int lockflags);
+int lv1_panic(int reboot);
+
+#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET 0x0100
+#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x0101
+#define L1GPU_DISPLAY_SYNC_HSYNC 1
+#define L1GPU_DISPLAY_SYNC_VSYNC 2
+#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x0102
+
+int lv1_gpu_open(int);
+int lv1_gpu_context_attribute(int context, int op, int, int, int, int);
+int lv1_gpu_memory_allocate(int size, int, int, int, int, uint64_t *handle,
+ uint64_t *paddr);
+int lv1_gpu_context_allocate(uint64_t handle, int, uint64_t *context);
+
+int lv1_open_device(int, int, int /* 0 */);
+int lv1_close_device(int, int);
+int lv1_setup_dma(int, int, uint64_t *dmabase);
+
+#define GELIC_GET_MAC_ADDRESS 0x0001
+#define GELIC_GET_LINK_STATUS 0x0002
+#define GELIC_LINK_UP 0x0001
+#define GELIC_FULL_DUPLEX 0x0002
+#define GELIC_AUTO_NEG 0x0004
+#define GELIC_SPEED_10 0x0010
+#define GELIC_SPEED_100 0x0020
+#define GELIC_SPEED_1000 0x0040
+#define GELIC_GET_VLAN_ID 0x0004
+
+int lv1_net_init(int bus, int dev);
+int lv1_net_control(int bus, int dev, int, int, int, int, uint64_t *);
+int lv1_net_start_tx_dma(int bus, int dev, uint32_t addr, int);
+int lv1_net_start_rx_dma(int bus, int dev, uint32_t addr, int);
+int lv1_net_stop_tx_dma(int bus, int dev, int);
+int lv1_net_stop_rx_dma(int bus, int dev, int);
+
+int lv1_get_repository_node_value(uint64_t lpar_id, uint64_t n1, uint64_t n2,
+ uint64_t n3, uint64_t n4, uint64_t *v1, uint64_t *v2);
+
+int lv1_storage_read(uint64_t dev_id, uint64_t region_id, uint64_t start_sector,
+ uint64_t sector_count, uint64_t flags, uint64_t buf, uint64_t *tag);
+int lv1_storage_check_async_status(uint64_t dev_id, uint64_t tag,
+ uint64_t *status);
+
+#endif
+
diff --git a/stand/powerpc/ps3/main.c b/stand/powerpc/ps3/main.c
new file mode 100644
index 0000000..be8708a
--- /dev/null
+++ b/stand/powerpc/ps3/main.c
@@ -0,0 +1,248 @@
+/*-
+ * Copyright (C) 2010 Nathan Whitehorn
+ * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
+ * 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 ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+
+#define _KERNEL
+#include <machine/cpufunc.h>
+
+#include "bootstrap.h"
+#include "lv1call.h"
+#include "ps3.h"
+#include "ps3devdesc.h"
+
+struct arch_switch archsw;
+extern void *_end;
+
+extern char bootprog_info[];
+
+int ps3_getdev(void **vdev, const char *devspec, const char **path);
+ssize_t ps3_copyin(const void *src, vm_offset_t dest, const size_t len);
+ssize_t ps3_copyout(vm_offset_t src, void *dest, const size_t len);
+ssize_t ps3_readin(const int fd, vm_offset_t dest, const size_t len);
+int ps3_autoload(void);
+int ps3_setcurrdev(struct env_var *ev, int flags, const void *value);
+
+static uint64_t basetb;
+
+int
+main(void)
+{
+ uint64_t maxmem = 0;
+ void *heapbase;
+ int i, err;
+ struct ps3_devdesc currdev;
+ struct open_file f;
+
+ lv1_get_physmem(&maxmem);
+
+ ps3mmu_init(maxmem);
+
+ /*
+ * Set up console.
+ */
+ cons_probe();
+
+ /*
+ * Set the heap to one page after the end of the loader.
+ */
+ heapbase = (void *)(maxmem - 0x80000);
+ setheap(heapbase, maxmem);
+
+ /*
+ * March through the device switch probing for things.
+ */
+ for (i = 0; devsw[i] != NULL; i++) {
+ if (devsw[i]->dv_init != NULL) {
+ err = (devsw[i]->dv_init)();
+ if (err) {
+ printf("\n%s: initialization failed err=%d\n",
+ devsw[i]->dv_name, err);
+ continue;
+ }
+ }
+
+ currdev.d_dev = devsw[i];
+ currdev.d_type = currdev.d_dev->dv_type;
+
+ if (strcmp(devsw[i]->dv_name, "cd") == 0) {
+ f.f_devdata = &currdev;
+ currdev.d_unit = 0;
+
+ if (devsw[i]->dv_open(&f, &currdev) == 0)
+ break;
+ }
+
+ if (strcmp(devsw[i]->dv_name, "disk") == 0) {
+ f.f_devdata = &currdev;
+ currdev.d_unit = 3;
+ currdev.d_disk.pnum = 1;
+ currdev.d_disk.ptype = PTYPE_GPT;
+
+ if (devsw[i]->dv_open(&f, &currdev) == 0)
+ break;
+ }
+
+ if (strcmp(devsw[i]->dv_name, "net") == 0)
+ break;
+ }
+
+ if (devsw[i] == NULL)
+ panic("No boot device found!");
+ else
+ printf("Boot device: %s\n", devsw[i]->dv_name);
+
+ /*
+ * Get timebase at boot.
+ */
+ basetb = mftb();
+
+ archsw.arch_getdev = ps3_getdev;
+ archsw.arch_copyin = ps3_copyin;
+ archsw.arch_copyout = ps3_copyout;
+ archsw.arch_readin = ps3_readin;
+ archsw.arch_autoload = ps3_autoload;
+
+ printf("\n%s", bootprog_info);
+ printf("Memory: %lldKB\n", maxmem / 1024);
+
+ env_setenv("currdev", EV_VOLATILE, ps3_fmtdev(&currdev),
+ ps3_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, ps3_fmtdev(&currdev), env_noset,
+ env_nounset);
+ setenv("LINES", "24", 1);
+ setenv("hw.platform", "ps3", 1);
+
+ interact(NULL); /* doesn't return */
+
+ return (0);
+}
+
+void
+ppc_exception(int code, vm_offset_t where, register_t msr)
+{
+ mtmsr(PSL_IR | PSL_DR | PSL_RI);
+ printf("Exception %x at %#lx!\n", code, where);
+ printf("Rebooting in 5 seconds...\n");
+ delay(10000000);
+ lv1_panic(1);
+}
+
+const u_int ns_per_tick = 12;
+
+void
+exit(int code)
+{
+ lv1_panic(code);
+}
+
+void
+delay(int usecs)
+{
+ uint64_t tb,ttb;
+ tb = mftb();
+
+ ttb = tb + howmany(usecs * 1000, ns_per_tick);
+ while (tb < ttb)
+ tb = mftb();
+}
+
+time_t
+getsecs(void)
+{
+ return ((time_t)((mftb() - basetb)*ns_per_tick/1000000000));
+}
+
+time_t
+time(time_t *tloc)
+{
+ time_t rv;
+
+ rv = getsecs();
+ if (tloc != NULL)
+ *tloc = rv;
+
+ return (rv);
+}
+
+ssize_t
+ps3_copyin(const void *src, vm_offset_t dest, const size_t len)
+{
+ bcopy(src, (void *)dest, len);
+ return (len);
+}
+
+ssize_t
+ps3_copyout(vm_offset_t src, void *dest, const size_t len)
+{
+ bcopy((void *)src, dest, len);
+ return (len);
+}
+
+ssize_t
+ps3_readin(const int fd, vm_offset_t dest, const size_t len)
+{
+ void *buf;
+ size_t resid, chunk, get;
+ ssize_t got;
+ vm_offset_t p;
+
+ p = dest;
+
+ chunk = min(PAGE_SIZE, len);
+ buf = malloc(chunk);
+ if (buf == NULL) {
+ printf("ps3_readin: buf malloc failed\n");
+ return(0);
+ }
+
+ for (resid = len; resid > 0; resid -= got, p += got) {
+ get = min(chunk, resid);
+ got = read(fd, buf, get);
+ if (got <= 0) {
+ if (got < 0)
+ printf("ps3_readin: read failed\n");
+ break;
+ }
+
+ bcopy(buf, (void *)p, got);
+ }
+
+ free(buf);
+ return (len - resid);
+}
+
+int
+ps3_autoload(void)
+{
+
+ return (0);
+}
+
diff --git a/stand/powerpc/ps3/metadata.c b/stand/powerpc/ps3/metadata.c
new file mode 100644
index 0000000..6f29c57
--- /dev/null
+++ b/stand/powerpc/ps3/metadata.c
@@ -0,0 +1,333 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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.
+ *
+ * from: FreeBSD: src/sys/boot/sparc64/loader/metadata.c,v 1.6
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/boot.h>
+
+#include <machine/metadata.h>
+
+#include "bootstrap.h"
+
+int
+md_getboothowto(char *kargs)
+{
+ char *cp;
+ int howto;
+ int active;
+ int i;
+
+ /* Parse kargs */
+ howto = 0;
+ if (kargs != NULL) {
+ cp = kargs;
+ active = 0;
+ while (*cp != 0) {
+ if (!active && (*cp == '-')) {
+ active = 1;
+ } else if (active)
+ switch (*cp) {
+ case 'a':
+ howto |= RB_ASKNAME;
+ break;
+ case 'C':
+ howto |= RB_CDROM;
+ break;
+ case 'd':
+ howto |= RB_KDB;
+ break;
+ case 'D':
+ howto |= RB_MULTIPLE;
+ break;
+ case 'm':
+ howto |= RB_MUTE;
+ break;
+ case 'g':
+ howto |= RB_GDB;
+ break;
+ case 'h':
+ howto |= RB_SERIAL;
+ break;
+ case 'p':
+ howto |= RB_PAUSE;
+ break;
+ case 'r':
+ howto |= RB_DFLTROOT;
+ break;
+ case 's':
+ howto |= RB_SINGLE;
+ break;
+ case 'v':
+ howto |= RB_VERBOSE;
+ break;
+ default:
+ active = 0;
+ break;
+ }
+ cp++;
+ }
+ }
+ /* get equivalents from the environment */
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+ if (!strcmp(getenv("console"), "comconsole"))
+ howto |= RB_SERIAL;
+ if (!strcmp(getenv("console"), "nullconsole"))
+ howto |= RB_MUTE;
+ return(howto);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+vm_offset_t
+md_copyenv(vm_offset_t addr)
+{
+ struct env_var *ep;
+
+ /* traverse the environment */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ archsw.arch_copyin(ep->ev_name, addr, strlen(ep->ev_name));
+ addr += strlen(ep->ev_name);
+ archsw.arch_copyin("=", addr, 1);
+ addr++;
+ if (ep->ev_value != NULL) {
+ archsw.arch_copyin(ep->ev_value, addr, strlen(ep->ev_value));
+ addr += strlen(ep->ev_value);
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ return(addr);
+}
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+
+static int align;
+
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ archsw.arch_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c) \
+ if (c) \
+ archsw.arch_copyin(s, a, strlen(s) + 1);\
+ a += roundup(strlen(s) + 1, align); \
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ archsw.arch_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), align); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c);\
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ archsw.arch_copyin(mm->md_data, a, mm->md_size);\
+ a += roundup(mm->md_size, align); \
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+vm_offset_t
+md_copymodules(vm_offset_t addr, int kern64)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ uint64_t scratch64;
+ int c;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must come first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ if (kern64) {
+ scratch64 = fp->f_addr;
+ MOD_ADDR(addr, scratch64, c);
+ scratch64 = fp->f_size;
+ MOD_SIZE(addr, scratch64, c);
+ } else {
+ MOD_ADDR(addr, fp->f_addr, c);
+ MOD_SIZE(addr, fp->f_size, c);
+ }
+ for (md = fp->f_metadata; md != NULL; md = md->md_next) {
+ if (!(md->md_type & MODINFOMD_NOCOPY)) {
+ MOD_METADATA(addr, md, c);
+ }
+ }
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Load the information expected by a powerpc kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+md_load_dual(char *args, vm_offset_t *modulep, int kern64)
+{
+ struct preloaded_file *kfp;
+ struct preloaded_file *xp;
+ struct file_metadata *md;
+ vm_offset_t kernend;
+ vm_offset_t addr;
+ vm_offset_t envp;
+ vm_offset_t size;
+ uint64_t scratch64;
+ char *rootdevname;
+ int howto;
+
+ align = kern64 ? 8 : 4;
+ howto = md_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied device
+ * This should perhaps go to MI code and/or have $rootdev tested/set by
+ * MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ if (rootdevname == NULL)
+ rootdevname = getenv("currdev");
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(rootdevname);
+
+ /* find the last module in the chain */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* copy our environment */
+ envp = addr;
+ addr = md_copyenv(addr);
+
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ kernend = 0;
+ kfp = file_findfile(NULL, kern64 ? "elf64 kernel" : "elf32 kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ if (kern64) {
+ scratch64 = envp;
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof scratch64, &scratch64);
+ scratch64 = kernend;
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof scratch64, &scratch64);
+ } else {
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ }
+
+ *modulep = addr;
+ size = md_copymodules(0, kern64);
+ kernend = roundup(addr + size, PAGE_SIZE);
+
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ if (kern64) {
+ scratch64 = kernend;
+ bcopy(&scratch64, md->md_data, sizeof scratch64);
+ } else {
+ bcopy(&kernend, md->md_data, sizeof kernend);
+ }
+
+ (void)md_copymodules(addr, kern64);
+
+ return(0);
+}
+
+int
+md_load(char *args, vm_offset_t *modulep)
+{
+ return (md_load_dual(args, modulep, 0));
+}
+
+int
+md_load64(char *args, vm_offset_t *modulep)
+{
+ return (md_load_dual(args, modulep, 1));
+}
+
diff --git a/stand/powerpc/ps3/ppc64_elf_freebsd.c b/stand/powerpc/ps3/ppc64_elf_freebsd.c
new file mode 100644
index 0000000..3eb991e
--- /dev/null
+++ b/stand/powerpc/ps3/ppc64_elf_freebsd.c
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 2001 Benno Rice <benno@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+
+#include <sys/param.h>
+#include <sys/linker.h>
+
+#include <machine/metadata.h>
+#include <machine/elf.h>
+
+#include <stand.h>
+
+#include "bootstrap.h"
+
+extern char end[];
+extern vm_offset_t reloc; /* From <arch>/conf.c */
+
+int
+ppc64_elf_loadfile(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ int r;
+
+ r = __elfN(loadfile)(filename, dest, result);
+ if (r != 0)
+ return (r);
+
+ /*
+ * No need to sync the icache for modules: this will
+ * be done by the kernel after relocation.
+ */
+ if (!strcmp((*result)->f_type, "elf kernel"))
+ __syncicache((void *) (*result)->f_addr, (*result)->f_size);
+ return (0);
+}
+
+int
+ppc64_elf_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *fmp;
+ vm_offset_t mdp;
+ Elf_Ehdr *e;
+ int error;
+ int (*entry)(u_long, u_long, u_long, void *, u_long);
+
+ if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) {
+ return(EFTYPE);
+ }
+ e = (Elf_Ehdr *)&fmp->md_data;
+
+ /* Handle function descriptor for ELFv1 kernels */
+ if ((e->e_flags & 3) == 2)
+ entry = e->e_entry;
+ else
+ entry = (void *)(uintptr_t)(*(uint64_t *)e->e_entry);
+
+ if ((error = md_load64(fp->f_args, &mdp)) != 0)
+ return (error);
+
+ printf("Kernel entry at %p ...\n", entry);
+
+ dev_cleanup();
+
+ entry(0 /* FDT */, 0 /* Phys. mem offset */, 0 /* OF entry */,
+ (void *)mdp, sizeof(mdp));
+
+ panic("exec returned");
+}
+
+struct file_format ppc_elf64 =
+{
+ ppc64_elf_loadfile,
+ ppc64_elf_exec
+};
diff --git a/stand/powerpc/ps3/ps3.h b/stand/powerpc/ps3/ps3.h
new file mode 100644
index 0000000..1a77002
--- /dev/null
+++ b/stand/powerpc/ps3/ps3.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PS3_H
+#define _PS3_H
+
+int ps3mmu_init(int maxmem);
+int ps3mmu_map(uint64_t va, uint64_t pa);
+void *ps3mmu_mapdev(uint64_t pa, size_t length);
+
+#endif
diff --git a/stand/powerpc/ps3/ps3bus.h b/stand/powerpc/ps3/ps3bus.h
new file mode 100644
index 0000000..a3b20f3
--- /dev/null
+++ b/stand/powerpc/ps3/ps3bus.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PS3_BUS_H
+#define _PS3_BUS_H
+
+enum {
+ PS3_BUS_TYPE_STOR = 5,
+};
+
+enum {
+ PS3_DEV_TYPE_STOR_DISK = 0,
+ PS3_DEV_TYPE_STOR_CDROM = 5,
+ PS3_DEV_TYPE_STOR_FLASH = 14,
+};
+
+#endif
diff --git a/stand/powerpc/ps3/ps3cdrom.c b/stand/powerpc/ps3/ps3cdrom.c
new file mode 100644
index 0000000..d8d41fd
--- /dev/null
+++ b/stand/powerpc/ps3/ps3cdrom.c
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (C) 2011 glevand <geoffrey.levand@mail.ru>
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <machine/stdarg.h>
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "ps3bus.h"
+#include "ps3devdesc.h"
+#include "ps3stor.h"
+
+#define dev_printf(dev, fmt, args...) \
+ printf("%s%d: " fmt "\n", dev->d_dev->dv_name, dev->d_unit, ##args)
+
+#ifdef CD_DEBUG
+#define DEBUG(fmt, args...) printf("%s:%d: " fmt "\n", __func__, __LINE__, ##args)
+#else
+#define DEBUG(fmt, args...)
+#endif
+
+static int ps3cdrom_init(void);
+static int ps3cdrom_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int ps3cdrom_open(struct open_file *f, ...);
+static int ps3cdrom_close(struct open_file *f);
+static int ps3cdrom_print(int verbose);
+
+struct devsw ps3cdrom = {
+ "cd",
+ DEVT_CD,
+ ps3cdrom_init,
+ ps3cdrom_strategy,
+ ps3cdrom_open,
+ ps3cdrom_close,
+ noioctl,
+ ps3cdrom_print,
+};
+
+static struct ps3_stordev stor_dev;
+
+static int ps3cdrom_init(void)
+{
+ int err;
+
+ err = ps3stor_setup(&stor_dev, PS3_DEV_TYPE_STOR_CDROM);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int ps3cdrom_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize)
+{
+ struct ps3_devdesc *dev = (struct ps3_devdesc *) devdata;
+ int err;
+
+ DEBUG("d_unit=%u dblk=%llu size=%u", dev->d_unit, dblk, size);
+
+ flag &= F_MASK;
+ if (flag != F_READ) {
+ dev_printf(dev, "write operation is not supported!");
+ return EROFS;
+ }
+
+ if (dblk % (stor_dev.sd_blksize / DEV_BSIZE) != 0)
+ return EINVAL;
+
+ dblk /= (stor_dev.sd_blksize / DEV_BSIZE);
+
+ if (size % stor_dev.sd_blksize) {
+ dev_printf(dev,
+ "size=%u is not multiple of device block size=%llu", size,
+ stor_dev.sd_blksize);
+ return EINVAL;
+ }
+
+ if (rsize)
+ *rsize = 0;
+
+ err = ps3stor_read_sectors(&stor_dev, dev->d_unit, dblk,
+ size / stor_dev.sd_blksize, 0, buf);
+
+ if (!err && rsize)
+ *rsize = size;
+
+ if (err)
+ dev_printf(dev,
+ "read operation failed dblk=%llu size=%d err=%d", dblk,
+ size, err);
+
+ return err;
+}
+
+static int ps3cdrom_open(struct open_file *f, ...)
+{
+ char buf[2048];
+ va_list ap;
+ struct ps3_devdesc *dev;
+ int err;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct ps3_devdesc *);
+ va_end(ap);
+
+ if (dev->d_unit > 0) {
+ dev_printf(dev, "attempt to open nonexistent disk");
+ return ENXIO;
+ }
+
+ err = ps3stor_read_sectors(&stor_dev, dev->d_unit, 16, 1, 0, buf);
+ if (err)
+ return EIO;
+
+ /* Do not attach if not ISO9660 (workaround for buggy firmware) */
+ if (memcmp(buf, "\001CD001", 6) != 0)
+ return EIO;
+
+ return 0;
+}
+
+static int ps3cdrom_close(struct open_file *f)
+{
+ return 0;
+}
+
+static int ps3cdrom_print(int verbose)
+{
+ return (0);
+}
diff --git a/stand/powerpc/ps3/ps3cons.c b/stand/powerpc/ps3/ps3cons.c
new file mode 100644
index 0000000..fa9ef32
--- /dev/null
+++ b/stand/powerpc/ps3/ps3cons.c
@@ -0,0 +1,173 @@
+/*-
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "bootstrap.h"
+#include "font.h"
+#include "lv1call.h"
+#include "ps3.h"
+
+#define FONT_SIZE 14
+#define FONT dflt_font_14
+#define XMARGIN 40
+#define YMARGIN 30
+#define BG_COLOR 0x00000000
+#define FG_COLOR 0xffffffff
+
+#define FB_SIZE (16*1024*1024)
+uint64_t fb_paddr = 0;
+uint32_t *fb_vaddr;
+
+int fb_width, fb_height;
+int x, y;
+
+static void ps3cons_probe(struct console *cp);
+static int ps3cons_init(int arg);
+static void ps3cons_putchar(int c);
+static int ps3cons_getchar();
+static int ps3cons_poll();
+
+struct console ps3console = {
+ "ps3",
+ "Playstation 3 Framebuffer",
+ 0,
+ ps3cons_probe,
+ ps3cons_init,
+ ps3cons_putchar,
+ ps3cons_getchar,
+ ps3cons_poll,
+};
+
+static void
+ps3cons_probe(struct console *cp)
+{
+ /* XXX: Get from HV */
+ fb_width = 720;
+ fb_height = 480;
+
+ cp->c_flags |= C_PRESENTIN|C_PRESENTOUT;
+}
+
+static int
+ps3cons_init(int arg)
+{
+ uint64_t fbhandle, fbcontext;
+ int i;
+
+ lv1_gpu_open(0);
+ lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET,
+ 0,0,0,0);
+ lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET,
+ 0,0,1,0);
+ lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
+ 0,L1GPU_DISPLAY_SYNC_VSYNC,0,0);
+ lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
+ 1,L1GPU_DISPLAY_SYNC_VSYNC,0,0);
+ lv1_gpu_memory_allocate(FB_SIZE, 0, 0, 0, 0, &fbhandle, &fb_paddr);
+ lv1_gpu_context_allocate(fbhandle, 0, &fbcontext);
+
+ lv1_gpu_context_attribute(fbcontext,
+ L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 0, 0, 0, 0);
+ lv1_gpu_context_attribute(fbcontext,
+ L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 1, 0, 0, 0);
+
+ fb_vaddr = ps3mmu_mapdev(fb_paddr, FB_SIZE);
+
+ x = y = 0;
+
+ /* Blank console */
+ for (i = 0; i < fb_width*fb_height; i++)
+ fb_vaddr[i] = BG_COLOR;
+
+ return (0);
+}
+
+static void
+ps3cons_putchar(int c)
+{
+ uint32_t fg, bg;
+ uint32_t *addr;
+ int i, j, k;
+ u_char *p;
+
+ fg = FG_COLOR;
+ bg = BG_COLOR;
+
+ switch (c) {
+ case '\0':
+ break;
+ case '\r':
+ x = 0;
+ break;
+ case '\n':
+ y += FONT_SIZE;
+ break;
+ case '\b':
+ x = max(0, x - 8);
+ break;
+ default:
+ /* Wrap long lines */
+ if (x + XMARGIN + FONT_SIZE > fb_width - XMARGIN) {
+ y += FONT_SIZE;
+ x = 0;
+ }
+
+ if (y + YMARGIN + FONT_SIZE > fb_height - YMARGIN)
+ y = 0;
+
+ addr = fb_vaddr + (y + YMARGIN)*fb_width + (x + XMARGIN);
+ p = FONT + c*FONT_SIZE;
+
+ for (i = 0; i < FONT_SIZE; i++) {
+ for (j = 0, k = 7; j < 8; j++, k--) {
+ if ((p[i] & (1 << k)) == 0)
+ *(addr + j) = bg;
+ else
+ *(addr + j) = fg;
+ }
+
+ addr += fb_width;
+ }
+
+ x += 8;
+ break;
+ }
+}
+
+static int
+ps3cons_getchar()
+{
+ return (-1);
+}
+
+static int
+ps3cons_poll()
+{
+ return (0);
+}
+
diff --git a/stand/powerpc/ps3/ps3devdesc.h b/stand/powerpc/ps3/ps3devdesc.h
new file mode 100644
index 0000000..5a6e52f
--- /dev/null
+++ b/stand/powerpc/ps3/ps3devdesc.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (C) 2000 Benno Rice.
+ * Copyright (C) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PS3_DEV_DESC_H
+#define _PS3_DEV_DESC_H
+
+/* Note: Must match the 'struct devdesc' in bootstrap.h */
+struct ps3_devdesc {
+ struct devsw *d_dev;
+ int d_type;
+ int d_unit;
+
+ union {
+ struct {
+ void *data;
+ int pnum;
+ int ptype;
+ } disk;
+ } d_kind;
+};
+
+#define d_disk d_kind.disk
+
+#define PTYPE_BSDLABEL 1
+#define PTYPE_GPT 2
+
+#endif
diff --git a/stand/powerpc/ps3/ps3disk.c b/stand/powerpc/ps3/ps3disk.c
new file mode 100644
index 0000000..c33a023
--- /dev/null
+++ b/stand/powerpc/ps3/ps3disk.c
@@ -0,0 +1,315 @@
+/*-
+ * Copyright (C) 2008 Semihalf, Rafal Jaworowski
+ * Copyright (C) 2009 Semihalf, Piotr Ziecik
+ * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/endian.h>
+#include <machine/stdarg.h>
+#include <stand.h>
+#include <uuid.h>
+
+#define FSTYPENAMES
+#include <sys/disklabel.h>
+#include <sys/diskmbr.h>
+#include <sys/gpt.h>
+
+#include "bootstrap.h"
+#include "ps3bus.h"
+#include "ps3devdesc.h"
+#include "ps3stor.h"
+
+#define dev_printf(dev, fmt, args...) \
+ printf("%s%d: " fmt "\n" , dev->d_dev->dv_name, dev->d_unit, ##args)
+
+#ifdef DISK_DEBUG
+#define DEBUG(fmt, args...) printf("%s:%d: " fmt "\n" , __func__ , __LINE__, ##args)
+#else
+#define DEBUG(fmt, args...)
+#endif
+
+struct open_dev;
+
+static int ps3disk_open_gpt(struct ps3_devdesc *dev, struct open_dev *od);
+static void ps3disk_uuid_letoh(uuid_t *uuid);
+
+static int ps3disk_init(void);
+static int ps3disk_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int ps3disk_open(struct open_file *f, ...);
+static int ps3disk_close(struct open_file *f);
+static int ps3disk_print(int verbose);
+
+struct devsw ps3disk = {
+ "disk",
+ DEVT_DISK,
+ ps3disk_init,
+ ps3disk_strategy,
+ ps3disk_open,
+ ps3disk_close,
+ noioctl,
+ ps3disk_print,
+};
+
+struct gpt_part {
+ int gp_index;
+ uuid_t gp_type;
+ uint64_t gp_start;
+ uint64_t gp_end;
+};
+
+struct open_dev {
+ uint64_t od_start;
+
+ union {
+ struct {
+ int nparts;
+ struct gpt_part *parts;
+ } gpt;
+ } od_kind;
+};
+
+#define od_gpt_nparts od_kind.gpt.nparts
+#define od_gpt_parts od_kind.gpt.parts
+
+static struct ps3_stordev stor_dev;
+
+static int ps3disk_init(void)
+{
+ int err;
+
+ err = ps3stor_setup(&stor_dev, PS3_DEV_TYPE_STOR_DISK);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int ps3disk_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize)
+{
+ struct ps3_devdesc *dev = (struct ps3_devdesc *) devdata;
+ struct open_dev *od = (struct open_dev *) dev->d_disk.data;
+ int err;
+
+ flag &= F_MASK;
+ if (flag != F_READ) {
+ dev_printf(dev, "write operation is not supported!\n");
+ return EROFS;
+ }
+
+ if (size % stor_dev.sd_blksize) {
+ dev_printf(dev, "size=%u is not multiple of device block size=%llu\n",
+ size, stor_dev.sd_blksize);
+ return EIO;
+ }
+
+ if (rsize)
+ *rsize = 0;
+
+ err = ps3stor_read_sectors(&stor_dev, dev->d_unit, od->od_start + dblk,
+ size / stor_dev.sd_blksize, 0, buf);
+
+ if (!err && rsize)
+ *rsize = size;
+
+ if (err)
+ dev_printf(dev, "read operation failed dblk=%llu size=%d err=%d\n",
+ dblk, size, err);
+
+ return err;
+}
+
+static int ps3disk_open(struct open_file *f, ...)
+{
+ va_list ap;
+ struct ps3_devdesc *dev;
+ struct open_dev *od;
+ int err;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct ps3_devdesc *);
+ va_end(ap);
+
+ od = malloc(sizeof(struct open_dev));
+ if (!od) {
+ dev_printf(dev, "couldn't allocate memory for new open_dev\n");
+ return ENOMEM;
+ }
+
+ err = ps3disk_open_gpt(dev, od);
+
+ if (err) {
+ dev_printf(dev, "couldn't open GPT disk error=%d\n", err);
+ free(od);
+ } else {
+ ((struct ps3_devdesc *) (f->f_devdata))->d_disk.data = od;
+ }
+
+ return err;
+}
+
+static int ps3disk_close(struct open_file *f)
+{
+ struct ps3_devdesc *dev = f->f_devdata;
+ struct open_dev *od = dev->d_disk.data;
+
+ if (dev->d_disk.ptype == PTYPE_GPT && od->od_gpt_nparts)
+ free(od->od_gpt_parts);
+
+ free(od);
+
+ dev->d_disk.data = NULL;
+
+ return 0;
+}
+
+static int ps3disk_print(int verbose)
+{
+ return (0);
+}
+
+static int ps3disk_open_gpt(struct ps3_devdesc *dev, struct open_dev *od)
+{
+ char buf[512];
+ struct gpt_hdr *hdr;
+ struct gpt_ent *ent;
+ daddr_t slba, elba, lba;
+ int nparts, eps, i, part, err;
+
+ od->od_gpt_nparts = 0;
+ od->od_gpt_parts = NULL;
+
+ err = ps3stor_read_sectors(&stor_dev, dev->d_unit, 0, 1, 0, buf);
+ if (err) {
+ err = EIO;
+ goto out;
+ }
+
+ if (le16toh(*((uint16_t *) (buf + DOSMAGICOFFSET))) != DOSMAGIC) {
+ err = ENXIO;
+ goto out;
+ }
+
+ err = ps3stor_read_sectors(&stor_dev, dev->d_unit, 1, 1, 0, buf);
+ if (err) {
+ err = EIO;
+ goto out;
+ }
+
+ hdr = (struct gpt_hdr *) buf;
+
+ if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) ||
+ le64toh(hdr->hdr_lba_self) != 1 || le32toh(hdr->hdr_revision) < 0x00010000 ||
+ le32toh(hdr->hdr_entsz) < sizeof(struct gpt_ent) ||
+ stor_dev.sd_blksize % le32toh(hdr->hdr_entsz) != 0) {
+ err = ENXIO;
+ goto out;
+ }
+
+ nparts = 0;
+ eps = stor_dev.sd_blksize / le32toh(hdr->hdr_entsz);
+ slba = le64toh(hdr->hdr_lba_table);
+ elba = slba + le32toh(hdr->hdr_entries) / eps;
+
+ for (lba = slba; lba < elba; lba++) {
+ err = ps3stor_read_sectors(&stor_dev, dev->d_unit, lba, 1, 0, buf);
+ if (err) {
+ err = EIO;
+ goto out;
+ }
+
+ ent = (struct gpt_ent *) buf;
+
+ for (i = 0; i < eps; i++) {
+ if (uuid_is_nil(&ent[i].ent_type, NULL) ||
+ le64toh(ent[i].ent_lba_start) == 0 ||
+ le64toh(ent[i].ent_lba_end) < le64toh(ent[i].ent_lba_start))
+ continue;
+
+ nparts++;
+ }
+ }
+
+ if (nparts) {
+ od->od_gpt_nparts = nparts;
+
+ od->od_gpt_parts = malloc(nparts * sizeof(struct gpt_part));
+ if (!od->od_gpt_parts) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ for (lba = slba, part = 0; lba < elba; lba++) {
+ err = ps3stor_read_sectors(&stor_dev, dev->d_unit, lba, 1, 0, buf);
+ if (err) {
+ err = EIO;
+ goto out;
+ }
+
+ ent = (struct gpt_ent *) buf;
+
+ for (i = 0; i < eps; i++) {
+ if (uuid_is_nil(&ent[i].ent_type, NULL) ||
+ le64toh(ent[i].ent_lba_start) == 0 ||
+ le64toh(ent[i].ent_lba_end) < le64toh(ent[i].ent_lba_start))
+ continue;
+
+ od->od_gpt_parts[part].gp_index = (lba - slba) * eps + i + 1;
+ od->od_gpt_parts[part].gp_type = ent[i].ent_type;
+ od->od_gpt_parts[part].gp_start = le64toh(ent[i].ent_lba_start);
+ od->od_gpt_parts[part].gp_end = le64toh(ent[i].ent_lba_end);
+ ps3disk_uuid_letoh(&od->od_gpt_parts[part].gp_type);
+ part++;
+ }
+ }
+ }
+
+ dev->d_disk.ptype = PTYPE_GPT;
+
+ if (od->od_gpt_nparts && !dev->d_disk.pnum)
+ dev->d_disk.pnum = od->od_gpt_parts[0].gp_index;
+
+ for (i = 0; i < od->od_gpt_nparts; i++)
+ if (od->od_gpt_parts[i].gp_index == dev->d_disk.pnum)
+ od->od_start = od->od_gpt_parts[i].gp_start;
+
+ err = 0;
+
+out:
+
+ if (err && od->od_gpt_parts)
+ free(od->od_gpt_parts);
+
+ return err;
+}
+
+static void ps3disk_uuid_letoh(uuid_t *uuid)
+{
+ uuid->time_low = le32toh(uuid->time_low);
+ uuid->time_mid = le16toh(uuid->time_mid);
+ uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version);
+}
diff --git a/stand/powerpc/ps3/ps3mmu.c b/stand/powerpc/ps3/ps3mmu.c
new file mode 100644
index 0000000..08dcf75
--- /dev/null
+++ b/stand/powerpc/ps3/ps3mmu.c
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <stdint.h>
+
+#define _KERNEL
+#include <machine/cpufunc.h>
+#include <machine/psl.h>
+#include <machine/pte.h>
+#include <machine/slb.h>
+#include <machine/param.h>
+
+#include "bootstrap.h"
+#include "lv1call.h"
+#include "ps3.h"
+
+register_t pteg_count, pteg_mask;
+uint64_t as_id;
+uint64_t virtual_avail;
+
+int
+ps3mmu_map(uint64_t va, uint64_t pa)
+{
+ struct lpte pt;
+ int shift;
+ uint64_t vsid, ptegidx;
+
+ if (pa < 0x8000000) { /* Phys mem? */
+ pt.pte_hi = LPTE_BIG;
+ pt.pte_lo = LPTE_M;
+ shift = 24;
+ vsid = 0;
+ } else {
+ pt.pte_hi = 0;
+ pt.pte_lo = LPTE_I | LPTE_G | LPTE_M | LPTE_NOEXEC;
+ shift = ADDR_PIDX_SHFT;
+ vsid = 1;
+ }
+
+ pt.pte_hi |= (vsid << LPTE_VSID_SHIFT) |
+ (((uint64_t)(va & ADDR_PIDX) >> ADDR_API_SHFT64) & LPTE_API);
+ pt.pte_lo |= pa;
+ ptegidx = vsid ^ (((uint64_t)va & ADDR_PIDX) >> shift);
+
+ pt.pte_hi |= LPTE_LOCKED | LPTE_VALID;
+ ptegidx &= pteg_mask;
+
+ return (lv1_insert_pte(ptegidx, &pt, LPTE_LOCKED));
+}
+
+void *
+ps3mmu_mapdev(uint64_t pa, size_t length)
+{
+ uint64_t spa;
+ void *mapstart;
+ int err;
+
+ mapstart = (void *)(uintptr_t)virtual_avail;
+
+ for (spa = pa; spa < pa + length; spa += PAGE_SIZE) {
+ err = ps3mmu_map(virtual_avail, spa);
+ virtual_avail += PAGE_SIZE;
+ if (err != 0)
+ return (NULL);
+ }
+
+ return (mapstart);
+}
+
+int
+ps3mmu_init(int maxmem)
+{
+ uint64_t ptsize;
+ int i;
+
+ i = lv1_setup_address_space(&as_id, &ptsize);
+ pteg_count = ptsize / sizeof(struct lpteg);
+ pteg_mask = pteg_count - 1;
+
+ for (i = 0; i < maxmem; i += 16*1024*1024)
+ ps3mmu_map(i,i);
+
+ virtual_avail = 0x10000000;
+
+ __asm __volatile ("slbia; slbmte %0, %1; slbmte %2,%3" ::
+ "r"((0 << SLBV_VSID_SHIFT) | SLBV_L), "r"(0 | SLBE_VALID),
+ "r"(1 << SLBV_VSID_SHIFT),
+ "r"((1 << SLBE_ESID_SHIFT) | SLBE_VALID | 1));
+
+ mtmsr(PSL_IR | PSL_DR | PSL_RI | PSL_ME);
+
+ return (0);
+}
+
diff --git a/stand/powerpc/ps3/ps3net.c b/stand/powerpc/ps3/ps3net.c
new file mode 100644
index 0000000..142eab8
--- /dev/null
+++ b/stand/powerpc/ps3/ps3net.c
@@ -0,0 +1,278 @@
+/*-
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+
+#define _KERNEL
+#include <machine/cpufunc.h>
+
+#include <stand.h>
+#include <net.h>
+#include <netif.h>
+#include "bootstrap.h"
+#include "lv1call.h"
+#include "ps3.h"
+
+#define GELIC_DESCR_OWNED 0xa0000000
+#define GELIC_CMDSTAT_NOIPSEC 0x00080000
+#define GELIC_CMDSTAT_LAST 0x00040000
+#define GELIC_RXERRORS 0x7def8000
+
+#define GELIC_POLL_PERIOD 100 /* microseconds */
+
+static int ps3net_probe(struct netif *, void *);
+static int ps3net_match(struct netif *, void *);
+static void ps3net_init(struct iodesc *, void *);
+static int ps3net_get(struct iodesc *, void *, size_t, time_t);
+static int ps3net_put(struct iodesc *, void *, size_t);
+static void ps3net_end(struct netif *);
+
+struct netif_stats ps3net_stats[1];
+struct netif_dif ps3net_ifs[] = {{0, 1, ps3net_stats, 0}};
+
+/* XXX: Get from firmware, not hardcoding */
+static int busid = 1;
+static int devid = 0;
+static int vlan;
+static uint64_t dma_base;
+
+struct gelic_dmadesc {
+ uint32_t paddr;
+ uint32_t len;
+ uint32_t next;
+ uint32_t cmd_stat;
+ uint32_t result_size;
+ uint32_t valid_size;
+ uint32_t data_stat;
+ uint32_t rxerror;
+};
+
+struct netif_driver ps3net = {
+ "net",
+ ps3net_match,
+ ps3net_probe,
+ ps3net_init,
+ ps3net_get,
+ ps3net_put,
+ ps3net_end,
+ ps3net_ifs, 1
+};
+
+static int
+ps3net_match(struct netif *nif, void *machdep_hint)
+{
+ return (1);
+}
+
+static int
+ps3net_probe(struct netif *nif, void *machdep_hint)
+{
+ return (0);
+}
+
+static int
+ps3net_put(struct iodesc *desc, void *pkt, size_t len)
+{
+ volatile static struct gelic_dmadesc txdesc __aligned(32);
+ volatile static char txbuf[1536] __aligned(128);
+ size_t sendlen;
+ int err;
+
+#if defined(NETIF_DEBUG)
+ struct ether_header *eh;
+
+ printf("net_put: desc %p, pkt %p, len %d\n", desc, pkt, len);
+ eh = pkt;
+ printf("dst: %s ", ether_sprintf(eh->ether_dhost));
+ printf("src: %s ", ether_sprintf(eh->ether_shost));
+ printf("type: 0x%x\n", eh->ether_type & 0xffff);
+#endif
+
+ while (txdesc.cmd_stat & GELIC_DESCR_OWNED) {
+ printf("Stalled XMIT!\n");
+ delay(10);
+ }
+
+ /*
+ * We must add 4 extra bytes to this packet to store the destination
+ * VLAN.
+ */
+ memcpy(txbuf, pkt, 12);
+ sendlen = 12;
+
+ if (vlan >= 0) {
+ sendlen += 4;
+ ((uint8_t *)txbuf)[12] = 0x81;
+ ((uint8_t *)txbuf)[13] = 0x00;
+ ((uint8_t *)txbuf)[14] = vlan >> 8;
+ ((uint8_t *)txbuf)[15] = vlan & 0xff;
+ }
+ memcpy((void *)txbuf + sendlen, pkt + 12, len - 12);
+ sendlen += len - 12;
+
+ bzero(&txdesc, sizeof(txdesc));
+ txdesc.paddr = dma_base + (uint32_t)txbuf;
+ txdesc.len = sendlen;
+ txdesc.cmd_stat = GELIC_CMDSTAT_NOIPSEC | GELIC_CMDSTAT_LAST |
+ GELIC_DESCR_OWNED;
+
+ powerpc_sync();
+
+ do {
+ err = lv1_net_start_tx_dma(busid, devid,
+ dma_base + (uint32_t)&txdesc, 0);
+ delay(1);
+ if (err != 0)
+ printf("TX Error: %d\n",err);
+ } while (err != 0);
+
+ return (len);
+}
+
+static int
+ps3net_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout)
+{
+ volatile static struct gelic_dmadesc rxdesc __aligned(32);
+ volatile static char rxbuf[1536] __aligned(128);
+ int err = 0;
+
+ if (len == 0)
+ goto restartdma;
+
+ timeout *= 1000000; /* convert to microseconds */
+ while (rxdesc.cmd_stat & GELIC_DESCR_OWNED) {
+ if (timeout < GELIC_POLL_PERIOD)
+ return (ETIMEDOUT);
+ delay(GELIC_POLL_PERIOD);
+ timeout -= GELIC_POLL_PERIOD;
+ }
+
+ delay(200);
+ if (rxdesc.rxerror & GELIC_RXERRORS) {
+ err = -1;
+ goto restartdma;
+ }
+
+ /*
+ * Copy the packet to the receive buffer, leaving out the
+ * 2 byte VLAN header.
+ */
+ len = min(len, rxdesc.valid_size - 2);
+ memcpy(pkt, (u_char *)rxbuf + 2, len);
+ err = len;
+
+#if defined(NETIF_DEBUG)
+{
+ struct ether_header *eh;
+
+ printf("net_get: desc %p, pkt %p, len %d\n", desc, pkt, len);
+ eh = pkt;
+ printf("dst: %s ", ether_sprintf(eh->ether_dhost));
+ printf("src: %s ", ether_sprintf(eh->ether_shost));
+ printf("type: 0x%x\n", eh->ether_type & 0xffff);
+}
+#endif
+
+restartdma:
+ lv1_net_stop_rx_dma(busid, devid, 0);
+ powerpc_sync();
+
+ bzero(&rxdesc, sizeof(rxdesc));
+ rxdesc.paddr = dma_base + (uint32_t)rxbuf;
+ rxdesc.len = sizeof(rxbuf);
+ rxdesc.next = 0;
+ rxdesc.cmd_stat = GELIC_DESCR_OWNED;
+ powerpc_sync();
+
+ lv1_net_start_rx_dma(busid, devid, dma_base + (uint32_t)&rxdesc, 0);
+
+ return (err);
+}
+
+static void
+ps3net_init(struct iodesc *desc, void *machdep_hint)
+{
+ uint64_t mac, val;
+ int i,err;
+
+ err = lv1_open_device(busid, devid, 0);
+
+ lv1_net_stop_tx_dma(busid, devid, 0);
+ lv1_net_stop_rx_dma(busid, devid, 0);
+
+ /*
+ * Wait for link to come up
+ */
+
+ for (i = 0; i < 1000; i++) {
+ lv1_net_control(busid, devid, GELIC_GET_LINK_STATUS, 2, 0,
+ 0, &val);
+ if (val & GELIC_LINK_UP)
+ break;
+ delay(500);
+ }
+
+ /*
+ * Set up DMA IOMMU entries
+ */
+
+ err = lv1_setup_dma(busid, devid, &dma_base);
+
+ /*
+ * Get MAC address and VLAN IDs
+ */
+
+ lv1_net_control(busid, devid, GELIC_GET_MAC_ADDRESS, 0, 0, 0, &mac);
+ bcopy(&((uint8_t *)&mac)[2], desc->myea, sizeof(desc->myea));
+
+ vlan = -1;
+ err = lv1_net_control(busid, devid, GELIC_GET_VLAN_ID, 2, 0,
+ 0, &val);
+ if (err == 0)
+ vlan = val;
+
+ /*
+ * Start RX DMA engine
+ */
+
+ ps3net_get(NULL, NULL, 0, 0);
+}
+
+static void
+ps3net_end(struct netif *nif)
+{
+ lv1_close_device(busid, devid);
+}
+
diff --git a/stand/powerpc/ps3/ps3repo.c b/stand/powerpc/ps3/ps3repo.c
new file mode 100644
index 0000000..0064769
--- /dev/null
+++ b/stand/powerpc/ps3/ps3repo.c
@@ -0,0 +1,249 @@
+/*-
+ * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stand.h>
+
+#include "lv1call.h"
+#include "ps3.h"
+#include "ps3repo.h"
+
+static uint64_t make_n1(const char *text, unsigned int index)
+{
+ uint64_t n1;
+
+ n1 = 0;
+ strncpy((char *) &n1, text, sizeof(n1));
+ n1 = (n1 >> 32) + index;
+
+ return n1;
+}
+
+static uint64_t make_n(const char *text, unsigned int index)
+{
+ uint64_t n;
+
+ n = 0;
+ strncpy((char *) &n, text, sizeof(n));
+ n = n + index;
+
+ return n;
+}
+
+int ps3repo_read_bus_type(unsigned int bus_index, uint64_t *bus_type)
+{
+ uint64_t v1, v2;
+ int err;
+
+ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, make_n1("bus", bus_index),
+ make_n("type", 0), 0, 0, &v1, &v2);
+
+ *bus_type = v1;
+
+ return err;
+}
+
+int ps3repo_read_bus_id(unsigned int bus_index, uint64_t *bus_id)
+{
+ uint64_t v1, v2;
+ int err;
+
+ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, make_n1("bus", bus_index),
+ make_n("id", 0), 0, 0, &v1, &v2);
+
+ *bus_id = v1;
+
+ return err;
+}
+
+int ps3repo_read_bus_num_dev(unsigned int bus_index, uint64_t *num_dev)
+{
+ uint64_t v1, v2;
+ int err;
+
+ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, make_n1("bus", bus_index),
+ make_n("num_dev", 0), 0, 0, &v1, &v2);
+
+ *num_dev = v1;
+
+ return err;
+}
+
+int ps3repo_read_bus_dev_type(unsigned int bus_index, unsigned int dev_index, uint64_t *dev_type)
+{
+ uint64_t v1, v2;
+ int err;
+
+ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, make_n1("bus", bus_index),
+ make_n("dev", dev_index), make_n("type", 0), 0, &v1, &v2);
+
+ *dev_type = v1;
+
+ return err;
+}
+
+int ps3repo_read_bus_dev_id(unsigned int bus_index, unsigned int dev_index, uint64_t *dev_id)
+{
+ uint64_t v1, v2;
+ int err;
+
+ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, make_n1("bus", bus_index),
+ make_n("dev", dev_index), make_n("id", 0), 0, &v1, &v2);
+
+ *dev_id = v1;
+
+ return err;
+}
+
+int ps3repo_read_bus_dev_blk_size(unsigned int bus_index, unsigned int dev_index, uint64_t *blk_size)
+{
+ uint64_t v1, v2;
+ int err;
+
+ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, make_n1("bus", bus_index),
+ make_n("dev", dev_index), make_n("blk_size", 0), 0, &v1, &v2);
+
+ *blk_size = v1;
+
+ return err;
+}
+
+int ps3repo_read_bus_dev_nblocks(unsigned int bus_index, unsigned int dev_index, uint64_t *nblocks)
+{
+ uint64_t v1, v2;
+ int err;
+
+ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, make_n1("bus", bus_index),
+ make_n("dev", dev_index), make_n("n_blocks", 0), 0, &v1, &v2);
+
+ *nblocks = v1;
+
+ return err;
+}
+
+int ps3repo_read_bus_dev_nregs(unsigned int bus_index, unsigned int dev_index, uint64_t *nregs)
+{
+ uint64_t v1, v2;
+ int err;
+
+ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, make_n1("bus", bus_index),
+ make_n("dev", dev_index), make_n("n_regs", 0), 0, &v1, &v2);
+
+ *nregs = v1;
+
+ return err;
+}
+
+int ps3repo_read_bus_dev_reg_id(unsigned int bus_index, unsigned int dev_index,
+ unsigned int reg_index, uint64_t *reg_id)
+{
+ uint64_t v1, v2;
+ int err;
+
+ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, make_n1("bus", bus_index),
+ make_n("dev", dev_index), make_n("region", reg_index), make_n("id", 0), &v1, &v2);
+
+ *reg_id = v1;
+
+ return err;
+}
+
+int ps3repo_read_bus_dev_reg_start(unsigned int bus_index, unsigned int dev_index,
+ unsigned int reg_index, uint64_t *reg_start)
+{
+ uint64_t v1, v2;
+ int err;
+
+ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, make_n1("bus", bus_index),
+ make_n("dev", dev_index), make_n("region", reg_index), make_n("start", 0), &v1, &v2);
+
+ *reg_start = v1;
+
+ return err;
+}
+
+int ps3repo_read_bus_dev_reg_size(unsigned int bus_index, unsigned int dev_index,
+ unsigned int reg_index, uint64_t *reg_size)
+{
+ uint64_t v1, v2;
+ int err;
+
+ err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, make_n1("bus", bus_index),
+ make_n("dev", dev_index), make_n("region", reg_index), make_n("size", 0), &v1, &v2);
+
+ *reg_size = v1;
+
+ return err;
+}
+
+int ps3repo_find_bus_by_type(uint64_t bus_type, unsigned int *bus_index)
+{
+ unsigned int i;
+ uint64_t type;
+ int err;
+
+ for (i = 0; i < 10; i++) {
+ err = ps3repo_read_bus_type(i, &type);
+ if (err) {
+ *bus_index = (unsigned int) -1;
+ return err;
+ }
+
+ if (type == bus_type) {
+ *bus_index = i;
+ return 0;
+ }
+ }
+
+ *bus_index = (unsigned int) -1;
+
+ return ENODEV;
+}
+
+int ps3repo_find_bus_dev_by_type(unsigned int bus_index, uint64_t dev_type,
+ unsigned int *dev_index)
+{
+ unsigned int i;
+ uint64_t type;
+ int err;
+
+ for (i = 0; i < 10; i++) {
+ err = ps3repo_read_bus_dev_type(bus_index, i, &type);
+ if (err) {
+ *dev_index = (unsigned int) -1;
+ return err;
+ }
+
+ if (type == dev_type) {
+ *dev_index = i;
+ return 0;
+ }
+ }
+
+ *dev_index = (unsigned int) -1;
+
+ return ENODEV;
+}
diff --git a/stand/powerpc/ps3/ps3repo.h b/stand/powerpc/ps3/ps3repo.h
new file mode 100644
index 0000000..68001df
--- /dev/null
+++ b/stand/powerpc/ps3/ps3repo.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PS3_REPO_H
+#define _PS3_REPO_H
+
+#define PS3_LPAR_ID_PME 1
+
+int ps3repo_read_bus_type(unsigned int bus_index, uint64_t *bus_type);
+int ps3repo_read_bus_id(unsigned int bus_index, uint64_t *bus_id);
+int ps3repo_read_bus_num_dev(unsigned int bus_index, uint64_t *num_dev);
+int ps3repo_read_bus_dev_type(unsigned int bus_index, unsigned int dev_index, uint64_t *dev_type);
+int ps3repo_read_bus_dev_id(unsigned int bus_index, unsigned int dev_index, uint64_t *dev_id);
+int ps3repo_read_bus_dev_blk_size(unsigned int bus_index, unsigned int dev_index, uint64_t *blk_size);
+int ps3repo_read_bus_dev_nblocks(unsigned int bus_index, unsigned int dev_index, uint64_t *nblocks);
+int ps3repo_read_bus_dev_nregs(unsigned int bus_index, unsigned int dev_index, uint64_t *nregs);
+int ps3repo_read_bus_dev_reg_id(unsigned int bus_index, unsigned int dev_index,
+ unsigned int reg_index, uint64_t *reg_id);
+int ps3repo_read_bus_dev_reg_start(unsigned int bus_index, unsigned int dev_index,
+ unsigned int reg_index, uint64_t *reg_start);
+int ps3repo_read_bus_dev_reg_size(unsigned int bus_index, unsigned int dev_index,
+ unsigned int reg_index, uint64_t *reg_size);
+int ps3repo_find_bus_by_type(uint64_t bus_type, unsigned int *bus_index);
+int ps3repo_find_bus_dev_by_type(unsigned int bus_index, uint64_t dev_type,
+ unsigned int *dev_index);
+
+#endif
diff --git a/stand/powerpc/ps3/ps3stor.c b/stand/powerpc/ps3/ps3stor.c
new file mode 100644
index 0000000..bbfc56a
--- /dev/null
+++ b/stand/powerpc/ps3/ps3stor.c
@@ -0,0 +1,176 @@
+/*-
+ * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "lv1call.h"
+#include "ps3bus.h"
+#include "ps3repo.h"
+#include "ps3stor.h"
+
+int ps3stor_setup(struct ps3_stordev *sd, int type)
+{
+ unsigned int i;
+ int err;
+
+ sd->sd_type = type;
+
+ err = ps3repo_find_bus_by_type(PS3_BUS_TYPE_STOR, &sd->sd_busidx);
+ if (err)
+ goto out;
+
+ err = ps3repo_read_bus_id(sd->sd_busidx, &sd->sd_busid);
+ if (err)
+ goto out;
+
+ err = ps3repo_find_bus_dev_by_type(sd->sd_busidx, type, &sd->sd_devidx);
+ if (err)
+ goto out;
+
+ err = ps3repo_read_bus_dev_id(sd->sd_busidx, sd->sd_devidx,
+ &sd->sd_devid);
+ if (err)
+ goto out;
+
+ err = ps3repo_read_bus_dev_blk_size(sd->sd_busidx, sd->sd_devidx,
+ &sd->sd_blksize);
+ if (err)
+ goto out;
+
+ err = ps3repo_read_bus_dev_nblocks(sd->sd_busidx, sd->sd_devidx,
+ &sd->sd_nblocks);
+ if (err)
+ goto out;
+
+ err = ps3repo_read_bus_dev_nregs(sd->sd_busidx, sd->sd_devidx,
+ &sd->sd_nregs);
+ if (err)
+ goto out;
+
+ for (i = 0; i < sd->sd_nregs; i++) {
+ err = ps3repo_read_bus_dev_reg_id(sd->sd_busidx, sd->sd_devidx,
+ i, &sd->sd_regs[i].sr_id);
+ if (err)
+ goto out;
+
+ err = ps3repo_read_bus_dev_reg_start(sd->sd_busidx,
+ sd->sd_devidx, i, &sd->sd_regs[i].sr_start);
+ if (err)
+ goto out;
+
+ err = ps3repo_read_bus_dev_reg_size(sd->sd_busidx,
+ sd->sd_devidx, i, &sd->sd_regs[i].sr_size);
+ if (err)
+ goto out;
+ }
+
+ if (!sd->sd_nregs) {
+ err = ENODEV;
+ goto out;
+ }
+
+ err = lv1_open_device(sd->sd_busid, sd->sd_devid, 0);
+ if (err)
+ goto out;
+
+ err = lv1_setup_dma(sd->sd_busid, sd->sd_devid, &sd->sd_dmabase);
+ if (err)
+ goto close_dev;
+
+ return 0;
+
+close_dev:
+
+ lv1_close_device(sd->sd_busid, sd->sd_devid);
+
+out:
+
+ return err;
+}
+
+static char dma_buf[2048] __aligned(2048);
+
+int ps3stor_read_sectors(struct ps3_stordev *sd, int regidx,
+ uint64_t start_sector, uint64_t sector_count, uint64_t flags, char *buf)
+{
+#define MIN(a, b) ((a) <= (b) ? (a) : (b))
+#define BOUNCE_SECTORS (sizeof(dma_buf) / sd->sd_blksize)
+#define ASYNC_STATUS_POLL_PERIOD 100 /* microseconds */
+
+ struct ps3_storreg *reg = &sd->sd_regs[regidx];
+ uint64_t nleft, nread, nsectors;
+ uint64_t tag, status;
+ unsigned int timeout;
+ int err = 0;
+
+ nleft = sector_count;
+ nread = 0;
+
+ while (nleft) {
+ nsectors = MIN(nleft, BOUNCE_SECTORS);
+
+ err = lv1_storage_read(sd->sd_devid, reg->sr_id,
+ start_sector + nread, nsectors, flags, (uint32_t)dma_buf,
+ &tag);
+ if (err)
+ return err;
+
+ timeout = 5000000; /* microseconds */
+
+ while (1) {
+ if (timeout < ASYNC_STATUS_POLL_PERIOD)
+ return ETIMEDOUT;
+
+ err = lv1_storage_check_async_status(sd->sd_devid, tag,
+ &status);
+ if (!err && !status)
+ break;
+
+ delay(ASYNC_STATUS_POLL_PERIOD);
+ timeout -= ASYNC_STATUS_POLL_PERIOD;
+ }
+
+ if (status != 0)
+ return EIO;
+
+ memcpy(buf + nread * sd->sd_blksize, (u_char *)dma_buf,
+ nsectors * sd->sd_blksize);
+ nread += nsectors;
+ nleft -= nsectors;
+ }
+
+ return err;
+
+#undef MIN
+#undef BOUNCE_SECTORS
+#undef ASYNC_STATUS_POLL_PERIOD
+}
+
+void ps3stor_print(struct ps3_stordev *sd)
+{
+}
diff --git a/stand/powerpc/ps3/ps3stor.h b/stand/powerpc/ps3/ps3stor.h
new file mode 100644
index 0000000..350b716
--- /dev/null
+++ b/stand/powerpc/ps3/ps3stor.h
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PS3_STOR_H
+#define _PS3_STOR_H
+
+#define PS3_STOR_DEV_MAXREGS 8
+
+struct ps3_storreg {
+ uint64_t sr_id;
+ uint64_t sr_start;
+ uint64_t sr_size;
+};
+
+struct ps3_stordev {
+ int sd_type;
+ unsigned int sd_busidx;
+ unsigned int sd_devidx;
+ uint64_t sd_busid;
+ uint64_t sd_devid;
+ uint64_t sd_blksize;
+ uint64_t sd_nblocks;
+ uint64_t sd_nregs;
+ struct ps3_storreg sd_regs[PS3_STOR_DEV_MAXREGS];
+ uint64_t sd_dmabase;
+};
+
+int ps3stor_setup(struct ps3_stordev *sd, int type);
+
+int ps3stor_read_sectors(struct ps3_stordev *sd, int regidx,
+ uint64_t start_sector, uint64_t sector_count, uint64_t flags, char *buf);
+
+void ps3stor_print(struct ps3_stordev *sd);
+
+#endif
diff --git a/stand/powerpc/ps3/start.S b/stand/powerpc/ps3/start.S
new file mode 100644
index 0000000..570b3f5
--- /dev/null
+++ b/stand/powerpc/ps3/start.S
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (C) 2010 Nathan Whitehorn
+ * 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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define LOCORE
+
+#include <machine/trap.h>
+
+/*
+ * KBoot and simulators will start this program from the _start symbol, with
+ * r3 pointing to a flattened device tree (kexec), r4 the physical address
+ * at which we were loaded, and r5 0 (kexec) or a pointer to Open Firmware
+ * (simulator). If r4 is non-zero, the first order of business is relocating
+ * ourselves to 0. In the kboot case, the PPE secondary thread will enter
+ * at 0x60.
+ *
+ * If started directly by the LV1 hypervisor, we are loaded to address 0
+ * and execution on both threads begins at 0x100 (EXC_RST).
+ */
+
+#define CACHELINE_SIZE 128
+#define SPR_CTRL 136
+
+/* KBoot thread 0 entry -- do relocation, then jump to main */
+.global _start
+_start:
+ mfmsr %r31
+ clrldi %r31,%r31,1
+ mtmsrd %r31
+ isync
+ cmpwi %r4,0
+ bne relocate_self
+relocated_start:
+ lis %r1,0x100
+ bl main
+
+. = 0x40
+.global secondary_spin_sem
+secondary_spin_sem:
+ .long 0
+
+. = 0x60
+thread1_start_kboot:
+ mfmsr %r31
+ clrldi %r31,%r31,1
+ mtmsrd %r31
+ isync
+
+ ba thread1_start /* kboot copies the first 256 bytes to
+ * address 0, so we are safe to jump
+ * (and stay) there */
+
+thread1_start:
+ li %r3,secondary_spin_sem@l
+1: lwz %r1,0(%r3) /* Spin on SECONDARY_SPIN_SEM_ADDRESS */
+ cmpwi %r1,0
+ beq 1b /* If the semaphore is still zero, spin again */
+
+ /* We have been woken up by thread 0 */
+ li %r0,0x100 /* Invalidate reset vector cache line */
+ icbi 0,%r0
+ isync
+ sync
+ ba 0x100 /* Jump to the reset vector */
+
+. = EXC_RST
+exc_rst:
+ mfmsr %r31
+ clrldi %r31,%r31,1
+ mtmsrd %r31
+ isync
+
+ mfspr %r3,SPR_CTRL
+ /* The first two bits of r0 are 01 (thread 1) or 10 (thread 0) */
+ cntlzw %r3,%r3 /* Now 0 for thread 0, 1 for thread 1 */
+
+ cmpwi %r3,0
+ bne thread1_start /* Send thread 1 to wait */
+
+ b relocated_start /* Main entry point for thread 0 */
+
+#define EXCEPTION_HANDLER(exc) \
+. = exc; \
+ li %r3, exc; \
+ mfsrr0 %r4; \
+ mfmsr %r5; \
+ clrldi %r6,%r5,1; \
+ mtmsrd %r6; \
+ isync; \
+ lis %r1,0x100; \
+ bl ppc_exception
+
+EXCEPTION_HANDLER(EXC_MCHK)
+EXCEPTION_HANDLER(EXC_DSI)
+EXCEPTION_HANDLER(EXC_DSE)
+EXCEPTION_HANDLER(EXC_ISI)
+EXCEPTION_HANDLER(EXC_ISE)
+EXCEPTION_HANDLER(EXC_EXI)
+EXCEPTION_HANDLER(EXC_ALI)
+EXCEPTION_HANDLER(EXC_PGM)
+EXCEPTION_HANDLER(EXC_FPU)
+EXCEPTION_HANDLER(EXC_DECR)
+EXCEPTION_HANDLER(EXC_SC)
+
+relocate_self:
+ /* We enter this with r4 the physical offset for our relocation */
+ lis %r8,_end@ha /* r8: copy length */
+ addi %r8,%r8,_end@l
+ li %r5,0x100 /* r5: dest address */
+1: add %r6,%r4,%r5 /* r6: source address */
+ ld %r7,0(%r6)
+ std %r7,0(%r5)
+ addi %r5,%r5,8
+ cmpw %r5,%r8
+ blt 1b
+
+ /*
+ * Now invalidate the cacheline with the second half of relocate_self,
+ * and do an absolute branch there in case we overwrote part of
+ * ourselves.
+ */
+
+ lis %r9,relocate_self_cache@ha
+ addi %r9,%r9,relocate_self_cache@l
+ dcbst 0,%r9
+ sync
+ icbi 0,%r9
+ sync
+ isync
+ ba relocate_self_cache
+
+relocate_self_cache:
+ /* Now invalidate the icache */
+ li %r5,0x100
+2: dcbst 0,%r5
+ sync
+ icbi 0,%r5
+ sync
+ isync
+ cmpw %r5,%r8
+ addi %r5,%r5,CACHELINE_SIZE
+ blt 2b
+
+ /* All done: absolute jump to relocated entry point */
+ ba relocated_start
+
diff --git a/stand/powerpc/ps3/version b/stand/powerpc/ps3/version
new file mode 100644
index 0000000..fdac54e
--- /dev/null
+++ b/stand/powerpc/ps3/version
@@ -0,0 +1,8 @@
+$FreeBSD$
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+0.3: Added GPT support to disk.
+0.2: Added disk support.
+0.1: Initial PS3/PowerPC version.
diff --git a/stand/powerpc/uboot/Makefile b/stand/powerpc/uboot/Makefile
new file mode 100644
index 0000000..d543ace
--- /dev/null
+++ b/stand/powerpc/uboot/Makefile
@@ -0,0 +1,37 @@
+# $FreeBSD$
+
+LOADER_UFS_SUPPORT?= yes
+LOADER_CD9660_SUPPORT?= no
+LOADER_EXT2FS_SUPPORT?= no
+LOADER_NET_SUPPORT?= yes
+LOADER_NFS_SUPPORT?= yes
+LOADER_TFTP_SUPPORT?= no
+LOADER_GZIP_SUPPORT?= no
+LOADER_BZIP2_SUPPORT?= no
+
+.include <bsd.init.mk>
+
+PROG= ubldr
+NEWVERSWHAT= "U-Boot loader" ${MACHINE_ARCH}
+INSTALLFLAGS= -b
+MAN=
+
+# Architecture-specific loader code
+SRCS= start.S conf.c vers.c
+SRCS+= ucmpdi2.c
+
+# Always add MI sources
+HELP_FILES= # Disable
+.include "${BOOTSRC}/loader.mk"
+.PATH: ${SYSDIR}/libkern
+
+CFLAGS+= -ffreestanding
+
+LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.powerpc
+
+.include "${BOOTSRC}/uboot.mk"
+
+DPADD= ${LIBFICL} ${LIBUBOOT} ${LIBFDT} ${LIBUBOOT_FDT} ${LIBSA}
+LDADD= ${LIBFICL} ${LIBUBOOT} ${LIBFDT} ${LIBUBOOT_FDT} ${LIBSA}
+
+.include <bsd.prog.mk>
diff --git a/stand/powerpc/uboot/conf.c b/stand/powerpc/uboot/conf.c
new file mode 100644
index 0000000..561238d
--- /dev/null
+++ b/stand/powerpc/uboot/conf.c
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "bootstrap.h"
+#include "libuboot.h"
+
+#if defined(LOADER_NET_SUPPORT)
+#include "dev_net.h"
+#endif
+
+/* Make sure we have an explicit reference to exit so libsa's panic pulls in the MD exit */
+void (*exitfn)(int) = exit;
+
+/*
+ * We could use linker sets for some or all of these, but
+ * then we would have to control what ended up linked into
+ * the bootstrap. So it's easier to conditionalise things
+ * here.
+ *
+ * XXX rename these arrays to be consistent and less namespace-hostile
+ */
+
+/* Exported for libstand */
+struct devsw *devsw[] = {
+#if defined(LOADER_DISK_SUPPORT) || defined(LOADER_CD9660_SUPPORT)
+ &uboot_storage,
+#endif
+#if defined(LOADER_NET_SUPPORT)
+ &netdev,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#if defined(LOADER_UFS_SUPPORT)
+ &ufs_fsops,
+#endif
+#if defined(LOADER_CD9660_SUPPORT)
+ &cd9660_fsops,
+#endif
+#if defined(LOADER_EXT2FS_SUPPORT)
+ &ext2fs_fsops,
+#endif
+#if defined(LOADER_NFS_SUPPORT)
+ &nfs_fsops,
+#endif
+#if defined(LOADER_TFTP_SUPPORT)
+ &tftp_fsops,
+#endif
+#if defined(LOADER_GZIP_SUPPORT)
+ &gzipfs_fsops,
+#endif
+#if defined(LOADER_BZIP2_SUPPORT)
+ &bzipfs_fsops,
+#endif
+ NULL
+};
+
+struct netif_driver *netif_drivers[] = {
+#if defined(LOADER_NET_SUPPORT)
+ &uboot_net,
+#endif
+ NULL,
+};
+
+/* Exported for PowerPC only */
+/*
+ * Sort formats so that those that can detect based on arguments
+ * rather than reading the file go first.
+ */
+
+struct file_format *file_formats[] = {
+ &uboot_elf,
+ NULL
+};
+
+/*
+ * Consoles
+ */
+extern struct console uboot_console;
+
+struct console *consoles[] = {
+ &uboot_console,
+ NULL
+};
diff --git a/stand/powerpc/uboot/ldscript.powerpc b/stand/powerpc/uboot/ldscript.powerpc
new file mode 100644
index 0000000..9238383
--- /dev/null
+++ b/stand/powerpc/uboot/ldscript.powerpc
@@ -0,0 +1,138 @@
+/* $FreeBSD$ */
+
+OUTPUT_FORMAT("elf32-powerpc-freebsd", "elf32-powerpc-freebsd",
+ "elf32-powerpc-freebsd")
+OUTPUT_ARCH(powerpc:common)
+ENTRY(_start)
+SEARCH_DIR(/usr/lib);
+PROVIDE (__stack = 0);
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x00010000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rela.got : { *(.rela.got) }
+ .rela.got1 : { *(.rela.got1) }
+ .rela.got2 : { *(.rela.got2) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rela.init : { *(.rela.init) }
+ .rela.fini : { *(.rela.fini) }
+ .rela.bss : { *(.rela.bss) }
+ .rela.plt : { *(.rela.plt) }
+ .rela.sdata : { *(.rela.sdata) }
+ .rela.sbss : { *(.rela.sbss) }
+ .rela.sdata2 : { *(.rela.sdata2) }
+ .rela.sbss2 : { *(.rela.sbss2) }
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ PROVIDE (etext = .);
+ .init : { *(.init) } =0
+ .fini : { *(.fini) } =0
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 : { *(.sdata2) }
+ .sbss2 : { *(.sbss2) }
+ /* Adjust the address for the data segment to the next page up. */
+ . = ((. + 0x1000) & ~(0x1000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .got1 : { *(.got1) }
+ .dynamic : { *(.dynamic) }
+ /* Put .ctors and .dtors next to the .got2 section, so that the pointers
+ get relocated with -mrelocatable. Also put in the .fixup pointers.
+ The current compiler no longer needs this, but keep it around for 2.7.2 */
+ PROVIDE (_GOT2_START_ = .);
+ .got2 : { *(.got2) }
+ PROVIDE (__CTOR_LIST__ = .);
+ .ctors : { *(.ctors) }
+ PROVIDE (__CTOR_END__ = .);
+ PROVIDE (__DTOR_LIST__ = .);
+ .dtors : { *(.dtors) }
+ PROVIDE (__DTOR_END__ = .);
+ PROVIDE (_FIXUP_START_ = .);
+ .fixup : { *(.fixup) }
+ PROVIDE (_FIXUP_END_ = .);
+ PROVIDE (_GOT2_END_ = .);
+ PROVIDE (_GOT_START_ = .);
+ .got : { *(.got) }
+ .got.plt : { *(.got.plt) }
+ PROVIDE (_GOT_END_ = .);
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ *(.sbss)
+ *(.scommon)
+ *(.dynsbss)
+ PROVIDE (__sbss_end = .);
+ }
+ .plt : { *(.plt) }
+ .bss :
+ {
+ PROVIDE (__bss_start = .);
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ /* 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) }
+ .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) }
+ /* These must appear regardless of . */
+}
+
diff --git a/stand/powerpc/uboot/start.S b/stand/powerpc/uboot/start.S
new file mode 100644
index 0000000..3e80576
--- /dev/null
+++ b/stand/powerpc/uboot/start.S
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+/*
+ * Entry point to the loader that U-Boot passes control to.
+ */
+ .text
+ .globl _start
+_start:
+ /* Hint where to look for the API signature */
+ lis %r11, uboot_address@ha
+ addi %r11, %r11, uboot_address@l
+ stw %r1, 0(%r11)
+ /* Save U-Boot's r14 */
+ lis %r11, saved_regs@ha
+ addi %r11, %r11, saved_regs@l
+ stw %r14, 0(%r11)
+ /* Disable interrupts */
+ mfmsr %r11
+ andi. %r11, %r11, ~0x8000@l
+ mtmsr %r11
+ b main
+
+/*
+ * syscall()
+ */
+ENTRY(syscall)
+ stwu %r1, -16(%r1)
+ mflr %r0
+ stw %r14, 8(%r1)
+ stw %r0, 20(%r1)
+ /* Restore U-Boot's r14 */
+ lis %r11, saved_regs@ha
+ addi %r11, %r11, saved_regs@l
+ lwz %r14, 0(%r11)
+ /* Enable interrupts */
+ mfmsr %r11
+ ori %r11, %r11, 0x8000@l
+ mtmsr %r11
+ /* Call into U-Boot */
+ lis %r11, syscall_ptr@ha
+ addi %r11, %r11, syscall_ptr@l
+ lwz %r11, 0(%r11)
+ mtctr %r11
+ bctrl
+ /* Disable interrupts */
+ mfmsr %r11
+ andi. %r11, %r11, ~0x8000@l
+ mtmsr %r11
+ /* Epilogue */
+ lwz %r11, 0(%r1)
+ lwz %r0, 4(%r11)
+ mtlr %r0
+ lwz %r14, 8(%r1)
+ mr %r1, %r11
+ blr
+
+/*
+ * Data section
+ */
+ .data
+GLOBAL(syscall_ptr)
+ .long 0
+GLOBAL(saved_regs)
+ .long 0 /* R14 */
+GLOBAL(uboot_address)
+ .long 0
diff --git a/stand/powerpc/uboot/version b/stand/powerpc/uboot/version
new file mode 100644
index 0000000..21556cf
--- /dev/null
+++ b/stand/powerpc/uboot/version
@@ -0,0 +1,11 @@
+$FreeBSD$
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+1.1: Flattened Device Tree blob support.
+1.0: Added storage support.
+0.6: Integrated with the new U-Boot API
+0.5: Full network functionality.
+0.2: Initial U-Boot/PowerPC version derived from the existing
+ OpenFirmware-based.
diff --git a/stand/sparc64/Makefile b/stand/sparc64/Makefile
new file mode 100644
index 0000000..6a81a05
--- /dev/null
+++ b/stand/sparc64/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+SUBDIR= boot1 loader
+.if ${MK_ZFS} != "no"
+SUBDIR+=zfsboot zfsloader
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/stand/sparc64/Makefile.inc b/stand/sparc64/Makefile.inc
new file mode 100644
index 0000000..a12699c
--- /dev/null
+++ b/stand/sparc64/Makefile.inc
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+CFLAGS+= -ffreestanding
+LDFLAGS+= -nostdlib
+
+.include "../Makefile.inc"
diff --git a/stand/sparc64/boot1/Makefile b/stand/sparc64/boot1/Makefile
new file mode 100644
index 0000000..36e3968
--- /dev/null
+++ b/stand/sparc64/boot1/Makefile
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= boot1.elf
+INTERNALPROG=
+MAN=
+FILES?= boot1
+SRCS= _start.s boot1.c
+CLEANFILES+=${FILES} boot1.aout
+
+BOOTBLOCKBASE= 0x4000
+
+CFLAGS.clang+=-mcmodel=small
+CFLAGS.gcc+=-mcmodel=medlow
+CFLAGS+=-Os -I${LDRSRC}
+LDFLAGS+=-Ttext ${BOOTBLOCKBASE} -Wl,-N
+
+# Construct boot1. sunlabel expects it to contain zeroed-out space for the
+# label, and to be of the correct size.
+${FILES}: boot1.aout
+ @set -- `ls -l ${.ALLSRC}`; x=$$((7680-$$5)); \
+ echo "$$x bytes available"; test $$x -ge 0
+ ${DD} if=/dev/zero of=${.TARGET} bs=512 count=16
+ ${DD} if=${.ALLSRC} of=${.TARGET} bs=512 oseek=1 conv=notrunc
+
+boot1.aout: boot1.elf
+ elf2aout -o ${.TARGET} ${.ALLSRC}
+
+boot1.o: ${SASRC}/ufsread.c
+
+.include <bsd.prog.mk>
diff --git a/stand/sparc64/boot1/_start.s b/stand/sparc64/boot1/_start.s
new file mode 100644
index 0000000..30f8019
--- /dev/null
+++ b/stand/sparc64/boot1/_start.s
@@ -0,0 +1,8 @@
+/* $FreeBSD$ */
+
+ .text
+ .globl _start
+_start:
+ call ofw_init
+ nop
+ sir
diff --git a/stand/sparc64/boot1/boot1.c b/stand/sparc64/boot1/boot1.c
new file mode 100644
index 0000000..4724ca1
--- /dev/null
+++ b/stand/sparc64/boot1/boot1.c
@@ -0,0 +1,751 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ * Copyright (c) 2001 Robert Drehmel
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/dirent.h>
+
+#include <machine/elf.h>
+#include <machine/stdarg.h>
+
+#include "paths.h"
+
+#define READ_BUF_SIZE 8192
+
+typedef int putc_func_t(char c, void *arg);
+typedef int32_t ofwh_t;
+
+struct sp_data {
+ char *sp_buf;
+ u_int sp_len;
+ u_int sp_size;
+};
+
+static const char digits[] = "0123456789abcdef";
+
+static char bootpath[128];
+static char bootargs[128];
+
+static ofwh_t bootdev;
+
+static uint32_t fs_off;
+
+int main(int ac, char **av);
+static void exit(int) __dead2;
+static void usage(void);
+
+#ifdef ZFSBOOT
+static void loadzfs(void);
+static int zbread(char *buf, off_t off, size_t bytes);
+#else
+static void load(const char *);
+#endif
+
+static void bcopy(const void *src, void *dst, size_t len);
+static void bzero(void *b, size_t len);
+
+static int domount(const char *device);
+static int dskread(void *buf, u_int64_t lba, int nblk);
+
+static void panic(const char *fmt, ...) __dead2;
+static int printf(const char *fmt, ...);
+static int putchar(char c, void *arg);
+static int vprintf(const char *fmt, va_list ap);
+static int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap);
+
+static int __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap);
+static int __puts(const char *s, putc_func_t *putc, void *arg);
+static int __sputc(char c, void *arg);
+static char *__uitoa(char *buf, u_int val, int base);
+static char *__ultoa(char *buf, u_long val, int base);
+
+/*
+ * Open Firmware interface functions
+ */
+typedef u_int64_t ofwcell_t;
+typedef u_int32_t u_ofwh_t;
+typedef int (*ofwfp_t)(ofwcell_t []);
+static ofwfp_t ofw; /* the PROM Open Firmware entry */
+
+void ofw_init(int, int, int, int, ofwfp_t);
+static ofwh_t ofw_finddevice(const char *);
+static ofwh_t ofw_open(const char *);
+static int ofw_getprop(ofwh_t, const char *, void *, size_t);
+static int ofw_read(ofwh_t, void *, size_t);
+static int ofw_write(ofwh_t, const void *, size_t);
+static int ofw_seek(ofwh_t, u_int64_t);
+static void ofw_exit(void) __dead2;
+
+static ofwh_t stdinh, stdouth;
+
+/*
+ * This has to stay here, as the PROM seems to ignore the
+ * entry point specified in the a.out header. (or elftoaout is broken)
+ */
+
+void
+ofw_init(int d, int d1, int d2, int d3, ofwfp_t ofwaddr)
+{
+ ofwh_t chosenh;
+ char *av[16];
+ char *p;
+ int ac;
+
+ ofw = ofwaddr;
+
+ chosenh = ofw_finddevice("/chosen");
+ ofw_getprop(chosenh, "stdin", &stdinh, sizeof(stdinh));
+ ofw_getprop(chosenh, "stdout", &stdouth, sizeof(stdouth));
+ ofw_getprop(chosenh, "bootargs", bootargs, sizeof(bootargs));
+ ofw_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath));
+
+ bootargs[sizeof(bootargs) - 1] = '\0';
+ bootpath[sizeof(bootpath) - 1] = '\0';
+
+ ac = 0;
+ p = bootargs;
+ for (;;) {
+ while (*p == ' ' && *p != '\0')
+ p++;
+ if (*p == '\0' || ac >= 16)
+ break;
+ av[ac++] = p;
+ while (*p != ' ' && *p != '\0')
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+ }
+
+ exit(main(ac, av));
+}
+
+static ofwh_t
+ofw_finddevice(const char *name)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"finddevice",
+ 1,
+ 1,
+ (ofwcell_t)name,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_finddevice: name=\"%s\"\n", name);
+ return (1);
+ }
+ return (args[4]);
+}
+
+static int
+ofw_getprop(ofwh_t ofwh, const char *name, void *buf, size_t len)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"getprop",
+ 4,
+ 1,
+ (u_ofwh_t)ofwh,
+ (ofwcell_t)name,
+ (ofwcell_t)buf,
+ len,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_getprop: ofwh=0x%x buf=%p len=%u\n",
+ ofwh, buf, len);
+ return (1);
+ }
+ return (0);
+}
+
+static ofwh_t
+ofw_open(const char *path)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"open",
+ 1,
+ 1,
+ (ofwcell_t)path,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_open: path=\"%s\"\n", path);
+ return (-1);
+ }
+ return (args[4]);
+}
+
+static int
+ofw_close(ofwh_t devh)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"close",
+ 1,
+ 0,
+ (u_ofwh_t)devh
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_close: devh=0x%x\n", devh);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+ofw_read(ofwh_t devh, void *buf, size_t len)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"read",
+ 3,
+ 1,
+ (u_ofwh_t)devh,
+ (ofwcell_t)buf,
+ len,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_read: devh=0x%x buf=%p len=%u\n", devh, buf, len);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+ofw_write(ofwh_t devh, const void *buf, size_t len)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"write",
+ 3,
+ 1,
+ (u_ofwh_t)devh,
+ (ofwcell_t)buf,
+ len,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_write: devh=0x%x buf=%p len=%u\n", devh, buf, len);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+ofw_seek(ofwh_t devh, u_int64_t off)
+{
+ ofwcell_t args[] = {
+ (ofwcell_t)"seek",
+ 3,
+ 1,
+ (u_ofwh_t)devh,
+ off >> 32,
+ off,
+ 0
+ };
+
+ if ((*ofw)(args)) {
+ printf("ofw_seek: devh=0x%x off=0x%lx\n", devh, off);
+ return (1);
+ }
+ return (0);
+}
+
+static void
+ofw_exit(void)
+{
+ ofwcell_t args[3];
+
+ args[0] = (ofwcell_t)"exit";
+ args[1] = 0;
+ args[2] = 0;
+
+ for (;;)
+ (*ofw)(args);
+}
+
+static void
+bcopy(const void *src, void *dst, size_t len)
+{
+ const char *s = src;
+ char *d = dst;
+
+ while (len-- != 0)
+ *d++ = *s++;
+}
+
+static void
+memcpy(void *dst, const void *src, size_t len)
+{
+
+ bcopy(src, dst, len);
+}
+
+static void
+bzero(void *b, size_t len)
+{
+ char *p = b;
+
+ while (len-- != 0)
+ *p++ = 0;
+}
+
+static int
+strcmp(const char *s1, const char *s2)
+{
+
+ for (; *s1 == *s2 && *s1; s1++, s2++)
+ ;
+ return ((u_char)*s1 - (u_char)*s2);
+}
+
+int
+main(int ac, char **av)
+{
+ const char *path;
+ int i;
+
+ path = PATH_LOADER;
+ for (i = 0; i < ac; i++) {
+ switch (av[i][0]) {
+ case '-':
+ switch (av[i][1]) {
+ default:
+ usage();
+ }
+ break;
+ default:
+ path = av[i];
+ break;
+ }
+ }
+
+#ifdef ZFSBOOT
+ printf(" \n>> FreeBSD/sparc64 ZFS boot block\n Boot path: %s\n",
+ bootpath);
+#else
+ printf(" \n>> FreeBSD/sparc64 boot block\n Boot path: %s\n"
+ " Boot loader: %s\n", bootpath, path);
+#endif
+
+ if (domount(bootpath) == -1)
+ panic("domount");
+
+#ifdef ZFSBOOT
+ loadzfs();
+#else
+ load(path);
+#endif
+ return (1);
+}
+
+static void
+usage(void)
+{
+
+ printf("usage: boot device [/path/to/loader]\n");
+ exit(1);
+}
+
+static void
+exit(int code)
+{
+
+ ofw_exit();
+}
+
+#ifdef ZFSBOOT
+
+#define VDEV_BOOT_OFFSET (2 * 256 * 1024)
+static char zbuf[READ_BUF_SIZE];
+
+static int
+zbread(char *buf, off_t off, size_t bytes)
+{
+ size_t len;
+ off_t poff;
+ off_t soff;
+ char *p;
+ unsigned int nb;
+ unsigned int lb;
+
+ p = buf;
+ soff = VDEV_BOOT_OFFSET + off;
+ lb = howmany(soff + bytes, DEV_BSIZE);
+ poff = soff;
+ while (poff < soff + bytes) {
+ nb = lb - poff / DEV_BSIZE;
+ if (nb > READ_BUF_SIZE / DEV_BSIZE)
+ nb = READ_BUF_SIZE / DEV_BSIZE;
+ if (dskread(zbuf, poff / DEV_BSIZE, nb))
+ break;
+ if ((poff / DEV_BSIZE + nb) * DEV_BSIZE > soff + bytes)
+ len = soff + bytes - poff;
+ else
+ len = (poff / DEV_BSIZE + nb) * DEV_BSIZE - poff;
+ memcpy(p, zbuf + poff % DEV_BSIZE, len);
+ p += len;
+ poff += len;
+ }
+ return (poff - soff);
+}
+
+static void
+loadzfs(void)
+{
+ Elf64_Ehdr eh;
+ Elf64_Phdr ph;
+ caddr_t p;
+ int i;
+
+ if (zbread((char *)&eh, 0, sizeof(eh)) != sizeof(eh)) {
+ printf("Can't read elf header\n");
+ return;
+ }
+ if (!IS_ELF(eh)) {
+ printf("Not an ELF file\n");
+ return;
+ }
+ for (i = 0; i < eh.e_phnum; i++) {
+ fs_off = eh.e_phoff + i * eh.e_phentsize;
+ if (zbread((char *)&ph, fs_off, sizeof(ph)) != sizeof(ph)) {
+ printf("Can't read program header %d\n", i);
+ return;
+ }
+ if (ph.p_type != PT_LOAD)
+ continue;
+ fs_off = ph.p_offset;
+ p = (caddr_t)ph.p_vaddr;
+ if (zbread(p, fs_off, ph.p_filesz) != ph.p_filesz) {
+ printf("Can't read content of section %d\n", i);
+ return;
+ }
+ if (ph.p_filesz != ph.p_memsz)
+ bzero(p + ph.p_filesz, ph.p_memsz - ph.p_filesz);
+ }
+ ofw_close(bootdev);
+ (*(void (*)(int, int, int, int, ofwfp_t))eh.e_entry)(0, 0, 0, 0, ofw);
+}
+
+#else
+
+#include "ufsread.c"
+
+static struct dmadat __dmadat;
+
+static void
+load(const char *fname)
+{
+ Elf64_Ehdr eh;
+ Elf64_Phdr ph;
+ caddr_t p;
+ ufs_ino_t ino;
+ int i;
+
+ if ((ino = lookup(fname)) == 0) {
+ printf("File %s not found\n", fname);
+ return;
+ }
+ if (fsread(ino, &eh, sizeof(eh)) != sizeof(eh)) {
+ printf("Can't read elf header\n");
+ return;
+ }
+ if (!IS_ELF(eh)) {
+ printf("Not an ELF file\n");
+ return;
+ }
+ for (i = 0; i < eh.e_phnum; i++) {
+ fs_off = eh.e_phoff + i * eh.e_phentsize;
+ if (fsread(ino, &ph, sizeof(ph)) != sizeof(ph)) {
+ printf("Can't read program header %d\n", i);
+ return;
+ }
+ if (ph.p_type != PT_LOAD)
+ continue;
+ fs_off = ph.p_offset;
+ p = (caddr_t)ph.p_vaddr;
+ if (fsread(ino, p, ph.p_filesz) != ph.p_filesz) {
+ printf("Can't read content of section %d\n", i);
+ return;
+ }
+ if (ph.p_filesz != ph.p_memsz)
+ bzero(p + ph.p_filesz, ph.p_memsz - ph.p_filesz);
+ }
+ ofw_close(bootdev);
+ (*(void (*)(int, int, int, int, ofwfp_t))eh.e_entry)(0, 0, 0, 0, ofw);
+}
+
+#endif /* ZFSBOOT */
+
+static int
+domount(const char *device)
+{
+
+ if ((bootdev = ofw_open(device)) == -1) {
+ printf("domount: can't open device\n");
+ return (-1);
+ }
+#ifndef ZFSBOOT
+ dmadat = &__dmadat;
+ if (fsread(0, NULL, 0)) {
+ printf("domount: can't read superblock\n");
+ return (-1);
+ }
+#endif
+ return (0);
+}
+
+static int
+dskread(void *buf, u_int64_t lba, int nblk)
+{
+
+ /*
+ * The Open Firmware should open the correct partition for us.
+ * That means, if we read from offset zero on an open instance handle,
+ * we should read from offset zero of that partition.
+ */
+ ofw_seek(bootdev, lba * DEV_BSIZE);
+ ofw_read(bootdev, buf, nblk * DEV_BSIZE);
+ return (0);
+}
+
+static void
+panic(const char *fmt, ...)
+{
+ char buf[128];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof buf, fmt, ap);
+ printf("panic: %s\n", buf);
+ va_end(ap);
+
+ exit(1);
+}
+
+static int
+printf(const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vprintf(fmt, ap);
+ va_end(ap);
+ return (ret);
+}
+
+static int
+putchar(char c, void *arg)
+{
+ char buf;
+
+ if (c == '\n') {
+ buf = '\r';
+ ofw_write(stdouth, &buf, 1);
+ }
+ buf = c;
+ ofw_write(stdouth, &buf, 1);
+ return (1);
+}
+
+static int
+vprintf(const char *fmt, va_list ap)
+{
+ int ret;
+
+ ret = __printf(fmt, putchar, 0, ap);
+ return (ret);
+}
+
+static int
+vsnprintf(char *str, size_t sz, const char *fmt, va_list ap)
+{
+ struct sp_data sp;
+ int ret;
+
+ sp.sp_buf = str;
+ sp.sp_len = 0;
+ sp.sp_size = sz;
+ ret = __printf(fmt, __sputc, &sp, ap);
+ return (ret);
+}
+
+static int
+__printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap)
+{
+ char buf[(sizeof(long) * 8) + 1];
+ char *nbuf;
+ u_long ul;
+ u_int ui;
+ int lflag;
+ int sflag;
+ char *s;
+ int pad;
+ int ret;
+ int c;
+
+ nbuf = &buf[sizeof buf - 1];
+ ret = 0;
+ while ((c = *fmt++) != 0) {
+ if (c != '%') {
+ ret += putc(c, arg);
+ continue;
+ }
+ lflag = 0;
+ sflag = 0;
+ pad = 0;
+reswitch: c = *fmt++;
+ switch (c) {
+ case '#':
+ sflag = 1;
+ goto reswitch;
+ case '%':
+ ret += putc('%', arg);
+ break;
+ case 'c':
+ c = va_arg(ap, int);
+ ret += putc(c, arg);
+ break;
+ case 'd':
+ if (lflag == 0) {
+ ui = (u_int)va_arg(ap, int);
+ if (ui < (int)ui) {
+ ui = -ui;
+ ret += putc('-', arg);
+ }
+ s = __uitoa(nbuf, ui, 10);
+ } else {
+ ul = (u_long)va_arg(ap, long);
+ if (ul < (long)ul) {
+ ul = -ul;
+ ret += putc('-', arg);
+ }
+ s = __ultoa(nbuf, ul, 10);
+ }
+ ret += __puts(s, putc, arg);
+ break;
+ case 'l':
+ lflag = 1;
+ goto reswitch;
+ case 'o':
+ if (lflag == 0) {
+ ui = (u_int)va_arg(ap, u_int);
+ s = __uitoa(nbuf, ui, 8);
+ } else {
+ ul = (u_long)va_arg(ap, u_long);
+ s = __ultoa(nbuf, ul, 8);
+ }
+ ret += __puts(s, putc, arg);
+ break;
+ case 'p':
+ ul = (u_long)va_arg(ap, void *);
+ s = __ultoa(nbuf, ul, 16);
+ ret += __puts("0x", putc, arg);
+ ret += __puts(s, putc, arg);
+ break;
+ case 's':
+ s = va_arg(ap, char *);
+ ret += __puts(s, putc, arg);
+ break;
+ case 'u':
+ if (lflag == 0) {
+ ui = va_arg(ap, u_int);
+ s = __uitoa(nbuf, ui, 10);
+ } else {
+ ul = va_arg(ap, u_long);
+ s = __ultoa(nbuf, ul, 10);
+ }
+ ret += __puts(s, putc, arg);
+ break;
+ case 'x':
+ if (lflag == 0) {
+ ui = va_arg(ap, u_int);
+ s = __uitoa(nbuf, ui, 16);
+ } else {
+ ul = va_arg(ap, u_long);
+ s = __ultoa(nbuf, ul, 16);
+ }
+ if (sflag)
+ ret += __puts("0x", putc, arg);
+ ret += __puts(s, putc, arg);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ pad = pad * 10 + c - '0';
+ goto reswitch;
+ default:
+ break;
+ }
+ }
+ return (ret);
+}
+
+static int
+__sputc(char c, void *arg)
+{
+ struct sp_data *sp;
+
+ sp = arg;
+ if (sp->sp_len < sp->sp_size)
+ sp->sp_buf[sp->sp_len++] = c;
+ sp->sp_buf[sp->sp_len] = '\0';
+ return (1);
+}
+
+static int
+__puts(const char *s, putc_func_t *putc, void *arg)
+{
+ const char *p;
+ int ret;
+
+ ret = 0;
+ for (p = s; *p != '\0'; p++)
+ ret += putc(*p, arg);
+ return (ret);
+}
+
+static char *
+__uitoa(char *buf, u_int ui, int base)
+{
+ char *p;
+
+ p = buf;
+ *p = '\0';
+ do
+ *--p = digits[ui % base];
+ while ((ui /= base) != 0);
+ return (p);
+}
+
+static char *
+__ultoa(char *buf, u_long ul, int base)
+{
+ char *p;
+
+ p = buf;
+ *p = '\0';
+ do
+ *--p = digits[ul % base];
+ while ((ul /= base) != 0);
+ return (p);
+}
diff --git a/stand/sparc64/loader/Makefile b/stand/sparc64/loader/Makefile
new file mode 100644
index 0000000..5388761
--- /dev/null
+++ b/stand/sparc64/loader/Makefile
@@ -0,0 +1,50 @@
+# $FreeBSD$
+
+LOADER_DISK_SUPPORT?= yes
+LOADER_UFS_SUPPORT?= yes
+LOADER_CD9660_SUPPORT?= yes
+LOADER_EXT2FS_SUPPORT?= no
+LOADER_MSDOS_SUPPORT?= no
+LOADER_NET_SUPPORT?= yes
+LOADER_NFS_SUPPORT?= yes
+LOADER_TFTP_SUPPORT?= yes
+LOADER_GZIP_SUPPORT?= yes
+LOADER_BZIP2_SUPPORT?= no
+LOADER_DEBUG?= no
+
+.include <bsd.init.mk>
+MK_SSP= no
+MAN=
+
+PROG?= loader
+NEWVERSWHAT?= "bootstrap loader" sparc64
+VERSION_FILE= ${.CURDIR}/../loader/version
+INSTALLFLAGS= -b
+
+# Architecture-specific loader code
+.PATH: ${BOOTSRC}/sparc64/loader
+SRCS= locore.S main.c metadata.c vers.c
+
+.if ${LOADER_DEBUG} == "yes"
+CFLAGS+= -DLOADER_DEBUG
+.endif
+
+.if exists(${.CURDIR}/help.sparc64)
+HELP_FILES+= help.sparc64
+.else
+HELP_FILES=
+.endif
+
+# Always add MI sources
+.include "${BOOTSRC}/loader.mk"
+
+LDFLAGS+= -static
+
+# Open Firmware standalone support library
+LIBOFW= ${BOOTOBJ}/ofw/libofw/libofw.a
+CFLAGS+= -I${BOOTSRC}/ofw/libofw/
+
+DPADD= ${LIBFICL} ${LIBZFSBOOT} ${LIBOFW} ${LIBSA}
+LDADD= ${LIBFICL} ${LIBZFSBOOT} ${LIBOFW} ${LIBSA}
+
+.include <bsd.prog.mk>
diff --git a/stand/sparc64/loader/help.sparc64 b/stand/sparc64/loader/help.sparc64
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/stand/sparc64/loader/help.sparc64
diff --git a/stand/sparc64/loader/locore.S b/stand/sparc64/loader/locore.S
new file mode 100644
index 0000000..a73f5bf
--- /dev/null
+++ b/stand/sparc64/loader/locore.S
@@ -0,0 +1,42 @@
+/*-
+ * Initial implementation:
+ * Copyright (c) 2001 Robert Drehmel
+ * All rights reserved.
+ *
+ * As long as the above copyright statement and this notice remain
+ * unchanged, you can do what ever you want with this file.
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#define LOCORE
+
+#include <machine/frame.h>
+#include <machine/fsr.h>
+#include <machine/intr_machdep.h>
+#include <machine/pstate.h>
+
+#define PAGE_SIZE 8192
+#define PAGE_SHIFT 13
+
+#define STACK_SIZE (2 * PAGE_SIZE)
+
+ENTRY(_start)
+ /* Limit interrupts. */
+ wrpr %g0, PIL_TICK - 1, %pil
+
+ /*
+ * PSTATE: privileged, interrupts enabled, floating point
+ * unit enabled
+ */
+ wrpr %g0, PSTATE_PRIV | PSTATE_IE | PSTATE_PEF, %pstate
+ wr %g0, FPRS_FEF, %fprs
+
+ setx stack + STACK_SIZE - SPOFF - CCFSZ, %l7, %l6
+ mov %l6, %sp
+ call main
+ mov %o4, %o0
+ sir
+
+ .comm stack, STACK_SIZE, 32
diff --git a/stand/sparc64/loader/main.c b/stand/sparc64/loader/main.c
new file mode 100644
index 0000000..e8cdf5f
--- /dev/null
+++ b/stand/sparc64/loader/main.c
@@ -0,0 +1,1000 @@
+/*-
+ * Initial implementation:
+ * Copyright (c) 2001 Robert Drehmel
+ * All rights reserved.
+ *
+ * As long as the above copyright statement and this notice remain
+ * unchanged, you can do what ever you want with this file.
+ */
+/*-
+ * Copyright (c) 2008 - 2012 Marius Strobl <marius@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * FreeBSD/sparc64 kernel loader - machine dependent part
+ *
+ * - implements copyin and readin functions that map kernel
+ * pages on demand. The machine independent code does not
+ * know the size of the kernel early enough to pre-enter
+ * TTEs and install just one 4MB mapping seemed to limiting
+ * to me.
+ */
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#ifdef LOADER_ZFS_SUPPORT
+#include <sys/vtoc.h>
+#include "../zfs/libzfs.h"
+#endif
+
+#include <vm/vm.h>
+#include <machine/asi.h>
+#include <machine/cmt.h>
+#include <machine/cpufunc.h>
+#include <machine/elf.h>
+#include <machine/fireplane.h>
+#include <machine/jbus.h>
+#include <machine/lsu.h>
+#include <machine/metadata.h>
+#include <machine/tte.h>
+#include <machine/tlb.h>
+#include <machine/upa.h>
+#include <machine/ver.h>
+#include <machine/vmparam.h>
+
+#include "bootstrap.h"
+#include "libofw.h"
+#include "dev_net.h"
+
+extern char bootprog_info[];
+
+enum {
+ HEAPVA = 0x800000,
+ HEAPSZ = 0x1000000,
+ LOADSZ = 0x1000000 /* for kernel and modules */
+};
+
+/* At least Sun Fire V1280 require page sized allocations to be claimed. */
+CTASSERT(HEAPSZ % PAGE_SIZE == 0);
+
+static struct mmu_ops {
+ void (*tlb_init)(void);
+ int (*mmu_mapin)(vm_offset_t va, vm_size_t len);
+} *mmu_ops;
+
+typedef void kernel_entry_t(vm_offset_t mdp, u_long o1, u_long o2, u_long o3,
+ void *openfirmware);
+
+static inline u_long dtlb_get_data_sun4u(u_int, u_int);
+static int dtlb_enter_sun4u(u_int, u_long data, vm_offset_t);
+static vm_offset_t dtlb_va_to_pa_sun4u(vm_offset_t);
+static inline u_long itlb_get_data_sun4u(u_int, u_int);
+static int itlb_enter_sun4u(u_int, u_long data, vm_offset_t);
+static vm_offset_t itlb_va_to_pa_sun4u(vm_offset_t);
+static void itlb_relocate_locked0_sun4u(void);
+extern vm_offset_t md_load(char *, vm_offset_t *, vm_offset_t *);
+static int sparc64_autoload(void);
+static ssize_t sparc64_readin(const int, vm_offset_t, const size_t);
+static ssize_t sparc64_copyin(const void *, vm_offset_t, size_t);
+static vm_offset_t claim_virt(vm_offset_t, size_t, int);
+static vm_offset_t alloc_phys(size_t, int);
+static int map_phys(int, size_t, vm_offset_t, vm_offset_t);
+static void release_phys(vm_offset_t, u_int);
+static int __elfN(exec)(struct preloaded_file *);
+static int mmu_mapin_sun4u(vm_offset_t, vm_size_t);
+static vm_offset_t init_heap(void);
+static phandle_t find_bsp_sun4u(phandle_t, uint32_t);
+const char *cpu_cpuid_prop_sun4u(void);
+uint32_t cpu_get_mid_sun4u(void);
+static void tlb_init_sun4u(void);
+
+#ifdef LOADER_DEBUG
+typedef u_int64_t tte_t;
+
+static void pmap_print_tlb_sun4u(void);
+static void pmap_print_tte_sun4u(tte_t, tte_t);
+#endif
+
+static struct mmu_ops mmu_ops_sun4u = { tlb_init_sun4u, mmu_mapin_sun4u };
+
+/* sun4u */
+struct tlb_entry *dtlb_store;
+struct tlb_entry *itlb_store;
+u_int dtlb_slot;
+u_int itlb_slot;
+static int cpu_impl;
+static u_int dtlb_slot_max;
+static u_int itlb_slot_max;
+static u_int tlb_locked;
+
+static vm_offset_t curkva = 0;
+static vm_offset_t heapva;
+
+static char bootpath[64];
+static phandle_t root;
+
+#ifdef LOADER_ZFS_SUPPORT
+static struct zfs_devdesc zfs_currdev;
+#endif
+
+/*
+ * Machine dependent structures that the machine independent
+ * loader part uses.
+ */
+struct devsw *devsw[] = {
+#ifdef LOADER_DISK_SUPPORT
+ &ofwdisk,
+#endif
+#ifdef LOADER_NET_SUPPORT
+ &netdev,
+#endif
+#ifdef LOADER_ZFS_SUPPORT
+ &zfs_dev,
+#endif
+ NULL
+};
+
+struct arch_switch archsw;
+
+static struct file_format sparc64_elf = {
+ __elfN(loadfile),
+ __elfN(exec)
+};
+
+struct file_format *file_formats[] = {
+ &sparc64_elf,
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#ifdef LOADER_ZFS_SUPPORT
+ &zfs_fsops,
+#endif
+#ifdef LOADER_UFS_SUPPORT
+ &ufs_fsops,
+#endif
+#ifdef LOADER_CD9660_SUPPORT
+ &cd9660_fsops,
+#endif
+#ifdef LOADER_ZIP_SUPPORT
+ &zipfs_fsops,
+#endif
+#ifdef LOADER_GZIP_SUPPORT
+ &gzipfs_fsops,
+#endif
+#ifdef LOADER_BZIP2_SUPPORT
+ &bzipfs_fsops,
+#endif
+#ifdef LOADER_NFS_SUPPORT
+ &nfs_fsops,
+#endif
+#ifdef LOADER_TFTP_SUPPORT
+ &tftp_fsops,
+#endif
+ NULL
+};
+
+struct netif_driver *netif_drivers[] = {
+#ifdef LOADER_NET_SUPPORT
+ &ofwnet,
+#endif
+ NULL
+};
+
+extern struct console ofwconsole;
+struct console *consoles[] = {
+ &ofwconsole,
+ NULL
+};
+
+#ifdef LOADER_DEBUG
+static int
+watch_phys_set_mask(vm_offset_t pa, u_long mask)
+{
+ u_long lsucr;
+
+ stxa(AA_DMMU_PWPR, ASI_DMMU, pa & (((2UL << 38) - 1) << 3));
+ lsucr = ldxa(0, ASI_LSU_CTL_REG);
+ lsucr = ((lsucr | LSU_PW) & ~LSU_PM_MASK) |
+ (mask << LSU_PM_SHIFT);
+ stxa(0, ASI_LSU_CTL_REG, lsucr);
+ return (0);
+}
+
+static int
+watch_phys_set(vm_offset_t pa, int sz)
+{
+ u_long off;
+
+ off = (u_long)pa & 7;
+ /* Test for misaligned watch points. */
+ if (off + sz > 8)
+ return (-1);
+ return (watch_phys_set_mask(pa, ((1 << sz) - 1) << off));
+}
+
+
+static int
+watch_virt_set_mask(vm_offset_t va, u_long mask)
+{
+ u_long lsucr;
+
+ stxa(AA_DMMU_VWPR, ASI_DMMU, va & (((2UL << 41) - 1) << 3));
+ lsucr = ldxa(0, ASI_LSU_CTL_REG);
+ lsucr = ((lsucr | LSU_VW) & ~LSU_VM_MASK) |
+ (mask << LSU_VM_SHIFT);
+ stxa(0, ASI_LSU_CTL_REG, lsucr);
+ return (0);
+}
+
+static int
+watch_virt_set(vm_offset_t va, int sz)
+{
+ u_long off;
+
+ off = (u_long)va & 7;
+ /* Test for misaligned watch points. */
+ if (off + sz > 8)
+ return (-1);
+ return (watch_virt_set_mask(va, ((1 << sz) - 1) << off));
+}
+#endif
+
+/*
+ * archsw functions
+ */
+static int
+sparc64_autoload(void)
+{
+
+ return (0);
+}
+
+static ssize_t
+sparc64_readin(const int fd, vm_offset_t va, const size_t len)
+{
+
+ mmu_ops->mmu_mapin(va, len);
+ return (read(fd, (void *)va, len));
+}
+
+static ssize_t
+sparc64_copyin(const void *src, vm_offset_t dest, size_t len)
+{
+
+ mmu_ops->mmu_mapin(dest, len);
+ memcpy((void *)dest, src, len);
+ return (len);
+}
+
+/*
+ * other MD functions
+ */
+static vm_offset_t
+claim_virt(vm_offset_t virt, size_t size, int align)
+{
+ vm_offset_t mva;
+
+ if (OF_call_method("claim", mmu, 3, 1, virt, size, align, &mva) == -1)
+ return ((vm_offset_t)-1);
+ return (mva);
+}
+
+static vm_offset_t
+alloc_phys(size_t size, int align)
+{
+ cell_t phys_hi, phys_low;
+
+ if (OF_call_method("claim", memory, 2, 2, size, align, &phys_low,
+ &phys_hi) == -1)
+ return ((vm_offset_t)-1);
+ return ((vm_offset_t)phys_hi << 32 | phys_low);
+}
+
+static int
+map_phys(int mode, size_t size, vm_offset_t virt, vm_offset_t phys)
+{
+
+ return (OF_call_method("map", mmu, 5, 0, (uint32_t)phys,
+ (uint32_t)(phys >> 32), virt, size, mode));
+}
+
+static void
+release_phys(vm_offset_t phys, u_int size)
+{
+
+ (void)OF_call_method("release", memory, 3, 0, (uint32_t)phys,
+ (uint32_t)(phys >> 32), size);
+}
+
+static int
+__elfN(exec)(struct preloaded_file *fp)
+{
+ struct file_metadata *fmp;
+ vm_offset_t mdp, dtbp;
+ Elf_Addr entry;
+ Elf_Ehdr *e;
+ int error;
+
+ if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == 0)
+ return (EFTYPE);
+ e = (Elf_Ehdr *)&fmp->md_data;
+
+ if ((error = md_load(fp->f_args, &mdp, &dtbp)) != 0)
+ return (error);
+
+ printf("jumping to kernel entry at %#lx.\n", e->e_entry);
+#ifdef LOADER_DEBUG
+ pmap_print_tlb_sun4u();
+#endif
+
+ dev_cleanup();
+
+ entry = e->e_entry;
+
+ OF_release((void *)heapva, HEAPSZ);
+
+ ((kernel_entry_t *)entry)(mdp, 0, 0, 0, openfirmware);
+
+ panic("%s: exec returned", __func__);
+}
+
+static inline u_long
+dtlb_get_data_sun4u(u_int tlb, u_int slot)
+{
+ u_long data, pstate;
+
+ slot = TLB_DAR_SLOT(tlb, slot);
+ /*
+ * We read ASI_DTLB_DATA_ACCESS_REG twice back-to-back in order to
+ * work around errata of USIII and beyond.
+ */
+ pstate = rdpr(pstate);
+ wrpr(pstate, pstate & ~PSTATE_IE, 0);
+ (void)ldxa(slot, ASI_DTLB_DATA_ACCESS_REG);
+ data = ldxa(slot, ASI_DTLB_DATA_ACCESS_REG);
+ wrpr(pstate, pstate, 0);
+ return (data);
+}
+
+static inline u_long
+itlb_get_data_sun4u(u_int tlb, u_int slot)
+{
+ u_long data, pstate;
+
+ slot = TLB_DAR_SLOT(tlb, slot);
+ /*
+ * We read ASI_DTLB_DATA_ACCESS_REG twice back-to-back in order to
+ * work around errata of USIII and beyond.
+ */
+ pstate = rdpr(pstate);
+ wrpr(pstate, pstate & ~PSTATE_IE, 0);
+ (void)ldxa(slot, ASI_ITLB_DATA_ACCESS_REG);
+ data = ldxa(slot, ASI_ITLB_DATA_ACCESS_REG);
+ wrpr(pstate, pstate, 0);
+ return (data);
+}
+
+static vm_offset_t
+dtlb_va_to_pa_sun4u(vm_offset_t va)
+{
+ u_long pstate, reg;
+ u_int i, tlb;
+
+ pstate = rdpr(pstate);
+ wrpr(pstate, pstate & ~PSTATE_IE, 0);
+ for (i = 0; i < dtlb_slot_max; i++) {
+ reg = ldxa(TLB_DAR_SLOT(tlb_locked, i),
+ ASI_DTLB_TAG_READ_REG);
+ if (TLB_TAR_VA(reg) != va)
+ continue;
+ reg = dtlb_get_data_sun4u(tlb_locked, i);
+ wrpr(pstate, pstate, 0);
+ reg >>= TD_PA_SHIFT;
+ if (cpu_impl == CPU_IMPL_SPARC64V ||
+ cpu_impl >= CPU_IMPL_ULTRASPARCIII)
+ return (reg & TD_PA_CH_MASK);
+ return (reg & TD_PA_SF_MASK);
+ }
+ wrpr(pstate, pstate, 0);
+ return (-1);
+}
+
+static vm_offset_t
+itlb_va_to_pa_sun4u(vm_offset_t va)
+{
+ u_long pstate, reg;
+ int i;
+
+ pstate = rdpr(pstate);
+ wrpr(pstate, pstate & ~PSTATE_IE, 0);
+ for (i = 0; i < itlb_slot_max; i++) {
+ reg = ldxa(TLB_DAR_SLOT(tlb_locked, i),
+ ASI_ITLB_TAG_READ_REG);
+ if (TLB_TAR_VA(reg) != va)
+ continue;
+ reg = itlb_get_data_sun4u(tlb_locked, i);
+ wrpr(pstate, pstate, 0);
+ reg >>= TD_PA_SHIFT;
+ if (cpu_impl == CPU_IMPL_SPARC64V ||
+ cpu_impl >= CPU_IMPL_ULTRASPARCIII)
+ return (reg & TD_PA_CH_MASK);
+ return (reg & TD_PA_SF_MASK);
+ }
+ wrpr(pstate, pstate, 0);
+ return (-1);
+}
+
+static int
+dtlb_enter_sun4u(u_int index, u_long data, vm_offset_t virt)
+{
+
+ return (OF_call_method("SUNW,dtlb-load", mmu, 3, 0, index, data,
+ virt));
+}
+
+static int
+itlb_enter_sun4u(u_int index, u_long data, vm_offset_t virt)
+{
+
+ if (cpu_impl == CPU_IMPL_ULTRASPARCIIIp && index == 0 &&
+ (data & TD_L) != 0)
+ panic("%s: won't enter locked TLB entry at index 0 on USIII+",
+ __func__);
+ return (OF_call_method("SUNW,itlb-load", mmu, 3, 0, index, data,
+ virt));
+}
+
+static void
+itlb_relocate_locked0_sun4u(void)
+{
+ u_long data, pstate, tag;
+ int i;
+
+ if (cpu_impl != CPU_IMPL_ULTRASPARCIIIp)
+ return;
+
+ pstate = rdpr(pstate);
+ wrpr(pstate, pstate & ~PSTATE_IE, 0);
+
+ data = itlb_get_data_sun4u(tlb_locked, 0);
+ if ((data & (TD_V | TD_L)) != (TD_V | TD_L)) {
+ wrpr(pstate, pstate, 0);
+ return;
+ }
+
+ /* Flush the mapping of slot 0. */
+ tag = ldxa(TLB_DAR_SLOT(tlb_locked, 0), ASI_ITLB_TAG_READ_REG);
+ stxa(TLB_DEMAP_VA(TLB_TAR_VA(tag)) | TLB_DEMAP_PRIMARY |
+ TLB_DEMAP_PAGE, ASI_IMMU_DEMAP, 0);
+ flush(0); /* The USIII-family ignores the address. */
+
+ /*
+ * Search a replacement slot != 0 and enter the data and tag
+ * that formerly were in slot 0.
+ */
+ for (i = 1; i < itlb_slot_max; i++) {
+ if ((itlb_get_data_sun4u(tlb_locked, i) & TD_V) != 0)
+ continue;
+
+ stxa(AA_IMMU_TAR, ASI_IMMU, tag);
+ stxa(TLB_DAR_SLOT(tlb_locked, i), ASI_ITLB_DATA_ACCESS_REG,
+ data);
+ flush(0); /* The USIII-family ignores the address. */
+ break;
+ }
+ wrpr(pstate, pstate, 0);
+ if (i == itlb_slot_max)
+ panic("%s: could not find a replacement slot", __func__);
+}
+
+static int
+mmu_mapin_sun4u(vm_offset_t va, vm_size_t len)
+{
+ vm_offset_t pa, mva;
+ u_long data;
+ u_int index;
+
+ if (va + len > curkva)
+ curkva = va + len;
+
+ pa = (vm_offset_t)-1;
+ len += va & PAGE_MASK_4M;
+ va &= ~PAGE_MASK_4M;
+ while (len) {
+ if (dtlb_va_to_pa_sun4u(va) == (vm_offset_t)-1 ||
+ itlb_va_to_pa_sun4u(va) == (vm_offset_t)-1) {
+ /* Allocate a physical page, claim the virtual area. */
+ if (pa == (vm_offset_t)-1) {
+ pa = alloc_phys(PAGE_SIZE_4M, PAGE_SIZE_4M);
+ if (pa == (vm_offset_t)-1)
+ panic("%s: out of memory", __func__);
+ mva = claim_virt(va, PAGE_SIZE_4M, 0);
+ if (mva != va)
+ panic("%s: can't claim virtual page "
+ "(wanted %#lx, got %#lx)",
+ __func__, va, mva);
+ /*
+ * The mappings may have changed, be paranoid.
+ */
+ continue;
+ }
+ /*
+ * Actually, we can only allocate two pages less at
+ * most (depending on the kernel TSB size).
+ */
+ if (dtlb_slot >= dtlb_slot_max)
+ panic("%s: out of dtlb_slots", __func__);
+ if (itlb_slot >= itlb_slot_max)
+ panic("%s: out of itlb_slots", __func__);
+ data = TD_V | TD_4M | TD_PA(pa) | TD_L | TD_CP |
+ TD_CV | TD_P | TD_W;
+ dtlb_store[dtlb_slot].te_pa = pa;
+ dtlb_store[dtlb_slot].te_va = va;
+ index = dtlb_slot_max - dtlb_slot - 1;
+ if (dtlb_enter_sun4u(index, data, va) < 0)
+ panic("%s: can't enter dTLB slot %d data "
+ "%#lx va %#lx", __func__, index, data,
+ va);
+ dtlb_slot++;
+ itlb_store[itlb_slot].te_pa = pa;
+ itlb_store[itlb_slot].te_va = va;
+ index = itlb_slot_max - itlb_slot - 1;
+ if (itlb_enter_sun4u(index, data, va) < 0)
+ panic("%s: can't enter iTLB slot %d data "
+ "%#lx va %#lxd", __func__, index, data,
+ va);
+ itlb_slot++;
+ pa = (vm_offset_t)-1;
+ }
+ len -= len > PAGE_SIZE_4M ? PAGE_SIZE_4M : len;
+ va += PAGE_SIZE_4M;
+ }
+ if (pa != (vm_offset_t)-1)
+ release_phys(pa, PAGE_SIZE_4M);
+ return (0);
+}
+
+static vm_offset_t
+init_heap(void)
+{
+
+ /* There is no need for continuous physical heap memory. */
+ heapva = (vm_offset_t)OF_claim((void *)HEAPVA, HEAPSZ, 32);
+ return (heapva);
+}
+
+static phandle_t
+find_bsp_sun4u(phandle_t node, uint32_t bspid)
+{
+ char type[sizeof("cpu")];
+ phandle_t child;
+ uint32_t cpuid;
+
+ for (; node > 0; node = OF_peer(node)) {
+ child = OF_child(node);
+ if (child > 0) {
+ child = find_bsp_sun4u(child, bspid);
+ if (child > 0)
+ return (child);
+ } else {
+ if (OF_getprop(node, "device_type", type,
+ sizeof(type)) <= 0)
+ continue;
+ if (strcmp(type, "cpu") != 0)
+ continue;
+ if (OF_getprop(node, cpu_cpuid_prop_sun4u(), &cpuid,
+ sizeof(cpuid)) <= 0)
+ continue;
+ if (cpuid == bspid)
+ return (node);
+ }
+ }
+ return (0);
+}
+
+const char *
+cpu_cpuid_prop_sun4u(void)
+{
+
+ switch (cpu_impl) {
+ case CPU_IMPL_SPARC64:
+ case CPU_IMPL_SPARC64V:
+ case CPU_IMPL_ULTRASPARCI:
+ case CPU_IMPL_ULTRASPARCII:
+ case CPU_IMPL_ULTRASPARCIIi:
+ case CPU_IMPL_ULTRASPARCIIe:
+ return ("upa-portid");
+ case CPU_IMPL_ULTRASPARCIII:
+ case CPU_IMPL_ULTRASPARCIIIp:
+ case CPU_IMPL_ULTRASPARCIIIi:
+ case CPU_IMPL_ULTRASPARCIIIip:
+ return ("portid");
+ case CPU_IMPL_ULTRASPARCIV:
+ case CPU_IMPL_ULTRASPARCIVp:
+ return ("cpuid");
+ default:
+ return ("");
+ }
+}
+
+uint32_t
+cpu_get_mid_sun4u(void)
+{
+
+ switch (cpu_impl) {
+ case CPU_IMPL_SPARC64:
+ case CPU_IMPL_SPARC64V:
+ case CPU_IMPL_ULTRASPARCI:
+ case CPU_IMPL_ULTRASPARCII:
+ case CPU_IMPL_ULTRASPARCIIi:
+ case CPU_IMPL_ULTRASPARCIIe:
+ return (UPA_CR_GET_MID(ldxa(0, ASI_UPA_CONFIG_REG)));
+ case CPU_IMPL_ULTRASPARCIII:
+ case CPU_IMPL_ULTRASPARCIIIp:
+ return (FIREPLANE_CR_GET_AID(ldxa(AA_FIREPLANE_CONFIG,
+ ASI_FIREPLANE_CONFIG_REG)));
+ case CPU_IMPL_ULTRASPARCIIIi:
+ case CPU_IMPL_ULTRASPARCIIIip:
+ return (JBUS_CR_GET_JID(ldxa(0, ASI_JBUS_CONFIG_REG)));
+ case CPU_IMPL_ULTRASPARCIV:
+ case CPU_IMPL_ULTRASPARCIVp:
+ return (INTR_ID_GET_ID(ldxa(AA_INTR_ID, ASI_INTR_ID)));
+ default:
+ return (0);
+ }
+}
+
+static void
+tlb_init_sun4u(void)
+{
+ phandle_t bsp;
+
+ cpu_impl = VER_IMPL(rdpr(ver));
+ switch (cpu_impl) {
+ case CPU_IMPL_SPARC64:
+ case CPU_IMPL_ULTRASPARCI:
+ case CPU_IMPL_ULTRASPARCII:
+ case CPU_IMPL_ULTRASPARCIIi:
+ case CPU_IMPL_ULTRASPARCIIe:
+ tlb_locked = TLB_DAR_T32;
+ break;
+ case CPU_IMPL_ULTRASPARCIII:
+ case CPU_IMPL_ULTRASPARCIIIp:
+ case CPU_IMPL_ULTRASPARCIIIi:
+ case CPU_IMPL_ULTRASPARCIIIip:
+ case CPU_IMPL_ULTRASPARCIV:
+ case CPU_IMPL_ULTRASPARCIVp:
+ tlb_locked = TLB_DAR_T16;
+ break;
+ case CPU_IMPL_SPARC64V:
+ tlb_locked = TLB_DAR_FTLB;
+ break;
+ }
+ bsp = find_bsp_sun4u(OF_child(root), cpu_get_mid_sun4u());
+ if (bsp == 0)
+ panic("%s: no node for bootcpu?!?!", __func__);
+
+ if (OF_getprop(bsp, "#dtlb-entries", &dtlb_slot_max,
+ sizeof(dtlb_slot_max)) == -1 ||
+ OF_getprop(bsp, "#itlb-entries", &itlb_slot_max,
+ sizeof(itlb_slot_max)) == -1)
+ panic("%s: can't get TLB slot max.", __func__);
+
+ if (cpu_impl == CPU_IMPL_ULTRASPARCIIIp) {
+#ifdef LOADER_DEBUG
+ printf("pre fixup:\n");
+ pmap_print_tlb_sun4u();
+#endif
+
+ /*
+ * Relocate the locked entry in it16 slot 0 (if existent)
+ * as part of working around Cheetah+ erratum 34.
+ */
+ itlb_relocate_locked0_sun4u();
+
+#ifdef LOADER_DEBUG
+ printf("post fixup:\n");
+ pmap_print_tlb_sun4u();
+#endif
+ }
+
+ dtlb_store = malloc(dtlb_slot_max * sizeof(*dtlb_store));
+ itlb_store = malloc(itlb_slot_max * sizeof(*itlb_store));
+ if (dtlb_store == NULL || itlb_store == NULL)
+ panic("%s: can't allocate TLB store", __func__);
+}
+
+#ifdef LOADER_ZFS_SUPPORT
+
+/* Set by sparc64_zfs_probe to provide partition size. */
+static size_t part_size;
+
+uint64_t
+ldi_get_size(void *priv __unused)
+{
+ return ((uint64_t)part_size);
+}
+
+static void
+sparc64_zfs_probe(void)
+{
+ struct vtoc8 vtoc;
+ char alias[64], devname[sizeof(alias) + sizeof(":x") - 1];
+ char type[sizeof("device_type")];
+ char *bdev, *dev, *odev;
+ uint64_t guid, *guidp;
+ int fd, len, part;
+ phandle_t aliases, options;
+
+ guid = 0;
+
+ /*
+ * Get the GUIDs of the ZFS pools on any additional disks listed in
+ * the boot-device environment variable.
+ */
+ if ((aliases = OF_finddevice("/aliases")) == -1)
+ goto out;
+ options = OF_finddevice("/options");
+ len = OF_getproplen(options, "boot-device");
+ if (len <= 0)
+ goto out;
+ bdev = odev = malloc(len + 1);
+ if (bdev == NULL)
+ goto out;
+ if (OF_getprop(options, "boot-device", bdev, len) <= 0)
+ goto out;
+ bdev[len] = '\0';
+ while ((dev = strsep(&bdev, " ")) != NULL) {
+ if (*dev == '\0')
+ continue;
+ strcpy(alias, dev);
+ (void)OF_getprop(aliases, dev, alias, sizeof(alias));
+ if (OF_getprop(OF_finddevice(alias), "device_type", type,
+ sizeof(type)) == -1)
+ continue;
+ if (strcmp(type, "block") != 0)
+ continue;
+
+ /* Find freebsd-zfs slices in the VTOC. */
+ fd = open(alias, O_RDONLY);
+ if (fd == -1)
+ continue;
+ lseek(fd, 0, SEEK_SET);
+ if (read(fd, &vtoc, sizeof(vtoc)) != sizeof(vtoc)) {
+ close(fd);
+ continue;
+ }
+ close(fd);
+
+ for (part = 0; part < 8; part++) {
+ if (part == 2 || vtoc.part[part].tag !=
+ VTOC_TAG_FREEBSD_ZFS)
+ continue;
+ part_size = vtoc.map[part].nblks;
+ (void)sprintf(devname, "%s:%c", alias, part + 'a');
+ /* Get the GUID of the ZFS pool on the boot device. */
+ if (strcmp(devname, bootpath) == 0)
+ guidp = &guid;
+ else
+ guidp = NULL;
+ if (zfs_probe_dev(devname, guidp) == ENXIO)
+ break;
+ }
+ }
+ free(odev);
+
+ out:
+ if (guid != 0) {
+ zfs_currdev.pool_guid = guid;
+ zfs_currdev.root_guid = 0;
+ zfs_currdev.d_dev = &zfs_dev;
+ zfs_currdev.d_type = zfs_currdev.d_dev->dv_type;
+ }
+}
+#endif /* LOADER_ZFS_SUPPORT */
+
+int
+main(int (*openfirm)(void *))
+{
+ char compatible[32];
+ struct devsw **dp;
+
+ /*
+ * Tell the Open Firmware functions where they find the OFW gate.
+ */
+ OF_init(openfirm);
+
+ archsw.arch_getdev = ofw_getdev;
+ archsw.arch_copyin = sparc64_copyin;
+ archsw.arch_copyout = ofw_copyout;
+ archsw.arch_readin = sparc64_readin;
+ archsw.arch_autoload = sparc64_autoload;
+#ifdef LOADER_ZFS_SUPPORT
+ archsw.arch_zfs_probe = sparc64_zfs_probe;
+#endif
+
+ if (init_heap() == (vm_offset_t)-1)
+ OF_exit();
+ setheap((void *)heapva, (void *)(heapva + HEAPSZ));
+
+ /*
+ * Probe for a console.
+ */
+ cons_probe();
+
+ if ((root = OF_peer(0)) == -1)
+ panic("%s: can't get root phandle", __func__);
+ OF_getprop(root, "compatible", compatible, sizeof(compatible));
+ mmu_ops = &mmu_ops_sun4u;
+
+ mmu_ops->tlb_init();
+
+ /*
+ * Set up the current device.
+ */
+ OF_getprop(chosen, "bootpath", bootpath, sizeof(bootpath));
+
+ /*
+ * Initialize devices.
+ */
+ for (dp = devsw; *dp != NULL; dp++)
+ if ((*dp)->dv_init != 0)
+ (*dp)->dv_init();
+
+#ifdef LOADER_ZFS_SUPPORT
+ if (zfs_currdev.pool_guid != 0) {
+ (void)strncpy(bootpath, zfs_fmtdev(&zfs_currdev),
+ sizeof(bootpath) - 1);
+ bootpath[sizeof(bootpath) - 1] = '\0';
+ } else
+#endif
+
+ /*
+ * Sun compatible bootable CD-ROMs have a disk label placed before
+ * the ISO 9660 data, with the actual file system being in the first
+ * partition, while the other partitions contain pseudo disk labels
+ * with embedded boot blocks for different architectures, which may
+ * be followed by UFS file systems.
+ * The firmware will set the boot path to the partition it boots from
+ * ('f' in the sun4u/sun4v case), but we want the kernel to be loaded
+ * from the ISO 9660 file system ('a'), so the boot path needs to be
+ * altered.
+ */
+ if (bootpath[strlen(bootpath) - 2] == ':' &&
+ bootpath[strlen(bootpath) - 1] == 'f')
+ bootpath[strlen(bootpath) - 1] = 'a';
+
+ env_setenv("currdev", EV_VOLATILE, bootpath,
+ ofw_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, bootpath,
+ env_noset, env_nounset);
+
+ printf("\n%s", bootprog_info);
+ printf("bootpath=\"%s\"\n", bootpath);
+
+ /* Give control to the machine independent loader code. */
+ interact(NULL);
+ return (1);
+}
+
+COMMAND_SET(heap, "heap", "show heap usage", command_heap);
+
+static int
+command_heap(int argc, char *argv[])
+{
+
+ mallocstats();
+ printf("heap base at %p, top at %p, upper limit at %p\n", heapva,
+ sbrk(0), heapva + HEAPSZ);
+ return(CMD_OK);
+}
+
+COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
+
+static int
+command_reboot(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; devsw[i] != NULL; ++i)
+ if (devsw[i]->dv_cleanup != NULL)
+ (devsw[i]->dv_cleanup)();
+
+ printf("Rebooting...\n");
+ OF_exit();
+}
+
+/* provide this for panic, as it's not in the startup code */
+void
+exit(int code)
+{
+
+ OF_exit();
+}
+
+#ifdef LOADER_DEBUG
+static const char *const page_sizes[] = {
+ " 8k", " 64k", "512k", " 4m"
+};
+
+static void
+pmap_print_tte_sun4u(tte_t tag, tte_t tte)
+{
+
+ printf("%s %s ",
+ page_sizes[(tte >> TD_SIZE_SHIFT) & TD_SIZE_MASK],
+ tag & TD_G ? "G" : " ");
+ printf(tte & TD_W ? "W " : " ");
+ printf(tte & TD_P ? "\e[33mP\e[0m " : " ");
+ printf(tte & TD_E ? "E " : " ");
+ printf(tte & TD_CV ? "CV " : " ");
+ printf(tte & TD_CP ? "CP " : " ");
+ printf(tte & TD_L ? "\e[32mL\e[0m " : " ");
+ printf(tte & TD_IE ? "IE " : " ");
+ printf(tte & TD_NFO ? "NFO " : " ");
+ printf("pa=0x%lx va=0x%lx ctx=%ld\n",
+ TD_PA(tte), TLB_TAR_VA(tag), TLB_TAR_CTX(tag));
+}
+
+static void
+pmap_print_tlb_sun4u(void)
+{
+ tte_t tag, tte;
+ u_long pstate;
+ int i;
+
+ pstate = rdpr(pstate);
+ for (i = 0; i < itlb_slot_max; i++) {
+ wrpr(pstate, pstate & ~PSTATE_IE, 0);
+ tte = itlb_get_data_sun4u(tlb_locked, i);
+ wrpr(pstate, pstate, 0);
+ if (!(tte & TD_V))
+ continue;
+ tag = ldxa(TLB_DAR_SLOT(tlb_locked, i),
+ ASI_ITLB_TAG_READ_REG);
+ printf("iTLB-%2u: ", i);
+ pmap_print_tte_sun4u(tag, tte);
+ }
+ for (i = 0; i < dtlb_slot_max; i++) {
+ wrpr(pstate, pstate & ~PSTATE_IE, 0);
+ tte = dtlb_get_data_sun4u(tlb_locked, i);
+ wrpr(pstate, pstate, 0);
+ if (!(tte & TD_V))
+ continue;
+ tag = ldxa(TLB_DAR_SLOT(tlb_locked, i),
+ ASI_DTLB_TAG_READ_REG);
+ printf("dTLB-%2u: ", i);
+ pmap_print_tte_sun4u(tag, tte);
+ }
+}
+#endif
diff --git a/stand/sparc64/loader/metadata.c b/stand/sparc64/loader/metadata.c
new file mode 100644
index 0000000..a3b3eb5
--- /dev/null
+++ b/stand/sparc64/loader/metadata.c
@@ -0,0 +1,345 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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.
+ *
+ * from: FreeBSD: src/sys/boot/i386/libi386/bootinfo.c,v 1.29
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/boot.h>
+
+#include <machine/metadata.h>
+
+#include "bootstrap.h"
+#include "libofw.h"
+
+extern struct tlb_entry *dtlb_store;
+extern struct tlb_entry *itlb_store;
+
+extern int dtlb_slot;
+extern int itlb_slot;
+
+static int md_bootserial(void);
+
+int
+md_getboothowto(char *kargs)
+{
+ char *cp;
+ int howto;
+ int active;
+ int i;
+
+ /* Parse kargs */
+ howto = 0;
+ if (kargs != NULL) {
+ cp = kargs;
+ active = 0;
+ while (*cp != 0) {
+ if (!active && (*cp == '-')) {
+ active = 1;
+ } else if (active)
+ switch (*cp) {
+ case 'a':
+ howto |= RB_ASKNAME;
+ break;
+ case 'C':
+ howto |= RB_CDROM;
+ break;
+ case 'd':
+ howto |= RB_KDB;
+ break;
+ case 'D':
+ howto |= RB_MULTIPLE;
+ break;
+ case 'm':
+ howto |= RB_MUTE;
+ break;
+ case 'g':
+ howto |= RB_GDB;
+ break;
+ case 'h':
+ howto |= RB_SERIAL;
+ break;
+ case 'p':
+ howto |= RB_PAUSE;
+ break;
+ case 'r':
+ howto |= RB_DFLTROOT;
+ break;
+ case 's':
+ howto |= RB_SINGLE;
+ break;
+ case 'v':
+ howto |= RB_VERBOSE;
+ break;
+ default:
+ active = 0;
+ break;
+ }
+ cp++;
+ }
+ }
+ /* get equivalents from the environment */
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+ if (md_bootserial() != -1)
+ howto |= RB_SERIAL;
+ return(howto);
+}
+
+static int
+md_bootserial(void)
+{
+ char buf[64];
+ ihandle_t inst;
+ phandle_t input;
+ phandle_t node;
+ phandle_t output;
+
+ if ((node = OF_finddevice("/options")) == -1)
+ return(-1);
+ if (OF_getprop(node, "input-device", buf, sizeof(buf)) == -1)
+ return(-1);
+ input = OF_finddevice(buf);
+ if (OF_getprop(node, "output-device", buf, sizeof(buf)) == -1)
+ return(-1);
+ output = OF_finddevice(buf);
+ if (input == -1 || output == -1 || OF_getproplen(input, "keyboard") >= 0) {
+ if ((node = OF_finddevice("/chosen")) == -1)
+ return(-1);
+ if (OF_getprop(node, "stdin", &inst, sizeof(inst)) == -1)
+ return(-1);
+ if ((input = OF_instance_to_package(inst)) == -1)
+ return(-1);
+ if (OF_getprop(node, "stdout", &inst, sizeof(inst)) == -1)
+ return(-1);
+ if ((output = OF_instance_to_package(inst)) == -1)
+ return(-1);
+ }
+ if (input != output)
+ return(-1);
+ if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1)
+ return(-1);
+ if (strcmp(buf, "serial") != 0)
+ return(-1);
+ return(0);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+vm_offset_t
+md_copyenv(vm_offset_t addr)
+{
+ struct env_var *ep;
+
+ /* traverse the environment */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ archsw.arch_copyin(ep->ev_name, addr, strlen(ep->ev_name));
+ addr += strlen(ep->ev_name);
+ archsw.arch_copyin("=", addr, 1);
+ addr++;
+ if (ep->ev_value != NULL) {
+ archsw.arch_copyin(ep->ev_value, addr, strlen(ep->ev_value));
+ addr += strlen(ep->ev_value);
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ return(addr);
+}
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ archsw.arch_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c) \
+ if (c) \
+ archsw.arch_copyin(s, a, strlen(s) + 1);\
+ a += roundup(strlen(s) + 1, sizeof(u_long));\
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ archsw.arch_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(u_long)); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c);\
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ archsw.arch_copyin(mm->md_data, a, mm->md_size);\
+ a += roundup(mm->md_size, sizeof(u_long)); \
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+vm_offset_t
+md_copymodules(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ int c;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must come first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ MOD_ADDR(addr, fp->f_addr, c);
+ MOD_SIZE(addr, fp->f_size, c);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next) {
+ if (!(md->md_type & MODINFOMD_NOCOPY)) {
+ MOD_METADATA(addr, md, c);
+ }
+ }
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Load the information expected by a sparc64 kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+md_load(char *args, vm_offset_t *modulep, vm_offset_t *dtbp)
+{
+ struct preloaded_file *kfp;
+ struct preloaded_file *xp;
+ struct file_metadata *md;
+ vm_offset_t kernend;
+ vm_offset_t addr;
+ vm_offset_t envp;
+ vm_offset_t size;
+ char *rootdevname;
+ int howto;
+
+ howto = md_getboothowto(args);
+ *dtbp = 0;
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied device
+ * This should perhaps go to MI code and/or have $rootdev tested/set by
+ * MI code before launching the kernel.
+ */
+ if ((rootdevname = getenv("rootdev")) == NULL)
+ rootdevname = getenv("currdev");
+ getrootmount(rootdevname);
+
+ /* find the last module in the chain */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* copy our environment */
+ envp = addr;
+ addr = md_copyenv(addr);
+
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ kernend = 0;
+ kfp = file_findfile(NULL, "elf64 kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ file_addmetadata(kfp, MODINFOMD_DTLB_SLOTS, sizeof dtlb_slot, &dtlb_slot);
+ file_addmetadata(kfp, MODINFOMD_ITLB_SLOTS, sizeof itlb_slot, &itlb_slot);
+ file_addmetadata(kfp, MODINFOMD_DTLB,
+ dtlb_slot * sizeof(*dtlb_store), dtlb_store);
+ file_addmetadata(kfp, MODINFOMD_ITLB,
+ itlb_slot * sizeof(*itlb_store), itlb_store);
+
+ *modulep = addr;
+ size = md_copymodules(0);
+ kernend = roundup(addr + size, PAGE_SIZE);
+
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ bcopy(&kernend, md->md_data, sizeof kernend);
+
+ (void)md_copymodules(addr);
+
+ return(0);
+}
diff --git a/stand/sparc64/loader/version b/stand/sparc64/loader/version
new file mode 100644
index 0000000..bef4091
--- /dev/null
+++ b/stand/sparc64/loader/version
@@ -0,0 +1,6 @@
+$FreeBSD$
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+1.0: I hate the loader.
diff --git a/stand/sparc64/zfsboot/Makefile b/stand/sparc64/zfsboot/Makefile
new file mode 100644
index 0000000..9e58c27
--- /dev/null
+++ b/stand/sparc64/zfsboot/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../boot1
+
+PROGNAME= zfsboot
+CFLAGS+= -DZFSBOOT
+FILES= zfsboot
+
+.include "${.CURDIR}/../boot1/Makefile"
diff --git a/stand/sparc64/zfsloader/Makefile b/stand/sparc64/zfsloader/Makefile
new file mode 100644
index 0000000..b4c554b
--- /dev/null
+++ b/stand/sparc64/zfsloader/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= zfsloader
+NEWVERSWHAT= "ZFS enabled bootstrap loader" sparc64
+HAVE_ZFS= yes
+
+.include "${.CURDIR}/../loader/Makefile"
diff --git a/stand/uboot.mk b/stand/uboot.mk
new file mode 100644
index 0000000..d120481
--- /dev/null
+++ b/stand/uboot.mk
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+SRCS+= main.c metadata.c
+
+.PATH: ${UBOOTSRC}/common
+
+CFLAGS+= -I${UBOOTSRC}/common
+
+# U-Boot standalone support library
+LIBUBOOT= ${BOOTOBJ}/uboot/lib/libuboot.a
+CFLAGS+= -I${UBOOTSRC}/lib
+CFLAGS+= -I${BOOTOBJ}/uboot/lib
+
+.include "${BOOTSRC}/fdt.mk"
+
+.if ${MK_FDT} == "yes"
+LIBUBOOT_FDT= ${BOOTOBJ}/uboot/fdt/libuboot_fdt.a
+.endif
diff --git a/stand/uboot/Makefile b/stand/uboot/Makefile
new file mode 100644
index 0000000..8c3a80d
--- /dev/null
+++ b/stand/uboot/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+SUBDIR= lib
+
+.if ${MK_FDT} != "no"
+SUBDIR+=fdt
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/stand/uboot/Makefile.inc b/stand/uboot/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/stand/uboot/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/stand/uboot/common/main.c b/stand/uboot/common/main.c
new file mode 100644
index 0000000..c4efb1f
--- /dev/null
+++ b/stand/uboot/common/main.c
@@ -0,0 +1,680 @@
+/*-
+ * Copyright (c) 2000 Benno Rice <benno@jeamland.net>
+ * Copyright (c) 2000 Stephane Potvin <sepotvin@videotron.ca>
+ * Copyright (c) 2007-2008 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * 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 AUTHORS 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+
+#include <stand.h>
+
+#include "api_public.h"
+#include "bootstrap.h"
+#include "glue.h"
+#include "libuboot.h"
+
+#ifndef nitems
+#define nitems(x) (sizeof((x)) / sizeof((x)[0]))
+#endif
+
+struct uboot_devdesc currdev;
+struct arch_switch archsw; /* MI/MD interface boundary */
+int devs_no;
+
+uintptr_t uboot_heap_start;
+uintptr_t uboot_heap_end;
+
+struct device_type {
+ const char *name;
+ int type;
+} device_types[] = {
+ { "disk", DEV_TYP_STOR },
+ { "ide", DEV_TYP_STOR | DT_STOR_IDE },
+ { "mmc", DEV_TYP_STOR | DT_STOR_MMC },
+ { "sata", DEV_TYP_STOR | DT_STOR_SATA },
+ { "scsi", DEV_TYP_STOR | DT_STOR_SCSI },
+ { "usb", DEV_TYP_STOR | DT_STOR_USB },
+ { "net", DEV_TYP_NET }
+};
+
+extern char end[];
+extern char bootprog_info[];
+
+extern unsigned char _etext[];
+extern unsigned char _edata[];
+extern unsigned char __bss_start[];
+extern unsigned char __sbss_start[];
+extern unsigned char __sbss_end[];
+extern unsigned char _end[];
+
+#ifdef LOADER_FDT_SUPPORT
+extern int command_fdt_internal(int argc, char *argv[]);
+#endif
+
+static void
+dump_sig(struct api_signature *sig)
+{
+#ifdef DEBUG
+ printf("signature:\n");
+ printf(" version\t= %d\n", sig->version);
+ printf(" checksum\t= 0x%08x\n", sig->checksum);
+ printf(" sc entry\t= 0x%08x\n", sig->syscall);
+#endif
+}
+
+static void
+dump_addr_info(void)
+{
+#ifdef DEBUG
+ printf("\naddresses info:\n");
+ printf(" _etext (sdata) = 0x%08x\n", (uint32_t)_etext);
+ printf(" _edata = 0x%08x\n", (uint32_t)_edata);
+ printf(" __sbss_start = 0x%08x\n", (uint32_t)__sbss_start);
+ printf(" __sbss_end = 0x%08x\n", (uint32_t)__sbss_end);
+ printf(" __sbss_start = 0x%08x\n", (uint32_t)__bss_start);
+ printf(" _end = 0x%08x\n", (uint32_t)_end);
+ printf(" syscall entry = 0x%08x\n", (uint32_t)syscall_ptr);
+#endif
+}
+
+static uint64_t
+memsize(struct sys_info *si, int flags)
+{
+ uint64_t size;
+ int i;
+
+ size = 0;
+ for (i = 0; i < si->mr_no; i++)
+ if (si->mr[i].flags == flags && si->mr[i].size)
+ size += (si->mr[i].size);
+
+ return (size);
+}
+
+static void
+meminfo(void)
+{
+ uint64_t size;
+ struct sys_info *si;
+ int t[3] = { MR_ATTR_DRAM, MR_ATTR_FLASH, MR_ATTR_SRAM };
+ int i;
+
+ if ((si = ub_get_sys_info()) == NULL)
+ panic("could not retrieve system info");
+
+ for (i = 0; i < 3; i++) {
+ size = memsize(si, t[i]);
+ if (size > 0)
+ printf("%s: %juMB\n", ub_mem_type(t[i]),
+ (uintmax_t)(size / 1024 / 1024));
+ }
+}
+
+static const char *
+get_device_type(const char *devstr, int *devtype)
+{
+ int i;
+ int namelen;
+ struct device_type *dt;
+
+ if (devstr) {
+ for (i = 0; i < nitems(device_types); i++) {
+ dt = &device_types[i];
+ namelen = strlen(dt->name);
+ if (strncmp(dt->name, devstr, namelen) == 0) {
+ *devtype = dt->type;
+ return (devstr + namelen);
+ }
+ }
+ printf("Unknown device type '%s'\n", devstr);
+ }
+
+ *devtype = -1;
+ return (NULL);
+}
+
+static const char *
+device_typename(int type)
+{
+ int i;
+
+ for (i = 0; i < nitems(device_types); i++)
+ if (device_types[i].type == type)
+ return (device_types[i].name);
+
+ return ("<unknown>");
+}
+
+/*
+ * Parse a device string into type, unit, slice and partition numbers. A
+ * returned value of -1 for type indicates a search should be done for the
+ * first loadable device, otherwise a returned value of -1 for unit
+ * indicates a search should be done for the first loadable device of the
+ * given type.
+ *
+ * The returned values for slice and partition are interpreted by
+ * disk_open().
+ *
+ * Valid device strings: For device types:
+ *
+ * <type_name> DEV_TYP_STOR, DEV_TYP_NET
+ * <type_name><unit> DEV_TYP_STOR, DEV_TYP_NET
+ * <type_name><unit>: DEV_TYP_STOR, DEV_TYP_NET
+ * <type_name><unit>:<slice> DEV_TYP_STOR
+ * <type_name><unit>:<slice>. DEV_TYP_STOR
+ * <type_name><unit>:<slice>.<partition> DEV_TYP_STOR
+ *
+ * For valid type names, see the device_types array, above.
+ *
+ * Slice numbers are 1-based. 0 is a wildcard.
+ */
+static void
+get_load_device(int *type, int *unit, int *slice, int *partition)
+{
+ char *devstr;
+ const char *p;
+ char *endp;
+
+ *type = -1;
+ *unit = -1;
+ *slice = 0;
+ *partition = -1;
+
+ devstr = ub_env_get("loaderdev");
+ if (devstr == NULL) {
+ printf("U-Boot env: loaderdev not set, will probe all devices.\n");
+ return;
+ }
+ printf("U-Boot env: loaderdev='%s'\n", devstr);
+
+ p = get_device_type(devstr, type);
+
+ /* Ignore optional spaces after the device name. */
+ while (*p == ' ')
+ p++;
+
+ /* Unknown device name, or a known name without unit number. */
+ if ((*type == -1) || (*p == '\0')) {
+ return;
+ }
+
+ /* Malformed unit number. */
+ if (!isdigit(*p)) {
+ *type = -1;
+ return;
+ }
+
+ /* Guaranteed to extract a number from the string, as *p is a digit. */
+ *unit = strtol(p, &endp, 10);
+ p = endp;
+
+ /* Known device name with unit number and nothing else. */
+ if (*p == '\0') {
+ return;
+ }
+
+ /* Device string is malformed beyond unit number. */
+ if (*p != ':') {
+ *type = -1;
+ *unit = -1;
+ return;
+ }
+
+ p++;
+
+ /* No slice and partition specification. */
+ if ('\0' == *p )
+ return;
+
+ /* Only DEV_TYP_STOR devices can have a slice specification. */
+ if (!(*type & DEV_TYP_STOR)) {
+ *type = -1;
+ *unit = -1;
+ return;
+ }
+
+ *slice = strtoul(p, &endp, 10);
+
+ /* Malformed slice number. */
+ if (p == endp) {
+ *type = -1;
+ *unit = -1;
+ *slice = 0;
+ return;
+ }
+
+ p = endp;
+
+ /* No partition specification. */
+ if (*p == '\0')
+ return;
+
+ /* Device string is malformed beyond slice number. */
+ if (*p != '.') {
+ *type = -1;
+ *unit = -1;
+ *slice = 0;
+ return;
+ }
+
+ p++;
+
+ /* No partition specification. */
+ if (*p == '\0')
+ return;
+
+ *partition = strtol(p, &endp, 10);
+ p = endp;
+
+ /* Full, valid device string. */
+ if (*endp == '\0')
+ return;
+
+ /* Junk beyond partition number. */
+ *type = -1;
+ *unit = -1;
+ *slice = 0;
+ *partition = -1;
+}
+
+static void
+print_disk_probe_info()
+{
+ char slice[32];
+ char partition[32];
+
+ if (currdev.d_disk.slice > 0)
+ sprintf(slice, "%d", currdev.d_disk.slice);
+ else
+ strcpy(slice, "<auto>");
+
+ if (currdev.d_disk.partition >= 0)
+ sprintf(partition, "%d", currdev.d_disk.partition);
+ else
+ strcpy(partition, "<auto>");
+
+ printf(" Checking unit=%d slice=%s partition=%s...",
+ currdev.d_unit, slice, partition);
+
+}
+
+static int
+probe_disks(int devidx, int load_type, int load_unit, int load_slice,
+ int load_partition)
+{
+ int open_result, unit;
+ struct open_file f;
+
+ currdev.d_disk.slice = load_slice;
+ currdev.d_disk.partition = load_partition;
+
+ f.f_devdata = &currdev;
+ open_result = -1;
+
+ if (load_type == -1) {
+ printf(" Probing all disk devices...\n");
+ /* Try each disk in succession until one works. */
+ for (currdev.d_unit = 0; currdev.d_unit < UB_MAX_DEV;
+ currdev.d_unit++) {
+ print_disk_probe_info();
+ open_result = devsw[devidx]->dv_open(&f, &currdev);
+ if (open_result == 0) {
+ printf(" good.\n");
+ return (0);
+ }
+ printf("\n");
+ }
+ return (-1);
+ }
+
+ if (load_unit == -1) {
+ printf(" Probing all %s devices...\n", device_typename(load_type));
+ /* Try each disk of given type in succession until one works. */
+ for (unit = 0; unit < UB_MAX_DEV; unit++) {
+ currdev.d_unit = uboot_diskgetunit(load_type, unit);
+ if (currdev.d_unit == -1)
+ break;
+ print_disk_probe_info();
+ open_result = devsw[devidx]->dv_open(&f, &currdev);
+ if (open_result == 0) {
+ printf(" good.\n");
+ return (0);
+ }
+ printf("\n");
+ }
+ return (-1);
+ }
+
+ if ((currdev.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) {
+ print_disk_probe_info();
+ open_result = devsw[devidx]->dv_open(&f,&currdev);
+ if (open_result == 0) {
+ printf(" good.\n");
+ return (0);
+ }
+ printf("\n");
+ }
+
+ printf(" Requested disk type/unit/slice/partition not found\n");
+ return (-1);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct api_signature *sig = NULL;
+ int load_type, load_unit, load_slice, load_partition;
+ int i;
+ const char *ldev;
+
+ /*
+ * We first check if a command line argument was passed to us containing
+ * API's signature address. If it wasn't then we try to search for the
+ * API signature via the usual hinted address.
+ * If we can't find the magic signature and related info, exit with a
+ * unique error code that U-Boot reports as "## Application terminated,
+ * rc = 0xnnbadab1". Hopefully 'badab1' looks enough like "bad api" to
+ * provide a clue. It's better than 0xffffffff anyway.
+ */
+ if (!api_parse_cmdline_sig(argc, argv, &sig) && !api_search_sig(&sig))
+ return (0x01badab1);
+
+ syscall_ptr = sig->syscall;
+ if (syscall_ptr == NULL)
+ return (0x02badab1);
+
+ if (sig->version > API_SIG_VERSION)
+ return (0x03badab1);
+
+ /* Clear BSS sections */
+ bzero(__sbss_start, __sbss_end - __sbss_start);
+ bzero(__bss_start, _end - __bss_start);
+
+ /*
+ * Initialise the heap as early as possible. Once this is done,
+ * alloc() is usable. We are using the stack u-boot set up near the top
+ * of physical ram; hopefully there is sufficient space between the end
+ * of our bss and the bottom of the u-boot stack to avoid overlap.
+ */
+ uboot_heap_start = round_page((uintptr_t)end);
+ uboot_heap_end = uboot_heap_start + 512 * 1024;
+ setheap((void *)uboot_heap_start, (void *)uboot_heap_end);
+
+ /*
+ * Set up console.
+ */
+ cons_probe();
+ printf("Compatible U-Boot API signature found @%p\n", sig);
+
+ printf("\n%s", bootprog_info);
+ printf("\n");
+
+ dump_sig(sig);
+ dump_addr_info();
+
+ meminfo();
+
+ /*
+ * Enumerate U-Boot devices
+ */
+ if ((devs_no = ub_dev_enum()) == 0)
+ panic("no U-Boot devices found");
+ printf("Number of U-Boot devices: %d\n", devs_no);
+
+ get_load_device(&load_type, &load_unit, &load_slice, &load_partition);
+
+ /*
+ * March through the device switch probing for things.
+ */
+ for (i = 0; devsw[i] != NULL; i++) {
+
+ if (devsw[i]->dv_init == NULL)
+ continue;
+ if ((devsw[i]->dv_init)() != 0)
+ continue;
+
+ printf("Found U-Boot device: %s\n", devsw[i]->dv_name);
+
+ currdev.d_dev = devsw[i];
+ currdev.d_type = currdev.d_dev->dv_type;
+ currdev.d_unit = 0;
+
+ if ((load_type == -1 || (load_type & DEV_TYP_STOR)) &&
+ strcmp(devsw[i]->dv_name, "disk") == 0) {
+ if (probe_disks(i, load_type, load_unit, load_slice,
+ load_partition) == 0)
+ break;
+ }
+
+ if ((load_type == -1 || (load_type & DEV_TYP_NET)) &&
+ strcmp(devsw[i]->dv_name, "net") == 0)
+ break;
+ }
+
+ /*
+ * If we couldn't find a boot device, return an error to u-boot.
+ * U-boot may be running a boot script that can try something different
+ * so returning an error is better than forcing a reboot.
+ */
+ if (devsw[i] == NULL) {
+ printf("No boot device found!\n");
+ return (0xbadef1ce);
+ }
+
+ ldev = uboot_fmtdev(&currdev);
+ env_setenv("currdev", EV_VOLATILE, ldev, uboot_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, ldev, env_noset, env_nounset);
+ printf("Booting from %s\n", ldev);
+
+ setenv("LINES", "24", 1); /* optional */
+ setenv("prompt", "loader>", 1);
+
+ archsw.arch_loadaddr = uboot_loadaddr;
+ archsw.arch_getdev = uboot_getdev;
+ archsw.arch_copyin = uboot_copyin;
+ archsw.arch_copyout = uboot_copyout;
+ archsw.arch_readin = uboot_readin;
+ archsw.arch_autoload = uboot_autoload;
+
+ interact(NULL); /* doesn't return */
+
+ return (0);
+}
+
+
+COMMAND_SET(heap, "heap", "show heap usage", command_heap);
+static int
+command_heap(int argc, char *argv[])
+{
+
+ printf("heap base at %p, top at %p, used %td\n", end, sbrk(0),
+ sbrk(0) - end);
+
+ return (CMD_OK);
+}
+
+COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
+static int
+command_reboot(int argc, char *argv[])
+{
+
+ printf("Resetting...\n");
+ ub_reset();
+
+ printf("Reset failed!\n");
+ while(1);
+}
+
+COMMAND_SET(devinfo, "devinfo", "show U-Boot devices", command_devinfo);
+static int
+command_devinfo(int argc, char *argv[])
+{
+ int i;
+
+ if ((devs_no = ub_dev_enum()) == 0) {
+ command_errmsg = "no U-Boot devices found!?";
+ return (CMD_ERROR);
+ }
+
+ printf("U-Boot devices:\n");
+ for (i = 0; i < devs_no; i++) {
+ ub_dump_di(i);
+ printf("\n");
+ }
+ return (CMD_OK);
+}
+
+COMMAND_SET(sysinfo, "sysinfo", "show U-Boot system info", command_sysinfo);
+static int
+command_sysinfo(int argc, char *argv[])
+{
+ struct sys_info *si;
+
+ if ((si = ub_get_sys_info()) == NULL) {
+ command_errmsg = "could not retrieve U-Boot sys info!?";
+ return (CMD_ERROR);
+ }
+
+ printf("U-Boot system info:\n");
+ ub_dump_si(si);
+ return (CMD_OK);
+}
+
+enum ubenv_action {
+ UBENV_UNKNOWN,
+ UBENV_SHOW,
+ UBENV_IMPORT
+};
+
+static void
+handle_uboot_env_var(enum ubenv_action action, const char * var)
+{
+ char ldvar[128];
+ const char *val;
+ char *wrk;
+ int len;
+
+ /*
+ * On an import with the variable name formatted as ldname=ubname,
+ * import the uboot variable ubname into the loader variable ldname,
+ * otherwise the historical behavior is to import to uboot.ubname.
+ */
+ if (action == UBENV_IMPORT) {
+ len = strcspn(var, "=");
+ if (len == 0) {
+ printf("name cannot start with '=': '%s'\n", var);
+ return;
+ }
+ if (var[len] == 0) {
+ strcpy(ldvar, "uboot.");
+ strncat(ldvar, var, sizeof(ldvar) - 7);
+ } else {
+ len = MIN(len, sizeof(ldvar) - 1);
+ strncpy(ldvar, var, len);
+ ldvar[len] = 0;
+ var = &var[len + 1];
+ }
+ }
+
+ /*
+ * If the user prepended "uboot." (which is how they usually see these
+ * names) strip it off as a convenience.
+ */
+ if (strncmp(var, "uboot.", 6) == 0) {
+ var = &var[6];
+ }
+
+ /* If there is no variable name left, punt. */
+ if (var[0] == 0) {
+ printf("empty variable name\n");
+ return;
+ }
+
+ val = ub_env_get(var);
+ if (action == UBENV_SHOW) {
+ if (val == NULL)
+ printf("uboot.%s is not set\n", var);
+ else
+ printf("uboot.%s=%s\n", var, val);
+ } else if (action == UBENV_IMPORT) {
+ if (val != NULL) {
+ setenv(ldvar, val, 1);
+ }
+ }
+}
+
+static int
+command_ubenv(int argc, char *argv[])
+{
+ enum ubenv_action action;
+ const char *var;
+ int i;
+
+ action = UBENV_UNKNOWN;
+ if (argc > 1) {
+ if (strcasecmp(argv[1], "import") == 0)
+ action = UBENV_IMPORT;
+ else if (strcasecmp(argv[1], "show") == 0)
+ action = UBENV_SHOW;
+ }
+ if (action == UBENV_UNKNOWN) {
+ command_errmsg = "usage: 'ubenv <import|show> [var ...]";
+ return (CMD_ERROR);
+ }
+
+ if (argc > 2) {
+ for (i = 2; i < argc; i++)
+ handle_uboot_env_var(action, argv[i]);
+ } else {
+ var = NULL;
+ for (;;) {
+ if ((var = ub_env_enum(var)) == NULL)
+ break;
+ handle_uboot_env_var(action, var);
+ }
+ }
+
+ return (CMD_OK);
+}
+COMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv);
+
+#ifdef LOADER_FDT_SUPPORT
+/*
+ * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
+ * and declaring it as extern is in contradiction with COMMAND_SET() macro
+ * (which uses static pointer), we're defining wrapper function, which
+ * calls the proper fdt handling routine.
+ */
+static int
+command_fdt(int argc, char *argv[])
+{
+
+ return (command_fdt_internal(argc, argv));
+}
+
+COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
+#endif
diff --git a/stand/uboot/common/metadata.c b/stand/uboot/common/metadata.c
new file mode 100644
index 0000000..38db4bc
--- /dev/null
+++ b/stand/uboot/common/metadata.c
@@ -0,0 +1,366 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (C) 2006 Semihalf, Piotr Kruszynski <ppk@semihalf.com>
+ * Copyright (C) 2007-2008 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/boot.h>
+
+#include <machine/elf.h>
+#include <machine/metadata.h>
+
+#include "api_public.h"
+#include "bootstrap.h"
+#include "glue.h"
+
+#if defined(LOADER_FDT_SUPPORT)
+#include <fdt_platform.h>
+#endif
+
+static int
+md_getboothowto(char *kargs)
+{
+ char *cp;
+ char *p;
+ int howto;
+ int active;
+ int i;
+
+ /* Parse kargs */
+ howto = 0;
+ if (kargs != NULL) {
+ cp = kargs;
+ active = 0;
+ while (*cp != 0) {
+ if (!active && (*cp == '-'))
+ active = 1;
+ else if (active)
+ switch (*cp) {
+ case 'a':
+ howto |= RB_ASKNAME;
+ break;
+ case 'C':
+ howto |= RB_CDROM;
+ break;
+ case 'd':
+ howto |= RB_KDB;
+ break;
+ case 'D':
+ howto |= RB_MULTIPLE;
+ break;
+ case 'm':
+ howto |= RB_MUTE;
+ break;
+ case 'g':
+ howto |= RB_GDB;
+ break;
+ case 'h':
+ howto |= RB_SERIAL;
+ break;
+ case 'p':
+ howto |= RB_PAUSE;
+ break;
+ case 'r':
+ howto |= RB_DFLTROOT;
+ break;
+ case 's':
+ howto |= RB_SINGLE;
+ break;
+ case 'v':
+ howto |= RB_VERBOSE;
+ break;
+ default:
+ active = 0;
+ break;
+ }
+ cp++;
+ }
+ }
+
+ /* get equivalents from the environment */
+ for (i = 0; howto_names[i].ev != NULL; i++) {
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+ }
+ if ((p = getenv("console"))) {
+ if (!strcmp(p, "comconsole"))
+ howto |= RB_SERIAL;
+ if (!strcmp(p, "nullconsole"))
+ howto |= RB_MUTE;
+ }
+
+ return(howto);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+static vm_offset_t
+md_copyenv(vm_offset_t addr)
+{
+ struct env_var *ep;
+
+ /* traverse the environment */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ archsw.arch_copyin(ep->ev_name, addr, strlen(ep->ev_name));
+ addr += strlen(ep->ev_name);
+ archsw.arch_copyin("=", addr, 1);
+ addr++;
+ if (ep->ev_value != NULL) {
+ archsw.arch_copyin(ep->ev_value, addr,
+ strlen(ep->ev_value));
+ addr += strlen(ep->ev_value);
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ }
+ archsw.arch_copyin("", addr, 1);
+ addr++;
+ return(addr);
+}
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ archsw.arch_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c) \
+ if (c) \
+ archsw.arch_copyin(s, a, strlen(s) + 1);\
+ a += roundup(strlen(s) + 1, sizeof(u_long));\
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ archsw.arch_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(u_long)); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c);\
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ archsw.arch_copyin(mm->md_data, a, mm->md_size);\
+ a += roundup(mm->md_size, sizeof(u_long)); \
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+static vm_offset_t
+md_copymodules(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ int c;
+ vm_offset_t a;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must be first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ a = fp->f_addr - __elfN(relocation_offset);
+ MOD_ADDR(addr, a, c);
+ MOD_SIZE(addr, fp->f_size, c);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next) {
+ if (!(md->md_type & MODINFOMD_NOCOPY))
+ MOD_METADATA(addr, md, c);
+ }
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Load the information expected by a kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+md_load(char *args, vm_offset_t *modulep)
+{
+ struct preloaded_file *kfp, *bfp;
+ struct preloaded_file *xp;
+ struct file_metadata *md;
+ struct bootinfo *bip;
+ vm_offset_t kernend;
+ vm_offset_t addr;
+ vm_offset_t envp;
+ vm_offset_t size;
+ vm_offset_t vaddr;
+#if defined(LOADER_FDT_SUPPORT)
+ vm_offset_t dtbp;
+ int dtb_size;
+#endif
+ char *rootdevname;
+ int howto;
+ int i;
+
+ /*
+ * These metadata addreses must be converted for kernel after
+ * relocation.
+ */
+ uint32_t mdt[] = {
+ MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND,
+ MODINFOMD_ENVP,
+#if defined(LOADER_FDT_SUPPORT)
+ MODINFOMD_DTBP
+#endif
+ };
+
+ howto = md_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied
+ * device. This should perhaps go to MI code and/or have $rootdev
+ * tested/set by MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ if (rootdevname == NULL)
+ rootdevname = getenv("currdev");
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(rootdevname);
+
+ /* Find the last module in the chain */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ /* Pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* Copy our environment */
+ envp = addr;
+ addr = md_copyenv(addr);
+
+ /* Pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+#if defined(LOADER_FDT_SUPPORT)
+ /* Handle device tree blob */
+ dtbp = addr;
+ dtb_size = fdt_copy(addr);
+
+ /* Pad to a page boundary */
+ if (dtb_size)
+ addr += roundup(dtb_size, PAGE_SIZE);
+#endif
+
+ kernend = 0;
+ kfp = file_findfile(NULL, "elf32 kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+
+#if defined(LOADER_FDT_SUPPORT)
+ if (dtb_size)
+ file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp);
+ else
+ pager_output("WARNING! Trying to fire up the kernel, but no "
+ "device tree blob found!\n");
+#endif
+
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+
+ /* Figure out the size and location of the metadata */
+ *modulep = addr;
+ size = md_copymodules(0);
+ kernend = roundup(addr + size, PAGE_SIZE);
+
+ /* Provide MODINFOMD_KERNEND */
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ bcopy(&kernend, md->md_data, sizeof kernend);
+
+ /* Convert addresses to the final VA */
+ *modulep -= __elfN(relocation_offset);
+
+ /* Do relocation fixup on metadata of each module. */
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ for (i = 0; i < nitems(mdt); i++) {
+ md = file_findmetadata(xp, mdt[i]);
+ if (md) {
+ bcopy(md->md_data, &vaddr, sizeof vaddr);
+ vaddr -= __elfN(relocation_offset);
+ bcopy(&vaddr, md->md_data, sizeof vaddr);
+ }
+ }
+ }
+
+ /* Only now copy actual modules and metadata */
+ (void)md_copymodules(addr);
+
+ return (0);
+}
diff --git a/stand/uboot/fdt/Makefile b/stand/uboot/fdt/Makefile
new file mode 100644
index 0000000..4bc38ec
--- /dev/null
+++ b/stand/uboot/fdt/Makefile
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+.PATH: ${LDRSRC}
+
+LIB= uboot_fdt
+INTERNALLIB=
+WARNS?= 2
+
+SRCS= uboot_fdt.c
+
+CFLAGS+= -ffreestanding -msoft-float
+
+# U-Boot library headers
+CFLAGS+= -I${UBOOTSRC}/lib
+
+# libfdt headers
+CFLAGS+= -I${FDTSRC}
+
+# Pick up the bootstrap header for some interface items
+CFLAGS+= -I${LDRSRC}
+
+.include <bsd.stand.mk>
+.include <bsd.lib.mk>
diff --git a/stand/uboot/fdt/uboot_fdt.c b/stand/uboot/fdt/uboot_fdt.c
new file mode 100644
index 0000000..aed7885
--- /dev/null
+++ b/stand/uboot/fdt/uboot_fdt.c
@@ -0,0 +1,191 @@
+/*-
+ * Copyright (c) 2009-2010 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <stand.h>
+#include <fdt_platform.h>
+
+#include "glue.h"
+
+#define STR(number) #number
+#define STRINGIFY(number) STR(number)
+
+int
+fdt_platform_load_dtb(void)
+{
+ struct fdt_header *hdr;
+ const char *s;
+ char *p;
+ int rv;
+
+ /*
+ * If the U-boot environment contains a variable giving the address of a
+ * valid blob in memory, use it. The U-boot README says the right
+ * variable for fdt data loaded into ram is fdt_addr_r, so try that
+ * first. Board vendors also use both fdtaddr and fdt_addr names.
+ */
+ s = ub_env_get("fdt_addr_r");
+ if (s == NULL)
+ s = ub_env_get("fdtaddr");
+ if (s == NULL)
+ s = ub_env_get("fdt_addr");
+ if (s != NULL && *s != '\0') {
+ hdr = (struct fdt_header *)strtoul(s, &p, 16);
+ if (*p == '\0') {
+ if (fdt_load_dtb_addr(hdr) == 0) {
+ printf("Using DTB provided by U-Boot at "
+ "address %p.\n", hdr);
+ rv = 0;
+ goto exit;
+ }
+ }
+ }
+
+ rv = 1;
+
+ /*
+ * Try to get FDT filename first from loader env and then from u-boot env
+ */
+ s = getenv("fdt_file");
+ if (s == NULL)
+ s = ub_env_get("fdtfile");
+ if (s == NULL)
+ s = ub_env_get("fdt_file");
+ if (s != NULL && *s != '\0') {
+ if (fdt_load_dtb_file(s) == 0) {
+ printf("Loaded DTB from file '%s'.\n", s);
+ rv = 0;
+ goto exit;
+ }
+ }
+
+exit:
+ if (rv == 0)
+ fdt_load_dtb_overlays(ub_env_get("fdt_overlays"));
+ return (rv);
+}
+
+void
+fdt_platform_fixups(void)
+{
+ static struct fdt_mem_region regions[UB_MAX_MR];
+ const char *env, *str;
+ char *end, *ethstr;
+ int eth_no, i, len, n;
+ struct sys_info *si;
+
+ env = NULL;
+ eth_no = 0;
+ ethstr = NULL;
+
+ /* Apply overlays before anything else */
+ fdt_apply_overlays();
+
+ /* Acquire sys_info */
+ si = ub_get_sys_info();
+
+ while ((env = ub_env_enum(env)) != NULL) {
+ if (strncmp(env, "eth", 3) == 0 &&
+ strncmp(env + (strlen(env) - 4), "addr", 4) == 0) {
+ /*
+ * Handle Ethernet addrs: parse uboot env eth%daddr
+ */
+
+ if (!eth_no) {
+ /*
+ * Check how many chars we will need to store
+ * maximal eth iface number.
+ */
+ len = strlen(STRINGIFY(TMP_MAX_ETH)) +
+ strlen("ethernet") + 1;
+
+ /*
+ * Reserve mem for string "ethernet" and len
+ * chars for iface no.
+ */
+ ethstr = (char *)malloc(len * sizeof(char));
+ bzero(ethstr, len * sizeof(char));
+ strcpy(ethstr, "ethernet0");
+ }
+
+ /* Extract interface number */
+ i = strtol(env + 3, &end, 10);
+ if (end == (env + 3))
+ /* 'ethaddr' means interface 0 address */
+ n = 0;
+ else
+ n = i;
+
+ if (n > TMP_MAX_ETH)
+ continue;
+
+ str = ub_env_get(env);
+
+ if (n != 0) {
+ /*
+ * Find the length of the interface id by
+ * taking in to account the first 3 and
+ * last 4 characters.
+ */
+ i = strlen(env) - 7;
+ strncpy(ethstr + 8, env + 3, i);
+ }
+
+ /* Modify blob */
+ fdt_fixup_ethernet(str, ethstr, len);
+
+ /* Clear ethernet..XXXX.. string */
+ bzero(ethstr + 8, len - 8);
+
+ if (n + 1 > eth_no)
+ eth_no = n + 1;
+ } else if (strcmp(env, "consoledev") == 0) {
+ str = ub_env_get(env);
+ fdt_fixup_stdout(str);
+ }
+ }
+
+ /* Modify cpu(s) and bus clock frequenties in /cpus node [Hz] */
+ fdt_fixup_cpubusfreqs(si->clk_cpu, si->clk_bus);
+
+ /* Extract the DRAM regions into fdt_mem_region format. */
+ for (i = 0, n = 0; i < si->mr_no && n < nitems(regions); i++) {
+ if (si->mr[i].flags == MR_ATTR_DRAM) {
+ regions[n].start = si->mr[i].start;
+ regions[n].size = si->mr[i].size;
+ n++;
+ }
+ }
+
+ /* Fixup memory regions */
+ fdt_fixup_memory(regions, n);
+}
diff --git a/stand/uboot/lib/Makefile b/stand/uboot/lib/Makefile
new file mode 100644
index 0000000..d12d48c
--- /dev/null
+++ b/stand/uboot/lib/Makefile
@@ -0,0 +1,31 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+.PATH: ${LDRSRC}
+
+LIB= uboot
+INTERNALLIB=
+WARNS?= 2
+
+SRCS= console.c copy.c devicename.c elf_freebsd.c glue.c
+SRCS+= module.c net.c reboot.c time.c
+
+CFLAGS+= -ffreestanding -msoft-float
+
+.if ${LOADER_DISK_SUPPORT:Uyes} == "yes"
+SRCS+= disk.c
+.endif
+
+.include "${BOOTSRC}/fdt.mk"
+
+# Pick up the bootstrap header for some interface items
+CFLAGS+= -I${LDRSRC}
+
+.ifdef(BOOT_DISK_DEBUG)
+# Make the disk code more talkative
+CFLAGS+= -DDISK_DEBUG
+.endif
+
+.include <bsd.stand.mk>
+.include <bsd.lib.mk>
diff --git a/stand/uboot/lib/api_public.h b/stand/uboot/lib/api_public.h
new file mode 100644
index 0000000..f393de8
--- /dev/null
+++ b/stand/uboot/lib/api_public.h
@@ -0,0 +1,160 @@
+/*
+ * (C) Copyright 2007-2008 Semihalf
+ *
+ * Written by: Rafal Jaworowski <raj@semihalf.com>
+ *
+ * This file is dual licensed; you can use it under the terms of
+ * either the GPL, or the BSD license, at your option.
+ *
+ * I. GPL:
+ *
+ * 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.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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
+ *
+ * Alternatively,
+ *
+ * II. BSD license:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * This file needs to be kept in sync with U-Boot reference:
+ * http://www.denx.de/cgi-bin/gitweb.cgi?p=u-boot.git;a=blob;f=include/api_public.h
+ */
+
+#ifndef _API_PUBLIC_H_
+#define _API_PUBLIC_H_
+
+#define API_EINVAL 1 /* invalid argument(s) */
+#define API_ENODEV 2 /* no device */
+#define API_ENOMEM 3 /* no memory */
+#define API_EBUSY 4 /* busy, occupied etc. */
+#define API_EIO 5 /* I/O error */
+#define API_ESYSC 6 /* syscall error */
+
+typedef int (*scp_t)(int, int *, ...);
+
+#define API_SIG_VERSION 1
+#define API_SIG_MAGIC "UBootAPI"
+#define API_SIG_MAGLEN 8
+
+struct api_signature {
+ char magic[API_SIG_MAGLEN]; /* magic string */
+ uint16_t version; /* API version */
+ uint32_t checksum; /* checksum of this sig struct */
+ scp_t syscall; /* entry point to the API */
+};
+
+enum {
+ API_RSVD = 0,
+ API_GETC,
+ API_PUTC,
+ API_TSTC,
+ API_PUTS,
+ API_RESET,
+ API_GET_SYS_INFO,
+ API_UDELAY,
+ API_GET_TIMER,
+ API_DEV_ENUM,
+ API_DEV_OPEN,
+ API_DEV_CLOSE,
+ API_DEV_READ,
+ API_DEV_WRITE,
+ API_ENV_ENUM,
+ API_ENV_GET,
+ API_ENV_SET,
+ API_MAXCALL
+};
+
+#define MR_ATTR_FLASH 0x0001
+#define MR_ATTR_DRAM 0x0002
+#define MR_ATTR_SRAM 0x0003
+
+struct mem_region {
+ unsigned long start;
+ unsigned long size;
+ int flags;
+};
+
+struct sys_info {
+ unsigned long clk_bus;
+ unsigned long clk_cpu;
+ unsigned long bar;
+ struct mem_region *mr;
+ int mr_no; /* number of memory regions */
+};
+
+#undef CFG_64BIT_LBA
+#ifdef CFG_64BIT_LBA
+typedef uint64_t lbasize_t;
+#else
+typedef unsigned long lbasize_t;
+#endif
+typedef unsigned long lbastart_t;
+
+#define DEV_TYP_NONE 0x0000
+#define DEV_TYP_NET 0x0001
+
+#define DEV_TYP_STOR 0x0002
+#define DT_STOR_IDE 0x0010
+#define DT_STOR_SCSI 0x0020
+#define DT_STOR_USB 0x0040
+#define DT_STOR_MMC 0x0080
+#define DT_STOR_SATA 0x0100
+
+#define DEV_STA_CLOSED 0x0000 /* invalid, closed */
+#define DEV_STA_OPEN 0x0001 /* open i.e. active */
+
+struct device_info {
+ int type;
+ void *cookie;
+
+ union {
+ struct {
+ lbasize_t block_count; /* no of blocks */
+ unsigned long block_size; /* size of one block */
+ } storage;
+
+ struct {
+ unsigned char hwaddr[6];
+ } net;
+ } info;
+#define di_stor info.storage
+#define di_net info.net
+
+ int state;
+};
+
+#endif /* _API_PUBLIC_H_ */
diff --git a/stand/uboot/lib/console.c b/stand/uboot/lib/console.c
new file mode 100644
index 0000000..f49f455c
--- /dev/null
+++ b/stand/uboot/lib/console.c
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "bootstrap.h"
+#include "glue.h"
+
+int console;
+
+static void uboot_cons_probe(struct console *cp);
+static int uboot_cons_init(int);
+static void uboot_cons_putchar(int);
+static int uboot_cons_getchar(void);
+static int uboot_cons_poll(void);
+
+struct console uboot_console = {
+ "uboot",
+ "U-Boot console",
+ 0,
+ uboot_cons_probe,
+ uboot_cons_init,
+ uboot_cons_putchar,
+ uboot_cons_getchar,
+ uboot_cons_poll,
+};
+
+static void
+uboot_cons_probe(struct console *cp)
+{
+
+ cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+}
+
+static int
+uboot_cons_init(int arg)
+{
+
+ return (0);
+}
+
+static void
+uboot_cons_putchar(int c)
+{
+
+ if (c == '\n')
+ ub_putc('\r');
+
+ ub_putc(c);
+}
+
+static int
+uboot_cons_getchar()
+{
+
+ return (ub_getc());
+}
+
+static int
+uboot_cons_poll()
+{
+
+ return (ub_tstc());
+}
diff --git a/stand/uboot/lib/copy.c b/stand/uboot/lib/copy.c
new file mode 100644
index 0000000..131b88d
--- /dev/null
+++ b/stand/uboot/lib/copy.c
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+
+#include <stand.h>
+#include <stdint.h>
+
+#include "api_public.h"
+#include "glue.h"
+#include "libuboot.h"
+
+/*
+ * MD primitives supporting placement of module data
+ */
+
+#ifdef __arm__
+#define KERN_ALIGN (2 * 1024 * 1024)
+#else
+#define KERN_ALIGN PAGE_SIZE
+#endif
+
+/*
+ * Avoid low memory, u-boot puts things like args and dtb blobs there.
+ */
+#define KERN_MINADDR max(KERN_ALIGN, (1024 * 1024))
+
+extern void _start(void); /* ubldr entry point address. */
+
+/*
+ * This is called for every object loaded (kernel, module, dtb file, etc). The
+ * expected return value is the next address at or after the given addr which is
+ * appropriate for loading the given object described by type and data. On each
+ * call the addr is the next address following the previously loaded object.
+ *
+ * The first call is for loading the kernel, and the addr argument will be zero,
+ * and we search for a big block of ram to load the kernel and modules.
+ *
+ * On subsequent calls the addr will be non-zero, and we just round it up so
+ * that each object begins on a page boundary.
+ */
+uint64_t
+uboot_loadaddr(u_int type, void *data, uint64_t addr)
+{
+ struct sys_info *si;
+ uint64_t sblock, eblock, subldr, eubldr;
+ uint64_t biggest_block, this_block;
+ uint64_t biggest_size, this_size;
+ int i;
+ char *envstr;
+
+ if (addr == 0) {
+ /*
+ * If the loader_kernaddr environment variable is set, blindly
+ * honor it. It had better be right. We force interpretation
+ * of the value in base-16 regardless of any leading 0x prefix,
+ * because that's the U-Boot convention.
+ */
+ envstr = ub_env_get("loader_kernaddr");
+ if (envstr != NULL)
+ return (strtoul(envstr, NULL, 16));
+
+ /*
+ * Find addr/size of largest DRAM block. Carve our own address
+ * range out of the block, because loading the kernel over the
+ * top ourself is a poor memory-conservation strategy. Avoid
+ * memory at beginning of the first block of physical ram,
+ * since u-boot likes to pass args and data there. Assume that
+ * u-boot has moved itself to the very top of ram and
+ * optimistically assume that we won't run into it up there.
+ */
+ if ((si = ub_get_sys_info()) == NULL)
+ panic("could not retrieve system info");
+
+ biggest_block = 0;
+ biggest_size = 0;
+ subldr = rounddown2((uintptr_t)_start, KERN_ALIGN);
+ eubldr = roundup2((uint64_t)uboot_heap_end, KERN_ALIGN);
+ for (i = 0; i < si->mr_no; i++) {
+ if (si->mr[i].flags != MR_ATTR_DRAM)
+ continue;
+ sblock = roundup2((uint64_t)si->mr[i].start,
+ KERN_ALIGN);
+ eblock = rounddown2((uint64_t)si->mr[i].start +
+ si->mr[i].size, KERN_ALIGN);
+ if (biggest_size == 0)
+ sblock += KERN_MINADDR;
+ if (subldr >= sblock && subldr < eblock) {
+ if (subldr - sblock > eblock - eubldr) {
+ this_block = sblock;
+ this_size = subldr - sblock;
+ } else {
+ this_block = eubldr;
+ this_size = eblock - eubldr;
+ }
+ } else if (subldr < sblock && eubldr < eblock) {
+ /* Loader is below or engulfs the sblock */
+ this_block = (eubldr < sblock) ? sblock : eubldr;
+ this_size = eblock - this_block;
+ } else {
+ this_block = 0;
+ this_size = 0;
+ }
+ if (biggest_size < this_size) {
+ biggest_block = this_block;
+ biggest_size = this_size;
+ }
+ }
+ if (biggest_size == 0)
+ panic("Not enough DRAM to load kernel\n");
+#if 0
+ printf("Loading kernel into region 0x%08jx-0x%08jx (%ju MiB)\n",
+ (uintmax_t)biggest_block,
+ (uintmax_t)biggest_block + biggest_size - 1,
+ (uintmax_t)biggest_size / 1024 / 1024);
+#endif
+ return (biggest_block);
+ }
+ return roundup2(addr, PAGE_SIZE);
+}
+
+ssize_t
+uboot_copyin(const void *src, vm_offset_t dest, const size_t len)
+{
+ bcopy(src, (void *)dest, len);
+ return (len);
+}
+
+ssize_t
+uboot_copyout(const vm_offset_t src, void *dest, const size_t len)
+{
+ bcopy((void *)src, dest, len);
+ return (len);
+}
+
+ssize_t
+uboot_readin(const int fd, vm_offset_t dest, const size_t len)
+{
+ return (read(fd, (void *)dest, len));
+}
diff --git a/stand/uboot/lib/devicename.c b/stand/uboot/lib/devicename.c
new file mode 100644
index 0000000..9e68f9d
--- /dev/null
+++ b/stand/uboot/lib/devicename.c
@@ -0,0 +1,201 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+#include "disk.h"
+#include "libuboot.h"
+
+static int uboot_parsedev(struct uboot_devdesc **dev, const char *devspec,
+ const char **path);
+
+/*
+ * Point (dev) at an allocated device specifier for the device matching the
+ * path in (devspec). If it contains an explicit device specification,
+ * use that. If not, use the default device.
+ */
+int
+uboot_getdev(void **vdev, const char *devspec, const char **path)
+{
+ struct uboot_devdesc **dev = (struct uboot_devdesc **)vdev;
+ int rv;
+
+ /*
+ * If it looks like this is just a path and no
+ * device, go with the current device.
+ */
+ if ((devspec == NULL) || (devspec[0] == '/') ||
+ (strchr(devspec, ':') == NULL)) {
+
+ if (((rv = uboot_parsedev(dev, getenv("currdev"), NULL)) == 0)
+ && (path != NULL))
+ *path = devspec;
+ return(rv);
+ }
+
+ /*
+ * Try to parse the device name off the beginning of the devspec.
+ */
+ return (uboot_parsedev(dev, devspec, path));
+}
+
+/*
+ * Point (dev) at an allocated device specifier matching the string version
+ * at the beginning of (devspec). Return a pointer to the remaining
+ * text in (path).
+ *
+ * In all cases, the beginning of (devspec) is compared to the names
+ * of known devices in the device switch, and then any following text
+ * is parsed according to the rules applied to the device type.
+ *
+ * For disk-type devices, the syntax is:
+ *
+ * disk<unit>[<partition>]:
+ *
+ */
+static int
+uboot_parsedev(struct uboot_devdesc **dev, const char *devspec,
+ const char **path)
+{
+ struct uboot_devdesc *idev;
+ struct devsw *dv;
+ char *cp;
+ const char *np;
+ int i, unit, err;
+
+ /* minimum length check */
+ if (strlen(devspec) < 2)
+ return(EINVAL);
+
+ /* look for a device that matches */
+ for (i = 0, dv = NULL; devsw[i] != NULL; i++) {
+ if (!strncmp(devspec, devsw[i]->dv_name,
+ strlen(devsw[i]->dv_name))) {
+ dv = devsw[i];
+ break;
+ }
+ }
+ if (dv == NULL)
+ return(ENOENT);
+ idev = malloc(sizeof(struct uboot_devdesc));
+ err = 0;
+ np = (devspec + strlen(dv->dv_name));
+
+ switch(dv->dv_type) {
+ case DEVT_NONE:
+ break;
+
+#ifdef LOADER_DISK_SUPPORT
+ case DEVT_DISK:
+ err = disk_parsedev((struct disk_devdesc *)idev, np, path);
+ if (err != 0)
+ goto fail;
+ break;
+#endif
+
+ case DEVT_NET:
+ unit = 0;
+
+ if (*np && (*np != ':')) {
+ /* get unit number if present */
+ unit = strtol(np, &cp, 0);
+ if (cp == np) {
+ err = EUNIT;
+ goto fail;
+ }
+ }
+ if (*cp && (*cp != ':')) {
+ err = EINVAL;
+ goto fail;
+ }
+ idev->d_unit = unit;
+
+ if (path != NULL)
+ *path = (*cp == 0) ? cp : cp + 1;
+ break;
+
+ default:
+ err = EINVAL;
+ goto fail;
+ }
+ idev->d_dev = dv;
+ idev->d_type = dv->dv_type;
+ if (dev == NULL) {
+ free(idev);
+ } else {
+ *dev = idev;
+ }
+ return (0);
+
+fail:
+ free(idev);
+ return (err);
+}
+
+
+char *
+uboot_fmtdev(void *vdev)
+{
+ struct uboot_devdesc *dev = (struct uboot_devdesc *)vdev;
+ static char buf[128];
+
+ switch(dev->d_type) {
+ case DEVT_NONE:
+ strcpy(buf, "(no device)");
+ break;
+
+ case DEVT_DISK:
+#ifdef LOADER_DISK_SUPPORT
+ return (disk_fmtdev(vdev));
+#endif
+
+ case DEVT_NET:
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
+ break;
+ }
+ return(buf);
+}
+
+/*
+ * Set currdev to suit the value being supplied in (value).
+ */
+int
+uboot_setcurrdev(struct env_var *ev, int flags, const void *value)
+{
+ struct uboot_devdesc *ncurr;
+ int rv;
+
+ if ((rv = uboot_parsedev(&ncurr, value, NULL)) != 0)
+ return (rv);
+ free(ncurr);
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return (0);
+}
diff --git a/stand/uboot/lib/disk.c b/stand/uboot/lib/disk.c
new file mode 100644
index 0000000..f5e44c7
--- /dev/null
+++ b/stand/uboot/lib/disk.c
@@ -0,0 +1,320 @@
+/*-
+ * Copyright (c) 2008 Semihalf, Rafal Jaworowski
+ * Copyright (c) 2009 Semihalf, Piotr Ziecik
+ * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
+ * 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.
+ *
+ */
+
+/*
+ * Block storage I/O routines for U-Boot
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/disk.h>
+#include <machine/stdarg.h>
+#include <stand.h>
+
+#include "api_public.h"
+#include "bootstrap.h"
+#include "disk.h"
+#include "glue.h"
+#include "libuboot.h"
+
+#define stor_printf(fmt, args...) do { \
+ printf("%s%d: ", dev->d_dev->dv_name, dev->d_unit); \
+ printf(fmt, ##args); \
+} while (0)
+
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf("%s(): ", __func__); \
+ printf(fmt,##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+static struct {
+ int opened; /* device is opened */
+ int handle; /* storage device handle */
+ int type; /* storage type */
+ off_t blocks; /* block count */
+ u_int bsize; /* block size */
+} stor_info[UB_MAX_DEV];
+
+#define SI(dev) (stor_info[(dev)->d_unit])
+
+static int stor_info_no = 0;
+static int stor_opendev(struct disk_devdesc *);
+static int stor_readdev(struct disk_devdesc *, daddr_t, size_t, char *);
+
+/* devsw I/F */
+static int stor_init(void);
+static int stor_strategy(void *, int, daddr_t, size_t, char *, size_t *);
+static int stor_open(struct open_file *, ...);
+static int stor_close(struct open_file *);
+static int stor_ioctl(struct open_file *f, u_long cmd, void *data);
+static int stor_print(int);
+static void stor_cleanup(void);
+
+struct devsw uboot_storage = {
+ "disk",
+ DEVT_DISK,
+ stor_init,
+ stor_strategy,
+ stor_open,
+ stor_close,
+ stor_ioctl,
+ stor_print,
+ stor_cleanup
+};
+
+static int
+stor_init(void)
+{
+ struct device_info *di;
+ int i;
+
+ if (devs_no == 0) {
+ printf("No U-Boot devices! Really enumerated?\n");
+ return (-1);
+ }
+
+ for (i = 0; i < devs_no; i++) {
+ di = ub_dev_get(i);
+ if ((di != NULL) && (di->type & DEV_TYP_STOR)) {
+ if (stor_info_no >= UB_MAX_DEV) {
+ printf("Too many storage devices: %d\n",
+ stor_info_no);
+ return (-1);
+ }
+ stor_info[stor_info_no].handle = i;
+ stor_info[stor_info_no].opened = 0;
+ stor_info[stor_info_no].type = di->type;
+ stor_info[stor_info_no].blocks =
+ di->di_stor.block_count;
+ stor_info[stor_info_no].bsize =
+ di->di_stor.block_size;
+ stor_info_no++;
+ }
+ }
+
+ if (!stor_info_no) {
+ debugf("No storage devices\n");
+ return (-1);
+ }
+
+ debugf("storage devices found: %d\n", stor_info_no);
+ return (0);
+}
+
+static void
+stor_cleanup(void)
+{
+ int i;
+
+ for (i = 0; i < stor_info_no; i++)
+ if (stor_info[i].opened > 0)
+ ub_dev_close(stor_info[i].handle);
+}
+
+static int
+stor_strategy(void *devdata, int rw, daddr_t blk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
+ daddr_t bcount;
+ int err;
+
+ rw &= F_MASK;
+ if (rw != F_READ) {
+ stor_printf("write attempt, operation not supported!\n");
+ return (EROFS);
+ }
+
+ if (size % SI(dev).bsize) {
+ stor_printf("size=%zu not multiple of device "
+ "block size=%d\n",
+ size, SI(dev).bsize);
+ return (EIO);
+ }
+ bcount = size / SI(dev).bsize;
+ if (rsize)
+ *rsize = 0;
+
+ err = stor_readdev(dev, blk + dev->d_offset, bcount, buf);
+ if (!err && rsize)
+ *rsize = size;
+
+ return (err);
+}
+
+static int
+stor_open(struct open_file *f, ...)
+{
+ va_list ap;
+ struct disk_devdesc *dev;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct disk_devdesc *);
+ va_end(ap);
+
+ return (stor_opendev(dev));
+}
+
+static int
+stor_opendev(struct disk_devdesc *dev)
+{
+ int err;
+
+ if (dev->d_unit < 0 || dev->d_unit >= stor_info_no)
+ return (EIO);
+
+ if (SI(dev).opened == 0) {
+ err = ub_dev_open(SI(dev).handle);
+ if (err != 0) {
+ stor_printf("device open failed with error=%d, "
+ "handle=%d\n", err, SI(dev).handle);
+ return (ENXIO);
+ }
+ SI(dev).opened++;
+ }
+ return (disk_open(dev, SI(dev).blocks * SI(dev).bsize,
+ SI(dev).bsize));
+}
+
+static int
+stor_close(struct open_file *f)
+{
+ struct disk_devdesc *dev;
+
+ dev = (struct disk_devdesc *)(f->f_devdata);
+ return (disk_close(dev));
+}
+
+static int
+stor_readdev(struct disk_devdesc *dev, daddr_t blk, size_t size, char *buf)
+{
+ lbasize_t real_size;
+ int err;
+
+ debugf("reading blk=%d size=%d @ 0x%08x\n", (int)blk, size, (uint32_t)buf);
+
+ err = ub_dev_read(SI(dev).handle, buf, size, blk, &real_size);
+ if (err != 0) {
+ stor_printf("read failed, error=%d\n", err);
+ return (EIO);
+ }
+
+ if (real_size != size) {
+ stor_printf("real size != size\n");
+ err = EIO;
+ }
+
+ return (err);
+}
+
+static int
+stor_print(int verbose)
+{
+ struct disk_devdesc dev;
+ static char line[80];
+ int i, ret = 0;
+
+ if (stor_info_no == 0)
+ return (ret);
+
+ printf("%s devices:", uboot_storage.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ for (i = 0; i < stor_info_no; i++) {
+ dev.d_dev = &uboot_storage;
+ dev.d_unit = i;
+ dev.d_slice = -1;
+ dev.d_partition = -1;
+ snprintf(line, sizeof(line), "\tdisk%d (%s)\n", i,
+ ub_stor_type(SI(&dev).type));
+ if ((ret = pager_output(line)) != 0)
+ break;
+ if (stor_opendev(&dev) == 0) {
+ sprintf(line, "\tdisk%d", i);
+ ret = disk_print(&dev, line, verbose);
+ disk_close(&dev);
+ if (ret != 0)
+ break;
+ }
+ }
+ return (ret);
+}
+
+static int
+stor_ioctl(struct open_file *f, u_long cmd, void *data)
+{
+ struct disk_devdesc *dev;
+ int rc;
+
+ dev = (struct disk_devdesc *)f->f_devdata;
+ rc = disk_ioctl(dev, cmd, data);
+ if (rc != ENOTTY)
+ return (rc);
+
+ switch (cmd) {
+ case DIOCGSECTORSIZE:
+ *(u_int *)data = SI(dev).bsize;
+ break;
+ case DIOCGMEDIASIZE:
+ *(uint64_t *)data = SI(dev).bsize * SI(dev).blocks;
+ break;
+ default:
+ return (ENOTTY);
+ }
+ return (0);
+}
+
+
+/*
+ * Return the device unit number for the given type and type-relative unit
+ * number.
+ */
+int
+uboot_diskgetunit(int type, int type_unit)
+{
+ int local_type_unit;
+ int i;
+
+ local_type_unit = 0;
+ for (i = 0; i < stor_info_no; i++) {
+ if ((stor_info[i].type & type) == type) {
+ if (local_type_unit == type_unit) {
+ return (i);
+ }
+ local_type_unit++;
+ }
+ }
+
+ return (-1);
+}
diff --git a/stand/uboot/lib/elf_freebsd.c b/stand/uboot/lib/elf_freebsd.c
new file mode 100644
index 0000000..a65fa26
--- /dev/null
+++ b/stand/uboot/lib/elf_freebsd.c
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 2001 Benno Rice <benno@FreeBSD.org>
+ * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/linker.h>
+
+#ifdef __mips__
+#include <sys/proc.h>
+#include <machine/frame.h>
+#endif
+#include <machine/md_var.h>
+#include <machine/metadata.h>
+#include <machine/elf.h>
+
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "libuboot.h"
+
+extern vm_offset_t md_load(char *, vm_offset_t *);
+
+int
+__elfN(uboot_load)(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ int r;
+
+ r = __elfN(loadfile)(filename, dest, result);
+ if (r != 0)
+ return (r);
+
+#if defined(__powerpc__)
+ /*
+ * No need to sync the icache for modules: this will
+ * be done by the kernel after relocation.
+ */
+ if (!strcmp((*result)->f_type, "elf kernel"))
+ __syncicache((void *) (*result)->f_addr, (*result)->f_size);
+#endif
+ return (0);
+}
+
+int
+__elfN(uboot_exec)(struct preloaded_file *fp)
+{
+ struct file_metadata *fmp;
+ vm_offset_t mdp;
+ Elf_Ehdr *e;
+ int error;
+ void (*entry)(void *);
+
+ if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return (EFTYPE);
+
+ e = (Elf_Ehdr *)&fmp->md_data;
+
+ if ((error = md_load(fp->f_args, &mdp)) != 0)
+ return (error);
+
+ entry = (void *)e->e_entry;
+ printf("Kernel entry at %p...\n", entry);
+
+ dev_cleanup();
+ printf("Kernel args: %s\n", fp->f_args);
+
+ (*entry)((void *)mdp);
+ panic("exec returned");
+}
+
+struct file_format uboot_elf = {
+ __elfN(uboot_load),
+ __elfN(uboot_exec)
+};
diff --git a/stand/uboot/lib/glue.c b/stand/uboot/lib/glue.c
new file mode 100644
index 0000000..312075a
--- /dev/null
+++ b/stand/uboot/lib/glue.c
@@ -0,0 +1,564 @@
+/*-
+ * Copyright (c) 2007-2008 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <crc32.h>
+#include <stand.h>
+#include "api_public.h"
+#include "glue.h"
+
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+/* Some random address used by U-Boot. */
+extern long uboot_address;
+
+static int
+valid_sig(struct api_signature *sig)
+{
+ uint32_t checksum;
+ struct api_signature s;
+
+ if (sig == NULL)
+ return (0);
+ /*
+ * Clear the checksum field (in the local copy) so as to calculate the
+ * CRC with the same initial contents as at the time when the sig was
+ * produced
+ */
+ s = *sig;
+ s.checksum = 0;
+
+ checksum = crc32((void *)&s, sizeof(struct api_signature));
+
+ if (checksum != sig->checksum)
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Checks to see if API signature's address was given to us as a command line
+ * argument by U-Boot.
+ *
+ * returns 1/0 depending on found/not found result
+ */
+int
+api_parse_cmdline_sig(int argc, char **argv, struct api_signature **sig)
+{
+ unsigned long api_address;
+ int c;
+
+ api_address = 0;
+ opterr = 0;
+ optreset = 1;
+ optind = 1;
+
+ while ((c = getopt (argc, argv, "a:")) != -1)
+ switch (c) {
+ case 'a':
+ api_address = strtoul(optarg, NULL, 16);
+ break;
+ default:
+ break;
+ }
+
+ if (api_address != 0) {
+ *sig = (struct api_signature *)api_address;
+ if (valid_sig(*sig))
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Searches for the U-Boot API signature
+ *
+ * returns 1/0 depending on found/not found result
+ */
+int
+api_search_sig(struct api_signature **sig)
+{
+ unsigned char *sp, *spend;
+
+ if (sig == NULL)
+ return (0);
+
+ if (uboot_address == 0)
+ uboot_address = 255 * 1024 * 1024;
+
+ sp = (void *)(uboot_address & API_SIG_SEARCH_MASK);
+ spend = sp + API_SIG_SEARCH_LEN - API_SIG_MAGLEN;
+
+ while (sp < spend) {
+ if (!bcmp(sp, API_SIG_MAGIC, API_SIG_MAGLEN)) {
+ *sig = (struct api_signature *)sp;
+ if (valid_sig(*sig))
+ return (1);
+ }
+ sp += API_SIG_MAGLEN;
+ }
+
+ *sig = NULL;
+ return (0);
+}
+
+/****************************************
+ *
+ * console
+ *
+ ****************************************/
+
+int
+ub_getc(void)
+{
+ int c;
+
+ if (!syscall(API_GETC, NULL, &c))
+ return (-1);
+
+ return (c);
+}
+
+int
+ub_tstc(void)
+{
+ int t;
+
+ if (!syscall(API_TSTC, NULL, &t))
+ return (-1);
+
+ return (t);
+}
+
+void
+ub_putc(const char c)
+{
+
+ syscall(API_PUTC, NULL, &c);
+}
+
+void
+ub_puts(const char *s)
+{
+
+ syscall(API_PUTS, NULL, s);
+}
+
+/****************************************
+ *
+ * system
+ *
+ ****************************************/
+
+void
+ub_reset(void)
+{
+
+ syscall(API_RESET, NULL);
+}
+
+static struct mem_region mr[UB_MAX_MR];
+static struct sys_info si;
+
+struct sys_info *
+ub_get_sys_info(void)
+{
+ int err = 0;
+
+ memset(&si, 0, sizeof(struct sys_info));
+ si.mr = mr;
+ si.mr_no = UB_MAX_MR;
+ memset(&mr, 0, sizeof(mr));
+
+ if (!syscall(API_GET_SYS_INFO, &err, &si))
+ return (NULL);
+
+ return ((err) ? NULL : &si);
+}
+
+/****************************************
+ *
+ * timing
+ *
+ ****************************************/
+
+void
+ub_udelay(unsigned long usec)
+{
+
+ syscall(API_UDELAY, NULL, &usec);
+}
+
+unsigned long
+ub_get_timer(unsigned long base)
+{
+ unsigned long cur;
+
+ if (!syscall(API_GET_TIMER, NULL, &cur, &base))
+ return (0);
+
+ return (cur);
+}
+
+/****************************************************************************
+ *
+ * devices
+ *
+ * Devices are identified by handles: numbers 0, 1, 2, ..., UB_MAX_DEV-1
+ *
+ ***************************************************************************/
+
+static struct device_info devices[UB_MAX_DEV];
+
+struct device_info *
+ub_dev_get(int i)
+{
+
+ return ((i < 0 || i >= UB_MAX_DEV) ? NULL : &devices[i]);
+}
+
+/*
+ * Enumerates the devices: fills out device_info elements in the devices[]
+ * array.
+ *
+ * returns: number of devices found
+ */
+int
+ub_dev_enum(void)
+{
+ struct device_info *di;
+ int n = 0;
+
+ memset(&devices, 0, sizeof(struct device_info) * UB_MAX_DEV);
+ di = &devices[0];
+
+ if (!syscall(API_DEV_ENUM, NULL, di))
+ return (0);
+
+ while (di->cookie != NULL) {
+
+ if (++n >= UB_MAX_DEV)
+ break;
+
+ /* take another device_info */
+ di++;
+
+ /* pass on the previous cookie */
+ di->cookie = devices[n - 1].cookie;
+
+ if (!syscall(API_DEV_ENUM, NULL, di))
+ return (0);
+ }
+
+ return (n);
+}
+
+/*
+ * handle: 0-based id of the device
+ *
+ * returns: 0 when OK, err otherwise
+ */
+int
+ub_dev_open(int handle)
+{
+ struct device_info *di;
+ int err = 0;
+
+ if (handle < 0 || handle >= UB_MAX_DEV)
+ return (API_EINVAL);
+
+ di = &devices[handle];
+ if (!syscall(API_DEV_OPEN, &err, di))
+ return (-1);
+
+ return (err);
+}
+
+int
+ub_dev_close(int handle)
+{
+ struct device_info *di;
+
+ if (handle < 0 || handle >= UB_MAX_DEV)
+ return (API_EINVAL);
+
+ di = &devices[handle];
+ if (!syscall(API_DEV_CLOSE, NULL, di))
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * Validates device for read/write, it has to:
+ *
+ * - have sane handle
+ * - be opened
+ *
+ * returns: 0/1 accordingly
+ */
+static int
+dev_valid(int handle)
+{
+
+ if (handle < 0 || handle >= UB_MAX_DEV)
+ return (0);
+
+ if (devices[handle].state != DEV_STA_OPEN)
+ return (0);
+
+ return (1);
+}
+
+static int
+dev_stor_valid(int handle)
+{
+
+ if (!dev_valid(handle))
+ return (0);
+
+ if (!(devices[handle].type & DEV_TYP_STOR))
+ return (0);
+
+ return (1);
+}
+
+int
+ub_dev_read(int handle, void *buf, lbasize_t len, lbastart_t start,
+ lbasize_t *rlen)
+{
+ struct device_info *di;
+ lbasize_t act_len;
+ int err = 0;
+
+ if (!dev_stor_valid(handle))
+ return (API_ENODEV);
+
+ di = &devices[handle];
+ if (!syscall(API_DEV_READ, &err, di, buf, &len, &start, &act_len))
+ return (API_ESYSC);
+
+ if (!err && rlen)
+ *rlen = act_len;
+
+ return (err);
+}
+
+static int
+dev_net_valid(int handle)
+{
+
+ if (!dev_valid(handle))
+ return (0);
+
+ if (devices[handle].type != DEV_TYP_NET)
+ return (0);
+
+ return (1);
+}
+
+int
+ub_dev_recv(int handle, void *buf, int len, int *rlen)
+{
+ struct device_info *di;
+ int err = 0, act_len;
+
+ if (!dev_net_valid(handle))
+ return (API_ENODEV);
+
+ di = &devices[handle];
+ if (!syscall(API_DEV_READ, &err, di, buf, &len, &act_len))
+ return (API_ESYSC);
+
+ if (!err)
+ *rlen = act_len;
+
+ return (err);
+}
+
+int
+ub_dev_send(int handle, void *buf, int len)
+{
+ struct device_info *di;
+ int err = 0;
+
+ if (!dev_net_valid(handle))
+ return (API_ENODEV);
+
+ di = &devices[handle];
+ if (!syscall(API_DEV_WRITE, &err, di, buf, &len))
+ return (API_ESYSC);
+
+ return (err);
+}
+
+char *
+ub_stor_type(int type)
+{
+
+ if (type & DT_STOR_IDE)
+ return ("IDE");
+
+ if (type & DT_STOR_SCSI)
+ return ("SCSI");
+
+ if (type & DT_STOR_USB)
+ return ("USB");
+
+ if (type & DT_STOR_MMC)
+ return ("MMC");
+
+ if (type & DT_STOR_SATA)
+ return ("SATA");
+
+ return ("Unknown");
+}
+
+char *
+ub_mem_type(int flags)
+{
+
+ switch (flags & 0x000F) {
+ case MR_ATTR_FLASH:
+ return ("FLASH");
+ case MR_ATTR_DRAM:
+ return ("DRAM");
+ case MR_ATTR_SRAM:
+ return ("SRAM");
+ default:
+ return ("Unknown");
+ }
+}
+
+void
+ub_dump_di(int handle)
+{
+ struct device_info *di = ub_dev_get(handle);
+ int i;
+
+ printf("device info (%d):\n", handle);
+ printf(" cookie\t= %p\n", di->cookie);
+ printf(" type\t\t= 0x%08x\n", di->type);
+
+ if (di->type == DEV_TYP_NET) {
+ printf(" hwaddr\t= ");
+ for (i = 0; i < 6; i++)
+ printf("%02x ", di->di_net.hwaddr[i]);
+
+ printf("\n");
+
+ } else if (di->type & DEV_TYP_STOR) {
+ printf(" type\t\t= %s\n", ub_stor_type(di->type));
+ printf(" blk size\t\t= %ld\n", di->di_stor.block_size);
+ printf(" blk count\t\t= %ld\n", di->di_stor.block_count);
+ }
+}
+
+void
+ub_dump_si(struct sys_info *si)
+{
+ int i;
+
+ printf("sys info:\n");
+ printf(" clkbus\t= %ld MHz\n", si->clk_bus / 1000 / 1000);
+ printf(" clkcpu\t= %ld MHz\n", si->clk_cpu / 1000 / 1000);
+ printf(" bar\t\t= 0x%08lx\n", si->bar);
+
+ printf("---\n");
+ for (i = 0; i < si->mr_no; i++) {
+ if (si->mr[i].flags == 0)
+ break;
+
+ printf(" start\t= 0x%08lx\n", si->mr[i].start);
+ printf(" size\t= 0x%08lx\n", si->mr[i].size);
+ printf(" type\t= %s\n", ub_mem_type(si->mr[i].flags));
+ printf("---\n");
+ }
+}
+
+/****************************************
+ *
+ * env vars
+ *
+ ****************************************/
+
+char *
+ub_env_get(const char *name)
+{
+ char *value;
+
+ if (!syscall(API_ENV_GET, NULL, name, &value))
+ return (NULL);
+
+ return (value);
+}
+
+void
+ub_env_set(const char *name, char *value)
+{
+
+ syscall(API_ENV_SET, NULL, name, value);
+}
+
+static char env_name[256];
+
+const char *
+ub_env_enum(const char *last)
+{
+ const char *env, *str;
+ int i;
+
+ /*
+ * It's OK to pass only the name piece as last (and not the whole
+ * 'name=val' string), since the API_ENUM_ENV call uses envmatch()
+ * internally, which handles such case
+ */
+ env = NULL;
+ if (!syscall(API_ENV_ENUM, NULL, last, &env))
+ return (NULL);
+
+ if (env == NULL || last == env)
+ /* no more env. variables to enumerate */
+ return (NULL);
+
+ /* next enumerated env var */
+ memset(env_name, 0, 256);
+ for (i = 0, str = env; *str != '=' && *str != '\0';)
+ env_name[i++] = *str++;
+
+ env_name[i] = '\0';
+
+ return (env_name);
+}
diff --git a/stand/uboot/lib/glue.h b/stand/uboot/lib/glue.h
new file mode 100644
index 0000000..4c2da66
--- /dev/null
+++ b/stand/uboot/lib/glue.h
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 2008 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * This is the header file for conveniency wrapper routines (API glue)
+ */
+
+#ifndef _API_GLUE_H_
+#define _API_GLUE_H_
+
+#include "api_public.h"
+
+/*
+ * Mask used to align the start address for API signature search to 1MiB
+ */
+#define API_SIG_SEARCH_MASK ~0x000fffff
+
+#ifdef __mips__
+/*
+ * On MIPS, U-Boot passes us a hint address, which is very close to the end of
+ * RAM (less than 1MiB), so searching for the API signature within more than
+ * that leads to exception.
+ */
+#define API_SIG_SEARCH_LEN 0x00100000
+#else
+/*
+ * Search for the API signature within 3MiB of the 1MiB-aligned address that
+ * U-Boot has hinted us.
+ */
+#define API_SIG_SEARCH_LEN 0x00300000
+#endif
+
+int syscall(int, int *, ...);
+void *syscall_ptr;
+
+int api_parse_cmdline_sig(int argc, char **argv, struct api_signature **sig);
+int api_search_sig(struct api_signature **sig);
+
+#define UB_MAX_MR 16 /* max mem regions number */
+#define UB_MAX_DEV 6 /* max devices number */
+
+/*
+ * The ub_ library calls are part of the application, not U-Boot code! They
+ * are front-end wrappers that are used by the consumer application: they
+ * prepare arguments for particular syscall and jump to the low level
+ * syscall()
+ */
+
+/* console */
+int ub_getc(void);
+int ub_tstc(void);
+void ub_putc(char);
+void ub_puts(const char *);
+
+/* system */
+void ub_reset(void);
+struct sys_info *ub_get_sys_info(void);
+
+/* time */
+void ub_udelay(unsigned long);
+unsigned long ub_get_timer(unsigned long);
+
+/* env vars */
+char *ub_env_get(const char *);
+void ub_env_set(const char *, char *);
+const char *ub_env_enum(const char *);
+
+/* devices */
+int ub_dev_enum(void);
+int ub_dev_open(int);
+int ub_dev_close(int);
+int ub_dev_read(int, void *, lbasize_t, lbastart_t, lbasize_t *);
+int ub_dev_send(int, void *, int);
+int ub_dev_recv(int, void *, int, int *);
+struct device_info *ub_dev_get(int);
+
+void ub_dump_di(int);
+void ub_dump_si(struct sys_info *);
+char *ub_mem_type(int);
+char *ub_stor_type(int);
+
+#endif /* _API_GLUE_H_ */
diff --git a/stand/uboot/lib/libuboot.h b/stand/uboot/lib/libuboot.h
new file mode 100644
index 0000000..c6cf93c
--- /dev/null
+++ b/stand/uboot/lib/libuboot.h
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (C) 2000 Benno Rice.
+ * Copyright (C) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct uboot_devdesc
+{
+ struct devsw *d_dev;
+ int d_type;
+ int d_unit;
+ void *d_opendata;
+ union {
+ struct {
+ int slice;
+ int partition;
+ off_t offset;
+ } disk;
+ } d_kind;
+};
+
+#define d_disk d_kind.disk
+
+/*
+ * Default network packet alignment in memory. On arm arches packets must be
+ * aligned to cacheline boundaries.
+ */
+#if defined(__aarch64__)
+#define PKTALIGN 128
+#elif defined(__arm__)
+#define PKTALIGN 64
+#else
+#define PKTALIGN 32
+#endif
+
+int uboot_getdev(void **vdev, const char *devspec, const char **path);
+char *uboot_fmtdev(void *vdev);
+int uboot_setcurrdev(struct env_var *ev, int flags, const void *value);
+
+extern int devs_no;
+extern struct netif_driver uboot_net;
+extern struct devsw uboot_storage;
+
+extern uintptr_t uboot_heap_start;
+extern uintptr_t uboot_heap_end;
+
+uint64_t uboot_loadaddr(u_int type, void *data, uint64_t addr);
+ssize_t uboot_copyin(const void *src, vm_offset_t dest, const size_t len);
+ssize_t uboot_copyout(const vm_offset_t src, void *dest, const size_t len);
+ssize_t uboot_readin(const int fd, vm_offset_t dest, const size_t len);
+extern int uboot_autoload(void);
+
+struct preloaded_file;
+struct file_format;
+
+extern struct file_format uboot_elf;
+
+void reboot(void);
+
+int uboot_diskgetunit(int type, int type_unit);
+
diff --git a/stand/uboot/lib/module.c b/stand/uboot/lib/module.c
new file mode 100644
index 0000000..0951ed6
--- /dev/null
+++ b/stand/uboot/lib/module.c
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * U-Boot-specific module functionality.
+ */
+
+#include <stand.h>
+#include <string.h>
+
+#if defined(LOADER_FDT_SUPPORT)
+#include <fdt_platform.h>
+#endif
+
+#include "bootstrap.h"
+#include "libuboot.h"
+
+int
+uboot_autoload(void)
+{
+#if defined(LOADER_FDT_SUPPORT)
+ int err;
+
+ if ((err = fdt_setup_fdtp()) != 0) {
+ printf("No valid device tree blob found!\n");
+ return (err);
+ }
+#endif
+
+ return(0);
+}
diff --git a/stand/uboot/lib/net.c b/stand/uboot/lib/net.c
new file mode 100644
index 0000000..a0d1cb4
--- /dev/null
+++ b/stand/uboot/lib/net.c
@@ -0,0 +1,364 @@
+/*-
+ * Copyright (c) 2000-2001 Benno Rice
+ * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+
+#include <stand.h>
+#include <net.h>
+#include <netif.h>
+
+#include "api_public.h"
+#include "glue.h"
+#include "libuboot.h"
+#include "dev_net.h"
+
+static int net_probe(struct netif *, void *);
+static int net_match(struct netif *, void *);
+static void net_init(struct iodesc *, void *);
+static ssize_t net_get(struct iodesc *, void **, time_t);
+static ssize_t net_put(struct iodesc *, void *, size_t);
+static void net_end(struct netif *);
+
+extern struct netif_stats net_stats[];
+
+struct netif_dif net_ifs[] = {
+ /* dif_unit dif_nsel dif_stats dif_private */
+ { 0, 1, &net_stats[0], 0, },
+};
+
+struct netif_stats net_stats[nitems(net_ifs)];
+
+struct netif_driver uboot_net = {
+ "uboot_eth", /* netif_bname */
+ net_match, /* netif_match */
+ net_probe, /* netif_probe */
+ net_init, /* netif_init */
+ net_get, /* netif_get */
+ net_put, /* netif_put */
+ net_end, /* netif_end */
+ net_ifs, /* netif_ifs */
+ nitems(net_ifs) /* netif_nifs */
+};
+
+struct uboot_softc {
+ uint32_t sc_pad;
+ uint8_t sc_rxbuf[ETHER_MAX_LEN];
+ uint8_t sc_txbuf[ETHER_MAX_LEN + PKTALIGN];
+ uint8_t *sc_txbufp;
+ int sc_handle; /* device handle for ub_dev_xxx */
+};
+
+static struct uboot_softc uboot_softc;
+
+/*
+ * get_env_net_params()
+ *
+ * Attempt to obtain all the parms we need for netbooting from the U-Boot
+ * environment. If we fail to obtain the values it may still be possible to
+ * netboot; the net_dev code will attempt to get the values from bootp, rarp,
+ * and other such sources.
+ *
+ * If rootip.s_addr is non-zero net_dev assumes the required global variables
+ * are set and skips the bootp inquiry. For that reason, we don't set rootip
+ * until we've verified that we have at least the minimum required info.
+ *
+ * This is called from netif_init() which can result in it getting called
+ * multiple times, by design. The network code at higher layers zeroes out
+ * rootip when it closes a network interface, so if it gets opened again we have
+ * to obtain all this info again.
+ */
+static void
+get_env_net_params()
+{
+ char *envstr;
+ in_addr_t rootaddr, serveraddr;
+
+ /*
+ * Silently get out right away if we don't have rootpath, because none
+ * of the other info we obtain below is sufficient to boot without it.
+ *
+ * If we do have rootpath, copy it into the global var and also set
+ * dhcp.root-path in the env. If we don't get all the other info from
+ * the u-boot env below, we will still try dhcp/bootp, but the server-
+ * provided path will not replace the user-provided value we set here.
+ */
+ if ((envstr = ub_env_get("rootpath")) == NULL)
+ return;
+ strlcpy(rootpath, envstr, sizeof(rootpath));
+ setenv("dhcp.root-path", rootpath, 0);
+
+ /*
+ * Our own IP address must be valid. Silently get out if it's not set,
+ * but whine if it's there and we can't parse it.
+ */
+ if ((envstr = ub_env_get("ipaddr")) == NULL)
+ return;
+ if ((myip.s_addr = inet_addr(envstr)) == INADDR_NONE) {
+ printf("Could not parse ipaddr '%s'\n", envstr);
+ return;
+ }
+
+ /*
+ * Netmask is optional, default to the "natural" netmask for our IP, but
+ * whine if it was provided and we couldn't parse it.
+ */
+ if ((envstr = ub_env_get("netmask")) != NULL &&
+ (netmask = inet_addr(envstr)) == INADDR_NONE) {
+ printf("Could not parse netmask '%s'\n", envstr);
+ }
+ if (netmask == INADDR_NONE) {
+ if (IN_CLASSA(myip.s_addr))
+ netmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(myip.s_addr))
+ netmask = IN_CLASSB_NET;
+ else
+ netmask = IN_CLASSC_NET;
+ }
+
+ /*
+ * Get optional serverip before rootpath; the latter can override it.
+ * Whine only if it's present but can't be parsed.
+ */
+ serveraddr = INADDR_NONE;
+ if ((envstr = ub_env_get("serverip")) != NULL) {
+ if ((serveraddr = inet_addr(envstr)) == INADDR_NONE)
+ printf("Could not parse serverip '%s'\n", envstr);
+ }
+
+ /*
+ * There must be a rootpath. It may be ip:/path or it may be just the
+ * path in which case the ip needs to be in serverip.
+ */
+ rootaddr = net_parse_rootpath();
+ if (rootaddr == INADDR_NONE)
+ rootaddr = serveraddr;
+ if (rootaddr == INADDR_NONE) {
+ printf("No server address for rootpath '%s'\n", envstr);
+ return;
+ }
+ rootip.s_addr = rootaddr;
+
+ /*
+ * Gateway IP is optional unless rootip is on a different net in which
+ * case whine if it's missing or we can't parse it, and set rootip addr
+ * to zero, which signals to other network code that network params
+ * aren't set (so it will try dhcp, bootp, etc).
+ */
+ envstr = ub_env_get("gatewayip");
+ if (!SAMENET(myip, rootip, netmask)) {
+ if (envstr == NULL) {
+ printf("Need gatewayip for a root server on a "
+ "different network.\n");
+ rootip.s_addr = 0;
+ return;
+ }
+ if ((gateip.s_addr = inet_addr(envstr) == INADDR_NONE)) {
+ printf("Could not parse gatewayip '%s'\n", envstr);
+ rootip.s_addr = 0;
+ return;
+ }
+ }
+}
+
+static int
+net_match(struct netif *nif, void *machdep_hint)
+{
+ char **a = (char **)machdep_hint;
+
+ if (memcmp("net", *a, 3) == 0)
+ return (1);
+
+ printf("net_match: could not match network device\n");
+ return (0);
+}
+
+static int
+net_probe(struct netif *nif, void *machdep_hint)
+{
+ struct device_info *di;
+ int i;
+
+ for (i = 0; i < devs_no; i++)
+ if ((di = ub_dev_get(i)) != NULL)
+ if (di->type == DEV_TYP_NET)
+ break;
+
+ if (i == devs_no) {
+ printf("net_probe: no network devices found, maybe not"
+ " enumerated yet..?\n");
+ return (-1);
+ }
+
+#if defined(NETIF_DEBUG)
+ printf("net_probe: network device found: %d\n", i);
+#endif
+ uboot_softc.sc_handle = i;
+
+ return (0);
+}
+
+static ssize_t
+net_put(struct iodesc *desc, void *pkt, size_t len)
+{
+ struct netif *nif = desc->io_netif;
+ struct uboot_softc *sc = nif->nif_devdata;
+ size_t sendlen;
+ ssize_t rv;
+
+#if defined(NETIF_DEBUG)
+ struct ether_header *eh;
+
+ printf("net_put: desc %p, pkt %p, len %d\n", desc, pkt, len);
+ eh = pkt;
+ printf("dst: %s ", ether_sprintf(eh->ether_dhost));
+ printf("src: %s ", ether_sprintf(eh->ether_shost));
+ printf("type: 0x%x\n", eh->ether_type & 0xffff);
+#endif
+
+ if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) {
+ sendlen = ETHER_MIN_LEN - ETHER_CRC_LEN;
+ bzero(sc->sc_txbufp, sendlen);
+ } else
+ sendlen = len;
+
+ memcpy(sc->sc_txbufp, pkt, len);
+
+ rv = ub_dev_send(sc->sc_handle, sc->sc_txbufp, sendlen);
+
+#if defined(NETIF_DEBUG)
+ printf("net_put: ub_send returned %d\n", rv);
+#endif
+ if (rv == 0)
+ rv = len;
+ else
+ rv = -1;
+
+ return (rv);
+}
+
+static ssize_t
+net_get(struct iodesc *desc, void **pkt, time_t timeout)
+{
+ struct netif *nif = desc->io_netif;
+ struct uboot_softc *sc = nif->nif_devdata;
+ time_t t;
+ int err, rlen;
+ size_t len;
+ char *buf;
+
+#if defined(NETIF_DEBUG)
+ printf("net_get: pkt %p, timeout %d\n", pkt, timeout);
+#endif
+ t = getsecs();
+ len = sizeof(sc->sc_rxbuf);
+ do {
+ err = ub_dev_recv(sc->sc_handle, sc->sc_rxbuf, len, &rlen);
+
+ if (err != 0) {
+ printf("net_get: ub_dev_recv() failed, error=%d\n",
+ err);
+ rlen = 0;
+ break;
+ }
+ } while ((rlen == -1 || rlen == 0) && (getsecs() - t < timeout));
+
+#if defined(NETIF_DEBUG)
+ printf("net_get: received len %d (%x)\n", rlen, rlen);
+#endif
+
+ if (rlen > 0) {
+ buf = malloc(rlen + ETHER_ALIGN);
+ if (buf == NULL)
+ return (-1);
+ memcpy(buf + ETHER_ALIGN, sc->sc_rxbuf, rlen);
+ *pkt = buf;
+ return ((ssize_t)rlen);
+ }
+
+ return (-1);
+}
+
+static void
+net_init(struct iodesc *desc, void *machdep_hint)
+{
+ struct netif *nif = desc->io_netif;
+ struct uboot_softc *sc;
+ struct device_info *di;
+ int err;
+
+ sc = nif->nif_devdata = &uboot_softc;
+
+ if ((err = ub_dev_open(sc->sc_handle)) != 0)
+ panic("%s%d: initialisation failed with error %d\n",
+ nif->nif_driver->netif_bname, nif->nif_unit, err);
+
+ /* Get MAC address */
+ di = ub_dev_get(sc->sc_handle);
+ memcpy(desc->myea, di->di_net.hwaddr, 6);
+ if (memcmp (desc->myea, "\0\0\0\0\0\0", 6) == 0) {
+ panic("%s%d: empty ethernet address!",
+ nif->nif_driver->netif_bname, nif->nif_unit);
+ }
+
+ /* Attempt to get netboot params from the u-boot env. */
+ get_env_net_params();
+ if (myip.s_addr != 0)
+ desc->myip = myip;
+
+#if defined(NETIF_DEBUG)
+ printf("network: %s%d attached to %s\n", nif->nif_driver->netif_bname,
+ nif->nif_unit, ether_sprintf(desc->myea));
+#endif
+
+ /* Set correct alignment for TX packets */
+ sc->sc_txbufp = sc->sc_txbuf;
+ if ((unsigned long)sc->sc_txbufp % PKTALIGN)
+ sc->sc_txbufp += PKTALIGN -
+ (unsigned long)sc->sc_txbufp % PKTALIGN;
+}
+
+static void
+net_end(struct netif *nif)
+{
+ struct uboot_softc *sc = nif->nif_devdata;
+ int err;
+
+ if ((err = ub_dev_close(sc->sc_handle)) != 0)
+ panic("%s%d: net_end failed with error %d\n",
+ nif->nif_driver->netif_bname, nif->nif_unit, err);
+}
diff --git a/stand/uboot/lib/reboot.c b/stand/uboot/lib/reboot.c
new file mode 100644
index 0000000..9f4d277
--- /dev/null
+++ b/stand/uboot/lib/reboot.c
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "glue.h"
+
+void
+exit(int code)
+{
+
+ ub_reset();
+}
diff --git a/stand/uboot/lib/time.c b/stand/uboot/lib/time.c
new file mode 100644
index 0000000..a3c73f4
--- /dev/null
+++ b/stand/uboot/lib/time.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2000 Benno Rice
+ * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+
+#include "glue.h"
+
+/*
+ * Return the time in seconds since the beginning of the day.
+ */
+time_t
+time(time_t *tloc)
+{
+ int secs;
+
+ secs = ub_get_timer(0) / 1000;
+ if (tloc)
+ *tloc = secs;
+
+ return (secs);
+}
+
+time_t
+getsecs(void)
+{
+
+ return (time(NULL));
+}
+
+/*
+ * Use U-Boot udelay() function to wait for a given microseconds period
+ */
+void
+delay(int usecs)
+{
+
+ ub_udelay(usecs);
+}
diff --git a/stand/usb/Makefile b/stand/usb/Makefile
new file mode 100644
index 0000000..1b94b3d
--- /dev/null
+++ b/stand/usb/Makefile
@@ -0,0 +1,58 @@
+#
+# $FreeBSD$
+#
+# Copyright (c) 2013 Hans Petter Selasky.
+# Copyright (c) 2014 SRI International
+# All rights reserved.
+#
+# This software was developed by SRI International and the University of
+# Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+# ("CTSRD"), as part of the DARPA CRASH research programme.
+#
+# 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.
+#
+
+LIB= usbboot
+INTERNALLIB=
+
+CFLAGS+= -DBOOTPROG=\"usbloader\"
+CFLAGS+= -ffunction-sections -fdata-sections
+CFLAGS+= -ffreestanding
+CFLAGS+= -Wformat -Wall
+CFLAGS+= -g
+CFLAGS+= -fno-pic
+
+.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64"
+CFLAGS+= -march=i386
+CFLAGS.gcc+= -mpreferred-stack-boundary=2
+.endif
+.if ${MACHINE_CPUARCH} == "amd64"
+CFLAGS+= -m32
+.endif
+.if ${MACHINE_CPUARCH} == "mips"
+CFLAGS+= -mno-abicalls
+.endif
+
+
+.include "usbcore.mk"
+.include "../kshim/kshim.mk"
+.include <bsd.lib.mk>
diff --git a/stand/usb/Makefile.test b/stand/usb/Makefile.test
new file mode 100644
index 0000000..7c6a66d1
--- /dev/null
+++ b/stand/usb/Makefile.test
@@ -0,0 +1,61 @@
+#
+# $FreeBSD$
+#
+# Copyright (c) 2013 Hans Petter Selasky. 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.
+#
+
+#
+# USB test application
+#
+
+.PATH: ${.CURDIR}
+
+PROG= usbloader
+MAN=
+SRCS=
+
+CFLAGS+= -Wall
+CFLAGS+= -g
+
+.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64"
+CFLAGS+= -march=i386
+CFLAGS.gcc+= -mpreferred-stack-boundary=2
+.endif
+.if ${MACHINE_CPUARCH} == "amd64"
+CFLAGS+= -m32
+.endif
+
+LDFLAGS+= -Wl,--gc-sections
+
+SRCS+= bsd_usbloader_test.c
+
+LDADD+= libusbboot.a
+DPADD+= libusbboot.a
+
+.include <bsd.prog.mk>
+
+${PROG}: libusbboot.a
+
+libusbboot.a:
+ make -f Makefile
diff --git a/stand/usb/bsd_usbloader_test.c b/stand/usb/bsd_usbloader_test.c
new file mode 100644
index 0000000..35140b8
--- /dev/null
+++ b/stand/usb/bsd_usbloader_test.c
@@ -0,0 +1,101 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2013 Hans Petter Selasky. 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 <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+
+extern int usleep(int);
+extern void callout_process(int);
+extern void usb_idle(void);
+extern void usb_init(void);
+extern void usb_uninit(void);
+
+#define hz 1000
+
+#ifdef HAVE_MALLOC
+void *
+usb_malloc(size_t size)
+{
+ return (malloc(size));
+}
+
+void
+usb_free(void *ptr)
+{
+ free(ptr);
+}
+#endif
+
+void
+DELAY(unsigned int delay)
+{
+ usleep(delay);
+}
+
+void
+delay(unsigned int delay)
+{
+ usleep(delay);
+}
+
+int
+pause(const char *what, int timeout)
+{
+ if (timeout == 0)
+ timeout = 1;
+
+ usleep((1000000 / hz) * timeout);
+
+ return (0);
+}
+
+int
+main(int argc, char **argv)
+{
+ uint32_t time;
+
+ usb_init();
+
+ time = 0;
+
+ while (1) {
+
+ usb_idle();
+
+ usleep(1000);
+
+ if (++time >= (1000 / hz)) {
+ time = 0;
+ callout_process(1);
+ }
+ }
+
+ usb_uninit();
+
+ return (0);
+}
diff --git a/stand/usb/storage/umass_common.c b/stand/usb/storage/umass_common.c
new file mode 100644
index 0000000..5ddd159
--- /dev/null
+++ b/stand/usb/storage/umass_common.c
@@ -0,0 +1,90 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky <hselasky@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 USB_GLOBAL_INCLUDE_FILE
+
+#include "umass_common.h"
+
+struct usb_attach_arg umass_uaa;
+
+static device_probe_t umass_probe;
+static device_attach_t umass_attach;
+static device_detach_t umass_detach;
+
+static devclass_t umass_devclass;
+
+static device_method_t umass_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, umass_probe),
+ DEVMETHOD(device_attach, umass_attach),
+ DEVMETHOD(device_detach, umass_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t umass_driver = {
+ .name = "umass",
+ .methods = umass_methods,
+};
+
+DRIVER_MODULE(umass, uhub, umass_driver, umass_devclass, NULL, 0);
+
+static int
+umass_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_mode != USB_MODE_HOST ||
+ uaa->info.bInterfaceClass != UICLASS_MASS ||
+ uaa->info.bInterfaceSubClass != UISUBCLASS_SCSI ||
+ uaa->info.bInterfaceProtocol != UIPROTO_MASS_BBB ||
+ device_get_unit(dev) != 0)
+ return (ENXIO);
+ return (0);
+}
+
+static int
+umass_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ umass_uaa = *uaa;
+ return (0); /* success */
+}
+
+static int
+umass_detach(device_t dev)
+{
+
+#ifdef USB_DEBUG
+ memset(&umass_uaa, 0, sizeof(umass_uaa));
+#endif
+ return (0);
+}
diff --git a/stand/usb/storage/umass_common.h b/stand/usb/storage/umass_common.h
new file mode 100644
index 0000000..a8ffd49
--- /dev/null
+++ b/stand/usb/storage/umass_common.h
@@ -0,0 +1,41 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky <hselasky@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ */
+
+#ifndef _UMASS_COMMON_H_
+#define _UMASS_COMMON_H_
+
+struct usb_attach_arg;
+struct devsw;
+
+extern struct usb_attach_arg umass_uaa;
+extern struct devsw umass_disk;
+
+#endif /* _UMASS_COMMON_H_ */
diff --git a/stand/usb/storage/umass_loader.c b/stand/usb/storage/umass_loader.c
new file mode 100644
index 0000000..fbb890b
--- /dev/null
+++ b/stand/usb/storage/umass_loader.c
@@ -0,0 +1,239 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky <hselasky@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 <sys/param.h>
+
+#include <bootstrap.h>
+#include <stdarg.h>
+
+#include <stand.h>
+#include <disk.h>
+
+#define HAVE_STANDARD_DEFS
+
+#include USB_GLOBAL_INCLUDE_FILE
+
+#include "umass_common.h"
+
+static int umass_disk_init(void);
+static int umass_disk_open(struct open_file *,...);
+static int umass_disk_close(struct open_file *);
+static void umass_disk_cleanup(void);
+static int umass_disk_ioctl(struct open_file *, u_long, void *);
+static int umass_disk_strategy(void *, int, daddr_t, size_t, char *, size_t *);
+static int umass_disk_print(int);
+
+struct devsw umass_disk = {
+ .dv_name = "umass",
+ .dv_type = DEVT_DISK,
+ .dv_init = umass_disk_init,
+ .dv_strategy = umass_disk_strategy,
+ .dv_open = umass_disk_open,
+ .dv_close = umass_disk_close,
+ .dv_ioctl = umass_disk_ioctl,
+ .dv_print = umass_disk_print,
+ .dv_cleanup = umass_disk_cleanup,
+};
+
+static int
+umass_disk_init(void)
+{
+ uint32_t time;
+
+ usb_init();
+ usb_needs_explore_all();
+
+ /* wait 8 seconds for a USB mass storage device to appear */
+ for (time = 0; time < (8 * hz); time++) {
+ usb_idle();
+ delay(1000000 / hz);
+ time++;
+ callout_process(1);
+ if (umass_uaa.device != NULL)
+ return (0);
+ }
+ return (0);
+}
+
+static int
+umass_disk_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
+ char *buf, size_t *rsizep)
+{
+ if (umass_uaa.device == NULL)
+ return (ENXIO);
+ if (rsizep != NULL)
+ *rsizep = 0;
+
+ flag &= F_MASK;
+ if (flag == F_WRITE) {
+ if (usb_msc_write_10(umass_uaa.device, 0, dblk, size >> 9, buf) != 0)
+ return (EINVAL);
+ } else if (flag == F_READ) {
+ if (usb_msc_read_10(umass_uaa.device, 0, dblk, size >> 9, buf) != 0)
+ return (EINVAL);
+ } else {
+ return (EROFS);
+ }
+
+ if (rsizep != NULL)
+ *rsizep = size;
+ return (0);
+}
+
+static int
+umass_disk_open_sub(struct disk_devdesc *dev)
+{
+ uint32_t nblock;
+ uint32_t blocksize;
+
+ if (usb_msc_read_capacity(umass_uaa.device, 0, &nblock, &blocksize) != 0)
+ return (EINVAL);
+
+ return (disk_open(dev, ((uint64_t)nblock + 1) * (uint64_t)blocksize, blocksize));
+}
+
+static int
+umass_disk_open(struct open_file *f,...)
+{
+ va_list ap;
+ struct disk_devdesc *dev;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct disk_devdesc *);
+ va_end(ap);
+
+ if (umass_uaa.device == NULL)
+ return (ENXIO);
+ if (dev->d_unit != 0)
+ return (EIO);
+ return (umass_disk_open_sub(dev));
+}
+
+static int
+umass_disk_ioctl(struct open_file *f, u_long cmd, void *buf)
+{
+ struct disk_devdesc *dev;
+ uint32_t nblock;
+ uint32_t blocksize;
+ int rc;
+
+ dev = (struct disk_devdesc *)(f->f_devdata);
+ if (dev == NULL)
+ return (EINVAL);
+
+ rc = disk_ioctl(dev, cmd, buf);
+ if (rc != ENOTTY)
+ return (rc);
+
+ switch (cmd) {
+ case DIOCGSECTORSIZE:
+ case DIOCGMEDIASIZE:
+ if (usb_msc_read_capacity(umass_uaa.device, 0,
+ &nblock, &blocksize) != 0)
+ return (EINVAL);
+
+ if (cmd == DIOCGMEDIASIZE)
+ *(uint64_t*)buf = nblock;
+ else
+ *(uint32_t*)buf = blocksize;
+
+ return (0);
+ default:
+ return (ENXIO);
+ }
+}
+
+static int
+umass_disk_close(struct open_file *f)
+{
+ struct disk_devdesc *dev;
+
+ dev = (struct disk_devdesc *)f->f_devdata;
+ return (disk_close(dev));
+}
+
+static int
+umass_disk_print(int verbose)
+{
+ struct disk_devdesc dev;
+
+ printf("%s devices:", umass_disk.dv_name);
+ if (pager_output("\n") != 0)
+ return (1);
+
+ memset(&dev, 0, sizeof(dev));
+
+ ret = pager_output(" umass0 UMASS device\n");
+ if (ret != 0)
+ return (ret);
+ dev.d_dev = &umass_disk;
+ dev.d_unit = 0;
+ dev.d_slice = -1;
+ dev.d_partition = -1;
+
+ if (umass_disk_open_sub(&dev) == 0) {
+ ret = disk_print(&dev, " umass0", verbose);
+ disk_close(&dev);
+ }
+ return (ret);
+}
+
+static void
+umass_disk_cleanup(void)
+{
+
+ usb_uninit();
+}
+
+
+/* USB specific functions */
+
+extern void callout_process(int);
+extern void usb_idle(void);
+extern void usb_init(void);
+extern void usb_uninit(void);
+
+void
+DELAY(unsigned int usdelay)
+{
+ delay(usdelay);
+}
+
+int
+pause(const char *what, int timeout)
+{
+ if (timeout == 0)
+ timeout = 1;
+
+ delay((1000000 / hz) * timeout);
+
+ return (0);
+}
diff --git a/stand/usb/tools/Makefile b/stand/usb/tools/Makefile
new file mode 100644
index 0000000..64cf28c
--- /dev/null
+++ b/stand/usb/tools/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= sysinit
+MAN=
+
+CFLAGS+= -I${.CURDIR}/../../kshim
+
+BINDIR?= /usr/bin
+
+.include <bsd.prog.mk>
diff --git a/stand/usb/tools/sysinit.c b/stand/usb/tools/sysinit.c
new file mode 100644
index 0000000..b968fe0
--- /dev/null
+++ b/stand/usb/tools/sysinit.c
@@ -0,0 +1,331 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2013 Hans Petter Selasky. 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.
+ */
+
+/*
+ * This utility sorts sysinit structure entries in binary format and
+ * prints out the result in C-format.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sysexits.h>
+#include "sysinit.h"
+
+static int opt_R;
+static const char *input_f;
+static const char *output_f;
+static const char *struct_name;
+static const char *keyword;
+static struct sysinit_data **start;
+static struct sysinit_data **stop;
+
+static int input_file = -1;
+static int output_file = -1;
+
+static uint8_t *input_ptr;
+static uint32_t input_len;
+
+static uint32_t endian32;
+
+static char scratch_buf[4096];
+
+static int success;
+
+static void do_sysinit(void);
+
+/* the following function converts the numbers into host endian format */
+
+static uint32_t
+read32(uint32_t val)
+{
+ uint32_t temp;
+ uint32_t endian;
+
+ endian = endian32;
+ temp = 0;
+
+ while (val) {
+ temp |= (val & 0xF) << ((endian & 0xF) * 4);
+ endian >>= 4;
+ val >>= 4;
+ }
+ return (temp);
+}
+
+static void
+do_write(int fd, const char *buf)
+{
+ int len = strlen(buf);
+
+ if (write(fd, buf, len) != len)
+ err(EX_SOFTWARE, "Could not write to output file");
+}
+
+static void *
+do_malloc(int size)
+{
+ void *ptr;
+
+ ptr = malloc(size);
+ if (ptr == NULL)
+ errx(EX_SOFTWARE, "Could not allocate memory");
+ return (ptr);
+}
+
+static void
+usage(void)
+{
+ errx(EX_USAGE, "sysinit -i sysinit.bin -o sysinit_data.c \\\n"
+ "\t" "-k sysinit -s sysinit_data [ -R (reverse)]");
+}
+
+static void
+cleanup(void)
+{
+ if (output_file >= 0)
+ close(output_file);
+ if (input_file >= 0)
+ close(input_file);
+ if (success == 0) {
+ if (output_f)
+ unlink(output_f);
+ }
+}
+
+static int
+compare(const void *_pa, const void *_pb)
+{
+ const struct sysinit_data * const *pa = _pa;
+ const struct sysinit_data * const *pb = _pb;
+
+ if ((*pa)->dw_msb_value > (*pb)->dw_msb_value)
+ return (1);
+
+ if ((*pa)->dw_msb_value < (*pb)->dw_msb_value)
+ return (-1);
+
+ if ((*pa)->dw_lsb_value > (*pb)->dw_lsb_value)
+ return (1);
+
+ if ((*pa)->dw_lsb_value < (*pb)->dw_lsb_value)
+ return (-1);
+
+ return (0); /* equal */
+}
+
+static int
+compare_R(const void *_pa, const void *_pb)
+{
+ const struct sysinit_data * const *pa = _pa;
+ const struct sysinit_data * const *pb = _pb;
+
+ if ((*pa)->dw_msb_value > (*pb)->dw_msb_value)
+ return (-1);
+
+ if ((*pa)->dw_msb_value < (*pb)->dw_msb_value)
+ return (1);
+
+ if ((*pa)->dw_lsb_value > (*pb)->dw_lsb_value)
+ return (-1);
+
+ if ((*pa)->dw_lsb_value < (*pb)->dw_lsb_value)
+ return (1);
+
+ return (0); /* equal */
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sysinit_data **sipp;
+ int c;
+ int entries;
+ off_t off;
+
+ while ((c = getopt(argc, argv, "k:s:i:o:Rh")) != -1) {
+ switch (c) {
+ case 'i':
+ input_f = optarg;
+ break;
+ case 'o':
+ output_f = optarg;
+ break;
+ case 'R':
+ opt_R = 1;
+ break;
+ case 'k':
+ keyword = optarg;
+ break;
+ case 's':
+ struct_name = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (input_f == NULL || output_f == NULL ||
+ struct_name == NULL || keyword == NULL)
+ usage();
+
+ atexit(&cleanup);
+
+ cleanup();
+
+ input_file = open(input_f, O_RDONLY);
+ if (input_file < 0)
+ err(EX_SOFTWARE, "Could not open input file: %s", input_f);
+
+ output_file = open(output_f, O_TRUNC | O_CREAT | O_RDWR, 0600);
+ if (output_file < 0)
+ err(EX_SOFTWARE, "Could not open output file: %s", output_f);
+
+ off = lseek(input_file, 0, SEEK_END);
+
+ input_ptr = do_malloc(off);
+ input_len = off;
+
+ if (input_len % (uint32_t)sizeof(struct sysinit_data)) {
+ errx(EX_SOFTWARE, "Input file size is not divisible by %u",
+ (unsigned int)sizeof(struct sysinit_data));
+ }
+ off = lseek(input_file, 0, SEEK_SET);
+ if (off < 0)
+ err(EX_SOFTWARE, "Could not seek to start of input file");
+
+ if (read(input_file, input_ptr, input_len) != input_len)
+ err(EX_SOFTWARE, "Could not read input file");
+
+ entries = input_len / (uint32_t)sizeof(struct sysinit_data);
+
+ start = do_malloc(sizeof(void *) * entries);
+ stop = start + entries;
+
+ for (c = 0; c != entries; c++)
+ start[c] = &((struct sysinit_data *)input_ptr)[c];
+
+ if (start != stop)
+ endian32 = (*start)->dw_endian32;
+
+ /* switch all fields to host endian order */
+ for (sipp = start; sipp < stop; sipp++) {
+ (*sipp)->dw_lsb_value = read32((*sipp)->dw_lsb_value);
+ (*sipp)->dw_msb_value = read32((*sipp)->dw_msb_value);
+ (*sipp)->dw_file_line = read32((*sipp)->dw_file_line);
+ }
+
+ if (opt_R == 0) {
+ /* sort entries, rising numerical order */
+ qsort(start, entries, sizeof(void *), &compare);
+ } else {
+ /* sort entries, falling numerical order */
+ qsort(start, entries, sizeof(void *), &compare_R);
+ }
+
+ /* safe all strings */
+ for (sipp = start; sipp < stop; sipp++) {
+ (*sipp)->b_keyword_name[sizeof((*sipp)->b_keyword_name) - 1] = 0;
+ (*sipp)->b_global_type[sizeof((*sipp)->b_global_type) - 1] = 0;
+ (*sipp)->b_global_name[sizeof((*sipp)->b_global_name) - 1] = 0;
+ (*sipp)->b_file_name[sizeof((*sipp)->b_file_name) - 1] = 0;
+ (*sipp)->b_debug_info[sizeof((*sipp)->b_debug_info) - 1] = 0;
+ }
+
+ if (strcmp(keyword, "sysinit") == 0)
+ do_sysinit();
+ else if (strcmp(keyword, "sysuninit") == 0)
+ do_sysinit();
+ else
+ errx(EX_USAGE, "Unknown keyword '%s'", keyword);
+
+ success = 1;
+
+ return (0);
+}
+
+static void
+do_sysinit(void)
+{
+ struct sysinit_data **sipp;
+ int c;
+
+ snprintf(scratch_buf, sizeof(scratch_buf),
+ "/*\n"
+ " * This file was automatically generated.\n"
+ " * Please do not edit.\n"
+ " */\n\n");
+
+ /* write out externals */
+ for (c = 0, sipp = start; sipp < stop; c++, sipp++) {
+ if (strcmp((const char *)(*sipp)->b_keyword_name, keyword))
+ continue;
+ if ((*sipp)->dw_msb_value == 0)
+ continue;
+
+ snprintf(scratch_buf, sizeof(scratch_buf),
+ "/* #%04u: %s entry at %s:%u */\n",
+ c, (*sipp)->b_debug_info, (*sipp)->b_file_name,
+ (unsigned int)(*sipp)->dw_file_line);
+
+ do_write(output_file, scratch_buf);
+
+ snprintf(scratch_buf, sizeof(scratch_buf),
+ "extern %s %s;\n\n", (*sipp)->b_global_type,
+ (*sipp)->b_global_name);
+
+ do_write(output_file, scratch_buf);
+ }
+
+ snprintf(scratch_buf, sizeof(scratch_buf),
+ "const void *%s[] = {\n", struct_name);
+
+ do_write(output_file, scratch_buf);
+
+ /* write out actual table */
+ for (c = 0, sipp = start; sipp < stop; c++, sipp++) {
+ if (strcmp((const char *)(*sipp)->b_keyword_name, keyword))
+ continue;
+ if ((*sipp)->dw_msb_value == 0)
+ continue;
+
+ snprintf(scratch_buf, sizeof(scratch_buf),
+ "\t&%s, /* #%04u */\n",
+ (*sipp)->b_global_name, (unsigned int)c);
+
+ do_write(output_file, scratch_buf);
+ }
+
+ snprintf(scratch_buf, sizeof(scratch_buf),
+ "\t(const void *)0\n"
+ "};\n");
+
+ do_write(output_file, scratch_buf);
+}
diff --git a/stand/usb/usb_busdma_loader.c b/stand/usb/usb_busdma_loader.c
new file mode 100644
index 0000000..90dc169
--- /dev/null
+++ b/stand/usb/usb_busdma_loader.c
@@ -0,0 +1,619 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2013 Hans Petter Selasky. 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 <bsd_global.h>
+
+#if USB_HAVE_BUSDMA
+static void usb_pc_common_mem_cb(struct usb_page_cache *pc,
+ void *vaddr, uint32_t length);
+#endif
+
+/*------------------------------------------------------------------------*
+ * usbd_get_page - lookup DMA-able memory for the given offset
+ *
+ * NOTE: Only call this function when the "page_cache" structure has
+ * been properly initialized !
+ *------------------------------------------------------------------------*/
+void
+usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset,
+ struct usb_page_search *res)
+{
+#if USB_HAVE_BUSDMA
+ struct usb_page *page;
+
+ if (pc->page_start) {
+
+ /* Case 1 - something has been loaded into DMA */
+
+ if (pc->buffer) {
+
+ /* Case 1a - Kernel Virtual Address */
+
+ res->buffer = USB_ADD_BYTES(pc->buffer, offset);
+ }
+ offset += pc->page_offset_buf;
+
+ /* compute destination page */
+
+ page = pc->page_start;
+
+ if (pc->ismultiseg) {
+
+ page += (offset / USB_PAGE_SIZE);
+
+ offset %= USB_PAGE_SIZE;
+
+ res->length = USB_PAGE_SIZE - offset;
+ res->physaddr = page->physaddr + offset;
+ } else {
+ res->length = (usb_size_t)-1;
+ res->physaddr = page->physaddr + offset;
+ }
+ if (!pc->buffer) {
+
+ /* Case 1b - Non Kernel Virtual Address */
+
+ res->buffer = USB_ADD_BYTES(page->buffer, offset);
+ }
+ return;
+ }
+#endif
+ /* Case 2 - Plain PIO */
+
+ res->buffer = USB_ADD_BYTES(pc->buffer, offset);
+ res->length = (usb_size_t)-1;
+#if USB_HAVE_BUSDMA
+ res->physaddr = 0;
+#endif
+}
+
+/*------------------------------------------------------------------------*
+ * usbd_copy_in - copy directly to DMA-able memory
+ *------------------------------------------------------------------------*/
+void
+usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset,
+ const void *ptr, usb_frlength_t len)
+{
+ struct usb_page_search buf_res;
+
+ while (len != 0) {
+
+ usbd_get_page(cache, offset, &buf_res);
+
+ if (buf_res.length > len) {
+ buf_res.length = len;
+ }
+ memcpy(buf_res.buffer, ptr, buf_res.length);
+
+ offset += buf_res.length;
+ len -= buf_res.length;
+ ptr = USB_ADD_BYTES(ptr, buf_res.length);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usbd_copy_out - copy directly from DMA-able memory
+ *------------------------------------------------------------------------*/
+void
+usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset,
+ void *ptr, usb_frlength_t len)
+{
+ struct usb_page_search res;
+
+ while (len != 0) {
+
+ usbd_get_page(cache, offset, &res);
+
+ if (res.length > len) {
+ res.length = len;
+ }
+ memcpy(ptr, res.buffer, res.length);
+
+ offset += res.length;
+ len -= res.length;
+ ptr = USB_ADD_BYTES(ptr, res.length);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usbd_frame_zero - zero DMA-able memory
+ *------------------------------------------------------------------------*/
+void
+usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset,
+ usb_frlength_t len)
+{
+ struct usb_page_search res;
+
+ while (len != 0) {
+
+ usbd_get_page(cache, offset, &res);
+
+ if (res.length > len) {
+ res.length = len;
+ }
+ memset(res.buffer, 0, res.length);
+
+ offset += res.length;
+ len -= res.length;
+ }
+}
+
+#if USB_HAVE_BUSDMA
+
+/*------------------------------------------------------------------------*
+ * usb_pc_common_mem_cb - BUS-DMA callback function
+ *------------------------------------------------------------------------*/
+static void
+usb_pc_common_mem_cb(struct usb_page_cache *pc,
+ void *vaddr, uint32_t length)
+{
+ struct usb_page *pg;
+ usb_size_t rem;
+ bus_size_t off;
+ bus_addr_t phys = (uintptr_t)vaddr; /* XXX */
+ uint32_t nseg;
+
+ if (length == 0)
+ nseg = 1;
+ else
+ nseg = ((length + USB_PAGE_SIZE - 1) / USB_PAGE_SIZE);
+
+ pg = pc->page_start;
+ pg->physaddr = phys & ~(USB_PAGE_SIZE - 1);
+ rem = phys & (USB_PAGE_SIZE - 1);
+ pc->page_offset_buf = rem;
+ pc->page_offset_end += rem;
+ length += rem;
+
+ for (off = USB_PAGE_SIZE; off < length; off += USB_PAGE_SIZE) {
+ pg++;
+ pg->physaddr = (phys + off) & ~(USB_PAGE_SIZE - 1);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb_pc_alloc_mem - allocate DMA'able memory
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+uint8_t
+usb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg,
+ usb_size_t size, usb_size_t align)
+{
+ void *ptr;
+ uint32_t rem;
+
+ /* allocate zeroed memory */
+
+ if (align != 1) {
+ ptr = malloc(size + align, XXX, XXX);
+ if (ptr == NULL)
+ goto error;
+
+ rem = (-((uintptr_t)ptr)) & (align - 1);
+ } else {
+ ptr = malloc(size, XXX, XXX);
+ if (ptr == NULL)
+ goto error;
+ rem = 0;
+ }
+
+ /* setup page cache */
+ pc->buffer = ((uint8_t *)ptr) + rem;
+ pc->page_start = pg;
+ pc->page_offset_buf = 0;
+ pc->page_offset_end = size;
+ pc->map = NULL;
+ pc->tag = ptr;
+ pc->ismultiseg = (align == 1);
+
+ /* compute physical address */
+ usb_pc_common_mem_cb(pc, pc->buffer, size);
+
+ usb_pc_cpu_flush(pc);
+ return (0);
+
+error:
+ /* reset most of the page cache */
+ pc->buffer = NULL;
+ pc->page_start = NULL;
+ pc->page_offset_buf = 0;
+ pc->page_offset_end = 0;
+ pc->map = NULL;
+ pc->tag = NULL;
+ return (1);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_pc_free_mem - free DMA memory
+ *
+ * This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usb_pc_free_mem(struct usb_page_cache *pc)
+{
+ if (pc != NULL && pc->buffer != NULL) {
+ free(pc->tag, XXX);
+ pc->buffer = NULL;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb_pc_load_mem - load virtual memory into DMA
+ *
+ * Return values:
+ * 0: Success
+ * Else: Error
+ *------------------------------------------------------------------------*/
+uint8_t
+usb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync)
+{
+ /* setup page cache */
+ pc->page_offset_buf = 0;
+ pc->page_offset_end = size;
+ pc->ismultiseg = 1;
+
+ mtx_assert(pc->tag_parent->mtx, MA_OWNED);
+
+ if (size > 0) {
+ /* compute physical address */
+ usb_pc_common_mem_cb(pc, pc->buffer, size);
+ }
+ if (sync == 0) {
+ /*
+ * Call callback so that refcount is decremented
+ * properly:
+ */
+ pc->tag_parent->dma_error = 0;
+ (pc->tag_parent->func) (pc->tag_parent);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_pc_cpu_invalidate - invalidate CPU cache
+ *------------------------------------------------------------------------*/
+void
+usb_pc_cpu_invalidate(struct usb_page_cache *pc)
+{
+ if (pc->page_offset_end == pc->page_offset_buf) {
+ /* nothing has been loaded into this page cache! */
+ return;
+ }
+ /* NOP */
+}
+
+/*------------------------------------------------------------------------*
+ * usb_pc_cpu_flush - flush CPU cache
+ *------------------------------------------------------------------------*/
+void
+usb_pc_cpu_flush(struct usb_page_cache *pc)
+{
+ if (pc->page_offset_end == pc->page_offset_buf) {
+ /* nothing has been loaded into this page cache! */
+ return;
+ }
+ /* NOP */
+}
+
+/*------------------------------------------------------------------------*
+ * usb_pc_dmamap_create - create a DMA map
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+uint8_t
+usb_pc_dmamap_create(struct usb_page_cache *pc, usb_size_t size)
+{
+ return (0); /* NOP, success */
+}
+
+/*------------------------------------------------------------------------*
+ * usb_pc_dmamap_destroy
+ *
+ * This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usb_pc_dmamap_destroy(struct usb_page_cache *pc)
+{
+ /* NOP */
+}
+
+/*------------------------------------------------------------------------*
+ * usb_dma_tag_setup - initialise USB DMA tags
+ *------------------------------------------------------------------------*/
+void
+usb_dma_tag_setup(struct usb_dma_parent_tag *udpt,
+ struct usb_dma_tag *udt, bus_dma_tag_t dmat,
+ struct mtx *mtx, usb_dma_callback_t *func,
+ uint8_t ndmabits, uint8_t nudt)
+{
+ memset(udpt, 0, sizeof(*udpt));
+
+ /* sanity checking */
+ if ((nudt == 0) ||
+ (ndmabits == 0) ||
+ (mtx == NULL)) {
+ /* something is corrupt */
+ return;
+ }
+ /* initialise condition variable */
+ cv_init(udpt->cv, "USB DMA CV");
+
+ /* store some information */
+ udpt->mtx = mtx;
+ udpt->func = func;
+ udpt->tag = dmat;
+ udpt->utag_first = udt;
+ udpt->utag_max = nudt;
+ udpt->dma_bits = ndmabits;
+
+ while (nudt--) {
+ memset(udt, 0, sizeof(*udt));
+ udt->tag_parent = udpt;
+ udt++;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb_bus_tag_unsetup - factored out code
+ *------------------------------------------------------------------------*/
+void
+usb_dma_tag_unsetup(struct usb_dma_parent_tag *udpt)
+{
+ struct usb_dma_tag *udt;
+ uint8_t nudt;
+
+ udt = udpt->utag_first;
+ nudt = udpt->utag_max;
+
+ while (nudt--) {
+ udt->align = 0;
+ udt++;
+ }
+
+ if (udpt->utag_max) {
+ /* destroy the condition variable */
+ cv_destroy(udpt->cv);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb_bdma_work_loop
+ *
+ * This function handles loading of virtual buffers into DMA and is
+ * only called when "dma_refcount" is zero.
+ *------------------------------------------------------------------------*/
+void
+usb_bdma_work_loop(struct usb_xfer_queue *pq)
+{
+ struct usb_xfer_root *info;
+ struct usb_xfer *xfer;
+ usb_frcount_t nframes;
+
+ xfer = pq->curr;
+ info = xfer->xroot;
+
+ mtx_assert(info->xfer_mtx, MA_OWNED);
+
+ if (xfer->error) {
+ /* some error happened */
+ USB_BUS_LOCK(info->bus);
+ usbd_transfer_done(xfer, 0);
+ USB_BUS_UNLOCK(info->bus);
+ return;
+ }
+ if (!xfer->flags_int.bdma_setup) {
+ struct usb_page *pg;
+ usb_frlength_t frlength_0;
+ uint8_t isread;
+
+ xfer->flags_int.bdma_setup = 1;
+
+ /* reset BUS-DMA load state */
+
+ info->dma_error = 0;
+
+ if (xfer->flags_int.isochronous_xfr) {
+ /* only one frame buffer */
+ nframes = 1;
+ frlength_0 = xfer->sumlen;
+ } else {
+ /* can be multiple frame buffers */
+ nframes = xfer->nframes;
+ frlength_0 = xfer->frlengths[0];
+ }
+
+ /*
+ * Set DMA direction first. This is needed to
+ * select the correct cache invalidate and cache
+ * flush operations.
+ */
+ isread = USB_GET_DATA_ISREAD(xfer);
+ pg = xfer->dma_page_ptr;
+
+ if (xfer->flags_int.control_xfr &&
+ xfer->flags_int.control_hdr) {
+ /* special case */
+ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
+ /* The device controller writes to memory */
+ xfer->frbuffers[0].isread = 1;
+ } else {
+ /* The host controller reads from memory */
+ xfer->frbuffers[0].isread = 0;
+ }
+ } else {
+ /* default case */
+ xfer->frbuffers[0].isread = isread;
+ }
+
+ /*
+ * Setup the "page_start" pointer which points to an array of
+ * USB pages where information about the physical address of a
+ * page will be stored. Also initialise the "isread" field of
+ * the USB page caches.
+ */
+ xfer->frbuffers[0].page_start = pg;
+
+ info->dma_nframes = nframes;
+ info->dma_currframe = 0;
+ info->dma_frlength_0 = frlength_0;
+
+ pg += (frlength_0 / USB_PAGE_SIZE);
+ pg += 2;
+
+ while (--nframes > 0) {
+ xfer->frbuffers[nframes].isread = isread;
+ xfer->frbuffers[nframes].page_start = pg;
+
+ pg += (xfer->frlengths[nframes] / USB_PAGE_SIZE);
+ pg += 2;
+ }
+
+ }
+ if (info->dma_error) {
+ USB_BUS_LOCK(info->bus);
+ usbd_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED);
+ USB_BUS_UNLOCK(info->bus);
+ return;
+ }
+ if (info->dma_currframe != info->dma_nframes) {
+
+ if (info->dma_currframe == 0) {
+ /* special case */
+ usb_pc_load_mem(xfer->frbuffers,
+ info->dma_frlength_0, 0);
+ } else {
+ /* default case */
+ nframes = info->dma_currframe;
+ usb_pc_load_mem(xfer->frbuffers + nframes,
+ xfer->frlengths[nframes], 0);
+ }
+
+ /* advance frame index */
+ info->dma_currframe++;
+
+ return;
+ }
+ /* go ahead */
+ usb_bdma_pre_sync(xfer);
+
+ /* start loading next USB transfer, if any */
+ usb_command_wrapper(pq, NULL);
+
+ /* finally start the hardware */
+ usbd_pipe_enter(xfer);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_bdma_done_event
+ *
+ * This function is called when the BUS-DMA has loaded virtual memory
+ * into DMA, if any.
+ *------------------------------------------------------------------------*/
+void
+usb_bdma_done_event(struct usb_dma_parent_tag *udpt)
+{
+ struct usb_xfer_root *info;
+
+ info = USB_DMATAG_TO_XROOT(udpt);
+
+ mtx_assert(info->xfer_mtx, MA_OWNED);
+
+ /* copy error */
+ info->dma_error = udpt->dma_error;
+
+ /* enter workloop again */
+ usb_command_wrapper(&info->dma_q,
+ info->dma_q.curr);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_bdma_pre_sync
+ *
+ * This function handles DMA synchronisation that must be done before
+ * an USB transfer is started.
+ *------------------------------------------------------------------------*/
+void
+usb_bdma_pre_sync(struct usb_xfer *xfer)
+{
+ struct usb_page_cache *pc;
+ usb_frcount_t nframes;
+
+ if (xfer->flags_int.isochronous_xfr) {
+ /* only one frame buffer */
+ nframes = 1;
+ } else {
+ /* can be multiple frame buffers */
+ nframes = xfer->nframes;
+ }
+
+ pc = xfer->frbuffers;
+
+ while (nframes--) {
+
+ if (pc->isread) {
+ usb_pc_cpu_invalidate(pc);
+ } else {
+ usb_pc_cpu_flush(pc);
+ }
+ pc++;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb_bdma_post_sync
+ *
+ * This function handles DMA synchronisation that must be done after
+ * an USB transfer is complete.
+ *------------------------------------------------------------------------*/
+void
+usb_bdma_post_sync(struct usb_xfer *xfer)
+{
+ struct usb_page_cache *pc;
+ usb_frcount_t nframes;
+
+ if (xfer->flags_int.isochronous_xfr) {
+ /* only one frame buffer */
+ nframes = 1;
+ } else {
+ /* can be multiple frame buffers */
+ nframes = xfer->nframes;
+ }
+
+ pc = xfer->frbuffers;
+
+ while (nframes--) {
+ if (pc->isread) {
+ usb_pc_cpu_invalidate(pc);
+ }
+ pc++;
+ }
+}
+#endif
diff --git a/stand/usb/usbcore.mk b/stand/usb/usbcore.mk
new file mode 100644
index 0000000..153a1d4
--- /dev/null
+++ b/stand/usb/usbcore.mk
@@ -0,0 +1,175 @@
+#
+# $FreeBSD$
+#
+# Copyright (c) 2013 Hans Petter Selasky.
+# Copyright (c) 2014 SRI International
+# All rights reserved.
+#
+# This software was developed by SRI International and the University of
+# Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+# ("CTSRD"), as part of the DARPA CRASH research programme.
+#
+# 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.
+#
+
+USBCOREDIR:= ${.PARSEDIR}
+S=${USBCOREDIR}/../..
+
+MACHDEP_DIRS=
+
+.if defined(HAVE_EXYNOS_EHCI)
+MACHDEP_DIRS+= ${S}/arm/samsung/exynos
+.endif
+
+.PATH: \
+ ${USBCOREDIR} \
+ ${USBCOREDIR}/storage \
+ ${S}/dev/usb \
+ ${S}/dev/usb/controller \
+ ${S}/dev/usb/serial \
+ ${S}/dev/usb/storage \
+ ${S}/dev/usb/template \
+ ${MACHDEP_DIRS}
+.undef S
+
+USB_POOL_SIZE?= 131072
+
+CFLAGS+= -DUSB_MSCTEST_BULK_SIZE=65536
+CFLAGS+= -DUSB_POOL_SIZE=${USB_POOL_SIZE}
+
+
+#
+# BUSDMA implementation
+#
+SRCS+= usb_busdma_loader.c
+
+#
+# USB controller drivers
+#
+
+KSRCS+= usb_controller.c
+
+.if defined(HAVE_AT91DCI)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"at91dci\""
+KSRCS+= at91dci.c
+.endif
+
+.if defined(HAVE_ATMEGADCI)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"atmegadci\""
+KSRCS+= atmegadci.c
+.endif
+
+.if defined(HAVE_AVR32DCI)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"avr32dci\""
+KSRCS+= avr32dci.c
+.endif
+
+.if defined(HAVE_DWCOTG)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"dwcotg\""
+KSRCS+= dwcotg.c
+.endif
+
+.if defined(HAVE_MUSBOTG)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"musbotg\""
+KSRCS+= musbotg.c
+.endif
+
+.if defined(HAVE_EHCI)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"ehci\""
+KSRCS+= ehci.c
+.endif
+
+.if defined(HAVE_EXYNOS_EHCI)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"combiner\", \"pad\", \"ehci\""
+KSRCS+= ehci.c
+KSRCS+= exynos5_combiner.c
+KSRCS+= exynos5_pad.c
+KSRCS+= exynos5_ehci.c
+.endif
+
+.if defined(HAVE_OHCI)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"ohci\""
+KSRCS+= ohci.c
+.endif
+
+.if defined(HAVE_UHCI)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"uhci\""
+KSRCS+= uhci.c
+.endif
+
+.if defined(HAVE_XHCI)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"xhci\""
+KSRCS+= xhci.c
+.endif
+
+.if defined(HAVE_USS820DCI)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"uss820dci\""
+KSRCS+= uss820dci.c
+.endif
+
+.if defined(HAVE_SAF1761OTG)
+CFLAGS += -DUSB_PCI_PROBE_LIST="\"saf1761otg\""
+CFLAGS += -DUSB_PCI_MEMORY_ADDRESS=0x900000007f100000ULL
+CFLAGS += -DUSB_PCI_MEMORY_SIZE=0x40000U
+KSRCS+= saf1761_otg.c
+KSRCS+= saf1761_otg_boot.c
+.endif
+
+#
+# USB core and templates
+#
+KSRCS+= usb_core.c
+KSRCS+= usb_debug.c
+KSRCS+= usb_device.c
+KSRCS+= usb_dynamic.c
+KSRCS+= usb_error.c
+KSRCS+= usb_handle_request.c
+KSRCS+= usb_hid.c
+KSRCS+= usb_hub.c
+KSRCS+= usb_lookup.c
+KSRCS+= usb_msctest.c
+KSRCS+= usb_parse.c
+KSRCS+= usb_request.c
+KSRCS+= usb_transfer.c
+KSRCS+= usb_util.c
+KSRCS+= usb_template.c
+KSRCS+= usb_template_cdce.c
+KSRCS+= usb_template_msc.c
+KSRCS+= usb_template_mtp.c
+KSRCS+= usb_template_modem.c
+KSRCS+= usb_template_mouse.c
+KSRCS+= usb_template_kbd.c
+KSRCS+= usb_template_audio.c
+KSRCS+= usb_template_phone.c
+KSRCS+= usb_template_serialnet.c
+KSRCS+= usb_template_midi.c
+
+#
+# USB mass storage support
+#
+SRCS+= umass_common.c
+
+.if defined(HAVE_UMASS_LOADER)
+CFLAGS+= -I${.CURDIR}/../common
+SRCS+= umass_loader.c
+.endif
+
diff --git a/stand/userboot/Makefile b/stand/userboot/Makefile
new file mode 100644
index 0000000..0a53a21
--- /dev/null
+++ b/stand/userboot/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+SUBDIR= test userboot
+
+.include <bsd.subdir.mk>
+
diff --git a/stand/userboot/Makefile.inc b/stand/userboot/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/stand/userboot/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/stand/userboot/test/Makefile b/stand/userboot/test/Makefile
new file mode 100644
index 0000000..1bc6679
--- /dev/null
+++ b/stand/userboot/test/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+
+MAN=
+
+.include <bsd.init.mk>
+MK_SSP= no
+
+PROG= test
+INTERNALPROG=
+
+CFLAGS+= -I${BOOTSRC}/userboot
+
+.include <bsd.prog.mk>
diff --git a/stand/userboot/test/Makefile.depend b/stand/userboot/test/Makefile.depend
new file mode 100644
index 0000000..3646e2e
--- /dev/null
+++ b/stand/userboot/test/Makefile.depend
@@ -0,0 +1,18 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ gnu/lib/libgcc \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/userboot/test/test.c b/stand/userboot/test/test.c
new file mode 100644
index 0000000..6c885c3
--- /dev/null
+++ b/stand/userboot/test/test.c
@@ -0,0 +1,475 @@
+/*-
+ * Copyright (c) 2011 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/disk.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <userboot.h>
+
+char *host_base = NULL;
+struct termios term, oldterm;
+char *image;
+size_t image_size;
+int disk_fd = -1;
+
+uint64_t regs[16];
+uint64_t pc;
+
+void test_exit(void *arg, int v);
+
+/*
+ * Console i/o
+ */
+
+void
+test_putc(void *arg, int ch)
+{
+ char c = ch;
+
+ write(1, &c, 1);
+}
+
+int
+test_getc(void *arg)
+{
+ char c;
+
+ if (read(0, &c, 1) == 1)
+ return c;
+ return -1;
+}
+
+int
+test_poll(void *arg)
+{
+ int n;
+
+ if (ioctl(0, FIONREAD, &n) >= 0)
+ return (n > 0);
+ return (0);
+}
+
+/*
+ * Host filesystem i/o
+ */
+
+struct test_file {
+ int tf_isdir;
+ size_t tf_size;
+ struct stat tf_stat;
+ union {
+ int fd;
+ DIR *dir;
+ } tf_u;
+};
+
+int
+test_open(void *arg, const char *filename, void **h_return)
+{
+ struct stat st;
+ struct test_file *tf;
+ char path[PATH_MAX];
+
+ if (!host_base)
+ return (ENOENT);
+
+ strlcpy(path, host_base, PATH_MAX);
+ if (path[strlen(path) - 1] == '/')
+ path[strlen(path) - 1] = 0;
+ strlcat(path, filename, PATH_MAX);
+ tf = malloc(sizeof(struct test_file));
+ if (stat(path, &tf->tf_stat) < 0) {
+ free(tf);
+ return (errno);
+ }
+
+ tf->tf_size = st.st_size;
+ if (S_ISDIR(tf->tf_stat.st_mode)) {
+ tf->tf_isdir = 1;
+ tf->tf_u.dir = opendir(path);
+ if (!tf->tf_u.dir)
+ goto out;
+ *h_return = tf;
+ return (0);
+ }
+ if (S_ISREG(tf->tf_stat.st_mode)) {
+ tf->tf_isdir = 0;
+ tf->tf_u.fd = open(path, O_RDONLY);
+ if (tf->tf_u.fd < 0)
+ goto out;
+ *h_return = tf;
+ return (0);
+ }
+
+out:
+ free(tf);
+ return (EINVAL);
+}
+
+int
+test_close(void *arg, void *h)
+{
+ struct test_file *tf = h;
+
+ if (tf->tf_isdir)
+ closedir(tf->tf_u.dir);
+ else
+ close(tf->tf_u.fd);
+ free(tf);
+
+ return (0);
+}
+
+int
+test_isdir(void *arg, void *h)
+{
+ struct test_file *tf = h;
+
+ return (tf->tf_isdir);
+}
+
+int
+test_read(void *arg, void *h, void *dst, size_t size, size_t *resid_return)
+{
+ struct test_file *tf = h;
+ ssize_t sz;
+
+ if (tf->tf_isdir)
+ return (EINVAL);
+ sz = read(tf->tf_u.fd, dst, size);
+ if (sz < 0)
+ return (EINVAL);
+ *resid_return = size - sz;
+ return (0);
+}
+
+int
+test_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return,
+ size_t *namelen_return, char *name)
+{
+ struct test_file *tf = h;
+ struct dirent *dp;
+
+ if (!tf->tf_isdir)
+ return (EINVAL);
+
+ dp = readdir(tf->tf_u.dir);
+ if (!dp)
+ return (ENOENT);
+
+ /*
+ * Note: d_namlen is in the range 0..255 and therefore less
+ * than PATH_MAX so we don't need to test before copying.
+ */
+ *fileno_return = dp->d_fileno;
+ *type_return = dp->d_type;
+ *namelen_return = dp->d_namlen;
+ memcpy(name, dp->d_name, dp->d_namlen);
+ name[dp->d_namlen] = 0;
+
+ return (0);
+}
+
+int
+test_seek(void *arg, void *h, uint64_t offset, int whence)
+{
+ struct test_file *tf = h;
+
+ if (tf->tf_isdir)
+ return (EINVAL);
+ if (lseek(tf->tf_u.fd, offset, whence) < 0)
+ return (errno);
+ return (0);
+}
+
+int
+test_stat(void *arg, void *h, int *mode_return, int *uid_return, int *gid_return,
+ uint64_t *size_return)
+{
+ struct test_file *tf = h;
+
+ *mode_return = tf->tf_stat.st_mode;
+ *uid_return = tf->tf_stat.st_uid;
+ *gid_return = tf->tf_stat.st_gid;
+ *size_return = tf->tf_stat.st_size;
+ return (0);
+}
+
+/*
+ * Disk image i/o
+ */
+
+int
+test_diskread(void *arg, int unit, uint64_t offset, void *dst, size_t size,
+ size_t *resid_return)
+{
+ ssize_t n;
+
+ if (unit != 0 || disk_fd == -1)
+ return (EIO);
+ n = pread(disk_fd, dst, size, offset);
+ if (n < 0)
+ return (errno);
+ *resid_return = size - n;
+ return (0);
+}
+
+int
+test_diskioctl(void *arg, int unit, u_long cmd, void *data)
+{
+ struct stat sb;
+
+ if (unit != 0 || disk_fd == -1)
+ return (EBADF);
+ switch (cmd) {
+ case DIOCGSECTORSIZE:
+ *(u_int *)data = 512;
+ break;
+ case DIOCGMEDIASIZE:
+ if (fstat(disk_fd, &sb) == 0)
+ *(off_t *)data = sb.st_size;
+ else
+ return (ENOTTY);
+ break;
+ default:
+ return (ENOTTY);
+ }
+ return (0);
+}
+
+/*
+ * Guest virtual machine i/o
+ *
+ * Note: guest addresses are kernel virtual
+ */
+
+int
+test_copyin(void *arg, const void *from, uint64_t to, size_t size)
+{
+
+ to &= 0x7fffffff;
+ if (to > image_size)
+ return (EFAULT);
+ if (to + size > image_size)
+ size = image_size - to;
+ memcpy(&image[to], from, size);
+ return(0);
+}
+
+int
+test_copyout(void *arg, uint64_t from, void *to, size_t size)
+{
+
+ from &= 0x7fffffff;
+ if (from > image_size)
+ return (EFAULT);
+ if (from + size > image_size)
+ size = image_size - from;
+ memcpy(to, &image[from], size);
+ return(0);
+}
+
+void
+test_setreg(void *arg, int r, uint64_t v)
+{
+
+ if (r < 0 || r >= 16)
+ return;
+ regs[r] = v;
+}
+
+void
+test_setmsr(void *arg, int r, uint64_t v)
+{
+}
+
+void
+test_setcr(void *arg, int r, uint64_t v)
+{
+}
+
+void
+test_setgdt(void *arg, uint64_t v, size_t sz)
+{
+}
+
+void
+test_exec(void *arg, uint64_t pc)
+{
+ printf("Execute at 0x%"PRIu64"\n", pc);
+ test_exit(arg, 0);
+}
+
+/*
+ * Misc
+ */
+
+void
+test_delay(void *arg, int usec)
+{
+
+ usleep(usec);
+}
+
+void
+test_exit(void *arg, int v)
+{
+
+ tcsetattr(0, TCSAFLUSH, &oldterm);
+ exit(v);
+}
+
+void
+test_getmem(void *arg, uint64_t *lowmem, uint64_t *highmem)
+{
+
+ *lowmem = 128*1024*1024;
+ *highmem = 0;
+}
+
+char *
+test_getenv(void *arg, int idx)
+{
+ static char *vars[] = {
+ "foo=bar",
+ "bar=barbar",
+ NULL
+ };
+
+ return (vars[idx]);
+}
+
+struct loader_callbacks cb = {
+ .putc = test_putc,
+ .getc = test_getc,
+ .poll = test_poll,
+
+ .open = test_open,
+ .close = test_close,
+ .isdir = test_isdir,
+ .read = test_read,
+ .readdir = test_readdir,
+ .seek = test_seek,
+ .stat = test_stat,
+
+ .diskread = test_diskread,
+ .diskioctl = test_diskioctl,
+
+ .copyin = test_copyin,
+ .copyout = test_copyout,
+ .setreg = test_setreg,
+ .setmsr = test_setmsr,
+ .setcr = test_setcr,
+ .setgdt = test_setgdt,
+ .exec = test_exec,
+
+ .delay = test_delay,
+ .exit = test_exit,
+ .getmem = test_getmem,
+
+ .getenv = test_getenv,
+};
+
+void
+usage()
+{
+
+ printf("usage: [-b <userboot shared object>] [-d <disk image path>] [-h <host filesystem path>\n");
+ exit(1);
+}
+
+int
+main(int argc, char** argv)
+{
+ void *h;
+ void (*func)(struct loader_callbacks *, void *, int, int);
+ int opt;
+ char *disk_image = NULL;
+ const char *userboot_obj = "/boot/userboot.so";
+
+ while ((opt = getopt(argc, argv, "b:d:h:")) != -1) {
+ switch (opt) {
+ case 'b':
+ userboot_obj = optarg;
+ break;
+
+ case 'd':
+ disk_image = optarg;
+ break;
+
+ case 'h':
+ host_base = optarg;
+ break;
+
+ case '?':
+ usage();
+ }
+ }
+
+ h = dlopen(userboot_obj, RTLD_LOCAL);
+ if (!h) {
+ printf("%s\n", dlerror());
+ return (1);
+ }
+ func = dlsym(h, "loader_main");
+ if (!func) {
+ printf("%s\n", dlerror());
+ return (1);
+ }
+
+ image_size = 128*1024*1024;
+ image = malloc(image_size);
+ if (disk_image) {
+ disk_fd = open(disk_image, O_RDONLY);
+ if (disk_fd < 0)
+ err(1, "Can't open disk image '%s'", disk_image);
+ }
+
+ tcgetattr(0, &term);
+ oldterm = term;
+ term.c_iflag &= ~(ICRNL);
+ term.c_lflag &= ~(ICANON|ECHO);
+ tcsetattr(0, TCSAFLUSH, &term);
+
+ func(&cb, NULL, USERBOOT_VERSION_3, disk_fd >= 0);
+}
diff --git a/stand/userboot/userboot.h b/stand/userboot/userboot.h
new file mode 100644
index 0000000..2e4a4b4
--- /dev/null
+++ b/stand/userboot/userboot.h
@@ -0,0 +1,213 @@
+/*-
+ * Copyright (c) 2011 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * USERBOOT interface versions
+ */
+#define USERBOOT_VERSION_1 1
+#define USERBOOT_VERSION_2 2
+#define USERBOOT_VERSION_3 3
+
+/*
+ * Version 4 added more generic callbacks for setting up
+ * registers and descriptors. The callback structure is
+ * backward compatible (new callbacks have been added at
+ * the tail end).
+ */
+#define USERBOOT_VERSION_4 4
+
+/*
+ * Exit codes from the loader
+ */
+#define USERBOOT_EXIT_QUIT 1
+#define USERBOOT_EXIT_REBOOT 2
+
+struct loader_callbacks {
+ /*
+ * Console i/o
+ */
+
+ /*
+ * Wait until a key is pressed on the console and then return it
+ */
+ int (*getc)(void *arg);
+
+ /*
+ * Write the character ch to the console
+ */
+ void (*putc)(void *arg, int ch);
+
+ /*
+ * Return non-zero if a key can be read from the console
+ */
+ int (*poll)(void *arg);
+
+ /*
+ * Host filesystem i/o
+ */
+
+ /*
+ * Open a file in the host filesystem
+ */
+ int (*open)(void *arg, const char *filename, void **h_return);
+
+ /*
+ * Close a file
+ */
+ int (*close)(void *arg, void *h);
+
+ /*
+ * Return non-zero if the file is a directory
+ */
+ int (*isdir)(void *arg, void *h);
+
+ /*
+ * Read size bytes from a file. The number of bytes remaining
+ * in dst after reading is returned in *resid_return
+ */
+ int (*read)(void *arg, void *h, void *dst, size_t size,
+ size_t *resid_return);
+
+ /*
+ * Read an entry from a directory. The entry's inode number is
+ * returned in *fileno_return, its type in *type_return and
+ * the name length in *namelen_return. The name itself is
+ * copied to the buffer name which must be at least PATH_MAX
+ * in size.
+ */
+ int (*readdir)(void *arg, void *h, uint32_t *fileno_return,
+ uint8_t *type_return, size_t *namelen_return, char *name);
+
+ /*
+ * Seek to a location within an open file
+ */
+ int (*seek)(void *arg, void *h, uint64_t offset,
+ int whence);
+
+ /*
+ * Return some stat(2) related information about the file
+ */
+ int (*stat)(void *arg, void *h, int *mode_return,
+ int *uid_return, int *gid_return, uint64_t *size_return);
+
+ /*
+ * Disk image i/o
+ */
+
+ /*
+ * Read from a disk image at the given offset
+ */
+ int (*diskread)(void *arg, int unit, uint64_t offset,
+ void *dst, size_t size, size_t *resid_return);
+
+ /*
+ * Guest virtual machine i/o
+ */
+
+ /*
+ * Copy to the guest address space
+ */
+ int (*copyin)(void *arg, const void *from,
+ uint64_t to, size_t size);
+
+ /*
+ * Copy from the guest address space
+ */
+ int (*copyout)(void *arg, uint64_t from,
+ void *to, size_t size);
+
+ /*
+ * Set a guest register value
+ */
+ void (*setreg)(void *arg, int, uint64_t);
+
+ /*
+ * Set a guest MSR value
+ */
+ void (*setmsr)(void *arg, int, uint64_t);
+
+ /*
+ * Set a guest CR value
+ */
+ void (*setcr)(void *arg, int, uint64_t);
+
+ /*
+ * Set the guest GDT address
+ */
+ void (*setgdt)(void *arg, uint64_t, size_t);
+
+ /*
+ * Transfer control to the guest at the given address
+ */
+ void (*exec)(void *arg, uint64_t pc);
+
+ /*
+ * Misc
+ */
+
+ /*
+ * Sleep for usec microseconds
+ */
+ void (*delay)(void *arg, int usec);
+
+ /*
+ * Exit with the given exit code
+ */
+ void (*exit)(void *arg, int v);
+
+ /*
+ * Return guest physical memory map details
+ */
+ void (*getmem)(void *arg, uint64_t *lowmem,
+ uint64_t *highmem);
+
+ /*
+ * ioctl interface to the disk device
+ */
+ int (*diskioctl)(void *arg, int unit, u_long cmd,
+ void *data);
+
+ /*
+ * Returns an environment variable in the form "name=value".
+ *
+ * If there are no more variables that need to be set in the
+ * loader environment then return NULL.
+ *
+ * 'num' is used as a handle for the callback to identify which
+ * environment variable to return next. It will begin at 0 and
+ * each invocation will add 1 to the previous value of 'num'.
+ */
+ char * (*getenv)(void *arg, int num);
+
+ /*
+ * Version 4 additions.
+ */
+ int (*vm_set_register)(void *arg, int vcpu, int reg, uint64_t val);
+ int (*vm_set_desc)(void *arg, int vcpu, int reg, uint64_t base,
+ u_int limit, u_int access);
+};
diff --git a/stand/userboot/userboot/Makefile b/stand/userboot/userboot/Makefile
new file mode 100644
index 0000000..cbada82
--- /dev/null
+++ b/stand/userboot/userboot/Makefile
@@ -0,0 +1,59 @@
+# $FreeBSD$
+
+MAN=
+
+LOADER_MSDOS_SUPPORT?= yes
+LOADER_UFS_SUPPORT?= yes
+LOADER_CD9660_SUPPORT?= no
+LOADER_EXT2FS_SUPPORT?= no
+
+.include <bsd.init.mk>
+
+MK_SSP= no
+
+SHLIB_NAME= userboot.so
+MK_CTF= no
+STRIP=
+LIBDIR= /boot
+
+SRCS= autoload.c
+SRCS+= bcache.c
+SRCS+= biossmap.c
+SRCS+= bootinfo.c
+SRCS+= bootinfo32.c
+SRCS+= bootinfo64.c
+SRCS+= conf.c
+SRCS+= console.c
+SRCS+= copy.c
+SRCS+= devicename.c
+SRCS+= elf32_freebsd.c
+SRCS+= elf64_freebsd.c
+SRCS+= host.c
+SRCS+= main.c
+SRCS+= userboot_cons.c
+SRCS+= userboot_disk.c
+SRCS+= vers.c
+
+CFLAGS+= -Wall
+CFLAGS+= -I${BOOTSRC}/userboot
+CFLAGS+= -ffreestanding
+
+CWARNFLAGS.main.c += -Wno-implicit-function-declaration
+
+LDFLAGS+= -nostdlib -Wl,-Bsymbolic
+
+NEWVERSWHAT= "User boot" ${MACHINE_CPUARCH}
+
+.if ${MK_ZFS} != "no"
+CFLAGS+= -DUSERBOOT_ZFS_SUPPORT
+LIBZFSBOOT= ${BOOTOBJ}/zfs/libzfsboot.a
+.endif
+
+# Always add MI sources
+HELP_FILES= # Disable
+.include "${BOOTSRC}/loader.mk"
+CFLAGS+= -I.
+DPADD+= ${LIBFICL} ${LIBZFSBOOT} ${LIBSA}
+LDADD+= ${LIBFICL} ${LIBZFSBOOT} ${LIBSA}
+
+.include <bsd.lib.mk>
diff --git a/stand/userboot/userboot/Makefile.depend b/stand/userboot/userboot/Makefile.depend
new file mode 100644
index 0000000..871417f
--- /dev/null
+++ b/stand/userboot/userboot/Makefile.depend
@@ -0,0 +1,16 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ sys/boot/userboot/ficl \
+ sys/boot/userboot/libstand \
+ sys/boot/userboot/zfs \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/userboot/userboot/autoload.c b/stand/userboot/userboot/autoload.c
new file mode 100644
index 0000000..a86afcf
--- /dev/null
+++ b/stand/userboot/userboot/autoload.c
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2011 Google, Inc.
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+int
+userboot_autoload(void)
+{
+
+ return (0);
+}
diff --git a/stand/userboot/userboot/biossmap.c b/stand/userboot/userboot/biossmap.c
new file mode 100644
index 0000000..9e556be
--- /dev/null
+++ b/stand/userboot/userboot/biossmap.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <machine/pc/bios.h>
+#include <machine/metadata.h>
+
+#include "bootstrap.h"
+#include "libuserboot.h"
+
+#define GB (1024UL * 1024 * 1024)
+
+void
+bios_addsmapdata(struct preloaded_file *kfp)
+{
+ uint64_t lowmem, highmem;
+ int smapnum, len;
+ struct bios_smap smap[3], *sm;
+
+ CALLBACK(getmem, &lowmem, &highmem);
+
+ sm = &smap[0];
+
+ sm->base = 0; /* base memory */
+ sm->length = 640 * 1024;
+ sm->type = SMAP_TYPE_MEMORY;
+ sm++;
+
+ sm->base = 0x100000; /* extended memory */
+ sm->length = lowmem - 0x100000;
+ sm->type = SMAP_TYPE_MEMORY;
+ sm++;
+
+ smapnum = 2;
+
+ if (highmem != 0) {
+ sm->base = 4 * GB;
+ sm->length = highmem;
+ sm->type = SMAP_TYPE_MEMORY;
+ smapnum++;
+ }
+
+ len = smapnum * sizeof(struct bios_smap);
+ file_addmetadata(kfp, MODINFOMD_SMAP, len, &smap[0]);
+}
diff --git a/stand/userboot/userboot/bootinfo.c b/stand/userboot/userboot/bootinfo.c
new file mode 100644
index 0000000..289ca07
--- /dev/null
+++ b/stand/userboot/userboot/bootinfo.c
@@ -0,0 +1,170 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/boot.h>
+
+#include "bootstrap.h"
+#include "libuserboot.h"
+
+int
+bi_getboothowto(char *kargs)
+{
+ char *cp;
+ char *curpos, *next, *string;
+ int howto;
+ int active;
+ int i;
+ int vidconsole;
+
+ /* Parse kargs */
+ howto = 0;
+ if (kargs != NULL) {
+ cp = kargs;
+ active = 0;
+ while (*cp != 0) {
+ if (!active && (*cp == '-')) {
+ active = 1;
+ } else if (active)
+ switch (*cp) {
+ case 'a':
+ howto |= RB_ASKNAME;
+ break;
+ case 'C':
+ howto |= RB_CDROM;
+ break;
+ case 'd':
+ howto |= RB_KDB;
+ break;
+ case 'D':
+ howto |= RB_MULTIPLE;
+ break;
+ case 'm':
+ howto |= RB_MUTE;
+ break;
+ case 'g':
+ howto |= RB_GDB;
+ break;
+ case 'h':
+ howto |= RB_SERIAL;
+ break;
+ case 'p':
+ howto |= RB_PAUSE;
+ break;
+ case 'r':
+ howto |= RB_DFLTROOT;
+ break;
+ case 's':
+ howto |= RB_SINGLE;
+ break;
+ case 'v':
+ howto |= RB_VERBOSE;
+ break;
+ default:
+ active = 0;
+ break;
+ }
+ cp++;
+ }
+ }
+ /* get equivalents from the environment */
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+
+ /* Enable selected consoles */
+ string = next = strdup(getenv("console"));
+ vidconsole = 0;
+ while (next != NULL) {
+ curpos = strsep(&next, " ,");
+ if (*curpos == '\0')
+ continue;
+ if (!strcmp(curpos, "vidconsole"))
+ vidconsole = 1;
+ else if (!strcmp(curpos, "comconsole"))
+ howto |= RB_SERIAL;
+ else if (!strcmp(curpos, "nullconsole"))
+ howto |= RB_MUTE;
+ }
+
+ if (vidconsole && (howto & RB_SERIAL))
+ howto |= RB_MULTIPLE;
+
+ /*
+ * XXX: Note that until the kernel is ready to respect multiple consoles
+ * for the boot messages, the first named console is the primary console
+ */
+ if (!strcmp(string, "vidconsole"))
+ howto &= ~RB_SERIAL;
+
+ free(string);
+
+ return(howto);
+}
+
+void
+bi_setboothowto(int howto)
+{
+ int i;
+
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (howto & howto_names[i].mask)
+ setenv(howto_names[i].ev, "YES", 1);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+vm_offset_t
+bi_copyenv(vm_offset_t addr)
+{
+ struct env_var *ep;
+
+ /* traverse the environment */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ CALLBACK(copyin, ep->ev_name, addr, strlen(ep->ev_name));
+ addr += strlen(ep->ev_name);
+ CALLBACK(copyin, "=", addr, 1);
+ addr++;
+ if (ep->ev_value != NULL) {
+ CALLBACK(copyin, ep->ev_value, addr, strlen(ep->ev_value));
+ addr += strlen(ep->ev_value);
+ }
+ CALLBACK(copyin, "", addr, 1);
+ addr++;
+ }
+ CALLBACK(copyin, "", addr, 1);
+ addr++;
+ return(addr);
+}
diff --git a/stand/userboot/userboot/bootinfo32.c b/stand/userboot/userboot/bootinfo32.c
new file mode 100644
index 0000000..6498461
--- /dev/null
+++ b/stand/userboot/userboot/bootinfo32.c
@@ -0,0 +1,262 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <i386/include/bootinfo.h>
+
+#include "bootstrap.h"
+#include "libuserboot.h"
+
+static struct bootinfo bi;
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ CALLBACK(copyin, &x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c); \
+ if (c) \
+ CALLBACK(copyin, s, a, strlen(s) + 1); \
+ a += roundup(strlen(s) + 1, sizeof(uint32_t));\
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ CALLBACK(copyin, &s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(uint32_t)); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c); \
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ CALLBACK(copyin, mm->md_data, a, mm->md_size); \
+ a += roundup(mm->md_size, sizeof(uint32_t));\
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+static vm_offset_t
+bi_copymodules32(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ int c;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must come first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ MOD_ADDR(addr, fp->f_addr, c);
+ MOD_SIZE(addr, fp->f_size, c);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next)
+ if (!(md->md_type & MODINFOMD_NOCOPY))
+ MOD_METADATA(addr, md, c);
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Load the information expected by an i386 kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The 'bootinfo' struct is constructed, and copied into the kernel space.
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernendp)
+{
+ struct preloaded_file *xp, *kfp;
+ struct i386_devdesc *rootdev;
+ struct file_metadata *md;
+ vm_offset_t addr;
+ vm_offset_t kernend;
+ vm_offset_t envp;
+ vm_offset_t size;
+ vm_offset_t ssym, esym;
+ char *rootdevname;
+ int bootdevnr, howto;
+ char *kernelname;
+ const char *kernelpath;
+ uint64_t lowmem, highmem;
+
+ howto = bi_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied device
+ * This should perhaps go to MI code and/or have $rootdev tested/set by
+ * MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ userboot_getdev((void **)(&rootdev), rootdevname, NULL);
+ if (rootdev == NULL) { /* bad $rootdev/$currdev */
+ printf("can't determine root device\n");
+ return(EINVAL);
+ }
+
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(userboot_fmtdev((void *)rootdev));
+
+ bootdevnr = 0;
+#if 0
+ if (bootdevnr == -1) {
+ printf("root device %s invalid\n", i386_fmtdev(rootdev));
+ return (EINVAL);
+ }
+#endif
+ free(rootdev);
+
+ /* find the last module in the chain */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* copy our environment */
+ envp = addr;
+ addr = bi_copyenv(addr);
+
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf32 kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ kernend = 0; /* fill it in later */
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ bios_addsmapdata(kfp);
+
+ /* Figure out the size and location of the metadata */
+ *modulep = addr;
+ size = bi_copymodules32(0);
+ kernend = roundup(addr + size, PAGE_SIZE);
+ *kernendp = kernend;
+
+ /* patch MODINFOMD_KERNEND */
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ bcopy(&kernend, md->md_data, sizeof kernend);
+
+ /* copy module list and metadata */
+ (void)bi_copymodules32(addr);
+
+ ssym = esym = 0;
+ md = file_findmetadata(kfp, MODINFOMD_SSYM);
+ if (md != NULL)
+ ssym = *((vm_offset_t *)&(md->md_data));
+ md = file_findmetadata(kfp, MODINFOMD_ESYM);
+ if (md != NULL)
+ esym = *((vm_offset_t *)&(md->md_data));
+ if (ssym == 0 || esym == 0)
+ ssym = esym = 0; /* sanity */
+
+ /* legacy bootinfo structure */
+ kernelname = getenv("kernelname");
+ userboot_getdev(NULL, kernelname, &kernelpath);
+ bi.bi_version = BOOTINFO_VERSION;
+ bi.bi_kernelname = 0; /* XXX char * -> kernel name */
+ bi.bi_nfs_diskless = 0; /* struct nfs_diskless * */
+ bi.bi_n_bios_used = 0; /* XXX would have to hook biosdisk driver for these */
+#if 0
+ for (i = 0; i < N_BIOS_GEOM; i++)
+ bi.bi_bios_geom[i] = bd_getbigeom(i);
+#endif
+ bi.bi_size = sizeof(bi);
+ CALLBACK(getmem, &lowmem, &highmem);
+ bi.bi_memsizes_valid = 1;
+ bi.bi_basemem = 640;
+ bi.bi_extmem = (lowmem - 0x100000) / 1024;
+ bi.bi_envp = envp;
+ bi.bi_modulep = *modulep;
+ bi.bi_kernend = kernend;
+ bi.bi_symtab = ssym; /* XXX this is only the primary kernel symtab */
+ bi.bi_esymtab = esym;
+
+ /*
+ * Copy the legacy bootinfo and kernel name to the guest at 0x2000
+ */
+ bi.bi_kernelname = 0x2000 + sizeof(bi);
+ CALLBACK(copyin, &bi, 0x2000, sizeof(bi));
+ CALLBACK(copyin, kernelname, 0x2000 + sizeof(bi), strlen(kernelname) + 1);
+
+ /* legacy boot arguments */
+ *howtop = howto | RB_BOOTINFO;
+ *bootdevp = bootdevnr;
+ *bip = 0x2000;
+
+ return(0);
+}
diff --git a/stand/userboot/userboot/bootinfo64.c b/stand/userboot/userboot/bootinfo64.c
new file mode 100644
index 0000000..10ff133
--- /dev/null
+++ b/stand/userboot/userboot/bootinfo64.c
@@ -0,0 +1,257 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <i386/include/bootinfo.h>
+#include <machine/cpufunc.h>
+#include <machine/psl.h>
+#include <machine/specialreg.h>
+
+#include "bootstrap.h"
+#include "libuserboot.h"
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ CALLBACK(copyin, &x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c); \
+ if (c) \
+ CALLBACK(copyin, s, a, strlen(s) + 1); \
+ a += roundup(strlen(s) + 1, sizeof(u_int64_t));\
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ CALLBACK(copyin, &s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(u_int64_t)); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c); \
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ CALLBACK(copyin, mm->md_data, a, mm->md_size); \
+ a += roundup(mm->md_size, sizeof(u_int64_t));\
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+static vm_offset_t
+bi_copymodules64(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ int c;
+ u_int64_t v;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must come first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ v = fp->f_addr;
+ MOD_ADDR(addr, v, c);
+ v = fp->f_size;
+ MOD_SIZE(addr, v, c);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next)
+ if (!(md->md_type & MODINFOMD_NOCOPY))
+ MOD_METADATA(addr, md, c);
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Check to see if this CPU supports long mode.
+ */
+static int
+bi_checkcpu(void)
+{
+#if 0
+ char *cpu_vendor;
+ int vendor[3];
+ int eflags, regs[4];
+
+ /* Check for presence of "cpuid". */
+ eflags = read_eflags();
+ write_eflags(eflags ^ PSL_ID);
+ if (!((eflags ^ read_eflags()) & PSL_ID))
+ return (0);
+
+ /* Fetch the vendor string. */
+ do_cpuid(0, regs);
+ vendor[0] = regs[1];
+ vendor[1] = regs[3];
+ vendor[2] = regs[2];
+ cpu_vendor = (char *)vendor;
+
+ /* Check for vendors that support AMD features. */
+ if (strncmp(cpu_vendor, INTEL_VENDOR_ID, 12) != 0 &&
+ strncmp(cpu_vendor, AMD_VENDOR_ID, 12) != 0 &&
+ strncmp(cpu_vendor, CENTAUR_VENDOR_ID, 12) != 0)
+ return (0);
+
+ /* Has to support AMD features. */
+ do_cpuid(0x80000000, regs);
+ if (!(regs[0] >= 0x80000001))
+ return (0);
+
+ /* Check for long mode. */
+ do_cpuid(0x80000001, regs);
+ return (regs[3] & AMDID_LM);
+#else
+ return (1);
+#endif
+}
+
+/*
+ * Load the information expected by an amd64 kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The 'bootinfo' struct is constructed, and copied into the kernel space.
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernendp)
+{
+ struct preloaded_file *xp, *kfp;
+ struct userboot_devdesc *rootdev;
+ struct file_metadata *md;
+ vm_offset_t addr;
+ u_int64_t kernend;
+ u_int64_t envp;
+ vm_offset_t size;
+ char *rootdevname;
+ int howto;
+
+ if (!bi_checkcpu()) {
+ printf("CPU doesn't support long mode\n");
+ return (EINVAL);
+ }
+
+ howto = bi_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied device
+ * This should perhaps go to MI code and/or have $rootdev tested/set by
+ * MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ userboot_getdev((void **)(&rootdev), rootdevname, NULL);
+ if (rootdev == NULL) { /* bad $rootdev/$currdev */
+ printf("can't determine root device\n");
+ return(EINVAL);
+ }
+
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(userboot_fmtdev((void *)rootdev));
+
+ /* find the last module in the chain */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* copy our environment */
+ envp = addr;
+ addr = bi_copyenv(addr);
+
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf64 kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ kernend = 0; /* fill it in later */
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ bios_addsmapdata(kfp);
+
+ /* Figure out the size and location of the metadata */
+ *modulep = addr;
+ size = bi_copymodules64(0);
+ kernend = roundup(addr + size, PAGE_SIZE);
+ *kernendp = kernend;
+
+ /* patch MODINFOMD_KERNEND */
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ bcopy(&kernend, md->md_data, sizeof kernend);
+
+ /* copy module list and metadata */
+ (void)bi_copymodules64(addr);
+
+ return(0);
+}
diff --git a/stand/userboot/userboot/conf.c b/stand/userboot/userboot/conf.c
new file mode 100644
index 0000000..b8daa73
--- /dev/null
+++ b/stand/userboot/userboot/conf.c
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 1997
+ * Matthias Drochner. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by Matthias Drochner.
+ * 4. 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.
+ *
+ * $NetBSD: conf.c,v 1.2 1997/03/22 09:03:29 thorpej Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+
+#include "libuserboot.h"
+
+#if defined(USERBOOT_ZFS_SUPPORT)
+#include "../zfs/libzfs.h"
+#endif
+
+/*
+ * We could use linker sets for some or all of these, but
+ * then we would have to control what ended up linked into
+ * the bootstrap. So it's easier to conditionalise things
+ * here.
+ *
+ * XXX rename these arrays to be consistent and less namespace-hostile
+ */
+
+/* Exported for libstand */
+struct devsw *devsw[] = {
+ &host_dev,
+ &userboot_disk,
+#if defined(USERBOOT_ZFS_SUPPORT)
+ &zfs_dev,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+ &host_fsops,
+ &ufs_fsops,
+ &cd9660_fsops,
+#if defined(USERBOOT_ZFS_SUPPORT)
+ &zfs_fsops,
+#endif
+ &gzipfs_fsops,
+ &bzipfs_fsops,
+ NULL
+};
+
+/* Exported for i386 only */
+/*
+ * Sort formats so that those that can detect based on arguments
+ * rather than reading the file go first.
+ */
+extern struct file_format i386_elf;
+extern struct file_format i386_elf_obj;
+extern struct file_format amd64_elf;
+extern struct file_format amd64_elf_obj;
+
+struct file_format *file_formats[] = {
+ &i386_elf,
+ &i386_elf_obj,
+ &amd64_elf,
+ &amd64_elf_obj,
+ NULL
+};
+
+/*
+ * Consoles
+ *
+ * We don't prototype these in libuserboot.h because they require
+ * data structures from bootstrap.h as well.
+ */
+extern struct console userboot_console;
+extern struct console userboot_comconsole;
+
+struct console *consoles[] = {
+ &userboot_console,
+ &userboot_comconsole,
+ NULL
+};
diff --git a/stand/userboot/userboot/copy.c b/stand/userboot/userboot/copy.c
new file mode 100644
index 0000000..f8763e9
--- /dev/null
+++ b/stand/userboot/userboot/copy.c
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 2011 Google, Inc.
+ * 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+
+#include "libuserboot.h"
+
+ssize_t
+userboot_copyin(const void *src, vm_offset_t va, size_t len)
+{
+
+ CALLBACK(copyin, src, va, len);
+ return (len);
+}
+
+ssize_t
+userboot_copyout(vm_offset_t va, void *dst, size_t len)
+{
+
+ CALLBACK(copyout, va, dst, len);
+ return (len);
+}
+
+ssize_t
+userboot_readin(int fd, vm_offset_t va, size_t len)
+{
+ ssize_t res, s;
+ size_t sz;
+ char buf[4096];
+
+ res = 0;
+ while (len > 0) {
+ sz = len;
+ if (sz > sizeof(buf))
+ sz = sizeof(buf);
+ s = read(fd, buf, sz);
+ if (s == 0)
+ break;
+ if (s < 0)
+ return (s);
+ CALLBACK(copyin, buf, va, s);
+ len -= s;
+ res += s;
+ va += s;
+ }
+ return (res);
+}
diff --git a/stand/userboot/userboot/devicename.c b/stand/userboot/userboot/devicename.c
new file mode 100644
index 0000000..efa5634
--- /dev/null
+++ b/stand/userboot/userboot/devicename.c
@@ -0,0 +1,224 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+#include "disk.h"
+#include "libuserboot.h"
+
+#if defined(USERBOOT_ZFS_SUPPORT)
+#include "../zfs/libzfs.h"
+#endif
+
+static int userboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **path);
+
+/*
+ * Point (dev) at an allocated device specifier for the device matching the
+ * path in (devspec). If it contains an explicit device specification,
+ * use that. If not, use the default device.
+ */
+int
+userboot_getdev(void **vdev, const char *devspec, const char **path)
+{
+ struct disk_devdesc **dev = (struct disk_devdesc **)vdev;
+ int rv;
+
+ /*
+ * If it looks like this is just a path and no
+ * device, go with the current device.
+ */
+ if ((devspec == NULL) ||
+ (devspec[0] == '/') ||
+ (strchr(devspec, ':') == NULL)) {
+
+ if (((rv = userboot_parsedev(dev, getenv("currdev"), NULL)) == 0) &&
+ (path != NULL))
+ *path = devspec;
+ return(rv);
+ }
+
+ /*
+ * Try to parse the device name off the beginning of the devspec
+ */
+ return(userboot_parsedev(dev, devspec, path));
+}
+
+/*
+ * Point (dev) at an allocated device specifier matching the string version
+ * at the beginning of (devspec). Return a pointer to the remaining
+ * text in (path).
+ *
+ * In all cases, the beginning of (devspec) is compared to the names
+ * of known devices in the device switch, and then any following text
+ * is parsed according to the rules applied to the device type.
+ *
+ * For disk-type devices, the syntax is:
+ *
+ * disk<unit>[s<slice>][<partition>]:
+ *
+ */
+static int
+userboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **path)
+{
+ struct disk_devdesc *idev;
+ struct devsw *dv;
+ int i, unit, err;
+ const char *cp;
+ const char *np;
+
+ /* minimum length check */
+ if (strlen(devspec) < 2)
+ return(EINVAL);
+
+ /* look for a device that matches */
+ for (i = 0, dv = NULL; devsw[i] != NULL; i++) {
+ if (!strncmp(devspec, devsw[i]->dv_name, strlen(devsw[i]->dv_name))) {
+ dv = devsw[i];
+ break;
+ }
+ }
+ if (dv == NULL)
+ return(ENOENT);
+ idev = malloc(sizeof(struct disk_devdesc));
+ err = 0;
+ np = (devspec + strlen(dv->dv_name));
+
+ switch(dv->dv_type) {
+ case DEVT_NONE: /* XXX what to do here? Do we care? */
+ break;
+
+ case DEVT_DISK:
+ err = disk_parsedev(idev, np, path);
+ if (err != 0)
+ goto fail;
+ break;
+
+ case DEVT_CD:
+ case DEVT_NET:
+ unit = 0;
+
+ if (*np && (*np != ':')) {
+ unit = strtol(np, (char **)&cp, 0); /* get unit number if present */
+ if (cp == np) {
+ err = EUNIT;
+ goto fail;
+ }
+ } else {
+ cp = np;
+ }
+ if (*cp && (*cp != ':')) {
+ err = EINVAL;
+ goto fail;
+ }
+
+ idev->d_unit = unit;
+ if (path != NULL)
+ *path = (*cp == 0) ? cp : cp + 1;
+ break;
+
+ case DEVT_ZFS:
+#if defined(USERBOOT_ZFS_SUPPORT)
+ err = zfs_parsedev((struct zfs_devdesc *)idev, np, path);
+ if (err != 0)
+ goto fail;
+ break;
+#else
+ /* FALLTHROUGH */
+#endif
+
+ default:
+ err = EINVAL;
+ goto fail;
+ }
+ idev->d_dev = dv;
+ idev->d_type = dv->dv_type;
+ if (dev == NULL) {
+ free(idev);
+ } else {
+ *dev = idev;
+ }
+ return(0);
+
+ fail:
+ free(idev);
+ return(err);
+}
+
+
+char *
+userboot_fmtdev(void *vdev)
+{
+ struct disk_devdesc *dev = (struct disk_devdesc *)vdev;
+ static char buf[128]; /* XXX device length constant? */
+
+ switch(dev->d_type) {
+ case DEVT_NONE:
+ strcpy(buf, "(no device)");
+ break;
+
+ case DEVT_CD:
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
+ break;
+
+ case DEVT_DISK:
+ return (disk_fmtdev(vdev));
+
+ case DEVT_NET:
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
+ break;
+
+ case DEVT_ZFS:
+#if defined(USERBOOT_ZFS_SUPPORT)
+ return (zfs_fmtdev(vdev));
+#else
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
+#endif
+ break;
+ }
+ return(buf);
+}
+
+
+/*
+ * Set currdev to suit the value being supplied in (value)
+ */
+int
+userboot_setcurrdev(struct env_var *ev, int flags, const void *value)
+{
+ struct disk_devdesc *ncurr;
+ int rv;
+
+ if ((rv = userboot_parsedev(&ncurr, value, NULL)) != 0)
+ return(rv);
+ free(ncurr);
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return(0);
+}
diff --git a/stand/userboot/userboot/elf32_freebsd.c b/stand/userboot/userboot/elf32_freebsd.c
new file mode 100644
index 0000000..d8ccc33
--- /dev/null
+++ b/stand/userboot/userboot/elf32_freebsd.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <string.h>
+#define _MACHINE_ELF_WANT_32BIT
+#include <i386/include/bootinfo.h>
+#include <i386/include/elf.h>
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "libuserboot.h"
+
+static int elf32_exec(struct preloaded_file *amp);
+static int elf32_obj_exec(struct preloaded_file *amp);
+
+struct file_format i386_elf = { elf32_loadfile, elf32_exec };
+struct file_format i386_elf_obj = { elf32_obj_loadfile, elf32_obj_exec };
+
+#define GUEST_STACK 0x1000 /* Initial stack base */
+#define GUEST_GDT 0x3000 /* Address of initial GDT */
+
+/*
+ * There is an ELF kernel and one or more ELF modules loaded.
+ * We wish to start executing the kernel image, so make such
+ * preparations as are required, and do so.
+ */
+static int
+elf32_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *md;
+ Elf_Ehdr *ehdr;
+ vm_offset_t entry, bootinfop, modulep, kernend;
+ int boothowto, err, bootdev;
+ uint32_t stack[1024], *sp;
+
+
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return(EFTYPE);
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+
+ err = bi_load32(fp->f_args, &boothowto, &bootdev, &bootinfop, &modulep, &kernend);
+ if (err != 0)
+ return(err);
+ entry = ehdr->e_entry & 0xffffff;
+
+#ifdef DEBUG
+ printf("Start @ 0x%lx ...\n", entry);
+#endif
+
+ dev_cleanup();
+
+ /*
+ * Build a scratch stack at physical 0x1000
+ */
+ memset(stack, 0, sizeof(stack));
+ sp = (uint32_t *)((char *)stack + sizeof(stack));
+ *--sp = kernend;
+ *--sp = modulep;
+ *--sp = bootinfop;
+ *--sp = 0;
+ *--sp = 0;
+ *--sp = 0;
+ *--sp = bootdev;
+ *--sp = boothowto;
+
+ /*
+ * Fake return address to mimic "new" boot blocks. For more
+ * details see recover_bootinfo in locore.S.
+ */
+ *--sp = 0xbeefface;
+ CALLBACK(copyin, stack, GUEST_STACK, sizeof(stack));
+ CALLBACK(setreg, 4, (char *)sp - (char *)stack + GUEST_STACK);
+
+ CALLBACK(setgdt, GUEST_GDT, 8 * 4 - 1);
+
+ CALLBACK(exec, entry);
+
+ panic("exec returned");
+}
+
+static int
+elf32_obj_exec(struct preloaded_file *fp)
+{
+ return (EFTYPE);
+}
diff --git a/stand/userboot/userboot/elf64_freebsd.c b/stand/userboot/userboot/elf64_freebsd.c
new file mode 100644
index 0000000..19d369d
--- /dev/null
+++ b/stand/userboot/userboot/elf64_freebsd.c
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <string.h>
+#include <i386/include/bootinfo.h>
+#include <machine/elf.h>
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "libuserboot.h"
+
+static int elf64_exec(struct preloaded_file *amp);
+static int elf64_obj_exec(struct preloaded_file *amp);
+
+struct file_format amd64_elf = { elf64_loadfile, elf64_exec };
+struct file_format amd64_elf_obj = { elf64_obj_loadfile, elf64_obj_exec };
+
+#define MSR_EFER 0xc0000080
+#define EFER_LME 0x00000100
+#define EFER_LMA 0x00000400 /* Long mode active (R) */
+#define CR4_PAE 0x00000020
+#define CR4_VMXE (1UL << 13)
+#define CR4_PSE 0x00000010
+#define CR0_PG 0x80000000
+#define CR0_PE 0x00000001 /* Protected mode Enable */
+#define CR0_NE 0x00000020 /* Numeric Error enable (EX16 vs IRQ13) */
+
+#define PG_V 0x001
+#define PG_RW 0x002
+#define PG_U 0x004
+#define PG_PS 0x080
+
+typedef u_int64_t p4_entry_t;
+typedef u_int64_t p3_entry_t;
+typedef u_int64_t p2_entry_t;
+
+#define GUEST_NULL_SEL 0
+#define GUEST_CODE_SEL 1
+#define GUEST_DATA_SEL 2
+#define GUEST_GDTR_LIMIT (3 * 8 - 1)
+
+static void
+setup_freebsd_gdt(uint64_t *gdtr)
+{
+ gdtr[GUEST_NULL_SEL] = 0;
+ gdtr[GUEST_CODE_SEL] = 0x0020980000000000;
+ gdtr[GUEST_DATA_SEL] = 0x0000900000000000;
+}
+
+/*
+ * There is an ELF kernel and one or more ELF modules loaded.
+ * We wish to start executing the kernel image, so make such
+ * preparations as are required, and do so.
+ */
+static int
+elf64_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *md;
+ Elf_Ehdr *ehdr;
+ vm_offset_t modulep, kernend;
+ int err;
+ int i;
+ uint32_t stack[1024];
+ p4_entry_t PT4[512];
+ p3_entry_t PT3[512];
+ p2_entry_t PT2[512];
+ uint64_t gdtr[3];
+
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return(EFTYPE);
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+
+ err = bi_load64(fp->f_args, &modulep, &kernend);
+ if (err != 0)
+ return(err);
+
+ bzero(PT4, PAGE_SIZE);
+ bzero(PT3, PAGE_SIZE);
+ bzero(PT2, PAGE_SIZE);
+
+ /*
+ * Build a scratch stack at physical 0x1000, page tables:
+ * PT4 at 0x2000,
+ * PT3 at 0x3000,
+ * PT2 at 0x4000,
+ * gdtr at 0x5000,
+ */
+
+ /*
+ * This is kinda brutal, but every single 1GB VM memory segment
+ * points to the same first 1GB of physical memory. But it is
+ * more than adequate.
+ */
+ for (i = 0; i < 512; i++) {
+ /* Each slot of the level 4 pages points to the same level 3 page */
+ PT4[i] = (p4_entry_t) 0x3000;
+ PT4[i] |= PG_V | PG_RW | PG_U;
+
+ /* Each slot of the level 3 pages points to the same level 2 page */
+ PT3[i] = (p3_entry_t) 0x4000;
+ PT3[i] |= PG_V | PG_RW | PG_U;
+
+ /* The level 2 page slots are mapped with 2MB pages for 1GB. */
+ PT2[i] = i * (2 * 1024 * 1024);
+ PT2[i] |= PG_V | PG_RW | PG_PS | PG_U;
+ }
+
+#ifdef DEBUG
+ printf("Start @ %#llx ...\n", ehdr->e_entry);
+#endif
+
+ dev_cleanup();
+
+ stack[0] = 0; /* return address */
+ stack[1] = modulep;
+ stack[2] = kernend;
+ CALLBACK(copyin, stack, 0x1000, sizeof(stack));
+ CALLBACK(copyin, PT4, 0x2000, sizeof(PT4));
+ CALLBACK(copyin, PT3, 0x3000, sizeof(PT3));
+ CALLBACK(copyin, PT2, 0x4000, sizeof(PT2));
+ CALLBACK(setreg, 4, 0x1000);
+
+ CALLBACK(setmsr, MSR_EFER, EFER_LMA | EFER_LME);
+ CALLBACK(setcr, 4, CR4_PAE | CR4_VMXE);
+ CALLBACK(setcr, 3, 0x2000);
+ CALLBACK(setcr, 0, CR0_PG | CR0_PE | CR0_NE);
+
+ setup_freebsd_gdt(gdtr);
+ CALLBACK(copyin, gdtr, 0x5000, sizeof(gdtr));
+ CALLBACK(setgdt, 0x5000, sizeof(gdtr));
+
+ CALLBACK(exec, ehdr->e_entry);
+
+ panic("exec returned");
+}
+
+static int
+elf64_obj_exec(struct preloaded_file *fp)
+{
+
+ return (EFTYPE);
+}
diff --git a/stand/userboot/userboot/host.c b/stand/userboot/userboot/host.c
new file mode 100644
index 0000000..94f8a3d
--- /dev/null
+++ b/stand/userboot/userboot/host.c
@@ -0,0 +1,202 @@
+/*-
+ * Copyright (c) 2011 Google, Inc.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Read from the host filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stand.h>
+#include <bootstrap.h>
+
+#include "libuserboot.h"
+
+/*
+ * Open a file.
+ */
+static int
+host_open(const char *upath, struct open_file *f)
+{
+
+ if (f->f_dev != &host_dev)
+ return (EINVAL);
+
+ return (CALLBACK(open, upath, &f->f_fsdata));
+}
+
+static int
+host_close(struct open_file *f)
+{
+
+ CALLBACK(close, f->f_fsdata);
+ f->f_fsdata = (void *)0;
+
+ return (0);
+}
+
+/*
+ * Copy a portion of a file into memory.
+ */
+static int
+host_read(struct open_file *f, void *start, size_t size, size_t *resid)
+{
+
+ return (CALLBACK(read, f->f_fsdata, start, size, resid));
+}
+
+/*
+ * Don't be silly - the bootstrap has no business writing anything.
+ */
+static int
+host_write(struct open_file *f, void *start, size_t size, size_t *resid)
+{
+
+ return (EROFS);
+}
+
+static off_t
+host_seek(struct open_file *f, off_t offset, int where)
+{
+
+ return (CALLBACK(seek, f->f_fsdata, offset, where));
+}
+
+static int
+host_stat(struct open_file *f, struct stat *sb)
+{
+ int mode;
+ int uid;
+ int gid;
+ uint64_t size;
+
+ CALLBACK(stat, f->f_fsdata, &mode, &uid, &gid, &size);
+ sb->st_mode = mode;
+ sb->st_uid = uid;
+ sb->st_gid = gid;
+ sb->st_size = size;
+ return (0);
+}
+
+static int
+host_readdir(struct open_file *f, struct dirent *d)
+{
+ uint32_t fileno;
+ uint8_t type;
+ size_t namelen;
+ int rc;
+
+ rc = CALLBACK(readdir, f->f_fsdata, &fileno, &type, &namelen,
+ d->d_name);
+ if (rc)
+ return (rc);
+
+ d->d_fileno = fileno;
+ d->d_type = type;
+ d->d_namlen = namelen;
+
+ return (0);
+}
+
+static int
+host_dev_init(void)
+{
+
+ return (0);
+}
+
+static int
+host_dev_print(int verbose)
+{
+ char line[80];
+
+ printf("%s devices:", host_dev.dv_name);
+ if (pager_output("\n") != 0)
+ return (1);
+
+ snprintf(line, sizeof(line), " host%d: Host filesystem\n", 0);
+ return (pager_output(line));
+}
+
+/*
+ * 'Open' the host device.
+ */
+static int
+host_dev_open(struct open_file *f, ...)
+{
+ va_list args;
+ struct devdesc *dev;
+
+ va_start(args, f);
+ dev = va_arg(args, struct devdesc*);
+ va_end(args);
+
+ return (0);
+}
+
+static int
+host_dev_close(struct open_file *f)
+{
+
+ return (0);
+}
+
+static int
+host_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+
+ return (ENOSYS);
+}
+
+struct fs_ops host_fsops = {
+ "host",
+ host_open,
+ host_close,
+ host_read,
+ host_write,
+ host_seek,
+ host_stat,
+ host_readdir
+};
+
+struct devsw host_dev = {
+ .dv_name = "host",
+ .dv_type = DEVT_NET,
+ .dv_init = host_dev_init,
+ .dv_strategy = host_dev_strategy,
+ .dv_open = host_dev_open,
+ .dv_close = host_dev_close,
+ .dv_ioctl = noioctl,
+ .dv_print = host_dev_print,
+ .dv_cleanup = NULL
+};
diff --git a/stand/userboot/userboot/libuserboot.h b/stand/userboot/userboot/libuserboot.h
new file mode 100644
index 0000000..e2048d5
--- /dev/null
+++ b/stand/userboot/userboot/libuserboot.h
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2011 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "userboot.h"
+
+extern struct loader_callbacks *callbacks;
+extern void *callbacks_arg;
+
+#define CALLBACK(fn, args...) (callbacks->fn(callbacks_arg , ##args))
+
+#define MAXDEV 31 /* maximum number of distinct devices */
+
+typedef unsigned long physaddr_t;
+
+/* exported devices */
+extern struct devsw userboot_disk;
+extern int userboot_disk_maxunit;
+extern struct devsw host_dev;
+
+/* access to host filesystem */
+struct fs_ops host_fsops;
+
+struct bootinfo;
+struct preloaded_file;
+extern int bi_load(struct bootinfo *, struct preloaded_file *);
+
+extern void delay(int);
+
+extern int userboot_autoload(void);
+extern ssize_t userboot_copyin(const void *, vm_offset_t, size_t);
+extern ssize_t userboot_copyout(vm_offset_t, void *, size_t);
+extern ssize_t userboot_readin(int, vm_offset_t, size_t);
+extern int userboot_getdev(void **, const char *, const char **);
+char *userboot_fmtdev(void *vdev);
+int userboot_setcurrdev(struct env_var *ev, int flags, const void *value);
+
+int bi_getboothowto(char *kargs);
+void bi_setboothowto(int howto);
+vm_offset_t bi_copyenv(vm_offset_t addr);
+int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip,
+ vm_offset_t *modulep, vm_offset_t *kernend);
+int bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernend);
+void bios_addsmapdata(struct preloaded_file *kfp);
diff --git a/stand/userboot/userboot/main.c b/stand/userboot/userboot/main.c
new file mode 100644
index 0000000..7f59eb7
--- /dev/null
+++ b/stand/userboot/userboot/main.c
@@ -0,0 +1,304 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 1998,2000 Doug Rabson <dfr@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include <setjmp.h>
+#include <sys/disk.h>
+
+#include "bootstrap.h"
+#include "disk.h"
+#include "libuserboot.h"
+
+#if defined(USERBOOT_ZFS_SUPPORT)
+#include "../zfs/libzfs.h"
+
+static void userboot_zfs_probe(void);
+static int userboot_zfs_found;
+#endif
+
+/* Minimum version required */
+#define USERBOOT_VERSION USERBOOT_VERSION_3
+
+#define MALLOCSZ (64*1024*1024)
+
+struct loader_callbacks *callbacks;
+void *callbacks_arg;
+
+extern char bootprog_info[];
+static jmp_buf jb;
+
+struct arch_switch archsw; /* MI/MD interface boundary */
+
+static void extract_currdev(void);
+
+void
+delay(int usec)
+{
+
+ CALLBACK(delay, usec);
+}
+
+void
+exit(int v)
+{
+
+ CALLBACK(exit, v);
+ longjmp(jb, 1);
+}
+
+void
+loader_main(struct loader_callbacks *cb, void *arg, int version, int ndisks)
+{
+ static char mallocbuf[MALLOCSZ];
+ char *var;
+ int i;
+
+ if (version < USERBOOT_VERSION)
+ abort();
+
+ callbacks = cb;
+ callbacks_arg = arg;
+ userboot_disk_maxunit = ndisks;
+
+ /*
+ * initialise the heap as early as possible. Once this is done,
+ * alloc() is usable.
+ */
+ setheap((void *)mallocbuf, (void *)(mallocbuf + sizeof(mallocbuf)));
+
+ /*
+ * Hook up the console
+ */
+ cons_probe();
+
+ printf("\n%s", bootprog_info);
+#if 0
+ printf("Memory: %ld k\n", memsize() / 1024);
+#endif
+
+ setenv("LINES", "24", 1); /* optional */
+
+ /*
+ * Set custom environment variables
+ */
+ i = 0;
+ while (1) {
+ var = CALLBACK(getenv, i++);
+ if (var == NULL)
+ break;
+ putenv(var);
+ }
+
+ archsw.arch_autoload = userboot_autoload;
+ archsw.arch_getdev = userboot_getdev;
+ archsw.arch_copyin = userboot_copyin;
+ archsw.arch_copyout = userboot_copyout;
+ archsw.arch_readin = userboot_readin;
+#if defined(USERBOOT_ZFS_SUPPORT)
+ archsw.arch_zfs_probe = userboot_zfs_probe;
+#endif
+
+ /*
+ * Initialise the block cache. Set the upper limit.
+ */
+ bcache_init(32768, 512);
+ /*
+ * March through the device switch probing for things.
+ */
+ for (i = 0; devsw[i] != NULL; i++)
+ if (devsw[i]->dv_init != NULL)
+ (devsw[i]->dv_init)();
+
+ extract_currdev();
+
+ if (setjmp(jb))
+ return;
+
+ interact(NULL); /* doesn't return */
+
+ exit(0);
+}
+
+/*
+ * Set the 'current device' by (if possible) recovering the boot device as
+ * supplied by the initial bootstrap.
+ */
+static void
+extract_currdev(void)
+{
+ struct disk_devdesc dev;
+
+ //bzero(&dev, sizeof(dev));
+
+#if defined(USERBOOT_ZFS_SUPPORT)
+ if (userboot_zfs_found) {
+ struct zfs_devdesc zdev;
+
+ /* Leave the pool/root guid's unassigned */
+ bzero(&zdev, sizeof(zdev));
+ zdev.d_dev = &zfs_dev;
+ zdev.d_type = zdev.d_dev->dv_type;
+
+ dev = *(struct disk_devdesc *)&zdev;
+ init_zfs_bootenv(zfs_fmtdev(&dev));
+ } else
+#endif
+
+ if (userboot_disk_maxunit > 0) {
+ dev.d_dev = &userboot_disk;
+ dev.d_type = dev.d_dev->dv_type;
+ dev.d_unit = 0;
+ dev.d_slice = 0;
+ dev.d_partition = 0;
+ /*
+ * If we cannot auto-detect the partition type then
+ * access the disk as a raw device.
+ */
+ if (dev.d_dev->dv_open(NULL, &dev)) {
+ dev.d_slice = -1;
+ dev.d_partition = -1;
+ }
+ } else {
+ dev.d_dev = &host_dev;
+ dev.d_type = dev.d_dev->dv_type;
+ dev.d_unit = 0;
+ }
+
+ env_setenv("currdev", EV_VOLATILE, userboot_fmtdev(&dev),
+ userboot_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, userboot_fmtdev(&dev),
+ env_noset, env_nounset);
+}
+
+#if defined(USERBOOT_ZFS_SUPPORT)
+static void
+userboot_zfs_probe(void)
+{
+ char devname[32];
+ uint64_t pool_guid;
+ int unit;
+
+ /*
+ * Open all the disks we can find and see if we can reconstruct
+ * ZFS pools from them. Record if any were found.
+ */
+ for (unit = 0; unit < userboot_disk_maxunit; unit++) {
+ sprintf(devname, "disk%d:", unit);
+ pool_guid = 0;
+ zfs_probe_dev(devname, &pool_guid);
+ if (pool_guid != 0)
+ userboot_zfs_found = 1;
+ }
+}
+
+COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
+ command_lszfs);
+
+static int
+command_lszfs(int argc, char *argv[])
+{
+ int err;
+
+ if (argc != 2) {
+ command_errmsg = "a single dataset must be supplied";
+ return (CMD_ERROR);
+ }
+
+ err = zfs_list(argv[1]);
+ if (err != 0) {
+ command_errmsg = strerror(err);
+ return (CMD_ERROR);
+ }
+ return (CMD_OK);
+}
+
+COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments",
+ command_reloadbe);
+
+static int
+command_reloadbe(int argc, char *argv[])
+{
+ int err;
+ char *root;
+
+ if (argc > 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ if (argc == 2) {
+ err = zfs_bootenv(argv[1]);
+ } else {
+ root = getenv("zfs_be_root");
+ if (root == NULL) {
+ return (CMD_OK);
+ }
+ err = zfs_bootenv(root);
+ }
+
+ if (err != 0) {
+ command_errmsg = strerror(err);
+ return (CMD_ERROR);
+ }
+
+ return (CMD_OK);
+}
+
+uint64_t
+ldi_get_size(void *priv)
+{
+ int fd = (uintptr_t) priv;
+ uint64_t size;
+
+ ioctl(fd, DIOCGMEDIASIZE, &size);
+ return (size);
+}
+#endif /* USERBOOT_ZFS_SUPPORT */
+
+COMMAND_SET(quit, "quit", "exit the loader", command_quit);
+
+static int
+command_quit(int argc, char *argv[])
+{
+
+ exit(USERBOOT_EXIT_QUIT);
+ return (CMD_OK);
+}
+
+COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
+
+static int
+command_reboot(int argc, char *argv[])
+{
+
+ exit(USERBOOT_EXIT_REBOOT);
+ return (CMD_OK);
+}
diff --git a/stand/userboot/userboot/userboot_cons.c b/stand/userboot/userboot/userboot_cons.c
new file mode 100644
index 0000000..6f73ad5
--- /dev/null
+++ b/stand/userboot/userboot/userboot_cons.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2011 Google, Inc.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "bootstrap.h"
+#include "libuserboot.h"
+
+int console;
+
+static struct console *userboot_comconsp;
+
+static void userboot_cons_probe(struct console *cp);
+static int userboot_cons_init(int);
+static void userboot_comcons_probe(struct console *cp);
+static int userboot_comcons_init(int);
+static void userboot_cons_putchar(int);
+static int userboot_cons_getchar(void);
+static int userboot_cons_poll(void);
+
+struct console userboot_console = {
+ "userboot",
+ "userboot",
+ 0,
+ userboot_cons_probe,
+ userboot_cons_init,
+ userboot_cons_putchar,
+ userboot_cons_getchar,
+ userboot_cons_poll,
+};
+
+/*
+ * Provide a simple alias to allow loader scripts to set the
+ * console to comconsole without resulting in an error
+ */
+struct console userboot_comconsole = {
+ "comconsole",
+ "comconsole",
+ 0,
+ userboot_comcons_probe,
+ userboot_comcons_init,
+ userboot_cons_putchar,
+ userboot_cons_getchar,
+ userboot_cons_poll,
+};
+
+static void
+userboot_cons_probe(struct console *cp)
+{
+
+ cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+}
+
+static int
+userboot_cons_init(int arg)
+{
+
+ return (0);
+}
+
+static void
+userboot_comcons_probe(struct console *cp)
+{
+
+ /*
+ * Save the console pointer so the comcons_init routine
+ * can set the C_PRESENT* flags. They are not set
+ * here to allow the existing userboot console to
+ * be elected the default.
+ */
+ userboot_comconsp = cp;
+}
+
+static int
+userboot_comcons_init(int arg)
+{
+
+ /*
+ * Set the C_PRESENT* flags to allow the comconsole
+ * to be selected as the active console
+ */
+ userboot_comconsp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+ return (0);
+}
+
+static void
+userboot_cons_putchar(int c)
+{
+
+ CALLBACK(putc, c);
+}
+
+static int
+userboot_cons_getchar()
+{
+
+ return (CALLBACK(getc));
+}
+
+static int
+userboot_cons_poll()
+{
+
+ return (CALLBACK(poll));
+}
diff --git a/stand/userboot/userboot/userboot_disk.c b/stand/userboot/userboot/userboot_disk.c
new file mode 100644
index 0000000..fc21b5f
--- /dev/null
+++ b/stand/userboot/userboot/userboot_disk.c
@@ -0,0 +1,242 @@
+/*-
+ * Copyright (c) 2011 Google, Inc.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Userboot disk image handling.
+ */
+
+#include <sys/disk.h>
+#include <stand.h>
+#include <stdarg.h>
+#include <bootstrap.h>
+
+#include "disk.h"
+#include "libuserboot.h"
+
+struct userdisk_info {
+ uint64_t mediasize;
+ uint16_t sectorsize;
+ int ud_open; /* reference counter */
+ void *ud_bcache; /* buffer cache data */
+};
+
+int userboot_disk_maxunit = 0;
+
+static int userdisk_maxunit = 0;
+static struct userdisk_info *ud_info;
+
+static int userdisk_init(void);
+static void userdisk_cleanup(void);
+static int userdisk_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int userdisk_realstrategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int userdisk_open(struct open_file *f, ...);
+static int userdisk_close(struct open_file *f);
+static int userdisk_ioctl(struct open_file *f, u_long cmd, void *data);
+static int userdisk_print(int verbose);
+
+struct devsw userboot_disk = {
+ "disk",
+ DEVT_DISK,
+ userdisk_init,
+ userdisk_strategy,
+ userdisk_open,
+ userdisk_close,
+ userdisk_ioctl,
+ userdisk_print,
+ userdisk_cleanup
+};
+
+/*
+ * Initialize userdisk_info structure for each disk.
+ */
+static int
+userdisk_init(void)
+{
+ off_t mediasize;
+ u_int sectorsize;
+ int i;
+
+ userdisk_maxunit = userboot_disk_maxunit;
+ if (userdisk_maxunit > 0) {
+ ud_info = malloc(sizeof(*ud_info) * userdisk_maxunit);
+ if (ud_info == NULL)
+ return (ENOMEM);
+ for (i = 0; i < userdisk_maxunit; i++) {
+ if (CALLBACK(diskioctl, i, DIOCGSECTORSIZE,
+ &sectorsize) != 0 || CALLBACK(diskioctl, i,
+ DIOCGMEDIASIZE, &mediasize) != 0)
+ return (ENXIO);
+ ud_info[i].mediasize = mediasize;
+ ud_info[i].sectorsize = sectorsize;
+ ud_info[i].ud_open = 0;
+ ud_info[i].ud_bcache = NULL;
+ }
+ }
+ bcache_add_dev(userdisk_maxunit);
+ return(0);
+}
+
+static void
+userdisk_cleanup(void)
+{
+
+ if (userdisk_maxunit > 0)
+ free(ud_info);
+}
+
+/*
+ * Print information about disks
+ */
+static int
+userdisk_print(int verbose)
+{
+ struct disk_devdesc dev;
+ char line[80];
+ int i, ret = 0;
+
+ if (userdisk_maxunit == 0)
+ return (0);
+
+ printf("%s devices:", userboot_disk.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ for (i = 0; i < userdisk_maxunit; i++) {
+ snprintf(line, sizeof(line),
+ " disk%d: Guest drive image\n", i);
+ ret = pager_output(line);
+ if (ret != 0)
+ break;
+ dev.d_dev = &userboot_disk;
+ dev.d_unit = i;
+ dev.d_slice = -1;
+ dev.d_partition = -1;
+ if (disk_open(&dev, ud_info[i].mediasize,
+ ud_info[i].sectorsize) == 0) {
+ snprintf(line, sizeof(line), " disk%d", i);
+ ret = disk_print(&dev, line, verbose);
+ disk_close(&dev);
+ if (ret != 0)
+ break;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Attempt to open the disk described by (dev) for use by (f).
+ */
+static int
+userdisk_open(struct open_file *f, ...)
+{
+ va_list ap;
+ struct disk_devdesc *dev;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct disk_devdesc *);
+ va_end(ap);
+
+ if (dev->d_unit < 0 || dev->d_unit >= userdisk_maxunit)
+ return (EIO);
+ ud_info[dev->d_unit].ud_open++;
+ if (ud_info[dev->d_unit].ud_bcache == NULL)
+ ud_info[dev->d_unit].ud_bcache = bcache_allocate();
+ return (disk_open(dev, ud_info[dev->d_unit].mediasize,
+ ud_info[dev->d_unit].sectorsize));
+}
+
+static int
+userdisk_close(struct open_file *f)
+{
+ struct disk_devdesc *dev;
+
+ dev = (struct disk_devdesc *)f->f_devdata;
+ ud_info[dev->d_unit].ud_open--;
+ if (ud_info[dev->d_unit].ud_open == 0) {
+ bcache_free(ud_info[dev->d_unit].ud_bcache);
+ ud_info[dev->d_unit].ud_bcache = NULL;
+ }
+ return (disk_close(dev));
+}
+
+static int
+userdisk_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct bcache_devdata bcd;
+ struct disk_devdesc *dev;
+
+ dev = (struct disk_devdesc *)devdata;
+ bcd.dv_strategy = userdisk_realstrategy;
+ bcd.dv_devdata = devdata;
+ bcd.dv_cache = ud_info[dev->d_unit].ud_bcache;
+ return (bcache_strategy(&bcd, rw, dblk + dev->d_offset,
+ size, buf, rsize));
+}
+
+static int
+userdisk_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct disk_devdesc *dev = devdata;
+ uint64_t off;
+ size_t resid;
+ int rc;
+
+ rw &= F_MASK;
+ if (rw == F_WRITE)
+ return (EROFS);
+ if (rw != F_READ)
+ return (EINVAL);
+ if (rsize)
+ *rsize = 0;
+ off = dblk * ud_info[dev->d_unit].sectorsize;
+ rc = CALLBACK(diskread, dev->d_unit, off, buf, size, &resid);
+ if (rc)
+ return (rc);
+ if (rsize)
+ *rsize = size - resid;
+ return (0);
+}
+
+static int
+userdisk_ioctl(struct open_file *f, u_long cmd, void *data)
+{
+ struct disk_devdesc *dev;
+ int rc;
+
+ dev = (struct disk_devdesc *)f->f_devdata;
+ rc = disk_ioctl(dev, cmd, data);
+ if (rc != ENOTTY)
+ return (rc);
+
+ return (CALLBACK(diskioctl, dev->d_unit, cmd, data));
+}
diff --git a/stand/userboot/userboot/version b/stand/userboot/userboot/version
new file mode 100644
index 0000000..ce6e270
--- /dev/null
+++ b/stand/userboot/userboot/version
@@ -0,0 +1,4 @@
+$FreeBSD$
+
+1.1: Initial userland boot
+
diff --git a/stand/zfs/Makefile b/stand/zfs/Makefile
new file mode 100644
index 0000000..e86c84b
--- /dev/null
+++ b/stand/zfs/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+LIB= zfsboot
+INTERNALLIB=
+
+.PATH: ${ZFSSRC}
+SRCS+= zfs.c skein.c skein_block.c
+# Do not unroll skein loops, reduce code size
+CFLAGS+= -DSKEIN_LOOP=111
+.PATH: ${SYSDIR}/crypto/skein
+
+CFLAGS+= -DBOOTPROG=\"zfsloader\"
+CFLAGS+= -I${LDRSRC}
+CFLAGS+= -I${SYSDIR}/cddl/boot/zfs
+CFLAGS+= -I${SYSDIR}/crypto/skein
+
+CFLAGS+= -Wformat -Wall
+
+.include <bsd.stand.mk>
+.include <bsd.lib.mk>
diff --git a/stand/zfs/Makefile.depend b/stand/zfs/Makefile.depend
new file mode 100644
index 0000000..18be76b
--- /dev/null
+++ b/stand/zfs/Makefile.depend
@@ -0,0 +1,13 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/zfs/devicename_stubs.c b/stand/zfs/devicename_stubs.c
new file mode 100644
index 0000000..41bf907
--- /dev/null
+++ b/stand/zfs/devicename_stubs.c
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include "libzfs.h"
+
+__attribute__((weak))
+int
+zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path)
+{
+ return (EINVAL);
+}
+
+__attribute__((weak))
+char *
+zfs_fmtdev(void *vdev)
+{
+ static char buf[128];
+
+ return (buf);
+}
diff --git a/stand/zfs/libzfs.h b/stand/zfs/libzfs.h
new file mode 100644
index 0000000..d1b46746
--- /dev/null
+++ b/stand/zfs/libzfs.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOT_LIBZFS_H_
+#define _BOOT_LIBZFS_H_
+
+#define ZFS_MAXNAMELEN 256
+
+/*
+ * ZFS fully-qualified device descriptor.
+ * Note, this must match the 'struct devdesc' declaration in bootstrap.h.
+ * Arch-specific device descriptors should be binary compatible with this
+ * structure if they are to support ZFS.
+ */
+struct zfs_devdesc
+{
+ struct devsw *d_dev;
+ int d_type;
+ int d_unit;
+ void *d_opendata;
+ uint64_t pool_guid;
+ uint64_t root_guid;
+};
+
+#ifdef LOADER_GELI_SUPPORT
+#include <crypto/intake.h>
+#endif
+
+struct zfs_boot_args
+{
+ uint32_t size;
+ uint32_t reserved;
+ uint64_t pool;
+ uint64_t root;
+ uint64_t primary_pool;
+ uint64_t primary_vdev;
+ union {
+ char gelipw[256];
+ struct {
+ char notapw; /*
+ * single null byte to stop keybuf
+ * being interpreted as a password
+ */
+ uint32_t keybuf_sentinel;
+#ifdef LOADER_GELI_SUPPORT
+ struct keybuf *keybuf;
+#else
+ void *keybuf;
+#endif
+ };
+ };
+};
+
+int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec,
+ const char **path);
+char *zfs_fmtdev(void *vdev);
+int zfs_probe_dev(const char *devname, uint64_t *pool_guid);
+int zfs_list(const char *name);
+uint64_t ldi_get_size(void *);
+void init_zfs_bootenv(char *currdev);
+int zfs_bootenv(const char *name);
+int zfs_belist_add(const char *name, uint64_t __unused);
+int zfs_set_env(void);
+
+extern struct devsw zfs_dev;
+extern struct fs_ops zfs_fsops;
+
+#endif /*_BOOT_LIBZFS_H_*/
diff --git a/stand/zfs/zfs.c b/stand/zfs/zfs.c
new file mode 100644
index 0000000..0334bdd
--- /dev/null
+++ b/stand/zfs/zfs.c
@@ -0,0 +1,956 @@
+/*-
+ * Copyright (c) 2007 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Stand-alone file reading package.
+ */
+
+#include <sys/disk.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <part.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stand.h>
+#include <bootstrap.h>
+
+#include "libzfs.h"
+
+#include "zfsimpl.c"
+
+/* Define the range of indexes to be populated with ZFS Boot Environments */
+#define ZFS_BE_FIRST 4
+#define ZFS_BE_LAST 8
+
+static int zfs_open(const char *path, struct open_file *f);
+static int zfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
+static int zfs_close(struct open_file *f);
+static int zfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t zfs_seek(struct open_file *f, off_t offset, int where);
+static int zfs_stat(struct open_file *f, struct stat *sb);
+static int zfs_readdir(struct open_file *f, struct dirent *d);
+
+struct devsw zfs_dev;
+
+struct fs_ops zfs_fsops = {
+ "zfs",
+ zfs_open,
+ zfs_close,
+ zfs_read,
+ zfs_write,
+ zfs_seek,
+ zfs_stat,
+ zfs_readdir
+};
+
+/*
+ * In-core open file.
+ */
+struct file {
+ off_t f_seekp; /* seek pointer */
+ dnode_phys_t f_dnode;
+ uint64_t f_zap_type; /* zap type for readdir */
+ uint64_t f_num_leafs; /* number of fzap leaf blocks */
+ zap_leaf_phys_t *f_zap_leaf; /* zap leaf buffer */
+};
+
+static int zfs_env_index;
+static int zfs_env_count;
+
+SLIST_HEAD(zfs_be_list, zfs_be_entry) zfs_be_head = SLIST_HEAD_INITIALIZER(zfs_be_head);
+struct zfs_be_list *zfs_be_headp;
+struct zfs_be_entry {
+ const char *name;
+ SLIST_ENTRY(zfs_be_entry) entries;
+} *zfs_be, *zfs_be_tmp;
+
+/*
+ * Open a file.
+ */
+static int
+zfs_open(const char *upath, struct open_file *f)
+{
+ struct zfsmount *mount = (struct zfsmount *)f->f_devdata;
+ struct file *fp;
+ int rc;
+
+ if (f->f_dev != &zfs_dev)
+ return (EINVAL);
+
+ /* allocate file system specific data structure */
+ fp = malloc(sizeof(struct file));
+ bzero(fp, sizeof(struct file));
+ f->f_fsdata = (void *)fp;
+
+ rc = zfs_lookup(mount, upath, &fp->f_dnode);
+ fp->f_seekp = 0;
+ if (rc) {
+ f->f_fsdata = NULL;
+ free(fp);
+ }
+ return (rc);
+}
+
+static int
+zfs_close(struct open_file *f)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ dnode_cache_obj = NULL;
+ f->f_fsdata = (void *)0;
+ if (fp == (struct file *)0)
+ return (0);
+
+ free(fp);
+ return (0);
+}
+
+/*
+ * Copy a portion of a file into kernel memory.
+ * Cross block boundaries when necessary.
+ */
+static int
+zfs_read(struct open_file *f, void *start, size_t size, size_t *resid /* out */)
+{
+ const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct stat sb;
+ size_t n;
+ int rc;
+
+ rc = zfs_stat(f, &sb);
+ if (rc)
+ return (rc);
+ n = size;
+ if (fp->f_seekp + n > sb.st_size)
+ n = sb.st_size - fp->f_seekp;
+
+ rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, start, n);
+ if (rc)
+ return (rc);
+
+ if (0) {
+ int i;
+ for (i = 0; i < n; i++)
+ putchar(((char*) start)[i]);
+ }
+ fp->f_seekp += n;
+ if (resid)
+ *resid = size - n;
+
+ return (0);
+}
+
+/*
+ * Don't be silly - the bootstrap has no business writing anything.
+ */
+static int
+zfs_write(struct open_file *f, void *start, size_t size, size_t *resid /* out */)
+{
+
+ return (EROFS);
+}
+
+static off_t
+zfs_seek(struct open_file *f, off_t offset, int where)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ switch (where) {
+ case SEEK_SET:
+ fp->f_seekp = offset;
+ break;
+ case SEEK_CUR:
+ fp->f_seekp += offset;
+ break;
+ case SEEK_END:
+ {
+ struct stat sb;
+ int error;
+
+ error = zfs_stat(f, &sb);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+ fp->f_seekp = sb.st_size - offset;
+ break;
+ }
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ return (fp->f_seekp);
+}
+
+static int
+zfs_stat(struct open_file *f, struct stat *sb)
+{
+ const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ return (zfs_dnode_stat(spa, &fp->f_dnode, sb));
+}
+
+static int
+zfs_readdir(struct open_file *f, struct dirent *d)
+{
+ const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa;
+ struct file *fp = (struct file *)f->f_fsdata;
+ mzap_ent_phys_t mze;
+ struct stat sb;
+ size_t bsize = fp->f_dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT;
+ int rc;
+
+ rc = zfs_stat(f, &sb);
+ if (rc)
+ return (rc);
+ if (!S_ISDIR(sb.st_mode))
+ return (ENOTDIR);
+
+ /*
+ * If this is the first read, get the zap type.
+ */
+ if (fp->f_seekp == 0) {
+ rc = dnode_read(spa, &fp->f_dnode,
+ 0, &fp->f_zap_type, sizeof(fp->f_zap_type));
+ if (rc)
+ return (rc);
+
+ if (fp->f_zap_type == ZBT_MICRO) {
+ fp->f_seekp = offsetof(mzap_phys_t, mz_chunk);
+ } else {
+ rc = dnode_read(spa, &fp->f_dnode,
+ offsetof(zap_phys_t, zap_num_leafs),
+ &fp->f_num_leafs,
+ sizeof(fp->f_num_leafs));
+ if (rc)
+ return (rc);
+
+ fp->f_seekp = bsize;
+ fp->f_zap_leaf = (zap_leaf_phys_t *)malloc(bsize);
+ rc = dnode_read(spa, &fp->f_dnode,
+ fp->f_seekp,
+ fp->f_zap_leaf,
+ bsize);
+ if (rc)
+ return (rc);
+ }
+ }
+
+ if (fp->f_zap_type == ZBT_MICRO) {
+ mzap_next:
+ if (fp->f_seekp >= bsize)
+ return (ENOENT);
+
+ rc = dnode_read(spa, &fp->f_dnode,
+ fp->f_seekp, &mze, sizeof(mze));
+ if (rc)
+ return (rc);
+ fp->f_seekp += sizeof(mze);
+
+ if (!mze.mze_name[0])
+ goto mzap_next;
+
+ d->d_fileno = ZFS_DIRENT_OBJ(mze.mze_value);
+ d->d_type = ZFS_DIRENT_TYPE(mze.mze_value);
+ strcpy(d->d_name, mze.mze_name);
+ d->d_namlen = strlen(d->d_name);
+ return (0);
+ } else {
+ zap_leaf_t zl;
+ zap_leaf_chunk_t *zc, *nc;
+ int chunk;
+ size_t namelen;
+ char *p;
+ uint64_t value;
+
+ /*
+ * Initialise this so we can use the ZAP size
+ * calculating macros.
+ */
+ zl.l_bs = ilog2(bsize);
+ zl.l_phys = fp->f_zap_leaf;
+
+ /*
+ * Figure out which chunk we are currently looking at
+ * and consider seeking to the next leaf. We use the
+ * low bits of f_seekp as a simple chunk index.
+ */
+ fzap_next:
+ chunk = fp->f_seekp & (bsize - 1);
+ if (chunk == ZAP_LEAF_NUMCHUNKS(&zl)) {
+ fp->f_seekp = rounddown2(fp->f_seekp, bsize) + bsize;
+ chunk = 0;
+
+ /*
+ * Check for EOF and read the new leaf.
+ */
+ if (fp->f_seekp >= bsize * fp->f_num_leafs)
+ return (ENOENT);
+
+ rc = dnode_read(spa, &fp->f_dnode,
+ fp->f_seekp,
+ fp->f_zap_leaf,
+ bsize);
+ if (rc)
+ return (rc);
+ }
+
+ zc = &ZAP_LEAF_CHUNK(&zl, chunk);
+ fp->f_seekp++;
+ if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY)
+ goto fzap_next;
+
+ namelen = zc->l_entry.le_name_numints;
+ if (namelen > sizeof(d->d_name))
+ namelen = sizeof(d->d_name);
+
+ /*
+ * Paste the name back together.
+ */
+ nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk);
+ p = d->d_name;
+ while (namelen > 0) {
+ int len;
+ len = namelen;
+ if (len > ZAP_LEAF_ARRAY_BYTES)
+ len = ZAP_LEAF_ARRAY_BYTES;
+ memcpy(p, nc->l_array.la_array, len);
+ p += len;
+ namelen -= len;
+ nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next);
+ }
+ d->d_name[sizeof(d->d_name) - 1] = 0;
+
+ /*
+ * Assume the first eight bytes of the value are
+ * a uint64_t.
+ */
+ value = fzap_leaf_value(&zl, zc);
+
+ d->d_fileno = ZFS_DIRENT_OBJ(value);
+ d->d_type = ZFS_DIRENT_TYPE(value);
+ d->d_namlen = strlen(d->d_name);
+
+ return (0);
+ }
+}
+
+static int
+vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t bytes)
+{
+ int fd, ret;
+ size_t res, size, remainder, rb_size, blksz;
+ unsigned secsz;
+ off_t off;
+ char *bouncebuf, *rb_buf;
+
+ fd = (uintptr_t) priv;
+ bouncebuf = NULL;
+
+ ret = ioctl(fd, DIOCGSECTORSIZE, &secsz);
+ if (ret != 0)
+ return (ret);
+
+ off = offset / secsz;
+ remainder = offset % secsz;
+ if (lseek(fd, off * secsz, SEEK_SET) == -1)
+ return (errno);
+
+ rb_buf = buf;
+ rb_size = bytes;
+ size = roundup2(bytes + remainder, secsz);
+ blksz = size;
+ if (remainder != 0 || size != bytes) {
+ bouncebuf = zfs_alloc(secsz);
+ if (bouncebuf == NULL) {
+ printf("vdev_read: out of memory\n");
+ return (ENOMEM);
+ }
+ rb_buf = bouncebuf;
+ blksz = rb_size - remainder;
+ }
+
+ while (bytes > 0) {
+ res = read(fd, rb_buf, rb_size);
+ if (res != rb_size) {
+ ret = EIO;
+ goto error;
+ }
+ if (bytes < blksz)
+ blksz = bytes;
+ if (bouncebuf != NULL)
+ memcpy(buf, rb_buf + remainder, blksz);
+ buf = (void *)((uintptr_t)buf + blksz);
+ bytes -= blksz;
+ remainder = 0;
+ blksz = rb_size;
+ }
+
+ ret = 0;
+error:
+ if (bouncebuf != NULL)
+ zfs_free(bouncebuf, secsz);
+ return (ret);
+}
+
+static int
+zfs_dev_init(void)
+{
+ spa_t *spa;
+ spa_t *next;
+ spa_t *prev;
+
+ zfs_init();
+ if (archsw.arch_zfs_probe == NULL)
+ return (ENXIO);
+ archsw.arch_zfs_probe();
+
+ prev = NULL;
+ spa = STAILQ_FIRST(&zfs_pools);
+ while (spa != NULL) {
+ next = STAILQ_NEXT(spa, spa_link);
+ if (zfs_spa_init(spa)) {
+ if (prev == NULL)
+ STAILQ_REMOVE_HEAD(&zfs_pools, spa_link);
+ else
+ STAILQ_REMOVE_AFTER(&zfs_pools, prev, spa_link);
+ } else
+ prev = spa;
+ spa = next;
+ }
+ return (0);
+}
+
+struct zfs_probe_args {
+ int fd;
+ const char *devname;
+ uint64_t *pool_guid;
+ u_int secsz;
+};
+
+static int
+zfs_diskread(void *arg, void *buf, size_t blocks, uint64_t offset)
+{
+ struct zfs_probe_args *ppa;
+
+ ppa = (struct zfs_probe_args *)arg;
+ return (vdev_read(NULL, (void *)(uintptr_t)ppa->fd,
+ offset * ppa->secsz, buf, blocks * ppa->secsz));
+}
+
+static int
+zfs_probe(int fd, uint64_t *pool_guid)
+{
+ spa_t *spa;
+ int ret;
+
+ ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa);
+ if (ret == 0 && pool_guid != NULL)
+ *pool_guid = spa->spa_guid;
+ return (ret);
+}
+
+static int
+zfs_probe_partition(void *arg, const char *partname,
+ const struct ptable_entry *part)
+{
+ struct zfs_probe_args *ppa, pa;
+ struct ptable *table;
+ char devname[32];
+ int ret;
+
+ /* Probe only freebsd-zfs and freebsd partitions */
+ if (part->type != PART_FREEBSD &&
+ part->type != PART_FREEBSD_ZFS)
+ return (0);
+
+ ppa = (struct zfs_probe_args *)arg;
+ strncpy(devname, ppa->devname, strlen(ppa->devname) - 1);
+ devname[strlen(ppa->devname) - 1] = '\0';
+ sprintf(devname, "%s%s:", devname, partname);
+ pa.fd = open(devname, O_RDONLY);
+ if (pa.fd == -1)
+ return (0);
+ ret = zfs_probe(pa.fd, ppa->pool_guid);
+ if (ret == 0)
+ return (0);
+ /* Do we have BSD label here? */
+ if (part->type == PART_FREEBSD) {
+ pa.devname = devname;
+ pa.pool_guid = ppa->pool_guid;
+ pa.secsz = ppa->secsz;
+ table = ptable_open(&pa, part->end - part->start + 1,
+ ppa->secsz, zfs_diskread);
+ if (table != NULL) {
+ ptable_iterate(table, &pa, zfs_probe_partition);
+ ptable_close(table);
+ }
+ }
+ close(pa.fd);
+ return (0);
+}
+
+int
+zfs_probe_dev(const char *devname, uint64_t *pool_guid)
+{
+ struct ptable *table;
+ struct zfs_probe_args pa;
+ uint64_t mediasz;
+ int ret;
+
+ if (pool_guid)
+ *pool_guid = 0;
+ pa.fd = open(devname, O_RDONLY);
+ if (pa.fd == -1)
+ return (ENXIO);
+ /* Probe the whole disk */
+ ret = zfs_probe(pa.fd, pool_guid);
+ if (ret == 0)
+ return (0);
+
+ /* Probe each partition */
+ ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz);
+ if (ret == 0)
+ ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz);
+ if (ret == 0) {
+ pa.devname = devname;
+ pa.pool_guid = pool_guid;
+ table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz,
+ zfs_diskread);
+ if (table != NULL) {
+ ptable_iterate(table, &pa, zfs_probe_partition);
+ ptable_close(table);
+ }
+ }
+ close(pa.fd);
+ if (pool_guid && *pool_guid == 0)
+ ret = ENXIO;
+ return (ret);
+}
+
+/*
+ * Print information about ZFS pools
+ */
+static int
+zfs_dev_print(int verbose)
+{
+ spa_t *spa;
+ char line[80];
+ int ret = 0;
+
+ if (STAILQ_EMPTY(&zfs_pools))
+ return (0);
+
+ printf("%s devices:", zfs_dev.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ if (verbose) {
+ return (spa_all_status());
+ }
+ STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
+ snprintf(line, sizeof(line), " zfs:%s\n", spa->spa_name);
+ ret = pager_output(line);
+ if (ret != 0)
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * Attempt to open the pool described by (dev) for use by (f).
+ */
+static int
+zfs_dev_open(struct open_file *f, ...)
+{
+ va_list args;
+ struct zfs_devdesc *dev;
+ struct zfsmount *mount;
+ spa_t *spa;
+ int rv;
+
+ va_start(args, f);
+ dev = va_arg(args, struct zfs_devdesc *);
+ va_end(args);
+
+ if (dev->pool_guid == 0)
+ spa = STAILQ_FIRST(&zfs_pools);
+ else
+ spa = spa_find_by_guid(dev->pool_guid);
+ if (!spa)
+ return (ENXIO);
+ mount = malloc(sizeof(*mount));
+ rv = zfs_mount(spa, dev->root_guid, mount);
+ if (rv != 0) {
+ free(mount);
+ return (rv);
+ }
+ if (mount->objset.os_type != DMU_OST_ZFS) {
+ printf("Unexpected object set type %ju\n",
+ (uintmax_t)mount->objset.os_type);
+ free(mount);
+ return (EIO);
+ }
+ f->f_devdata = mount;
+ free(dev);
+ return (0);
+}
+
+static int
+zfs_dev_close(struct open_file *f)
+{
+
+ free(f->f_devdata);
+ f->f_devdata = NULL;
+ return (0);
+}
+
+static int
+zfs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
+{
+
+ return (ENOSYS);
+}
+
+struct devsw zfs_dev = {
+ .dv_name = "zfs",
+ .dv_type = DEVT_ZFS,
+ .dv_init = zfs_dev_init,
+ .dv_strategy = zfs_dev_strategy,
+ .dv_open = zfs_dev_open,
+ .dv_close = zfs_dev_close,
+ .dv_ioctl = noioctl,
+ .dv_print = zfs_dev_print,
+ .dv_cleanup = NULL
+};
+
+int
+zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path)
+{
+ static char rootname[ZFS_MAXNAMELEN];
+ static char poolname[ZFS_MAXNAMELEN];
+ spa_t *spa;
+ const char *end;
+ const char *np;
+ const char *sep;
+ int rv;
+
+ np = devspec;
+ if (*np != ':')
+ return (EINVAL);
+ np++;
+ end = strchr(np, ':');
+ if (end == NULL)
+ return (EINVAL);
+ sep = strchr(np, '/');
+ if (sep == NULL || sep >= end)
+ sep = end;
+ memcpy(poolname, np, sep - np);
+ poolname[sep - np] = '\0';
+ if (sep < end) {
+ sep++;
+ memcpy(rootname, sep, end - sep);
+ rootname[end - sep] = '\0';
+ }
+ else
+ rootname[0] = '\0';
+
+ spa = spa_find_by_name(poolname);
+ if (!spa)
+ return (ENXIO);
+ dev->pool_guid = spa->spa_guid;
+ rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid);
+ if (rv != 0)
+ return (rv);
+ if (path != NULL)
+ *path = (*end == '\0') ? end : end + 1;
+ dev->d_dev = &zfs_dev;
+ dev->d_type = zfs_dev.dv_type;
+ return (0);
+}
+
+char *
+zfs_fmtdev(void *vdev)
+{
+ static char rootname[ZFS_MAXNAMELEN];
+ static char buf[2 * ZFS_MAXNAMELEN + 8];
+ struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
+ spa_t *spa;
+
+ buf[0] = '\0';
+ if (dev->d_type != DEVT_ZFS)
+ return (buf);
+
+ if (dev->pool_guid == 0) {
+ spa = STAILQ_FIRST(&zfs_pools);
+ dev->pool_guid = spa->spa_guid;
+ } else
+ spa = spa_find_by_guid(dev->pool_guid);
+ if (spa == NULL) {
+ printf("ZFS: can't find pool by guid\n");
+ return (buf);
+ }
+ if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) {
+ printf("ZFS: can't find root filesystem\n");
+ return (buf);
+ }
+ if (zfs_rlookup(spa, dev->root_guid, rootname)) {
+ printf("ZFS: can't find filesystem by guid\n");
+ return (buf);
+ }
+
+ if (rootname[0] == '\0')
+ sprintf(buf, "%s:%s:", dev->d_dev->dv_name, spa->spa_name);
+ else
+ sprintf(buf, "%s:%s/%s:", dev->d_dev->dv_name, spa->spa_name,
+ rootname);
+ return (buf);
+}
+
+int
+zfs_list(const char *name)
+{
+ static char poolname[ZFS_MAXNAMELEN];
+ uint64_t objid;
+ spa_t *spa;
+ const char *dsname;
+ int len;
+ int rv;
+
+ len = strlen(name);
+ dsname = strchr(name, '/');
+ if (dsname != NULL) {
+ len = dsname - name;
+ dsname++;
+ } else
+ dsname = "";
+ memcpy(poolname, name, len);
+ poolname[len] = '\0';
+
+ spa = spa_find_by_name(poolname);
+ if (!spa)
+ return (ENXIO);
+ rv = zfs_lookup_dataset(spa, dsname, &objid);
+ if (rv != 0)
+ return (rv);
+
+ return (zfs_list_dataset(spa, objid));
+}
+
+void
+init_zfs_bootenv(char *currdev)
+{
+ char *beroot;
+
+ if (strlen(currdev) == 0)
+ return;
+ if(strncmp(currdev, "zfs:", 4) != 0)
+ return;
+ /* Remove the trailing : */
+ currdev[strlen(currdev) - 1] = '\0';
+ setenv("zfs_be_active", currdev, 1);
+ setenv("zfs_be_currpage", "1", 1);
+ /* Forward past zfs: */
+ currdev = strchr(currdev, ':');
+ currdev++;
+ /* Remove the last element (current bootenv) */
+ beroot = strrchr(currdev, '/');
+ if (beroot != NULL)
+ beroot[0] = '\0';
+ beroot = currdev;
+ setenv("zfs_be_root", beroot, 1);
+}
+
+int
+zfs_bootenv(const char *name)
+{
+ static char poolname[ZFS_MAXNAMELEN], *dsname, *root;
+ char becount[4];
+ uint64_t objid;
+ spa_t *spa;
+ int len, rv, pages, perpage, currpage;
+
+ if (name == NULL)
+ return (EINVAL);
+ if ((root = getenv("zfs_be_root")) == NULL)
+ return (EINVAL);
+
+ if (strcmp(name, root) != 0) {
+ if (setenv("zfs_be_root", name, 1) != 0)
+ return (ENOMEM);
+ }
+
+ SLIST_INIT(&zfs_be_head);
+ zfs_env_count = 0;
+ len = strlen(name);
+ dsname = strchr(name, '/');
+ if (dsname != NULL) {
+ len = dsname - name;
+ dsname++;
+ } else
+ dsname = "";
+ memcpy(poolname, name, len);
+ poolname[len] = '\0';
+
+ spa = spa_find_by_name(poolname);
+ if (!spa)
+ return (ENXIO);
+ rv = zfs_lookup_dataset(spa, dsname, &objid);
+ if (rv != 0)
+ return (rv);
+ rv = zfs_callback_dataset(spa, objid, zfs_belist_add);
+
+ /* Calculate and store the number of pages of BEs */
+ perpage = (ZFS_BE_LAST - ZFS_BE_FIRST + 1);
+ pages = (zfs_env_count / perpage) + ((zfs_env_count % perpage) > 0 ? 1 : 0);
+ snprintf(becount, 4, "%d", pages);
+ if (setenv("zfs_be_pages", becount, 1) != 0)
+ return (ENOMEM);
+
+ /* Roll over the page counter if it has exceeded the maximum */
+ currpage = strtol(getenv("zfs_be_currpage"), NULL, 10);
+ if (currpage > pages) {
+ if (setenv("zfs_be_currpage", "1", 1) != 0)
+ return (ENOMEM);
+ }
+
+ /* Populate the menu environment variables */
+ zfs_set_env();
+
+ /* Clean up the SLIST of ZFS BEs */
+ while (!SLIST_EMPTY(&zfs_be_head)) {
+ zfs_be = SLIST_FIRST(&zfs_be_head);
+ SLIST_REMOVE_HEAD(&zfs_be_head, entries);
+ free(zfs_be);
+ }
+
+ return (rv);
+}
+
+int
+zfs_belist_add(const char *name, uint64_t value __unused)
+{
+
+ /* Skip special datasets that start with a $ character */
+ if (strncmp(name, "$", 1) == 0) {
+ return (0);
+ }
+ /* Add the boot environment to the head of the SLIST */
+ zfs_be = malloc(sizeof(struct zfs_be_entry));
+ if (zfs_be == NULL) {
+ return (ENOMEM);
+ }
+ zfs_be->name = name;
+ SLIST_INSERT_HEAD(&zfs_be_head, zfs_be, entries);
+ zfs_env_count++;
+
+ return (0);
+}
+
+int
+zfs_set_env(void)
+{
+ char envname[32], envval[256];
+ char *beroot, *pagenum;
+ int rv, page, ctr;
+
+ beroot = getenv("zfs_be_root");
+ if (beroot == NULL) {
+ return (1);
+ }
+
+ pagenum = getenv("zfs_be_currpage");
+ if (pagenum != NULL) {
+ page = strtol(pagenum, NULL, 10);
+ } else {
+ page = 1;
+ }
+
+ ctr = 1;
+ rv = 0;
+ zfs_env_index = ZFS_BE_FIRST;
+ SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) {
+ /* Skip to the requested page number */
+ if (ctr <= ((ZFS_BE_LAST - ZFS_BE_FIRST + 1) * (page - 1))) {
+ ctr++;
+ continue;
+ }
+
+ snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index);
+ snprintf(envval, sizeof(envval), "%s", zfs_be->name);
+ rv = setenv(envname, envval, 1);
+ if (rv != 0) {
+ break;
+ }
+
+ snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index);
+ rv = setenv(envname, envval, 1);
+ if (rv != 0){
+ break;
+ }
+
+ snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index);
+ rv = setenv(envname, "set_bootenv", 1);
+ if (rv != 0){
+ break;
+ }
+
+ snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index);
+ snprintf(envval, sizeof(envval), "zfs:%s/%s", beroot, zfs_be->name);
+ rv = setenv(envname, envval, 1);
+ if (rv != 0){
+ break;
+ }
+
+ zfs_env_index++;
+ if (zfs_env_index > ZFS_BE_LAST) {
+ break;
+ }
+
+ }
+
+ for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) {
+ snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index);
+ (void)unsetenv(envname);
+ snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index);
+ (void)unsetenv(envname);
+ snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index);
+ (void)unsetenv(envname);
+ snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index);
+ (void)unsetenv(envname);
+ }
+
+ return (rv);
+}
diff --git a/stand/zfs/zfsimpl.c b/stand/zfs/zfsimpl.c
new file mode 100644
index 0000000..6e0d9b6
--- /dev/null
+++ b/stand/zfs/zfsimpl.c
@@ -0,0 +1,2536 @@
+/*-
+ * Copyright (c) 2007 Doug Rabson
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Stand-alone ZFS file reader.
+ */
+
+#include <sys/stat.h>
+#include <sys/stdint.h>
+
+#include "zfsimpl.h"
+#include "zfssubr.c"
+
+
+struct zfsmount {
+ const spa_t *spa;
+ objset_phys_t objset;
+ uint64_t rootobj;
+};
+
+/*
+ * List of all vdevs, chained through v_alllink.
+ */
+static vdev_list_t zfs_vdevs;
+
+ /*
+ * List of ZFS features supported for read
+ */
+static const char *features_for_read[] = {
+ "org.illumos:lz4_compress",
+ "com.delphix:hole_birth",
+ "com.delphix:extensible_dataset",
+ "com.delphix:embedded_data",
+ "org.open-zfs:large_blocks",
+ "org.illumos:sha512",
+ "org.illumos:skein",
+ "org.zfsonlinux:large_dnode",
+ NULL
+};
+
+/*
+ * List of all pools, chained through spa_link.
+ */
+static spa_list_t zfs_pools;
+
+static const dnode_phys_t *dnode_cache_obj;
+static uint64_t dnode_cache_bn;
+static char *dnode_cache_buf;
+static char *zap_scratch;
+static char *zfs_temp_buf, *zfs_temp_end, *zfs_temp_ptr;
+
+#define TEMP_SIZE (1024 * 1024)
+
+static int zio_read(const spa_t *spa, const blkptr_t *bp, void *buf);
+static int zfs_get_root(const spa_t *spa, uint64_t *objid);
+static int zfs_rlookup(const spa_t *spa, uint64_t objnum, char *result);
+static int zap_lookup(const spa_t *spa, const dnode_phys_t *dnode,
+ const char *name, uint64_t integer_size, uint64_t num_integers,
+ void *value);
+
+static void
+zfs_init(void)
+{
+ STAILQ_INIT(&zfs_vdevs);
+ STAILQ_INIT(&zfs_pools);
+
+ zfs_temp_buf = malloc(TEMP_SIZE);
+ zfs_temp_end = zfs_temp_buf + TEMP_SIZE;
+ zfs_temp_ptr = zfs_temp_buf;
+ dnode_cache_buf = malloc(SPA_MAXBLOCKSIZE);
+ zap_scratch = malloc(SPA_MAXBLOCKSIZE);
+
+ zfs_init_crc();
+}
+
+static void *
+zfs_alloc(size_t size)
+{
+ char *ptr;
+
+ if (zfs_temp_ptr + size > zfs_temp_end) {
+ printf("ZFS: out of temporary buffer space\n");
+ for (;;) ;
+ }
+ ptr = zfs_temp_ptr;
+ zfs_temp_ptr += size;
+
+ return (ptr);
+}
+
+static void
+zfs_free(void *ptr, size_t size)
+{
+
+ zfs_temp_ptr -= size;
+ if (zfs_temp_ptr != ptr) {
+ printf("ZFS: zfs_alloc()/zfs_free() mismatch\n");
+ for (;;) ;
+ }
+}
+
+static int
+xdr_int(const unsigned char **xdr, int *ip)
+{
+ *ip = ((*xdr)[0] << 24)
+ | ((*xdr)[1] << 16)
+ | ((*xdr)[2] << 8)
+ | ((*xdr)[3] << 0);
+ (*xdr) += 4;
+ return (0);
+}
+
+static int
+xdr_u_int(const unsigned char **xdr, u_int *ip)
+{
+ *ip = ((*xdr)[0] << 24)
+ | ((*xdr)[1] << 16)
+ | ((*xdr)[2] << 8)
+ | ((*xdr)[3] << 0);
+ (*xdr) += 4;
+ return (0);
+}
+
+static int
+xdr_uint64_t(const unsigned char **xdr, uint64_t *lp)
+{
+ u_int hi, lo;
+
+ xdr_u_int(xdr, &hi);
+ xdr_u_int(xdr, &lo);
+ *lp = (((uint64_t) hi) << 32) | lo;
+ return (0);
+}
+
+static int
+nvlist_find(const unsigned char *nvlist, const char *name, int type,
+ int* elementsp, void *valuep)
+{
+ const unsigned char *p, *pair;
+ int junk;
+ int encoded_size, decoded_size;
+
+ p = nvlist;
+ xdr_int(&p, &junk);
+ xdr_int(&p, &junk);
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ while (encoded_size && decoded_size) {
+ int namelen, pairtype, elements;
+ const char *pairname;
+
+ xdr_int(&p, &namelen);
+ pairname = (const char*) p;
+ p += roundup(namelen, 4);
+ xdr_int(&p, &pairtype);
+
+ if (!memcmp(name, pairname, namelen) && type == pairtype) {
+ xdr_int(&p, &elements);
+ if (elementsp)
+ *elementsp = elements;
+ if (type == DATA_TYPE_UINT64) {
+ xdr_uint64_t(&p, (uint64_t *) valuep);
+ return (0);
+ } else if (type == DATA_TYPE_STRING) {
+ int len;
+ xdr_int(&p, &len);
+ (*(const char**) valuep) = (const char*) p;
+ return (0);
+ } else if (type == DATA_TYPE_NVLIST
+ || type == DATA_TYPE_NVLIST_ARRAY) {
+ (*(const unsigned char**) valuep) =
+ (const unsigned char*) p;
+ return (0);
+ } else {
+ return (EIO);
+ }
+ } else {
+ /*
+ * Not the pair we are looking for, skip to the next one.
+ */
+ p = pair + encoded_size;
+ }
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ }
+
+ return (EIO);
+}
+
+static int
+nvlist_check_features_for_read(const unsigned char *nvlist)
+{
+ const unsigned char *p, *pair;
+ int junk;
+ int encoded_size, decoded_size;
+ int rc;
+
+ rc = 0;
+
+ p = nvlist;
+ xdr_int(&p, &junk);
+ xdr_int(&p, &junk);
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ while (encoded_size && decoded_size) {
+ int namelen, pairtype;
+ const char *pairname;
+ int i, found;
+
+ found = 0;
+
+ xdr_int(&p, &namelen);
+ pairname = (const char*) p;
+ p += roundup(namelen, 4);
+ xdr_int(&p, &pairtype);
+
+ for (i = 0; features_for_read[i] != NULL; i++) {
+ if (!memcmp(pairname, features_for_read[i], namelen)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ printf("ZFS: unsupported feature: %s\n", pairname);
+ rc = EIO;
+ }
+
+ p = pair + encoded_size;
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ }
+
+ return (rc);
+}
+
+/*
+ * Return the next nvlist in an nvlist array.
+ */
+static const unsigned char *
+nvlist_next(const unsigned char *nvlist)
+{
+ const unsigned char *p, *pair;
+ int junk;
+ int encoded_size, decoded_size;
+
+ p = nvlist;
+ xdr_int(&p, &junk);
+ xdr_int(&p, &junk);
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ while (encoded_size && decoded_size) {
+ p = pair + encoded_size;
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ }
+
+ return p;
+}
+
+#ifdef TEST
+
+static const unsigned char *
+nvlist_print(const unsigned char *nvlist, unsigned int indent)
+{
+ static const char* typenames[] = {
+ "DATA_TYPE_UNKNOWN",
+ "DATA_TYPE_BOOLEAN",
+ "DATA_TYPE_BYTE",
+ "DATA_TYPE_INT16",
+ "DATA_TYPE_UINT16",
+ "DATA_TYPE_INT32",
+ "DATA_TYPE_UINT32",
+ "DATA_TYPE_INT64",
+ "DATA_TYPE_UINT64",
+ "DATA_TYPE_STRING",
+ "DATA_TYPE_BYTE_ARRAY",
+ "DATA_TYPE_INT16_ARRAY",
+ "DATA_TYPE_UINT16_ARRAY",
+ "DATA_TYPE_INT32_ARRAY",
+ "DATA_TYPE_UINT32_ARRAY",
+ "DATA_TYPE_INT64_ARRAY",
+ "DATA_TYPE_UINT64_ARRAY",
+ "DATA_TYPE_STRING_ARRAY",
+ "DATA_TYPE_HRTIME",
+ "DATA_TYPE_NVLIST",
+ "DATA_TYPE_NVLIST_ARRAY",
+ "DATA_TYPE_BOOLEAN_VALUE",
+ "DATA_TYPE_INT8",
+ "DATA_TYPE_UINT8",
+ "DATA_TYPE_BOOLEAN_ARRAY",
+ "DATA_TYPE_INT8_ARRAY",
+ "DATA_TYPE_UINT8_ARRAY"
+ };
+
+ unsigned int i, j;
+ const unsigned char *p, *pair;
+ int junk;
+ int encoded_size, decoded_size;
+
+ p = nvlist;
+ xdr_int(&p, &junk);
+ xdr_int(&p, &junk);
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ while (encoded_size && decoded_size) {
+ int namelen, pairtype, elements;
+ const char *pairname;
+
+ xdr_int(&p, &namelen);
+ pairname = (const char*) p;
+ p += roundup(namelen, 4);
+ xdr_int(&p, &pairtype);
+
+ for (i = 0; i < indent; i++)
+ printf(" ");
+ printf("%s %s", typenames[pairtype], pairname);
+
+ xdr_int(&p, &elements);
+ switch (pairtype) {
+ case DATA_TYPE_UINT64: {
+ uint64_t val;
+ xdr_uint64_t(&p, &val);
+ printf(" = 0x%jx\n", (uintmax_t)val);
+ break;
+ }
+
+ case DATA_TYPE_STRING: {
+ int len;
+ xdr_int(&p, &len);
+ printf(" = \"%s\"\n", p);
+ break;
+ }
+
+ case DATA_TYPE_NVLIST:
+ printf("\n");
+ nvlist_print(p, indent + 1);
+ break;
+
+ case DATA_TYPE_NVLIST_ARRAY:
+ for (j = 0; j < elements; j++) {
+ printf("[%d]\n", j);
+ p = nvlist_print(p, indent + 1);
+ if (j != elements - 1) {
+ for (i = 0; i < indent; i++)
+ printf(" ");
+ printf("%s %s", typenames[pairtype], pairname);
+ }
+ }
+ break;
+
+ default:
+ printf("\n");
+ }
+
+ p = pair + encoded_size;
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ }
+
+ return p;
+}
+
+#endif
+
+static int
+vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf,
+ off_t offset, size_t size)
+{
+ size_t psize;
+ int rc;
+
+ if (!vdev->v_phys_read)
+ return (EIO);
+
+ if (bp) {
+ psize = BP_GET_PSIZE(bp);
+ } else {
+ psize = size;
+ }
+
+ /*printf("ZFS: reading %zu bytes at 0x%jx to %p\n", psize, (uintmax_t)offset, buf);*/
+ rc = vdev->v_phys_read(vdev, vdev->v_read_priv, offset, buf, psize);
+ if (rc)
+ return (rc);
+ if (bp && zio_checksum_verify(vdev->spa, bp, buf))
+ return (EIO);
+
+ return (0);
+}
+
+static int
+vdev_disk_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
+ off_t offset, size_t bytes)
+{
+
+ return (vdev_read_phys(vdev, bp, buf,
+ offset + VDEV_LABEL_START_SIZE, bytes));
+}
+
+
+static int
+vdev_mirror_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
+ off_t offset, size_t bytes)
+{
+ vdev_t *kid;
+ int rc;
+
+ rc = EIO;
+ STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
+ if (kid->v_state != VDEV_STATE_HEALTHY)
+ continue;
+ rc = kid->v_read(kid, bp, buf, offset, bytes);
+ if (!rc)
+ return (0);
+ }
+
+ return (rc);
+}
+
+static int
+vdev_replacing_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
+ off_t offset, size_t bytes)
+{
+ vdev_t *kid;
+
+ /*
+ * Here we should have two kids:
+ * First one which is the one we are replacing and we can trust
+ * only this one to have valid data, but it might not be present.
+ * Second one is that one we are replacing with. It is most likely
+ * healthy, but we can't trust it has needed data, so we won't use it.
+ */
+ kid = STAILQ_FIRST(&vdev->v_children);
+ if (kid == NULL)
+ return (EIO);
+ if (kid->v_state != VDEV_STATE_HEALTHY)
+ return (EIO);
+ return (kid->v_read(kid, bp, buf, offset, bytes));
+}
+
+static vdev_t *
+vdev_find(uint64_t guid)
+{
+ vdev_t *vdev;
+
+ STAILQ_FOREACH(vdev, &zfs_vdevs, v_alllink)
+ if (vdev->v_guid == guid)
+ return (vdev);
+
+ return (0);
+}
+
+static vdev_t *
+vdev_create(uint64_t guid, vdev_read_t *_read)
+{
+ vdev_t *vdev;
+
+ vdev = malloc(sizeof(vdev_t));
+ memset(vdev, 0, sizeof(vdev_t));
+ STAILQ_INIT(&vdev->v_children);
+ vdev->v_guid = guid;
+ vdev->v_state = VDEV_STATE_OFFLINE;
+ vdev->v_read = _read;
+ vdev->v_phys_read = 0;
+ vdev->v_read_priv = 0;
+ STAILQ_INSERT_TAIL(&zfs_vdevs, vdev, v_alllink);
+
+ return (vdev);
+}
+
+static int
+vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev,
+ vdev_t **vdevp, int is_newer)
+{
+ int rc;
+ uint64_t guid, id, ashift, nparity;
+ const char *type;
+ const char *path;
+ vdev_t *vdev, *kid;
+ const unsigned char *kids;
+ int nkids, i, is_new;
+ uint64_t is_offline, is_faulted, is_degraded, is_removed, isnt_present;
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
+ NULL, &guid)
+ || nvlist_find(nvlist, ZPOOL_CONFIG_ID, DATA_TYPE_UINT64, NULL, &id)
+ || nvlist_find(nvlist, ZPOOL_CONFIG_TYPE, DATA_TYPE_STRING,
+ NULL, &type)) {
+ printf("ZFS: can't find vdev details\n");
+ return (ENOENT);
+ }
+
+ if (strcmp(type, VDEV_TYPE_MIRROR)
+ && strcmp(type, VDEV_TYPE_DISK)
+#ifdef ZFS_TEST
+ && strcmp(type, VDEV_TYPE_FILE)
+#endif
+ && strcmp(type, VDEV_TYPE_RAIDZ)
+ && strcmp(type, VDEV_TYPE_REPLACING)) {
+ printf("ZFS: can only boot from disk, mirror, raidz1, raidz2 and raidz3 vdevs\n");
+ return (EIO);
+ }
+
+ is_offline = is_removed = is_faulted = is_degraded = isnt_present = 0;
+
+ nvlist_find(nvlist, ZPOOL_CONFIG_OFFLINE, DATA_TYPE_UINT64, NULL,
+ &is_offline);
+ nvlist_find(nvlist, ZPOOL_CONFIG_REMOVED, DATA_TYPE_UINT64, NULL,
+ &is_removed);
+ nvlist_find(nvlist, ZPOOL_CONFIG_FAULTED, DATA_TYPE_UINT64, NULL,
+ &is_faulted);
+ nvlist_find(nvlist, ZPOOL_CONFIG_DEGRADED, DATA_TYPE_UINT64, NULL,
+ &is_degraded);
+ nvlist_find(nvlist, ZPOOL_CONFIG_NOT_PRESENT, DATA_TYPE_UINT64, NULL,
+ &isnt_present);
+
+ vdev = vdev_find(guid);
+ if (!vdev) {
+ is_new = 1;
+
+ if (!strcmp(type, VDEV_TYPE_MIRROR))
+ vdev = vdev_create(guid, vdev_mirror_read);
+ else if (!strcmp(type, VDEV_TYPE_RAIDZ))
+ vdev = vdev_create(guid, vdev_raidz_read);
+ else if (!strcmp(type, VDEV_TYPE_REPLACING))
+ vdev = vdev_create(guid, vdev_replacing_read);
+ else
+ vdev = vdev_create(guid, vdev_disk_read);
+
+ vdev->v_id = id;
+ vdev->v_top = pvdev != NULL ? pvdev : vdev;
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_ASHIFT,
+ DATA_TYPE_UINT64, NULL, &ashift) == 0) {
+ vdev->v_ashift = ashift;
+ } else {
+ vdev->v_ashift = 0;
+ }
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_NPARITY,
+ DATA_TYPE_UINT64, NULL, &nparity) == 0) {
+ vdev->v_nparity = nparity;
+ } else {
+ vdev->v_nparity = 0;
+ }
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_PATH,
+ DATA_TYPE_STRING, NULL, &path) == 0) {
+ if (strncmp(path, "/dev/", 5) == 0)
+ path += 5;
+ vdev->v_name = strdup(path);
+ } else {
+ if (!strcmp(type, "raidz")) {
+ if (vdev->v_nparity == 1)
+ vdev->v_name = "raidz1";
+ else if (vdev->v_nparity == 2)
+ vdev->v_name = "raidz2";
+ else if (vdev->v_nparity == 3)
+ vdev->v_name = "raidz3";
+ else {
+ printf("ZFS: can only boot from disk, mirror, raidz1, raidz2 and raidz3 vdevs\n");
+ return (EIO);
+ }
+ } else {
+ vdev->v_name = strdup(type);
+ }
+ }
+ } else {
+ is_new = 0;
+ }
+
+ if (is_new || is_newer) {
+ /*
+ * This is either new vdev or we've already seen this vdev,
+ * but from an older vdev label, so let's refresh its state
+ * from the newer label.
+ */
+ if (is_offline)
+ vdev->v_state = VDEV_STATE_OFFLINE;
+ else if (is_removed)
+ vdev->v_state = VDEV_STATE_REMOVED;
+ else if (is_faulted)
+ vdev->v_state = VDEV_STATE_FAULTED;
+ else if (is_degraded)
+ vdev->v_state = VDEV_STATE_DEGRADED;
+ else if (isnt_present)
+ vdev->v_state = VDEV_STATE_CANT_OPEN;
+ }
+
+ rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY,
+ &nkids, &kids);
+ /*
+ * Its ok if we don't have any kids.
+ */
+ if (rc == 0) {
+ vdev->v_nchildren = nkids;
+ for (i = 0; i < nkids; i++) {
+ rc = vdev_init_from_nvlist(kids, vdev, &kid, is_newer);
+ if (rc)
+ return (rc);
+ if (is_new)
+ STAILQ_INSERT_TAIL(&vdev->v_children, kid,
+ v_childlink);
+ kids = nvlist_next(kids);
+ }
+ } else {
+ vdev->v_nchildren = 0;
+ }
+
+ if (vdevp)
+ *vdevp = vdev;
+ return (0);
+}
+
+static void
+vdev_set_state(vdev_t *vdev)
+{
+ vdev_t *kid;
+ int good_kids;
+ int bad_kids;
+
+ /*
+ * A mirror or raidz is healthy if all its kids are healthy. A
+ * mirror is degraded if any of its kids is healthy; a raidz
+ * is degraded if at most nparity kids are offline.
+ */
+ if (STAILQ_FIRST(&vdev->v_children)) {
+ good_kids = 0;
+ bad_kids = 0;
+ STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
+ if (kid->v_state == VDEV_STATE_HEALTHY)
+ good_kids++;
+ else
+ bad_kids++;
+ }
+ if (bad_kids == 0) {
+ vdev->v_state = VDEV_STATE_HEALTHY;
+ } else {
+ if (vdev->v_read == vdev_mirror_read) {
+ if (good_kids) {
+ vdev->v_state = VDEV_STATE_DEGRADED;
+ } else {
+ vdev->v_state = VDEV_STATE_OFFLINE;
+ }
+ } else if (vdev->v_read == vdev_raidz_read) {
+ if (bad_kids > vdev->v_nparity) {
+ vdev->v_state = VDEV_STATE_OFFLINE;
+ } else {
+ vdev->v_state = VDEV_STATE_DEGRADED;
+ }
+ }
+ }
+ }
+}
+
+static spa_t *
+spa_find_by_guid(uint64_t guid)
+{
+ spa_t *spa;
+
+ STAILQ_FOREACH(spa, &zfs_pools, spa_link)
+ if (spa->spa_guid == guid)
+ return (spa);
+
+ return (0);
+}
+
+static spa_t *
+spa_find_by_name(const char *name)
+{
+ spa_t *spa;
+
+ STAILQ_FOREACH(spa, &zfs_pools, spa_link)
+ if (!strcmp(spa->spa_name, name))
+ return (spa);
+
+ return (0);
+}
+
+#ifdef BOOT2
+static spa_t *
+spa_get_primary(void)
+{
+
+ return (STAILQ_FIRST(&zfs_pools));
+}
+
+static vdev_t *
+spa_get_primary_vdev(const spa_t *spa)
+{
+ vdev_t *vdev;
+ vdev_t *kid;
+
+ if (spa == NULL)
+ spa = spa_get_primary();
+ if (spa == NULL)
+ return (NULL);
+ vdev = STAILQ_FIRST(&spa->spa_vdevs);
+ if (vdev == NULL)
+ return (NULL);
+ for (kid = STAILQ_FIRST(&vdev->v_children); kid != NULL;
+ kid = STAILQ_FIRST(&vdev->v_children))
+ vdev = kid;
+ return (vdev);
+}
+#endif
+
+static spa_t *
+spa_create(uint64_t guid, const char *name)
+{
+ spa_t *spa;
+
+ if ((spa = malloc(sizeof(spa_t))) == NULL)
+ return (NULL);
+ memset(spa, 0, sizeof(spa_t));
+ if ((spa->spa_name = strdup(name)) == NULL) {
+ free(spa);
+ return (NULL);
+ }
+ STAILQ_INIT(&spa->spa_vdevs);
+ spa->spa_guid = guid;
+ STAILQ_INSERT_TAIL(&zfs_pools, spa, spa_link);
+
+ return (spa);
+}
+
+static const char *
+state_name(vdev_state_t state)
+{
+ static const char* names[] = {
+ "UNKNOWN",
+ "CLOSED",
+ "OFFLINE",
+ "REMOVED",
+ "CANT_OPEN",
+ "FAULTED",
+ "DEGRADED",
+ "ONLINE"
+ };
+ return names[state];
+}
+
+#ifdef BOOT2
+
+#define pager_printf printf
+
+#else
+
+static int
+pager_printf(const char *fmt, ...)
+{
+ char line[80];
+ va_list args;
+
+ va_start(args, fmt);
+ vsprintf(line, fmt, args);
+ va_end(args);
+
+ return (pager_output(line));
+}
+
+#endif
+
+#define STATUS_FORMAT " %s %s\n"
+
+static int
+print_state(int indent, const char *name, vdev_state_t state)
+{
+ char buf[512];
+ int i;
+
+ buf[0] = 0;
+ for (i = 0; i < indent; i++)
+ strcat(buf, " ");
+ strcat(buf, name);
+
+ return (pager_printf(STATUS_FORMAT, buf, state_name(state)));
+}
+
+static int
+vdev_status(vdev_t *vdev, int indent)
+{
+ vdev_t *kid;
+ int ret;
+ ret = print_state(indent, vdev->v_name, vdev->v_state);
+ if (ret != 0)
+ return (ret);
+
+ STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
+ ret = vdev_status(kid, indent + 1);
+ if (ret != 0)
+ return (ret);
+ }
+ return (ret);
+}
+
+static int
+spa_status(spa_t *spa)
+{
+ static char bootfs[ZFS_MAXNAMELEN];
+ uint64_t rootid;
+ vdev_t *vdev;
+ int good_kids, bad_kids, degraded_kids, ret;
+ vdev_state_t state;
+
+ ret = pager_printf(" pool: %s\n", spa->spa_name);
+ if (ret != 0)
+ return (ret);
+
+ if (zfs_get_root(spa, &rootid) == 0 &&
+ zfs_rlookup(spa, rootid, bootfs) == 0) {
+ if (bootfs[0] == '\0')
+ ret = pager_printf("bootfs: %s\n", spa->spa_name);
+ else
+ ret = pager_printf("bootfs: %s/%s\n", spa->spa_name,
+ bootfs);
+ if (ret != 0)
+ return (ret);
+ }
+ ret = pager_printf("config:\n\n");
+ if (ret != 0)
+ return (ret);
+ ret = pager_printf(STATUS_FORMAT, "NAME", "STATE");
+ if (ret != 0)
+ return (ret);
+
+ good_kids = 0;
+ degraded_kids = 0;
+ bad_kids = 0;
+ STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink) {
+ if (vdev->v_state == VDEV_STATE_HEALTHY)
+ good_kids++;
+ else if (vdev->v_state == VDEV_STATE_DEGRADED)
+ degraded_kids++;
+ else
+ bad_kids++;
+ }
+
+ state = VDEV_STATE_CLOSED;
+ if (good_kids > 0 && (degraded_kids + bad_kids) == 0)
+ state = VDEV_STATE_HEALTHY;
+ else if ((good_kids + degraded_kids) > 0)
+ state = VDEV_STATE_DEGRADED;
+
+ ret = print_state(0, spa->spa_name, state);
+ if (ret != 0)
+ return (ret);
+ STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink) {
+ ret = vdev_status(vdev, 1);
+ if (ret != 0)
+ return (ret);
+ }
+ return (ret);
+}
+
+static int
+spa_all_status(void)
+{
+ spa_t *spa;
+ int first = 1, ret = 0;
+
+ STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
+ if (!first) {
+ ret = pager_printf("\n");
+ if (ret != 0)
+ return (ret);
+ }
+ first = 0;
+ ret = spa_status(spa);
+ if (ret != 0)
+ return (ret);
+ }
+ return (ret);
+}
+
+uint64_t
+vdev_label_offset(uint64_t psize, int l, uint64_t offset)
+{
+ uint64_t label_offset;
+
+ if (l < VDEV_LABELS / 2)
+ label_offset = 0;
+ else
+ label_offset = psize - VDEV_LABELS * sizeof (vdev_label_t);
+
+ return (offset + l * sizeof (vdev_label_t) + label_offset);
+}
+
+static int
+vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
+{
+ vdev_t vtmp;
+ vdev_phys_t *vdev_label = (vdev_phys_t *) zap_scratch;
+ vdev_phys_t *tmp_label;
+ spa_t *spa;
+ vdev_t *vdev, *top_vdev, *pool_vdev;
+ off_t off;
+ blkptr_t bp;
+ const unsigned char *nvlist = NULL;
+ uint64_t val;
+ uint64_t guid;
+ uint64_t best_txg = 0;
+ uint64_t pool_txg, pool_guid;
+ uint64_t psize;
+ const char *pool_name;
+ const unsigned char *vdevs;
+ const unsigned char *features;
+ int i, l, rc, is_newer;
+ char *upbuf;
+ const struct uberblock *up;
+
+ /*
+ * Load the vdev label and figure out which
+ * uberblock is most current.
+ */
+ memset(&vtmp, 0, sizeof(vtmp));
+ vtmp.v_phys_read = _read;
+ vtmp.v_read_priv = read_priv;
+ psize = P2ALIGN(ldi_get_size(read_priv),
+ (uint64_t)sizeof (vdev_label_t));
+
+ /* Test for minimum pool size. */
+ if (psize < SPA_MINDEVSIZE)
+ return (EIO);
+
+ tmp_label = zfs_alloc(sizeof(vdev_phys_t));
+
+ for (l = 0; l < VDEV_LABELS; l++) {
+ off = vdev_label_offset(psize, l,
+ offsetof(vdev_label_t, vl_vdev_phys));
+
+ BP_ZERO(&bp);
+ BP_SET_LSIZE(&bp, sizeof(vdev_phys_t));
+ BP_SET_PSIZE(&bp, sizeof(vdev_phys_t));
+ BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
+ BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
+ DVA_SET_OFFSET(BP_IDENTITY(&bp), off);
+ ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0);
+
+ if (vdev_read_phys(&vtmp, &bp, tmp_label, off, 0))
+ continue;
+
+ if (tmp_label->vp_nvlist[0] != NV_ENCODE_XDR)
+ continue;
+
+ nvlist = (const unsigned char *) tmp_label->vp_nvlist + 4;
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_TXG,
+ DATA_TYPE_UINT64, NULL, &pool_txg) != 0)
+ continue;
+
+ if (best_txg <= pool_txg) {
+ best_txg = pool_txg;
+ memcpy(vdev_label, tmp_label, sizeof (vdev_phys_t));
+ }
+ }
+
+ zfs_free(tmp_label, sizeof (vdev_phys_t));
+
+ if (best_txg == 0)
+ return (EIO);
+
+ if (vdev_label->vp_nvlist[0] != NV_ENCODE_XDR)
+ return (EIO);
+
+ nvlist = (const unsigned char *) vdev_label->vp_nvlist + 4;
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_VERSION, DATA_TYPE_UINT64,
+ NULL, &val) != 0) {
+ return (EIO);
+ }
+
+ if (!SPA_VERSION_IS_SUPPORTED(val)) {
+ printf("ZFS: unsupported ZFS version %u (should be %u)\n",
+ (unsigned) val, (unsigned) SPA_VERSION);
+ return (EIO);
+ }
+
+ /* Check ZFS features for read */
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_FEATURES_FOR_READ,
+ DATA_TYPE_NVLIST, NULL, &features) == 0 &&
+ nvlist_check_features_for_read(features) != 0) {
+ return (EIO);
+ }
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_STATE, DATA_TYPE_UINT64,
+ NULL, &val) != 0) {
+ return (EIO);
+ }
+
+ if (val == POOL_STATE_DESTROYED) {
+ /* We don't boot only from destroyed pools. */
+ return (EIO);
+ }
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_TXG, DATA_TYPE_UINT64,
+ NULL, &pool_txg) != 0 ||
+ nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
+ NULL, &pool_guid) != 0 ||
+ nvlist_find(nvlist, ZPOOL_CONFIG_POOL_NAME, DATA_TYPE_STRING,
+ NULL, &pool_name) != 0) {
+ /*
+ * Cache and spare devices end up here - just ignore
+ * them.
+ */
+ /*printf("ZFS: can't find pool details\n");*/
+ return (EIO);
+ }
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_IS_LOG, DATA_TYPE_UINT64,
+ NULL, &val) == 0 && val != 0) {
+ return (EIO);
+ }
+
+ /*
+ * Create the pool if this is the first time we've seen it.
+ */
+ spa = spa_find_by_guid(pool_guid);
+ if (spa == NULL) {
+ spa = spa_create(pool_guid, pool_name);
+ if (spa == NULL)
+ return (ENOMEM);
+ }
+ if (pool_txg > spa->spa_txg) {
+ spa->spa_txg = pool_txg;
+ is_newer = 1;
+ } else {
+ is_newer = 0;
+ }
+
+ /*
+ * Get the vdev tree and create our in-core copy of it.
+ * If we already have a vdev with this guid, this must
+ * be some kind of alias (overlapping slices, dangerously dedicated
+ * disks etc).
+ */
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
+ NULL, &guid) != 0) {
+ return (EIO);
+ }
+ vdev = vdev_find(guid);
+ if (vdev && vdev->v_phys_read) /* Has this vdev already been inited? */
+ return (EIO);
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST,
+ NULL, &vdevs)) {
+ return (EIO);
+ }
+
+ rc = vdev_init_from_nvlist(vdevs, NULL, &top_vdev, is_newer);
+ if (rc != 0)
+ return (rc);
+
+ /*
+ * Add the toplevel vdev to the pool if its not already there.
+ */
+ STAILQ_FOREACH(pool_vdev, &spa->spa_vdevs, v_childlink)
+ if (top_vdev == pool_vdev)
+ break;
+ if (!pool_vdev && top_vdev) {
+ top_vdev->spa = spa;
+ STAILQ_INSERT_TAIL(&spa->spa_vdevs, top_vdev, v_childlink);
+ }
+
+ /*
+ * We should already have created an incomplete vdev for this
+ * vdev. Find it and initialise it with our read proc.
+ */
+ vdev = vdev_find(guid);
+ if (vdev) {
+ vdev->v_phys_read = _read;
+ vdev->v_read_priv = read_priv;
+ vdev->v_state = VDEV_STATE_HEALTHY;
+ } else {
+ printf("ZFS: inconsistent nvlist contents\n");
+ return (EIO);
+ }
+
+ /*
+ * Re-evaluate top-level vdev state.
+ */
+ vdev_set_state(top_vdev);
+
+ /*
+ * Ok, we are happy with the pool so far. Lets find
+ * the best uberblock and then we can actually access
+ * the contents of the pool.
+ */
+ upbuf = zfs_alloc(VDEV_UBERBLOCK_SIZE(vdev));
+ up = (const struct uberblock *)upbuf;
+ for (l = 0; l < VDEV_LABELS; l++) {
+ for (i = 0; i < VDEV_UBERBLOCK_COUNT(vdev); i++) {
+ off = vdev_label_offset(psize, l,
+ VDEV_UBERBLOCK_OFFSET(vdev, i));
+ BP_ZERO(&bp);
+ DVA_SET_OFFSET(&bp.blk_dva[0], off);
+ BP_SET_LSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev));
+ BP_SET_PSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev));
+ BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
+ BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
+ ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0);
+
+ if (vdev_read_phys(vdev, &bp, upbuf, off, 0))
+ continue;
+
+ if (up->ub_magic != UBERBLOCK_MAGIC)
+ continue;
+ if (up->ub_txg < spa->spa_txg)
+ continue;
+ if (up->ub_txg > spa->spa_uberblock.ub_txg ||
+ (up->ub_txg == spa->spa_uberblock.ub_txg &&
+ up->ub_timestamp >
+ spa->spa_uberblock.ub_timestamp)) {
+ spa->spa_uberblock = *up;
+ }
+ }
+ }
+ zfs_free(upbuf, VDEV_UBERBLOCK_SIZE(vdev));
+
+ vdev->spa = spa;
+ if (spap != NULL)
+ *spap = spa;
+ return (0);
+}
+
+static int
+ilog2(int n)
+{
+ int v;
+
+ for (v = 0; v < 32; v++)
+ if (n == (1 << v))
+ return v;
+ return -1;
+}
+
+static int
+zio_read_gang(const spa_t *spa, const blkptr_t *bp, void *buf)
+{
+ blkptr_t gbh_bp;
+ zio_gbh_phys_t zio_gb;
+ char *pbuf;
+ int i;
+
+ /* Artificial BP for gang block header. */
+ gbh_bp = *bp;
+ BP_SET_PSIZE(&gbh_bp, SPA_GANGBLOCKSIZE);
+ BP_SET_LSIZE(&gbh_bp, SPA_GANGBLOCKSIZE);
+ BP_SET_CHECKSUM(&gbh_bp, ZIO_CHECKSUM_GANG_HEADER);
+ BP_SET_COMPRESS(&gbh_bp, ZIO_COMPRESS_OFF);
+ for (i = 0; i < SPA_DVAS_PER_BP; i++)
+ DVA_SET_GANG(&gbh_bp.blk_dva[i], 0);
+
+ /* Read gang header block using the artificial BP. */
+ if (zio_read(spa, &gbh_bp, &zio_gb))
+ return (EIO);
+
+ pbuf = buf;
+ for (i = 0; i < SPA_GBH_NBLKPTRS; i++) {
+ blkptr_t *gbp = &zio_gb.zg_blkptr[i];
+
+ if (BP_IS_HOLE(gbp))
+ continue;
+ if (zio_read(spa, gbp, pbuf))
+ return (EIO);
+ pbuf += BP_GET_PSIZE(gbp);
+ }
+
+ if (zio_checksum_verify(spa, bp, buf))
+ return (EIO);
+ return (0);
+}
+
+static int
+zio_read(const spa_t *spa, const blkptr_t *bp, void *buf)
+{
+ int cpfunc = BP_GET_COMPRESS(bp);
+ uint64_t align, size;
+ void *pbuf;
+ int i, error;
+
+ /*
+ * Process data embedded in block pointer
+ */
+ if (BP_IS_EMBEDDED(bp)) {
+ ASSERT(BPE_GET_ETYPE(bp) == BP_EMBEDDED_TYPE_DATA);
+
+ size = BPE_GET_PSIZE(bp);
+ ASSERT(size <= BPE_PAYLOAD_SIZE);
+
+ if (cpfunc != ZIO_COMPRESS_OFF)
+ pbuf = zfs_alloc(size);
+ else
+ pbuf = buf;
+
+ decode_embedded_bp_compressed(bp, pbuf);
+ error = 0;
+
+ if (cpfunc != ZIO_COMPRESS_OFF) {
+ error = zio_decompress_data(cpfunc, pbuf,
+ size, buf, BP_GET_LSIZE(bp));
+ zfs_free(pbuf, size);
+ }
+ if (error != 0)
+ printf("ZFS: i/o error - unable to decompress block pointer data, error %d\n",
+ error);
+ return (error);
+ }
+
+ error = EIO;
+
+ for (i = 0; i < SPA_DVAS_PER_BP; i++) {
+ const dva_t *dva = &bp->blk_dva[i];
+ vdev_t *vdev;
+ int vdevid;
+ off_t offset;
+
+ if (!dva->dva_word[0] && !dva->dva_word[1])
+ continue;
+
+ vdevid = DVA_GET_VDEV(dva);
+ offset = DVA_GET_OFFSET(dva);
+ STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink) {
+ if (vdev->v_id == vdevid)
+ break;
+ }
+ if (!vdev || !vdev->v_read)
+ continue;
+
+ size = BP_GET_PSIZE(bp);
+ if (vdev->v_read == vdev_raidz_read) {
+ align = 1ULL << vdev->v_top->v_ashift;
+ if (P2PHASE(size, align) != 0)
+ size = P2ROUNDUP(size, align);
+ }
+ if (size != BP_GET_PSIZE(bp) || cpfunc != ZIO_COMPRESS_OFF)
+ pbuf = zfs_alloc(size);
+ else
+ pbuf = buf;
+
+ if (DVA_GET_GANG(dva))
+ error = zio_read_gang(spa, bp, pbuf);
+ else
+ error = vdev->v_read(vdev, bp, pbuf, offset, size);
+ if (error == 0) {
+ if (cpfunc != ZIO_COMPRESS_OFF)
+ error = zio_decompress_data(cpfunc, pbuf,
+ BP_GET_PSIZE(bp), buf, BP_GET_LSIZE(bp));
+ else if (size != BP_GET_PSIZE(bp))
+ bcopy(pbuf, buf, BP_GET_PSIZE(bp));
+ }
+ if (buf != pbuf)
+ zfs_free(pbuf, size);
+ if (error == 0)
+ break;
+ }
+ if (error != 0)
+ printf("ZFS: i/o error - all block copies unavailable\n");
+ return (error);
+}
+
+static int
+dnode_read(const spa_t *spa, const dnode_phys_t *dnode, off_t offset, void *buf, size_t buflen)
+{
+ int ibshift = dnode->dn_indblkshift - SPA_BLKPTRSHIFT;
+ int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
+ int nlevels = dnode->dn_nlevels;
+ int i, rc;
+
+ if (bsize > SPA_MAXBLOCKSIZE) {
+ printf("ZFS: I/O error - blocks larger than %llu are not "
+ "supported\n", SPA_MAXBLOCKSIZE);
+ return (EIO);
+ }
+
+ /*
+ * Note: bsize may not be a power of two here so we need to do an
+ * actual divide rather than a bitshift.
+ */
+ while (buflen > 0) {
+ uint64_t bn = offset / bsize;
+ int boff = offset % bsize;
+ int ibn;
+ const blkptr_t *indbp;
+ blkptr_t bp;
+
+ if (bn > dnode->dn_maxblkid)
+ return (EIO);
+
+ if (dnode == dnode_cache_obj && bn == dnode_cache_bn)
+ goto cached;
+
+ indbp = dnode->dn_blkptr;
+ for (i = 0; i < nlevels; i++) {
+ /*
+ * Copy the bp from the indirect array so that
+ * we can re-use the scratch buffer for multi-level
+ * objects.
+ */
+ ibn = bn >> ((nlevels - i - 1) * ibshift);
+ ibn &= ((1 << ibshift) - 1);
+ bp = indbp[ibn];
+ if (BP_IS_HOLE(&bp)) {
+ memset(dnode_cache_buf, 0, bsize);
+ break;
+ }
+ rc = zio_read(spa, &bp, dnode_cache_buf);
+ if (rc)
+ return (rc);
+ indbp = (const blkptr_t *) dnode_cache_buf;
+ }
+ dnode_cache_obj = dnode;
+ dnode_cache_bn = bn;
+ cached:
+
+ /*
+ * The buffer contains our data block. Copy what we
+ * need from it and loop.
+ */
+ i = bsize - boff;
+ if (i > buflen) i = buflen;
+ memcpy(buf, &dnode_cache_buf[boff], i);
+ buf = ((char*) buf) + i;
+ offset += i;
+ buflen -= i;
+ }
+
+ return (0);
+}
+
+/*
+ * Lookup a value in a microzap directory. Assumes that the zap
+ * scratch buffer contains the directory contents.
+ */
+static int
+mzap_lookup(const dnode_phys_t *dnode, const char *name, uint64_t *value)
+{
+ const mzap_phys_t *mz;
+ const mzap_ent_phys_t *mze;
+ size_t size;
+ int chunks, i;
+
+ /*
+ * Microzap objects use exactly one block. Read the whole
+ * thing.
+ */
+ size = dnode->dn_datablkszsec * 512;
+
+ mz = (const mzap_phys_t *) zap_scratch;
+ chunks = size / MZAP_ENT_LEN - 1;
+
+ for (i = 0; i < chunks; i++) {
+ mze = &mz->mz_chunk[i];
+ if (!strcmp(mze->mze_name, name)) {
+ *value = mze->mze_value;
+ return (0);
+ }
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Compare a name with a zap leaf entry. Return non-zero if the name
+ * matches.
+ */
+static int
+fzap_name_equal(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc, const char *name)
+{
+ size_t namelen;
+ const zap_leaf_chunk_t *nc;
+ const char *p;
+
+ namelen = zc->l_entry.le_name_numints;
+
+ nc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_name_chunk);
+ p = name;
+ while (namelen > 0) {
+ size_t len;
+ len = namelen;
+ if (len > ZAP_LEAF_ARRAY_BYTES)
+ len = ZAP_LEAF_ARRAY_BYTES;
+ if (memcmp(p, nc->l_array.la_array, len))
+ return (0);
+ p += len;
+ namelen -= len;
+ nc = &ZAP_LEAF_CHUNK(zl, nc->l_array.la_next);
+ }
+
+ return 1;
+}
+
+/*
+ * Extract a uint64_t value from a zap leaf entry.
+ */
+static uint64_t
+fzap_leaf_value(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc)
+{
+ const zap_leaf_chunk_t *vc;
+ int i;
+ uint64_t value;
+ const uint8_t *p;
+
+ vc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_value_chunk);
+ for (i = 0, value = 0, p = vc->l_array.la_array; i < 8; i++) {
+ value = (value << 8) | p[i];
+ }
+
+ return value;
+}
+
+static void
+stv(int len, void *addr, uint64_t value)
+{
+ switch (len) {
+ case 1:
+ *(uint8_t *)addr = value;
+ return;
+ case 2:
+ *(uint16_t *)addr = value;
+ return;
+ case 4:
+ *(uint32_t *)addr = value;
+ return;
+ case 8:
+ *(uint64_t *)addr = value;
+ return;
+ }
+}
+
+/*
+ * Extract a array from a zap leaf entry.
+ */
+static void
+fzap_leaf_array(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc,
+ uint64_t integer_size, uint64_t num_integers, void *buf)
+{
+ uint64_t array_int_len = zc->l_entry.le_value_intlen;
+ uint64_t value = 0;
+ uint64_t *u64 = buf;
+ char *p = buf;
+ int len = MIN(zc->l_entry.le_value_numints, num_integers);
+ int chunk = zc->l_entry.le_value_chunk;
+ int byten = 0;
+
+ if (integer_size == 8 && len == 1) {
+ *u64 = fzap_leaf_value(zl, zc);
+ return;
+ }
+
+ while (len > 0) {
+ struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(zl, chunk).l_array;
+ int i;
+
+ ASSERT3U(chunk, <, ZAP_LEAF_NUMCHUNKS(zl));
+ for (i = 0; i < ZAP_LEAF_ARRAY_BYTES && len > 0; i++) {
+ value = (value << 8) | la->la_array[i];
+ byten++;
+ if (byten == array_int_len) {
+ stv(integer_size, p, value);
+ byten = 0;
+ len--;
+ if (len == 0)
+ return;
+ p += integer_size;
+ }
+ }
+ chunk = la->la_next;
+ }
+}
+
+/*
+ * Lookup a value in a fatzap directory. Assumes that the zap scratch
+ * buffer contains the directory header.
+ */
+static int
+fzap_lookup(const spa_t *spa, const dnode_phys_t *dnode, const char *name,
+ uint64_t integer_size, uint64_t num_integers, void *value)
+{
+ int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
+ zap_phys_t zh = *(zap_phys_t *) zap_scratch;
+ fat_zap_t z;
+ uint64_t *ptrtbl;
+ uint64_t hash;
+ int rc;
+
+ if (zh.zap_magic != ZAP_MAGIC)
+ return (EIO);
+
+ z.zap_block_shift = ilog2(bsize);
+ z.zap_phys = (zap_phys_t *) zap_scratch;
+
+ /*
+ * Figure out where the pointer table is and read it in if necessary.
+ */
+ if (zh.zap_ptrtbl.zt_blk) {
+ rc = dnode_read(spa, dnode, zh.zap_ptrtbl.zt_blk * bsize,
+ zap_scratch, bsize);
+ if (rc)
+ return (rc);
+ ptrtbl = (uint64_t *) zap_scratch;
+ } else {
+ ptrtbl = &ZAP_EMBEDDED_PTRTBL_ENT(&z, 0);
+ }
+
+ hash = zap_hash(zh.zap_salt, name);
+
+ zap_leaf_t zl;
+ zl.l_bs = z.zap_block_shift;
+
+ off_t off = ptrtbl[hash >> (64 - zh.zap_ptrtbl.zt_shift)] << zl.l_bs;
+ zap_leaf_chunk_t *zc;
+
+ rc = dnode_read(spa, dnode, off, zap_scratch, bsize);
+ if (rc)
+ return (rc);
+
+ zl.l_phys = (zap_leaf_phys_t *) zap_scratch;
+
+ /*
+ * Make sure this chunk matches our hash.
+ */
+ if (zl.l_phys->l_hdr.lh_prefix_len > 0
+ && zl.l_phys->l_hdr.lh_prefix
+ != hash >> (64 - zl.l_phys->l_hdr.lh_prefix_len))
+ return (ENOENT);
+
+ /*
+ * Hash within the chunk to find our entry.
+ */
+ int shift = (64 - ZAP_LEAF_HASH_SHIFT(&zl) - zl.l_phys->l_hdr.lh_prefix_len);
+ int h = (hash >> shift) & ((1 << ZAP_LEAF_HASH_SHIFT(&zl)) - 1);
+ h = zl.l_phys->l_hash[h];
+ if (h == 0xffff)
+ return (ENOENT);
+ zc = &ZAP_LEAF_CHUNK(&zl, h);
+ while (zc->l_entry.le_hash != hash) {
+ if (zc->l_entry.le_next == 0xffff) {
+ zc = NULL;
+ break;
+ }
+ zc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_next);
+ }
+ if (fzap_name_equal(&zl, zc, name)) {
+ if (zc->l_entry.le_value_intlen * zc->l_entry.le_value_numints >
+ integer_size * num_integers)
+ return (E2BIG);
+ fzap_leaf_array(&zl, zc, integer_size, num_integers, value);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Lookup a name in a zap object and return its value as a uint64_t.
+ */
+static int
+zap_lookup(const spa_t *spa, const dnode_phys_t *dnode, const char *name,
+ uint64_t integer_size, uint64_t num_integers, void *value)
+{
+ int rc;
+ uint64_t zap_type;
+ size_t size = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
+
+ rc = dnode_read(spa, dnode, 0, zap_scratch, size);
+ if (rc)
+ return (rc);
+
+ zap_type = *(uint64_t *) zap_scratch;
+ if (zap_type == ZBT_MICRO)
+ return mzap_lookup(dnode, name, value);
+ else if (zap_type == ZBT_HEADER) {
+ return fzap_lookup(spa, dnode, name, integer_size,
+ num_integers, value);
+ }
+ printf("ZFS: invalid zap_type=%d\n", (int)zap_type);
+ return (EIO);
+}
+
+/*
+ * List a microzap directory. Assumes that the zap scratch buffer contains
+ * the directory contents.
+ */
+static int
+mzap_list(const dnode_phys_t *dnode, int (*callback)(const char *, uint64_t))
+{
+ const mzap_phys_t *mz;
+ const mzap_ent_phys_t *mze;
+ size_t size;
+ int chunks, i, rc;
+
+ /*
+ * Microzap objects use exactly one block. Read the whole
+ * thing.
+ */
+ size = dnode->dn_datablkszsec * 512;
+ mz = (const mzap_phys_t *) zap_scratch;
+ chunks = size / MZAP_ENT_LEN - 1;
+
+ for (i = 0; i < chunks; i++) {
+ mze = &mz->mz_chunk[i];
+ if (mze->mze_name[0]) {
+ rc = callback(mze->mze_name, mze->mze_value);
+ if (rc != 0)
+ return (rc);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * List a fatzap directory. Assumes that the zap scratch buffer contains
+ * the directory header.
+ */
+static int
+fzap_list(const spa_t *spa, const dnode_phys_t *dnode, int (*callback)(const char *, uint64_t))
+{
+ int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
+ zap_phys_t zh = *(zap_phys_t *) zap_scratch;
+ fat_zap_t z;
+ int i, j, rc;
+
+ if (zh.zap_magic != ZAP_MAGIC)
+ return (EIO);
+
+ z.zap_block_shift = ilog2(bsize);
+ z.zap_phys = (zap_phys_t *) zap_scratch;
+
+ /*
+ * This assumes that the leaf blocks start at block 1. The
+ * documentation isn't exactly clear on this.
+ */
+ zap_leaf_t zl;
+ zl.l_bs = z.zap_block_shift;
+ for (i = 0; i < zh.zap_num_leafs; i++) {
+ off_t off = (i + 1) << zl.l_bs;
+ char name[256], *p;
+ uint64_t value;
+
+ if (dnode_read(spa, dnode, off, zap_scratch, bsize))
+ return (EIO);
+
+ zl.l_phys = (zap_leaf_phys_t *) zap_scratch;
+
+ for (j = 0; j < ZAP_LEAF_NUMCHUNKS(&zl); j++) {
+ zap_leaf_chunk_t *zc, *nc;
+ int namelen;
+
+ zc = &ZAP_LEAF_CHUNK(&zl, j);
+ if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY)
+ continue;
+ namelen = zc->l_entry.le_name_numints;
+ if (namelen > sizeof(name))
+ namelen = sizeof(name);
+
+ /*
+ * Paste the name back together.
+ */
+ nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk);
+ p = name;
+ while (namelen > 0) {
+ int len;
+ len = namelen;
+ if (len > ZAP_LEAF_ARRAY_BYTES)
+ len = ZAP_LEAF_ARRAY_BYTES;
+ memcpy(p, nc->l_array.la_array, len);
+ p += len;
+ namelen -= len;
+ nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next);
+ }
+
+ /*
+ * Assume the first eight bytes of the value are
+ * a uint64_t.
+ */
+ value = fzap_leaf_value(&zl, zc);
+
+ //printf("%s 0x%jx\n", name, (uintmax_t)value);
+ rc = callback((const char *)name, value);
+ if (rc != 0)
+ return (rc);
+ }
+ }
+
+ return (0);
+}
+
+static int zfs_printf(const char *name, uint64_t value __unused)
+{
+
+ printf("%s\n", name);
+
+ return (0);
+}
+
+/*
+ * List a zap directory.
+ */
+static int
+zap_list(const spa_t *spa, const dnode_phys_t *dnode)
+{
+ uint64_t zap_type;
+ size_t size = dnode->dn_datablkszsec * 512;
+
+ if (dnode_read(spa, dnode, 0, zap_scratch, size))
+ return (EIO);
+
+ zap_type = *(uint64_t *) zap_scratch;
+ if (zap_type == ZBT_MICRO)
+ return mzap_list(dnode, zfs_printf);
+ else
+ return fzap_list(spa, dnode, zfs_printf);
+}
+
+static int
+objset_get_dnode(const spa_t *spa, const objset_phys_t *os, uint64_t objnum, dnode_phys_t *dnode)
+{
+ off_t offset;
+
+ offset = objnum * sizeof(dnode_phys_t);
+ return dnode_read(spa, &os->os_meta_dnode, offset,
+ dnode, sizeof(dnode_phys_t));
+}
+
+static int
+mzap_rlookup(const spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value)
+{
+ const mzap_phys_t *mz;
+ const mzap_ent_phys_t *mze;
+ size_t size;
+ int chunks, i;
+
+ /*
+ * Microzap objects use exactly one block. Read the whole
+ * thing.
+ */
+ size = dnode->dn_datablkszsec * 512;
+
+ mz = (const mzap_phys_t *) zap_scratch;
+ chunks = size / MZAP_ENT_LEN - 1;
+
+ for (i = 0; i < chunks; i++) {
+ mze = &mz->mz_chunk[i];
+ if (value == mze->mze_value) {
+ strcpy(name, mze->mze_name);
+ return (0);
+ }
+ }
+
+ return (ENOENT);
+}
+
+static void
+fzap_name_copy(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc, char *name)
+{
+ size_t namelen;
+ const zap_leaf_chunk_t *nc;
+ char *p;
+
+ namelen = zc->l_entry.le_name_numints;
+
+ nc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_name_chunk);
+ p = name;
+ while (namelen > 0) {
+ size_t len;
+ len = namelen;
+ if (len > ZAP_LEAF_ARRAY_BYTES)
+ len = ZAP_LEAF_ARRAY_BYTES;
+ memcpy(p, nc->l_array.la_array, len);
+ p += len;
+ namelen -= len;
+ nc = &ZAP_LEAF_CHUNK(zl, nc->l_array.la_next);
+ }
+
+ *p = '\0';
+}
+
+static int
+fzap_rlookup(const spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value)
+{
+ int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
+ zap_phys_t zh = *(zap_phys_t *) zap_scratch;
+ fat_zap_t z;
+ int i, j;
+
+ if (zh.zap_magic != ZAP_MAGIC)
+ return (EIO);
+
+ z.zap_block_shift = ilog2(bsize);
+ z.zap_phys = (zap_phys_t *) zap_scratch;
+
+ /*
+ * This assumes that the leaf blocks start at block 1. The
+ * documentation isn't exactly clear on this.
+ */
+ zap_leaf_t zl;
+ zl.l_bs = z.zap_block_shift;
+ for (i = 0; i < zh.zap_num_leafs; i++) {
+ off_t off = (i + 1) << zl.l_bs;
+
+ if (dnode_read(spa, dnode, off, zap_scratch, bsize))
+ return (EIO);
+
+ zl.l_phys = (zap_leaf_phys_t *) zap_scratch;
+
+ for (j = 0; j < ZAP_LEAF_NUMCHUNKS(&zl); j++) {
+ zap_leaf_chunk_t *zc;
+
+ zc = &ZAP_LEAF_CHUNK(&zl, j);
+ if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY)
+ continue;
+ if (zc->l_entry.le_value_intlen != 8 ||
+ zc->l_entry.le_value_numints != 1)
+ continue;
+
+ if (fzap_leaf_value(&zl, zc) == value) {
+ fzap_name_copy(&zl, zc, name);
+ return (0);
+ }
+ }
+ }
+
+ return (ENOENT);
+}
+
+static int
+zap_rlookup(const spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value)
+{
+ int rc;
+ uint64_t zap_type;
+ size_t size = dnode->dn_datablkszsec * 512;
+
+ rc = dnode_read(spa, dnode, 0, zap_scratch, size);
+ if (rc)
+ return (rc);
+
+ zap_type = *(uint64_t *) zap_scratch;
+ if (zap_type == ZBT_MICRO)
+ return mzap_rlookup(spa, dnode, name, value);
+ else
+ return fzap_rlookup(spa, dnode, name, value);
+}
+
+static int
+zfs_rlookup(const spa_t *spa, uint64_t objnum, char *result)
+{
+ char name[256];
+ char component[256];
+ uint64_t dir_obj, parent_obj, child_dir_zapobj;
+ dnode_phys_t child_dir_zap, dataset, dir, parent;
+ dsl_dir_phys_t *dd;
+ dsl_dataset_phys_t *ds;
+ char *p;
+ int len;
+
+ p = &name[sizeof(name) - 1];
+ *p = '\0';
+
+ if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
+ printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
+ return (EIO);
+ }
+ ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
+ dir_obj = ds->ds_dir_obj;
+
+ for (;;) {
+ if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir) != 0)
+ return (EIO);
+ dd = (dsl_dir_phys_t *)&dir.dn_bonus;
+
+ /* Actual loop condition. */
+ parent_obj = dd->dd_parent_obj;
+ if (parent_obj == 0)
+ break;
+
+ if (objset_get_dnode(spa, &spa->spa_mos, parent_obj, &parent) != 0)
+ return (EIO);
+ dd = (dsl_dir_phys_t *)&parent.dn_bonus;
+ child_dir_zapobj = dd->dd_child_dir_zapobj;
+ if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap) != 0)
+ return (EIO);
+ if (zap_rlookup(spa, &child_dir_zap, component, dir_obj) != 0)
+ return (EIO);
+
+ len = strlen(component);
+ p -= len;
+ memcpy(p, component, len);
+ --p;
+ *p = '/';
+
+ /* Actual loop iteration. */
+ dir_obj = parent_obj;
+ }
+
+ if (*p != '\0')
+ ++p;
+ strcpy(result, p);
+
+ return (0);
+}
+
+static int
+zfs_lookup_dataset(const spa_t *spa, const char *name, uint64_t *objnum)
+{
+ char element[256];
+ uint64_t dir_obj, child_dir_zapobj;
+ dnode_phys_t child_dir_zap, dir;
+ dsl_dir_phys_t *dd;
+ const char *p, *q;
+
+ if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT, &dir))
+ return (EIO);
+ if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, sizeof (dir_obj),
+ 1, &dir_obj))
+ return (EIO);
+
+ p = name;
+ for (;;) {
+ if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir))
+ return (EIO);
+ dd = (dsl_dir_phys_t *)&dir.dn_bonus;
+
+ while (*p == '/')
+ p++;
+ /* Actual loop condition #1. */
+ if (*p == '\0')
+ break;
+
+ q = strchr(p, '/');
+ if (q) {
+ memcpy(element, p, q - p);
+ element[q - p] = '\0';
+ p = q + 1;
+ } else {
+ strcpy(element, p);
+ p += strlen(p);
+ }
+
+ child_dir_zapobj = dd->dd_child_dir_zapobj;
+ if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap) != 0)
+ return (EIO);
+
+ /* Actual loop condition #2. */
+ if (zap_lookup(spa, &child_dir_zap, element, sizeof (dir_obj),
+ 1, &dir_obj) != 0)
+ return (ENOENT);
+ }
+
+ *objnum = dd->dd_head_dataset_obj;
+ return (0);
+}
+
+#ifndef BOOT2
+static int
+zfs_list_dataset(const spa_t *spa, uint64_t objnum/*, int pos, char *entry*/)
+{
+ uint64_t dir_obj, child_dir_zapobj;
+ dnode_phys_t child_dir_zap, dir, dataset;
+ dsl_dataset_phys_t *ds;
+ dsl_dir_phys_t *dd;
+
+ if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
+ printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
+ return (EIO);
+ }
+ ds = (dsl_dataset_phys_t *) &dataset.dn_bonus;
+ dir_obj = ds->ds_dir_obj;
+
+ if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir)) {
+ printf("ZFS: can't find dirobj %ju\n", (uintmax_t)dir_obj);
+ return (EIO);
+ }
+ dd = (dsl_dir_phys_t *)&dir.dn_bonus;
+
+ child_dir_zapobj = dd->dd_child_dir_zapobj;
+ if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap) != 0) {
+ printf("ZFS: can't find child zap %ju\n", (uintmax_t)dir_obj);
+ return (EIO);
+ }
+
+ return (zap_list(spa, &child_dir_zap) != 0);
+}
+
+int
+zfs_callback_dataset(const spa_t *spa, uint64_t objnum, int (*callback)(const char *, uint64_t))
+{
+ uint64_t dir_obj, child_dir_zapobj, zap_type;
+ dnode_phys_t child_dir_zap, dir, dataset;
+ dsl_dataset_phys_t *ds;
+ dsl_dir_phys_t *dd;
+ int err;
+
+ err = objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset);
+ if (err != 0) {
+ printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
+ return (err);
+ }
+ ds = (dsl_dataset_phys_t *) &dataset.dn_bonus;
+ dir_obj = ds->ds_dir_obj;
+
+ err = objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir);
+ if (err != 0) {
+ printf("ZFS: can't find dirobj %ju\n", (uintmax_t)dir_obj);
+ return (err);
+ }
+ dd = (dsl_dir_phys_t *)&dir.dn_bonus;
+
+ child_dir_zapobj = dd->dd_child_dir_zapobj;
+ err = objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap);
+ if (err != 0) {
+ printf("ZFS: can't find child zap %ju\n", (uintmax_t)dir_obj);
+ return (err);
+ }
+
+ err = dnode_read(spa, &child_dir_zap, 0, zap_scratch, child_dir_zap.dn_datablkszsec * 512);
+ if (err != 0)
+ return (err);
+
+ zap_type = *(uint64_t *) zap_scratch;
+ if (zap_type == ZBT_MICRO)
+ return mzap_list(&child_dir_zap, callback);
+ else
+ return fzap_list(spa, &child_dir_zap, callback);
+}
+#endif
+
+/*
+ * Find the object set given the object number of its dataset object
+ * and return its details in *objset
+ */
+static int
+zfs_mount_dataset(const spa_t *spa, uint64_t objnum, objset_phys_t *objset)
+{
+ dnode_phys_t dataset;
+ dsl_dataset_phys_t *ds;
+
+ if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
+ printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
+ return (EIO);
+ }
+
+ ds = (dsl_dataset_phys_t *) &dataset.dn_bonus;
+ if (zio_read(spa, &ds->ds_bp, objset)) {
+ printf("ZFS: can't read object set for dataset %ju\n",
+ (uintmax_t)objnum);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+/*
+ * Find the object set pointed to by the BOOTFS property or the root
+ * dataset if there is none and return its details in *objset
+ */
+static int
+zfs_get_root(const spa_t *spa, uint64_t *objid)
+{
+ dnode_phys_t dir, propdir;
+ uint64_t props, bootfs, root;
+
+ *objid = 0;
+
+ /*
+ * Start with the MOS directory object.
+ */
+ if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT, &dir)) {
+ printf("ZFS: can't read MOS object directory\n");
+ return (EIO);
+ }
+
+ /*
+ * Lookup the pool_props and see if we can find a bootfs.
+ */
+ if (zap_lookup(spa, &dir, DMU_POOL_PROPS, sizeof (props), 1, &props) == 0
+ && objset_get_dnode(spa, &spa->spa_mos, props, &propdir) == 0
+ && zap_lookup(spa, &propdir, "bootfs", sizeof (bootfs), 1, &bootfs) == 0
+ && bootfs != 0)
+ {
+ *objid = bootfs;
+ return (0);
+ }
+ /*
+ * Lookup the root dataset directory
+ */
+ if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, sizeof (root), 1, &root)
+ || objset_get_dnode(spa, &spa->spa_mos, root, &dir)) {
+ printf("ZFS: can't find root dsl_dir\n");
+ return (EIO);
+ }
+
+ /*
+ * Use the information from the dataset directory's bonus buffer
+ * to find the dataset object and from that the object set itself.
+ */
+ dsl_dir_phys_t *dd = (dsl_dir_phys_t *) &dir.dn_bonus;
+ *objid = dd->dd_head_dataset_obj;
+ return (0);
+}
+
+static int
+zfs_mount(const spa_t *spa, uint64_t rootobj, struct zfsmount *mount)
+{
+
+ mount->spa = spa;
+
+ /*
+ * Find the root object set if not explicitly provided
+ */
+ if (rootobj == 0 && zfs_get_root(spa, &rootobj)) {
+ printf("ZFS: can't find root filesystem\n");
+ return (EIO);
+ }
+
+ if (zfs_mount_dataset(spa, rootobj, &mount->objset)) {
+ printf("ZFS: can't open root filesystem\n");
+ return (EIO);
+ }
+
+ mount->rootobj = rootobj;
+
+ return (0);
+}
+
+/*
+ * callback function for feature name checks.
+ */
+static int
+check_feature(const char *name, uint64_t value)
+{
+ int i;
+
+ if (value == 0)
+ return (0);
+ if (name[0] == '\0')
+ return (0);
+
+ for (i = 0; features_for_read[i] != NULL; i++) {
+ if (strcmp(name, features_for_read[i]) == 0)
+ return (0);
+ }
+ printf("ZFS: unsupported feature: %s\n", name);
+ return (EIO);
+}
+
+/*
+ * Checks whether the MOS features that are active are supported.
+ */
+static int
+check_mos_features(const spa_t *spa)
+{
+ dnode_phys_t dir;
+ uint64_t objnum, zap_type;
+ size_t size;
+ int rc;
+
+ if ((rc = objset_get_dnode(spa, &spa->spa_mos, DMU_OT_OBJECT_DIRECTORY,
+ &dir)) != 0)
+ return (rc);
+ if ((rc = zap_lookup(spa, &dir, DMU_POOL_FEATURES_FOR_READ,
+ sizeof (objnum), 1, &objnum)) != 0) {
+ /*
+ * It is older pool without features. As we have already
+ * tested the label, just return without raising the error.
+ */
+ return (0);
+ }
+
+ if ((rc = objset_get_dnode(spa, &spa->spa_mos, objnum, &dir)) != 0)
+ return (rc);
+
+ if (dir.dn_type != DMU_OTN_ZAP_METADATA)
+ return (EIO);
+
+ size = dir.dn_datablkszsec * 512;
+ if (dnode_read(spa, &dir, 0, zap_scratch, size))
+ return (EIO);
+
+ zap_type = *(uint64_t *) zap_scratch;
+ if (zap_type == ZBT_MICRO)
+ rc = mzap_list(&dir, check_feature);
+ else
+ rc = fzap_list(spa, &dir, check_feature);
+
+ return (rc);
+}
+
+static int
+zfs_spa_init(spa_t *spa)
+{
+ dnode_phys_t dir;
+ int rc;
+
+ if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) {
+ printf("ZFS: can't read MOS of pool %s\n", spa->spa_name);
+ return (EIO);
+ }
+ if (spa->spa_mos.os_type != DMU_OST_META) {
+ printf("ZFS: corrupted MOS of pool %s\n", spa->spa_name);
+ return (EIO);
+ }
+
+ if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT,
+ &dir)) {
+ printf("ZFS: failed to read pool %s directory object\n",
+ spa->spa_name);
+ return (EIO);
+ }
+ /* this is allowed to fail, older pools do not have salt */
+ rc = zap_lookup(spa, &dir, DMU_POOL_CHECKSUM_SALT, 1,
+ sizeof (spa->spa_cksum_salt.zcs_bytes),
+ spa->spa_cksum_salt.zcs_bytes);
+
+ rc = check_mos_features(spa);
+ if (rc != 0) {
+ printf("ZFS: pool %s is not supported\n", spa->spa_name);
+ }
+
+ return (rc);
+}
+
+static int
+zfs_dnode_stat(const spa_t *spa, dnode_phys_t *dn, struct stat *sb)
+{
+
+ if (dn->dn_bonustype != DMU_OT_SA) {
+ znode_phys_t *zp = (znode_phys_t *)dn->dn_bonus;
+
+ sb->st_mode = zp->zp_mode;
+ sb->st_uid = zp->zp_uid;
+ sb->st_gid = zp->zp_gid;
+ sb->st_size = zp->zp_size;
+ } else {
+ sa_hdr_phys_t *sahdrp;
+ int hdrsize;
+ size_t size = 0;
+ void *buf = NULL;
+
+ if (dn->dn_bonuslen != 0)
+ sahdrp = (sa_hdr_phys_t *)DN_BONUS(dn);
+ else {
+ if ((dn->dn_flags & DNODE_FLAG_SPILL_BLKPTR) != 0) {
+ blkptr_t *bp = DN_SPILL_BLKPTR(dn);
+ int error;
+
+ size = BP_GET_LSIZE(bp);
+ buf = zfs_alloc(size);
+ error = zio_read(spa, bp, buf);
+ if (error != 0) {
+ zfs_free(buf, size);
+ return (error);
+ }
+ sahdrp = buf;
+ } else {
+ return (EIO);
+ }
+ }
+ hdrsize = SA_HDR_SIZE(sahdrp);
+ sb->st_mode = *(uint64_t *)((char *)sahdrp + hdrsize +
+ SA_MODE_OFFSET);
+ sb->st_uid = *(uint64_t *)((char *)sahdrp + hdrsize +
+ SA_UID_OFFSET);
+ sb->st_gid = *(uint64_t *)((char *)sahdrp + hdrsize +
+ SA_GID_OFFSET);
+ sb->st_size = *(uint64_t *)((char *)sahdrp + hdrsize +
+ SA_SIZE_OFFSET);
+ if (buf != NULL)
+ zfs_free(buf, size);
+ }
+
+ return (0);
+}
+
+static int
+zfs_dnode_readlink(const spa_t *spa, dnode_phys_t *dn, char *path, size_t psize)
+{
+ int rc = 0;
+
+ if (dn->dn_bonustype == DMU_OT_SA) {
+ sa_hdr_phys_t *sahdrp = NULL;
+ size_t size = 0;
+ void *buf = NULL;
+ int hdrsize;
+ char *p;
+
+ if (dn->dn_bonuslen != 0)
+ sahdrp = (sa_hdr_phys_t *)DN_BONUS(dn);
+ else {
+ blkptr_t *bp;
+
+ if ((dn->dn_flags & DNODE_FLAG_SPILL_BLKPTR) == 0)
+ return (EIO);
+ bp = DN_SPILL_BLKPTR(dn);
+
+ size = BP_GET_LSIZE(bp);
+ buf = zfs_alloc(size);
+ rc = zio_read(spa, bp, buf);
+ if (rc != 0) {
+ zfs_free(buf, size);
+ return (rc);
+ }
+ sahdrp = buf;
+ }
+ hdrsize = SA_HDR_SIZE(sahdrp);
+ p = (char *)((uintptr_t)sahdrp + hdrsize + SA_SYMLINK_OFFSET);
+ memcpy(path, p, psize);
+ if (buf != NULL)
+ zfs_free(buf, size);
+ return (0);
+ }
+ /*
+ * Second test is purely to silence bogus compiler
+ * warning about accessing past the end of dn_bonus.
+ */
+ if (psize + sizeof(znode_phys_t) <= dn->dn_bonuslen &&
+ sizeof(znode_phys_t) <= sizeof(dn->dn_bonus)) {
+ memcpy(path, &dn->dn_bonus[sizeof(znode_phys_t)], psize);
+ } else {
+ rc = dnode_read(spa, dn, 0, path, psize);
+ }
+ return (rc);
+}
+
+struct obj_list {
+ uint64_t objnum;
+ STAILQ_ENTRY(obj_list) entry;
+};
+
+/*
+ * Lookup a file and return its dnode.
+ */
+static int
+zfs_lookup(const struct zfsmount *mount, const char *upath, dnode_phys_t *dnode)
+{
+ int rc;
+ uint64_t objnum;
+ const spa_t *spa;
+ dnode_phys_t dn;
+ const char *p, *q;
+ char element[256];
+ char path[1024];
+ int symlinks_followed = 0;
+ struct stat sb;
+ struct obj_list *entry, *tentry;
+ STAILQ_HEAD(, obj_list) on_cache = STAILQ_HEAD_INITIALIZER(on_cache);
+
+ spa = mount->spa;
+ if (mount->objset.os_type != DMU_OST_ZFS) {
+ printf("ZFS: unexpected object set type %ju\n",
+ (uintmax_t)mount->objset.os_type);
+ return (EIO);
+ }
+
+ if ((entry = malloc(sizeof(struct obj_list))) == NULL)
+ return (ENOMEM);
+
+ /*
+ * Get the root directory dnode.
+ */
+ rc = objset_get_dnode(spa, &mount->objset, MASTER_NODE_OBJ, &dn);
+ if (rc) {
+ free(entry);
+ return (rc);
+ }
+
+ rc = zap_lookup(spa, &dn, ZFS_ROOT_OBJ, sizeof (objnum), 1, &objnum);
+ if (rc) {
+ free(entry);
+ return (rc);
+ }
+ entry->objnum = objnum;
+ STAILQ_INSERT_HEAD(&on_cache, entry, entry);
+
+ rc = objset_get_dnode(spa, &mount->objset, objnum, &dn);
+ if (rc != 0)
+ goto done;
+
+ p = upath;
+ while (p && *p) {
+ rc = objset_get_dnode(spa, &mount->objset, objnum, &dn);
+ if (rc != 0)
+ goto done;
+
+ while (*p == '/')
+ p++;
+ if (*p == '\0')
+ break;
+ q = p;
+ while (*q != '\0' && *q != '/')
+ q++;
+
+ /* skip dot */
+ if (p + 1 == q && p[0] == '.') {
+ p++;
+ continue;
+ }
+ /* double dot */
+ if (p + 2 == q && p[0] == '.' && p[1] == '.') {
+ p += 2;
+ if (STAILQ_FIRST(&on_cache) ==
+ STAILQ_LAST(&on_cache, obj_list, entry)) {
+ rc = ENOENT;
+ goto done;
+ }
+ entry = STAILQ_FIRST(&on_cache);
+ STAILQ_REMOVE_HEAD(&on_cache, entry);
+ free(entry);
+ objnum = (STAILQ_FIRST(&on_cache))->objnum;
+ continue;
+ }
+ if (q - p + 1 > sizeof(element)) {
+ rc = ENAMETOOLONG;
+ goto done;
+ }
+ memcpy(element, p, q - p);
+ element[q - p] = 0;
+ p = q;
+
+ if ((rc = zfs_dnode_stat(spa, &dn, &sb)) != 0)
+ goto done;
+ if (!S_ISDIR(sb.st_mode)) {
+ rc = ENOTDIR;
+ goto done;
+ }
+
+ rc = zap_lookup(spa, &dn, element, sizeof (objnum), 1, &objnum);
+ if (rc)
+ goto done;
+ objnum = ZFS_DIRENT_OBJ(objnum);
+
+ if ((entry = malloc(sizeof(struct obj_list))) == NULL) {
+ rc = ENOMEM;
+ goto done;
+ }
+ entry->objnum = objnum;
+ STAILQ_INSERT_HEAD(&on_cache, entry, entry);
+ rc = objset_get_dnode(spa, &mount->objset, objnum, &dn);
+ if (rc)
+ goto done;
+
+ /*
+ * Check for symlink.
+ */
+ rc = zfs_dnode_stat(spa, &dn, &sb);
+ if (rc)
+ goto done;
+ if (S_ISLNK(sb.st_mode)) {
+ if (symlinks_followed > 10) {
+ rc = EMLINK;
+ goto done;
+ }
+ symlinks_followed++;
+
+ /*
+ * Read the link value and copy the tail of our
+ * current path onto the end.
+ */
+ if (sb.st_size + strlen(p) + 1 > sizeof(path)) {
+ rc = ENAMETOOLONG;
+ goto done;
+ }
+ strcpy(&path[sb.st_size], p);
+
+ rc = zfs_dnode_readlink(spa, &dn, path, sb.st_size);
+ if (rc != 0)
+ goto done;
+
+ /*
+ * Restart with the new path, starting either at
+ * the root or at the parent depending whether or
+ * not the link is relative.
+ */
+ p = path;
+ if (*p == '/') {
+ while (STAILQ_FIRST(&on_cache) !=
+ STAILQ_LAST(&on_cache, obj_list, entry)) {
+ entry = STAILQ_FIRST(&on_cache);
+ STAILQ_REMOVE_HEAD(&on_cache, entry);
+ free(entry);
+ }
+ } else {
+ entry = STAILQ_FIRST(&on_cache);
+ STAILQ_REMOVE_HEAD(&on_cache, entry);
+ free(entry);
+ }
+ objnum = (STAILQ_FIRST(&on_cache))->objnum;
+ }
+ }
+
+ *dnode = dn;
+done:
+ STAILQ_FOREACH_SAFE(entry, &on_cache, entry, tentry)
+ free(entry);
+ return (rc);
+}
diff --git a/stand/zfs32/Makefile b/stand/zfs32/Makefile
new file mode 100644
index 0000000..32d0074
--- /dev/null
+++ b/stand/zfs32/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+DO32=1
+
+.include "${.CURDIR}/../zfs/Makefile"
OpenPOWER on IntegriCloud