Imported Upstream version 3.4 upstream/3.4
authorBdale Garbee <bdale@gag.com>
Tue, 20 May 2008 05:09:32 +0000 (23:09 -0600)
committerBdale Garbee <bdale@gag.com>
Tue, 20 May 2008 05:09:32 +0000 (23:09 -0600)
100 files changed:
ChangeLog [new file with mode: 0644]
LIMITATIONS [new file with mode: 0644]
Make.defaults [new file with mode: 0644]
Make.rules [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
README.gnu-efi [new file with mode: 0644]
TODO [new file with mode: 0644]
alloc.c [new file with mode: 0644]
alternate.c [new file with mode: 0644]
bootparams.c [new file with mode: 0644]
chooser.c [new file with mode: 0644]
chooser.h [new file with mode: 0644]
choosers/Makefile [new file with mode: 0644]
choosers/simple.c [new file with mode: 0644]
choosers/simple.h [new file with mode: 0644]
choosers/textmenu.c [new file with mode: 0644]
choosers/textmenu.h [new file with mode: 0644]
config.c [new file with mode: 0644]
config.h [new file with mode: 0644]
devschemes/Makefile [new file with mode: 0644]
devschemes/simple.c [new file with mode: 0644]
docs/devschemes.txt [new file with mode: 0644]
docs/edd30.txt [new file with mode: 0644]
docs/elilo.txt [new file with mode: 0644]
docs/eliloalt.txt [new file with mode: 0644]
docs/elilovars.txt [new file with mode: 0644]
docs/fpswa.txt [new file with mode: 0644]
docs/netbooting.txt [new file with mode: 0644]
docs/simple_chooser.txt [new file with mode: 0644]
docs/textmenu_chooser.txt [new file with mode: 0644]
elf.h [new file with mode: 0644]
elilo-ia32.efi [new file with mode: 0755]
elilo-ia64.efi [new file with mode: 0755]
elilo.c [new file with mode: 0644]
elilo.h [new file with mode: 0644]
elilo_debug.h [new file with mode: 0644]
examples/netboot/dhcpd-pxe.conf [new file with mode: 0644]
examples/netboot/dhcpd.conf [new file with mode: 0644]
examples/textmenu_chooser/elilo-textmenu.conf [new file with mode: 0644]
examples/textmenu_chooser/general.msg [new file with mode: 0644]
examples/textmenu_chooser/params.msg [new file with mode: 0644]
examples/textmenu_chooser/textmenu-message.msg [new file with mode: 0644]
fileops.c [new file with mode: 0644]
fileops.h [new file with mode: 0644]
fs/Makefile [new file with mode: 0644]
fs/ext2_fs.h [new file with mode: 0644]
fs/ext2_fs_i.h [new file with mode: 0644]
fs/ext2_fs_sb.h [new file with mode: 0644]
fs/ext2_private.h [new file with mode: 0644]
fs/ext2fs.c [new file with mode: 0644]
fs/ext2fs.h [new file with mode: 0644]
fs/fs.h [new file with mode: 0644]
fs/localfs.c [new file with mode: 0644]
fs/localfs.h [new file with mode: 0644]
fs/netfs.c [new file with mode: 0644]
fs/netfs.h [new file with mode: 0644]
getopt.c [new file with mode: 0644]
getopt.h [new file with mode: 0644]
glue_ext2fs.c [new file with mode: 0644]
glue_ext2fs.h [new file with mode: 0644]
glue_localfs.c [new file with mode: 0644]
glue_localfs.h [new file with mode: 0644]
glue_netfs.c [new file with mode: 0644]
glue_netfs.h [new file with mode: 0644]
gnu-efi-3.0a-ia32.patch [new file with mode: 0644]
ia32/Makefile [new file with mode: 0644]
ia32/bin_to_h.c [new file with mode: 0644]
ia32/config.c [new file with mode: 0644]
ia32/private.h [new file with mode: 0644]
ia32/rmswitch.S [new file with mode: 0644]
ia32/sysdeps.h [new file with mode: 0644]
ia32/system.c [new file with mode: 0644]
ia64/Makefile [new file with mode: 0644]
ia64/config.c [new file with mode: 0644]
ia64/fpswa.c [new file with mode: 0644]
ia64/gzip.c [new file with mode: 0644]
ia64/gzip.h [new file with mode: 0644]
ia64/gzip_loader.c [new file with mode: 0644]
ia64/inflate.c [new file with mode: 0644]
ia64/longjmp.S [new file with mode: 0644]
ia64/memcpy.S [new file with mode: 0644]
ia64/memset.S [new file with mode: 0644]
ia64/plain_loader.c [new file with mode: 0644]
ia64/private.h [new file with mode: 0644]
ia64/setjmp.S [new file with mode: 0644]
ia64/setjmp.h [new file with mode: 0644]
ia64/sysdeps.h [new file with mode: 0644]
ia64/system.c [new file with mode: 0644]
initrd.c [new file with mode: 0644]
loader.c [new file with mode: 0644]
loader.h [new file with mode: 0644]
strops.c [new file with mode: 0644]
strops.h [new file with mode: 0644]
sysdeps.h [new file with mode: 0644]
tools/Makefile [new file with mode: 0644]
tools/eliloalt.c [new file with mode: 0644]
util.c [new file with mode: 0644]
vars.c [new file with mode: 0644]
vars.h [new file with mode: 0644]

diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..07aba8e
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,338 @@
+2003-08-20 Stephane Eranian <eranian@hpl.hp.com>
+       * released 3.4
+2003-08-19 Stephane Eranian <eranian@hpl.hp.com>
+       * integrated ia32 updates from Matt
+         Tolentino <matthew.e.tolentino@intel.com>
+2003-08-13 Stephane Eranian <eranian@hpl.hp.com>
+       * updated elilo.txt and netbooting.txt
+       * fix a bug in choosers/simple.c:print_infos().
+         it needs to check if config file path is absolute
+         when printing filename.
+       * move definitions of CHAR_SLASH CHAR_BACKSLASH to elilo.h
+       * fix a bug in read_config() where it would try other
+         filename even when the user explicitely specified one
+         via -C, now it fails it that file cannot be opened.
+       * updated simple chooser set of builtin command keys
+       * command keys are only valid if first on the line
+       * increase default buffer size and increment when netbooting
+2003-06-04 Stephane Eranian <eranian@hpl.hp.com>
+       * fix fs/netfs.c to work with recent version
+         of EFI (14.61 or higher) which do not have the 
+         TFTP problem anymore. fix submitted by Guy Laborde
+2003-04-21 Stephane Eranian <eranian@hpl.hp.com>
+       * ext2fs support is turned off by default to avoid
+         problems with ext3-formatted partitions.
+       * added gcc version check. MUST use 3.0 or higher
+2003-03-03 Stephane Eranian <eranian@hpl.hp.com>
+       * added check on dev_tab in fs/*fs.c:*_uninstall()
+2003-02-07 Stephane Eranian <eranian@hpl.hp.com>
+       * clean up in glue_localfs.c w.r.t. CHAR16 in set_default_path()
+       * added support for extracting basename of bootloader path
+         when using BOOTP (DHCP) only. The prefix is then used for all files
+         open via netfs. Suggestion and initial patch by Guy Laborde from HP.
+2003-01-28 Stephane Eranian <eranian@hpl.hp.com>
+       * fix the set_default_path() routine in glue_localfs.c. It would not
+         correctly get the basename of the devpath. This caused the
+         elilo.conf not to be found sometimes.
+2003-01-21 Stephane Eranian <eranian@hpl.hp.com>
+       * fix bug in glue_netfs.c convert_ip2decstr() which caused some IP
+         addresses to be incorrectly converted to strings.
+2002-11-01 Stephane Eranian <eranian@hpl.hp.com>
+       * fix bug in -r option for IA64. There is no argument to this option.
+2002-10-15 Stephane Eranian <eranian@hpl.hp.com>
+       * fixed a double free bug for the kernel memory in case of abort.
+         (bug spotted by Levent Akyl from Intel)
+       * released 3.3a
+2002-09-14 Stephane Eranian <eranian@hpl.hp.com>
+       * applied patch from Andreas Schwab <schwab@suse.de> to eliloalt.c.
+         eliloalt dynamically selects a variable in /proc/efi/vars.
+2002-09-12 Stephane Eranian <eranian@hpl.hp.com>
+       * removed extra free() from fs/ext2fs.c:ext2fs_init_state().
+         Bug report and fix by NOMURA Jun'ichi <j-nomura@ce.jp.nec.com>
+       * rewrote fs/ext2fs.c:read_bytes() to large memory stack buffer which
+         was bigger than the 128KB limit of EFI causing some weird fimrware
+         errors. bug reported by OMURA Jun'ichi <j-nomura@ce.jp.nec.com>
+       * on IA-64 forbid the use of f32-f127 by the compiler (EFI spec)
+2002-09-10 Stephane Eranian <eranian@hpl.hp.com>
+       * fix a bug in argify() that was causing an EFI assertion 
+         when aborting at the elilo prompt when netbooted.
+2002-08-26 Stephane Eranian <eranian@hpl.hp.com>
+       * fixed devschemes/simple.c to use SPrint() instead of its own buggy
+         conversion code (spotted by Richard Hirst).
+       * fix bug in argify() when there was no NULL character in the string.
+       * released 3.3
+2002-08-19 Stephane Eranian <eranian@hpl.hp.com>
+       * added fpswa.txt in the docs directory
+       * updated elilo.txt
+2002-08-15 Stephane Eranian <eranian@hpl.hp.com>
+       * added -F file option for IA-64 to allow a specific fpswa driver to be loaded
+       * fixed fpswa.c to try and load the driver from all accessible partitions
+       * added support to load (plain or gzipped) big-endian ELF/ia64 binaries using p_paddr.
+       * fixed problem in fs/netfs.c causing large (>4MB) binaries to fail the Mftp() call
+2002-06-13 Stephane Eranian <eranian@hpl.hp.com>
+       * Changed the despecialization character for the variables from \\ to &
+         to avoid conflicts with \\ as a path separator
+2002-06-11 Stephane Eranian <eranian@hpl.hp.com>
+       * fixed the return value in efi_main(). elilo was always returning
+         success even in case of failure. Bug reported by Egan Ford <egan@sense.net>
+       * applied patch from Richard Hirst <rhirst@linuxcare.com> to fix an
+         initialization bug in choosers/textmenu.c 
+       * applied patch from Richard Hirst <rhirst@linuxcare.com> to make elilo
+         compliant with EFI spec with regards to where it looks for files.
+         With this patch, elilo will look in the directory it was loaded
+         from, not on the root of the partition anymore.
+2002-03-04 Stephane Eranian <eranian@hpl.hp.com>
+       * released version 3.2
+       * cleanup some GNU extension in fs/ext2fs.c (variable size array)
+       * updated all documentation. Added netbooting.txt, simple_chooser.txt,
+          eliloalt.txt, elilovar.txt
+2002-02-21 Stephane Eranian <eranian@hpl.hp.com>
+       * added a Linux utility program (elilovar in tools) to set/read/delete 
+         the EliloAlt EFI variable used to specify an alternate kernel to boot.
+       * rename DBG_PRINT() to DBG_PRT, PRINT_ERR() to ERR_PRT()
+       * added support for hostname,domain name extraction in fs/netfs.c
+       * fixed all known bugs in alternate.c
+       * integrated patch from SGI to fix load offset for relocatable kernels (Jack Steiner, Brent Casavant)
+2002-02-21 Michael Johnston <michael.johnston@intel.com> and Chris Ahna <christopher.j.ahna@intel.com>
+       * major update to ia32 support: can now boot 2.4.x,  and 2.2.x kernels
+2002-02-20 Stephane Eranian <eranian@hpl.hp.com>
+       * fixed missing netfs_fd_free() in case of file not found in netfs.c
+2002-02-19 Stephane Eranian <eranian@hpl.hp.com>
+       * added support for substitution variables (vars.c)
+       * changed the bootparam structure size back to 4kB
+       * added support to simple to print final command line option with tab key
+       * got rid of all the \r characters in strings use only \n (adjust emulator)
+       * added EFICRT0 variable in Makefile to indicate location of loader script+crt0
+2002-02-14 Stephane Eranian <eranian@hpl.hp.com>
+       * added support for message= option to simple chooser
+       * added support for description= option to simple chooser
+2002-02-13 Stephane Eranian <eranian@hpl.hp.com>
+       * choosers/textmenu.c: new textmenu chooser (by rhirst@linuxcare.com) used by Debian
+       * config.c: added support for dynamic global/per-image option management
+       * ia64/plain_loader.c,ia64/gzip.c: fix load_offset (<bcasavan@sgi.com>)
+       * added cmd line (-E) and config option (noedd30) to not set EDD30 EFI variable to 
+         true if not already TRUE (request by Matt_Domsch@dell.com)
+       * added support for multiple devname schemes and probing
+
+2002-01-31 Stephane Eranian <eranian@hpl.hp.com>
+       * cleaned up alternate.c
+       * added support for ctrl-U (clear line) in chooser/simple.c 
+
+2002-01-25 Stephane Eranian <eranian@hpl.hp.com>
+       * added support for architecture specific config file (elilo-ia64.conf, elilo-ia32.conf).
+
+2002-01-13 Stephane Eranian <eranian@hpl.hp.com>
+       * removed call to Reset() in ext2fs.c
+
+2001-08-17 Stephane Eranian <eranian@hpl.hp.com>
+       * released 3.1
+       * added support for command line architecture specific options:
+         sysdeps_get_cmdline_opts(), sysdeps_print_cmdline_opts(),
+         syspdeps_getopt()
+       * added IA-64 command line option (-r) for relocation
+       * fix behavior when kernel specified on command line but prompt
+         mode was specified in config file. In this case, we now autoboot
+         and ignore the prompt directive.
+       * updated elilo.txt
+2001-08-15  Brent Casavant <bcasavan@sgi.com>
+       * fix a bug in config.c:find_option() where it would do
+         a strXcmp() on a NULL string.
+
+2001-08-01  Stephane Eranian <eranian@hpl.hp.com>
+       * fixed bug in fs/netfs.c where it would not handle the small buffer 
+         error correctly. The retry path was not allocating a bigger buffer.
+       * Global config options are now used if the user specifies a non-label
+         load target, i.e. a kernel image file.
+       * added support for architecture dependent config file image options (sys_img_options_t).
+       * added support for setjmp/longjmp.
+       * added support for aborting during a compressed load
+       * added support for user to abort a load of a compressed file.
+       * added 2 new ia-64 only config file options allowing kernel relocation: 
+         'relocatable' as a global or per image option.
+       * added support for kernel relocation on memory error. Based on code from 
+         Brent Casavant <bcasavan@sgi.com>.
+       * added slash/backslash conversion for filenames on vfat filesystems.
+
+2001-07-23  Stephane Eranian <eranian@hpl.hp.com>
+       * fixed error in netfs.c where the kernel name was not correctly set in
+         netfs_query_layer()
+       * fixed to wait_timeout() to correct the problem with the interactive prompt when
+         return is hit directly when no text
+       * fixed command line argument destruction problem, now we make a copy of them. This
+         was affecting elilo when called directly from bootmanager with NVRAM options.
+
+2001-06-28  Stephane Eranian <eranian@hpl.hp.com>
+       * removed W2U() hack to get from wide-char to unicode. Use -fshort-wchar option instead.
+       * split gnu-efi package in two different packages: the libary+include+crt and the bootloader.
+       * restructured the fileops module. Now use direct function calls.
+       * added support for accessing files on different devices.
+       * fixed a buffer leak in simple_chooser.c. Renamed simple_chooser.c to simple.c.
+       * created a strops.c file to incorporate all string operations functions.
+       * added support for ext2fs filesystem.
+       * restructured code to allow additional filesystems to be added easily.
+       * cleaned up add-on chooser interface.
+       * restructured code to use the EFI protocol interface to install filesystems.
+       * added compile-time options to turn on and off specific filesystems.
+       * added support for architecture specific configuration options (elilo.conf).
+       * added fpswa option to IA-64 to designate a fpswa driver file.
+       * incoporated IA-32 support from Mike Johnston <michael.johnston@intel.com>
+       * incorporated rewritten gzip.c:flush_window() from Tony Luck <tony.luck@intel.com>
+       * added interface for custom device naming schemes (devnames directory).
+        * added support for 2 possible config file (now just on netboot). The first
+          (primary) choice uses a host specific filename based on the IP address. Suggestion
+         from Egan Ford <egan@sense.net>.
+
+2001-04-06  Stephane Eranian <eranian@hpl.hp.com>
+
+       * incorporated patches from David and Michael Johnston at Intel
+         to get the package to compile for IA-32 linux target.
+
+       * Fixed ELILO to compile for Ia-32 (does not execute yet, though):
+         Makefile and start_kernel() function.
+
+2001-04-06  Andreas Schwab <schwab@suse.de>
+
+       * Fixed config.c  to
+         get the timeout directive to do something. implemented the global
+         root= directive.
+
+       * Fix the efi_main() to deal with the -C option properly
+
+2001-04-05  Stephane Eranian <eranian@hpl.hp.com>
+
+       * update efi library to latest EFI toolkit 1.02 as distributed
+         by Intel. Fixed header + library files to compile with GCC
+
+       * merged ELI and LILO (as of gnu-efi-1.1) together, mostly
+         taking the config file feature of ELI.
+
+       * renamed LILO to ELILO to make the distinction
+
+       * restructured code to make it easier to understand and maintain
+
+       * fixed FPSWA driver checking and loading: we try all possible
+         files and let the driver itself figure out if it is the most
+         recent.
+       * added support for compression (gzip) but keep support for plain
+         ELF image. ELILO autodetects the format
+
+       * change the way the kernel is invoked. Now we call it in 
+         physical memory mode. This breaks the dependency between the
+         kernel code and the loader. No more lilo_start.c madness.
+
+       * changed the way the boot_params are passed. We don't use the 
+         ZERO_PAGE_ADDR trick anymore. Instead we use EFI runtime memory.
+         The address of the structure is passed to the kernel in r28
+         by our convention.
+
+       * released as gnu-efi-2.0
+
+2001-04-03  David Mosberger  <davidm@hpl.hp.com>
+
+       * gnuefi/reloc_ia32.c (_relocate): Change return type from "void"
+       to "int".  Return error status if relocation fails for some
+       reason.
+
+       * gnuefi/elf_ia32_efi.lds: Drop unneeded ".rel.reloc" section.
+
+       * gnuefi/crt0-efi-ia32.S (_start): Exit if _relocate() returns with
+       non-zero exit status.
+
+       * inc/ia32/efibind.h [__GNUC__]: Force 8-byte alignment for 64-bit
+       types as that is what EFI appears to be expecting, despite the
+       "#pragma pack()" at the beginning of the file!
+
+2001-03-29  David Mosberger  <davidm@hpl.hp.com>
+
+       * gnuefi/reloc_ia32.c: Add a couple of defines to work around
+       libc/efilib collision on uint64_t et al.
+       (_relocate): Use ELF32_R_TYPE() instead of ELFW(R_TYPE)().
+
+       * gnuefi/crt0-efi-ia32.S (dummy): Add a dummy relocation entry.
+
+2001-03-29  David Mosberger  <davidm@hpl.hp.com>
+
+        * gnuefi/reloc_ia32.c: Add a couple of defines to work around
+        libc/efilib collision on uint64_t et al.
+        (_relocate): Use ELF32_R_TYPE() instead of ELFW(R_TYPE)().
+
+        * gnuefi/crt0-efi-ia32.S (dummy): Add a dummy relocation entry.
+
+2000-10-26  David Mosberger  <davidm@hpl.hp.com>
+       * gnuefi/elf_ia64_efi.lds: Mention .rela.sdata.
+       * Make.defaults (CFLAGS): Remove -nostdinc flags so we can pick
+       up the C compiler's stdarg.h.
+       * inc/stdarg.h: Remove this file.  It's not correct for gcc (nor
+       most other optimizing compilers).
+
+2000-10-10  Stephane Eranian <eranian@hpl.hp.com>
+
+       * cleaned up the error message and printing of those.
+       * added support to load the FPSWA from a file in case support is not
+         present in the firmware already
+       * fixed split_args() to do the right thing when you have leading spaces
+         before kernel name
+       * changed the argify() function to rely on \0 instead of LoadOptionSize
+         as the field seems to be broken with current firmware
+       * bumped version to 1.0
+
+2000-10-04  David Mosberger  <davidm@hpl.hp.com>
+       * gnuefi/reloc_ia64.S: Reserve space for up to 750 function descriptors.
+
+       * gnuefi/elf_ia64_efi.lds: Add .sdata section for small data and
+       put __gp in the "middle" of it.
+
+       * gnuefi/crt0-efi-ia64.S (_start): Use movl/add to load
+       gp-relative addresses that could be out of the range of the addl
+       offset.
+       * gnuefi/reloc_ia64.S (_relocate): Ditto.
+
+       * apps/Makefile: Remove standard rules and include Make.rules instead.
+       * lilo/Makefile: Ditto.
+
+       * Make.rules: New file.
+
+2000-08-04  Stephane Eranian <eranian@hpl.hp.com>
+       * released version 0.9
+       * incorporated ACPI changes for Asuza by NEC < kouchi@hpc.bs1.fc.nec.co.jp>
+       * added support for initrd (-i option) original ELI code from Bill Nottingham <notting@redhat.com>)
+       * lots of cleanups 
+       * got rid of #ifdef LILO_DEBUG and uses macro instead
+       * fix a few extra memory leaks in create_boot_params()
+       * added exit capability just before starting the kernel
+
+2000-06-22  David Mosberger  <davidm@hpl.hp.com>
+
+       * gnuefi/elf_ia64_efi.lds: Add .srodata, .ctors, .IA64.unwind,
+       .IA64.unwind_info to .data section and .rela.ctors to .rela
+       section.
+
+2000-04-03  David Mosberger  <davidm@hpl.hp.com>
+
+       * lilo/lilo.c (LILO_VERSION): Up version number to 0.9.
+
+       * gnuefi/elf_ia64_efi.lds: Include .IA_64.unwind and
+       .IA_64.unwind_info in .data segment to avoid EFI load error
+       "ImageAddress: pointer outside of image" error due to the .dynsym
+       relocations against these sections.
+
+       * ChangeLog: Moved from lilo/ChangeLogs.
+
+       * gnuefi/reloc_ia64.S: fixed typo: .space directive had constant
+       100 hardcoded instead of using MAX_FUNCTION_DESCRIPTORS
+       macro. Duh.
+
+Fri Mar 17 15:19:18 PST 2000 Stephane Eranian <eranian@hpl.hp.com>
+
+       * Released 0.8
+       * replace the  getopt.c with new version free with better license
+       * created a documentation file
+       * fix a couple of memory leaks
+       * code cleanups
+       * created a separate directory for lilo in the gnu-efi package.
+       * added support for the BOOT_IMAGE argument to kernel
+       * default is to build natively now
diff --git a/LIMITATIONS b/LIMITATIONS
new file mode 100644 (file)
index 0000000..2381a6a
--- /dev/null
@@ -0,0 +1,3 @@
+
+See the TODO file, for more on the known limitations of this version of elilo.
+
diff --git a/Make.defaults b/Make.defaults
new file mode 100644 (file)
index 0000000..523f0fc
--- /dev/null
@@ -0,0 +1,131 @@
+#
+#  Copyright (C) 2001-2003 Hewlett-Packard Co.
+#      Contributed by Stephane Eranian <eranian@hpl.hp.com>
+#
+# This file is part of ELILO, the LINUX EFI boot loader.
+#
+#  ELILO is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2, or (at your option)
+#  any later version.
+#
+#  ELILO is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with ELILO; see the file COPYING.  If not, write to the Free
+#  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+#  02111-1307, USA.
+#
+# Please check out the elilo.txt for complete documentation on how
+# to use this program.
+#
+
+#
+# File system selection. At least one filesystem must be enabled
+#
+CONFIG_localfs=y
+CONFIG_netfs=y
+
+#
+# WARNING WARNING WARNING
+#
+# Use this option with caution. This filesystem module does not
+# support ext3 formatted partitions, i.e., it does not know how
+# to recover from failures (ignores the log).
+#
+CONFIG_ext2fs=n
+
+#
+# Chooser selection(at least one must be defined)
+#
+CONFIG_chooser_simple=y
+CONFIG_chooser_textmenu=y
+
+#
+# Enable IP-address based config file (elilo.conf) when netbooted
+#
+CONFIG_machspec_netconfig=y
+
+#
+# Indicate where the EFI include and libaries are.
+# They are installed as part of the GNU-EFI package installation
+#
+EFIINC    = /usr/include/efi
+GNUEFILIB   = /usr/lib
+EFILIB    = /usr/lib
+EFICRT0           = /usr/lib
+
+CDIR    := $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi)
+TOPDIR  =
+
+ARCH      = $(shell uname -m | sed s,i[3456789]86,ia32,)
+INCDIR    = -I. -I$(TOPDIR) -I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/protocol
+CPPFLAGS   = -DCONFIG_$(ARCH) 
+
+OPTIMFLAGS = -O2 
+DEBUGFLAGS = -Wall
+CFLAGS    = $(OPTIMFLAGS) -fpic -fshort-wchar $(DEBUGFLAGS)
+LDFLAGS           = -nostdlib
+INSTALL           = install
+
+ifeq ($(CONFIG_machspec_netconfig),y)
+CFLAGS += -DENABLE_MACHINE_SPECIFIC_NETCONFIG
+endif
+
+ifeq ($(CONFIG_localfs),y)
+CFLAGS += -DCONFIG_LOCALFS
+endif
+
+ifeq ($(CONFIG_netfs),y)
+CFLAGS += -DCONFIG_NETFS
+endif
+
+ifeq ($(CONFIG_ext2fs),y)
+CFLAGS += -DCONFIG_EXT2FS
+endif
+
+ifeq ($(CONFIG_chooser_simple),y)
+CFLAGS += -DCONFIG_CHOOSER_SIMPLE
+endif
+
+ifeq ($(CONFIG_chooser_textmenu),y)
+CFLAGS += -DCONFIG_CHOOSER_TEXTMENU
+endif
+
+ifeq ($(ARCH),ia64)
+  prefix       = 
+  prefix       = /opt/gcc3.1/bin/
+  CC           = $(prefix)gcc
+  AS           = $(prefix)as
+  LD           = $(prefix)ld
+  LD           = ld
+  AR           = $(prefix)ar
+  RANLIB       = $(prefix)ranlib
+  OBJCOPY      = $(prefix)objcopy
+
+GCC_VERSION=$(shell $(CROSS_COMPILE)$(CC) -v 2>&1 | fgrep 'gcc version' | cut -f3 -d' ' | cut -f1 -d'.')
+
+ifneq ($(GCC_VERSION),2)
+        CFLAGS += -frename-registers
+endif
+#
+# EFI specs allows only lower floating point partition to be used
+# 
+# Redhat 8.0 gcc-3.x version is reported to produce working EFI binaries.
+# Redhat 9.0 gcc-3.x version is reported to produce BAD binaries.
+#
+CFLAGS += -mfixed-range=f32-f127
+else
+ ifeq ($(ARCH),ia32)
+  prefix       = 
+  CC           = $(prefix)gcc3
+  AS           = $(prefix)as
+  LD           = $(prefix)ld
+  AR           = $(prefix)ar
+  RANLIB       = $(prefix)ranlib
+  OBJCOPY      = $(prefix)objcopy
+ endif
+endif
diff --git a/Make.rules b/Make.rules
new file mode 100644 (file)
index 0000000..2f4ade2
--- /dev/null
@@ -0,0 +1,35 @@
+#
+#  Copyright (C) 2001-2003 Hewlett-Packard Co.
+#      Contributed by Stephane Eranian <eranian@hpl.hp.com>
+#
+# This file is part of ELILO, the LINUX EFI boot loader.
+#
+#  ELILO is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2, or (at your option)
+#  any later version.
+#
+#  ELILO is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with ELILO; see the file COPYING.  If not, write to the Free
+#  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+#  02111-1307, USA.
+#
+# Please check out the elilo.txt for complete documentation on how
+# to use this program.
+#
+
+%.efi: %.so 
+       $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \
+                  -j .rela -j .reloc --target=$(FORMAT) $*.so $@
+
+%.so: %.o 
+       $(LD) $(LDFLAGS) $^ -o $@ $(LOADLIBES)
+
+%.o: %.c 
+       $(CC) $(INCDIR) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..d8ec67d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,103 @@
+#
+#  Copyright (C) 2001-2003 Hewlett-Packard Co.
+#      Contributed by Stephane Eranian <eranian@hpl.hp.com>
+#
+# This file is part of ELILO, the LINUX EFI boot loader.
+#
+#  ELILO is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2, or (at your option)
+#  any later version.
+#
+#  ELILO is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with ELILO; see the file COPYING.  If not, write to the Free
+#  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+#  02111-1307, USA.
+#
+# Please check out the elilo.txt for complete documentation on how
+# to use this program.
+#
+
+include Make.defaults
+TOPDIR=.
+
+
+CRTOBJS       = $(EFICRT0)/crt0-efi-$(ARCH).o
+LDSCRIPT      = $(EFICRT0)/elf_$(ARCH)_efi.lds
+
+LDFLAGS             += -T $(LDSCRIPT) -shared -Bsymbolic -L$(EFILIB) -L$(GNUEFILIB) $(CRTOBJS)
+LOADLIBES     = -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name)
+FORMAT        = efi-app-$(ARCH)
+
+FILESYSTEM    =
+
+ifeq ($(CONFIG_localfs),y)
+FILESYSTEMS += glue_localfs.o
+endif
+
+ifeq ($(CONFIG_ext2fs),y)
+FILESYSTEMS += glue_ext2fs.o
+endif
+
+ifeq ($(CONFIG_netfs),y)
+FILESYSTEMS += glue_netfs.o
+endif
+
+SUBDIRS = fs choosers devschemes tools
+
+ifeq ($(ARCH),ia64)
+SUBDIRS += ia64
+endif
+
+ifeq ($(ARCH),ia32)
+SUBDIRS += ia32
+endif
+
+FILES = elilo.o getopt.o strops.o loader.o \
+        fileops.o util.o vars.o alloc.o chooser.o  \
+       config.o initrd.o alternate.o bootparams.o \
+       fs/fs.o \
+       choosers/choosers.o \
+       devschemes/devschemes.o \
+       $(ARCH)/sysdeps.o \
+       $(FILESYSTEMS)
+
+TARGETS = elilo.efi
+
+all: check_gcc $(SUBDIRS) $(TARGETS)
+
+elilo.efi: elilo.so
+
+elilo.so: $(FILES)
+
+elilo.o : elilo.c
+
+fileops.o : Make.defaults
+chooser.o : Make.defaults
+
+$(SUBDIRS): dummy
+       $(MAKE) -C $@
+
+dummy:
+
+clean:
+       @set -e ; for d in $(SUBDIRS) ; do $(MAKE) -C $$d $@ ; done
+       rm -f $(TARGETS) *~ *.so $(FILES)
+
+.PRECIOUS: elilo.so
+
+#
+# on both platforms you must use gcc 3.0 or higher 
+#
+check_gcc:
+ifeq ($(GCC_VERSION),2)
+       @echo "you need to use a version of gcc >= 3.0, you are using `$(CC) --version`"
+       @exit 1
+endif
+
+include Make.rules
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..29724e7
--- /dev/null
+++ b/README
@@ -0,0 +1,74 @@
+                     ELILO: the IA-32 and IA-64 Linux Loader
+                    ---------------------------------------
+                      Stephane Eranian <eranian@hpl.hp.com>
+
+                                August 2003
+
+                  Copyright (C) 2000-2003 Hewlett-Packard Co.
+
+
+This package contains version 3.4 of elilo, the EFI boot loader
+for IA-64(IPF) and IA-32(x86) EFI-based platforms.
+
+
+RELEASE NOTES:
+--------------
+       Take a look at the Changelog for a detailed list of changes 
+       since 3.3a.
+
+       - The major new feature of this release pertains to netbooting.
+         With elilo-3.4, the bootloader will look for files ONLY in the
+         directory if was downloaded from on the TFTP server. Of course,
+         if you specific absolute path, files can be placed anywhere in
+         the TFTP directory structure. This may break some setup but
+         an explicit error message is printed warning the user. 
+
+       - There were a bunch of important bug fixes, including handling
+         of paths when booting from the local disk. 
+
+       - Downloading of large files work with EFI versions prior to 14.60
+         where there was a bug but also with the fixed version of EFI
+         starting at 14.60.
+
+       - There were also some updates for elilo on IA-32. The loader
+         can load unmodified Linux kernel/initrd image from either the
+         local disk or via netbooting. Thanks to Matt Tolentino at Intel
+         for the IA-32 updates.
+
+       - The ext2fs support code is still present but is not compiled in
+         anymore. This code does not understand ext3fs and might lead to
+         errors because it does not understand the journal.
+
+       This package is known to compile and produce working binaries
+       when used in conjunction with gnu-efi-3.0a. This package is
+       available from the HP Labs FTP site:
+
+       ftp://ftp.hpl.hp.com/pub/linux-ia64/gnu-efi-3.0a.tar.gz 
+
+       For IA-64, a toolchain know to produce working binaries is:
+               gcc-3.1
+               binutiuls 2.13.90
+
+       Your may have problems with newer toolchains due to some
+       dependencies in the gnu-efi package. Those dependencies
+       will be fixed eventually.
+
+       For IA-32, the Redhat 8.0 toolchain is known to produce
+       working binaries when used with gnu-efi-3.0a + loader
+       script patch which is included in the gnu-efi-3.0a-ia32.patch
+       in this package. The toolchain includes:
+
+       gcc: gcc version 3.2 20020903 (Red Hat Linux 8.0 3.2-7)
+       as : GNU assembler version 2.13.90.0.2 (i386-redhat-linux) 
+           using BFD version 2.13.90.0.2 20020802
+       ld : GNU ld version 2.13.90.0.2 20020802
+
+       The Redhat 9.0 toolchain does not work at the moment.
+
+DOCUMENTATION:
+--------------
+       PLEASE READ THE docs/elilo.txt file for some documentation on how
+       to use this program. For netbooting refer to docs/netbooting.txt.
+
+       Make sure you read the README.gnu-efi file for required packages.
+
diff --git a/README.gnu-efi b/README.gnu-efi
new file mode 100644 (file)
index 0000000..1653032
--- /dev/null
@@ -0,0 +1,31 @@
+
+                   IMPORTANT Information related to the gnu-efi package
+                   ----------------------------------------------------
+                                       August 2003
+
+As of version elilo-3.0, the gnu-efi package is now split in two different packages:
+
+       -> gnu-efi-X.y: contains the EFI library, header, files, and crt0.
+
+       -> elilo-X.y  : contains the ELILO bootloader.
+       
+Note that X.y don't need to match for both packages. However elilo-3.x requires at
+least gnu-efi >= 3.0. When using a version of gcc >3.0 you MUST use at least gnu-efi-3.0a.
+
+IMPORTANT NOTE FOR IA-32:
+-------------------------
+       For IA-32, the Redhat 8.0 toolchain is known to produce
+       working binaries when used with gnu-efi-3.0a + loader
+       script patch which is included in the gnu-efi-3.0a-ia32.patch
+       in this package. The toolchain includes:
+
+       gcc: gcc version 3.2 20020903 (Red Hat Linux 8.0 3.2-7)
+       as: GNU assembler version 2.13.90.0.2 (i386-redhat-linux) using BFD version
+       2.13.90.0.2 20020802
+       ld: GNU ld version 2.13.90.0.2 20020802
+
+       The Redhat 9.0 toolchain does not work at the moment.
+
+The gnu-efi  package can be downloaded from:
+
+       ftp://ftp.hpl.hp.com/pub/linux-ia64/gnu-efi-X.y.tar.gz
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..d996a16
--- /dev/null
+++ b/TODO
@@ -0,0 +1,18 @@
+Some of the things TO DO:
+-------------------------
+       - a better device naming scheme (take into account removable media)
+
+       - ability to rescan devices like when something gets inserted just
+         before invoking elilo and the efi shell is not used (the mount())
+
+       - ability to list files from the interactive mode
+
+       - GUI-based chooser (a la RH9.x)!
+
+       - UGA support?
+
+       - Convert all filesystems (ext2fs, netfs) to use the FilesystemProtocol interface instead
+
+       - cleanup x86 loader: use the same structure as IA-64
+
+       - support for subnetting in the config file when netbooting
diff --git a/alloc.c b/alloc.c
new file mode 100644 (file)
index 0000000..373fbdc
--- /dev/null
+++ b/alloc.c
@@ -0,0 +1,240 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+
+#define NALLOC 512
+
+typedef enum { ALLOC_POOL, ALLOC_PAGES } alloc_types_t;
+
+typedef struct _alloc_entry {
+       struct _alloc_entry     *next;
+       struct _alloc_entry     *prev;
+       VOID                    *addr;
+       UINTN                   size; /* bytes for pool, page count for pages */
+       alloc_types_t           type;
+} alloc_entry_t;
+
+static alloc_entry_t allocs[NALLOC];
+static alloc_entry_t *free_allocs, *used_allocs;
+
+static VOID *kmem_addr;
+static UINTN kmem_pgcnt;
+
+/*
+ * initializes the free list which is singly linked
+ */
+INTN
+alloc_init(VOID)
+{
+       UINTN i;
+
+       for(i=0; i < NALLOC-1; i++) {
+               allocs[i].next = allocs+i+1;
+       }
+       allocs[i].next = NULL;
+
+       free_allocs = allocs;
+       used_allocs = NULL;
+
+       return 0;
+}
+
+static VOID
+alloc_add(VOID * addr, UINTN size, alloc_types_t type)
+{
+       alloc_entry_t *alloc;
+
+       /* remove from freelist */
+       alloc       = free_allocs;
+       free_allocs = free_allocs->next;
+       
+       alloc->prev = NULL;
+       alloc->next = used_allocs;
+       alloc->addr = addr;
+       alloc->type = type;
+       alloc->size = size;
+
+       /* add to used list */
+       if (used_allocs) used_allocs->prev = alloc;
+
+       used_allocs = alloc;
+}
+
+VOID *
+alloc(UINTN size, EFI_MEMORY_TYPE type)
+{
+       EFI_STATUS status;
+       VOID *tmp = 0;
+
+       /* no more free slots */
+       if (free_allocs == NULL) {
+               ERR_PRT((L"allocator:  no more slots\n"));
+               return NULL;
+       }
+
+       if (type == 0) type = EfiLoaderData;
+
+       status = BS->AllocatePool (type, size, &tmp);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"allocator: AllocatePool(%d, %d, 0x%x) failed (%r)\n", type, size, status));
+               return NULL;
+       }
+       alloc_add(tmp, size, ALLOC_POOL);
+
+        DBG_PRT((L"alloc: allocated %d bytes @[0x%lx-0x%lx]\n", size, tmp, tmp+size));
+
+       return tmp;
+}
+
+/*
+ *  no possibility to partially free an allocated group of pages
+ */
+VOID *
+alloc_pages(UINTN pgcnt, EFI_MEMORY_TYPE type, EFI_ALLOCATE_TYPE where, VOID *addr)
+{
+       EFI_STATUS status;
+       EFI_PHYSICAL_ADDRESS tmp = (EFI_PHYSICAL_ADDRESS)addr;
+
+       /* no more free slots */
+       if (free_allocs == NULL) {
+               ERR_PRT((L"allocator:  no more slots\n"));
+               return NULL;
+       }
+
+       status = BS->AllocatePages(where, type , pgcnt, &tmp);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"allocator: AllocatePages(%d, %d, %d, 0x%lx) failed (%r)\n", where, type, pgcnt, tmp, status));
+               return NULL;
+       }
+       /* XXX: will cause warning on IA-32 */
+       addr = (VOID *)tmp;
+
+       alloc_add(addr, pgcnt, ALLOC_PAGES);
+
+        DBG_PRT((L"allocator: allocated %d pages @0x%lx\n", pgcnt, tmp));
+
+       return addr;
+}
+
+/*
+ * free previously allocated slot
+ */
+VOID
+free(VOID *addr)
+{
+       alloc_entry_t *p;
+
+       /* find allocation record */
+       for(p=used_allocs; p ; p = p->next) {
+               if (p->addr == addr) goto found;
+       }
+       /* not found */
+        ERR_PRT((L"allocator: invalid free @ 0x%lx\n", addr));
+       return; 
+found:
+        DBG_PRT((L"free: %s @0x%lx size=%ld\n", 
+               p->type == ALLOC_POOL ? L"Pool": L"Page", 
+               addr, p->size));
+
+       if (p->type == ALLOC_POOL) 
+               BS->FreePool(addr);
+       else
+               BS->FreePages((EFI_PHYSICAL_ADDRESS)addr, p->size);
+
+       /* remove from used list */
+       if (p->next) 
+               p->next->prev = p->prev;
+               
+       if (p->prev) 
+               p->prev->next = p->next;
+       else
+               used_allocs = p->next;
+
+       /* put back on free list */
+       p->next     = free_allocs;
+       free_allocs = p;
+}
+
+/*
+ * garbage collect all used allocations.
+ * will put the allocator in initial state 
+ */
+VOID
+free_all(VOID)
+{
+       alloc_entry_t *tmp;
+
+       while(used_allocs) {
+
+               DBG_PRT((L"free_all %a @ 0x%lx\n", used_allocs->type == ALLOC_POOL ? "pool" : "pages", used_allocs->addr));
+       
+               if (used_allocs->type == ALLOC_POOL)
+                       BS->FreePool(used_allocs->addr);
+               else
+                       BS->FreePages((EFI_PHYSICAL_ADDRESS)used_allocs->addr, used_allocs->size);
+
+               tmp = used_allocs->next;
+
+               /* put back on free list */
+               used_allocs->next = free_allocs;
+               free_allocs = used_allocs;
+
+               used_allocs = tmp;
+       }
+}
+
+INTN
+alloc_kmem(VOID *start_addr, UINTN pgcnt)
+{
+       if (alloc_pages(pgcnt, EfiLoaderData, AllocateAddress, start_addr) == 0) return -1;
+
+       kmem_addr  = start_addr;
+       kmem_pgcnt = pgcnt;
+
+       return 0;
+}
+
+VOID
+free_kmem(VOID)
+{
+       DBG_PRT((L"free_kmem before (%lx, %ld)\n", kmem_addr, kmem_pgcnt));
+       if (kmem_addr && kmem_pgcnt != 0) {
+               free(kmem_addr);
+               kmem_addr  = NULL;
+               kmem_pgcnt = 0;
+       }
+       DBG_PRT((L"free_kmem after (%lx, %ld)\n", kmem_addr, kmem_pgcnt));
+}
+
+VOID
+free_all_memory(VOID)
+{
+       free_all();
+       free_kmem();
+}
diff --git a/alternate.c b/alternate.c
new file mode 100644 (file)
index 0000000..df138ad
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+
+#define ELILO_ALTK_VAR L"EliloAlt"
+
+/*
+ * we use a NULL GUID for now because it is more convenient
+ */
+static EFI_GUID altk_guid={0,};
+
+/*
+ * Check for the existence of an EFI variable named EliloAlt.
+ * It contains the name of an alternate kernel (and possible options) 
+ * to boot ONLY once. 
+ * The variable is then deleted. This ensures that next reboot won't
+ * pick it up. 
+ *
+ * The content of the variable is assumed to be Unicode string. It is
+ * preferrably NULL terminated but the code can deal with this as well.
+ *
+ * The size of the buffer MUST be expressed in bytes (not CHAR16).
+ *
+ * Return:
+ *     - 0 if successful
+ *     - -1 in case nothing happened (no variable found)
+ *       Please note that no fatal error is reported by this function
+ */
+INTN
+alternate_kernel(CHAR16 *buffer, INTN size)
+{
+       EFI_STATUS status;
+       INTN ret = -1;
+
+       /*
+        * size of the buffer is expressed in bytes
+        *
+        * we reserve one Unicode for CHAR_NULL (forced), so
+        * the buffer must be at least 2 more bytes to accomodate one Unicode
+        */
+       if (size < 4) return -1;
+
+       /*
+        * reserve for CHAR_NULL
+        */
+       size-=2;
+
+       /*
+        * Look if the variable exists. 
+        * We may fail because:
+        *      - the variable does not exist
+        *      - our buffer size is too small.
+        */
+       status = RT->GetVariable(ELILO_ALTK_VAR, &altk_guid, NULL, &size, buffer);
+       if (EFI_ERROR(status)) {
+               DBG_PRT((L"cannot access variable %s: %r", ELILO_ALTK_VAR, status));
+
+               /* if buffer is too small, erase variable because it's unuseable */
+               if (status == EFI_BUFFER_TOO_SMALL) goto delete_var;
+
+               return -1;
+       }
+
+       /*
+        * sanity check
+        *
+        * verify that size (expressed in bytes) is multiple of 2. If 
+        * not then it can't possibly be representing a UNICODE string
+        * (2bytes/character).
+        *
+        * We also consider empty as useless (2nd test).
+        */
+       if (size & 0x1) {
+               Print(L"invalid content for %s variable, erasing variable\n", ELILO_ALTK_VAR);
+               goto delete_var;
+       }
+
+       /* We also consider empty as useless (2nd test) */
+       if (size == 2) goto delete_var;
+
+       buffer[size] = CHAR_NULL;
+
+       VERB_PRT(2, Print(L"found alternate variable %s : %s\n", ELILO_ALTK_VAR, buffer));
+
+       ret = 0;
+delete_var:
+       status = RT->SetVariable(ELILO_ALTK_VAR, &altk_guid, 0, 0, NULL);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"cannot erase variable %s", ELILO_ALTK_VAR));
+       }
+       return ret;
+}
diff --git a/bootparams.c b/bootparams.c
new file mode 100644 (file)
index 0000000..a6c9d7c
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+
+/*
+ * Initialize the generic part of the boot parameters and call the architecture specific
+ * subroutine.
+ * Output:
+ *     cookie: the memory map cookie to use with ExitBootServices()
+ * Return:
+ *     NULL: if an error occured
+ *     bp  : the address of the bootparams otherwise (opaque type)
+ */
+VOID *
+create_boot_params(CHAR16 *args, memdesc_t *initrd, UINTN *cookie)
+{
+/* 
+ * XXX: need cleanup
+ * See ia32 code for explanation on why it is so big.
+ */
+#define        BOOT_PARAM_MEMSIZE      16384   
+       UINTN bpsize, cmdline_size;
+       boot_params_t *bp;
+       CHAR8 *cp;
+       CHAR16 ch;
+
+       /*
+        * Allocate runtime services memory to hold memory descriptor table and
+        * command line arguments and fetch memory map:
+        *
+        * arg_size = number of character in ASCII form
+        */
+       cmdline_size = StrLen(args) + 1;
+       bpsize       = sizeof(boot_params_t) + cmdline_size;
+
+       if (bpsize > BOOT_PARAM_MEMSIZE) {
+               ERR_PRT((L"BOOT_PARAM_MEMSIZE too small, need at least %d bytes", bpsize));
+               return NULL;
+       }
+
+
+       /*
+        * Allocate memory for boot parameters.
+        * This CANNOT be EfiLoaderData or EfiLoaderCode as the kernel
+        * frees this region when initializing.
+        */
+
+       bp = (boot_params_t *)alloc(BOOT_PARAM_MEMSIZE, EfiLoaderData);
+       if (bp == NULL) {
+               ERR_PRT((L"can't allocate boot params"));
+               return 0;
+       }
+
+       VERB_PRT(3, Print(L"boot params @ 0x%lx\n", bp));
+
+/* XXX: need to fix this for 3.5 */
+#ifdef CONFIG_ia64
+       cp = ((CHAR8 *)bp) + BOOT_PARAM_MEMSIZE - cmdline_size;
+#elif defined CONFIG_ia32
+       cp = ((CHAR8 *)bp) + BOOT_PARAM_MEMSIZE - 2048;
+#endif
+
+       /* 
+        * clear entire buffer. The boot param structure is bigger than
+        * needs be but this allows the kernel bootparam structure to grow
+        * (up to BOOT_PARAM_MEMSIZE) without having to worry about fixing the bootloader.
+        * By convention between the laoder and the kernel, the value 0 means 
+        * don't care or not set.
+        */
+       Memset(bp, 0, BOOT_PARAM_MEMSIZE);
+
+       if (sysdeps_create_boot_params(bp, cp, initrd, cookie) == -1) return 0;
+
+       /*
+        * Convert kernel command line args from UNICODE to ASCII and put them where
+        * the kernel expects them:
+        */
+       while (1) {
+               ch = *args++;
+               if (!ch) break;
+               *cp++ = ch;
+       }
+       *cp++ = '\0';
+
+       return bp;
+}
+
+VOID
+free_boot_params(VOID *bp)
+{
+       boot_params_t *real_bp = (boot_params_t *)bp;
+
+       sysdeps_free_boot_params(real_bp);
+
+       free(real_bp);
+}
+
diff --git a/chooser.c b/chooser.c
new file mode 100644 (file)
index 0000000..b257aba
--- /dev/null
+++ b/chooser.c
@@ -0,0 +1,114 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+
+#ifdef CONFIG_CHOOSER_SIMPLE
+#include "choosers/simple.h"
+#endif
+
+#ifdef CONFIG_CHOOSER_TEXTMENU
+#include "choosers/textmenu.h"
+#endif
+
+static chooser_t *choosers_tab[]={
+#ifdef CONFIG_CHOOSER_SIMPLE
+       &simple_chooser,
+#endif
+#ifdef CONFIG_CHOOSER_TEXTMENU
+       &textmenu_chooser,
+#endif
+       NULL
+};
+/*
+ * The intent of this module is to provide a mechanism by which alternate
+ * choosers can be installed. Developers can write new choosers and
+ * add them to the list. They will be probe and the best match
+ * will be started first. It should be possible to switch to another
+ * chooser using a key combination. There is a default simple text-based
+ * chooser that must always be present.
+ *
+ * Currently you can specify a chooser via "-c name" when you invoke elilo,
+ * or via "chooser=name" in the config file.  If the specified chooser
+ * probes ok it will be selected, otherwise the first one that probes ok
+ * will be used.
+ *
+ * XXX: at this time, not all chooser functionalities are implemented.
+ *
+ */
+chooser_func_t *kernel_chooser;
+
+INTN
+init_chooser(EFI_HANDLE dev)
+{
+       chooser_t **p;
+       CHAR16 *chooser_name = L"none";
+
+       kernel_chooser = NULL;
+
+       for (p=choosers_tab; *p; p++) {
+
+               VERB_PRT(4, Print(L"trying chooser %s\n", (*p)->chooser_name));
+
+               if ((*p)->chooser_probe(dev) == 0) {
+                       /*
+                        * if that's the one we asked for, then go for it
+                        */
+                       if (!StrCmp(elilo_opt.chooser, (*p)->chooser_name)) {
+                               kernel_chooser = (*p)->chooser_func;
+                               chooser_name = (*p)->chooser_name;
+                               break;
+                       }
+
+                       if (kernel_chooser == NULL) {
+                               kernel_chooser = (*p)->chooser_func;
+                               chooser_name = (*p)->chooser_name;
+                       }
+               }
+       }
+
+       if (kernel_chooser) {
+               VERB_PRT(2, Print(L"selected chooser %s\n", chooser_name));
+               return 0;
+       }
+
+       ERR_PRT((L"No chooser selected. Impossible to proceed"));
+       return -1;
+}
+
+INTN
+exist_chooser(CHAR16 *name)
+{
+       chooser_t **p;
+
+       for (p=choosers_tab; *p; p++) {
+               if (!StrCmp(name, (*p)->chooser_name)) return 0;
+       }
+       return -1;
+}
+
diff --git a/chooser.h b/chooser.h
new file mode 100644 (file)
index 0000000..a63d9e2
--- /dev/null
+++ b/chooser.h
@@ -0,0 +1,60 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  GNU EFI is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  GNU EFI is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU EFI; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __ELILO_CHOOSER_H__
+#define __ELILO_CHOOSER_H__
+
+typedef INTN chooser_func_t(CHAR16 **argv, INTN arc, INTN index, CHAR16 *kname, CHAR16 *cmdline);
+
+/*
+ * structure to interface to generic chooser selection code.
+ *
+ * XXX: must add support for moer than one choice (rating scheme)
+ *
+ * Interface for probe:
+ *     dev: boot device handle
+ *     return: -1, if not support, 0 if supported
+ *
+ * Interface for chooser:
+ *     argv,argc: command line argument passed to elilo
+ *     index    : position of first non-option argument (min value=1)
+ *     kname    : selected kernel name
+ *     cmdline  : requested kernel command line
+ *     return:
+ *             -1: if error
+ *              0: if successful
+ */
+typedef struct {
+       CHAR16          *chooser_name;
+       INTN            (*chooser_probe)(EFI_HANDLE dev);
+       chooser_func_t  *chooser_func;
+} chooser_t;
+
+extern INTN init_chooser(EFI_HANDLE);
+extern INTN exist_chooser(CHAR16 *name);
+
+extern chooser_func_t *kernel_chooser;
+
+#endif /* __ELILO_CHOOSER_H__ */
diff --git a/choosers/Makefile b/choosers/Makefile
new file mode 100644 (file)
index 0000000..31b10d0
--- /dev/null
@@ -0,0 +1,58 @@
+#
+#  Copyright (C) 2001-2003 Hewlett-Packard Co.
+#      Contributed by Stephane Eranian <eranian@hpl.hp.com>
+#
+# This file is part of the ELILO, the EFI Linux boot loader.
+#
+#  ELILO is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2, or (at your option)
+#  any later version.
+#
+#  ELILO is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with ELILO; see the file COPYING.  If not, write to the Free
+#  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+#  02111-1307, USA.
+#
+# Please check out the elilo.txt for complete documentation on how
+# to use this program.
+#
+
+include ../Make.defaults
+include ../Make.rules
+
+TOPDIR=$(CDIR)/..
+
+FILES=
+
+ifeq ($(CONFIG_chooser_simple),y)
+FILES +=simple.o
+endif
+
+ifeq ($(CONFIG_chooser_textmenu),y)
+FILES +=textmenu.o
+endif
+
+TARGET=choosers.o
+
+all: $(TARGET)
+
+$(TARGET): check-choosers $(TOPDIR)/Make.defaults $(FILES)
+       $(LD) -o $@ -r $(FILES)
+
+clean:
+       $(RM) -f $(TARGET) $(FILES)
+       
+check-choosers:
+       @if [ -n "$(FILES)" ]; then \
+               exit 0; \
+       else \
+               echo "You need to define at least one chooser in Make.defaults"; \
+               exit 1; \
+       fi
+
diff --git a/choosers/simple.c b/choosers/simple.c
new file mode 100644 (file)
index 0000000..9b0725e
--- /dev/null
@@ -0,0 +1,410 @@
+/* 
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "vars.h"
+
+/* static is ugly but does the job here! */
+static CHAR16 **alt_argv;
+
+static VOID
+display_label_info(CHAR16 *name)
+{
+       CHAR16 *desc;
+       CHAR16 initrd_name[CMDLINE_MAXLEN];
+       CHAR16 options_tmp[CMDLINE_MAXLEN];
+       CHAR16 options[CMDLINE_MAXLEN];
+       CHAR16 kname[FILENAME_MAXLEN];
+
+       desc = find_description(name);
+       if (desc) {
+               Print(L"desc   : %s\n", desc);
+       }
+
+       initrd_name[0] = options_tmp[0] = kname[0] = CHAR_NULL;
+
+       if (find_label(name, kname, options_tmp, initrd_name) == -1) {
+               StrCpy(kname, name);
+               Print(L"\n");
+       }
+       subst_vars(options_tmp, options, CMDLINE_MAXLEN);
+
+       Print(L"cmdline: %s %s\n", kname, options);
+       if (initrd_name[0]) Print(L"initrd : %s\n", initrd_name);
+}
+
+static VOID
+print_infos(int force)
+{
+       CHAR16 *config_file;
+       CHAR16 dpath[FILENAME_MAXLEN];
+       CHAR16 *boot_dev_name;
+       UINT8 is_abs;
+
+       boot_dev_name = fops_bootdev_name();
+       config_file   = get_config_file();
+
+       fops_getdefault_path(dpath, FILENAME_MAXLEN);
+
+       if (force || elilo_opt.verbose > 0)
+               Print(L"default file path: %s:%s\n", boot_dev_name, dpath);
+
+       is_abs = config_file && (config_file[0] == CHAR_BACKSLASH || config_file[0] == CHAR_SLASH) ? 1 : 0;
+
+       if (force || elilo_opt.verbose > 0)
+                       Print(L"config file      : %s%s\n", config_file && is_abs == 0 ? dpath : L"", config_file ? config_file : L"none used");
+
+       if (alt_argv) {
+               CHAR16 **p = alt_argv;
+               Print(L"found alternate default choice :");
+               while (*p) Print(L" %s", *p++);
+               Print(L"\n");
+       }
+}
+
+static VOID
+print_help(int force)
+{
+       if (force || elilo_opt.verbose > 0)
+               Print(L"command list (must be first character):\n=:print device list, %%:print variable list, &:print paths, ?:help\nTAB:print label information\n");
+}
+
+/*
+ * interactively select a kernel image and options.
+ * The kernel can be an actual filename or a label in the config file
+ * Return:
+ *     -1: if unsucessful
+ *      0: otherwise
+ */
+static INTN
+select_kernel(CHAR16 *buffer, INTN size)
+{
+#define CHAR_CTRL_C    L'\003' /* Unicode CTRL-C */
+#define CHAR_CTRL_D    L'\004' /* Unicode CTRL-D */
+#define CHAR_CTRL_U    L'\025' /* Unicode CTRL-U */
+//#define CHAR_TAB     L'\t'
+       SIMPLE_INPUT_INTERFACE *ip = systab->ConIn;
+       EFI_INPUT_KEY key;
+       EFI_STATUS status;
+       INTN pos = 0, ret;
+       INT8 first_time = 1;
+
+       /* 
+        * let's give some help first
+        */
+       print_help(0);
+
+       print_infos(0);
+
+reprint:
+       buffer[pos] = CHAR_NULL;
+
+       Print(L"\nELILO boot: %s", buffer);
+       /*
+        * autoboot with default choice after timeout expires
+        */
+       if (first_time && (ret=wait_timeout(elilo_opt.timeout)) != 1) {
+               return ret == -1 ? -1: 0;
+       }
+       first_time = 0;
+
+       for (;;) {
+               while ((status=ip->ReadKeyStroke(ip, &key)) == EFI_NOT_READY);
+               if (EFI_ERROR(status)) {
+                       ERR_PRT((L"select_kernel readkey: %r", status));
+                       return -1;
+               } 
+               switch (key.UnicodeChar) {
+                       case CHAR_TAB:
+                               Print(L"\n");
+                               if (pos == 0) {
+                                       print_label_list();
+                                       Print(L"(or a kernel file name: [[dev_name:/]path/]kernel_image cmdline options)\n");
+                               } else {
+                                       buffer[pos] = CHAR_NULL;
+                                       display_label_info(buffer);
+                               }
+                               goto reprint;
+                       case L'%':
+                               if (pos>0) goto normal_char;
+                               Print(L"\n");
+                               print_vars();
+                               goto reprint;
+                       case L'?':
+                               if (pos>0) goto normal_char;
+                               Print(L"\n");
+                               print_help(1);
+                               goto reprint;
+                       case L'&':
+                               if (pos>0) goto normal_char;
+                               Print(L"\n");
+                               print_infos(1);
+                               goto reprint;
+                       case L'=':
+                               if (pos>0) goto normal_char;
+                               Print(L"\n");
+                               print_devices();
+                               goto reprint;
+                       case CHAR_BACKSPACE:
+                               if (pos == 0) break;
+                               pos--;
+                               Print(L"\b \b");
+                               break;
+                       case CHAR_CTRL_U: /* clear line */
+                               while (pos) {
+                                       Print(L"\b \b");
+                                       pos--;
+                               }
+                               break;
+                       case CHAR_CTRL_C: /* kill line */
+                               pos = 0;
+                               goto reprint;
+                       case CHAR_LINEFEED:
+                       case CHAR_CARRIAGE_RETURN:
+                               buffer[pos]   = CHAR_NULL;
+                               Print(L"\n");
+                               return 0;
+                       default:
+normal_char:
+                               if (key.UnicodeChar == CHAR_CTRL_D || key.ScanCode == 0x17 ) {
+                                       Print(L"\nGiving up then...\n");
+                                       return  -1;
+                               }
+                               if (key.UnicodeChar == CHAR_NULL) break;
+
+                               if (pos > size-1) break;
+
+                               buffer[pos++] = key.UnicodeChar;
+
+                               /* Write the character out */
+                               Print(L"%c", key.UnicodeChar);
+               }
+       }
+       return 0;
+}
+
+static VOID
+display_message(VOID)
+{
+       fops_fd_t fd;
+       EFI_STATUS status;
+       INTN len, i;
+       CHAR16 *filename;
+       CHAR8 buf[256];
+
+       if ((filename = get_message_filename(0)) == NULL) return;
+
+       if (*filename == CHAR_NULL) return;
+
+       VERB_PRT(3, Print(L"opening message file %s\n", filename));
+
+       status = fops_open(filename, &fd);
+       if (EFI_ERROR(status)) {
+               VERB_PRT(3, Print(L"message file %s not found\n", filename));
+               return;
+       }
+       len = 256;
+       Print(L"\n");
+       while ((status = fops_read(fd, buf, &len)) == EFI_SUCCESS) {
+               /* XXX: ugly ! */
+               for (i=0; i < len; i++) {
+                       Print(L"%c", (CHAR16)buf[i]);
+               }
+               if (len < 256) break;
+       }
+       fops_close(fd);
+}
+
+static INTN
+simple_choose(CHAR16 **argv, INTN argc, INTN index, CHAR16 *kname, CHAR16 *cmdline)
+{      
+#      define BOOT_IMG_STR     L"BOOT_IMAGE="
+       CHAR16 buffer[CMDLINE_MAXLEN];
+       CHAR16 alt_buffer[CMDLINE_MAXLEN];
+       CHAR16 initrd_name[CMDLINE_MAXLEN];
+       CHAR16 args[CMDLINE_MAXLEN];
+       CHAR16 devname[CMDLINE_MAXLEN];
+       CHAR16 dpath[FILENAME_MAXLEN];
+       CHAR16 *slash_pos, *colon_pos, *backslash_pos;
+       UINTN len;
+       INTN ret;
+
+       buffer[0] = alt_buffer[0] = CHAR_NULL;
+
+       display_message();
+
+restart:
+       initrd_name[0] = kname[0] = cmdline[0] = args[0] = CHAR_NULL;
+
+       /* reset per image loader options */
+       Memset(&elilo_opt.img_opt, 0, sizeof(elilo_opt.img_opt));
+
+       /*
+        * check for alternate kernel image and params in EFI variable
+        */
+       if (elilo_opt.alt_check && alternate_kernel(alt_buffer, sizeof(alt_buffer)) == 0) {
+               argc     = argify(alt_buffer,sizeof(alt_buffer), argv); 
+               alt_argv = argv;
+               index    = 0;
+               args[0]  = initrd_name[0] = 0;
+               /* 
+                * don't check twice because the variable is deleted after
+                * first access
+                */
+               elilo_opt.alt_check = 0; 
+       }
+
+       if (elilo_opt.prompt) {
+               ret = select_kernel(buffer, sizeof(buffer));
+               if (ret == -1) return -1;
+               argc    = argify(buffer,sizeof(buffer), argv); 
+               index   = 0;
+       }
+
+       /*
+        * if we found an alternate choice and the user
+        * did not force it manually, then use the alternate
+        * option.
+        */
+       if (alt_buffer[0] && buffer[0] == CHAR_NULL) {
+               StrCpy(buffer, alt_buffer);
+       }
+
+       /*
+        * First search for matching label in the config file
+        * if options were specified on command line, they take
+        * precedence over the ones in the config file
+        *
+        * if no match is found, the args and initrd arguments may
+        * still be modified by global options in the config file.
+        */
+       ret = find_label(argv[index], kname, args, initrd_name);
+
+       /*
+        * not found, so assume first argument is kernel name and
+        * not label name 
+        */
+       if (ret == -1) {
+               if (argv[index]) 
+                       StrCpy(kname, argv[index]);
+               else
+                       StrCpy(kname, elilo_opt.default_kernel);
+       }
+       /*
+        * no matter what happened for kname, if user specified
+        * additional options, they override the ones in the
+        * config file 
+        */
+       if (argc > 1+index) {
+               /*StrCpy(args, argv[++index]);*/
+               while (++index < argc) {
+                       StrCat(args, L" ");
+                       StrCat(args, argv[index]);
+               }
+       }
+       /*
+        * if initrd specified on command line, it overrides
+        * the one defined in config file, if any
+        */
+       if (elilo_opt.initrd[0] == CHAR_NULL && initrd_name[0] != CHAR_NULL) {
+               StrCpy(elilo_opt.initrd, initrd_name);
+       }
+
+       VERB_PRT(1,  { Print(L"kernel     is  '%s'\n", kname);
+                      Print(L"arguments  are '%s'\n", args);
+                       if (elilo_opt.initrd[0]) Print(L"initrd      is '%s'\n", elilo_opt.initrd);
+                     });
+
+       if (elilo_opt.prompt == 0) {
+               /* minimal printing */
+               Print(L"ELILO\n");
+               ret = wait_timeout(elilo_opt.delay);
+               if (ret != 0) {
+                       elilo_opt.prompt = 1;
+                       elilo_opt.timeout =  ELILO_TIMEOUT_INFINITY;
+                       goto restart;
+               }
+       }
+       /*
+        * add the device name, if not already specified, 
+        * so that we know where we came from
+        */
+       slash_pos     = StrChr(kname, L'/');
+       backslash_pos = StrChr(kname, L'\\');
+       colon_pos     = StrChr(kname, L':');
+
+       if (backslash_pos && backslash_pos < slash_pos) slash_pos = backslash_pos;
+
+       if (colon_pos == NULL || (slash_pos && (slash_pos < colon_pos))) {
+               StrCpy(devname, fops_bootdev_name());
+               StrCat(devname, L":");
+
+               /* the default path is always terminated with a separator */
+               if (kname[0] != L'/' && kname[0] != L'\\') {
+                       fops_getdefault_path(dpath,FILENAME_MAXLEN); 
+                       StrCat(devname, dpath);
+               }
+       } else {
+               devname[0] = CHAR_NULL;
+       }
+
+       /*
+        * create final argument list to the kernel
+        */
+       len = StrLen(BOOT_IMG_STR)      /* BOOT_IMAGE= */
+            +StrLen(devname)           /* device name */
+            +StrLen(kname)             /* kernel name */
+            +1                         /* space */
+            +StrLen(args);             /* args length */
+
+       if (len >= CMDLINE_MAXLEN-1) {
+               ERR_PRT((L" arguments list too long cannot fit BOOT_IMAGE\n"));
+               return -1;
+       }
+       StrCpy(cmdline, L"BOOT_IMAGE=");
+       StrCat(cmdline, devname);
+       StrCat(cmdline, kname);
+       StrCat(cmdline, L" ");
+       StrCat(cmdline, args);
+
+       return 0;
+}
+
+static INTN
+simple_probe(EFI_HANDLE dev)
+{
+       /* this chooser always work */
+       return 0;
+}
+
+chooser_t simple_chooser={
+       L"simple",
+       simple_probe,
+       simple_choose
+};
+
diff --git a/choosers/simple.h b/choosers/simple.h
new file mode 100644 (file)
index 0000000..5cf109d
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  GNU EFI is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  GNU EFI is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU EFI; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __ELILO_CHOOSER_SIMPLE_H__
+#define __ELILO_CHOOSER_SIMPLE_H__
+
+#include "fileops.h"
+
+extern chooser_t simple_chooser;
+
+#endif
+
+
diff --git a/choosers/textmenu.c b/choosers/textmenu.c
new file mode 100644 (file)
index 0000000..9379b0e
--- /dev/null
@@ -0,0 +1,528 @@
+/* 
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Richard Hirst <rhirst@linuxcare.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+
+#define MAX_LABELS     16
+#define MSGBUFLEN      4096
+
+static UINT8 msgbuf[MSGBUFLEN];
+static CHAR16 *labels[MAX_LABELS];
+static CHAR16 *descriptions[MAX_LABELS];
+static INTN nlabels;
+static INTN CursorRow, CursorCol, PromptRow, PromptCol;
+static INTN MenuRow, MenuCol, MenuWidth, MenuHeight;
+static INTN DisplayParsed, CurrentAttr, PromptAttr;
+static INTN PromptWidth, MenuHiAttr, MenuLoAttr;
+static INTN PromptLen, MenuActive, MenuFirst;
+static CHAR16 PromptBuf[CMDLINE_MAXLEN];
+
+#define DEF_ATTR       EFI_TEXT_ATTR(EFI_LIGHTGRAY,EFI_BLACK)
+
+
+#define ClearScreen()  ST->ConOut->ClearScreen(ST->ConOut)
+#define SetTextAttr(a) ST->ConOut->SetAttribute(ST->ConOut, a)
+
+static INTN
+tohex(INTN c)
+{
+       if (c >= '0' && c <= '9')
+               return c - '0';
+       else if (c >= 'A' && c <= 'F')
+               return c = c - 'A' + 10;
+       else if (c >= 'a' && c <= 'f')
+               return c = c - 'a' + 10;
+       else
+               return 16;
+}
+
+static VOID
+paint_msg(UINT8 *msg)
+{
+       INTN c;
+
+       CursorCol = CursorRow = 0;
+       CurrentAttr = DEF_ATTR;
+       SetTextAttr(CurrentAttr);
+       ClearScreen();
+       while ((c = *msg++)) {
+               /* First map VGA to EFI line drawing chars */
+               if      (c == 0xda) c = BOXDRAW_DOWN_RIGHT;
+               else if (c == 0xc4) c = BOXDRAW_HORIZONTAL;
+               else if (c == 0xbf) c = BOXDRAW_DOWN_LEFT;
+               else if (c == 0xb3) c = BOXDRAW_VERTICAL;
+               else if (c == 0xd9) c = BOXDRAW_UP_LEFT;
+               else if (c == 0xc0) c = BOXDRAW_UP_RIGHT;
+               else if (c == 0xb4) c = BOXDRAW_VERTICAL_LEFT;
+               else if (c == 0xc3) c = BOXDRAW_VERTICAL_RIGHT;
+               else if (c == 0x1e) c = GEOMETRICSHAPE_UP_TRIANGLE;
+               else if (c == 0x1f) c = GEOMETRICSHAPE_DOWN_TRIANGLE;
+               else if (c > 0x7f)  c = '?';
+
+               /* Now print the printable chars, then process controls */
+               if (c >= ' ') {
+                       Print(L"%c", c);
+                       CursorCol++;
+               }
+               else {
+                       switch (c) {
+                       case '\r':              /* Ignore CR */
+                               break;
+                       case '\n':              /* LF treated as cr/lf */
+                               CursorRow++;
+                               CursorCol = 0;
+                               Print(L"\n");
+                               break;
+                       case 0x0c:              /* FF - Clear screen */
+                               CursorCol = CursorRow = 0;
+                               ClearScreen();
+                               break;
+                       case 0x0f:              /* ^O - Attributes */
+                               if (msg[0] && msg[1]) {
+                                       INTN bg = tohex(*msg++);
+                                       INTN fg = tohex(*msg++);
+
+                                       if (bg < 16 || fg < 16) {
+                                               CurrentAttr = EFI_TEXT_ATTR(fg, bg); 
+                                               SetTextAttr(CurrentAttr);
+                                       }
+                               }
+                               break;
+                       case 0x01:              /* ^A - Prompt */
+                               if (!DisplayParsed) {
+                                       if (!PromptRow) {
+                                               PromptRow = CursorRow;
+                                               PromptCol = CursorCol;
+                                               PromptAttr = CurrentAttr;
+                                       }
+                                       else if (!PromptWidth)
+                                               PromptWidth = CursorCol - PromptCol;
+                                       /* else bad syntax */
+                               }
+                               break;
+                       case 0x02:              /* ^B - Menu */
+                               if (!DisplayParsed) {
+                                       if (!MenuRow) {
+                                               MenuRow = CursorRow;
+                                               MenuCol = CursorCol;
+                                               MenuLoAttr = CurrentAttr;
+                                       }
+                                       else if (!MenuWidth) {
+                                               MenuWidth = CursorCol - MenuCol;
+                                               MenuHeight = CursorRow - MenuRow + 1;
+                                               MenuHiAttr = CurrentAttr;
+                                       }
+                                       /* else bad syntax */
+                               }
+                               break;
+                       default:
+                               Print(L"?");
+                               CursorCol++;
+                       }
+               }
+       }
+}
+
+
+static VOID
+paint_prompt(VOID)
+{
+       INTN offset = PromptLen > PromptWidth - 1 ? PromptLen - PromptWidth + 1: 0;
+       SetTextAttr(PromptAttr);
+       PrintAt(PromptCol, PromptRow, L"%s%s", PromptBuf + offset, L" \b");
+       SetTextAttr(CurrentAttr);
+}
+
+static VOID
+paint_menu(VOID)
+{
+       INTN i, j;
+
+       for (i = 0; i < MenuHeight; i++) {
+               INTN attr = (i + MenuFirst == MenuActive) ? MenuHiAttr: MenuLoAttr;
+               CHAR16 description[80];
+
+               for (j = 0; j < MenuWidth; j++)
+                       description[j] = ' ';
+               description[MenuWidth] = '\0';
+               if (i + MenuFirst < nlabels) {
+                       for (j = 0; descriptions[i + MenuFirst][j] && j < MenuWidth; j++)
+                               description[j+1] = descriptions[i + MenuFirst][j];
+               }
+               SetTextAttr(attr);
+               PrintAt(MenuCol, MenuRow + i, L"%-.*s", MenuWidth, description);
+               SetTextAttr(CurrentAttr);
+       }
+       paint_prompt();
+}
+
+static INTN
+read_message_file(INTN msg, INT8 *buf, INTN max)
+{
+       CHAR16 *filename;
+       fops_fd_t message_fd;
+       EFI_STATUS status;
+       INTN len = max;
+
+       if (msg > 10) return 0;
+
+       if ((filename = get_message_filename(msg)) == NULL) {
+               VERB_PRT(3, Print(L"no message file specified\n"));
+               return 0;
+       }
+
+       VERB_PRT(3, Print(L"opening message file %s\n", filename));
+
+       status = fops_open(filename, &message_fd);
+       if (EFI_ERROR(status)) {
+               VERB_PRT(3, Print(L"message file %s not found\n", filename));
+               return 0;
+       }
+
+       status = fops_read(message_fd, buf, &len);
+       if (EFI_ERROR(status)) {
+               VERB_PRT(3, Print(L"Error reading message file\n"));
+               len = 0;
+       }
+
+       fops_close(message_fd);
+
+       VERB_PRT(3, Print(L"done reading message file %s\n", filename));
+
+       return len;
+}
+
+
+/*
+ * interactively select a kernel image and options.
+ * The kernel can be an actual filename or a label in the config file
+ * Return:
+ *     -1: if unsucessful
+ *      0: otherwise
+ */
+static INTN
+select_kernel(CHAR16 *label, INTN lsize)
+{
+#define CHAR_CTRL_C    (L'\003') /* Unicode CTRL-C */
+#define CHAR_CTRL_D    (L'\004') /* Unicode CTRL-D */
+#define CHAR_CTRL_F    (L'\006') /* Unicode CTRL-F */
+#define CHAR_DEL       (L'\177') /* Unicode DEL */
+       SIMPLE_INPUT_INTERFACE *ip = systab->ConIn;
+       EFI_INPUT_KEY key;
+       EFI_STATUS status;
+       INT8 first_time = 1;
+       INTN i;
+       INT8 fn = 0;
+
+reprint:
+       i = read_message_file(0, msgbuf, MSGBUFLEN-1);
+       msgbuf[i] = 0;
+       paint_msg(msgbuf);
+       DisplayParsed = 1;
+       paint_menu();
+        CurrentAttr = PromptAttr;
+       SetTextAttr(CurrentAttr);
+
+       for (;;) {
+               while ((status=ip->ReadKeyStroke(ip, &key)) == EFI_NOT_READY);
+               if (EFI_ERROR(status)) {
+                       SetTextAttr(EFI_TEXT_ATTR(EFI_LIGHTGRAY,EFI_BLACK));
+                       ClearScreen();
+                       ERR_PRT((L"select_kernel readkey: %r", status));
+                       return -1;
+               } 
+               if (key.UnicodeChar == CHAR_CTRL_F) {
+                       fn = 1;
+                       continue;
+               }
+               if (fn) {
+                       if (key.UnicodeChar >= '0' && key.UnicodeChar <= '9') {
+                               if (key.UnicodeChar == '0')
+                                       key.ScanCode = SCAN_F10;
+                               else
+                                       key.ScanCode = SCAN_F1 + key.UnicodeChar - '1';
+                               key.UnicodeChar = 0;
+                       }
+                       fn = 0;
+               }
+               if (key.ScanCode == SCAN_UP) {
+                       if (MenuActive)
+                               MenuActive--;
+                       else
+                               continue;
+                       if (MenuActive < MenuFirst)
+                               MenuFirst = MenuActive;
+                       paint_menu();
+                       continue;
+               }
+               else if (key.ScanCode == SCAN_DOWN) {
+                       if (MenuActive < nlabels - 1)
+                               MenuActive++;
+                       else
+                               continue;
+                       if (MenuActive >= MenuFirst + MenuHeight)
+                               MenuFirst = MenuActive - MenuHeight + 1;
+                       paint_menu();
+                       continue;
+               }
+               else if (key.ScanCode >= SCAN_F1 && key.ScanCode <= SCAN_F10) {
+                       i = read_message_file(key.ScanCode - SCAN_F1 + 1, msgbuf, MSGBUFLEN-1);
+                       if (i) {
+                               msgbuf[i] = 0;
+                               paint_msg(msgbuf);
+                               while ((status=ip->ReadKeyStroke(ip, &key)) == EFI_NOT_READY);
+                               goto reprint;
+                       }
+               }
+
+               switch (key.UnicodeChar) {
+                       /* XXX Do we really want this in textmenual mode? */
+                       case L'?':
+                               Print(L"\n");
+                               print_devices();
+                               first_time = 0;
+                               goto reprint;
+                       case CHAR_BACKSPACE:
+                       case CHAR_DEL:
+                               if (PromptLen == 0) break;
+                               PromptLen--;
+                               PromptBuf[PromptLen] = 0;
+                               if (PromptLen >= PromptWidth-2)
+                                       paint_prompt();
+                               else
+                                       Print(L"\b \b");
+                               break;
+
+                       case CHAR_LINEFEED:
+                       case CHAR_CARRIAGE_RETURN:
+                               StrCpy(label, labels[MenuActive]);
+                               SetTextAttr(EFI_TEXT_ATTR(EFI_LIGHTGRAY,EFI_BLACK));
+                               ClearScreen();
+                               return 0;
+
+                       default:
+                               if ( key.UnicodeChar == CHAR_CTRL_D
+                                               || key.UnicodeChar == CHAR_CTRL_C) {
+                                       SetTextAttr(EFI_TEXT_ATTR(EFI_LIGHTGRAY,EFI_BLACK));
+                                       ClearScreen();
+                                       Print(L"\nGiving up then...\n");
+                                       return  -1;
+                               }
+                               if (key.UnicodeChar == CHAR_NULL) break;
+
+                               if (PromptLen > CMDLINE_MAXLEN-1) break;
+
+                               if (key.UnicodeChar < ' ' || key.UnicodeChar > 0x7e)
+                                       key.UnicodeChar = '?';
+                               PromptBuf[PromptLen++] = key.UnicodeChar;
+                               PromptBuf[PromptLen]   = 0;
+
+                               /* Write the character out */
+                               if (PromptLen >= PromptWidth-1)
+                                       paint_prompt();
+                               else
+                                       Print(L"%c", key.UnicodeChar);
+               }
+       }
+       return 0;
+}
+
+INTN
+textmenu_choose(CHAR16 **argv, INTN argc, INTN index, CHAR16 *kname, CHAR16 *cmdline)
+{      
+#      define BOOT_IMG_STR     L"BOOT_IMAGE="
+       CHAR16 label[CMDLINE_MAXLEN];
+       CHAR16 initrd_name[CMDLINE_MAXLEN];
+       CHAR16 args[CMDLINE_MAXLEN];
+       CHAR16 devname[CMDLINE_MAXLEN];
+       CHAR16 dpath[FILENAME_MAXLEN];
+       CHAR16 *slash_pos, *colon_pos, *backslash_pos;
+       UINTN len;
+       INTN ret;
+       VOID *handle = NULL;
+
+       /* Clear all static variables, as we might be called more than once */
+
+       CursorRow = CursorCol = PromptRow = PromptCol = 0;
+       MenuRow = MenuCol = MenuWidth = MenuHeight = 0;
+       DisplayParsed = CurrentAttr = PromptAttr = 0;
+       PromptWidth = MenuHiAttr = MenuLoAttr = 0;
+       PromptLen = MenuActive = MenuFirst = 0;
+       PromptBuf[0] = CHAR_NULL;
+
+       nlabels = 0;
+       while (nlabels < MAX_LABELS && (handle = get_next_description(handle, labels + nlabels, descriptions + nlabels))) {
+               if (descriptions[nlabels][0] == 0)
+                       descriptions[nlabels] = labels[nlabels];
+               nlabels++;
+       }
+restart:
+       initrd_name[0] = kname[0] = cmdline[0] = args[0] = CHAR_NULL;
+
+       /* reset per image loader options */
+       Memset(&elilo_opt.img_opt, 0, sizeof(elilo_opt.img_opt));
+
+       if (elilo_opt.prompt) {
+               ret = select_kernel(label, sizeof(label));
+               if (ret == -1) return -1;
+               argc    = argify(PromptBuf,sizeof(PromptBuf), argv); 
+               index   = 0;
+       }
+
+       /*
+        * check for alternate kernel image and params in EFI variable
+        */
+       if (elilo_opt.alt_check && alternate_kernel(PromptBuf, sizeof(PromptBuf)) == 0) {
+               argc    = argify(PromptBuf,sizeof(PromptBuf), argv); 
+               index   = 0;
+               label[0] = args[0] = initrd_name[0] = 0;
+       }
+
+       /*
+        * First search for matching label in the config file
+        * if options were specified on command line, they take
+        * precedence over the ones in the config file
+        *
+        * if no match is found, the args and initrd arguments may
+        * still be modified by global options in the config file.
+        */
+       if (label[0])
+               ret = find_label(label, kname, args, initrd_name);
+       else
+               ret = find_label(argv[index], kname, args, initrd_name);
+
+       /*
+        * not found, so assume first argument is kernel name and
+        * not label name 
+        */
+       if (ret == -1) {
+               if (argv[index]) 
+                       StrCpy(kname, argv[index]);
+               else
+                       StrCpy(kname, elilo_opt.default_kernel);
+       }
+       /*
+        * no matter what happened for kname, if user specified
+        * additional options, they override the ones in the
+        * config file 
+        */
+       if (label[0])
+               index--;
+       if (argc > 1+index) {
+               /*StrCpy(args, argv[++index]);*/
+               while (++index < argc) {
+                       StrCat(args, L" ");
+                       StrCat(args, argv[index]);
+               }
+       }
+       /*
+        * if initrd specified on command line, it overrides
+        * the one defined in config file, if any
+        */
+       if (elilo_opt.initrd[0] == CHAR_NULL && initrd_name[0] != CHAR_NULL) {
+               StrCpy(elilo_opt.initrd, initrd_name);
+       }
+
+       VERB_PRT(1,  { Print(L"kernel     is  '%s'\n", kname);
+                      Print(L"arguments  are '%s'\n", args);
+                       if (elilo_opt.initrd[0]) Print(L"initrd      is '%s'\n", elilo_opt.initrd);
+                     });
+
+       if (elilo_opt.prompt == 0) {
+               /* minimal printing */
+               Print(L"ELILO\n");
+               ret = wait_timeout(elilo_opt.delay);
+               if (ret != 0) {
+                       elilo_opt.prompt = 1;
+                       elilo_opt.timeout =  ELILO_TIMEOUT_INFINITY;
+                       goto restart;
+               }
+       }
+
+       /*
+        * add the device name, if not already specified, 
+        * so that we know where we came from
+        */
+       slash_pos     = StrChr(kname, L'/');
+       backslash_pos = StrChr(kname, L'\\');
+       colon_pos     = StrChr(kname, L':');
+
+       if (backslash_pos && backslash_pos < slash_pos) slash_pos = backslash_pos;
+
+       if (colon_pos == NULL || (slash_pos && (slash_pos < colon_pos))) {
+               StrCpy(devname, fops_bootdev_name());
+               StrCat(devname, L":");
+
+               /* the default path is always terminated with a separator */
+               if (kname[0] != L'/' && kname[0] != L'\\') {
+                       fops_getdefault_path(dpath,FILENAME_MAXLEN); 
+                       StrCat(devname, dpath);
+               }
+       } else {
+               devname[0] = CHAR_NULL;
+       }
+       /*
+        * create final argument list to the kernel
+        */
+       len = StrLen(BOOT_IMG_STR)      /* BOOT_IMAGE= */
+            +StrLen(devname)           /* device name */
+            +StrLen(kname)             /* kernel name */
+            +1                         /* space */
+            +StrLen(args);             /* args length */
+
+       if (len >= CMDLINE_MAXLEN-1) {
+               SetTextAttr(EFI_TEXT_ATTR(EFI_LIGHTGRAY,EFI_BLACK));
+               ClearScreen();
+               ERR_PRT((L" arguments list too long cannot fit BOOT_IMAGE\n"));
+               return -1;
+       }
+       StrCpy(cmdline, L"BOOT_IMAGE=");
+       StrCat(cmdline, devname);
+       StrCat(cmdline, kname);
+       StrCat(cmdline, L" ");
+       StrCat(cmdline, args);
+
+       VERB_PRT(3, Print(L"final command line is '%s'\n", cmdline));
+
+       return 0;
+}
+
+static INTN
+textmenu_probe(EFI_HANDLE dev)
+{
+       /* this chooser always works */
+       return 0;
+}
+
+chooser_t textmenu_chooser={
+       L"textmenu",
+       textmenu_probe,
+       textmenu_choose
+};
+
diff --git a/choosers/textmenu.h b/choosers/textmenu.h
new file mode 100644 (file)
index 0000000..4b50b01
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Richard Hirst <rhirst@linuxcare.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  GNU EFI is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  GNU EFI is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU EFI; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __ELILO_CHOOSER_TEXTMENU_H__
+#define __ELILO_CHOOSER_TEXTMENU_H__
+
+#include "fileops.h"
+
+extern chooser_t textmenu_chooser;
+
+#endif
+
+
diff --git a/config.c b/config.c
new file mode 100644 (file)
index 0000000..ac789b9
--- /dev/null
+++ b/config.c
@@ -0,0 +1,1171 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ *
+ * Portions of this file are derived from the  LILO/x86
+ * Copyright 1992-1997 Werner Almesberger. 
+ */
+
+#include <efi.h>
+#include <efilib.h>
+#include <efistdarg.h>
+
+#include "elilo.h"
+#include "config.h"
+
+/*
+ * The first default config file is architecture dependent. This is useful
+ * in case of network booting where the server is used for both types of
+ * architectures.
+ */
+#if defined(CONFIG_ia64)
+#define ELILO_ARCH_DEFAULT_CONFIG      L"elilo-ia64.conf"
+#elif defined (CONFIG_ia32)
+#define ELILO_ARCH_DEFAULT_CONFIG      L"elilo-ia32.conf"
+#else
+#error "You need to specfy your default arch config file"
+#endif
+
+/* 
+ * last resort config file. Common to all architectures
+ */
+#define ELILO_DEFAULT_CONFIG   L"elilo.conf"
+
+#define MAX_STRING     CMDLINE_MAXLEN
+#define CONFIG_BUFSIZE 512     /* input buffer size */
+
+/*
+ * maximum number of message files.
+ *
+ * main message= goes at entry 0, entries [1-12] are used for function keys
+ *
+ */
+#define MAX_MESSAGES   13
+
+typedef struct boot_image {
+       struct boot_image *next;
+       CHAR16  label[MAX_STRING];
+       CHAR16  kname[FILENAME_MAXLEN];
+       CHAR16  options[MAX_STRING];
+       CHAR16  initrd[FILENAME_MAXLEN];
+       CHAR16  root[FILENAME_MAXLEN];
+       CHAR16  fallback[MAX_STRING];
+       CHAR16  description[MAX_STRING];
+
+       UINTN   ramdisk;
+       UINTN   readonly;
+       UINTN   literal;
+
+       sys_img_options_t sys_img_opts; /* architecture specific image options */
+} boot_image_t;
+
+typedef enum {
+       TOK_ERR,
+       TOK_EQUAL,
+       TOK_STR,
+       TOK_EOF
+} token_t;
+
+/*
+ * global shared options
+ * architecture specific global options are private to each architecture
+ */
+typedef struct {
+       CHAR16          root[FILENAME_MAXLEN];  /* globally defined root fs */
+       CHAR16          initrd[FILENAME_MAXLEN];/* globally defined initrd  */
+       CHAR16          options[MAX_STRING];
+       CHAR16          default_image_name[MAX_STRING];
+       CHAR16          message_file[MAX_MESSAGES][FILENAME_MAXLEN]; 
+       CHAR16          chooser[FILENAME_MAXLEN];/* which image chooser to use */
+       CHAR16          config_file[FILENAME_MAXLEN];
+       boot_image_t    *default_image;
+
+       UINTN           readonly;
+
+       /* 
+        * options that may affect global elilo options
+        */
+       UINTN alt_check;
+       UINTN debug;
+       UINTN delay;
+       UINTN prompt;
+       UINTN timeout;
+       UINTN verbose;
+       UINTN edd30_no_force; /* don't force EDD30 if not set */
+} global_config_t;
+
+/*
+ * structure used to point to a group of options.
+ * Several group for the same category are supported via a linked list.
+ */
+typedef struct _config_option_group {
+       struct _config_option_group     *next;    /* pointer to next group */
+       config_option_t                 *options; /* the table of options for this group */
+       UINTN                           nentries; /* number of entries for this group */
+} config_option_group_t;
+
+static option_action_t do_image, do_literal, do_options;
+static INTN check_verbosity(VOID *), check_chooser(VOID *);
+
+static global_config_t global_config;  /* options shared by all images */
+
+/*
+ * Core global options: shared by all architectures, all modules
+ */
+static config_option_t global_common_options[]={
+{OPT_STR,      OPT_GLOBAL,     L"default",     NULL,           NULL,                   global_config.default_image_name},
+{OPT_NUM,      OPT_GLOBAL,     L"timeout",     NULL,           NULL,                   &global_config.timeout},
+{OPT_NUM,      OPT_GLOBAL,     L"delay",       NULL,           NULL,                   &global_config.delay},
+{OPT_BOOL,     OPT_GLOBAL,     L"debug",       NULL,           NULL,                   &global_config.debug},
+{OPT_BOOL,     OPT_GLOBAL,     L"prompt",      NULL,           NULL,                   &global_config.prompt},
+{OPT_NUM,      OPT_GLOBAL,     L"verbose",     NULL,           check_verbosity,        &global_config.verbose},
+{OPT_FILE,     OPT_GLOBAL,     L"root",        NULL,           NULL,                   global_config.root},
+{OPT_BOOL,     OPT_GLOBAL,     L"read-only",   NULL,           NULL,                   &global_config.readonly},
+{OPT_BOOL,     OPT_GLOBAL,     L"noedd30",     NULL,           NULL,                   &global_config.edd30_no_force},
+{OPT_CMD,      OPT_GLOBAL,     L"append",      NULL,           NULL,                   global_config.options},
+{OPT_FILE,     OPT_GLOBAL,     L"initrd",      NULL,           NULL,                   global_config.initrd},
+{OPT_FILE,     OPT_GLOBAL,     L"image",       do_image,       NULL,                   opt_offsetof(kname)},
+{OPT_BOOL,     OPT_GLOBAL,     L"checkalt",    NULL,           NULL,                   &global_config.alt_check},
+{OPT_STR,      OPT_GLOBAL,     L"chooser",     NULL,           check_chooser,          global_config.chooser},
+{OPT_FILE,     OPT_GLOBAL,     L"message",     NULL,           NULL,                   global_config.message_file[0]},
+{OPT_FILE,     OPT_GLOBAL,     L"f1",          NULL,           NULL,                   global_config.message_file[1]},
+{OPT_FILE,     OPT_GLOBAL,     L"f2",          NULL,           NULL,                   global_config.message_file[2]},
+{OPT_FILE,     OPT_GLOBAL,     L"f3",          NULL,           NULL,                   global_config.message_file[3]},
+{OPT_FILE,     OPT_GLOBAL,     L"f4",          NULL,           NULL,                   global_config.message_file[4]},
+{OPT_FILE,     OPT_GLOBAL,     L"f5",          NULL,           NULL,                   global_config.message_file[5]},
+{OPT_FILE,     OPT_GLOBAL,     L"f6",          NULL,           NULL,                   global_config.message_file[6]},
+{OPT_FILE,     OPT_GLOBAL,     L"f7",          NULL,           NULL,                   global_config.message_file[7]},
+{OPT_FILE,     OPT_GLOBAL,     L"f8",          NULL,           NULL,                   global_config.message_file[8]},
+{OPT_FILE,     OPT_GLOBAL,     L"f9",          NULL,           NULL,                   global_config.message_file[9]},
+{OPT_FILE,     OPT_GLOBAL,     L"f10",         NULL,           NULL,                   global_config.message_file[10]},
+{OPT_FILE,     OPT_GLOBAL,     L"f11",         NULL,           NULL,                   global_config.message_file[11]},
+{OPT_FILE,     OPT_GLOBAL,     L"f12",         NULL,           NULL,                   global_config.message_file[12]}
+};
+
+static config_option_t image_common_options[]={
+    {OPT_FILE, OPT_IMAGE,      L"root",        NULL,           NULL,   opt_offsetof(root)},
+    {OPT_BOOL, OPT_IMAGE,      L"read-only",   NULL,           NULL,   opt_offsetof(readonly)},
+    {OPT_CMD,  OPT_IMAGE,      L"append",      do_options,     NULL,   opt_offsetof(options)},
+    {OPT_CMD,  OPT_IMAGE,      L"literal",     do_literal,     NULL,   NULL},
+    {OPT_FILE, OPT_IMAGE,      L"initrd",      NULL,           NULL,   opt_offsetof(initrd)},
+    {OPT_STR,  OPT_IMAGE,      L"label",       NULL,           NULL,   opt_offsetof(label)},
+    {OPT_FILE, OPT_IMAGE,      L"image",       do_image,       NULL,   opt_offsetof(kname)},
+    {OPT_STR,  OPT_IMAGE,      L"description", NULL,           NULL,   opt_offsetof(description)},
+};
+
+#define OPTION_IS_GLOBAL(p)    ((p)->scope == OPT_GLOBAL)
+#define OPTION_IS_IMG_SYS(p)   ((p)->scope == OPT_IMAGE_SYS)
+
+#define CHAR_EOF       (CHAR16)-1      /* Unicode version of EOF */
+#define CHAR_NUM0      L'0'
+#define CHAR_NUM9      L'9'
+
+static UINTN line_num;
+static INTN back; /* can go back by one char */
+
+static config_option_group_t *global_option_list;
+static config_option_group_t *image_option_list;
+
+
+static config_option_group_t *current_options;
+
+static boot_image_t *image_list, *first_image;
+static boot_image_t *current_img;
+
+static INT8 config_buf[CONFIG_BUFSIZE];        /* input buffer: file must be in ASCII! */
+static UINTN buf_max, buf_pos;
+
+static fops_fd_t config_fd;
+
+static VOID 
+config_error(CHAR16 *msg,...)
+{
+    va_list ap;
+    extern UINTN _IPrint (UINTN, UINTN, SIMPLE_TEXT_OUTPUT_INTERFACE *, CHAR16 *, CHAR8 *, va_list);
+
+    Print(L"near line %d: ",line_num);
+
+    va_start(ap,msg);
+    _IPrint((UINTN)-1, (UINTN)-1, systab->ConOut, msg, NULL, ap);
+    va_end(ap);
+    Print(L"\n");
+}
+
+/*
+ * low level read routine
+ * Return:
+ *
+ * Success:
+ *     - the next available unicode character
+ * Error:
+ *     - CHAR_EOF : indicating error or EOF
+ *
+ * XXX: we suppose that the file is in ASCII format!
+ */
+static CHAR16
+getc(VOID)
+{
+       EFI_STATUS status;
+
+       if (buf_pos == 0 || buf_pos == buf_max) {
+               buf_max = CONFIG_BUFSIZE;
+               status = fops_read(config_fd, config_buf, &buf_max);
+               if (EFI_ERROR(status) || buf_max == 0) return CHAR_EOF;
+
+               buf_pos  = 0;
+       }
+       return (CHAR16)config_buf[buf_pos++];
+}
+
+
+/*
+ * get the next unicode character with a one character
+ * rewind buffer.
+ */
+static CHAR16
+next(VOID)
+{
+    CHAR16 ch;
+
+    if (back) {
+       ch = back;
+       back = 0;
+       return ch;
+    }
+    return getc();
+}
+
+/*
+ * rewind by one character
+ */
+static VOID
+again(CHAR16 ch)
+{
+    if (back) { config_error(L"config: again invoked twice"); }
+    back = ch;
+}
+
+/*
+ * Look for matching option in the current group
+ *
+ * Return:
+ *     - pointer to option if found
+ *     - NULL if not found
+ */
+static config_option_t *
+find_option(config_option_group_t *grp, CHAR16 *str)
+{
+       config_option_t *p = NULL;
+       config_option_t *end;
+
+       while(grp) {
+               p = grp->options;
+               end = grp->options+grp->nentries;
+
+               while (p != end) {
+                       if (!StrCmp(str, p->name)) return p;
+                       p++;
+               }
+               grp = grp->next;
+       }
+       return NULL;
+}
+
+/*
+ * main parser function
+ * Return:
+ *     - a token code representing the kind of element found
+ *     - TOK_EOF: end-of-file (or error) detected
+ *     - TOK_STR: if string is found
+ *     - TOK_EQUAL: for the '=' sign
+ *     - TOK_ERR: in case of (parsing) error
+ */
+static token_t
+get_token(CHAR16 *str, UINTN maxlen)
+{
+    INTN ch, escaped;
+    CHAR16 *here;
+
+    for (;;) {
+       while ((ch = next()), ch == ' ' || ch == '\t' || ch == '\n') if (ch == '\n') line_num++;
+
+       if (ch == CHAR_EOF) return TOK_EOF;
+
+       if (ch != '#') break;
+
+       /* skip comment line */
+       while ((ch = next()), ch != '\n') if (ch == CHAR_EOF) return TOK_EOF;
+       line_num++;
+    }
+    if (ch == '=') return TOK_EQUAL;
+
+    if (ch == '"') {
+       here = str;
+       while (here-str < maxlen) {
+           if ((ch = next()) == CHAR_EOF) {
+                   config_error(L"EOF in quoted string");
+                   return TOK_ERR;
+           }
+           if (ch == '"') {
+               *here = 0;
+               return TOK_STR;
+           }
+           if (ch == '\\') {
+               ch = next();
+               if (ch != '"' && ch != '\\' && ch != '\n') {
+                   config_error(L"Bad use of \\ in quoted string");
+                   return TOK_ERR;
+               }
+               if (ch == '\n') continue;
+#if 0
+                   while ((ch = next()), ch == ' ' || ch == '\t');
+                   if (!ch) continue;
+                   again(ch);
+                   ch = ' ';
+               }
+#endif
+           }
+           if (ch == '\n' || ch == '\t') {
+               config_error(L"\\n and \\t are not allowed in quoted strings");
+               return TOK_ERR;
+           }
+           *here++ = ch;
+       }
+       config_error(L"Quoted string is too long");
+       return TOK_ERR;
+    }
+
+    here    = str;
+    escaped = 0;
+
+    while (here-str < maxlen) {
+       if (escaped) {
+           if (ch == CHAR_EOF) {
+                   config_error(L"\\ precedes EOF");
+                   return TOK_ERR;
+           }
+           if (ch == '\n') line_num++;
+           else *here++ = ch == '\t' ? ' ' : ch;
+           escaped = 0;
+       }
+       else {
+           if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '#' ||
+             ch == '=' || ch == CHAR_EOF) {
+               again(ch);
+               *here = 0;
+               return TOK_STR;
+           }
+           if (!(escaped = (ch == '\\'))) *here++ = ch;
+       }
+       ch = next();
+    }
+    config_error(L"Token is too long");
+    return TOK_ERR;
+}
+
+static INTN
+image_check(boot_image_t *img)
+{
+       boot_image_t *b;
+
+       if (img == NULL) return -1;
+
+       /* do the obvious first */
+       if (img->label[0] == '\0') {
+               config_error(L"image has no label");
+               return -1;
+       }
+
+       /* image_list points to the */
+       for(b=image_list; b; b = b->next) {
+               if (img == b) continue;
+               if (!StrCmp(img->label, b->label)) {
+                       config_error(L"image with label %s already defined", img->label);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static INTN 
+global_check(VOID)
+{
+       return 0;
+}
+
+static INTN
+final_check(VOID)
+{
+       boot_image_t *b;
+
+       if (global_config.default_image_name[0]) {
+               for(b=image_list; b; b = b->next) {
+                       if (!StrCmp(b->label, global_config.default_image_name)) goto found;
+               }
+               config_error(L"default image '%s' not defined ", global_config.default_image_name);
+               return -1;
+       }
+       global_config.default_image = first_image;
+       return 0;
+found:
+       global_config.default_image = b;
+       return 0;
+}
+
+/*
+ * depending on the active set of options
+ * adjust the option data pointer to:
+ * if global option set:
+ *     - just the straight value from p->data
+ * if image option set:
+ *     - adjust as offset to image
+ * Return:
+ *     - the adjusted pointer
+ */
+static inline VOID *
+adjust_pointer(config_option_t *p)
+{
+       /*
+        * adjust pointer
+        */
+       if (OPTION_IS_GLOBAL(p)) return p->data;
+
+       if (OPTION_IS_IMG_SYS(p)) return (VOID *)((UINTN)&current_img->sys_img_opts + p->data);
+
+       return (VOID *)((UINTN)current_img + p->data);
+}
+
+/*
+ * create a new image entry
+ */
+static INTN
+do_image(config_option_t *p, VOID *str)
+{
+       boot_image_t *img;
+       
+       /*
+        * if we move to a new image from the current one
+        * then we need to check for validity of current.
+        *
+        * if this is the first image, then check the global
+        * options.
+        */
+       if (current_img) {
+               if (image_check(current_img) == -1) return -1;
+       } else if (global_check() == -1) return -1;
+
+       img = (boot_image_t *)alloc(sizeof(boot_image_t), EfiLoaderData);
+       if (img == NULL) return -1;
+
+       Memset(img, 0, sizeof(boot_image_t));
+
+       DBG_PRT((L"must do image on %s", (CHAR16 *)str));
+
+       /* copy kernel file name */
+       StrCpy(img->kname, str);
+
+       /* switch to image mode */
+       current_options = image_option_list;
+
+       /* keep track of first image in case no default is defined */
+       if (image_list == NULL) first_image = img;
+
+       /* append to end of image list, so when a chooser asks for the list
+        * it gets them in the order they were in in the config file
+        */
+       if (image_list == NULL)
+               image_list = img;
+       else {
+               boot_image_t * p = image_list;
+
+               while (p->next)
+                       p = p->next;
+               p->next = img;
+       }
+
+       /* update current image */
+       current_img = img;
+
+       return 0;
+}
+
+/*
+ * by default all boolean options are defined
+ * as false. This function sets the boolean
+ * to true
+ */
+static INTN
+do_boolean(config_option_t *p)
+{
+       INT8 *buf;
+
+       buf = adjust_pointer(p);
+
+       if (p->action) return p->action(p, NULL);
+
+       /* set the field to true, overwrite if already defined */
+       *buf = 1;
+
+       return 0;
+}
+       
+/*
+ * the image option 'literal' requires special handling
+ * because it overrides any defined option be it global or
+ * local. so we use the option field and record the fact that
+ * it should be interpreted as literal
+ */
+static INTN
+do_literal(config_option_t *p, VOID *str)
+{
+       /*
+        * we know we have a valid current image at this point
+        */
+       StrCpy(current_img->options, str);
+
+       current_img->literal = 1;
+
+       return 0;
+}
+
+static INTN
+do_options(config_option_t *p, VOID *str)
+{
+       /* we ignore append if literal is already defined */
+       if (current_img->literal) return 0;
+
+       /*
+        * we know we have a valid current image at this point
+        */
+       StrCpy(current_img->options, str);
+
+       return 0;
+}
+
+static INTN
+do_numeric(config_option_t *p)
+{
+       CHAR16 numstr[MAX_STRING];
+       CHAR16 *str;
+       token_t tok;
+       UINTN *buf;
+       UINTN tmp;
+
+       /*
+        * match the '=' sign
+        */
+       tok = get_token(numstr, MAX_STRING);
+       if (tok != TOK_EQUAL) {
+               config_error(L"Option %s expects an equal signal + value", p->name);
+               return -1;
+       }
+
+       /*
+        * now get the value
+        * XXX: = lexer should return TOK_NUM (and we'd be done)
+        */
+       tok = get_token(numstr, MAX_STRING);
+       if (tok != TOK_STR) {
+               config_error(L"Option %s expects a value", p->name);
+               return -1;
+       }
+       str = numstr;
+       /*
+        * if there is a customized way of doing the operation
+        * do it and bypass generic
+        */
+       if (p->action) return p->action(p, str);
+
+       /*
+        * no field to update
+        */
+       if (p->data == NULL) return 0;
+
+       buf = (UINTN *)adjust_pointer(p);
+
+       while (*str && *str >= CHAR_NUM0 && *str <= CHAR_NUM9) str++;
+       if (*str) {
+               config_error(L"%s is expecting a numeric decimal value", p->name);
+               return -1;
+       }
+
+       tmp = Atoi(numstr);
+
+       if (p->check && p->check(&tmp) == -1) return -1;
+
+       /*
+        * check for multiple definitions in the same context
+        * XXX: zero must not be a valid number !
+        */
+       if (*buf) {
+               config_error(L"option %s is already defined in this context", p->name);
+               return -1;
+       }
+
+       *buf = tmp;
+
+       return 0;
+}
+
+static INTN
+check_verbosity(VOID *data)
+{
+       UINTN *val = (UINTN *)data;
+
+       if (*val > 5) {
+               config_error(L"Verbosity level must be in [0-5] and not %d", *val);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * here we simply check if chooser is compiled in. At this point, the chooser
+ * initialization is not done, so we don't know if that chooser will even be
+ * useable.
+ */
+static INTN
+check_chooser(VOID *data)
+{
+       CHAR16 *chooser = (CHAR16 *)data;
+
+       if (exist_chooser(chooser) == -1) {
+               config_error(L"chooser %s is unknown\n", chooser);
+               return -1;
+       }
+       return 0;
+}
+
+
+static INTN
+do_string_core(config_option_t *p, CHAR16 *str, UINTN maxlen, CHAR16 *msg)
+{
+       token_t tok;
+       CHAR16 *buf;
+
+       /*
+        * match the '=' sign
+        */
+       tok = get_token(str, maxlen);
+       if (tok != TOK_EQUAL) {
+               config_error(L"Option %s expects an equal signal + %s", p->name, msg);
+               return -1;
+       }
+
+       /*
+        * now get the value
+        */
+       tok = get_token(str, maxlen);
+       if (tok != TOK_STR) {
+               config_error(L"Option %s expects %s", p->name, msg);
+               return -1;
+       }
+
+       /*
+        * if there is a customized way of doing the operation
+        * do it and bypass generic
+        */
+       if (p->action) return p->action(p, str);
+
+       /*
+        * no field to update
+        */
+       if (p->data == NULL) return 0;
+
+       buf = adjust_pointer(p);
+
+       if (*buf != CHAR_NULL) {
+               config_error(L"'%s' already defined", p->name);
+               return -1;
+       }
+       if (p->check && p->check(str) == -1) return -1;
+
+       /*
+        * initialize field
+        */
+       StrCpy(buf, str);
+
+       return 0;
+}
+       
+static INTN
+do_string(config_option_t *p)
+{
+       CHAR16 str[MAX_STRING];
+
+       return do_string_core(p, str, MAX_STRING, L"string");
+}
+       
+static INTN
+do_file(config_option_t *p)
+{
+       CHAR16 str[FILENAME_MAXLEN];
+
+       return do_string_core(p, str, FILENAME_MAXLEN, L"filename");
+}
+
+
+static INTN
+do_cmd(config_option_t *p)
+{
+       CHAR16 str[CMDLINE_MAXLEN];
+       return do_string_core(p, str, CMDLINE_MAXLEN, L"kernel options");
+}
+
+
+INTN
+config_parse(VOID)
+{
+       CHAR16 str[MAX_STRING];
+       INTN ret = -1;
+       token_t tok;
+       config_option_t *p;
+
+       for (;;) {
+               tok = get_token(str, MAX_STRING);
+
+               if (tok == TOK_EOF) break;
+
+               if (tok == TOK_ERR) return -1;
+
+               if ( (p = find_option(current_options, str)) == NULL) {
+                       config_error(L"Unkown option %s", str);
+                       return -1;
+               }
+
+               /* make sure we trap in case of error */
+               ret = -1;
+
+               switch(p->type) {
+                       case OPT_BOOL:
+                               ret = do_boolean(p);
+                               break;
+                       case OPT_STR:
+                               ret = do_string(p);
+                               break;
+                       case OPT_NUM:
+                               ret = do_numeric(p);
+                               break;
+                       case OPT_FILE:
+                               ret = do_file(p);
+                               break;
+                       case OPT_CMD:
+                               ret = do_cmd(p);
+                               break;
+                       default:
+                               config_error(L"Unkown option type %d", p->type);
+               }
+               if (ret == -1) goto error;
+       }
+       if (current_img) {
+               ret = image_check(current_img); 
+       } else {
+               config_error(L"No image defined !");
+       }
+       if (ret == 0) ret = final_check();
+error:
+       return ret;
+}
+       
+static VOID
+update_elilo_opt(VOID)
+{
+       /*
+        * update boolean options first
+        * Rule: by default not set unless explcitely requested on command line
+        * therefore we can just update from global_config is set there.
+        */
+       if (global_config.alt_check) elilo_opt.alt_check = 1;
+
+       if (global_config.debug) elilo_opt.debug   = 1;
+       if (global_config.prompt) elilo_opt.prompt = 1;
+
+       /*
+        * update only if not set on command line
+        * XXX: rely on the existence of a non-valid value as a marker than
+        * the option was not specified on the command line
+        */
+       if (global_config.verbose && elilo_opt.verbose == 0) 
+               elilo_opt.verbose = global_config.verbose;
+
+       if (global_config.chooser[0] && elilo_opt.chooser[0] == 0)
+               StrCpy(elilo_opt.chooser, global_config.chooser);
+
+       /*
+        * if edd30_no_force not set by command line option but via config
+        * file, then propagate
+        */
+       if (global_config.edd30_no_force && elilo_opt.edd30_no_force == 0)
+               elilo_opt.edd30_no_force = 1;
+
+       /*
+        * Difficult case of numeric where 0 can be a valid value
+        * XXX: find a better way of handling these options!
+        */
+       if (global_config.delay && elilo_opt.delay_set == 0)
+               elilo_opt.delay = global_config.delay;
+
+       /* readjusted by caller if necessary. 0 not a valid value here */
+       elilo_opt.timeout = global_config.timeout;
+
+       /* initrd is updated later when we have an image match */
+}
+
+/*
+ * When called after read_config(), this function returns the config file
+ * used by the loader, or NULL if no file was found.
+ */
+CHAR16 *
+get_config_file(VOID)
+{
+       return global_config.config_file[0] ? global_config.config_file : NULL; 
+}
+
+EFI_STATUS
+read_config(CHAR16 *filename, INTN retry)
+{
+       EFI_STATUS status;
+       INTN ret;
+       
+       if (filename == NULL) return EFI_INVALID_PARAMETER;
+
+       VERB_PRT(3, Print(L"trying config file %s\n", filename));
+
+       StrCpy(global_config.config_file, filename);
+
+       status = fops_open(filename, &config_fd);
+       if (EFI_ERROR(status)) {
+               /* 
+                * if the user explicitely specified a filename and we can't
+                * find it, then we must fail.
+                */
+               if (elilo_opt.parse_only || retry == 0) {
+                       VERB_PRT(3, Print(L"cannot open config file %s\n", filename));
+                       return status;
+               }
+               /*
+                * if not already submitted filename,
+                */
+               if (StrCmp(filename, ELILO_ARCH_DEFAULT_CONFIG)) {
+                       /*
+                        * try the arch default file, now
+                        */
+                       VERB_PRT(3,Print(L"config file %s not found, trying %s\n", 
+                                  filename, ELILO_ARCH_DEFAULT_CONFIG));
+
+                       StrCpy(global_config.config_file,ELILO_ARCH_DEFAULT_CONFIG);
+
+                       status = fops_open(ELILO_ARCH_DEFAULT_CONFIG, &config_fd);
+               } 
+               /*
+                * if arch specific did not work, try generic
+                */
+               if (EFI_ERROR(status) && StrCmp(filename, ELILO_DEFAULT_CONFIG)) {
+                       /*
+                        * try the default file as a last resort
+                        */
+                       VERB_PRT(3,Print(L"config file %s not found, trying %s\n", 
+                                  ELILO_ARCH_DEFAULT_CONFIG, ELILO_DEFAULT_CONFIG));
+
+                       StrCpy(global_config.config_file, ELILO_DEFAULT_CONFIG);
+                       status = fops_open(ELILO_DEFAULT_CONFIG, &config_fd);
+               }
+               /*
+                * if nothing worked, then bail out
+                */
+               if (EFI_ERROR(status)) {
+                       VERB_PRT(3, Print(L"no valid config file found\n"));
+                       global_config.config_file[0] = CHAR_NULL;
+                       return status;
+               }
+       }
+       /*
+        * start numbering at line 1
+        */
+       line_num = 1;
+
+       ret = config_parse();
+
+       fops_close(config_fd);
+
+       DBG_PRT((L"done parsing config file\n"));
+
+       if (ret != 0) return EFI_INVALID_PARAMETER;
+
+       update_elilo_opt();
+
+       return EFI_SUCCESS;
+}
+
+VOID
+print_label_list(VOID)
+{
+       boot_image_t *img, *dfl = global_config.default_image;
+
+       if (dfl) Print(L"%s ", dfl->label);
+
+       for (img = image_list; img; img = img->next) {
+               if (img != dfl) Print(L"%s   ", img->label);
+       }
+}
+
+/* Make labels and corresponding descriptions available to choosers.  The
+ * idea is that the caller passes NULL for 'prev'; the first time, and
+ * passes the previously returned value on subsequent calls.  The caller
+ * repeats until this fn returns NULL.
+ */
+
+VOID *
+get_next_description(VOID *prev, CHAR16 **label, CHAR16 **description)
+{
+       boot_image_t *img = (boot_image_t *)prev;
+
+       if (img == NULL)
+               img = image_list;
+       else
+               img = img->next;
+
+       if (img) {
+               *label = img->label;
+               *description = img->description;
+               return (void *)img;
+       }
+       else
+               return NULL;
+}
+
+/*
+ * find a description using the label name
+ * return NULL if label not found or no description defined
+ */
+CHAR16 *
+find_description(CHAR16 *label)
+{
+       boot_image_t *img;
+
+       /* Attempt to find the image name now */
+       for (img = image_list; img; img = img->next) {
+               if (StriCmp(img->label, label) == 0) {
+                       return img->description;
+               }
+       }
+       return NULL;
+}
+
+INTN
+find_label(CHAR16 *label, CHAR16 *kname, CHAR16 *options, CHAR16 *initrd)
+{
+       boot_image_t *img;
+
+       if (label == NULL) {
+               if (global_config.default_image == NULL) return -1;
+               img = global_config.default_image;
+               goto found;
+       }
+
+       options[0] = 0;
+
+       /* Attempt to find the image name now */
+       for (img = image_list; img; img = img->next) {
+               if (StriCmp(img->label, label) == 0) {
+                       goto found;
+               }
+       }
+       /*
+        * when the label does not exist, we still propagate the global options
+        */
+       if (global_config.root[0]) {
+               StrCpy(options, L" root=");
+               StrCat(options, global_config.root);
+       }
+
+       if (global_config.options[0]) {
+               StrCat(options, L" ");
+               StrCat(options, global_config.options);
+       }
+       if (global_config.readonly) StrCat(options, L" ro");
+
+       if (global_config.initrd[0]) StrCpy(initrd, global_config.initrd);
+
+       /* make sure we don't get garbage here */
+       elilo_opt.sys_img_opts = NULL;
+
+       return -1;
+found:
+       StrCpy(kname, img->kname);
+
+       /*
+        * literal option overrides any other image-based or global option
+        *
+        * In any case, the image option has priority over the global option
+        */
+       if (img->literal == 0) {
+               if (img->root[0] || global_config.root[0]) {
+                       StrCat(options, L"root=");
+                       StrCat(options, img->root[0] ? img->root : global_config.root);
+               }
+               /* XXX: check max length */
+               if (img->options[0] || global_config.options[0]) {
+                       StrCat(options, L" ");
+                       StrCat(options, img->options[0] ? img->options: global_config.options);
+               }
+               if (img->readonly || global_config.readonly) {
+                       StrCat(options, L" ro");
+               }
+       } else {
+               /* literal options replace everything */
+               StrCpy(options, img->options);
+       }
+
+       /* per image initrd has precedence over global initrd */
+       if (img->initrd[0]) 
+               StrCpy(initrd, img->initrd);
+       else if (global_config.initrd[0])
+               StrCpy(initrd, global_config.initrd);
+
+       /*
+        * point to architecture dependent options for this image
+        */
+       elilo_opt.sys_img_opts = &img->sys_img_opts;
+
+       DBG_PRT((L"label %s: kname=%s options=%s initrd=%s", img->label, kname, options, initrd));
+
+       return 0;
+}
+
+static VOID
+print_options(config_option_group_t *grp, BOOLEAN first)
+{
+       config_option_t *end, *p;
+       CHAR16 *str;
+
+       while (grp) {
+               p = grp->options;
+               end = grp->options+grp->nentries;
+               while (p != end) {
+                       str = NULL;
+                       switch(p->type) {
+                               case OPT_BOOL:
+                                       str = L"%s";
+                                       break;
+                               case OPT_STR :
+                                       str = L"%s=string";
+                                       break;
+                               case OPT_FILE :
+                                       str = L"%s=filename";
+                                       break;
+                               case OPT_CMD :
+                                       str = L"%s=kernel_options";
+                                       break;
+                               case OPT_NUM :
+                                       str = L"%s=number";
+                                       break;
+                               default: 
+                                       break;
+                       }
+                       if (str && first == FALSE) Print(L", ");
+                       if (str) Print(str, p->name);
+                       first = FALSE;
+                       p++;
+               }
+               grp = grp->next;
+       }
+}
+
+
+VOID
+print_config_options(VOID)
+{
+       Print(L"Global options supported:\n");
+
+       print_options(global_option_list, TRUE);
+       Print(L"\n\n");
+
+       Print(L"Image options supported:\n");
+       print_options(image_option_list, TRUE);
+       Print(L"\n");
+}
+
+
+/*
+ * returns a pointer to filename used for message N (which). NULL if it does
+ * not exist.
+ */
+CHAR16 *
+get_message_filename(INTN which)
+{
+       if (which < 0 || which >= MAX_MESSAGES) return NULL;
+       return global_config.message_file[which];
+}
+
+INTN
+register_config_options(config_option_t *opt, UINTN n, config_option_group_scope_t group)
+{
+       config_option_group_t *newgrp, **grp;
+
+       if (opt == NULL || n == 0 || (group != OPTIONS_GROUP_GLOBAL && group != OPTIONS_GROUP_IMAGE)) return -1;
+
+       VERB_PRT(3, Print(L"registering %d options for group %s\n", n, group == OPTIONS_GROUP_GLOBAL ? L"global" : L"image"));
+
+       newgrp = alloc(sizeof(config_option_group_t), EfiLoaderData);
+       if (newgrp == NULL) return -1;
+
+       grp = group == OPTIONS_GROUP_GLOBAL ? &global_option_list : &image_option_list;
+
+       if (*grp) while ((*grp)->next) grp = &(*grp)->next;
+
+       newgrp->options  = opt;
+       newgrp->next     = NULL;
+       newgrp->nentries = n;
+
+       if (*grp) { 
+               (*grp)->next = newgrp;
+       } else {
+               *grp = newgrp;  
+       }
+       return 0;
+}
+
+/*
+ * initialize config options: register global and per image options
+ */
+INTN
+config_init(VOID)
+{
+       INTN ret;
+
+       ret = register_config_options(global_common_options, 
+                                     sizeof(global_common_options)/sizeof(config_option_t),
+                                     OPTIONS_GROUP_GLOBAL);
+       if (ret == -1) return -1;
+
+       ret = register_config_options(image_common_options, 
+                                     sizeof(image_common_options)/sizeof(config_option_t),
+                                     OPTIONS_GROUP_IMAGE);
+
+       current_options = global_option_list;
+
+       return ret;
+}
+
diff --git a/config.h b/config.h
new file mode 100644 (file)
index 0000000..7f4a8c9
--- /dev/null
+++ b/config.h
@@ -0,0 +1,56 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __ELILO_CONFIG_H__
+#define __ELILO_CONFIG_H__
+
+#define opt_offsetof(opt)      (&((boot_image_t *)(0x0))->opt)
+
+typedef enum { OPT_NOTUSED, OPT_STR, OPT_CMD, OPT_BOOL, OPT_NUM, OPT_FILE } config_option_type_t;
+
+typedef enum { OPT_GLOBAL,     /* global and shared among architectures */
+              OPT_IMAGE,       /* per image only and shared among architectures */
+              OPT_IMAGE_SYS    /* per image and architecture specific (offset base in sys_img_opts) */
+} config_option_scope_t;
+
+typedef enum { OPTIONS_GROUP_GLOBAL,   /* group must be added to global set of options */
+              OPTIONS_GROUP_IMAGE,     /* group must be added to per-image set of options */
+} config_option_group_scope_t;
+
+struct _config_option_t;
+typedef INTN option_action_t(struct _config_option_t *, VOID *);
+
+typedef struct _config_option_t {
+    config_option_type_t       type;   /* option type: OPT_CMD, OPT_BOOL, ... */
+    config_option_scope_t      scope;  /* option scope: global, per image, per image_sys */
+    CHAR16                     *name;  /* unicode string for the option */
+    option_action_t            *action; /* option specific action */
+    INTN                       (*check)(void *);       /* check valid arguments, NULL=don't need */
+    VOID                       *data;  /* which data structure to record the option */
+} config_option_t;
+
+extern INTN register_config_options(config_option_t *opt, UINTN nentries, config_option_group_scope_t);
+
+#endif /* __ELILO_CONFIG_H__ */
diff --git a/devschemes/Makefile b/devschemes/Makefile
new file mode 100644 (file)
index 0000000..6ac05af
--- /dev/null
@@ -0,0 +1,46 @@
+#
+#  Copyright (C) 2001-2003 Hewlett-Packard Co.
+#      Contributed by Stephane Eranian <eranian@hpl.hp.com>
+#
+# This file is part of the ELILO, the EFI Linux boot loader.
+#
+#  ELILO is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2, or (at your option)
+#  any later version.
+#
+#  ELILO is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with ELILO; see the file COPYING.  If not, write to the Free
+#  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+#  02111-1307, USA.
+#
+# Please check out the elilo.txt for complete documentation on how
+# to use this program.
+#
+
+include ../Make.defaults
+include ../Make.rules
+
+TOPDIR=$(CDIR)/..
+
+FILES=simple.o
+
+
+TARGET=devschemes.o
+
+all: $(TARGET)
+
+#
+# XXX: does not trigger recompile when changing filesystem selection
+#      without doing make clean.
+#
+$(TARGET): $(FILES)
+       $(LD) -r -o $@ $(FILES)
+
+clean:
+       $(RM) -f $(TARGET) $(FILES)
diff --git a/devschemes/simple.c b/devschemes/simple.c
new file mode 100644 (file)
index 0000000..22b0d06
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Look at the README.devschemes for more explanations on how
+ * to use devschemes.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "fileops.h"
+
+#define NAMING_SCHEME  L"simple"
+
+typedef struct {
+       INT8 type;
+       INT8 subtype;
+       INTN (*device_func)(device_t *dev, EFI_DEVICE_PATH *dp);
+} devices_types_t;
+
+
+static UINT32 atapi_count, scsi_count, net_count;
+
+static INTN
+atapi_device(device_t *dev, EFI_DEVICE_PATH *dp)
+{
+       //ATAPI_DEVICE_PATH *atapi = (ATAPI_DEVICE_PATH *)dp;
+
+       dev->name[0] = L'a';
+       dev->name[1] = L't';
+       dev->name[2] = L'a';
+       dev->name[3] = L'p';
+       dev->name[4] = L'i';
+
+       SPrint(dev->name+5,FILEOPS_DEVNAME_MAXLEN-5-1,  L"%d", atapi_count);
+       atapi_count++;
+
+       return 0;
+}
+
+static INTN
+scsi_device(device_t *dev, EFI_DEVICE_PATH *dp)
+{
+       //SCSI_DEVICE_PATH *scsi = (SCSI_DEVICE_PATH *)dp;
+
+       dev->name[0]   = L's';
+       dev->name[1]   = L'c';
+       dev->name[2]   = L's';
+       dev->name[3]   = L'i';
+
+       SPrint(dev->name+4, FILEOPS_DEVNAME_MAXLEN-4-1, L"%d", scsi_count);
+       scsi_count++;
+
+       return 0;
+}
+
+static INTN
+network_device(device_t *dev, EFI_DEVICE_PATH *dp)
+{
+       //MAC_ADDR_DEVICE_PATH *mac = (MAC_ADDR_DEVICE_PATH *)dp;
+
+       dev->name[0] = L'n';
+       dev->name[1] = L'e';
+       dev->name[2] = L't';
+
+       SPrint(dev->name+3, FILEOPS_DEVNAME_MAXLEN-3-1, L"%d", net_count);
+       net_count++;
+
+       return 0;
+}
+
+/*
+ * what we are looking for in the device path
+ */
+static devices_types_t dev_types[]={
+       { MESSAGING_DEVICE_PATH, MSG_ATAPI_DP, atapi_device},
+       { MESSAGING_DEVICE_PATH, MSG_SCSI_DP, scsi_device},
+       { MESSAGING_DEVICE_PATH, MSG_MAC_ADDR_DP, network_device},
+       { 0, 0 , NULL}
+};
+
+static INTN
+simple_scheme(device_t *tab, UINTN n)
+{
+       EFI_DEVICE_PATH *dp1, *dp;
+       devices_types_t *p;
+       UINTN i;
+
+       /*
+        * note that this test is necessary but not sufficient to guarantee that this scheme
+        * will work because, we have no way of detecting that the machine got actually
+        * rebooted if the EDD30 variable was forced. this comes from the fact, that elilo
+        * can be invoked once, aborted and then restarted with no machine reboot.
+        *
+        * XXX: there may be a way to detect this with the another variable which would
+        * be in volatile memory only
+        */
+       if (elilo_opt.edd30_on == 0) {
+               VERB_PRT(4, Print(L"%s device naming scheme only works with EDD3.0 enabled\n", NAMING_SCHEME));
+               return -1;
+       }
+
+       for(i=0; i < n; i++) {
+               dp = DevicePathFromHandle(tab[i].dev);
+               if (dp == NULL) {
+                       ERR_PRT((L"cannot get device path for device %d", i));
+                       continue;
+               }
+               dp1 = dp = UnpackDevicePath(dp);
+
+               while (!IsDevicePathEnd(dp)) {
+                       p = dev_types;
+                       while (p->type) {
+                               if (   p->type == DevicePathType(dp) 
+                                   && p->subtype == DevicePathSubType(dp)) {
+                                       (*p->device_func)(tab+i, dp);
+                                       goto done;
+                               }
+
+                               p++;
+                       }
+                       dp = NextDevicePathNode(dp);
+               }
+done:
+                       FreePool(dp1); 
+       }
+       return 0;
+}
+
+devname_scheme_t simple_devname_scheme={
+       NAMING_SCHEME,
+       simple_scheme
+};
diff --git a/docs/devschemes.txt b/docs/devschemes.txt
new file mode 100644 (file)
index 0000000..a7dc06e
--- /dev/null
@@ -0,0 +1,106 @@
+
+Some explanations of what the devschemes are for:
+-------------------------------------------------
+Copyright (c) 2001-2003 Hewlett-Packard Co
+Contributed by Stephane Eranian <eranian@hpl.hp.com>
+
+
+       Whether or not EDD3.0 is enabled, EFI uses a device naming scheme which is
+       somewhat detailed. It tries to follow the hardware path from the System bus
+       down to the actual partition on the media. The following example shows
+       a typical block device path from a SCSI disk:
+
+       blk2 : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun0,Lun0)/HD(Part1,Sig00000000).
+
+       Elilo allows the user to load files from any device. This means that it
+       must provide a way for the user to specify a file path which include
+       the device name. While it is theoritically possible to specify the 
+       path mentioned above, it is far from practical because the names
+       are too long. There is too much details which the user (especially of
+       a boot loader) does not care about.
+
+       So Elilo, just like the EFI shell, must have a way of assigning logical
+       names to device names (paths). The EFI shell uses the fsX notation wherever
+       it finds a block devices for which it has detected a valid EFI filesystem
+       (i.e. a Fat32+ filesystem). This assignment is done on the fly, and depending
+       on the status of the removable media (like CDROM or floppy) the mapping can
+       change.
+
+       Those fsX: names are a pure abstraction of the EFI shell and have nothing to 
+       do with the EFI specification. 
+
+       Now Elilo could try to emulate then, i.e. try to reproduce the way the EFI shell
+       assigns names. However the problem is that, AT THIS POINT, Elilo recognized more
+       device from which it can load files from than the Shell does. This comes from the
+       fact that it can load files from the network or from an Ext2fs, for instance. 
+       We plan on fixing those issues in the next releases of Elilo. Anyway, the problem
+       would still be to make sure that if we follow the same naming scheme, they match
+       at all times, i.e. fs0: maps to the same device in both the EFI shell and Elilo
+       which may be tricky as both EFI and Elilo continue to evolve. Also the Shell
+       naming schemes as some problems which removable devices which would not make it
+       easy from the bootloader.
+
+       Another possible solution would be to use the Linux kernel naming scheme, i.e., 
+       hda, hda1, fd0, sda... Of course, we would drop the /dev prefix in this case. 
+       While it would make it very convenient for users and easy to configure from 
+       a running system, it is even more difficult that the EFI Shell scheme. Again, 
+       to succeed, the loader scheme would have to match EXACTLY what the Linux kernel
+       does for all possible devices. This is a very complicated task as his any
+       naming problem. The recent discussions about the devfs support in the kernel is 
+       just yet another proof of the complexity.  Another issue here is that this would
+       create a dependency between the loader and the kernel because we would need the
+       way the naming is done in the kernel. Again, this is very complicated, just thinnking
+       about how the PCI buses are scanned looking from devices. 
+
+       So it looks like there is single solutions. Clearly, that is not easy and there are
+       multiple schemes possible. For now, we felt that it was better for Elilo to use its
+       own naming scheme independent from the EFI shell and the Linux kernel. While this introduces
+       yet another scheme, we believe its advantages outweight the software complexity associated
+       with the two schemes described above. 
+       
+       However, we recognize the need for flexibilty and that is exactly why, this version
+       of Elilo provide an internal interface which can used to develop custom naming schemes. 
+
+       The way the filepaths are translated by Elilo is very basic and based on a simple
+       string matching algorithm. A full pathname is specified as follows:
+
+               dev_name:/path/to/my/file.
+
+       The 'dev_name' is created by Elilo and can be anything relevant to the user. There is
+       an internal binding from the name to the actual EFI device handle and more precisely
+       the filsystem interface associated to it (the device handle is never exposed to the
+       boot loader).
+       
+       By default, Elilo uses an extremely simple scheme which is similar to the EFI shell.
+       if simply builds the device names as follows:
+
+               devXXX.
+
+       The XXX is just the number of the device. It is incremented by one each time recognized
+       filesystem is detected on that device. The clear advantage is simplicity. The downside
+       is that is creates a 'flat' namespace where every new device detected (like a floppy
+       is inserted) will show up anywhere in the set of devices and may push some fixed 
+       devices up or down. So it hard to rely on devXXX to always mean the same things. 
+
+       Now custom naming schemes can be added on top of this, which would partially or totally
+       replace this default scheme. Elilo is shipped with one such scheme called 'simple'.
+       It provides an example of how one can develop a new scheme. The main characteristic
+       of 'simple' is that it tries to group devices by controller (Messaging Device in 
+       EFI terminology). Hence, all the ATAPI devices show up as atapiXXX and the SCSI
+       device show up as SCSIXXX. This implicitely shields the SCSI (fixed most likely) devices
+       from the removable media coming from ATAPI (like floppy or CDROM). So it is slightly
+       better but far from perfect.
+
+       Here is an example of what it looks like on an actual system:
+
+           scsi0 :   vfat : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun0,Lun0)/HD(Part1,Sig00000000)
+          scsi1 :   vfat : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun6,Lun0)/HD(Part1,Sig00000000)
+         atapi0 :   vfat : Acpi(PNP0A03,0)/Pci(3|1)/Ata(Secondary,Master)/CDROM(Entry1)
+          scsi2 : ext2fs : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun0,Lun0)/HD(Part2,Sig00000000)
+          scsi3 : ext2fs : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun6,Lun0)/HD(Part2,Sig00000000)
+           net0 :  netfs : Acpi(PNP0A03,0)/Pci(5|0)/Mac(00D0B7A6FC25)
+
+
+       The 'simple' scheme is not fully satifactory but developers are strongly encouraged 
+       to enhance it or better create new schemes.
+
diff --git a/docs/edd30.txt b/docs/edd30.txt
new file mode 100644 (file)
index 0000000..9af06f0
--- /dev/null
@@ -0,0 +1,102 @@
+Information related to EDD3.0 and ELILO
+---------------------------------------
+Last updated: 02/02/14
+
+As of elilo-3.0, the filesystem/device naming scheme takes advantage 
+of the EDD3.0 support present in the underlying EFI/BIOS firmware layers.
+
+The EDD3.0 support is supposedly turned on by default in all version of the
+EFI firmware on IA-64 (and possibly IA-32). 
+
+Support can be enabled or disabled dynamically using an EFI environment 
+variable called "EDD30". The GUID of the variable is as follows:
+
+#define EDD30_GUID \
+{0x964e5b21, 0x6459, 0x11d2, { 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}}
+
+This is a boolean variable. When true, support is enabled. 
+You can check if support is enabled by typing:
+
+       Shell> edd30
+       Usage edd30 [On | Off]
+       Used to support OS Loader debug
+       EDD30 DevicePath Mode is On
+
+at this EFI shell prompt. You can enable support by forcing the variable
+to TRUE. This can be accomplished by typing:
+
+       Shell> edd30 on
+       EDD30 DevicePath Mode is On
+
+Alternatively an EFI application can use RT->SetVariable() to set the
+value of the EDD30 variable.
+
+If the variable was set to false, then to take advantage of EDD3.0 
+support after switching the variablle to TRUE, the system MUST be 
+rebooted as EDD affects the core of EFI naming scheme.
+
+Elilo will check if the variable is defined and valid. If it is set
+to true, then the device name schemes that rely on it will work properly.
+That's the case for the 'simple' scheme. If the variable is not set to true
+or does not exist, elilo will use a generic builtin scheme with names
+such as dev001, dev002 and so on. The "EDD30" variable is like a system 
+variable therefore it is expected not to be overloaded by users for others 
+purposes.  Elilo is fully functional even when EDD30 is off.
+
+By default, if EDD30 is off, elilo will try and set the variable to TRUE.
+However, some controllers do not support EDD30 and forcing the variable
+may cause problems. Therefore as of elilo-3.2, there is an option to 
+avoid forcing the variable. In the config file, you can use 'noedd30' option
+and on the command line, you can use the -E option.
+
+When the variable is forced back to TRUE, th effect will only be seen after
+a reboot. Shall you decide not to reboot immediately, elilo
+will system still operate using the generic naming scheme.
+
+When EDD3.0 is enabled the output of the EFI shell 'map' command looks similar
+to this:
+
+Device mapping table
+  fs0  : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun0,Lun0)/HD(Part1,Sig00000000)
+  fs1  : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun6,Lun0)/HD(Part1,Sig00000000)
+  fs2  : Acpi(PNP0A03,0)/Pci(3|1)/Ata(Secondary,Master)/CDROM(Entry1)
+  blk0 : Acpi(PNP0A03,0)/Pci(3|1)/Ata(Primary,Master)
+  blk1 : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun0,Lun0)
+  blk2 : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun0,Lun0)/HD(Part1,Sig00000000)
+  blk3 : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun0,Lun0)/HD(Part2,Sig00000000)
+  blk4 : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun0,Lun0)/HD(Part3,Sig00000000)
+  blk5 : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun6,Lun0)
+  blk6 : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun6,Lun0)/HD(Part1,Sig00000000)
+  blk7 : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun6,Lun0)/HD(Part2,Sig00000000)
+  blk8 : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun6,Lun0)/HD(Part3,Sig00000000)
+  blk9 : Acpi(PNP0A03,2)/Pci(0|0)/Scsi(Pun6,Lun0)/HD(Part3,Sig00000000)/HD(Part1,Sig00000200)
+  blkA : Acpi(PNP0A03,0)/Pci(3|1)/Ata(Secondary,Master)
+  blkB : Acpi(PNP0A03,0)/Pci(3|1)/Ata(Secondary,Master)/CDROM(Entry1)
+Shell> 
+
+The same system with EDD3.0 disabled will look like this:
+Device mapping table
+  fs0  : VenHw(Unknown Device:80)/HD(Part1,Sig00000000)
+  fs1  : VenHw(Unknown Device:81)/HD(Part1,Sig00000000)
+  fs2  : VenHw(Unknown Device:FF)/CDROM(Entry1)
+  blk0 : VenHw(Unknown Device:00)
+  blk1 : VenHw(Unknown Device:80)
+  blk2 : VenHw(Unknown Device:80)/HD(Part1,Sig00000000)
+  blk3 : VenHw(Unknown Device:80)/HD(Part2,Sig00000000)
+  blk4 : VenHw(Unknown Device:80)/HD(Part3,Sig00000000)
+  blk5 : VenHw(Unknown Device:81)
+  blk6 : VenHw(Unknown Device:81)/HD(Part1,Sig00000000)
+  blk7 : VenHw(Unknown Device:81)/HD(Part2,Sig00000000)
+  blk8 : VenHw(Unknown Device:81)/HD(Part3,Sig00000000)
+  blk9 : VenHw(Unknown Device:81)/HD(Part3,Sig00000000)/HD(Part1,Sig00000200)
+  blkA : VenHw(Unknown Device:FF)
+  blkB : VenHw(Unknown Device:FF)/CDROM(Entry1)
+Shell> 
+
+
+EDD3.0 is an industry standard and the working draft can be downloaded from the
+following web site:
+       http://www.t13.org/
+
+The document reference is D1386 (Enhanced Disk Drive Services 3.0).
+
diff --git a/docs/elilo.txt b/docs/elilo.txt
new file mode 100644 (file)
index 0000000..fdc1131
--- /dev/null
@@ -0,0 +1,478 @@
+       --------------------------------------------------------------------
+       ELILO.EFI: Linux boot loader for EFI/IA-64 and EFI/IA-32 based systems
+       --------------------------------------------------------------------
+                      Stephane Eranian <eranian@hpl.hp.com>
+
+                                August 2003
+
+                  Copyright (C) 2000-2003 Hewlett-Packard Co.
+
+
+I/ Introduction
+   ------------
+
+This document describes how to use ELILO on for both IA-64 and IA-32 EFI-based platforms.
+This document describes ELILO version 3.4. 
+
+II/ Command line options
+    --------------------
+
+       elilo [-hDpPVvaE] [-d nsec] [-C config] [-i initrd] [-c chooser] [kernel [kernel options...]]
+
+       -h      Display a list of all possible command line options.
+
+       -V      Print the version number and exit.
+
+       -d nsec Specify the number of 10th of seconds before loading the
+               kernel.
+
+       -C file Specify the config file to use. The default is elilo.conf in the directory
+               that elilo.efi was loaded from.
+
+       -P      Verify config file syntax only. this option causes ELILO to 
+               parse the config file and generate a report on the console.
+               No kernel is loaded.
+
+       -v      Turn on verbose mode. ELILO prints more message about what it 
+               is doing. For each occurrence of this option the verbosity level
+               is increased by one. The maximum level is 5.
+
+       -a      Always check for alternate kernel image. The default behavior
+               of ELILO is to NOT look for an alternate image. This 
+               option overrides this behavior and ELILO is checking for 
+               alternate images no matter what. Alternate images are
+               specified using the EliloAlt EFI variable.
+
+       -p      force interactive prompt mode. Valid when no kernel image is 
+               specified on the command line.
+
+       -D      print debug output.
+
+       -E      don't force EDD30 variable to TRUE when FALSE.
+
+       -i file Use file as the initial ramdisk (initrd).
+
+       -c name Specify which kernel chooser to use.  Default is 'simple', and
+               the only other choice at present is 'textmenu'.
+
+       In addition, elilo supports platform specific options:
+
+       For IA-64:
+       ----------
+               -r      the kernel image can be relocated if initial load address is not
+                       available. This options requires a special version of the kernel.
+
+               -F file will try to load the FPSWA driver indicated by 'file'. Only this file
+                       will be attempted. When no specific file is given, elilo will try
+                       loading \efi\intel firmware\fpswa.efi from all accessible EFI system
+                       partitions.
+       For IA-32:
+       ----------
+               no option defined.
+
+       All file names (including the kernel file) can include a device name using the 
+       following syntax:
+
+               dev_name:/path/to/my/kernel
+
+       The 'dev_name' component depends on the naming scheme selected and the detected
+       devices for your system.  Some choosers may print the information automatically
+       or on demand, see chooser specific documentation for more on this. See README.devschemes 
+       for more information on device naming schemes. The slash character '/' can be used as 
+       a directory separator on any file systems including the EFI file system (FAT32).
+
+
+III/ Configuration File
+     ------------------
+
+     ELILO supports a config file with options similar to the LILO/x86 boot loader.
+
+     Elilo will use the following sequence (shown in order) when looking for its config 
+     file when none is specified on the command line:
+
+       1/ AABBCCDD.conf (netbooting with regular DHCP)
+          where AABBCCDD is the hexadecimal representation
+          of the IP address assigned during the DHCP phase.
+
+       2/ elilo-ia64.conf or elilo-ia32.conf
+          The choice depends on the client platform. This step allows
+          the same DHCP/PXE server to provide files for both types of clients.
+
+       3/ elilo.conf
+
+     Unless explicitly specified on the command line, elilo looks for its config file
+     in the filesystem and directory it was loaded from. For instance, if elilo.efi
+     is invoked as:
+
+     fs0:\> \efi\debian\elilo.efi
+
+     Then elilo will look for its configuration file in fs0:\efi\debian and not
+     in the root directory of fs0:. The prefix fs0:\efi\debian will be used for
+     all other files that elilo needs to download when their paths are specified 
+     as being relative.
+
+     IMPORTANT:
+     This rule also applies when a specific config file is passed via the -C 
+     option. For example:
+
+     fs0:\> \efi\debian\elilo.efi -C elilo.conf
+
+     This will look for elilo.conf in fs0:\efi\debian and not in fs0:\.
+     To get to the elilo.conf in fs0:\, you need to specify the absolute
+     path:
+
+     fs0:\> \efi\debian\elilo.efi -C \elilo.conf
+
+
+     The configuration file is an ASCII file and not a UNICODE file.
+
+     The config file contains additional options to change the behavior of the loader. 
+     If the same option is specified in the config file AND on the command line, the
+     latter takes precedence. Not all options available in the config file have an 
+     equivalent on command line.
+
+     When elilo is invoked with the -h option, it prints the list of support command line
+     options but also the list of config file options. For each option it also prints
+     the type of data expected.
+
+     The config file options are divided in 2 groups:
+
+
+       - image options which are specific to a particular kernel image. Each kernel image
+         must be identified with a logical name called a label. 
+
+       - global options which affect the behavior of ELILO and apply to all images.
+
+     The ELILO config file follows the LILO/x86 syntax. First come the global
+     options, then the list of images and options for each of them, if
+     necessary. At least one image MUST be defined and it is possible to have
+     an empty list of global options.
+
+     Options have types. Three types are defined:
+       - boolean: set or not set
+       - string : a string of characters which can be quoted if necessary
+       - number (in decimal) 
+       - filename: a string interpreted as a file name
+
+    
+    The config file supports the following options:
+
+    Global Options:
+    ---------------
+    default=value      Name the default image to boot. If not defined ELILO
+                       will boot the first defined image.
+
+    timeout=number     The number of 10th of seconds to wait while in
+                       interactive mode before auto booting default kernel.
+                       Default is infinity.
+
+    delay=number       The number of 10th of seconds to wait before
+                       auto booting when not in interactive mode. 
+                       Default is 0.
+   
+    prompt             Force interactive mode
+
+    verbose=number     Set level of verbosity [0-5]. Default 0 (no verbose)
+
+    root=filename      Set global root filesystem for Linux/ia64
+
+    read-only          Force root filesystem to be mounted read-only
+
+    append=string      Append a string of options to kernel command line
+
+    initrd=filename    Name of initrd file
+
+    image=filename     Define a new image
+
+    chooser=name       Specify kernel chooser to use: 'simple' or 'textmenu'.
+
+    message=filename   a message that is printed on the main screen if supported by 
+                       the chooser.
+
+    fX=filename                Some choosers may take advantage of this option to
+                       display the content of a file when a certain function
+                       key X is pressed. X can vary from 1-12 to cover 
+                       function keys F1 to F12.
+
+    noedd30            do not force the EDD30 EFI variable to TRUE when FALSE. In other
+                       words, don't force the EDD30 mode if not set.
+
+    Image options:
+    --------------
+    root=filename      Set root filesystem for kernel
+
+    read-only          Force root filesystem to be mounted read-only
+
+    append=string      Append a string of options to kernel command line
+
+    initrd=filename    Name of initrd file
+
+    label=string       Logical name of image (used in interactive mode)
+
+    description=string One line text description of the image.
+
+    IA-64 specific options:
+    -----------------------
+
+    Global options:
+    ---------------
+    fpswa=file          Specify the filename for a specific FPSWA to load. 
+                       If this option is used then no other file will be tried.
+
+    relocatable                In case of memory allocation error at initial load point of
+                       kernel, allow attempt to relocate (assume kernels is relocatable)
+
+    Image options:
+    --------------
+    relocatable                In case of memory allocation error at initial load point of
+                       kernel, allow attempt to relocate (assume this kernel is relocatable)
+
+    IA-32 specific options:
+    -----------------------
+    legacy-free                Indicate that the host machine does not have a legacy BIOS at all.
+
+
+    The user can specify a kernel and related kernel options using the image label. Alternatively,
+    the user can also specify a kernel file that is not specified in the config file. In any case,
+    some of the global options (such as append) are always concatenated to whatever the user type.
+
+
+IV/ Booting from the local system
+    -----------------------------
+
+    The elilo.efi binary must be in an EFI system partition (FAT32). The config
+    file, kernel image, and optional initrd ramdisk can be on the same partition
+    or even another EFI partition. In the following discussion we assume that all 
+    files are on the same EFI partition which is recognized by the EFI shell (nshell) 
+    as fs0. The kernel and initrd can be copied from the any linux filesystems to the 
+    EFI partition using either the mtools (mcopy) or by mounting the EFI partition as 
+    a vfat partition. However you do not really need this because most linux 
+    distributions install both files in the EFI partition and mount this partition in /boot/efi.
+
+    To boot a kernel, simply power cycle the machine. Once you get to the EFI
+    shell prompt, change to the filesystem that maps to the partition where elilo is.
+
+       Shell> fs0:
+       fs0:\>
+
+       You might need to make sure that the Shell Path is set such that it will load
+       ELILO from fs0:. You can verify this by typing:
+       fs0:\> set
+       path : fs0:\
+       
+       At this point you can invoke ELILO:
+
+       fs0:\> elilo
+
+       If there is no config file, then it will: 
+               - pick up the kernel image named vmlinux if it exists, otherwise it will abort.
+               - pass no argument to the kernel.
+       
+       You can specify the kernel image and its options on the command line.
+       For instance you can do:
+
+       fs0:\> elilo vmlinux root=/dev/sda5
+
+       You can specify as many parameters as you want. The syntax follows the kernel
+       rule, i.e., list of value pairs (or even single values) separated by space.
+       A more complicated example would be:
+
+       fs0:\> elilo -i initrd-2.4.9 vmlinuz-2.4.9 root=/dev/sda2 console=tty0 console="ttyS0,115200n8"
+
+       In this example, notice the double quotes. They are required because the comma is a control
+       character for nshell.
+
+    In the case a config file is found, then elilo will behave according to
+    the options in that file. However if elilo is invoked with command line options, they
+    will be combined or they will override (if conflicting) what is defined in the config file.
+
+    As of version 3.3, elilo is fully compliant with the EFI specification (1.10) with regards
+    to where the bootloader (elilo.efi) must be located in the EFI system partition. In 
+    section 11.2.1.3 of the EFI1.10 specification, it is said that in order to avoid conflicts
+    between various loaders for various OSes or distributions of the same OS, every vendor MUST
+    use a dedicated directory: \EFI\vendor\. The bootloader must be placed in this directory.
+    This has always been possible as this is a matter of creating the directory and copying
+    the elilo.efi file in it. However up until version 3.3, elilo would look for its config file
+    and kernel/initrd in the root (/) of the partition it was loaded from. As of version 3.3,
+    elilo will now ONLY look for its configuration file FROM THE DIRECTORY IT WAS LOADED FROM. 
+    The same applies to the kernel and initrd files unless absolute paths are specified. Let us 
+    look at a simple example:
+
+       - suppose elilo.efi is in \EFI\DIST if fs0:  (for the EFI Shell)
+
+       - if you invoke elilo as follows:
+
+               fs0:\> \efi\dist\elilo -v -p
+               default file path: \efi\dist\
+               config file      : \efi\dist\elilo.conf
+               ELILO boot: 
+
+
+         Note that this is the same if you invoke elilo directly from \efi or \efi\dist.
+    File references in the configuration file are treated as relative to the directory
+    elilo was loaded from except if they use an absolute path. 
+
+   As of version 3.4 a similar rule applies to the network boot sequence, see netbooting.txt
+   for details.
+
+V/ Interactive mode
+   ----------------
+
+   Elilo can be forced into interactive mode using the "prompt" option in the config
+   file or with the -p option. In this mode, the user can select a kernel to load.
+
+   The interface depends on the chooser, it may be a simple command line prompt as provided
+   by the simple chooser or a more sophisticated screen with scroll menus as provided by
+   textmenu. Most choosers depends on the elilo config file to get the information they
+   display. The simple chooser can operated without the elilo config file. However it
+   is always better to have this file, to create handy logical names for each possible
+   boot choices. The logical names are specified with the "label" option in the config
+   file. They represent a specific kernel "image" and its specific options.
+
+   In elilo, the user can select a particular kernel image using the corresponding label
+   name. A simple example is as follows:
+
+      If we suppose that the following is defined in elilo.conf:
+
+       image=vmlinuz-2.4.9
+       label=linux-up
+       initrd=initrd-2.4.9
+       root=/dev/sda2
+       append="console=tty0 console=ttyS0,115200n8"
+
+      then the user can specify linux-up at the prompt and elilo will load the 
+      vmlinuz-2.4.9 kernel file and the initrd-2.4.9 ramdisk and will pass 
+
+         "root=/dev/sda2 console=tty0 console=ttyS0,115200n8"
+
+      as command line arguments to the kernel.
+
+      This behavior is identical to Lilo/x86. However, elilo further allows the user
+      to specify actual kernel files names as well, i.e., kernels that are not defined
+      in the configuration file. If we reuse the above example and the simple chooser, 
+      the user can type:
+
+      ELILO boot: vmlinux-2.4.18 root=/dev/sda2
+
+      and elilo will boot the vmlinuz-2.4.18 kernel if it exists.
+
+VI/ The alternate kernel image
+    --------------------------
+
+    Oftentimes when debugging kernels you want to reboot the machine once with
+    your test kernel and, if something goes wrong, you want to fall back to a more
+    stable kernel. In addition you want to be able to do this from a remote machine. 
+    Many things can go wrong when doing kernel debugging. It could be that you don't
+    even reach user-mode. In this case however, you still want to fall back to
+    a stable kernel. The feature you'd like from a boot loader is 'boot once and
+    then fall back to safe kernel'.
+
+    Elilo offers this feature and it's called 'alternate kernel image'.  
+    You can configure elilo to load a kernel only once and then whatever 
+    happens the next reboot falls back to a different kernel hopefully more stable.
+
+    To do this, elilo relies on an EFI variable called 'EliloAlt' with a NULL GUID.
+    The content of this variable is a UNICODE string containing the kernel file name
+    and its command line options.
+
+    When the -a option is specified on the command line or if the "checkalt" option
+    is present in the config file, elilo will check for the presence of this variable.
+    If found and the content is a valid UNICODE string, elilo will use it as the kernel
+    to boot. There is no verification made on the validity of the kernel name or options.
+    Then the variable is deleted. If the variable is rejected because it does not look
+    sane, it is also deleted.
+
+    The variable can be set from a running Linux system using the /proc/efi/vars
+    interface. In the tools directory of this package, there is a Linux tool called
+    elilovar which can be used to create, modify, print, and delete the EliloAlt
+    variable. Refer to eliloalt.txt for more information on this tool.
+
+VII/ Auto booting the machine
+     -----------------------
+
+    Once you're satisfied with your machine setup, it is good to install an 
+    auto boot procedure.  You have two options to do this:
+       - from the EFI boot manager menu
+       - from the EFI shell
+
+    The first option is preferred and is used by most Linux distributions.
+    Elilo can be invoked directly from the boot manager. You need to get into
+    the 'boot maintenance' menu and use load file a file. This can be tedious
+    so instead it is recommended that you use a Linux tool called efibootmgr
+    which is also shipped in most distributions. With this tool, you can
+    create your own boot option and change the boot order.
+
+    
+    
+    The second approach use the EFI shell and a shell script with a special name: 'startup.nsh'.
+
+    When the system boots, it looks for EFI partitions and if it finds
+    a 'startup.nsh' file in ANY of these it will jumpstart execution from it.
+
+    So the typical way of auto booting your Linux/ia64 system is to simply create
+    such a file with the following content:
+
+       # cat /boot/startup.nsh
+       elilo vmlinuz root=/dev/sda2
+
+    Of course, this can be simplified if there is a configuration file.
+
+
+VII/ Netbooting 
+     ----------
+
+     Please refer to netbooting.txt for a complete description of how to boot
+     from the network.
+
+
+XII/ Booting on EFI/ia32 platforms
+     -----------------------------
+
+       Until PC comes with the EFI firmware built in, you need to boot from a
+       floppy that has the EFI firmware on it. Such floppy can be
+       constructed from the EFI sample implementation and toolkit that is
+       available from the Intel Developer Web site at:
+
+               http://developer.intel.com/technology/efi/
+
+       To use elilo on IA-32, you can put it on a floppy and
+       on a FAT32 partition (msdos partition). You can also
+       netbooting if you network adapter has support for UNDI/PXE.
+
+       Elilo/ia32 is capable of booting unmodified 2.2.x. and 2.4.x kernels
+       as they are shipped by distributors today (such as Redhat7.2). You don't need 
+       to recompile the kernel with special options. Elilo ONLY takes compressed kernel
+       image which are typically obtained via a 'make bzImage'. Plain elf/32 kernel can't 
+       be booted (plain vmlinux will not work). Similarly, existing initial ramdisks can 
+       be used without modifications. 
+
+IX/ Credits
+    -------
+
+       Intel Corp.
+       Stephane Eranian <eranian@hpl.hp.com>
+       David Mosberger  <davidm@hpl.hp.com>
+       Johannes Erdfelt <jerdfelt@valinux.com>
+       Richard Hirst    <rhirst@linuxcare.com>
+       Chris Ahna       <christopher.j.ahna@intel.com>
+       Mike Johnston    <michael.johnston@intel.com>
+
+X/ Bug reports
+   -----------
+
+       You can submit bugs to <eranian@hpl.hp.com> or to the Linux/ia64
+       mailing list at linux-ia64@linuxia64.org. Visit http://www.linuxia64.org
+       to subscribe to this list.
+
+XIII/ Reference
+      ---------
+
+       EFI v1.02 specifications are available from the following web site:
+
+       http://developer.intel.com/technology/efi/
+
+       The latest sources of ELILO can be downloaded at:
+
+               ftp://ftp.hpl.hp.com/pub/linux-ia64
+
diff --git a/docs/eliloalt.txt b/docs/eliloalt.txt
new file mode 100644 (file)
index 0000000..e94adeb
--- /dev/null
@@ -0,0 +1,77 @@
+Information related to the eliloalt Linux tool
+---------------------------------------------
+(c) 2002-2003 Hewlett Packard Co
+    Contributed by Stephane Eranian <eranian@hpl.hp.com>
+
+    Last updated: March 1st, 2002
+
+
+The elilo alternate image feature uses an EFI variable called EliloAlt. 
+The content of this variable is a UNICODE string containing a kernel
+image file and its command line options. The variable has a NULL GUID.
+
+To create, read, or modify the variable you need to use the EFI variable
+service and the SetVariable() or GetVariable() interface. The service
+is available when EFI is in boot service mode, i.e., prior to loading
+the kernel but it is also available at runtime when the Linux kernel
+has taken over the control of the machine. 
+
+In order to modify the variable, you can either write a small EFI applications
+or use the Linux/ia64 interface to the EFI variables which use the /proc filesystem.
+
+The elilalt tool included in this package uses the /proc interface to EFI variables
+to create, read, or modify the EliloAlt variable. This tool is a Linux/ia64 application
+and NOT an EFI application.
+
+
+Because modiyfing the EliloAlt can influence how the machine is booted, you must
+be root to run the program, even when you simply want to read the content of
+the variable.
+
+Eliloalt provides the following options:
+
+       -h, --help              display this help and exit
+       --version               output version information and exit
+       -s, --set cmdline       set elilo alternate variable to cmdline
+       -p, --print             print elilo alternate variable
+       -d, --delete            print elilo alternate variable
+
+1/ Creation of the variable
+
+       To create the variable you can type:
+
+       # eliloalt -s "vmlinuz-2.4.9 root=/dev/sdb2 hdc=ide-scsi"
+
+       It is VERY important to use quotes to make sure that the entire list of arguments is 
+       treated as a single argument to the program. Otherwise only the first element 
+       (here vmlinuz-2.4.9) will be used.
+
+
+2/ Printing the content of the variable
+
+       To print the content of the variable you need to type:
+
+       # eliloalt -p
+       EliloAlt="vmlinuz-2.4.9 root=/dev/sdb2 hdc=ide-scsi"
+
+3/ Modifying the variable
+
+       You can simply use the -s option:
+       # eliloalt -s "vmlinuz-2.4.18 root=/dev/sdb2"
+       # eliloalt -p
+       EliloAlt="vmlinuz-2.4.18 root=/dev/sdb2"
+       
+3/ Deleting the variable
+
+       You must use the -d option:
+       # eliloalt -p
+       EliloAlt="vmlinuz-2.4.18 root=/dev/sdb2"
+       # eliloalt -d
+       # eliloalt -p
+       variable not defined
+       
+       Keep in mind that the variable is automatically deleted by elilo if:
+               - the checkalt option is specified in the config file 
+           OR
+               - the -a is used on the command line
+
diff --git a/docs/elilovars.txt b/docs/elilovars.txt
new file mode 100644 (file)
index 0000000..9350aa4
--- /dev/null
@@ -0,0 +1,53 @@
+Information related to variable support in ELILO
+------------------------------------------------
+(c) 2002-2003 Hewlett Packard Co
+    Contributed by Stephane Eranian <eranian@hpl.hp.com>
+
+    Last updated: 06/13/2002
+
+As of version 3.2, elilo has internal variables which can be programmed
+by any module inside the bootloader code. These variables are used
+to parametrize some of the arguments passed on the kernel command line.
+
+The value of a variable is a Unicode string. Variables names are composed
+of a single Unicode character, such as 'I' for instance. Variable names
+are case sensitive. Elilo has support for 52 variables: A-Z and a-z. 
+
+The '%' sign is used to name a variable. For instance, '%I' indicates 
+variable 'I'. If '%I'  is present on the command line to the kernel, 
+it will be replaced (string replacement) with the value (a string) of 'I'. 
+By default, the Elilo core does not assign any values to any variables. 
+It is up to each module to do so. When a variable is not used, its content 
+is the empty string "", which means that the '%d' notation, for instance, will 
+be replaced with the empty string.
+
+Let us look at an example:
+  Supposing that the following variables are defined:
+       'I'     -> "192.168.2.5"
+       'G'     -> "192.168.2.1"
+       'M'     -> "255.255.255.0"
+       'z'     -> ""
+
+  Then a command line of this form (provided as an append= option in elilo.conf):
+
+        root=/dev/nfs nfsroot=15.4.88.178:/mnt2 ip=%I:%z:%G:%N:jung:eth0:on
+
+  Would pass the following to the kernel:
+
+        root=/dev/nfs nfsroot=15.4.88.178:/mnt2 ip=192.168.2.5::192.168.2.1:255.255.255.0:jung:eth0:on
+
+Of course, the meaning of each variable is up to each individual module, the
+names used here are not necessarily representative of the actual names used
+by elilo.
+
+Some choosers, (such as simple) have support to print the list of
+defined variables. For instance, in simple (the default chooser) you
+can press '%' to see the list of defined variables.
+
+Variables can be useful when netbooting, for instance, to get the 
+dynamically assigned IP, netmask, and gateway addresses.
+
+In case the % character needs to be passed to the kernel, it is possible
+to "despecialize" a character using the & symbol in front of it.
+
+See netbooting.txt for more information on this.
diff --git a/docs/fpswa.txt b/docs/fpswa.txt
new file mode 100644 (file)
index 0000000..2945e4e
--- /dev/null
@@ -0,0 +1,22 @@
+Information related to the FPSWA driver for EFI/ia64 platforms
+--------------------------------------------------------------
+(c) 2002-2003 Hewlett Packard Co
+    Contributed by Stephane Eranian <eranian@hpl.hp.com>
+
+This document is irrelevant for EFI/ia32 platforms.
+
+On all EFI/ia64 platforms, the bootloader is responsible for checking for
+the presence on the EFI system partition of an updated version of the 
+Floating-Point Software Assist (FPSWA) EFI driver (fpswa.efi). For every 
+instance found, elilo will load the driver and let EFI decide if it is 
+newer than the currently installed version. In the end at most one driver 
+is kept in memory.
+
+Elilo will look for a file called fpswa.efi in the \EFI\Intel Firmware
+(there is a space between l and F) directory on ALL accessible EFI system 
+partitions (including on removable media). It will do so even when elilo
+is downloaded from the network. It does not look for the driver in the
+ext2fs partitions.
+
+The FPSWA reference document is available from the Intel developer's web
+site at http://developer.intel.com/design/itanium.
diff --git a/docs/netbooting.txt b/docs/netbooting.txt
new file mode 100644 (file)
index 0000000..2f51740
--- /dev/null
@@ -0,0 +1,396 @@
+How to netboot using ELILO
+--------------------------
+
+Copyright (C) 2002-2003 Hewlett-Packard Co.
+Contributed by Stephane Eranian <eranian@hpl.hp.com>
+
+Last updated: 03/08/11
+
+EFI has full support for the PXE and DHCP protocol. As such 
+it is relatively easy to boot a machine from the network using EFI.
+The elilo loader has full support for both PXE and DHCP, therefore
+it is possible to download the elilo config file, the Linux kernel image 
+and the initial ramdisk from a remote server. There are many ways
+netbooting can be configured but in this document we focus 
+only on two very common cases:
+
+               - netboot but use local root filesystem.
+               - booting a diskless machine, i.e., use a NFS root filesystem.
+
+1/ How to get EFI to netboot?
+
+   You do not need any additional software to get EFI to start a netboot session.
+   Any EFI machine can be configured to start a PXE/DHCP session IF it has a network
+   adapter that has support for the UNDI/PXE protocol. Most modern cards do have such
+   support. 
+
+   To enable netbooting, you need to go into the EFI boot manager maintenance menu
+   and 'Add a boot option'. On the screen you see the list of devices to boot from.
+   At least one of them should be of the form:
+
+       Load File [Acpi(PNP0A03,0)/Pci(5|0)/Mac(00D0B7A6FC25)]
+
+   which represent Ethernet card (Mac address). If you don't have such option, it means that
+   you either do not have a network adapter in your machine or it does not have the
+   UNDI/PXE support in its option ROM.
+
+   You need to select this option and give it a logical name such as 'netboot', for instance.
+   Next, you leave the maintenance menu and go back to the main menu. You now have a new
+   boot menu option. If you select 'netboot' then EFI will start the PXE/DCHP discovery
+   request and look for a server to get an IP address. 
+
+   On the server side, you can use a standard DHCP server, such as the one shipped on
+   Redhat7.2 (dhcpd) or a PXE server (not yet available for Linux, probably available for Windows).
+   In this document we show both options. You also need a TFTP server somewhere on the network, 
+   it will be used to download the actual files.
+
+
+2/ Netbooting using DHCP
+
+   There is nothing specific to EFI that needs to be set in the /etc/dhcpd.conf file. 
+   Clearly the filename option must contains the path to the elilo.efi binary.
+
+   Elilo will auto-detect whether it was downloaded via PXE or DHCP and it will adapt
+   the kind of requests it makes to download the other files that it needs, such as
+   its config file.
+
+   A simple dhcpd.conf file which uses fixed IP addresses could be as follows:
+
+   subnet 192.168.2.0 netmask 255.255.255.0 {
+        host test_machine {
+                hardware ethernet      00:D0:B7:A6:FC:25;
+                fixed-address          192.168.2.10;
+                filename               "elilo.efi";
+                option domain-name     "mydomain.com";
+                option host-name        "test_machine";
+               option routers          192.168.2.1;
+               option subnet-mask      255.255.255.0;
+
+        }
+    }
+
+   For the tftp server, you need to make sure that it is ACTIVATED by inetd or xinetd depending
+   on your distribution. In most distributions, it is disabled by default for security reasons.
+   On distributions using xinetd, you need to check /etc/xinet.d/tftp. For inetd you need to 
+   check /etc/inetd.conf. It is typical to have the root directory for tftp be /tftpboot but it
+   can be anything. In this document we will use /tftpboot as the root directory. The files
+   that we need are as follows:
+       - elilo.efi
+       - the elilo config file
+       - the kernel image
+       - the initial ramdisk (optional)
+  
+
+   a/ Location of the files in the tftp directory tree
+
+   For elilo version 3.3b or higher, it is possible to place the files listed above
+   in any subdirectory below the tftp root. Of course the dhcpd.conf file must
+   point to the location of elilo.efi and provide the path from the tftp root
+   directory.
+
+   Elilo will look for its config file, the kernel image, the initial ramdisk (optional)
+   only from the directory it was loaded from. This is useful when the same tftp server
+   is used to services many different kind of clients. 
+
+   Here is a simple example, suppose the dhcpd.conf file contains the following definition:
+
+   subnet 192.168.2.0 netmask 255.255.255.0 {
+        host test_machine {
+                hardware ethernet      00:D0:B7:A6:FC:25;
+                fixed-address          192.168.2.10;
+
+                filename               "/rx6000/elilo.efi";
+
+                option domain-name     "mydomain.com";
+                option host-name        "test_machine";
+               option routers          192.168.2.1;
+               option subnet-mask      255.255.255.0;
+
+        }
+    }
+
+    Elilo will be downloaded from /tftpboot/rx6000. Then elilo will look
+    for all the other files it needs in /tftpboot/rx6000. This rule is 
+    applied to all files, including the all the variation of the config
+    file.
+
+   b/ Getting the config file
+
+      With DHCP, elilo will first try to download its configuration file. It will try
+      several file names and they are as follows:
+
+       1) AABBCCDD.conf 
+          where AABBCCDD is the hexadecimal representation of the IP address assigned to
+          the machine by DHCP. The hexadecimal string (AABBCCDD) MUST use upper case
+          characters.
+
+          This filename is an opportunity to specify a machine specific configuration file.
+
+       2) elilo-ia32.config or elilo-ia64.conf
+          
+          Depending on the machine (client side) architecture elilo will try the IA-32 or
+          IA-64 file.
+
+          This filename is an opportunity to specify a architecture specific configuration file.
+          This distinction between the architectures is useful when the same TFTP server services
+          the two types of clients : IA32- and IA-64 machines.
+
+        3) elilo.conf
+
+  All files use the same format. Elilo will stop at the first match. In case no file is found,
+  it will try to download a default kernel file name (vmlinux).
+
+  c/ Getting the kernel
+
+     The second request from elilo is typically the kernel image. The filename is based on what
+     is in the elilo config file. The path name depends on how the TFTP server is configured. 
+     For security reasons it is customary to have the server do a change root in /tftpboot.
+     Hence filenames are relative to /tftpboot and therefore you don't need to specify it.
+
+     For instance if elilo.conf contains:
+       image=vmlinuz.249
+       label=linux-up
+       root=/dev/sdb2
+
+    and the user selects linux-up, then elilo will request a filename of 'vmlinux.249' 
+    which must therefore be in /tftpboot. Check the configuration of your TFTP server for
+    more on this.
+  
+  d/ Getting the initial ramdisk
+
+     This step is optional. It follows exactly the same naming rules explained for the kernel image.
+     The initial ramdisk file must therefore be somewhere under /tftpboot. 
+
+      For instance if elilo.conf contains:
+       image=vmlinuz.249
+       label=linux-up
+       root=/dev/sdb2
+       initrd=ramdisk/initrd.249
+
+    and the user selects linux-up, then elilo will request a filename of 'ramdisk/initrd.249' 
+    which must therefore be under /tftpboot. 
+  
+
+  e/ Getting IP address information
+
+     When elilo is netbooted, the network filesystem module initializes some elilo variables
+     with the information it received from the DHCP server. At a minimum, it received the 
+     IP address.
+
+     The following information is stored in the elilo variables indicated below:
+       - assigned IP address -> %I
+       - assigned netmask    -> %M
+       - assigned domainname -> %D
+       - assigned gateway    -> %G
+
+      These variables can be used to dynamically adjust the command line arguments passed to the kernel.
+      See section 5/ below for an example.
+
+3/ Netbooting using PXE
+
+   EFI has builtin support for PXE. In fact it first tries PXE and then default to DHCP
+   when it does not find a valid PXE server.
+
+   There is a PXE server package available from Linux/ia32 however this package does not
+   have the necessary extensions to talk to the EFI side, at least on IA-64 platforms.
+
+   There is no need for special options or compile time flags to get elilo to work
+   with PXE instead of standard DHCP. When netbooted, elilo will automatically detect
+   if it has been downloaded via PXE or DHCP and it will adujst how subsequent files
+   are requested.
+
+   You need a special version of the DHCPD server developed by the Internet Software Consortium
+   (http://www.isc.org) with a special patch to add the PXE extensions. Unfortunately as of 
+   version 3.0xx, the patch has not yet made it into the official tree. It is supposed to show
+   up in version 3.1 of the dhcpd server.
+
+   In any case, the elilo package contains a simple example of how you can configure the
+   /etc/dhcpd.conf file for a PXE-aware DHCP server using the extensions provided in the
+   patch. You can look in examples/dhcpd-pxe.conf. The syntax is very different from
+   a standard dhcpd server. 
+
+   The key elements to keep in mind are the PXE layers used by elilo to request the different
+   files:
+
+       Layer 0 : to get the name of the boot loader (elilo.efi)
+       Layer 1 : to get the name of the elilo config file
+       Layer 2 : to get the name of the kernel image
+
+  There is an IMPORTANT distinction between those layers.  The first two (0,1) and requested
+  systematically whereas the last one is used only when the configuration file is not found, i.e.,
+  what is the default kernel to boot. The actual files are STILL downloaded via TFTP. Therefore
+  the TFTP server must also be configured (see previous section for more on this).
+
+
+  a/ Getting the config file
+
+  In this mode, elilo use the PXE layer 1 to get the config file to use. Therefore this must
+  be set on the server side. Elilo will use the following sequence when
+  looking for a config file:
+
+       - use the name provide by the PXE server Layer 1 or
+
+       - elilo-ia64.conf/elilo-ia32.conf or
+
+       - elilo.conf
+
+  Elilo stops at the first match. With PXE, elilo does not try to download a config file named after 
+  the assigned IP address as it does for DHCP because there is enough flexibility in the PXE server 
+  configuration to do this.
+
+  b/ Getting the kernel image
+
+  When there is no config file, elilo will use the kernel name returned by 
+  PXE layer 2. If it is not specified there, then it default to 'vmlinux'.
+
+  c/ Getting the initial ramdisk
+
+  The filename for the ramdisk MUST come from the config file. Elilo does not use a PXE layer
+  to ask for a default name.
+
+  d/ Getting IP address information
+
+  When elilo is netbooted, the network filesystem module initializes some elilo variables
+  with the information it received from the DHCP server. At a minimum, it received the 
+  IP address.
+
+  The following information is stored in the variables indicated below:
+       - assigned IP address -> %I
+       - assigned netmask    -> %M
+       - assigned domainname -> %D
+       - assigned gateway    -> %G
+
+  These variables can be used to dynamically adjust the command line arguments passed to the kernel.
+  See section 5/ below for an example of how to use the variable.
+
+
+4/ Netbooting and using a local root filesystem
+
+       This is the simplest configuration where the boot loader, its config file, the kernel
+       and its optional initial ramdisk are downloaded from the network BUT the kernel uses
+       the local disk for its root filesystem.
+
+       For such configuration there is no special option necessary in the elilo config file.
+       You simply need to specify which partition is the root partition.  A typical elilo.conf
+       would look as follows:
+
+       image=vmlinuz.249
+       label=linux-up
+       root=/dev/sdb2
+       initrd=ramdisk/initrd.249
+
+5/ Netbooting a diskless machine
+
+   In this configuration we do not use the local machine's disks but instead rely on
+   a remote server to provide the root filesystem via NFS.
+
+   a/ Prerequisites
+
+   By default most kernels shipped by distributors do not have the support
+   compiled in for such configuration. This means that you need to recompile
+   your own kernel. For instance, vmlinuz-2.4.9 as shipped in Redhat7.2 on
+   both ia32 and ia64 platforms does not have the support builtin. 
+       
+   To get this configuration to work, you need to have a kernel compiled
+   such that it accepts a root filesystem over NFS (CONFIG_ROOT_NFS). This
+   necessitates that the network stack be configured with the, so called,
+   IP plug-and-play support (CONFIG_IP_PNP).
+
+   b/ On the server side
+
+   You need to have:
+       - a NFS file server  to provide the root filesystem.
+       - a DHCP/PXE server to get the IP address and download the boot loader.
+
+   Note that both do not need to be on the same machine. There is no special 
+   DHCP/PXE configuration option required to get this working. All you need
+   is a kernel compiled with the options mentioned in a/. You also need to
+   make sure that the permission on the NFS server are set appropriately
+   to allow root access from the client machine (no_root_squash), see 
+   man 'exports' for more on this.
+
+   c/ The elilo configuration file
+
+   To boot successfully, the kernel needs to:
+       - get an IP address and related networking parameters
+       - contact the NFS server to get its root filesystem
+       
+   The 2.4.x kernel series provides several options to get the IP address:
+       - it can do an internal DHCP request (CONFIG_IP_PNP_DHCP)
+       - it can do an internal RARP request (CONFIG_IP_PNP_RARP)
+       - it can do an internal BOOTP request (CONFIG_IP_PNP_BOOTP)
+       - it can get the IP address from the command line
+
+   The choice is up to you but it is a little bit stupid to go through a
+   DHCP/BOOTP/RARP phase again when this is already done by the EFI firmware.
+   So in this document, we describe how you can pass the information provided
+   by EFI on the command line of the kernel.
+
+   The syntax used to pass IP information on the command line is described in
+   the kernel source tree in Documentation/nfsroot.txt. The option is called
+   "ip=" and has the following syntax:
+
+   ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>
+
+   To designate the NFS server, you must use the "nfsroot=" option. It has the
+   following syntax:
+       nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
+
+   Depending on how you want your system configured you can hardcode the
+   values of the parameters in the elilo configuration file. For instance:
+
+   image=/vmlinuz
+        label=nfsroot
+        description="kernel with NFS root"
+        append="root=/dev/nfs nfsroot=192.168.2.22:/ia64_rootfs ip=192.168.2.5::192.168.2.1:255.255.255.0:test_machine:eth0:on"
+
+  Note the root=/dev/nfs indicates that the root filesystem is over NFS. 
+
+  This example works fine however, it is not very flexible because the IP
+  address, the gateway, netmask and hostname are fixed and do not used the
+  values EFI used to download the boot loader and the kernel.
+
+  Elilo provides a way to dynamically customize the parameters passed on the
+  command line using substitution variables. We describe those variables in
+  elilovar.txt. The basic idea is to allow the parameters to use the dynamic
+  information obtained by the DHCP/PXE phase.
+
+  The network support in elilo defines several variables which contained
+  network related information produced by the DHCP/PXE phase. The set of 
+  variable is:
+       %I -> the IP address obtained by DHCP/PXE
+       %M -> the netmask obtained by DHCP/PXE
+       %G -> the gateway obtained by DHCP/PXE
+       %H -> the hostname obtained by DHCP/PXE
+       %D -> the domain name obtained by DHCP/PXE
+
+ So, the configuration file can then be customized as follows:
+   image=/vmlinuz
+        label=nfsroot
+        description="kernel with NFS root"
+        append="root=/dev/nfs nfsroot=192.168.2.22:/ia64_rootfs ip=%I::%G:%M:%H:eth0:on"
+
+ Not all parameters are necessary or even used by the kernel or the user level
+ configuration scripts. There is no variable to substitute the NFS server or
+ the mount point on that server.
+
+
+ In the case of a DHCP boot, this type of customization makes sense only for
+ the shared configuration file, elilo-ia64.conf/elilo-ia32.conf or elilo.conf.
+ The configuration file based on the IP address (such as C0A80205.conf in this
+ case) would provide another way of customizing parameters for a specific
+ client (IP address). The same thing holds if the name of the config file 
+ returned by the PXE server is specific to a client.
+
+
+6/ References
+
+   More information on the PXE protocol can be found at the following web site:
+
+               http://developer.intel.com/ial/wfm/
+
+   The source code for the standard and (possibly) PXE-enhanced DHCPD can be
+    downloaded from:
+
+               http://www.isc.org/     
+
diff --git a/docs/simple_chooser.txt b/docs/simple_chooser.txt
new file mode 100644 (file)
index 0000000..49e6de5
--- /dev/null
@@ -0,0 +1,156 @@
+Information about the simple chooser
+--------------------------------------
+Copyright (C) 2002-2003 Hewlett-Packard Co.
+Contributed by Stephane Eranian <eranian@hpl.hp.com>
+
+Last updated: 02/02/14
+
+Chooser name: simple
+Command line option: -C simple
+Config file option: chooser=simple, description, message
+
+The simple chooser is the default chooser. However it is possible
+to disable it at compile time, it is highly recommended to keep it
+in. Elilo must have at least one chooser compiled in.
+
+The simple chooser is very basic as its name indicates! It provides
+a simple one line text mode command prompt similar to what you get
+with Lilo/x86.  
+
+Any chooser becomes visible to the user ONLY when the interactive
+mode is entered.
+
+The simple chooser allows the user to select a kernel to boot.
+The user can use a label as specified in the elilo config file 
+or a kernel file name. File names can be specified with
+absolute names in the form dev_name:/path/to/my_kernel.
+
+1/ Activation
+
+   The chooser is activated from:
+
+       - command line  with the -c simple
+       - the config file with the chooser=simple option
+
+2/ Supported options
+
+   The simple chooser supports the following options in the config file:
+
+       message=filename  : display a message before the prompt. The filename
+                           must be an ASCII file
+
+       description=string: a description of the kernel image (ASCII)
+
+   All other options have their standard meaning. The chooser does not recognize 
+   the fX (X varies from 1-12) options
+
+2/ Builtin commands
+   
+The simple chooser has some builtin command which the user can
+get to by typing certain keys as the first character on the command line:
+
+    TAB:       shows the list of defined labels and their descriptions.
+
+               If the user did not type anything, i.e., the line is empty,
+               pressing TAB will print the list of labels defined in the
+               elilo config file.
+
+               If the user already typed a name and if the name corresponds
+               to a specified label, the chooser will show how the label
+               is expanded and what the final command line to the kernel will
+               look like.
+
+               If the line is empty pressing TAB generates something similar to:
+               ELILO boot: 
+               linux-up linux nfsroot   (or any kernel file name: [dev_name:]/path/file)
+
+               Note that first label correspond to the default label used if the user
+               hits the enter key with an empty line. This label is not necessarily
+               the first one in the config file.
+
+               Now pressing TAB with a full label name:
+
+               ELILO boot: linux-up
+               desc   : my default UP kernel
+               cmdline: vmlinuz root=/dev/sdb2 console=ttyS0,115200n8 console=tty0 ro
+
+               The desc line shows whatever was specified in the "description" option 
+               for this particular image in the config  file.
+
+    =  :       shows the list of accessible devices
+
+               this key force elilo to print the list of detected devices. Elilo will 
+               auto-detect the devices which are accessible to load a config file, the kernel, the
+               initrd from. Those devices typically represent disk partition, CDROM, floppy, or
+               a network path. The list of devices is highly system dependent.
+               It also depends on the filesystem support compiled into elilo.
+
+               The way the devices are named depends on the device naming scheme
+               selected. It also depends on whether the EDD30 support is activated.
+               For instance, pressing the ? could look as follows:
+
+               ELILO boot: 
+               scsi0 :   vfat : Acpi(PNP0A03,2)/Pci(1|0)/Scsi(Pun0,Lun0)/HD(Part1,Sig72040800)
+               scsi1 :   vfat : Acpi(PNP0A03,2)/Pci(1|0)/Scsi(Pun6,Lun0)/HD(Part1,Sig00000000)
+               scsi2 : ext2fs : Acpi(PNP0A03,2)/Pci(1|0)/Scsi(Pun0,Lun0)/HD(Part2,Sig72040800)
+               scsi3 : ext2fs : Acpi(PNP0A03,2)/Pci(1|0)/Scsi(Pun6,Lun0)/HD(Part2,Sig00000000)
+               net0 :  netfs : Acpi(PNP0A03,0)/Pci(5|0)/Mac(00D0B7A6FC25)
+               5 devices available for booting
+               boot device net0: netfs
+
+               Here the vfat (EFI partition type), ext2fs and network filesysten (not to be confused
+               with NFS) were compiled into elilo and were detected on the machine. The left handside
+               of the colon show the logical name associated with the device. For instance,
+               scsi0 corresponds to the first partition of SCSI disk ID 0 and is an EFI partition.
+               The net0 correspond to a network device, here the Ethernet adapter. The last line
+               show the device used to load elilo itself, in the case elilo was downloaded from the
+               network.
+
+               To get a kernel from scsi0, the user can simply type:
+
+                       ELILO boot: scsi0:/usr/src/linux/vmlinux 
+
+               Note that even though elilo was not downloaded from the network, it is still possible
+               to get the kernel and initrd from a remote machine.
+
+    %  :       shows the list of defined variables
+
+               Elilo has builtin variables which are used to dynamically customized the command line
+               parameters passed to the kernel. The list of variables depends on the support compiled
+               into elilo. Not all elilo subsystems use variables. Typically the network file system
+               does. Pressing '%' only prints the variables that are defined with their current values.
+               Some variables are only defined once the subsystem that creates them has been used.
+               In other words, if the network filesystem was not used to load elilo, then the variables
+               defined by it are not created.
+
+               If the network was actually used, pressing '%' could generate the following output:
+               ELILO boot:     
+               D = "mydomain.com
+               G = "192.168.3.1"
+               H = "test_machine"
+               I = "192.168.3.4"
+               M = "255.255.255.0"
+
+    &  :       shows the list default path
+
+               The path is used as a prefix for all filenames specified as
+               relative.
+
+    ?  :       shows the list of supported command keys
+
+
+The simple chooser has also some builtin command line editing commands:
+
+       ESC      : abort (leave elilo)
+
+       CTRL-D   : abort (leave elilo)
+
+       CTRL-C   : kill line
+                  empty current line and prompt for new input
+
+       CTRL-H   : erase the previous character
+
+       CTRL-U   : clear current line
+                  reset the buffer (does not display correctly if buffer spans more than one line)
+
+       Backspace: erase character
diff --git a/docs/textmenu_chooser.txt b/docs/textmenu_chooser.txt
new file mode 100644 (file)
index 0000000..5ad4caf
--- /dev/null
@@ -0,0 +1,61 @@
+Information about the textmenu chooser
+--------------------------------------
+Copyright (C) 2002-2003 Hewlett-Packard Co.
+Contributed by <rhirst@linuxcare.com>
+
+Last updated: 02/02/14
+
+Chooser name: textmenu
+Command line option: -C textmenu
+Config file option: chooser=textmenu
+
+The textmenu chooser provides a facility whereby you can draw a colour
+boot screen, using line-drawing characters to give the impression of a
+dialog box, with a scrollable menu from which a boot image can be chosen
+via cursor up/down keys.  A one-line input field is provided for additional
+parameter input.  Menu entries are taken from the description= fields in
+the config file.
+
+The message file format is based on that used for syslinux/isolinux on ia32
+platforms, which is copyright H. Peter Anvin.  It is basically a text file
+containing text and graphic characters, along with some control codes to
+specify colour attributes, menu, and prompt field positions.  There are two
+classes of message files; the main file, specified via message=, which
+includes menu and prompt field markers, and the additional help files which
+are invoked via function keys.  Graphic characters are taken from the
+standard IBM VGA character set, and using an appropriate font makes file
+creation easier.  Under Linux you can find a VGA font in the dosemu package.
+Included in the elilo source is sys2ansi.pl (taken from syslinux), which can
+be used to translate colour attributes such that they display correctly in an
+xterm.
+
+Valid control codes:
+
+0x01 ^A        Mark top left or bottom right of menu area.  Current attributes
+       at top left marker are used for inactive menu entries, current
+       attributes when bottom right marker is found are used for the
+       currently active menu attributes.
+
+0x02 ^B        Mark left- or right-hand end of the prompt field.  Current attributes
+       at the left had end are used to display and parameters entered.
+
+0x0A ^J        Linefeed, does implied carriage return.
+
+0x0C ^L        Clear screen
+
+0x0D ^M        Carriage return; ignored so files can be 'DOS' or 'UNIX' format.
+
+0x0F ^O        Attribute specfication; Two hex digits should follow this code, the
+       first being the background colour required, the second the foreground.
+
+       0 = black               8 = dark grey
+       1 = dark blue           9 = bright blue
+       2 = dark green          a = bright green
+       3 = dark cyan           b = bright cyan
+       4 = dark red            c = bright red
+       5 = dark purple         d = bright purple
+       6 = brown               e = yellow
+       7 = light grey          f = white
+
+An example of a config file and message file are included in the examples 
+directory.
diff --git a/elf.h b/elf.h
new file mode 100644 (file)
index 0000000..ad367f5
--- /dev/null
+++ b/elf.h
@@ -0,0 +1,632 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  GNU EFI is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  GNU EFI is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU EFI; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ *
+ * Portions of this file are derived from the  LILO/x86
+ * Copyright 1992-1997 Werner Almesberger. 
+ */
+
+#ifndef __LINUX_ELF_H__
+#define __LINUX_ELF_H__
+
+/*
+ * This file was derived from <linux/elf.h>.
+ */
+
+#include <efi.h>
+
+/* 32-bit ELF base types. */
+typedef UINT32 Elf32_Addr;
+typedef UINT16 Elf32_Half;
+typedef UINT32 Elf32_Off;
+typedef INT32  Elf32_Sword;
+typedef UINT32 Elf32_Word;
+
+/* 64-bit ELF base types. */
+typedef UINT64 Elf64_Addr;
+typedef UINT16 Elf64_Half;
+typedef INT16  Elf64_SHalf;
+typedef UINT64 Elf64_Off;
+typedef INT64  Elf64_Sword;
+typedef UINT64 Elf64_Word;
+
+/* These constants are for the segment types stored in the image headers */
+#define PT_NULL    0
+#define PT_LOAD    1
+#define PT_DYNAMIC 2
+#define PT_INTERP  3
+#define PT_NOTE    4
+#define PT_SHLIB   5
+#define PT_PHDR    6
+#define PT_LOPROC  0x70000000
+#define PT_HIPROC  0x7fffffff
+#define PT_MIPS_REGINFO                0x70000000
+
+/* Flags in the e_flags field of the header */
+#define EF_MIPS_NOREORDER 0x00000001
+#define EF_MIPS_PIC       0x00000002
+#define EF_MIPS_CPIC      0x00000004
+#define EF_MIPS_ARCH      0xf0000000
+
+/* These constants define the different elf file types */
+#define ET_NONE   0
+#define ET_REL    1
+#define ET_EXEC   2
+#define ET_DYN    3
+#define ET_CORE   4
+#define ET_LOPROC 0xff00
+#define ET_HIPROC 0xffff
+
+/* These constants define the various ELF target machines */
+#define EM_NONE  0
+#define EM_M32   1
+#define EM_SPARC 2
+#define EM_386   3
+#define EM_68K   4
+#define EM_88K   5
+#define EM_486   6   /* Perhaps disused */
+#define EM_860   7
+
+#define EM_MIPS                8       /* MIPS R3000 (officially, big-endian only) */
+
+#define EM_MIPS_RS4_BE 10      /* MIPS R4000 big-endian */
+
+#define EM_PARISC      15      /* HPPA */
+
+#define EM_SPARC32PLUS 18      /* Sun's "v8plus" */
+
+#define EM_PPC        20       /* PowerPC */
+
+#define EM_SH         42       /* SuperH */
+
+#define EM_SPARCV9     43      /* SPARC v9 64-bit */
+
+#define EM_IA_64       50      /* HP/Intel IA-64 */
+
+/*
+ * This is an interim value that we will use until the committee comes
+ * up with a final number.
+ */
+#define EM_ALPHA       0x9026
+
+
+/* This is the info that is needed to parse the dynamic section of the file */
+#define DT_NULL                0
+#define DT_NEEDED      1
+#define DT_PLTRELSZ    2
+#define DT_PLTGOT      3
+#define DT_HASH                4
+#define DT_STRTAB      5
+#define DT_SYMTAB      6
+#define DT_RELA                7
+#define DT_RELASZ      8
+#define DT_RELAENT     9
+#define DT_STRSZ       10
+#define DT_SYMENT      11
+#define DT_INIT                12
+#define DT_FINI                13
+#define DT_SONAME      14
+#define DT_RPATH       15
+#define DT_SYMBOLIC    16
+#define DT_REL         17
+#define DT_RELSZ       18
+#define DT_RELENT      19
+#define DT_PLTREL      20
+#define DT_DEBUG       21
+#define DT_TEXTREL     22
+#define DT_JMPREL      23
+#define DT_LOPROC      0x70000000
+#define DT_HIPROC      0x7fffffff
+#define DT_MIPS_RLD_VERSION    0x70000001
+#define DT_MIPS_TIME_STAMP     0x70000002
+#define DT_MIPS_ICHECKSUM      0x70000003
+#define DT_MIPS_IVERSION       0x70000004
+#define DT_MIPS_FLAGS          0x70000005
+#  define RHF_NONE               0
+#  define RHF_HARDWAY            1
+#  define RHF_NOTPOT             2
+#define DT_MIPS_BASE_ADDRESS   0x70000006
+#define DT_MIPS_CONFLICT       0x70000008
+#define DT_MIPS_LIBLIST                0x70000009
+#define DT_MIPS_LOCAL_GOTNO    0x7000000a
+#define DT_MIPS_CONFLICTNO     0x7000000b
+#define DT_MIPS_LIBLISTNO      0x70000010
+#define DT_MIPS_SYMTABNO       0x70000011
+#define DT_MIPS_UNREFEXTNO     0x70000012
+#define DT_MIPS_GOTSYM         0x70000013
+#define DT_MIPS_HIPAGENO       0x70000014
+#define DT_MIPS_RLD_MAP                0x70000016
+
+/* This info is needed when parsing the symbol table */
+#define STB_LOCAL  0
+#define STB_GLOBAL 1
+#define STB_WEAK   2
+
+#define STT_NOTYPE  0
+#define STT_OBJECT  1
+#define STT_FUNC    2
+#define STT_SECTION 3
+#define STT_FILE    4
+
+#define ELF32_ST_BIND(x) ((x) >> 4)
+#define ELF32_ST_TYPE(x) (((unsigned int) x) & 0xf)
+
+/* Symbolic values for the entries in the auxiliary table
+   put on the initial stack */
+#define AT_NULL   0    /* end of vector */
+#define AT_IGNORE 1    /* entry should be ignored */
+#define AT_EXECFD 2    /* file descriptor of program */
+#define AT_PHDR   3    /* program headers for program */
+#define AT_PHENT  4    /* size of program header entry */
+#define AT_PHNUM  5    /* number of program headers */
+#define AT_PAGESZ 6    /* system page size */
+#define AT_BASE   7    /* base address of interpreter */
+#define AT_FLAGS  8    /* flags */
+#define AT_ENTRY  9    /* entry point of program */
+#define AT_NOTELF 10   /* program is not ELF */
+#define AT_UID    11   /* real uid */
+#define AT_EUID   12   /* effective uid */
+#define AT_GID    13   /* real gid */
+#define AT_EGID   14   /* effective gid */
+#define AT_PLATFORM 15  /* string identifying CPU for optimizations */
+#define AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
+
+typedef struct dynamic{
+  Elf32_Sword d_tag;
+  union{
+    Elf32_Sword        d_val;
+    Elf32_Addr d_ptr;
+  } d_un;
+} Elf32_Dyn;
+
+typedef struct {
+  Elf64_Word d_tag;            /* entry tag value */
+  union {
+    Elf64_Word d_val;
+    Elf64_Word d_ptr;
+  } d_un;
+} Elf64_Dyn;
+
+/* The following are used with relocations */
+#define ELF32_R_SYM(x) ((x) >> 8)
+#define ELF32_R_TYPE(x) ((x) & 0xff)
+
+#define R_386_NONE     0
+#define R_386_32       1
+#define R_386_PC32     2
+#define R_386_GOT32    3
+#define R_386_PLT32    4
+#define R_386_COPY     5
+#define R_386_GLOB_DAT 6
+#define R_386_JMP_SLOT 7
+#define R_386_RELATIVE 8
+#define R_386_GOTOFF   9
+#define R_386_GOTPC    10
+#define R_386_NUM      11
+
+#define R_MIPS_NONE            0
+#define R_MIPS_16              1
+#define R_MIPS_32              2
+#define R_MIPS_REL32           3
+#define R_MIPS_26              4
+#define R_MIPS_HI16            5
+#define R_MIPS_LO16            6
+#define R_MIPS_GPREL16         7
+#define R_MIPS_LITERAL         8
+#define R_MIPS_GOT16           9
+#define R_MIPS_PC16            10
+#define R_MIPS_CALL16          11
+#define R_MIPS_GPREL32         12
+/* The remaining relocs are defined on Irix, although they are not
+   in the MIPS ELF ABI.  */
+#define R_MIPS_UNUSED1         13
+#define R_MIPS_UNUSED2         14
+#define R_MIPS_UNUSED3         15
+#define R_MIPS_SHIFT5          16
+#define R_MIPS_SHIFT6          17
+#define R_MIPS_64              18
+#define R_MIPS_GOT_DISP                19
+#define R_MIPS_GOT_PAGE                20
+#define R_MIPS_GOT_OFST                21
+/*
+ * The following two relocation types are specified in the the MIPS ABI
+ * conformance guide version 1.2 but not yet in the psABI.
+ */
+#define R_MIPS_GOTHI16         22
+#define R_MIPS_GOTLO16         23
+#define R_MIPS_SUB             24
+#define R_MIPS_INSERT_A                25
+#define R_MIPS_INSERT_B                26
+#define R_MIPS_DELETE          27
+#define R_MIPS_HIGHER          28
+#define R_MIPS_HIGHEST         29
+/*
+ * The following two relocation types are specified in the the MIPS ABI
+ * conformance guide version 1.2 but not yet in the psABI.
+ */
+#define R_MIPS_CALLHI16                30
+#define R_MIPS_CALLLO16                31
+/*
+ * This range is reserved for vendor specific relocations.
+ */
+#define R_MIPS_LOVENDOR                100
+#define R_MIPS_HIVENDOR                127
+
+
+/*
+ * Sparc ELF relocation types
+ */
+#define        R_SPARC_NONE            0
+#define        R_SPARC_8               1
+#define        R_SPARC_16              2
+#define        R_SPARC_32              3
+#define        R_SPARC_DISP8           4
+#define        R_SPARC_DISP16          5
+#define        R_SPARC_DISP32          6
+#define        R_SPARC_WDISP30         7
+#define        R_SPARC_WDISP22         8
+#define        R_SPARC_HI22            9
+#define        R_SPARC_22              10
+#define        R_SPARC_13              11
+#define        R_SPARC_LO10            12
+#define        R_SPARC_GOT10           13
+#define        R_SPARC_GOT13           14
+#define        R_SPARC_GOT22           15
+#define        R_SPARC_PC10            16
+#define        R_SPARC_PC22            17
+#define        R_SPARC_WPLT30          18
+#define        R_SPARC_COPY            19
+#define        R_SPARC_GLOB_DAT        20
+#define        R_SPARC_JMP_SLOT        21
+#define        R_SPARC_RELATIVE        22
+#define        R_SPARC_UA32            23
+#define R_SPARC_PLT32          24
+#define R_SPARC_HIPLT22                25
+#define R_SPARC_LOPLT10                26
+#define R_SPARC_PCPLT32                27
+#define R_SPARC_PCPLT22                28
+#define R_SPARC_PCPLT10                29
+#define R_SPARC_10             30
+#define R_SPARC_11             31
+#define R_SPARC_WDISP16                40
+#define R_SPARC_WDISP19                41
+#define R_SPARC_7              43
+#define R_SPARC_5              44
+#define R_SPARC_6              45
+
+/* Bits present in AT_HWCAP, primarily for Sparc32.  */
+
+#define HWCAP_SPARC_FLUSH       1    /* CPU supports flush instruction. */
+#define HWCAP_SPARC_STBAR       2
+#define HWCAP_SPARC_SWAP        4
+#define HWCAP_SPARC_MULDIV      8
+#define HWCAP_SPARC_V9         16
+
+
+/*
+ * 68k ELF relocation types
+ */
+#define R_68K_NONE     0
+#define R_68K_32       1
+#define R_68K_16       2
+#define R_68K_8                3
+#define R_68K_PC32     4
+#define R_68K_PC16     5
+#define R_68K_PC8      6
+#define R_68K_GOT32    7
+#define R_68K_GOT16    8
+#define R_68K_GOT8     9
+#define R_68K_GOT32O   10
+#define R_68K_GOT16O   11
+#define R_68K_GOT8O    12
+#define R_68K_PLT32    13
+#define R_68K_PLT16    14
+#define R_68K_PLT8     15
+#define R_68K_PLT32O   16
+#define R_68K_PLT16O   17
+#define R_68K_PLT8O    18
+#define R_68K_COPY     19
+#define R_68K_GLOB_DAT 20
+#define R_68K_JMP_SLOT 21
+#define R_68K_RELATIVE 22
+
+/*
+ * Alpha ELF relocation types
+ */
+#define R_ALPHA_NONE            0       /* No reloc */
+#define R_ALPHA_REFLONG         1       /* Direct 32 bit */
+#define R_ALPHA_REFQUAD         2       /* Direct 64 bit */
+#define R_ALPHA_GPREL32         3       /* GP relative 32 bit */
+#define R_ALPHA_LITERAL         4       /* GP relative 16 bit w/optimization */
+#define R_ALPHA_LITUSE          5       /* Optimization hint for LITERAL */
+#define R_ALPHA_GPDISP          6       /* Add displacement to GP */
+#define R_ALPHA_BRADDR          7       /* PC+4 relative 23 bit shifted */
+#define R_ALPHA_HINT            8       /* PC+4 relative 16 bit shifted */
+#define R_ALPHA_SREL16          9       /* PC relative 16 bit */
+#define R_ALPHA_SREL32          10      /* PC relative 32 bit */
+#define R_ALPHA_SREL64          11      /* PC relative 64 bit */
+#define R_ALPHA_OP_PUSH         12      /* OP stack push */
+#define R_ALPHA_OP_STORE        13      /* OP stack pop and store */
+#define R_ALPHA_OP_PSUB         14      /* OP stack subtract */
+#define R_ALPHA_OP_PRSHIFT      15      /* OP stack right shift */
+#define R_ALPHA_GPVALUE         16
+#define R_ALPHA_GPRELHIGH       17
+#define R_ALPHA_GPRELLOW        18
+#define R_ALPHA_IMMED_GP_16     19
+#define R_ALPHA_IMMED_GP_HI32   20
+#define R_ALPHA_IMMED_SCN_HI32  21
+#define R_ALPHA_IMMED_BR_HI32   22
+#define R_ALPHA_IMMED_LO32      23
+#define R_ALPHA_COPY            24      /* Copy symbol at runtime */
+#define R_ALPHA_GLOB_DAT        25      /* Create GOT entry */
+#define R_ALPHA_JMP_SLOT        26      /* Create PLT entry */
+#define R_ALPHA_RELATIVE        27      /* Adjust by program base */
+
+/* Legal values for e_flags field of Elf64_Ehdr.  */
+
+#define EF_ALPHA_32BIT         1       /* All addresses are below 2GB */
+
+
+typedef struct elf32_rel {
+  Elf32_Addr   r_offset;
+  Elf32_Word   r_info;
+} Elf32_Rel;
+
+typedef struct elf64_rel {
+  Elf64_Addr r_offset; /* Location at which to apply the action */
+  Elf64_Word r_info;   /* index and type of relocation */
+} Elf64_Rel;
+
+typedef struct elf32_rela{
+  Elf32_Addr   r_offset;
+  Elf32_Word   r_info;
+  Elf32_Sword  r_addend;
+} Elf32_Rela;
+
+typedef struct elf64_rela {
+  Elf64_Addr r_offset; /* Location at which to apply the action */
+  Elf64_Word r_info;   /* index and type of relocation */
+  Elf64_Word r_addend; /* Constant addend used to compute value */
+} Elf64_Rela;
+
+typedef struct elf32_sym{
+  Elf32_Word   st_name;
+  Elf32_Addr   st_value;
+  Elf32_Word   st_size;
+  unsigned char        st_info;
+  unsigned char        st_other;
+  Elf32_Half   st_shndx;
+} Elf32_Sym;
+
+typedef struct elf64_sym {
+  Elf32_Word st_name;          /* Symbol name, index in string tbl (yes, Elf32) */
+  unsigned char        st_info;        /* Type and binding attributes */
+  unsigned char        st_other;       /* No defined meaning, 0 */
+  Elf64_Half st_shndx;         /* Associated section index */
+  Elf64_Addr st_value;         /* Value of the symbol */
+  Elf64_Word st_size;          /* Associated symbol size */
+} Elf64_Sym;
+
+
+#define EI_NIDENT      16
+
+typedef struct elf32_hdr{
+  unsigned char        e_ident[EI_NIDENT];
+  Elf32_Half   e_type;
+  Elf32_Half   e_machine;
+  Elf32_Word   e_version;
+  Elf32_Addr   e_entry;  /* Entry point */
+  Elf32_Off    e_phoff;
+  Elf32_Off    e_shoff;
+  Elf32_Word   e_flags;
+  Elf32_Half   e_ehsize;
+  Elf32_Half   e_phentsize;
+  Elf32_Half   e_phnum;
+  Elf32_Half   e_shentsize;
+  Elf32_Half   e_shnum;
+  Elf32_Half   e_shstrndx;
+} Elf32_Ehdr;
+
+typedef struct elf64_hdr {
+  unsigned char        e_ident[16];            /* ELF "magic number" */
+  Elf64_SHalf e_type;
+  Elf64_Half e_machine;
+  INT32 e_version;
+  Elf64_Addr e_entry;          /* Entry point virtual address */
+  Elf64_Off e_phoff;           /* Program header table file offset */
+  Elf64_Off e_shoff;           /* Section header table file offset */
+  INT32 e_flags;
+  Elf64_SHalf e_ehsize;
+  Elf64_SHalf e_phentsize;
+  Elf64_SHalf e_phnum;
+  Elf64_SHalf e_shentsize;
+  Elf64_SHalf e_shnum;
+  Elf64_SHalf e_shstrndx;
+} Elf64_Ehdr;
+
+/* These constants define the permissions on sections in the program
+   header, p_flags. */
+#define PF_R           0x4
+#define PF_W           0x2
+#define PF_X           0x1
+
+typedef struct elf32_phdr{
+  Elf32_Word   p_type;
+  Elf32_Off    p_offset;
+  Elf32_Addr   p_vaddr;
+  Elf32_Addr   p_paddr;
+  Elf32_Word   p_filesz;
+  Elf32_Word   p_memsz;
+  Elf32_Word   p_flags;
+  Elf32_Word   p_align;
+} Elf32_Phdr;
+
+typedef struct elf64_phdr {
+  INT32 p_type;
+  INT32 p_flags;
+  Elf64_Off p_offset;          /* Segment file offset */
+  Elf64_Addr p_vaddr;          /* Segment virtual address */
+  Elf64_Addr p_paddr;          /* Segment physical address */
+  Elf64_Word p_filesz;         /* Segment size in file */
+  Elf64_Word p_memsz;          /* Segment size in memory */
+  Elf64_Word p_align;          /* Segment alignment, file & memory */
+} Elf64_Phdr;
+
+/* sh_type */
+#define SHT_NULL       0
+#define SHT_PROGBITS   1
+#define SHT_SYMTAB     2
+#define SHT_STRTAB     3
+#define SHT_RELA       4
+#define SHT_HASH       5
+#define SHT_DYNAMIC    6
+#define SHT_NOTE       7
+#define SHT_NOBITS     8
+#define SHT_REL                9
+#define SHT_SHLIB      10
+#define SHT_DYNSYM     11
+#define SHT_NUM                12
+#define SHT_LOPROC     0x70000000
+#define SHT_HIPROC     0x7fffffff
+#define SHT_LOUSER     0x80000000
+#define SHT_HIUSER     0xffffffff
+#define SHT_MIPS_LIST          0x70000000
+#define SHT_MIPS_CONFLICT      0x70000002
+#define SHT_MIPS_GPTAB         0x70000003
+#define SHT_MIPS_UCODE         0x70000004
+
+/* sh_flags */
+#define SHF_WRITE      0x1
+#define SHF_ALLOC      0x2
+#define SHF_EXECINSTR  0x4
+#define SHF_MASKPROC   0xf0000000
+#define SHF_MIPS_GPREL 0x10000000
+
+/* special section indexes */
+#define SHN_UNDEF      0
+#define SHN_LORESERVE  0xff00
+#define SHN_LOPROC     0xff00
+#define SHN_HIPROC     0xff1f
+#define SHN_ABS                0xfff1
+#define SHN_COMMON     0xfff2
+#define SHN_HIRESERVE  0xffff
+#define SHN_MIPS_ACCOMON       0xff00
+typedef struct {
+  Elf32_Word   sh_name;
+  Elf32_Word   sh_type;
+  Elf32_Word   sh_flags;
+  Elf32_Addr   sh_addr;
+  Elf32_Off    sh_offset;
+  Elf32_Word   sh_size;
+  Elf32_Word   sh_link;
+  Elf32_Word   sh_info;
+  Elf32_Word   sh_addralign;
+  Elf32_Word   sh_entsize;
+} Elf32_Shdr;
+
+typedef struct elf64_shdr {
+  Elf32_Word sh_name;          /* Section name, index in string tbl (yes Elf32) */
+  Elf32_Word sh_type;          /* Type of section (yes Elf32) */
+  Elf64_Word sh_flags;         /* Miscellaneous section attributes */
+  Elf64_Addr sh_addr;          /* Section virtual addr at execution */
+  Elf64_Off sh_offset;         /* Section file offset */
+  Elf64_Word sh_size;          /* Size of section in bytes */
+  Elf32_Word sh_link;          /* Index of another section (yes Elf32) */
+  Elf32_Word sh_info;          /* Additional section information (yes Elf32) */
+  Elf64_Word sh_addralign;     /* Section alignment */
+  Elf64_Word sh_entsize;       /* Entry size if section holds table */
+} Elf64_Shdr;
+
+#define        EI_MAG0         0               /* e_ident[] indexes */
+#define        EI_MAG1         1
+#define        EI_MAG2         2
+#define        EI_MAG3         3
+#define        EI_CLASS        4
+#define        EI_DATA         5
+#define        EI_VERSION      6
+#define        EI_PAD          7
+
+#define        ELFMAG0         0x7f            /* EI_MAG */
+#define        ELFMAG1         'E'
+#define        ELFMAG2         'L'
+#define        ELFMAG3         'F'
+#define        ELFMAG          "\177ELF"
+#define        SELFMAG         4
+
+#define        ELFCLASSNONE    0               /* EI_CLASS */
+#define        ELFCLASS32      1
+#define        ELFCLASS64      2
+#define        ELFCLASSNUM     3
+
+#define ELFDATANONE    0               /* e_ident[EI_DATA] */
+#define ELFDATA2LSB    1
+#define ELFDATA2MSB    2
+
+#define EV_NONE                0               /* e_version, EI_VERSION */
+#define EV_CURRENT     1
+#define EV_NUM         2
+
+/* Notes used in ET_CORE */
+#define NT_PRSTATUS    1
+#define NT_PRFPREG     2
+#define NT_PRPSINFO    3
+#define NT_TASKSTRUCT  4
+
+/* Note header in a PT_NOTE section */
+typedef struct elf32_note {
+  Elf32_Word   n_namesz;       /* Name size */
+  Elf32_Word   n_descsz;       /* Content size */
+  Elf32_Word   n_type;         /* Content type */
+} Elf32_Nhdr;
+
+/* Note header in a PT_NOTE section */
+/*
+ * For now we use the 32 bit version of the structure until we figure
+ * out whether we need anything better.  Note - on the Alpha, "unsigned int"
+ * is only 32 bits.
+ */
+typedef struct elf64_note {
+  Elf32_Word n_namesz; /* Name size */
+  Elf32_Word n_descsz; /* Content size */
+  Elf32_Word n_type;   /* Content type */
+} Elf64_Nhdr;
+
+#if ELF_CLASS == ELFCLASS32
+
+extern Elf32_Dyn _DYNAMIC [];
+#define elfhdr         elf32_hdr
+#define elf_phdr       elf32_phdr
+#define elf_note       elf32_note
+
+#else
+
+extern Elf64_Dyn _DYNAMIC [];
+#define elfhdr         elf64_hdr
+#define elf_phdr       elf64_phdr
+#define elf_note       elf64_note
+
+#endif
+
+
+#endif /* __LINUX_ELF_H__ */
diff --git a/elilo-ia32.efi b/elilo-ia32.efi
new file mode 100755 (executable)
index 0000000..3f8cd33
Binary files /dev/null and b/elilo-ia32.efi differ
diff --git a/elilo-ia64.efi b/elilo-ia64.efi
new file mode 100755 (executable)
index 0000000..6022fed
Binary files /dev/null and b/elilo-ia64.efi differ
diff --git a/elilo.c b/elilo.c
new file mode 100644 (file)
index 0000000..0959c71
--- /dev/null
+++ b/elilo.c
@@ -0,0 +1,654 @@
+/* 
+ * elilo.c - IA-64/IA-32 EFI Linux loader
+ *
+ *  Copyright (C) 1999-2003 Hewlett-Packard Co.
+ *     Contributed by David Mosberger <davidm@hpl.hp.com>.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ *  Copyright (C) 1999-2000 VA Linux Systems
+ *       Contributed by Johannes Erdfelt <jerdfelt@valinux.com>.
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "vars.h"
+
+#include "getopt.h"
+#include "fileops.h"
+#include "loader.h"
+#include "config.h" /* for config_init() */
+
+#define ELILO_VERSION                  L"3.4"
+#define ELILO_SHARED_CMDLINE_OPTS      L"pPMC:aDhd:i:vVc:E"
+
+elilo_config_t elilo_opt;
+
+EFI_SYSTEM_TABLE *systab;      /* pointer to EFI system table */
+
+/*
+ * Load the Linux kernel in memory from the boot media
+ * Output:
+ *     kd = address of kernel entry point
+ *        + end address of kernel code+data+bss
+ *        + kernel entry point
+ * Return:
+ *     ELILO_LOAD_ERROR   : if something went wrong
+ *     ELILO_LOAD_ABORTED : user interruption while loading
+ *     ELILO_LOAD_SUCCESS : otherwise
+ */
+static INTN
+do_kernel_load(CHAR16 *kname, kdesc_t *kd)
+{
+       loader_ops_t    *ldops;
+       EFI_STATUS status;
+       fops_fd_t fd;
+
+       status = fops_open(kname, &fd);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"Kernel file  not found %s", kname));
+               return ELILO_LOAD_ERROR;
+       }
+       fops_close(fd);
+
+       ldops = loader_probe(kname);
+       if (ldops == NULL) {
+               ERR_PRT((L"Cannot find a loader for %s", kname));
+               return ELILO_LOAD_ERROR;
+       }
+
+       VERB_PRT(1,Print(L"Using %s loader\n", ldops->ld_name));
+       
+       return ldops->ld_load_kernel(kname, kd);
+}
+
+INTN
+kernel_load(EFI_HANDLE image, CHAR16 *kname, kdesc_t *kd, memdesc_t *imem)
+{
+
+       /*
+        * Now let's try to load the kernel !
+        */
+       switch(do_kernel_load(kname, kd)) {
+               case ELILO_LOAD_SUCCESS:
+                       break;
+
+               case ELILO_LOAD_ERROR:
+                       /* XXX: add fallback support */
+                       return ELILO_LOAD_ERROR;
+               
+               case ELILO_LOAD_ABORTED:
+                       /* we drop initrd in case we aborted the load */
+                       elilo_opt.initrd[0] = CHAR_NULL;
+
+                       /* will go back to interactive selection */
+                       elilo_opt.prompt  = 1; 
+                       elilo_opt.timeout = ELILO_DEFAULT_TIMEOUT;
+                       elilo_opt.delay   = 0;
+
+                       return ELILO_LOAD_RETRY;        
+       }
+
+       VERB_PRT(3, Print(L"kernel loaded in [0x%lx-0x%lx] entry=0x%lx\n", 
+                         (UINT64)kd->kstart, (UINT64)kd->kend, (UINT64)kd->kentry));
+
+       if (elilo_opt.initrd[0]) {
+
+               if (sysdeps_initrd_get_addr(kd, imem) == -1) goto exit_error;
+
+               switch(load_initrd(elilo_opt.initrd, imem)) {
+                       case ELILO_LOAD_SUCCESS:
+                               break;
+                       case ELILO_LOAD_ERROR:
+                               goto exit_error;
+                       case ELILO_LOAD_ABORTED:
+                               /* the free_kmem() is the responsibility of the loader */
+
+                               /* we drop initrd in case we aborted the load */
+                               elilo_opt.initrd[0] = CHAR_NULL;
+                               elilo_opt.prompt    = 1; 
+                               elilo_opt.timeout   = ELILO_DEFAULT_TIMEOUT;
+                               elilo_opt.delay     = 0;
+
+                               return ELILO_LOAD_RETRY;
+               }
+       }
+       return ELILO_LOAD_SUCCESS;
+exit_error:
+       free_kmem();
+       if (imem->start_addr) free(imem->start_addr);
+
+       return ELILO_LOAD_ERROR;
+}
+
+static INTN
+main_loop(EFI_HANDLE dev, CHAR16 **argv, INTN argc, INTN index, EFI_HANDLE image)
+{
+       CHAR16 kname[FILENAME_MAXLEN];
+       CHAR16 cmdline_tmp[CMDLINE_MAXLEN];
+       CHAR16 cmdline[CMDLINE_MAXLEN];
+       VOID *bp;
+       UINTN cookie;
+       EFI_STATUS status = EFI_SUCCESS;
+       kdesc_t kd;
+       memdesc_t imem;
+       INTN r;
+
+       /*
+        * First place where we potentially do system dependent
+        * operations. It is a one time opportunity before entering
+        * the main loop
+        */
+       if (sysdeps_preloop_actions(dev, argv, argc, index, image) == -1) return -1;
+
+       for(;;) {
+               kname[0] = cmdline_tmp[0] = cmdline[0] = CHAR_NULL;
+               imem.start_addr = 0; imem.pgcnt = 0;
+               elilo_opt.sys_img_opts = NULL;
+
+               if (kernel_chooser(argv, argc, index, kname, cmdline_tmp) == -1) goto exit_error;
+
+               switch (kernel_load(image, kname, &kd, &imem)) {
+                       case ELILO_LOAD_SUCCESS: 
+                               goto do_launch;
+                       case ELILO_LOAD_ERROR:
+                               goto exit_error;
+                       /* otherwise we retry ! */
+               }
+       } 
+
+do_launch:
+       r =subst_vars(cmdline_tmp, cmdline, CMDLINE_MAXLEN);
+
+       VERB_PRT(3, Print(L"final cmdline(%d): %s\n", r, cmdline));
+
+       /* free resources associated with file accesses (before ExitBootServices) */
+       close_devices();
+
+       /* No console output permitted after create_boot_params()! */
+       if ((bp=create_boot_params(cmdline, &imem, &cookie)) == 0) goto error;
+
+       /* terminate bootservices */
+       status = BS->ExitBootServices(image, cookie);
+       if (EFI_ERROR(status)) goto bad_exit;
+
+       start_kernel(kd.kentry, bp);
+       /* NOT REACHED */
+
+       ERR_PRT((L"start_kernel() return !"));
+bad_exit:      
+       /*
+        * we are still in BootService mode
+        */
+       ERR_PRT((L"ExitBootServices failed %r", status));
+error:
+       free_kmem();
+       if (imem.start_addr) free(imem.start_addr);
+       if (bp) free_boot_params(bp);
+exit_error:
+       return ELILO_LOAD_ERROR;
+
+}
+
+static VOID
+elilo_help(VOID)
+{
+       
+       Print(L"-d secs   timeout in 10th of second before booting\n");
+       Print(L"-h        this help text\n");
+       Print(L"-V        print version\n");
+       Print(L"-v        verbose level(can appear multiple times)\n");
+       Print(L"-a        always check for alternate kernel image\n");
+       Print(L"-i file   load file as the initial ramdisk\n");
+       Print(L"-C file   indicate the config file to use\n");
+       Print(L"-P        parse config file only (verify syntax)\n");
+       Print(L"-D        enable debug prints\n");
+       Print(L"-p        force interactive mode\n");
+       Print(L"-c name   image chooser to use\n");
+       Print(L"-E        do not force EDD30 variable\n");
+
+       sysdeps_print_cmdline_opts();
+
+       Print(L"\n");
+
+       print_config_options();
+}
+
+/*
+ * XXX: hack to work around a problem with EFI command line argument when booted
+ * from network. In this case, it looks like LoadOptions/LoadOptionsSize contain
+ * garbage
+ */
+static CHAR16 *default_load_options;
+static UINTN default_load_options_size;
+static INTN done_fixups;
+
+static VOID
+fixupargs(EFI_LOADED_IMAGE *info)
+{
+       EFI_STATUS status;
+       EFI_PXE_BASE_CODE *pxe;
+
+#define FAKE_ELILONAME L"elilo-forced"
+
+       status = BS->HandleProtocol (info->DeviceHandle, &PxeBaseCodeProtocol, (VOID **)&pxe);
+       if (EFI_ERROR(status)) return;
+
+       default_load_options      = info->LoadOptions;
+       default_load_options_size = info->LoadOptionsSize;
+
+       info->LoadOptions     = FAKE_ELILONAME;
+       info->LoadOptionsSize = StrLen(info->LoadOptions)*sizeof(CHAR16);
+
+       done_fixups = 1;
+}
+
+/*
+ * we restore the arguments in case we modified them just to make sure
+ * we don't confuse caller.
+ */
+static VOID
+unfixupargs(EFI_LOADED_IMAGE *info)
+{
+       if (done_fixups == 0) return;
+
+       info->LoadOptions     = default_load_options;
+       info->LoadOptionsSize = default_load_options_size;
+}
+
+
+/*
+ * in order to get fully detailed EFI path names to devices, EDD3.0 specification must
+ * be turned on. On new versions of EFI, this is the default. An environment variable
+ * called EDD30 reflects the current settings. If true, then EDD3.0 is enabled 
+ * and device path names show the detailed device types. If false, then a default 
+ * generic name is used instead. This has some implications of ELILO's ability to 
+ * provide a better naming scheme for detected devices. If EDD3.0 is enabled
+ * then much more precise names can be used which makes it easy to use. 
+ * If EDD3.0 is nont enabled, ELILO will still work but will use very generic names
+ * for devices.
+ *
+ * ELILO will check the value of the variable. If not true, then it will force it to
+ * true. This will require a reboot for EFI to make use of the new value.
+ * Return:
+ *     EFI_SUCCESS: if variable is already true or was set to true
+ *     EFI error code: otherwise, like when could not forced variable or unrecognized variable content
+ */
+#define EDD30_GUID \
+{0x964e5b21, 0x6459, 0x11d2, { 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}}
+
+#define EDD30_ATTR (EFI_VARIABLE_RUNTIME_ACCESS|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_NON_VOLATILE)
+
+static EFI_GUID edd30_guid = EDD30_GUID;
+
+static INTN
+check_edd30(VOID)
+{
+       EFI_STATUS      status;
+       UINTN           l = sizeof(BOOLEAN);
+       UINT8           bool = FALSE;
+       INTN            ret = -1;
+
+       status = RT->GetVariable(L"EDD30", &edd30_guid, NULL, &l, &bool);
+       if (status == EFI_BUFFER_TOO_SMALL || (bool != TRUE && bool != FALSE)) {
+               ERR_PRT((L"Warning: EDD30 EFI variable is not boolean value: forcing it to TRUE"));
+               return -1;
+       }
+       if (status == EFI_SUCCESS && bool == TRUE) {
+               VERB_PRT(3, Print(L"EDD30 is TRUE\n"));
+               elilo_opt.edd30_on = TRUE;
+               ret = 0;
+       } else {
+               VERB_PRT(4, 
+                       if (status != EFI_SUCCESS) {
+                               Print(L"EDD30 EFI variable not defined\n");
+                       } else {
+                               Print(L"EDD30 EFI variable is false\n");
+                       }
+               );
+       }
+
+       return ret;
+}
+
+static INTN
+force_edd30(VOID)
+{
+       EFI_STATUS      status;
+       UINTN           l = sizeof(BOOLEAN);
+       UINT8           bool;
+
+       bool = TRUE;
+       status = RT->SetVariable(L"EDD30", &edd30_guid, EDD30_ATTR, l, &bool);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"can't set EDD30 variable: ignoring it"));
+               return -1;
+       }
+
+       VERB_PRT(3, Print(L"EDD30 variable forced to TRUE. You should reboot to take advantage of EDD3.0.\n"));
+
+       return 0;
+}
+
+/*
+ * That's equivalent of main(): main entry point
+ */
+EFI_STATUS
+efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *system_tab)
+{
+       CHAR16 *argv[MAX_ARGS];
+       CHAR16 optstring[MAX_ARGS];
+       EFI_LOADED_IMAGE *info;
+       EFI_STATUS status, ret = EFI_LOAD_ERROR;
+       INTN argc = 0, c;
+       INTN edd30_status, retry;
+       CHAR16 *ptr, *arglist = NULL;
+       BOOLEAN devices_initialized = FALSE;
+       CHAR16 dpath[FILENAME_MAXLEN];
+       CHAR16 *devpath;
+
+       //elilo_opt.verbose=3;
+       //elilo_opt.debug=1;
+
+       /* initialize global variable */
+       systab = system_tab;
+       
+       /* initialize EFI library */
+       InitializeLib(image, systab);
+       /*
+        * disable the platform watchdog timer if we go interactive
+        * XXX: we should reinstate it once we're done
+        * It seems like with PXE, the timer is set to a few minutes
+        * and sometimes triggers if we stay too long in interactive
+        * mode.
+        * XXX: clean this up !
+        */
+       BS->SetWatchdogTimer(0, 0x0, 0, NULL);
+
+       /* initialize memory allocator */
+       if (alloc_init() == -1) return EFI_LOAD_ERROR;
+
+       status = BS->HandleProtocol(image, &LoadedImageProtocol, (VOID **) &info);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"image handle does not support LOADED_IMAGE protocol"));
+               return EFI_LOAD_ERROR;
+       }
+
+       VERB_PRT(5,Print(L"Loaded at 0x%lx size=%d bytes code=%d data=%d\n", info->ImageBase, info->ImageSize, info->ImageCodeType, info->ImageDataType));
+
+       /*
+        * verify EDD3.0 status. Users may have to reboot 
+        */
+       edd30_status = check_edd30();
+
+       /*
+        * initialize configuration empire
+        */
+       if (config_init() == -1) goto do_exit;
+
+       /* 
+        * architecture-specific initializations
+        */
+       if (sysdeps_init(info->DeviceHandle) == -1) goto do_exit;
+       if (sysdeps_register_options() == -1) goto do_exit;
+
+       /*
+        * This may be required in case Elilo was booted with absolutely no arguments
+        * Elilo's logic is that just like normal Linux programs at least one argument
+        * (argv[0]) exists at all times and that it usually gives the name of the program
+        * (the command used to start it).
+       ERR_PRT((L"LoadOptions=%x OpenSize=%d", info->LoadOptions, info->LoadOptionsSize));
+        */
+
+       /*
+        * in case there is something wrong with the default boot_device
+        * we default to basic fixups and we need to force interactive
+        * mode to make sure the user has a chance of specifying a kernel
+        */
+       fixupargs(info);
+
+#if 0
+       Print(L"LoadOptions=%x OpenSize=%d\n", info->LoadOptions, info->LoadOptionsSize);
+       { INTN i; for (i=0; i< info->LoadOptionsSize>>1; i++) Print(L"options[%d]=%d (%c)\n", i, ((CHAR16 *)info->LoadOptions)[i], ((CHAR16 *)info->LoadOptions)[i]); }
+#endif
+
+       /*
+        * we must copy argument because argify modifies the string.
+        * This caused problems when arguments are coming from NVRAM
+        * as passed by the EFI boot manager
+        *
+        * We add an extra character to the buffer in case the LoadOptions is not 
+        * NULL terminated. The extra space will be used to ADD the extra terminator.
+        */
+       arglist = alloc(info->LoadOptionsSize+sizeof(CHAR16), EfiLoaderData);
+       if (arglist == NULL) {
+               ERR_PRT((L"cannot copy argument list"));
+               return EFI_OUT_OF_RESOURCES;
+       }
+       Memcpy(arglist, info->LoadOptions, info->LoadOptionsSize);
+
+       argc = argify(arglist,info->LoadOptionsSize, argv); 
+
+       StrCpy(optstring, ELILO_SHARED_CMDLINE_OPTS);
+       StrCat(optstring, sysdeps_get_cmdline_opts());
+
+       while ((c=Getopt(argc, argv, optstring)) != -1 ) {
+               switch(c) {
+                       case 'a':
+                               elilo_opt.alt_check = 1;
+                               break;
+                       case 'D':
+                               elilo_opt.debug = 1;
+                               break;
+                       case 'p':
+                               elilo_opt.prompt = 1;
+                               break;
+                       case 'v':
+                               elilo_opt.verbose++;
+                               if (elilo_opt.verbose > 5) elilo_opt.verbose = 5;
+                               break;
+                       case 'h':
+                               elilo_help();
+                               ret = EFI_SUCCESS;
+                               goto do_exit;
+                       case 'd':
+                               /*
+                                * zero is a valid value here, so we use the delay-set to mark the
+                                * fact that the user specified a value on cmdline. See config.c
+                                */
+                               elilo_opt.delay = Atoi(Optarg);
+                               elilo_opt.delay_set = 1;
+                               break;
+                       case 'E':
+                               /* don't force EDD30 EFI variable if not already set */
+                               elilo_opt.edd30_no_force = 1;
+                               break;
+                       case 'i':
+                               if (StrLen(Optarg) >= FILENAME_MAXLEN-1) {
+                                       Print(L"initrd filename is limited to %d characters\n", FILENAME_MAXLEN);
+                                       goto do_exit;
+                               }
+                               StrCpy(elilo_opt.initrd, Optarg);
+                               break;
+                       case 'C':
+                               if (StrLen(Optarg) >= FILENAME_MAXLEN-1) {
+                                       Print(L"config filename is limited to %d characters\n", FILENAME_MAXLEN);
+                                       goto do_exit;
+                               }
+                               StrCpy(elilo_opt.config, Optarg);
+                               break;
+                       case 'M': /* builtin debug tool */
+                               { mmap_desc_t mdesc; 
+                                       if (get_memmap(&mdesc) == -1) {
+                                               Print(L"Cannot get memory map\n");
+                                               return EFI_LOAD_ERROR;
+                                       }
+                                       print_memmap(&mdesc);
+                                       ret = EFI_SUCCESS;
+                                       goto do_exit;
+                               }
+                       case 'V':
+                               Print(L"ELILO v%s for EFI/%a\n", ELILO_VERSION, ELILO_ARCH);
+                               ret = EFI_SUCCESS;
+                               goto do_exit;
+                       case 'P':
+                               /* cmdline only option */
+                               elilo_opt.parse_only =  1;
+                               break;
+                       case 'c':
+                               if (StrLen(Optarg) >= FILENAME_MAXLEN-1) {
+                                       Print(L"chooser name is limited to %d characters\n", FILENAME_MAXLEN);
+                                       goto do_exit;
+                               }
+                               StrCpy(elilo_opt.chooser, Optarg);
+                               break;
+                       default:
+                               /*
+                                * try system dependent options before declaring error
+                                */
+                               if (sysdeps_getopt(c, Optind, Optarg) == 0) continue;
+
+                               Print(L"Unknown option -%c\n", (CHAR16)c);
+                               goto do_exit;
+               }
+       }
+       DBG_PRT((L"Optind=%d optarg=%x argc=%d", Optind, Optarg, argc));
+
+       /*
+        * we can't defer this phase any longer...
+        * Must be done after the elilo_opt are initialized (at least partially)
+        */
+       if (init_devices(info->DeviceHandle) == -1) goto do_exit;
+
+       devices_initialized = TRUE;
+
+       devpath = DevicePathToStr(info->FilePath);
+
+       /*
+        * set per fileops defaults files for configuration and kernel
+        */
+       fops_setdefaults(elilo_opt.default_config, elilo_opt.default_kernel, FILENAME_MAXLEN, devpath);
+
+       /*
+        * XXX: won't be visible if verbose not required from command line
+        */
+       VERB_PRT(2,Print(L"Default config: %s\nDefault_kernel: %s\n",
+                       elilo_opt.default_config,elilo_opt.default_kernel));
+       /*
+        * use default config file if not specified by user
+        */
+       ptr = elilo_opt.config[0] == CHAR_NULL ?  (retry=1,elilo_opt.default_config) : (retry=0,elilo_opt.config);
+
+       /*
+        * parse config file (verbose becomes visible if set)
+        */
+       ret = read_config(ptr, retry);
+       Print(L"read_config=%r\n", ret);
+       /*
+        * when the config file is not found, we fail only if:
+        *      - the user did not specified interactive mode
+        *      - the user did not explicitely specify the config file
+        */
+       if (ret == EFI_NOT_FOUND || ret == EFI_TFTP_ERROR) {
+               if (elilo_opt.prompt == 0 && elilo_opt.config[0] != CHAR_NULL) {
+                       Print(L"config file %s not found\n", ptr);
+                       goto do_exit;
+               }
+               fops_getdefault_path(dpath, FILENAME_MAXLEN);
+               if (ret == EFI_TFTP_ERROR)
+                       Print(L"no config file found on TFTP server in %s\n", dpath);
+               else
+                       Print(L"no config file found in %s\n", dpath);
+       }
+       /*
+        * stop if just doing parsing
+        */
+       if (elilo_opt.parse_only) {
+               if (ret == EFI_SUCCESS)
+                       Print(L"Config file %s parsed successfully\n", ptr);
+               goto do_exit;
+       }
+       /*
+        * if there was an error when parsing the config file, then
+        * we force interactive mode to give a chance to the user.
+        * We also clear the error. 
+        */
+       if (ret && argc == 1) {
+               Print(L"forcing interactive mode because of errors\n");
+               elilo_opt.prompt = 1;
+       }
+
+       /*
+        * If EDD30 EFI variable was not set to TRUE (or not defined), we
+        * we try to force it. This will take effect at the next reboot.
+        *
+        * Some controllers don't have EDD30 support, in this case forcing it 
+        * may cause problems, therefore we check the edd_no_force option
+        * before making the call.
+        */
+       if (edd30_status == -1 && elilo_opt.edd30_no_force == 0) {
+               force_edd30();
+       }
+
+       ret = EFI_LOAD_ERROR;
+
+       /*
+        * if the user specified a kernel on the command line
+        * then we don't go to interactive mode even if it
+        * was set in the config file or set because of an
+        * error parsing the config file.
+        */
+       if (argc > Optind) elilo_opt.prompt = 0;
+
+
+       /* set default timeout if going interactive */
+       if ((elilo_opt.prompt && elilo_opt.timeout == 0)) {
+               elilo_opt.timeout = ELILO_DEFAULT_TIMEOUT;
+       }
+
+       /*
+        * which chooser we will use 
+        */
+       if (init_chooser(info->DeviceHandle) == -1) {
+               ERR_PRT((L"Cannot find a decent chooser\n"));
+               goto do_exit;
+       }
+
+       //if (elilo_opt.prompt == 0) VERB_PRT(1, print_devices());
+
+       main_loop(info->DeviceHandle, argv, argc, Optind, image);
+       /* should not return */
+do_exit:
+       unfixupargs(info);
+
+       //if (arglist) free(arglist);
+
+       /* free all resources assiocated with file accesses */
+       if (devices_initialized) close_devices();
+
+       /* garbage collect all remaining allocations */
+       free_all_memory();
+
+       return ret;
+}
diff --git a/elilo.h b/elilo.h
new file mode 100644 (file)
index 0000000..9d28617
--- /dev/null
+++ b/elilo.h
@@ -0,0 +1,201 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ *  Copyright (C) 2001 Silicon Graphics, Inc.
+ *      Contributed by Brent Casavant <bcasavan@sgi.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  GNU EFI is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  GNU EFI is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU EFI; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __ELILO_H__
+#define __ELILO_H__
+
+#include <efi.h>
+
+#include "elilo_debug.h"
+
+#include "fileops.h"
+#include "chooser.h"
+
+#include "strops.h"
+
+#include "sysdeps.h"
+
+#define MB             (1024*1024) /* 1 megabyte */
+
+/* Round X up/down to A, which must be an integer power of two.  */
+#define ROUNDUP(x,a)   (((x) + (a) - 1) & ~((a) - 1))
+#define ROUNDDOWN(x,a)  ((x) & ~((a) - 1))
+
+/*
+ * Elilo Boot modes
+ */
+#define ELILO_LOAD_SUCCESS     0
+#define ELILO_LOAD_ABORTED     1
+#define ELILO_LOAD_ERROR       2
+#define ELILO_LOAD_RETRY       3
+
+#define ELILO_DEFAULT_TIMEOUT  ELILO_TIMEOUT_INFINITY
+#define ELILO_TIMEOUT_INFINITY (~0UL)
+
+#define CMDLINE_MAXLEN         512 /* needed by ia32 */
+#define FILENAME_MAXLEN                256
+#define MAX_ARGS               256
+
+typedef struct {
+       UINT8 nothing_yet;
+} image_opt_t;
+
+typedef struct {
+       /*
+        * list of options controllable from both the command line
+        * and the config file
+        */
+       UINTN alt_check;        /* always check for alternate kernel */
+       UINTN debug;            /* debug print() on */
+       UINTN delay;            /* delay before booting the image */
+       UINTN verbose;          /* verbosity level [1-5] */
+       CHAR16 initrd[FILENAME_MAXLEN];         /* name of file for initial ramdisk */
+       UINT8 delay_set;        /* mark whether or not delay was specified on cmdline */
+       UINT8 edd30_on;         /* true is EDD30 variable is TRUE */
+       UINT8 edd30_no_force;   /* don't force EDD30 variable to true */
+       /*
+        * list of options controllable from either the command line
+        * or the config file
+        */
+       UINTN prompt;           /* enter interactive mode */
+       UINTN parse_only;       /* only parses the config file */
+       UINTN timeout;          /* timeout before leaving interactive mode*/
+
+       image_opt_t img_opt;    /* architecture independent per image options*/
+
+       sys_img_options_t *sys_img_opts;        /* architecture depdendent per image options */
+
+       CHAR16 default_kernel[FILENAME_MAXLEN];
+       CHAR16 default_config[FILENAME_MAXLEN];
+
+       CHAR16 config[FILENAME_MAXLEN];         /* name of config file */
+       CHAR16 chooser[FILENAME_MAXLEN];        /* image chooser to use */
+} elilo_config_t;
+
+
+extern elilo_config_t elilo_opt;
+
+extern EFI_SYSTEM_TABLE *systab;
+
+typedef struct {
+       VOID    *start_addr;
+       UINTN   pgcnt;
+} memdesc_t;
+
+typedef struct {
+       VOID *kstart;
+       VOID *kend;
+       VOID *kentry;
+} kdesc_t;
+
+typedef struct {
+       EFI_MEMORY_DESCRIPTOR   *md;
+       UINTN                   map_size;
+       UINTN                   desc_size;
+       UINTN                   cookie;
+       UINT32                  desc_version;
+} mmap_desc_t;
+
+#ifndef MAX
+#define MAX(a,b)       ((a)>(b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b)       ((a)>(b) ? (b) : (a))
+#endif
+
+#define VERB_PRT(n,cmd) \
+       { if (elilo_opt.verbose >= (n)) { cmd; } }
+
+
+/* from alloc.c */
+extern INTN alloc_init(VOID);
+extern VOID *alloc(UINTN, EFI_MEMORY_TYPE);
+extern VOID free(VOID *);
+extern VOID *alloc_pages(UINTN, EFI_MEMORY_TYPE, EFI_ALLOCATE_TYPE, VOID *);
+extern VOID free_pages(VOID *);
+extern VOID free_all(VOID);
+extern INTN alloc_kmem(VOID *, UINTN);
+extern VOID free_kmem(VOID);
+extern VOID free_all_memory(VOID);
+
+/* from util.c */
+extern INTN read_file(UINTN fd, UINTN, CHAR8 *);
+extern EFI_STATUS check_abort(VOID);
+extern VOID reset_input(VOID);
+extern INTN wait_timeout(UINTN);
+extern INTN argify(CHAR16 *, UINTN, CHAR16 **);
+extern VOID unargify(CHAR16 **, CHAR16 **);
+extern void split_args(CHAR16 *, CHAR16 *, CHAR16 *);
+extern INTN get_memmap(mmap_desc_t *);
+extern VOID free_memmap(mmap_desc_t *);
+extern INTN find_kernel_memory(VOID *low_addr, VOID *max_addr, UINTN alignment, VOID ** start);
+extern VOID print_memmap(mmap_desc_t *);
+extern VOID ascii2U(CHAR8 *, CHAR16 *, UINTN);
+extern VOID U2ascii(CHAR16 *, CHAR8 *, UINTN);
+
+/* from config.c (more in config.h) */
+extern EFI_STATUS read_config(CHAR16 *, INTN retry);
+extern VOID print_config_options(VOID);
+extern INTN find_label(CHAR16 *, CHAR16 *, CHAR16 *, CHAR16 *);
+extern VOID print_label_list(VOID);
+extern INTN config_init(VOID);
+extern CHAR16 *get_message_filename(INTN which);
+extern CHAR16 *find_description(CHAR16 *label);
+extern VOID *get_next_description(VOID *prev, CHAR16 **label, CHAR16 **description);
+extern CHAR16 *get_config_file(VOID);
+
+/* from initrd.c */
+extern INTN load_initrd(CHAR16 *, memdesc_t *);
+
+/* from alternate.c */
+extern INTN alternate_kernel(CHAR16 *, INTN);
+
+/* from bootparams.c */
+extern VOID *create_boot_params (CHAR16 *, memdesc_t *, UINTN *);
+extern VOID free_boot_params(VOID *bp);
+
+/*
+ * architecture-specific API
+ */
+
+
+extern INTN sysdeps_create_boot_params(boot_params_t *, CHAR8 *, memdesc_t *, UINTN *);
+extern VOID sysdeps_free_boot_params(boot_params_t *);
+extern INTN sysdeps_init(EFI_HANDLE dev);
+extern INTN sysdeps_initrd_get_addr(kdesc_t *, memdesc_t *);
+extern INTN sysdeps_preloop_actions(EFI_HANDLE, CHAR16 **, INTN, INTN, EFI_HANDLE);
+extern CHAR16 *sysdeps_get_cmdline_opts(VOID);
+extern INTN sysdeps_getopt(INTN, INTN, CHAR16 *);
+extern VOID sysdeps_print_cmdline_opts(VOID);
+extern INTN sysdeps_register_options(VOID);
+
+#define        CHAR_SLASH      L'/'
+#define CHAR_BACKSLASH L'\\'
+
+#endif /* __ELILO_H__ */
diff --git a/elilo_debug.h b/elilo_debug.h
new file mode 100644 (file)
index 0000000..bdea9e2
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  GNU EFI is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  GNU EFI is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU EFI; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __ELILO_DEBUG__
+#define __ELILO_DEBUG__
+
+#define ELILO_DEBUG 1
+
+#define ERR_PRT(a)     do { Print(L"%a(line %d):", __FILE__, __LINE__); Print a; Print(L"\n"); } while (0);
+
+#ifdef ELILO_DEBUG
+#define DBG_PRT(a)     do { \
+       if (elilo_opt.debug) { \
+               Print(L"%a(line %d):", __FILE__, __LINE__); \
+               Print a; \
+               Print(L"\n"); \
+} } while (0);
+#else
+#define DBG_PRT(a)
+#endif
+
+#endif /* __ELILO_DEBUG_H__ */
diff --git a/examples/netboot/dhcpd-pxe.conf b/examples/netboot/dhcpd-pxe.conf
new file mode 100644 (file)
index 0000000..e14a713
--- /dev/null
@@ -0,0 +1,188 @@
+#
+# Enable proxyDHCP operation.
+#
+dhcpd-operation normal;
+
+#
+# BootServer is turned on
+#
+bootserver-operation on;
+
+ddns-update-style ad-hoc;
+
+#
+# if this dhcpd server is not "master"
+#
+not authoritative;
+
+#-------------------------------------------------------------
+# For each of the 3 servers (builtin) define the DHCPD option
+# tags we are interested in.
+#-------------------------------------------------------------
+
+#
+# Define DHCPD request option tags
+#
+
+#
+# This option is used to determine the client boot-time binary runtime
+# environment.
+#
+option client-architecture code 93 =
+       unsigned integer 16;
+
+#
+# Now go to the DHCPD proxy option tags
+#
+option space proxy;
+
+option proxy.boot-prompt code 10 =
+       { unsigned integer 8, text };
+
+option proxy.boot-menu code 9 = array of
+       { unsigned integer 16, unsigned integer 8, text };
+
+option proxy.boot-servers code 8 = array of
+       { unsigned integer 16, unsigned integer 8, array of ip-address };
+
+option proxy.discovery-control code 6 = unsigned integer 8;
+
+#
+# Now go to the PXE Bootserver options
+#
+option space bs;
+
+option bs.boot-item code 71 =
+       { unsigned integer 16, unsigned integer 16 };
+
+#-------------------------------------------------------------
+# Actual configuration
+#-------------------------------------------------------------
+
+subnet 192.168.2.0 netmask 255.255.255.0 {
+#
+# In this section define regular DHCPD options
+#
+
+       #
+       # Here we show settings with fixed addresses, but dynamic 
+       # allocation is possible as well
+       #
+       host test1 {
+               hardware ethernet 00:d0:b7:c7:fb:f8;
+               fixed-address 192.168.2.10;
+       }
+       host test2 {
+               hardware ethernet 00:d0:b7:aa:f0:e3;
+               fixed-address 192.168.2.11;
+       }
+
+       #
+       # Now we look at options for every possible type of requests
+       #
+
+
+       #
+       #
+       # If requets was received by the ProxyDHCPD
+       if proxy {
+
+               #
+               # Provide proxyDHCP information for Intel ia64
+               # architecture machines.
+               # 
+               if option client-architecture = 00:02 {
+                       #
+                       # Notify of PXE aware server
+                       #
+                       option vendor-class-identifier "PXEClient";
+       
+                       #
+                       # Force unicast
+                       #
+                       option proxy.discovery-control 3;
+
+                       #
+                       # Print a nice boot menu
+                       #
+                       # ServerTypes:
+                       # 14 -> means Redhat install 
+                       # 13 -> means Redhat Boot 
+                       # 23 & 26 are length of string following. 
+                       #
+                       option proxy.boot-menu
+                       14 23 "Remote Redhat/ia64 boot",
+                       13 26 "Remote Redhat/ia64 install";
+
+                       #
+                       # list of possible bootservers for a ServerType
+                       #
+                       # Currently not possible to define more than one type
+                       #
+                       option proxy.boot-servers
+                       14 1 192.168.2.32;
+
+                       #
+                       # A boot prompt
+                       # 30 is timeout in seconds
+                       #
+                       option proxy.boot-prompt
+                       30 "Press <F8> or <M> for menu. Press <Esc> to local boot.";
+
+                       #
+                       #
+                       vendor-option-space proxy;
+               }
+       } else if bootserver {
+
+               if option client-architecture = 00:02 {
+                       #
+                       # Now analyze bootserver request option tags
+                       #
+
+
+                       # ELILO Layering:
+                       #       Layer 0: bootloader binary (elilo.efi)
+                       #       Layer 1: elilo configuration file (elilo.conf)
+                       #       Layer 2: Linux/ia64 kernel
+
+                       if substring(option bs.boot-item, 2, 2) = 00:00 {
+
+                               filename "/tftpboot/elilo.efi";
+       
+                               #
+                               # identify reply layer & server type
+                               #
+                               option bs.boot-item 14 0;
+
+                       } else if substring(option bs.boot-item, 2, 2) = 00:01 {
+
+                               filename "/tftpboot/elilo.conf";
+
+                               #
+                               # identify reply layer & server type
+                               #
+                               option bs.boot-item 14 1;
+
+                       } else if substring(option bs.boot-item, 2, 3) = 00:02 {
+
+                               filename "/tftpboot/vmlinux";
+
+                               #
+                               # identify reply layer & server type
+                               #
+                               option bs.boot-item 14 2;
+                       }
+                       #
+                       #
+                       vendor-option-space bs;
+
+                       option vendor-class-identifier "PXEClient";
+               }
+       } else {
+               #
+               # notify of PXE aware DHCPD server
+               #
+               option vendor-class-identifier "PXEClient";
+       }
+}
diff --git a/examples/netboot/dhcpd.conf b/examples/netboot/dhcpd.conf
new file mode 100644 (file)
index 0000000..1d2aece
--- /dev/null
@@ -0,0 +1,14 @@
+subnet 192.168.2.0 netmask 255.255.255.0 {
+
+       option domain-name "mydomain.com";
+       option subnet-mask 255.255.255.0;
+       option routers     15.4.88.1;
+       
+       # here we use a fixed address
+       host test_machine {
+               hardware ethernet 00:D0:B7:C7:FB:F8;
+               fixed-address 192.168.2.10;
+               filename "/tftpboot/elilo.efi";
+                option host-name "test_machine";
+       }
+}
diff --git a/examples/textmenu_chooser/elilo-textmenu.conf b/examples/textmenu_chooser/elilo-textmenu.conf
new file mode 100644 (file)
index 0000000..c46a8d0
--- /dev/null
@@ -0,0 +1,45 @@
+#
+# force chooser to textmenu
+chooser=textmenu
+
+delay=20
+prompt
+
+#
+# the files containing the text (with attributes) to display
+#
+message=textmenu-message.msg
+
+#
+# files to load when the corresponding function key is pressed
+#
+f1=general.msg
+f2=params.msg
+
+image=debian/linux
+       label=debian
+       description="Install Debian GNU/Linux"
+       read-only
+       initrd=debian/root.bin
+       root=/dev/ram
+
+image=debian/linux
+       label=sda1
+       description="Boot Debian Linux, root on sda1"
+       read-only
+       root=/dev/sda1
+
+image=debian/linux.old
+       label=old
+       description="Boot Debian Linux, old kernel"
+       read-only
+       root=/dev/sda1
+
+image=debian/linux
+       label=shell
+       description="Execute a shell"
+       read-only
+       initrd=debian/root.bin
+       root=/dev/ram
+       append="init=/bin/sh"
+
diff --git a/examples/textmenu_chooser/general.msg b/examples/textmenu_chooser/general.msg
new file mode 100644 (file)
index 0000000..7ec35ff
--- /dev/null
@@ -0,0 +1,25 @@
+\f ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß 
+\ f10\f 
\ f7fÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ \ f74General Screen\ f7f ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\ f70¿\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70           This is the general screen. You entered by pressing F1           ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                   Press any key to return to main screen                   ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70 Help: [\ f71F1\ f70-General]  [\ f71F2\ f70-Params]                                            ³\ f10
\ f7fÀ\ f70ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\ f10
diff --git a/examples/textmenu_chooser/params.msg b/examples/textmenu_chooser/params.msg
new file mode 100644 (file)
index 0000000..cac9e39
--- /dev/null
@@ -0,0 +1,25 @@
+\f ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß 
+\ f10\f 
\ f7fÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ \ f74Params Screen\ f7f ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\ f70¿\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70           This is the params screen. You entered by pressing F2            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                   Press any key to return to main screen                   ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70 Help: [\ f71F1\ f70-General]  [\ f71F2\ f70-Params]                                            ³\ f10
\ f7fÀ\ f70ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\ f10
diff --git a/examples/textmenu_chooser/textmenu-message.msg b/examples/textmenu_chooser/textmenu-message.msg
new file mode 100644 (file)
index 0000000..f63dfdb
--- /dev/null
@@ -0,0 +1,25 @@
+\f ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß 
+\ f10\f 
\ f7fÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ \ f74Install and Recovery Disk\ f7f ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\ f70¿\ f10
\ f7f³\ f70 This CD demonstrates the elilo textmenu chooser.  You can select an entry  ³\ f10
\ f7f³\ f70 from the menu with the curosr keys, enter exptra parameters at the propmt, ³\ f10
\ f7f³\ f70 and press <return> to start the process.  Help can be made available via   ³\ f10
\ f7f³\ f70 the function keys.  On a serial console, use Ctrl-F followed by the        ³\ f10
\ f7f³\ f70 relevant function key number.                                              ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70 \ f7eBeware that this is an install disk, and misuse can result in the loss of  \ f70³\ f10
\ f7f³\ f70 \ f7eany data currently on your disks.                                          \ f70³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70            ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\ f7f¿\ f70              ³\ f10
\ f7f³\ f70            ³\ f70\ 2          This is where the menu goes           \ f7f³\ f70\1e             ³\ f10
\ f7f³\ f70            ³\ f70\ f1e               (active colour)                  \ f7f³\ f70              ³\ f10
\ f7f³\ f70            ³\ f70              (inactive colour)                 \ f7f³\ f70              ³\ f10
\ f7f³\ f70            ³\ f70                                                \ f1e\ 2\ f7f³\ f70\1f             ³\ f10
\ f7f³\ f70            À\ f7fÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\ f70              ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70 Boot: \ f35\ 1                                                                  \ 1\ f70   ³\ f10
\ f7f³\ f70                                                                            ³\ f10
\ f7f³\ f70 Help: [\ f71F1\ f70-General]  [\ f71F2\ f70-Params]                                            ³\ f10
\ f7fÀ\ f70ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\ f10
diff --git a/fileops.c b/fileops.c
new file mode 100644 (file)
index 0000000..674fc5c
--- /dev/null
+++ b/fileops.c
@@ -0,0 +1,628 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "fileops.h"
+
+/*
+ * import filesystems
+ */
+#ifdef CONFIG_LOCALFS
+#include "glue_localfs.h"
+#endif
+#ifdef CONFIG_NETFS
+#include "glue_netfs.h"
+#endif
+
+#ifdef CONFIG_EXT2FS
+#include "glue_ext2fs.h"
+#endif
+
+/*
+ * table of device naming schemes.
+ * we will probe each one in sequential order and stop at first match.
+ */
+extern devname_scheme_t simple_devname_scheme;
+
+static devname_scheme_t *devname_scheme_tab[]={
+       &simple_devname_scheme,
+       NULL
+};
+
+
+/*
+ * Once we are able to create Boot Services drivers we won't need
+ * this hack and we'll be able to load the driver from the boot manager
+ * or EFI shell. So for now we explicitely invoke the probe routine
+ * of each driver that we know about
+ */
+
+typedef EFI_STATUS (fops_fixups_t)(EFI_HANDLE dev, device_t *d);
+
+/*
+ * List of filesystem we currently support
+ */
+
+
+static fileops_fs_t *fs_tab[]={
+#ifdef CONFIG_LOCALFS
+       &localfs_glue, 
+#endif
+#ifdef CONFIG_EXT2FS
+       &ext2fs_glue,
+#endif
+#ifdef CONFIG_NETFS
+       &netfs_glue, 
+#endif
+       NULL
+};
+
+static device_t *dev_tab;
+static UINTN ndev, ndev_boot;
+static device_t *boot_dev;
+
+typedef struct _fdesc_t {
+       struct _fdesc_t *next;  /* pointer to next free (when in free list) */
+       fileops_t       *fops;  /* pointer to filesystem-specific glue interface */
+       UINTN           fh;     /* file descriptor from lower level protocol */
+} fdesc_t;
+
+#define FILEOPS_FD_MAX 16
+
+static fdesc_t fd_tab[FILEOPS_FD_MAX];
+static fdesc_t *free_fd;
+
+static devname_scheme_t *current_devname_scheme;
+
+#define FDESC_IDX(f)   (fops_fd_t)(f-fd_tab)
+#define FDESC_INVALID_FD(fd)   (fd >= FILEOPS_FD_MAX || fd_tab[(fd)].fops == NULL)
+#define FDESC_F(fd)            (fd_tab+(fd))
+
+static fdesc_t *
+fd_alloc(VOID)
+{
+       fdesc_t *tmp = NULL;
+
+       if (free_fd == NULL) {
+               ERR_PRT((L"out of file descriptor"));
+       } else {
+               tmp = free_fd;
+               free_fd = free_fd->next;
+       }
+       return tmp;
+}
+
+static VOID
+fd_free(fdesc_t *fd)
+{
+       if (fd == NULL) {
+               ERR_PRT((L"invalid fd"));
+       }
+       fd->fops = NULL; /* mark as invalid */
+       fd->next = free_fd;
+       free_fd = fd;
+}
+
+static fileops_t *
+glue_filesystem(EFI_GUID *proto, EFI_HANDLE dev, fops_fs_glue_t glue)
+{
+       fileops_t *fops;
+       VOID *intf = NULL;
+       EFI_STATUS status;
+
+       status = BS->HandleProtocol(dev, proto, &intf);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"unable to locate %g: should not happen", proto));
+               return NULL; /* should not happen */
+       }
+       fops = (fileops_t *)alloc(sizeof(*fops), EfiLoaderData);
+       if (fops == NULL) {
+               ERR_PRT((L"failed to allocate fileops"));
+               return NULL; /* XXX uninstall protocol */
+       }
+       Memset(fops, 0, sizeof(*fops));
+
+       fops->dev = dev;
+
+       /* initialize private interface now */
+       glue(fops, intf);
+
+       return fops;
+}
+
+INTN
+fops_split_path(CHAR16 *path, CHAR16 *dev)
+{
+       CHAR16 *p, *d = dev;
+       UINTN len = FILEOPS_DEVNAME_MAXLEN;
+#      define CHAR_COLON       L':'
+
+       p = StrChr(path, CHAR_COLON);
+       if (p == NULL) {
+               *dev = CHAR_NULL; 
+               return 0;
+       }
+       /* copy device part of the name */
+       for (d = dev ; path != p ;) {
+               if (--len == 0) return -1; /* too long */
+               *d++ = *path++;
+       }
+       *d = CHAR_NULL;
+       return 0;
+}
+
+static device_t *
+find_device_byname(CHAR16 *dev)
+{
+       UINTN i;
+       for(i=0; i < ndev; i++) {
+               if (!StrCmp(dev, dev_tab[i].name)) return dev_tab+i;
+       }
+       return NULL;
+}
+
+
+/*
+ * This function is used to walk through the device list and get names.
+ * arguments:
+ *     - pidx = opaque cookie returned by previous call (must be zero for initial call)
+ *     - type is the type of filesystem to look for, e.g., vfat or ext2fs. NULL means ANY
+ *     - maxlen is the size in bytes of the returned string buffer
+ *     - idx is the opaque cookie returned (can be reused by next call)
+ *     - name a string buffer to hold the name of the next matching device
+ *     - dev will receive the EFI_HANDLE corresponding to the device  if not NULL
+ *
+ * return:
+ *     - EFI_INVALID_PARAMETER: when NULL values are passed for required arguments.
+ *     - EFI_NOT_FOUND: when reached the end of the list of invalid cookie
+ *     - EFI_BUFFER_TOO_SMALL is device name does not fit into string buffer
+ *     - EFI_SUCCESS otherwise
+ */
+EFI_STATUS
+fops_get_next_device(UINTN pidx, CHAR16 *type, UINTN maxlen, UINTN *idx, CHAR16 *name, EFI_HANDLE *dev)
+{
+       UINTN i;
+
+       if (name == NULL || idx == NULL) return EFI_INVALID_PARAMETER;
+
+       for(i=pidx; i < ndev; i++) {
+               if (type == NULL || !StrCmp(dev_tab[i].fops->name, type)) goto found;
+       }
+       return EFI_NOT_FOUND;
+found:
+       if (StrLen(dev_tab[i].name) >= maxlen) {
+               *idx = pidx; /* will restart from same place! */
+               return EFI_BUFFER_TOO_SMALL;
+       }
+       StrCpy(name, dev_tab[i].name);
+       *idx = i+1;
+       if (dev) *dev = dev_tab[i].dev; 
+
+       return EFI_SUCCESS;
+}
+
+EFI_STATUS
+fops_get_device_handle(CHAR16 *name, EFI_HANDLE *dev)
+{
+       UINTN i;
+
+       if (name == NULL || dev == NULL) return EFI_INVALID_PARAMETER;
+
+       for(i=0; i < ndev; i++) {
+               if (!StrCmp(dev_tab[i].name, name)) goto found;
+       }
+       return EFI_NOT_FOUND;
+found:
+       *dev = dev_tab[i].dev; 
+
+       return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+fops_open(CHAR16 *fullname, fops_fd_t *fd)
+{
+       fdesc_t *f;
+       EFI_STATUS status;
+       fileops_t *fops;
+       device_t *dev;
+       CHAR16 dev_name[FILEOPS_DEVNAME_MAXLEN];
+       CHAR16 *name;
+
+
+       if (fd == NULL || fullname == NULL || *fullname == CHAR_NULL || fops_split_path(fullname, dev_name) == -1) 
+               return EFI_INVALID_PARAMETER;
+
+       DBG_PRT((L"fops_open(%s), dev:%s:", fullname ? fullname : L"(NULL)", dev_name));
+
+       name = fullname;
+       if (dev_name[0] == CHAR_NULL) {
+               if (boot_dev == NULL) return EFI_INVALID_PARAMETER;
+               fops = boot_dev->fops;
+       } else {
+               if ((dev=find_device_byname(dev_name)) == NULL) return EFI_INVALID_PARAMETER;
+
+               name += StrLen(dev_name)+1;
+
+               fops = dev->fops;
+       }
+
+       if (fops == NULL) return EFI_INVALID_PARAMETER;
+
+       if ((f=fd_alloc()) == NULL) return EFI_OUT_OF_RESOURCES;
+
+       DBG_PRT((L"dev:%s: fullname:%s: name:%s: f=%d", dev_name, fullname, name, f - fd_tab));
+
+       status = fops->open(fops->intf, name, &f->fh);
+       if (EFI_ERROR(status)) goto error;
+
+       f->fops = fops;
+
+       *fd = FDESC_IDX(f);
+
+       return EFI_SUCCESS;
+error:
+       fd_free(f);
+       return status;
+}
+
+EFI_STATUS
+fops_read(fops_fd_t fd, VOID *buf, UINTN *size)
+{
+       fdesc_t *f;
+
+       if (buf == NULL || FDESC_INVALID_FD(fd) || size == NULL) return EFI_INVALID_PARAMETER;
+
+       f = FDESC_F(fd);
+
+       return f->fops->read(f->fops->intf, f->fh, buf, size);
+}
+
+EFI_STATUS
+fops_close(fops_fd_t fd)
+{
+       fdesc_t *f;
+       EFI_STATUS status;
+
+       if (FDESC_INVALID_FD(fd)) return EFI_INVALID_PARAMETER;
+
+       f = FDESC_F(fd);
+
+       status = f->fops->close(f->fops->intf, f->fh);
+
+       fd_free(f);
+
+       return status;
+}
+
+EFI_STATUS
+fops_infosize(fops_fd_t fd, UINT64 *size)
+{
+       fdesc_t *f;
+
+       if (FDESC_INVALID_FD(fd) || size == NULL) return EFI_INVALID_PARAMETER;
+
+       f = FDESC_F(fd);
+
+       return f->fops->infosize(f->fops->intf, f->fh, size);
+}
+
+EFI_STATUS
+fops_seek(fops_fd_t fd, UINT64 newpos)
+{
+       fdesc_t *f;
+
+       if (FDESC_INVALID_FD(fd)) return EFI_INVALID_PARAMETER;
+
+       f = FDESC_F(fd);
+
+       return f->fops->seek(f->fops->intf, f->fh, newpos);
+}
+
+EFI_STATUS
+fops_setdefaults(CHAR16 *config, CHAR16 *kname, UINTN maxlen, CHAR16 *devpath)
+{
+#define FILEOPS_DEFAULT_KERNEL         L"vmlinux"
+#define FILEOPS_DEFAULT_CONFIG         L"elilo.conf"
+
+       if (config == NULL || kname == NULL) return EFI_INVALID_PARAMETER;
+
+       if (boot_dev == NULL || boot_dev->fops == NULL) {
+               if (boot_dev == NULL) 
+                       Print(L"Warning boot device not recognized\n");
+               else
+                       Print(L"Unknown filesystem on boot device\n");
+
+               Print(L"Using builtin defaults for kernel and config file\n");
+
+               StrnCpy(config, FILEOPS_DEFAULT_CONFIG, maxlen-1);
+               StrnCpy(kname, FILEOPS_DEFAULT_KERNEL, maxlen-1);
+
+               return EFI_UNSUPPORTED;
+       }
+       
+       return boot_dev->fops->setdefaults(boot_dev->fops->intf, config, kname, maxlen, devpath);
+}
+
+EFI_STATUS
+fops_getdefault_path(CHAR16 *path, UINTN maxlen)
+{
+       if (path == NULL || maxlen == 0) return EFI_INVALID_PARAMETER;
+
+       /*
+        * if underlying filesystem implements the call, then we call
+        * otherwise we return an empty string
+        */
+       if (boot_dev->fops->getdefault_path) 
+               return boot_dev->fops->getdefault_path(path, maxlen);
+
+       path[0] = CHAR_NULL;
+
+       return EFI_SUCCESS;
+}
+
+CHAR16 *
+fops_bootdev_name(VOID)
+{
+       return boot_dev ? boot_dev->name : L"not supported";
+}
+
+static INTN
+add_dev_tab(EFI_GUID *proto, EFI_HANDLE boot_handle, UINTN size, fops_fs_glue_t glue)
+{
+       EFI_STATUS status;
+       EFI_HANDLE *tab;
+       UINTN i;
+       static UINTN idx;
+
+       if (size == 0) return 0;
+
+       tab = (EFI_HANDLE *)alloc(size, EfiLoaderData);
+       if (tab == NULL) {
+               ERR_PRT((L"failed to allocate handle table"));
+               return -1;
+       }
+
+       Memset(tab, 0, size);
+
+       /*
+        * get the actual device handles now
+        */
+       status = BS->LocateHandle(ByProtocol, proto, NULL, &size, tab);
+       if (status != EFI_SUCCESS) {
+               ERR_PRT((L"failed to get handles for proto %g size=%d: %r", proto, size, status));
+               free(tab);
+               return -1;
+       }
+       size /= sizeof(EFI_HANDLE);
+
+       for(i=0; i < size; i++) {
+               dev_tab[idx].dev  = tab[i];
+               dev_tab[idx].fops = glue_filesystem(proto, tab[i], glue);
+
+               if (tab[i] == boot_handle) boot_dev = dev_tab+idx;
+
+               /* count the ones we recognized */
+               if (dev_tab[idx].fops) ndev_boot++;
+
+               /* assign a generic name for now */
+               dev_tab[idx].name[0] = L'd';
+               dev_tab[idx].name[1] = L'e';
+               dev_tab[idx].name[2] = L'v';
+               dev_tab[idx].name[3] = L'0' + idx/100;
+               dev_tab[idx].name[4] = L'0' + (idx%100)/10;
+               dev_tab[idx].name[5] = L'0' + (idx%100) % 10;
+               dev_tab[idx].name[6] = CHAR_NULL;
+#ifdef ELILO_DEBUG
+               if (elilo_opt.debug) {
+                       EFI_DEVICE_PATH *dp;
+                       CHAR16 *str, *str2;
+                               
+                       str = NULL;
+                       dp = DevicePathFromHandle(dev_tab[idx].dev);
+                       if (dp) str = DevicePathToStr(dp);
+
+                       str2 = str == NULL ? L"Unknown" : str;
+
+                       DBG_PRT((L"%s : %-8s : %s\n", dev_tab[idx].name,
+                               (dev_tab[idx].fops ? dev_tab[idx].fops->name: L"N/A"), str2));
+
+                       if (str) FreePool(str);
+               }
+#endif
+
+               idx++;
+       }
+       free(tab);
+
+       /* remember actual number of bootable devices */
+       ndev = idx;
+
+       return 0;
+}
+
+static INTN
+probe_devname_schemes(device_t *dev_tab, INTN ndev)
+{
+       devname_scheme_t **p;
+
+       for (p = devname_scheme_tab; *p ; p++) {
+               if ((*p)->install_scheme(dev_tab, ndev) == 0) goto found;
+       }
+       ERR_PRT((L"No devname schemes worked, using builtin\n"));
+       return -1;
+found:
+       VERB_PRT(3, Print(L"devname scheme: %s\n", (*p)->name));
+       current_devname_scheme = *p;
+       return 0;
+}
+
+static INTN
+find_filesystems(EFI_HANDLE boot_handle)
+{
+       UINTN size, total = 0;
+       fileops_fs_t **fs;
+
+       /*
+        * 1st pass, figure out how big a table we need
+        */
+       for(fs = fs_tab; *fs; fs++) {
+               size = 0;
+               BS->LocateHandle(ByProtocol, &(*fs)->proto, NULL, &size, NULL);
+               total += size;
+       }
+       if (total == 0) {
+               ERR_PRT((L"No useable filesystem found"));
+               return -1;
+       }
+       total /= sizeof(EFI_HANDLE);
+       DBG_PRT((L"found %d filesystems", total));
+
+       dev_tab = (device_t *)alloc(total*sizeof(device_t), EfiLoaderData);
+       if (dev_tab == NULL) {
+               ERR_PRT((L"failed to allocate handle table"));
+               return -1;
+       }
+
+       Memset(dev_tab, 0, total*sizeof(device_t));
+
+       /* 
+        * do a 2nd pass to initialize the table now
+        */
+       for(fs = fs_tab; *fs; fs++) {
+               size = 0;
+
+               BS->LocateHandle(ByProtocol, &(*fs)->proto, NULL, &size, NULL);
+               if (size == 0) continue;
+
+               add_dev_tab(&(*fs)->proto, boot_handle, size, (*fs)->glue);
+       }
+       probe_devname_schemes(dev_tab, ndev);
+
+       return 0;
+}
+
+static INTN
+fileops_init(VOID)
+{
+       UINTN i;
+
+       for (i=0; i < FILEOPS_FD_MAX-1; i++) {
+               fd_tab[i].next = &fd_tab[i+1];
+       }
+       fd_tab[i].next = NULL;
+       free_fd        = fd_tab;
+
+       return 0;
+}
+
+/*
+ * both functions will go away once we go with boottime drivers
+ */
+static INTN
+install_filesystems(VOID)
+{
+       fileops_fs_t **fs;
+
+       for(fs = fs_tab; *fs; fs++) (*fs)->install();
+
+       return 0;
+}
+
+static INTN
+uninstall_filesystems(VOID)
+{
+       fileops_fs_t **fs;
+
+       for(fs = fs_tab; *fs; fs++) (*fs)->uninstall();
+
+       return 0;
+}
+
+INTN
+init_devices(EFI_HANDLE boot_handle)
+{
+       /* simulate driver installation */
+       install_filesystems();
+
+       /*
+        * now let's do our part
+        */
+       fileops_init();
+
+       return find_filesystems(boot_handle);
+}
+
+INTN
+close_devices(VOID)
+{
+       INTN i;
+
+       for(i=0; i < FILEOPS_FD_MAX; i++) {
+               fops_close(i);
+       }
+       free(dev_tab);
+
+       /*
+        * simulate driver removal
+        */
+       uninstall_filesystems();
+
+       return 0;
+}
+
+VOID
+print_devices(VOID)
+{
+       UINTN idx;
+       EFI_DEVICE_PATH *dp;
+       CHAR16 *str, *str2;
+
+       for(idx=0; idx< ndev; idx++) {
+               str = NULL;
+
+               dp = DevicePathFromHandle(dev_tab[idx].dev);
+               if (dp) str = DevicePathToStr(dp);
+
+               str2 = str == NULL ? L"Unknown" : str;
+
+               Print(L"%8s : %-6s : %s\n", dev_tab[idx].name,
+                               (dev_tab[idx].fops ? dev_tab[idx].fops->name: L"N/A"), str2);
+
+               if (str) FreePool(str);
+       }
+       Print(L"%d devices available for booting\n", ndev_boot);
+
+       if (boot_dev == NULL) {
+               Print(L"boot device not detected\n");
+       } else {
+               Print(L"boot device %s: %s\n", boot_dev->name,
+                               (boot_dev->fops ? boot_dev->fops->name : L"No file access"));
+       }
+}
diff --git a/fileops.h b/fileops.h
new file mode 100644 (file)
index 0000000..0150f35
--- /dev/null
+++ b/fileops.h
@@ -0,0 +1,123 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  GNU EFI is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  GNU EFI is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU EFI; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __FILEOPS_H__
+#define __FILEOPS_H__
+
+#define FILEOPS_NAME_MAXLEN    32      /* length of filesystem name */
+
+/*
+ * upper-level interface used by the bootloader
+ */
+typedef UINTN  fops_fd_t;
+
+extern EFI_STATUS fops_open(CHAR16 *name, fops_fd_t *fd);
+extern EFI_STATUS fops_read(fops_fd_t fd,VOID *buf, UINTN *size);
+extern EFI_STATUS fops_close(fops_fd_t fd);
+extern EFI_STATUS fops_infosize(fops_fd_t fd, UINT64 *size);
+extern EFI_STATUS fops_seek(fops_fd_t fd, UINT64 newpos);
+extern EFI_STATUS fops_setdefaults(CHAR16 *config, CHAR16 *kname, UINTN maxlen, CHAR16 *devpath);
+extern EFI_STATUS fops_getdefault_path(CHAR16 *path, UINTN maxlen);
+extern CHAR16     *fops_bootdev_name(VOID);
+
+
+/*
+ * fileops interface used by underlying filesystems layer
+ */
+typedef EFI_STATUS (*fops_open_t)(VOID *intf, CHAR16 *name, fops_fd_t *fd);
+typedef EFI_STATUS (*fops_read_t)(VOID *intf, fops_fd_t fd, VOID *buf, UINTN *size);
+typedef        EFI_STATUS (*fops_close_t)(VOID *intf, fops_fd_t fd);
+typedef        EFI_STATUS (*fops_infosize_t)(VOID *intf, fops_fd_t fd, UINT64 *size);
+typedef        EFI_STATUS (*fops_seek_t)(VOID *intf, fops_fd_t fd, UINT64 newpos);
+typedef        EFI_STATUS (*fops_setdefaults_t)(VOID *intf, CHAR16 *config, CHAR16 *kname, UINTN maxlen, CHAR16 *devpath);
+typedef        EFI_STATUS (*fops_getdefault_path_t)(CHAR16 *path, UINTN maxlen);
+
+typedef struct {
+       VOID *intf;     /* pointer to underlying interface */
+
+       fops_open_t             open;
+       fops_read_t             read;
+       fops_close_t            close;
+       fops_infosize_t         infosize;
+       fops_seek_t             seek;
+       fops_setdefaults_t      setdefaults;
+       fops_getdefault_path_t  getdefault_path;
+
+       EFI_HANDLE dev; /* handle on device on which proto is installed */
+       CHAR16 name[FILEOPS_NAME_MAXLEN];
+} fileops_t;
+
+/* 
+ * used to register a new filsystem
+ */
+typedef INTN (*fops_fs_glue_t)(fileops_t *this, VOID *intf);
+typedef EFI_STATUS (*fops_fs_install_t)(VOID);
+typedef EFI_STATUS (*fops_fs_uninstall_t)(VOID);
+
+typedef struct {
+       EFI_GUID                proto;     /* GUID of filesystem */
+       fops_fs_glue_t          glue;      /* glue routine */
+       fops_fs_install_t       install;   /* to go away with real EFI drivers */
+       fops_fs_uninstall_t     uninstall; /* to go away with real EFI drivers */
+} fileops_fs_t;
+
+
+/*
+ * device description
+ */
+#define FILEOPS_DEVNAME_MAXLEN 16
+
+typedef struct {
+       EFI_HANDLE              dev;
+       fileops_t               *fops;
+       CHAR16                  name[FILEOPS_DEVNAME_MAXLEN];
+} device_t;
+
+extern INTN init_devices(EFI_HANDLE boot_handle);
+extern INTN close_devices(VOID);
+extern VOID print_devices(VOID);
+extern EFI_STATUS fops_get_next_device(UINTN pidx, CHAR16 *type, UINTN maxlen, UINTN *idx, CHAR16 *name, EFI_HANDLE *dev);
+extern INTN fops_split_path(CHAR16 *path, CHAR16 *dev);
+extern EFI_STATUS fops_get_device_handle(CHAR16 *name, EFI_HANDLE *dev);
+
+/*
+ * device naming schemes
+ *
+ * Interface:
+ *     the scheme() function arguments are:
+ *             - the list of detected bootable device
+ *             - the number of entries in that table as argument
+ *
+ *     There is no expected return value. If the scheme() cannot perform
+ *     its tasks, then IT MUST NOT OVERWRITE the generic naming scheme (devXXX) that
+ *     is ALWAYS installed by default. Partial modifications are possible, although
+ *     not recommended.
+ */
+typedef struct {
+       CHAR16  *name;
+       INTN    (*install_scheme)(device_t *tab, UINTN ndev);
+} devname_scheme_t;
+
+#endif /* __FILEOPS_H__ */
diff --git a/fs/Makefile b/fs/Makefile
new file mode 100644 (file)
index 0000000..1829d96
--- /dev/null
@@ -0,0 +1,70 @@
+#
+#  Copyright (C) 2001-2003 Hewlett-Packard Co.
+#      Contributed by Stephane Eranian <eranian@hpl.hp.com>
+#
+# This file is part of the ELILO, the EFI Linux boot loader.
+#
+#  ELILO is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2, or (at your option)
+#  any later version.
+#
+#  ELILO is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with ELILO; see the file COPYING.  If not, write to the Free
+#  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+#  02111-1307, USA.
+#
+# Please check out the elilo.txt for complete documentation on how
+# to use this program.
+#
+
+include ../Make.defaults
+include ../Make.rules
+
+TOPDIR=$(CDIR)/..
+
+FILES=
+ifeq ($(CONFIG_localfs),y)
+FILES += localfs.o
+endif
+
+#
+# Ext2 is now disabled by default. See top level Makefile
+# for details
+#
+ifeq ($(CONFIG_ext2fs),y)
+FILES += ext2fs.o
+endif
+
+ifeq ($(CONFIG_netfs),y)
+FILES += netfs.o
+endif
+
+
+TARGET=fs.o
+
+all: $(TARGET)
+
+#
+# XXX: does not trigger recompile when changing filesystem selection
+#      without doing make clean.
+#
+$(TARGET): check-filesystems $(TOPDIR)/Make.defaults $(FILES)
+       $(LD) -r -o $@ $(FILES)
+
+clean:
+       $(RM) -f $(TARGET) $(FILES)
+
+check-filesystems:
+       @if [ -n "$(FILES)" ]; then \
+               exit 0; \
+       else \
+               echo "You need to define at least one filesystem in Make.defaults"; \
+               exit 1; \
+       fi
+
diff --git a/fs/ext2_fs.h b/fs/ext2_fs.h
new file mode 100644 (file)
index 0000000..a2d5458
--- /dev/null
@@ -0,0 +1,626 @@
+/*
+ *  linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ *  from
+ *
+ *  linux/include/linux/minix_fs.h
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_H
+#define _LINUX_EXT2_FS_H
+
+/*
+ * The second extended filesystem constants/structures
+ */
+
+/*
+ * Define EXT2FS_DEBUG to produce debug messages
+ */
+#undef EXT2FS_DEBUG
+
+/*
+ * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files
+ */
+#define EXT2_PREALLOCATE
+#define EXT2_DEFAULT_PREALLOC_BLOCKS   8
+
+/*
+ * The second extended file system version
+ */
+#define EXT2FS_DATE            "95/08/09"
+#define EXT2FS_VERSION         "0.5b"
+
+/*
+ * Debug code
+ */
+#ifdef EXT2FS_DEBUG
+#      define ext2_debug(f, a...)      { \
+                                       printk ("EXT2-fs DEBUG (%s, %d): %s:", \
+                                               __FILE__, __LINE__, __FUNCTION__); \
+                                       printk (f, ## a); \
+                                       }
+#else
+#      define ext2_debug(f, a...)      /**/
+#endif
+
+/*
+ * Special inodes numbers
+ */
+#define        EXT2_BAD_INO             1      /* Bad blocks inode */
+#define EXT2_ROOT_INO           2      /* Root inode */
+#define EXT2_ACL_IDX_INO        3      /* ACL inode */
+#define EXT2_ACL_DATA_INO       4      /* ACL inode */
+#define EXT2_BOOT_LOADER_INO    5      /* Boot loader inode */
+#define EXT2_UNDEL_DIR_INO      6      /* Undelete directory inode */
+
+/* First non-reserved inode for old ext2 filesystems */
+#define EXT2_GOOD_OLD_FIRST_INO        11
+
+/*
+ * The second extended file system magic number
+ */
+#define EXT2_SUPER_MAGIC       0xEF53
+
+/*
+ * Maximal count of links to a file
+ */
+#define EXT2_LINK_MAX          32000
+
+/*
+ * Macro-instructions used to manage several block sizes
+ */
+#define EXT2_MIN_BLOCK_SIZE            1024
+#define        EXT2_MAX_BLOCK_SIZE             4096
+#define EXT2_MIN_BLOCK_LOG_SIZE                  10
+#ifdef __KERNEL__
+# define EXT2_BLOCK_SIZE(s)            ((s)->s_blocksize)
+#else
+# define EXT2_BLOCK_SIZE(s)            (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+#endif
+#define EXT2_ACLE_PER_BLOCK(s)         (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry))
+#define        EXT2_ADDR_PER_BLOCK(s)          (EXT2_BLOCK_SIZE(s) / sizeof (__u32))
+#ifdef __KERNEL__
+# define EXT2_BLOCK_SIZE_BITS(s)       ((s)->s_blocksize_bits)
+#else
+# define EXT2_BLOCK_SIZE_BITS(s)       ((s)->s_log_block_size + 10)
+#endif
+#ifdef __KERNEL__
+#define        EXT2_ADDR_PER_BLOCK_BITS(s)     ((s)->u.ext2_sb.s_addr_per_block_bits)
+#define EXT2_INODE_SIZE(s)             ((s)->u.ext2_sb.s_inode_size)
+#define EXT2_FIRST_INO(s)              ((s)->u.ext2_sb.s_first_ino)
+#else
+#define EXT2_INODE_SIZE(s)     (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+                                EXT2_GOOD_OLD_INODE_SIZE : \
+                                (s)->s_inode_size)
+#define EXT2_FIRST_INO(s)      (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+                                EXT2_GOOD_OLD_FIRST_INO : \
+                                (s)->s_first_ino)
+#endif
+
+/*
+ * Macro-instructions used to manage fragments
+ */
+#define EXT2_MIN_FRAG_SIZE             1024
+#define        EXT2_MAX_FRAG_SIZE              4096
+#define EXT2_MIN_FRAG_LOG_SIZE           10
+#ifdef __KERNEL__
+# define EXT2_FRAG_SIZE(s)             ((s)->u.ext2_sb.s_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s)       ((s)->u.ext2_sb.s_frags_per_block)
+#else
+# define EXT2_FRAG_SIZE(s)             (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s)       (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
+#endif
+
+/*
+ * ACL structures
+ */
+struct ext2_acl_header /* Header of Access Control Lists */
+{
+       __u32   aclh_size;
+       __u32   aclh_file_count;
+       __u32   aclh_acle_count;
+       __u32   aclh_first_acle;
+};
+
+struct ext2_acl_entry  /* Access Control List Entry */
+{
+       __u32   acle_size;
+       __u16   acle_perms;     /* Access permissions */
+       __u16   acle_type;      /* Type of entry */
+       __u16   acle_tag;       /* User or group identity */
+       __u16   acle_pad1;
+       __u32   acle_next;      /* Pointer on next entry for the */
+                                       /* same inode or on next free entry */
+};
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct ext2_group_desc
+{
+       __u32   bg_block_bitmap;                /* Blocks bitmap block */
+       __u32   bg_inode_bitmap;                /* Inodes bitmap block */
+       __u32   bg_inode_table;         /* Inodes table block */
+       __u16   bg_free_blocks_count;   /* Free blocks count */
+       __u16   bg_free_inodes_count;   /* Free inodes count */
+       __u16   bg_used_dirs_count;     /* Directories count */
+       __u16   bg_pad;
+       __u32   bg_reserved[3];
+};
+
+/*
+ * Macro-instructions used to manage group descriptors
+ */
+#ifdef __KERNEL__
+# define EXT2_BLOCKS_PER_GROUP(s)      ((s)->u.ext2_sb.s_blocks_per_group)
+# define EXT2_DESC_PER_BLOCK(s)                ((s)->u.ext2_sb.s_desc_per_block)
+# define EXT2_INODES_PER_GROUP(s)      ((s)->u.ext2_sb.s_inodes_per_group)
+# define EXT2_DESC_PER_BLOCK_BITS(s)   ((s)->u.ext2_sb.s_desc_per_block_bits)
+#else
+# define EXT2_BLOCKS_PER_GROUP(s)      ((s)->s_blocks_per_group)
+# define EXT2_DESC_PER_BLOCK(s)                (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
+# define EXT2_INODES_PER_GROUP(s)      ((s)->s_inodes_per_group)
+#endif
+
+/*
+ * Constants relative to the data blocks
+ */
+#define        EXT2_NDIR_BLOCKS                12
+#define        EXT2_IND_BLOCK                  EXT2_NDIR_BLOCKS
+#define        EXT2_DIND_BLOCK                 (EXT2_IND_BLOCK + 1)
+#define        EXT2_TIND_BLOCK                 (EXT2_DIND_BLOCK + 1)
+#define        EXT2_N_BLOCKS                   (EXT2_TIND_BLOCK + 1)
+
+/*
+ * Inode flags
+ */
+#define        EXT2_SECRM_FL                   0x00000001 /* Secure deletion */
+#define        EXT2_UNRM_FL                    0x00000002 /* Undelete */
+#define        EXT2_COMPR_FL                   0x00000004 /* Compress file */
+#define EXT2_SYNC_FL                   0x00000008 /* Synchronous updates */
+#define EXT2_IMMUTABLE_FL              0x00000010 /* Immutable file */
+#define EXT2_APPEND_FL                 0x00000020 /* writes to file may only append */
+#define EXT2_NODUMP_FL                 0x00000040 /* do not dump file */
+#define EXT2_NOATIME_FL                        0x00000080 /* do not update atime */
+/* Reserved for compression usage... */
+#define EXT2_DIRTY_FL                  0x00000100
+#define EXT2_COMPRBLK_FL               0x00000200 /* One or more compressed clusters */
+#define EXT2_NOCOMP_FL                 0x00000400 /* Don't compress */
+#define EXT2_ECOMPR_FL                 0x00000800 /* Compression error */
+/* End compression flags --- maybe not all used */     
+#define EXT2_BTREE_FL                  0x00001000 /* btree format dir */
+#define EXT2_RESERVED_FL               0x80000000 /* reserved for ext2 lib */
+
+#define EXT2_FL_USER_VISIBLE           0x00001FFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE                0x000000FF /* User modifiable flags */
+
+/*
+ * ioctl commands
+ */
+#define        EXT2_IOC_GETFLAGS               _IOR('f', 1, long)
+#define        EXT2_IOC_SETFLAGS               _IOW('f', 2, long)
+#define        EXT2_IOC_GETVERSION             _IOR('v', 1, long)
+#define        EXT2_IOC_SETVERSION             _IOW('v', 2, long)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext2_inode {
+       __u16   i_mode;         /* File mode */
+       __u16   i_uid;          /* Low 16 bits of Owner Uid */
+       __u32   i_size;         /* Size in bytes */
+       __u32   i_atime;        /* Access time */
+       __u32   i_ctime;        /* Creation time */
+       __u32   i_mtime;        /* Modification time */
+       __u32   i_dtime;        /* Deletion Time */
+       __u16   i_gid;          /* Low 16 bits of Group Id */
+       __u16   i_links_count;  /* Links count */
+       __u32   i_blocks;       /* Blocks count */
+       __u32   i_flags;        /* File flags */
+       union {
+               struct {
+                       __u32  l_i_reserved1;
+               } linux1;
+               struct {
+                       __u32  h_i_translator;
+               } hurd1;
+               struct {
+                       __u32  m_i_reserved1;
+               } masix1;
+       } osd1;                         /* OS dependent 1 */
+       __u32   i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+       __u32   i_generation;   /* File version (for NFS) */
+       __u32   i_file_acl;     /* File ACL */
+       __u32   i_dir_acl;      /* Directory ACL */
+       __u32   i_faddr;        /* Fragment address */
+       union {
+               struct {
+                       __u8    l_i_frag;       /* Fragment number */
+                       __u8    l_i_fsize;      /* Fragment size */
+                       __u16   i_pad1;
+                       __u16   l_i_uid_high;   /* these 2 fields    */
+                       __u16   l_i_gid_high;   /* were reserved2[0] */
+                       __u32   l_i_reserved2;
+               } linux2;
+               struct {
+                       __u8    h_i_frag;       /* Fragment number */
+                       __u8    h_i_fsize;      /* Fragment size */
+                       __u16   h_i_mode_high;
+                       __u16   h_i_uid_high;
+                       __u16   h_i_gid_high;
+                       __u32   h_i_author;
+               } hurd2;
+               struct {
+                       __u8    m_i_frag;       /* Fragment number */
+                       __u8    m_i_fsize;      /* Fragment size */
+                       __u16   m_pad1;
+                       __u32   m_i_reserved2[2];
+               } masix2;
+       } osd2;                         /* OS dependent 2 */
+};
+
+#define i_size_high    i_dir_acl
+
+#if defined(__KERNEL__) || defined(__linux__)
+#define i_reserved1    osd1.linux1.l_i_reserved1
+#define i_frag         osd2.linux2.l_i_frag
+#define i_fsize                osd2.linux2.l_i_fsize
+#define i_uid_low      i_uid
+#define i_gid_low      i_gid
+#define i_uid_high     osd2.linux2.l_i_uid_high
+#define i_gid_high     osd2.linux2.l_i_gid_high
+#define i_reserved2    osd2.linux2.l_i_reserved2
+#endif
+
+#ifdef __hurd__
+#define i_translator   osd1.hurd1.h_i_translator
+#define i_frag         osd2.hurd2.h_i_frag;
+#define i_fsize                osd2.hurd2.h_i_fsize;
+#define i_uid_high     osd2.hurd2.h_i_uid_high
+#define i_gid_high     osd2.hurd2.h_i_gid_high
+#define i_author       osd2.hurd2.h_i_author
+#endif
+
+#ifdef __masix__
+#define i_reserved1    osd1.masix1.m_i_reserved1
+#define i_frag         osd2.masix2.m_i_frag
+#define i_fsize                osd2.masix2.m_i_fsize
+#define i_reserved2    osd2.masix2.m_i_reserved2
+#endif
+
+/*
+ * File system states
+ */
+#define        EXT2_VALID_FS                   0x0001  /* Unmounted cleanly */
+#define        EXT2_ERROR_FS                   0x0002  /* Errors detected */
+
+/*
+ * Mount flags
+ */
+#define EXT2_MOUNT_CHECK               0x0001  /* Do mount-time checks */
+#define EXT2_MOUNT_GRPID               0x0004  /* Create files with directory's group */
+#define EXT2_MOUNT_DEBUG               0x0008  /* Some debugging messages */
+#define EXT2_MOUNT_ERRORS_CONT         0x0010  /* Continue on errors */
+#define EXT2_MOUNT_ERRORS_RO           0x0020  /* Remount fs ro on errors */
+#define EXT2_MOUNT_ERRORS_PANIC                0x0040  /* Panic on errors */
+#define EXT2_MOUNT_MINIX_DF            0x0080  /* Mimics the Minix statfs */
+#define EXT2_MOUNT_NO_UID32            0x0200  /* Disable 32-bit UIDs */
+
+#define clear_opt(o, opt)              o &= ~EXT2_MOUNT_##opt
+#define set_opt(o, opt)                        o |= EXT2_MOUNT_##opt
+#define test_opt(sb, opt)              ((sb)->u.ext2_sb.s_mount_opt & \
+                                        EXT2_MOUNT_##opt)
+/*
+ * Maximal mount counts between two filesystem checks
+ */
+#define EXT2_DFL_MAX_MNT_COUNT         20      /* Allow 20 mounts */
+#define EXT2_DFL_CHECKINTERVAL         0       /* Don't use interval check */
+
+/*
+ * Behaviour when detecting errors
+ */
+#define EXT2_ERRORS_CONTINUE           1       /* Continue execution */
+#define EXT2_ERRORS_RO                 2       /* Remount fs read-only */
+#define EXT2_ERRORS_PANIC              3       /* Panic */
+#define EXT2_ERRORS_DEFAULT            EXT2_ERRORS_CONTINUE
+
+/*
+ * Structure of the super block
+ */
+struct ext2_super_block {
+       __u32   s_inodes_count;         /* Inodes count */
+       __u32   s_blocks_count;         /* Blocks count */
+       __u32   s_r_blocks_count;       /* Reserved blocks count */
+       __u32   s_free_blocks_count;    /* Free blocks count */
+       __u32   s_free_inodes_count;    /* Free inodes count */
+       __u32   s_first_data_block;     /* First Data Block */
+       __u32   s_log_block_size;       /* Block size */
+       __s32   s_log_frag_size;        /* Fragment size */
+       __u32   s_blocks_per_group;     /* # Blocks per group */
+       __u32   s_frags_per_group;      /* # Fragments per group */
+       __u32   s_inodes_per_group;     /* # Inodes per group */
+       __u32   s_mtime;                /* Mount time */
+       __u32   s_wtime;                /* Write time */
+       __u16   s_mnt_count;            /* Mount count */
+       __s16   s_max_mnt_count;        /* Maximal mount count */
+       __u16   s_magic;                /* Magic signature */
+       __u16   s_state;                /* File system state */
+       __u16   s_errors;               /* Behaviour when detecting errors */
+       __u16   s_minor_rev_level;      /* minor revision level */
+       __u32   s_lastcheck;            /* time of last check */
+       __u32   s_checkinterval;        /* max. time between checks */
+       __u32   s_creator_os;           /* OS */
+       __u32   s_rev_level;            /* Revision level */
+       __u16   s_def_resuid;           /* Default uid for reserved blocks */
+       __u16   s_def_resgid;           /* Default gid for reserved blocks */
+       /*
+        * These fields are for EXT2_DYNAMIC_REV superblocks only.
+        *
+        * Note: the difference between the compatible feature set and
+        * the incompatible feature set is that if there is a bit set
+        * in the incompatible feature set that the kernel doesn't
+        * know about, it should refuse to mount the filesystem.
+        * 
+        * e2fsck's requirements are more strict; if it doesn't know
+        * about a feature in either the compatible or incompatible
+        * feature set, it must abort and not try to meddle with
+        * things it doesn't understand...
+        */
+       __u32   s_first_ino;            /* First non-reserved inode */
+       __u16   s_inode_size;           /* size of inode structure */
+       __u16   s_block_group_nr;       /* block group # of this superblock */
+       __u32   s_feature_compat;       /* compatible feature set */
+       __u32   s_feature_incompat;     /* incompatible feature set */
+       __u32   s_feature_ro_compat;    /* readonly-compatible feature set */
+       __u8    s_uuid[16];             /* 128-bit uuid for volume */
+       char    s_volume_name[16];      /* volume name */
+       char    s_last_mounted[64];     /* directory where last mounted */
+       __u32   s_algorithm_usage_bitmap; /* For compression */
+       /*
+        * Performance hints.  Directory preallocation should only
+        * happen if the EXT2_COMPAT_PREALLOC flag is on.
+        */
+       __u8    s_prealloc_blocks;      /* Nr of blocks to try to preallocate*/
+       __u8    s_prealloc_dir_blocks;  /* Nr to preallocate for dirs */
+       __u16   s_padding1;
+       __u32   s_reserved[204];        /* Padding to the end of the block */
+};
+
+#ifdef __KERNEL__
+#define EXT2_SB(sb)    (&((sb)->u.ext2_sb))
+#else
+/* Assume that user mode programs are passing in an ext2fs superblock, not
+ * a kernel struct super_block.  This will allow us to call the feature-test
+ * macros from user land. */
+#define EXT2_SB(sb)    (sb)
+#endif
+
+/*
+ * Codes for operating systems
+ */
+#define EXT2_OS_LINUX          0
+#define EXT2_OS_HURD           1
+#define EXT2_OS_MASIX          2
+#define EXT2_OS_FREEBSD                3
+#define EXT2_OS_LITES          4
+
+/*
+ * Revision levels
+ */
+#define EXT2_GOOD_OLD_REV      0       /* The good old (original) format */
+#define EXT2_DYNAMIC_REV       1       /* V2 format w/ dynamic inode sizes */
+
+#define EXT2_CURRENT_REV       EXT2_GOOD_OLD_REV
+#define EXT2_MAX_SUPP_REV      EXT2_DYNAMIC_REV
+
+#define EXT2_GOOD_OLD_INODE_SIZE 128
+
+/*
+ * Feature set definitions
+ */
+
+#define EXT2_HAS_COMPAT_FEATURE(sb,mask)                       \
+       ( EXT2_SB(sb)->s_es->s_feature_compat & cpu_to_le32(mask) )
+#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask)                    \
+       ( EXT2_SB(sb)->s_es->s_feature_ro_compat & cpu_to_le32(mask) )
+#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask)                     \
+       ( EXT2_SB(sb)->s_es->s_feature_incompat & cpu_to_le32(mask) )
+#define EXT2_SET_COMPAT_FEATURE(sb,mask)                       \
+       EXT2_SB(sb)->s_es->s_feature_compat |= cpu_to_le32(mask)
+#define EXT2_SET_RO_COMPAT_FEATURE(sb,mask)                    \
+       EXT2_SB(sb)->s_es->s_feature_ro_compat |= cpu_to_le32(mask)
+#define EXT2_SET_INCOMPAT_FEATURE(sb,mask)                     \
+       EXT2_SB(sb)->s_es->s_feature_incompat |= cpu_to_le32(mask)
+#define EXT2_CLEAR_COMPAT_FEATURE(sb,mask)                     \
+       EXT2_SB(sb)->s_es->s_feature_compat &= ~cpu_to_le32(mask)
+#define EXT2_CLEAR_RO_COMPAT_FEATURE(sb,mask)                  \
+       EXT2_SB(sb)->s_es->s_feature_ro_compat &= ~cpu_to_le32(mask)
+#define EXT2_CLEAR_INCOMPAT_FEATURE(sb,mask)                   \
+       EXT2_SB(sb)->s_es->s_feature_incompat &= ~cpu_to_le32(mask)
+
+#define EXT2_FEATURE_COMPAT_DIR_PREALLOC       0x0001
+
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER    0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE      0x0002
+#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR       0x0004
+
+#define EXT2_FEATURE_INCOMPAT_COMPRESSION      0x0001
+#define EXT2_FEATURE_INCOMPAT_FILETYPE         0x0002
+
+#define EXT2_FEATURE_COMPAT_SUPP       0
+#define EXT2_FEATURE_INCOMPAT_SUPP     EXT2_FEATURE_INCOMPAT_FILETYPE
+#define EXT2_FEATURE_RO_COMPAT_SUPP    (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+                                        EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+                                        EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+
+/*
+ * Default values for user and/or group using reserved blocks
+ */
+#define        EXT2_DEF_RESUID         0
+#define        EXT2_DEF_RESGID         0
+
+/*
+ * Structure of a directory entry
+ */
+#define EXT2_NAME_LEN 255
+
+struct ext2_dir_entry {
+       __u32   inode;                  /* Inode number */
+       __u16   rec_len;                /* Directory entry length */
+       __u16   name_len;               /* Name length */
+       char    name[EXT2_NAME_LEN];    /* File name */
+};
+
+/*
+ * The new version of the directory entry.  Since EXT2 structures are
+ * stored in intel byte order, and the name_len field could never be
+ * bigger than 255 chars, it's safe to reclaim the extra byte for the
+ * file_type field.
+ */
+struct ext2_dir_entry_2 {
+       __u32   inode;                  /* Inode number */
+       __u16   rec_len;                /* Directory entry length */
+       __u8    name_len;               /* Name length */
+       __u8    file_type;
+       char    name[EXT2_NAME_LEN];    /* File name */
+};
+
+/*
+ * Ext2 directory file types.  Only the low 3 bits are used.  The
+ * other bits are reserved for now.
+ */
+#define EXT2_FT_UNKNOWN                0
+#define EXT2_FT_REG_FILE       1
+#define EXT2_FT_DIR            2
+#define EXT2_FT_CHRDEV         3
+#define EXT2_FT_BLKDEV                 4
+#define EXT2_FT_FIFO           5
+#define EXT2_FT_SOCK           6
+#define EXT2_FT_SYMLINK                7
+
+#define EXT2_FT_MAX            8
+
+/*
+ * EXT2_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT2_DIR_PAD                   4
+#define EXT2_DIR_ROUND                         (EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(name_len)     (((name_len) + 8 + EXT2_DIR_ROUND) & \
+                                        ~EXT2_DIR_ROUND)
+
+#ifdef __KERNEL__
+/*
+ * Function prototypes
+ */
+
+/*
+ * Ok, these declarations are also in <linux/kernel.h> but none of the
+ * ext2 source programs needs to include it so they are duplicated here.
+ */
+# define NORET_TYPE    /**/
+# define ATTRIB_NORET  __attribute__((noreturn))
+# define NORET_AND     noreturn,
+
+/* acl.c */
+extern int ext2_permission (struct inode *, int);
+
+/* balloc.c */
+extern int ext2_bg_has_super(struct super_block *sb, int group);
+extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
+extern int ext2_new_block (const struct inode *, unsigned long,
+                          __u32 *, __u32 *, int *);
+extern void ext2_free_blocks (const struct inode *, unsigned long,
+                             unsigned long);
+extern unsigned long ext2_count_free_blocks (struct super_block *);
+extern void ext2_check_blocks_bitmap (struct super_block *);
+extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
+                                                   unsigned int block_group,
+                                                   struct buffer_head ** bh);
+
+/* bitmap.c */
+extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
+
+/* dir.c */
+extern int ext2_check_dir_entry (const char *, struct inode *,
+                                struct ext2_dir_entry_2 *, struct buffer_head *,
+                                unsigned long);
+
+/* file.c */
+extern int ext2_read (struct inode *, struct file *, char *, int);
+extern int ext2_write (struct inode *, struct file *, char *, int);
+
+/* fsync.c */
+extern int ext2_sync_file (struct file *, struct dentry *, int);
+extern int ext2_fsync_inode (struct inode *, int);
+
+/* ialloc.c */
+extern struct inode * ext2_new_inode (const struct inode *, int);
+extern void ext2_free_inode (struct inode *);
+extern unsigned long ext2_count_free_inodes (struct super_block *);
+extern void ext2_check_inodes_bitmap (struct super_block *);
+
+/* inode.c */
+
+extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *);
+extern struct buffer_head * ext2_bread (struct inode *, int, int, int *);
+
+extern void ext2_read_inode (struct inode *);
+extern void ext2_write_inode (struct inode *, int);
+extern void ext2_put_inode (struct inode *);
+extern void ext2_delete_inode (struct inode *);
+extern int ext2_sync_inode (struct inode *);
+extern void ext2_discard_prealloc (struct inode *);
+
+/* ioctl.c */
+extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
+                      unsigned long);
+
+/* namei.c */
+extern struct inode_operations ext2_dir_inode_operations;
+
+/* super.c */
+extern void ext2_error (struct super_block *, const char *, const char *, ...)
+       __attribute__ ((format (printf, 3, 4)));
+extern NORET_TYPE void ext2_panic (struct super_block *, const char *,
+                                  const char *, ...)
+       __attribute__ ((NORET_AND format (printf, 3, 4)));
+extern void ext2_warning (struct super_block *, const char *, const char *, ...)
+       __attribute__ ((format (printf, 3, 4)));
+extern void ext2_update_dynamic_rev (struct super_block *sb);
+extern void ext2_put_super (struct super_block *);
+extern void ext2_write_super (struct super_block *);
+extern int ext2_remount (struct super_block *, int *, char *);
+extern struct super_block * ext2_read_super (struct super_block *,void *,int);
+extern int ext2_statfs (struct super_block *, struct statfs *);
+
+/* truncate.c */
+extern void ext2_truncate (struct inode *);
+
+/*
+ * Inodes and files operations
+ */
+
+/* dir.c */
+extern struct file_operations ext2_dir_operations;
+
+/* file.c */
+extern struct inode_operations ext2_file_inode_operations;
+extern struct file_operations ext2_file_operations;
+
+/* symlink.c */
+extern struct inode_operations ext2_fast_symlink_inode_operations;
+
+extern struct address_space_operations ext2_aops;
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_EXT2_FS_H */
diff --git a/fs/ext2_fs_i.h b/fs/ext2_fs_i.h
new file mode 100644 (file)
index 0000000..72bcd5c
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  linux/include/linux/ext2_fs_i.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ *  from
+ *
+ *  linux/include/linux/minix_fs_i.h
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_I
+#define _LINUX_EXT2_FS_I
+
+/*
+ * second extended file system inode data in memory
+ */
+struct ext2_inode_info {
+       __u32   i_data[15];
+       __u32   i_flags;
+       __u32   i_faddr;
+       __u8    i_frag_no;
+       __u8    i_frag_size;
+       __u16   i_osync;
+       __u32   i_file_acl;
+       __u32   i_dir_acl;
+       __u32   i_dtime;
+       __u32   not_used_1;     /* FIX: not used/ 2.2 placeholder */
+       __u32   i_block_group;
+       __u32   i_next_alloc_block;
+       __u32   i_next_alloc_goal;
+       __u32   i_prealloc_block;
+       __u32   i_prealloc_count;
+       __u32   i_high_size;
+       int     i_new_inode:1;  /* Is a freshly allocated inode */
+};
+
+#endif /* _LINUX_EXT2_FS_I */
diff --git a/fs/ext2_fs_sb.h b/fs/ext2_fs_sb.h
new file mode 100644 (file)
index 0000000..2e72dfb
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *  linux/include/linux/ext2_fs_sb.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ *  from
+ *
+ *  linux/include/linux/minix_fs_sb.h
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_SB
+#define _LINUX_EXT2_FS_SB
+
+/*
+ * The following is not needed anymore since the descriptors buffer
+ * heads are now dynamically allocated
+ */
+/* #define EXT2_MAX_GROUP_DESC 8 */
+
+#define EXT2_MAX_GROUP_LOADED  8
+
+/*
+ * second extended-fs super-block data in memory
+ */
+struct ext2_sb_info {
+       unsigned long s_frag_size;      /* Size of a fragment in bytes */
+       unsigned long s_frags_per_block;/* Number of fragments per block */
+       unsigned long s_inodes_per_block;/* Number of inodes per block */
+       unsigned long s_frags_per_group;/* Number of fragments in a group */
+       unsigned long s_blocks_per_group;/* Number of blocks in a group */
+       unsigned long s_inodes_per_group;/* Number of inodes in a group */
+       unsigned long s_itb_per_group;  /* Number of inode table blocks per group */
+       unsigned long s_gdb_count;      /* Number of group descriptor blocks */
+       unsigned long s_desc_per_block; /* Number of group descriptors per block */
+       unsigned long s_groups_count;   /* Number of groups in the fs */
+       struct buffer_head * s_sbh;     /* Buffer containing the super block */
+       struct ext2_super_block * s_es; /* Pointer to the super block in the buffer */
+       struct buffer_head ** s_group_desc;
+       unsigned short s_loaded_inode_bitmaps;
+       unsigned short s_loaded_block_bitmaps;
+       unsigned long s_inode_bitmap_number[EXT2_MAX_GROUP_LOADED];
+       struct buffer_head * s_inode_bitmap[EXT2_MAX_GROUP_LOADED];
+       unsigned long s_block_bitmap_number[EXT2_MAX_GROUP_LOADED];
+       struct buffer_head * s_block_bitmap[EXT2_MAX_GROUP_LOADED];
+       unsigned long  s_mount_opt;
+       uid_t s_resuid;
+       gid_t s_resgid;
+       unsigned short s_mount_state;
+       unsigned short s_pad;
+       int s_addr_per_block_bits;
+       int s_desc_per_block_bits;
+       int s_inode_size;
+       int s_first_ino;
+};
+
+#endif /* _LINUX_EXT2_FS_SB */
diff --git a/fs/ext2_private.h b/fs/ext2_private.h
new file mode 100644 (file)
index 0000000..c7c44b1
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __FS_EXT2FS_PRIVATE_H__
+#define __FS_EXT2FS_PRIVATE_H__
+
+/*
+ * Implement the Linux kernel internal data types
+ * used by ext2
+ */
+typedef UINT32 __u32;
+typedef INT32  __s32;
+typedef UINT16 __u16;
+typedef INT16  __s16;
+typedef UINT8   __u8;
+typedef INT8    __s8;
+typedef UINT32  uid_t; 
+typedef UINT32  gid_t;
+
+/*
+ * Get some constant from linux/stat.h
+ */
+#define S_IFMT  00170000
+#define S_IFSOCK 0140000
+#define S_IFLNK         0120000
+#define S_IFREG  0100000
+#define S_IFBLK  0060000
+#define S_IFDIR  0040000
+#define S_IFCHR  0020000
+#define S_IFIFO  0010000
+#define S_ISUID  0004000
+#define S_ISGID  0002000
+#define S_ISVTX  0001000
+
+#define S_ISLNK(m)     (((m) & S_IFMT) == S_IFLNK)
+#define S_ISREG(m)     (((m) & S_IFMT) == S_IFREG)
+#define S_ISDIR(m)     (((m) & S_IFMT) == S_IFDIR)
+#define S_ISCHR(m)     (((m) & S_IFMT) == S_IFCHR)
+#define S_ISBLK(m)     (((m) & S_IFMT) == S_IFBLK)
+#define S_ISFIFO(m)    (((m) & S_IFMT) == S_IFIFO)
+#define S_ISSOCK(m)    (((m) & S_IFMT) == S_IFSOCK)
+
+
+#include "fs/ext2_fs.h"
+#include "fs/ext2_fs_sb.h"
+#include "fs/ext2_fs_i.h"
+
+#endif /* __FS_EXT2FS_PRIVATE_H__*/
diff --git a/fs/ext2fs.c b/fs/ext2fs.c
new file mode 100644 (file)
index 0000000..4eaea27
--- /dev/null
@@ -0,0 +1,1004 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ *
+ * The ext2 code in this file is derived from aboot-0.7 (the Linux/Alpha
+ * bootloader) and credits are attributed to:
+ *
+ * This file has been ported from the DEC 32-bit Linux version
+ * by David Mosberger (davidm@cs.arizona.edu).
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+
+#define FS_NAME L"ext2fs"
+
+#ifdef PATH_MAX
+#error You must have included some Linux header file by error
+#endif
+
+#define PATH_MAX               4095
+#define EXT2FS_PATH_MAXLEN     PATH_MAX
+
+#include "fs/ext2fs.h"
+#include "fs/ext2_private.h"
+
+/*
+ * should we decide to spin off ext2, let's say to a boottime driver
+ * we would have to change this
+ */
+#define EXT2FS_MEMTYPE EfiLoaderData
+
+/* ext2 file size is __u32 */
+#define EXT2_FILESIZE_MAX      (0x100000000UL)
+
+/* 
+ * Number of simultaneous open files. This needs to be high because
+ * directories are kept open while traversing long path names.
+ */
+#define MAX_OPEN_FILES         32 
+
+typedef struct inode_table_entry {
+       struct  ext2_inode      inode;
+       int                     inumber;
+       int                     free;
+       unsigned short          old_mode;
+
+       UINT32                  pos;    /* current position in file ext2 uses __u32 !*/
+} inode_entry_t;
+
+
+typedef struct {
+       struct ext2_super_block sb;
+       struct ext2_group_desc *gds;
+       struct ext2_inode *root_inode;
+       int ngroups;
+       int directlim;                  /* Maximum direct blkno */
+       int ind1lim;                    /* Maximum single-indir blkno */
+       int ind2lim;                    /* Maximum double-indir blkno */
+       int ptrs_per_blk;               /* ptrs/indirect block */
+       CHAR8 blkbuf[EXT2_MAX_BLOCK_SIZE];
+       int cached_iblkno;
+       CHAR8 iblkbuf[EXT2_MAX_BLOCK_SIZE];
+       int cached_diblkno;
+       CHAR8 diblkbuf[EXT2_MAX_BLOCK_SIZE];
+       long blocksize;
+
+
+       inode_entry_t inode_table[MAX_OPEN_FILES];
+
+       /* fields added to fit the protocol construct */
+       EFI_BLOCK_IO *blkio;
+       UINT32       mediaid;
+       EFI_HANDLE   dev;
+} ext2fs_priv_state_t;
+
+
+typedef union {
+       ext2fs_interface_t pub_intf;
+       struct {
+               ext2fs_interface_t  pub_intf;
+               ext2fs_priv_state_t priv_data;
+       } ext2fs_priv;
+} ext2fs_t;
+
+#define FS_PRIVATE(n)  (&(((ext2fs_t *)n)->ext2fs_priv.priv_data))
+
+typedef union {
+       EFI_HANDLE *dev;
+       ext2fs_t  *intf;
+} dev_tab_t;
+
+static dev_tab_t *dev_tab;     /* holds all devices we found */
+static UINTN ndev;             /* how many entries in dev_tab */
+
+
+static EFI_GUID Ext2FsProtocol = EXT2FS_PROTOCOL;
+
+static INTN
+read_bytes(EFI_BLOCK_IO *blkio, UINT32 mediaid, UINTN offset, VOID *addr, UINTN size)
+{
+       EFI_STATUS status;
+       UINT8 *buffer;
+       UINTN count, buffer_size;
+       EFI_LBA base;
+       INTN ret  = EFI_INVALID_PARAMETER;
+
+       base        = offset / blkio->Media->BlockSize;
+       count       = (size + blkio->Media->BlockSize -1) / blkio->Media->BlockSize;
+       buffer_size = count * blkio->Media->BlockSize;
+
+       DBG_PRT((L"readblock(%x, %d,%d) base=%d count=%d", blkio, offset, size, base, count));
+
+       /*
+        * slow but avoid large buffer on the stack
+        */
+       buffer = (UINT8 *)alloc(buffer_size, EfiLoaderData);
+       if (buffer == NULL) {
+               ERR_PRT((L"cannot allocate ext2fs buffer size=%d", buffer_size));
+               return ret;
+       }
+
+       DBG_PRT((L"readblock(%x, %d, %d, %d, %x)", blkio, mediaid, base, buffer_size, buffer));
+
+       status = blkio->ReadBlocks(blkio, mediaid, base, buffer_size, buffer); 
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"readblock(%d,%d)=%r", base, buffer_size, status));
+               goto error;
+       }
+
+       DBG_PRT((L"readblock(%d,%d)->%r", offset, buffer_size, status));
+
+       Memcpy(addr, buffer+(offset-(base*blkio->Media->BlockSize)), size);
+
+       ret = 0;
+
+error:
+       free(buffer);
+
+       return ret;
+}
+
+/*
+ * Read the specified inode from the disk and return it to the user.
+ * Returns NULL if the inode can't be read...
+ */
+static struct ext2_inode *
+ext2_iget(ext2fs_priv_state_t *e2fs, int ino)
+{
+       int i;
+       struct ext2_inode *ip;
+       struct inode_table_entry *itp = 0;
+       int group;
+       long offset;
+
+       ip = 0;
+       for (i = 0; i < MAX_OPEN_FILES; i++) {
+               DBG_PRT((L"ext2_iget: looping, entry %d inode %d free %d",
+                      i, e2fs->inode_table[i].inumber, e2fs->inode_table[i].free));
+               if (e2fs->inode_table[i].free) {
+                       itp = &e2fs->inode_table[i];
+                       ip = &itp->inode;
+                       break;
+               }
+       }
+       if (!ip) {
+               ERR_PRT((L"ext2_iget: no free inodes"));
+               return NULL;
+       }
+
+
+       group = (ino-1) / e2fs->sb.s_inodes_per_group;
+
+       DBG_PRT((L" itp-inode_table=%d bg_inode_table=%d  group=%d ino=%d\n", (UINTN)(itp-e2fs->inode_table),
+                       (UINTN)(e2fs->gds[group].bg_inode_table), (UINTN)group, (UINTN)ino));
+
+       offset = ((long) e2fs->gds[group].bg_inode_table * e2fs->blocksize)
+               + (((ino - 1) % EXT2_INODES_PER_GROUP(&e2fs->sb)) * EXT2_INODE_SIZE(&e2fs->sb));
+       
+       DBG_PRT((L"ext2_iget: reading %d bytes at offset %d"
+              " ((%d * %d) + ((%d) %% %d) * %d) "
+              "(inode %d -> table %d)", 
+              sizeof(struct ext2_inode), 
+              (UINTN)offset,
+              (UINTN)e2fs->gds[group].bg_inode_table, (UINTN)e2fs->blocksize,
+              (UINTN)(ino - 1), (UINTN)EXT2_INODES_PER_GROUP(&e2fs->sb), EXT2_INODE_SIZE(&e2fs->sb),
+              (UINTN)ino, (UINTN) (itp - e2fs->inode_table)));
+
+       if (read_bytes(e2fs->blkio, e2fs->mediaid, offset, ip, sizeof(struct ext2_inode))) {
+               ERR_PRT((L"ext2_iget: read error"));
+               return NULL;
+       }
+       
+       DBG_PRT((L"mode=%x uid=%d size=%d gid=%d links=%d flags=%d",
+                                       (UINTN)ip->i_mode,
+                                       (UINTN)ip->i_uid,
+                                       (UINTN)ip->i_size,
+                                       (UINTN)ip->i_gid,
+                                       (UINTN)ip->i_flags));
+
+       itp->free = 0;
+       itp->inumber = ino;
+       itp->old_mode = ip->i_mode;
+
+       return ip;
+}
+
+
+/*
+ * Release our hold on an inode.  Since this is a read-only application,
+ * don't worry about putting back any changes...
+ */
+static void 
+ext2_iput(ext2fs_priv_state_t *e2fs, struct ext2_inode *ip)
+{
+       struct inode_table_entry *itp;
+
+       /* Find and free the inode table slot we used... */
+       itp = (struct inode_table_entry *)ip;
+
+       DBG_PRT((L"ext2_iput: inode %d table %d", itp->inumber, (int) (itp - e2fs->inode_table)));
+
+       itp->inumber = 0;
+       itp->free = 1;
+}
+
+
+/*
+ * Map a block offset into a file into an absolute block number.
+ * (traverse the indirect blocks if necessary).  Note: Double-indirect
+ * blocks allow us to map over 64Mb on a 1k file system.  Therefore, for
+ * our purposes, we will NOT bother with triple indirect blocks.
+ *
+ * The "allocate" argument is set if we want to *allocate* a block
+ * and we don't already have one allocated.
+ */
+static int 
+ext2_blkno(ext2fs_priv_state_t *e2fs, struct ext2_inode *ip, int blkoff)
+{
+       unsigned int *lp;
+       unsigned int *ilp;
+       unsigned int *dlp;
+       int blkno;
+       int iblkno;
+       int diblkno;
+       long offset;
+
+       ilp = (unsigned int *)e2fs->iblkbuf;
+       dlp = (unsigned int *)e2fs->diblkbuf;
+       lp = (unsigned int *)e2fs->blkbuf;
+
+       /* If it's a direct block, it's easy! */
+       if (blkoff <= e2fs->directlim) {
+               return ip->i_block[blkoff];
+       }
+
+       /* Is it a single-indirect? */
+       if (blkoff <= e2fs->ind1lim) {
+               iblkno = ip->i_block[EXT2_IND_BLOCK];
+
+               if (iblkno == 0) {
+                       return 0;
+               }
+
+               /* Read the indirect block */
+               if (e2fs->cached_iblkno != iblkno) {
+                       offset = iblkno * e2fs->blocksize;
+                       if (read_bytes(e2fs->blkio, e2fs->mediaid, offset, e2fs->iblkbuf, e2fs->blocksize)) {
+                               ERR_PRT((L"ext2_blkno: error on iblk read"));
+                               return 0;
+                       }
+                       e2fs->cached_iblkno = iblkno;
+               }
+
+               blkno = ilp[blkoff-(e2fs->directlim+1)];
+               return blkno;
+       }
+
+       /* Is it a double-indirect? */
+       if (blkoff <= e2fs->ind2lim) {
+               /* Find the double-indirect block */
+               diblkno = ip->i_block[EXT2_DIND_BLOCK];
+
+               if (diblkno == 0) {
+                       return 0;
+               }
+
+               /* Read in the double-indirect block */
+               if (e2fs->cached_diblkno != diblkno) {
+                       offset = diblkno * e2fs->blocksize;
+                       if (read_bytes(e2fs->blkio, e2fs->mediaid, offset,  e2fs->diblkbuf, e2fs->blocksize)) {
+                               ERR_PRT((L"ext2_blkno: err reading dindr blk"));
+                               return 0;
+                       }
+                       e2fs->cached_diblkno = diblkno;
+               }
+
+               /* Find the single-indirect block pointer ... */
+               iblkno = dlp[(blkoff - (e2fs->ind1lim+1)) / e2fs->ptrs_per_blk];
+
+               if (iblkno == 0) {
+                       return 0;
+               }
+
+               /* Read the indirect block */
+    
+               if (e2fs->cached_iblkno != iblkno) {
+                       offset = iblkno * e2fs->blocksize;
+                       if (read_bytes(e2fs->blkio, e2fs->mediaid, offset, e2fs->iblkbuf, e2fs->blocksize)) {
+                               ERR_PRT((L"ext2_blkno: err on iblk read"));
+                               return 0;
+                       }
+                       e2fs->cached_iblkno = iblkno;
+               }
+
+               /* Find the block itself. */
+               blkno = ilp[(blkoff-(e2fs->ind1lim+1)) % e2fs->ptrs_per_blk];
+               return blkno;
+       }
+
+       if (blkoff > e2fs->ind2lim) {
+               ERR_PRT((L"ext2_blkno: block number too large: %d", blkoff));
+               return 0;
+       }
+       return -1;
+}
+
+
+static int
+ext2_breadi(ext2fs_priv_state_t *e2fs, struct ext2_inode *ip, long blkno, long nblks, CHAR8 *buffer)
+{ 
+       long dev_blkno, ncontig, offset, nbytes, tot_bytes;
+
+       tot_bytes = 0;
+       if ((blkno+nblks)*e2fs->blocksize > ip->i_size)
+               nblks = (ip->i_size + e2fs->blocksize) / e2fs->blocksize - blkno;
+
+       while (nblks) {
+               /*
+                * Contiguous reads are a lot faster, so we try to group
+                * as many blocks as possible:
+                */
+               ncontig = 0; nbytes = 0;
+               dev_blkno = ext2_blkno(e2fs,ip, blkno);
+               do {
+                       ++blkno; ++ncontig; --nblks;
+                       nbytes += e2fs->blocksize;
+               } while (nblks &&
+                        ext2_blkno(e2fs, ip, blkno) == dev_blkno + ncontig);
+
+               if (dev_blkno == 0) {
+                       /* This is a "hole" */
+                       Memset(buffer, 0, nbytes);
+               } else {
+                       /* Read it for real */
+                       offset = dev_blkno*e2fs->blocksize;
+                       DBG_PRT((L"ext2_bread: reading %d bytes at offset %d", nbytes, offset));
+
+                       if (read_bytes(e2fs->blkio, e2fs->mediaid, offset, buffer, nbytes)) {
+                               ERR_PRT((L"ext2_bread: read error"));
+                               return -1;
+                       }
+               }
+               buffer    += nbytes;
+               tot_bytes += nbytes;
+       }
+       return tot_bytes;
+}
+
+static struct ext2_dir_entry_2 *
+ext2_readdiri(ext2fs_priv_state_t *e2fs, struct ext2_inode *dir_inode, int rewind)
+{
+       struct ext2_dir_entry_2 *dp;
+       static int diroffset = 0, blockoffset = 0;
+
+       /* Reading a different directory, invalidate previous state */
+       if (rewind) {
+               diroffset = 0;
+               blockoffset = 0;
+               /* read first block */
+               if (ext2_breadi(e2fs, dir_inode, 0, 1, e2fs->blkbuf) < 0)
+                       return NULL;
+       }
+
+       DBG_PRT((L"ext2_readdiri: blkoffset %d diroffset %d len %d", blockoffset, diroffset, dir_inode->i_size));
+
+       if (blockoffset >= e2fs->blocksize) {
+               diroffset += e2fs->blocksize;
+               if (diroffset >= dir_inode->i_size)
+                       return NULL;
+               ERR_PRT((L"ext2_readdiri: reading block at %d", diroffset));
+               /* assume that this will read the whole block */
+               if (ext2_breadi(e2fs, dir_inode, diroffset / e2fs->blocksize, 1, e2fs->blkbuf) < 0) return NULL;
+               blockoffset = 0;
+       }
+
+       dp = (struct ext2_dir_entry_2 *) (e2fs->blkbuf + blockoffset);
+       blockoffset += dp->rec_len;
+
+#if 0
+       Print(L"ext2_readdiri: returning %x = ");
+       { INTN i; for(i=0; i < dp->name_len; i++) Print(L"%c", (CHAR16)dp->name[i]); Print(L"\n");}
+#endif
+       return dp;
+}
+
+/*
+ * the string 'name' is modified by this call as per the parsing that
+ * is done in strtok_simple()
+ */
+static struct ext2_inode *
+ext2_namei(ext2fs_priv_state_t *e2fs, CHAR8 *name)
+{
+       CHAR8 *component;
+       struct ext2_inode *dir_inode;
+       struct ext2_dir_entry_2 *dp;
+       int next_ino;
+
+       /* start at the root: */
+       if (!e2fs->root_inode)
+               e2fs->root_inode = ext2_iget(e2fs, EXT2_ROOT_INO);
+       dir_inode = e2fs->root_inode;
+       if (!dir_inode)
+         return NULL;
+
+       component = strtok_simple(name, '/');
+       while (component) {
+               int component_length;
+               int rewind = 0;
+               /*
+                * Search for the specified component in the current
+                * directory inode.
+                */
+               next_ino = -1;
+               component_length = strlena(component);
+
+               DBG_PRT((L"ext2_namei: component = %a", component));
+
+               /* rewind the first time through */
+               while ((dp = ext2_readdiri(e2fs, dir_inode, !rewind++))) {
+                       if ((dp->name_len == component_length) &&
+                           (strncmpa(component, dp->name,
+                                    component_length) == 0))
+                       {
+                               /* Found it! */
+                               DBG_PRT((L"ext2_namei: found entry %a", component));
+                               next_ino = dp->inode;
+                               break;
+                       }
+                       DBG_PRT((L"ext2_namei: looping"));
+               }
+       
+               DBG_PRT((L"ext2_namei: next_ino = %d", next_ino));
+
+               /*
+                * At this point, we're done with this directory whether
+                * we've succeeded or failed...
+                */
+               if (dir_inode != e2fs->root_inode) ext2_iput(e2fs, dir_inode);
+
+               /*
+                * If next_ino is negative, then we've failed (gone
+                * all the way through without finding anything)
+                */
+               if (next_ino < 0) {
+                       return NULL;
+               }
+
+               /*
+                * Otherwise, we can get this inode and find the next
+                * component string...
+                */
+               dir_inode = ext2_iget(e2fs, next_ino);
+               if (!dir_inode)
+                 return NULL;
+
+               component = strtok_simple(NULL, '/');
+       }
+
+       /*
+        * If we get here, then we got through all the components.
+        * Whatever we got must match up with the last one.
+        */
+       return dir_inode;
+}
+
+
+/*
+ * Read block number "blkno" from the specified file.
+ */
+static int 
+ext2_bread(ext2fs_priv_state_t *e2fs, int fd, long blkno, long nblks, char *buffer)
+{
+       struct ext2_inode * ip;
+       ip = &e2fs->inode_table[fd].inode;
+       return ext2_breadi(e2fs, ip, blkno, nblks, buffer);
+}
+
+#if 0
+/*
+ * Note: don't mix any kind of file lookup or other I/O with this or
+ * you will lose horribly (as it reuses blkbuf)
+ */
+static const char *
+ext2_readdir(ext2fs_priv_state_t *e2fs, int fd, int rewind)
+{
+       struct ext2_inode * ip = &e2fs->inode_table[fd].inode;
+       struct ext2_dir_entry_2 * ent;
+       if (!S_ISDIR(ip->i_mode)) {
+               ERR_PRT((L"fd %d (inode %d) is not a directory (mode %x)",
+                      fd, e2fs->inode_table[fd].inumber, ip->i_mode));
+               return NULL;
+       }
+       ent = ext2_readdiri(e2fs, ip, rewind);
+       if (ent) {
+               ent->name[ent->name_len] = '\0';
+               return ent->name;
+       } else { 
+               return NULL;
+       }
+}
+#endif
+
+static int 
+ext2_fstat(ext2fs_priv_state_t *e2fs, int fd, ext2fs_stat_t *buf)
+{
+       struct ext2_inode * ip = &e2fs->inode_table[fd].inode;
+
+       Memset(buf, 0, sizeof(*buf));
+
+       /* fill in relevant fields */
+       buf->st_ino = e2fs->inode_table[fd].inumber;
+       buf->st_mode = ip->i_mode;
+       buf->st_nlink = ip->i_links_count;
+       buf->st_uid = ip->i_uid;
+       buf->st_gid = ip->i_gid;
+       buf->st_size = ip->i_size;
+       buf->st_atime = ip->i_atime;
+       buf->st_mtime = ip->i_mtime;
+       buf->st_ctime = ip->i_ctime;
+
+       return 0; /* NOTHING CAN GO WROGN! */
+}
+
+static EFI_STATUS
+ext2fs_fstat(ext2fs_interface_t *this, UINTN fd, ext2fs_stat_t *st)
+{
+       ext2fs_priv_state_t *e2fs;
+
+       if (this == NULL || fd > MAX_OPEN_FILES || st == NULL) return EFI_INVALID_PARAMETER;
+
+       e2fs = FS_PRIVATE(this);
+
+       ext2_fstat(e2fs, fd, st);
+
+       return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+ext2fs_seek(ext2fs_interface_t *this, UINTN fd, UINT64 newpos)
+{
+       ext2fs_priv_state_t *e2fs;
+
+       if (this == NULL || fd > MAX_OPEN_FILES || newpos >= EXT2_FILESIZE_MAX) return EFI_INVALID_PARAMETER;
+
+       e2fs = FS_PRIVATE(this);
+       if (newpos > (UINT64)e2fs->inode_table[fd].inode.i_size) return EFI_INVALID_PARAMETER;
+
+       e2fs->inode_table[fd].pos = newpos;
+
+       return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+ext2fs_read(ext2fs_interface_t *this, UINTN fd, VOID *buf, UINTN *size)
+{
+       ext2fs_priv_state_t *e2fs;
+       UINTN count, nc, bofs, bnum, pos;
+       EFI_STATUS ret = EFI_INVALID_PARAMETER;
+       CHAR8 *block;
+
+       if (this == NULL || size == NULL || buf == NULL || fd > MAX_OPEN_FILES) return EFI_INVALID_PARAMETER;
+
+       e2fs = FS_PRIVATE(this);
+
+       count = MIN(*size, e2fs->inode_table[fd].inode.i_size - e2fs->inode_table[fd].pos);
+
+       if (count == 0)  {
+               *size = 0;
+               return EFI_SUCCESS;
+       }
+       block = e2fs->blkbuf;
+
+       *size = 0;
+
+       pos = e2fs->inode_table[fd].pos;  
+       DBG_PRT((L"size=%d i_size=%d count=%d pos=%ld", *size,e2fs->inode_table[fd].inode.i_size, count, pos));
+       while (count) {
+               bnum = pos / e2fs->blocksize;
+               bofs = pos % e2fs->blocksize;
+               nc   = MIN(count, e2fs->blocksize - bofs);
+
+               DBG_PRT((L"bnum =%d bofs=%d nc=%d *size=%d", bnum, bofs, nc, *size));
+
+               if (ext2_bread(e2fs, fd, bnum, 1, block) == -1) goto error;
+#if 0          
+               { int i; char *p = block+bofs; 
+                       for(i=MIN(nc, 64); i>=0 ; i--, p++) {
+                               if (i % 16 == 0) Print(L"\n");
+                               Print(L"%02x ", (UINTN)*p & 0xff);
+                       }
+               }
+#endif
+
+               Memcpy(buf, block+bofs, nc);
+               count -= nc;
+               pos   += nc;
+               buf   += nc;
+               *size += nc;
+       }
+
+       e2fs->inode_table[fd].pos += *size;
+       ret = EFI_SUCCESS;
+error:
+       DBG_PRT((L"*size=%d ret=%r", *size, ret));
+       return ret;
+}
+
+static struct ext2_inode *
+ext2_follow_link(ext2fs_priv_state_t *e2fs, struct ext2_inode * from, const char * base)
+{
+       char *linkto;
+
+       if (from->i_blocks) {
+               linkto = e2fs->blkbuf;
+               if (ext2_breadi(e2fs, from, 0, 1, e2fs->blkbuf) == -1)
+                       return NULL;
+               DBG_PRT((L"long link!"));
+       } else {
+               linkto = (char*)from->i_block;
+       }
+       DBG_PRT((L"symlink to %s", linkto));
+
+       /* Resolve relative links */
+       if (linkto[0] != '/') {
+               char *end = strrchra(base, '/');
+               if (end) {
+                       //char fullname[(end - base + 1) + strlena(linkto) + 1];
+                       char fullname[EXT2FS_PATH_MAXLEN];
+
+                       if (((end - base + 1) + strlena(linkto) + 1) >= EXT2FS_PATH_MAXLEN) {
+                               Print(L"%s: filename too long, can't resolve\n", __FUNCTION__);
+                               return NULL;
+                       }
+
+                       strncpya(fullname, base, end - base + 1);
+                       fullname[end - base + 1] = '\0';
+                       strcata(fullname, linkto);
+                       DBG_PRT((L"resolved to %s", fullname));
+                       return ext2_namei(e2fs, fullname);
+               } else {
+                       /* Assume it's in the root */
+                       return ext2_namei(e2fs, linkto);
+               }
+       } else {
+               return ext2_namei(e2fs, linkto);
+       }
+}
+
+static int
+ext2_open(ext2fs_priv_state_t *e2fs, char *filename)
+{
+       /*
+        * Unix-like open routine.  Returns a small integer (actually
+        * an index into the inode table...
+        */
+       struct ext2_inode * ip;
+
+       ip = ext2_namei(e2fs, filename);
+       if (ip) {
+               struct inode_table_entry *itp;
+
+               while (S_ISLNK(ip->i_mode)) {
+                       ip = ext2_follow_link(e2fs, ip, filename);
+                       if (!ip) return -1;
+               }
+               itp = (struct inode_table_entry *)ip;
+               return itp - e2fs->inode_table;
+       } else
+               return -1;
+}
+
+static void ext2_close(ext2fs_priv_state_t *e2fs, int fd)
+{
+       /* blah, hack, don't close the root inode ever */
+       if (&e2fs->inode_table[fd].inode != e2fs->root_inode)
+               ext2_iput(e2fs, &e2fs->inode_table[fd].inode);
+}
+
+static EFI_STATUS
+ext2fs_close(ext2fs_interface_t *this, UINTN fd)
+{
+       ext2fs_priv_state_t *e2fs;
+
+       if (this == NULL || fd > MAX_OPEN_FILES) return EFI_INVALID_PARAMETER;
+
+       e2fs = FS_PRIVATE(this);
+
+       ext2_close(e2fs, fd);
+
+       return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+ext2fs_open(ext2fs_interface_t *this, CHAR16 *name, UINTN *fd)
+{
+       ext2fs_priv_state_t *e2fs;
+       CHAR8 filename[EXT2FS_PATH_MAXLEN]; /* XXX: kind of big for a stack object */
+       INTN tmp;
+
+       DBG_PRT((L"name:%s fd=%x", name, fd));
+
+       if (this == NULL || name == NULL || fd == NULL || StrLen(name) >=EXT2FS_PATH_MAXLEN) return EFI_INVALID_PARAMETER;
+
+       e2fs = FS_PRIVATE(this);
+
+       /*
+        * XXX: for security reasons, we may have to force a prefix like /boot to all filenames
+        */
+       StrnXCpy(filename, name, EXT2FS_PATH_MAXLEN);
+       
+       DBG_PRT((L"ASCII name:%a UTF-name:%s", filename, name));
+
+       tmp = ext2_open(e2fs, filename);
+       if (tmp != -1) {
+               *fd = (UINTN)tmp;
+               e2fs->inode_table[tmp].pos = 0; /* reset file position */
+       }
+
+       DBG_PRT((L"name: %s fd=%d tmp=%d", name, *fd, tmp));
+
+       return tmp == -1 ? EFI_NOT_FOUND : EFI_SUCCESS;
+}
+
+static EFI_STATUS
+ext2fs_name(ext2fs_interface_t *this, CHAR16 *name, UINTN maxlen)
+{
+       if (name == NULL || maxlen < 1) return EFI_INVALID_PARAMETER;
+
+       StrnCpy(name, FS_NAME, maxlen-1);
+
+       name[maxlen-1] = CHAR_NULL;
+
+       return EFI_SUCCESS;
+}
+
+static INTN
+ext2fs_init_state(ext2fs_t *ext2fs, EFI_HANDLE dev, EFI_BLOCK_IO *blkio, struct ext2_super_block *sb)
+{
+       ext2fs_priv_state_t *e2fs = FS_PRIVATE(ext2fs);
+       UINTN i;
+       EFI_STATUS status;
+
+       Memset(ext2fs, 0, sizeof(*ext2fs));
+
+       e2fs->dev     = dev;
+       e2fs->blkio   = blkio;
+       e2fs->mediaid = blkio->Media->MediaId;
+
+       /* fools gcc builtin memcpy */
+       Memcpy(&e2fs->sb, sb, sizeof(*sb));
+
+       e2fs->ngroups = (sb->s_blocks_count - sb->s_first_data_block + EXT2_BLOCKS_PER_GROUP(sb) - 1) / EXT2_BLOCKS_PER_GROUP(sb);
+
+       e2fs->gds = (struct ext2_group_desc *)alloc(e2fs->ngroups * sizeof(struct ext2_group_desc), EXT2FS_MEMTYPE);
+       if (e2fs->gds == NULL) {
+               ERR_PRT((L"failed to allocate gds"));
+               return EFI_OUT_OF_RESOURCES;
+       }
+       
+       e2fs->blocksize = EXT2_BLOCK_SIZE(sb);
+
+       DBG_PRT((L"gds_size=%d gds_offset=%d ngroups=%d blocksize=%d",
+                               e2fs->ngroups * sizeof(struct ext2_group_desc), 
+                               e2fs->blocksize * (EXT2_MIN_BLOCK_SIZE/e2fs->blocksize + 1),
+                               e2fs->ngroups, (UINTN)e2fs->blocksize));
+
+       /* read in the group descriptors (immediately follows superblock) */
+       status = read_bytes(blkio, e2fs->mediaid, e2fs->blocksize * (EXT2_MIN_BLOCK_SIZE/e2fs->blocksize + 1),
+                       e2fs->gds, e2fs->ngroups * sizeof(struct ext2_group_desc));
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"cannot read gds: %r", status));
+               free(e2fs->gds);
+               return EFI_INVALID_PARAMETER;
+       }
+#if 0
+       { int i; char *p = (char *)e2fs->gds;
+               for(i=e2fs->ngroups*sizeof(*e2fs->gds); i ; i--, p++) {
+                       if (i % 16 == 0) Print(L"\n");
+                       Print(L"%02x ", (UINTN)*p & 0xff);
+
+               }
+       }
+#endif
+
+       e2fs->cached_diblkno = -1;
+       e2fs->cached_iblkno  = -1;
+
+       /* initialize the inode table */
+       for (i = 0; i < MAX_OPEN_FILES; i++) {
+               e2fs->inode_table[i].free = 1;
+               e2fs->inode_table[i].inumber = 0;
+       }
+       /* clear the root inode pointer (very important!) */
+       e2fs->root_inode = NULL;
+
+       /*
+        * Calculate direct/indirect block limits for this file system
+        * (blocksize dependent):
+       ext2_blocksize = EXT2_BLOCK_SIZE(&sb);
+        */
+       e2fs->directlim    = EXT2_NDIR_BLOCKS - 1;
+       e2fs->ptrs_per_blk = e2fs->blocksize/sizeof(unsigned int);
+       e2fs->ind1lim      = e2fs->ptrs_per_blk + e2fs->directlim;
+       e2fs->ind2lim      = (e2fs->ptrs_per_blk * e2fs->ptrs_per_blk) + e2fs->directlim;
+
+       ext2fs->pub_intf.ext2fs_name     = ext2fs_name;
+       ext2fs->pub_intf.ext2fs_open     = ext2fs_open;
+       ext2fs->pub_intf.ext2fs_read     = ext2fs_read;
+       ext2fs->pub_intf.ext2fs_close    = ext2fs_close;
+       ext2fs->pub_intf.ext2fs_seek     = ext2fs_seek;
+       ext2fs->pub_intf.ext2fs_fstat    = ext2fs_fstat;
+
+       return EFI_SUCCESS;
+}
+
+
+static EFI_STATUS
+ext2fs_install_one(EFI_HANDLE dev, VOID **intf)
+{
+       struct ext2_super_block sb;
+       long sb_block = 1;
+       EFI_STATUS status;
+       EFI_BLOCK_IO *blkio;
+       ext2fs_t *ext2fs;
+
+       status = BS->HandleProtocol (dev, &Ext2FsProtocol, (VOID **)&ext2fs);
+       if (status == EFI_SUCCESS) {
+               ERR_PRT((L"Warning: found existing %s protocol on device", FS_NAME));
+               goto found;
+       }
+       
+       status = BS->HandleProtocol(dev, &BlockIoProtocol, (VOID **)&blkio);
+       if (EFI_ERROR(status)) return EFI_INVALID_PARAMETER;
+       
+       VERB_PRT(5,
+               { EFI_DEVICE_PATH *dp; CHAR16 *str;
+                 dp  = DevicePathFromHandle(dev);
+                 str = DevicePathToStr(dp);
+                 Print(L"dev:%s\nLogical partition: %s  BlockSize: %d WriteCaching: %s \n", str, 
+                         blkio->Media->LogicalPartition ? L"Yes": L"No",
+                         blkio->Media->BlockSize,
+                         blkio->Media->WriteCaching ? L"Yes":L"No");
+                 FreePool(str);
+               });
+       if (blkio->Media->LogicalPartition == FALSE) return EFI_INVALID_PARAMETER;
+
+#if 0
+       /*
+        * Used to be necessary on some older versions of EFI to avoid getting
+        * stuck. Now can cause problems with some SCSI controllers when enabled. 
+        * Does not seem necessary with EFI 12.38
+        */
+       blkio->Reset(blkio, FALSE);
+#endif
+
+       status = read_bytes(blkio, blkio->Media->MediaId, sb_block * EXT2_MIN_BLOCK_SIZE, &sb, sizeof(sb));
+       if (EFI_ERROR(status)) {
+               DBG_PRT((L"cannot read superblock: %r", status));
+               return EFI_INVALID_PARAMETER;
+       }
+       
+       if (sb.s_magic != EXT2_SUPER_MAGIC) {
+               DBG_PRT((L"bad magic 0x%x\n", sb.s_magic));
+               return EFI_INVALID_PARAMETER;
+       }
+       
+       ext2fs = (ext2fs_t *)alloc(sizeof(*ext2fs), EXT2FS_MEMTYPE);
+       if (ext2fs == NULL) return EFI_OUT_OF_RESOURCES;
+
+       status = ext2fs_init_state(ext2fs, dev, blkio, &sb);
+       if (status != EFI_SUCCESS) {
+               free(ext2fs);
+               return status;
+       }
+
+       status = LibInstallProtocolInterfaces(&dev, &Ext2FsProtocol, ext2fs, NULL);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"Cannot install %s protocol: %r", FS_NAME, status));
+               free(ext2fs);
+               return status;
+       }
+found:
+       if (intf) *intf = (VOID *)ext2fs;
+
+       VERB_PRT(3,
+               { EFI_DEVICE_PATH *dp; CHAR16 *str;
+                 dp  = DevicePathFromHandle(dev);
+                 str = DevicePathToStr(dp);
+                 Print(L"dev:%s %s detected\n", str, FS_NAME);
+                 FreePool(str);
+               });
+
+       return EFI_SUCCESS;
+}
+
+EFI_STATUS
+ext2fs_install(VOID)
+{
+       UINTN size = 0;
+       UINTN i;
+       EFI_STATUS status;
+       VOID *intf;
+
+       BS->LocateHandle(ByProtocol, &BlockIoProtocol, NULL, &size, NULL);
+       if (size == 0) return EFI_UNSUPPORTED; /* no device found, oh well */
+
+       DBG_PRT((L"size=%d", size));
+
+       dev_tab = (dev_tab_t *)alloc(size, EfiLoaderData);
+       if (dev_tab == NULL) {
+               ERR_PRT((L"failed to allocate handle table"));
+               return EFI_OUT_OF_RESOURCES;
+       }
+       
+       status = BS->LocateHandle(ByProtocol, &BlockIoProtocol, NULL, &size, (VOID **)dev_tab);
+       if (status != EFI_SUCCESS) {
+               ERR_PRT((L"failed to get handles: %r", status));
+               free(dev_tab);
+               return status;
+       }
+       ndev = size / sizeof(EFI_HANDLE);
+
+       for(i=0; i < ndev; i++) {
+               intf = NULL;
+               ext2fs_install_one(dev_tab[i].dev, &intf);
+               /* override device handle with interface pointer */
+               dev_tab[i].intf = intf;
+       }
+
+       return EFI_SUCCESS;
+}
+       
+EFI_STATUS
+ext2fs_uninstall(VOID)
+{
+       
+       ext2fs_priv_state_t *e2fs;
+       EFI_STATUS status;
+       UINTN i;
+
+       for(i=0; i < ndev; i++) {
+               if (dev_tab[i].intf == NULL) continue;
+               e2fs = FS_PRIVATE(dev_tab[i].intf);
+               status = BS->UninstallProtocolInterface(e2fs->dev, &Ext2FsProtocol, dev_tab[i].intf);
+               if (EFI_ERROR(status)) {
+                       ERR_PRT((L"Uninstall %s error: %r", FS_NAME, status));
+                       continue;
+               }
+               VERB_PRT(3,
+                       { EFI_DEVICE_PATH *dp; CHAR16 *str;
+                       dp  = DevicePathFromHandle(e2fs->dev);
+                       str = DevicePathToStr(dp);
+                       Print(L"uninstalled %s on %s\n", FS_NAME, str);
+                       FreePool(str);
+                       });
+               free(dev_tab[i].intf);
+       }
+       if (dev_tab) free(dev_tab);
+
+       return EFI_SUCCESS;
+}
diff --git a/fs/ext2fs.h b/fs/ext2fs.h
new file mode 100644 (file)
index 0000000..938c6be
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+#ifndef __EXT2FS_H__
+#define __EXT2FS_H__
+
+INTERFACE_DECL(_ext2fs_interface_t);
+
+/*
+ * simplified stat structure
+ * XXX: need to cleanup types !
+ */
+typedef struct {
+       unsigned long   st_ino;
+       unsigned long   st_nlink;
+       unsigned int    st_mode;
+       unsigned int    st_uid;
+       unsigned int    st_gid;
+       unsigned long   st_size;
+       unsigned long   st_atime;
+       unsigned long   st_mtime;
+       unsigned long   st_ctime;
+} ext2fs_stat_t;
+
+
+typedef struct _ext2fs_interface_t {
+       EFI_STATUS (*ext2fs_name)(struct _ext2fs_interface_t *this, CHAR16 *name, UINTN maxlen);
+       EFI_STATUS (*ext2fs_open)(struct _ext2fs_interface_t *this, CHAR16 *name, UINTN *fd);
+       EFI_STATUS (*ext2fs_read)(struct _ext2fs_interface_t *this, UINTN fd, VOID *buf, UINTN *size);
+       EFI_STATUS (*ext2fs_close)(struct _ext2fs_interface_t *this, UINTN fd);
+       EFI_STATUS (*ext2fs_fstat)(struct _ext2fs_interface_t *this, UINTN fd, ext2fs_stat_t *st);
+       EFI_STATUS (*ext2fs_seek)(struct _ext2fs_interface_t *this, UINTN fd, UINT64 newpos);
+} ext2fs_interface_t;
+
+#define EXT2FS_PROTOCOL \
+    { 0x6ea924f6, 0xc9f2, 0x4331, {0x83, 0x54, 0x19, 0xd0, 0x17, 0x50, 0xd9, 0xc7} }
+
+extern EFI_STATUS ext2fs_install(VOID);
+extern EFI_STATUS ext2fs_uninstall(VOID);
+
+
+#endif /* __EXT2FS_H__ */
diff --git a/fs/fs.h b/fs/fs.h
new file mode 100644 (file)
index 0000000..8db1b9f
--- /dev/null
+++ b/fs/fs.h
@@ -0,0 +1,1480 @@
+#ifndef _LINUX_FS_H
+#define _LINUX_FS_H
+
+/*
+ * This file has definitions for some important file table
+ * structures etc.
+ */
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <linux/limits.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/vfs.h>
+#include <linux/net.h>
+#include <linux/kdev_t.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/dcache.h>
+#include <linux/stat.h>
+#include <linux/cache.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+
+#include <asm/atomic.h>
+#include <asm/bitops.h>
+
+struct poll_table_struct;
+
+
+/*
+ * It's silly to have NR_OPEN bigger than NR_FILE, but you can change
+ * the file limit at runtime and only root can increase the per-process
+ * nr_file rlimit, so it's safe to set up a ridiculously high absolute
+ * upper limit on files-per-process.
+ *
+ * Some programs (notably those using select()) may have to be 
+ * recompiled to take full advantage of the new limits..  
+ */
+
+/* Fixed constants first: */
+#undef NR_OPEN
+#define NR_OPEN (1024*1024)    /* Absolute upper limit on fd num */
+#define INR_OPEN 1024          /* Initial setting for nfile rlimits */
+
+#define BLOCK_SIZE_BITS 10
+#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS)
+
+/* And dynamically-tunable limits and defaults: */
+struct files_stat_struct {
+       int nr_files;           /* read only */
+       int nr_free_files;      /* read only */
+       int max_files;          /* tunable */
+};
+extern struct files_stat_struct files_stat;
+
+struct inodes_stat_t {
+       int nr_inodes;
+       int nr_unused;
+       int dummy[5];
+};
+extern struct inodes_stat_t inodes_stat;
+
+extern int max_super_blocks, nr_super_blocks;
+extern int leases_enable, dir_notify_enable, lease_break_time;
+
+#define NR_FILE  8192  /* this can well be larger on a larger system */
+#define NR_RESERVED_FILES 10 /* reserved for root */
+#define NR_SUPER 256
+
+#define MAY_EXEC 1
+#define MAY_WRITE 2
+#define MAY_READ 4
+
+#define FMODE_READ 1
+#define FMODE_WRITE 2
+
+#define READ 0
+#define WRITE 1
+#define READA 2                /* read-ahead  - don't block if no resources */
+#define SPECIAL 4      /* For non-blockdevice requests in request queue */
+
+#define SEL_IN         1
+#define SEL_OUT                2
+#define SEL_EX         4
+
+/* public flags for file_system_type */
+#define FS_REQUIRES_DEV 1 
+#define FS_NO_DCACHE   2 /* Only dcache the necessary things. */
+#define FS_NO_PRELIM   4 /* prevent preloading of dentries, even if
+                          * FS_NO_DCACHE is not set.
+                          */
+#define FS_SINGLE      8 /*
+                          * Filesystem that can have only one superblock;
+                          * kernel-wide vfsmnt is placed in ->kern_mnt by
+                          * kern_mount() which must be called _after_
+                          * register_filesystem().
+                          */
+#define FS_NOMOUNT     16 /* Never mount from userland */
+#define FS_LITTER      32 /* Keeps the tree in dcache */
+#define FS_ODD_RENAME  32768   /* Temporary stuff; will go away as soon
+                                 * as nfs_rename() will be cleaned up
+                                 */
+/*
+ * These are the fs-independent mount-flags: up to 32 flags are supported
+ */
+#define MS_RDONLY       1      /* Mount read-only */
+#define MS_NOSUID       2      /* Ignore suid and sgid bits */
+#define MS_NODEV        4      /* Disallow access to device special files */
+#define MS_NOEXEC       8      /* Disallow program execution */
+#define MS_SYNCHRONOUS 16      /* Writes are synced at once */
+#define MS_REMOUNT     32      /* Alter flags of a mounted FS */
+#define MS_MANDLOCK    64      /* Allow mandatory locks on an FS */
+#define MS_NOATIME     1024    /* Do not update access times. */
+#define MS_NODIRATIME  2048    /* Do not update directory access times */
+#define MS_BIND                4096
+
+/*
+ * Flags that can be altered by MS_REMOUNT
+ */
+#define MS_RMT_MASK    (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|\
+                       MS_SYNCHRONOUS|MS_MANDLOCK|MS_NOATIME|MS_NODIRATIME)
+
+/*
+ * Old magic mount flag and mask
+ */
+#define MS_MGC_VAL 0xC0ED0000
+#define MS_MGC_MSK 0xffff0000
+
+/* Inode flags - they have nothing to superblock flags now */
+
+#define S_SYNC         1       /* Writes are synced at once */
+#define S_NOATIME      2       /* Do not update access times */
+#define S_QUOTA                4       /* Quota initialized for file */
+#define S_APPEND       8       /* Append-only file */
+#define S_IMMUTABLE    16      /* Immutable file */
+#define S_DEAD         32      /* removed, but still open directory */
+
+/*
+ * Note that nosuid etc flags are inode-specific: setting some file-system
+ * flags just means all the inodes inherit those flags by default. It might be
+ * possible to override it selectively if you really wanted to with some
+ * ioctl() that is not currently implemented.
+ *
+ * Exception: MS_RDONLY is always applied to the entire file system.
+ *
+ * Unfortunately, it is possible to change a filesystems flags with it mounted
+ * with files in use.  This means that all of the inodes will not have their
+ * i_flags updated.  Hence, i_flags no longer inherit the superblock mount
+ * flags, so these have to be checked separately. -- rmk@arm.uk.linux.org
+ */
+#define __IS_FLG(inode,flg) ((inode)->i_sb->s_flags & (flg))
+
+#define IS_RDONLY(inode) ((inode)->i_sb->s_flags & MS_RDONLY)
+#define IS_NOSUID(inode)       __IS_FLG(inode, MS_NOSUID)
+#define IS_NODEV(inode)                __IS_FLG(inode, MS_NODEV)
+#define IS_NOEXEC(inode)       __IS_FLG(inode, MS_NOEXEC)
+#define IS_SYNC(inode)         (__IS_FLG(inode, MS_SYNCHRONOUS) || ((inode)->i_flags & S_SYNC))
+#define IS_MANDLOCK(inode)     __IS_FLG(inode, MS_MANDLOCK)
+
+#define IS_QUOTAINIT(inode)    ((inode)->i_flags & S_QUOTA)
+#define IS_APPEND(inode)       ((inode)->i_flags & S_APPEND)
+#define IS_IMMUTABLE(inode)    ((inode)->i_flags & S_IMMUTABLE)
+#define IS_NOATIME(inode)      (__IS_FLG(inode, MS_NOATIME) || ((inode)->i_flags & S_NOATIME))
+#define IS_NODIRATIME(inode)   __IS_FLG(inode, MS_NODIRATIME)
+
+#define IS_DEADDIR(inode)      ((inode)->i_flags & S_DEAD)
+
+/* the read-only stuff doesn't really belong here, but any other place is
+   probably as bad and I don't want to create yet another include file. */
+
+#define BLKROSET   _IO(0x12,93)        /* set device read-only (0 = read-write) */
+#define BLKROGET   _IO(0x12,94)        /* get read-only status (0 = read_write) */
+#define BLKRRPART  _IO(0x12,95)        /* re-read partition table */
+#define BLKGETSIZE _IO(0x12,96)        /* return device size */
+#define BLKFLSBUF  _IO(0x12,97)        /* flush buffer cache */
+#define BLKRASET   _IO(0x12,98)        /* Set read ahead for block device */
+#define BLKRAGET   _IO(0x12,99)        /* get current read ahead setting */
+#define BLKFRASET  _IO(0x12,100)/* set filesystem (mm/filemap.c) read-ahead */
+#define BLKFRAGET  _IO(0x12,101)/* get filesystem (mm/filemap.c) read-ahead */
+#define BLKSECTSET _IO(0x12,102)/* set max sectors per request (ll_rw_blk.c) */
+#define BLKSECTGET _IO(0x12,103)/* get max sectors per request (ll_rw_blk.c) */
+#define BLKSSZGET  _IO(0x12,104)/* get block device sector size */
+#if 0
+#define BLKPG      _IO(0x12,105)/* See blkpg.h */
+#define BLKELVGET  _IOR(0x12,106,sizeof(blkelv_ioctl_arg_t))/* elevator get */
+#define BLKELVSET  _IOW(0x12,107,sizeof(blkelv_ioctl_arg_t))/* elevator set */
+/* This was here just to show that the number is taken -
+   probably all these _IO(0x12,*) ioctls should be moved to blkpg.h. */
+#endif
+
+
+#define BMAP_IOCTL 1           /* obsolete - kept for compatibility */
+#define FIBMAP    _IO(0x00,1)  /* bmap access */
+#define FIGETBSZ   _IO(0x00,2) /* get the block size used for bmap */
+
+#ifdef __KERNEL__
+
+#include <asm/semaphore.h>
+#include <asm/byteorder.h>
+
+extern void update_atime (struct inode *);
+#define UPDATE_ATIME(inode) update_atime (inode)
+
+extern void buffer_init(unsigned long);
+extern void inode_init(unsigned long);
+
+/* bh state bits */
+#define BH_Uptodate    0       /* 1 if the buffer contains valid data */
+#define BH_Dirty       1       /* 1 if the buffer is dirty */
+#define BH_Lock                2       /* 1 if the buffer is locked */
+#define BH_Req         3       /* 0 if the buffer has been invalidated */
+#define BH_Mapped      4       /* 1 if the buffer has a disk mapping */
+#define BH_New         5       /* 1 if the buffer is new and not yet written out */
+#define BH_Protected   6       /* 1 if the buffer is protected */
+
+/*
+ * Try to keep the most commonly used fields in single cache lines (16
+ * bytes) to improve performance.  This ordering should be
+ * particularly beneficial on 32-bit processors.
+ * 
+ * We use the first 16 bytes for the data which is used in searches
+ * over the block hash lists (ie. getblk() and friends).
+ * 
+ * The second 16 bytes we use for lru buffer scans, as used by
+ * sync_buffers() and refill_freelist().  -- sct
+ */
+struct buffer_head {
+       /* First cache line: */
+       struct buffer_head *b_next;     /* Hash queue list */
+       unsigned long b_blocknr;        /* block number */
+       unsigned short b_size;          /* block size */
+       unsigned short b_list;          /* List that this buffer appears */
+       kdev_t b_dev;                   /* device (B_FREE = free) */
+
+       atomic_t b_count;               /* users using this block */
+       kdev_t b_rdev;                  /* Real device */
+       unsigned long b_state;          /* buffer state bitmap (see above) */
+       unsigned long b_flushtime;      /* Time when (dirty) buffer should be written */
+
+       struct buffer_head *b_next_free;/* lru/free list linkage */
+       struct buffer_head *b_prev_free;/* doubly linked list of buffers */
+       struct buffer_head *b_this_page;/* circular list of buffers in one page */
+       struct buffer_head *b_reqnext;  /* request queue */
+
+       struct buffer_head **b_pprev;   /* doubly linked list of hash-queue */
+       char * b_data;                  /* pointer to data block */
+       struct page *b_page;            /* the page this bh is mapped to */
+       void (*b_end_io)(struct buffer_head *bh, int uptodate); /* I/O completion */
+       void *b_private;                /* reserved for b_end_io */
+
+       unsigned long b_rsector;        /* Real buffer location on disk */
+       wait_queue_head_t b_wait;
+
+       struct inode *       b_inode;
+       struct list_head     b_inode_buffers;   /* doubly linked list of inode dirty buffers */
+};
+
+typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
+void init_buffer(struct buffer_head *, bh_end_io_t *, void *);
+
+#define __buffer_state(bh, state)      (((bh)->b_state & (1UL << BH_##state)) != 0)
+
+#define buffer_uptodate(bh)    __buffer_state(bh,Uptodate)
+#define buffer_dirty(bh)       __buffer_state(bh,Dirty)
+#define buffer_locked(bh)      __buffer_state(bh,Lock)
+#define buffer_req(bh)         __buffer_state(bh,Req)
+#define buffer_mapped(bh)      __buffer_state(bh,Mapped)
+#define buffer_new(bh)         __buffer_state(bh,New)
+#define buffer_protected(bh)   __buffer_state(bh,Protected)
+
+#define bh_offset(bh)          ((unsigned long)(bh)->b_data & ~PAGE_MASK)
+
+extern void set_bh_page(struct buffer_head *bh, struct page *page, unsigned long offset);
+
+#define touch_buffer(bh)       SetPageReferenced(bh->b_page)
+
+
+#include <linux/pipe_fs_i.h>
+#include <linux/minix_fs_i.h>
+#include <linux/ext2_fs_i.h>
+#include <linux/hpfs_fs_i.h>
+#include <linux/ntfs_fs_i.h>
+#include <linux/msdos_fs_i.h>
+#include <linux/umsdos_fs_i.h>
+#include <linux/iso_fs_i.h>
+#include <linux/nfs_fs_i.h>
+#include <linux/sysv_fs_i.h>
+#include <linux/affs_fs_i.h>
+#include <linux/ufs_fs_i.h>
+#include <linux/efs_fs_i.h>
+#include <linux/coda_fs_i.h>
+#include <linux/romfs_fs_i.h>
+#include <linux/shmem_fs.h>
+#include <linux/smb_fs_i.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/adfs_fs_i.h>
+#include <linux/qnx4_fs_i.h>
+#include <linux/reiserfs_fs_i.h>
+#include <linux/bfs_fs_i.h>
+#include <linux/udf_fs_i.h>
+#include <linux/ncp_fs_i.h>
+#include <linux/proc_fs_i.h>
+#include <linux/usbdev_fs_i.h>
+
+/*
+ * Attribute flags.  These should be or-ed together to figure out what
+ * has been changed!
+ */
+#define ATTR_MODE      1
+#define ATTR_UID       2
+#define ATTR_GID       4
+#define ATTR_SIZE      8
+#define ATTR_ATIME     16
+#define ATTR_MTIME     32
+#define ATTR_CTIME     64
+#define ATTR_ATIME_SET 128
+#define ATTR_MTIME_SET 256
+#define ATTR_FORCE     512     /* Not a change, but a change it */
+#define ATTR_ATTR_FLAG 1024
+
+/*
+ * This is the Inode Attributes structure, used for notify_change().  It
+ * uses the above definitions as flags, to know which values have changed.
+ * Also, in this manner, a Filesystem can look at only the values it cares
+ * about.  Basically, these are the attributes that the VFS layer can
+ * request to change from the FS layer.
+ *
+ * Derek Atkins <warlord@MIT.EDU> 94-10-20
+ */
+struct iattr {
+       unsigned int    ia_valid;
+       umode_t         ia_mode;
+       uid_t           ia_uid;
+       gid_t           ia_gid;
+       loff_t          ia_size;
+       time_t          ia_atime;
+       time_t          ia_mtime;
+       time_t          ia_ctime;
+       unsigned int    ia_attr_flags;
+};
+
+/*
+ * This is the inode attributes flag definitions
+ */
+#define ATTR_FLAG_SYNCRONOUS   1       /* Syncronous write */
+#define ATTR_FLAG_NOATIME      2       /* Don't update atime */
+#define ATTR_FLAG_APPEND       4       /* Append-only file */
+#define ATTR_FLAG_IMMUTABLE    8       /* Immutable file */
+#define ATTR_FLAG_NODIRATIME   16      /* Don't update atime for directory */
+
+/*
+ * Includes for diskquotas and mount structures.
+ */
+#include <linux/quota.h>
+#include <linux/mount.h>
+
+/*
+ * oh the beauties of C type declarations.
+ */
+struct page;
+struct address_space;
+
+struct address_space_operations {
+       int (*writepage)(struct page *);
+       int (*readpage)(struct file *, struct page *);
+       int (*sync_page)(struct page *);
+       int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
+       int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
+       /* Unfortunately this kludge is needed for FIBMAP. Don't use it */
+       int (*bmap)(struct address_space *, long);
+};
+
+struct address_space {
+       struct list_head        clean_pages;    /* list of clean pages */
+       struct list_head        dirty_pages;    /* list of dirty pages */
+       struct list_head        locked_pages;   /* list of locked pages */
+       unsigned long           nrpages;        /* number of total pages */
+       struct address_space_operations *a_ops; /* methods */
+       struct inode            *host;          /* owner: inode, block_device */
+       struct vm_area_struct   *i_mmap;        /* list of private mappings */
+       struct vm_area_struct   *i_mmap_shared; /* list of shared mappings */
+       spinlock_t              i_shared_lock;  /* and spinlock protecting it */
+       int                     gfp_mask;       /* how to allocate the pages */
+};
+
+struct char_device {
+       struct list_head        hash;
+       atomic_t                count;
+       dev_t                   dev;
+       atomic_t                openers;
+       struct semaphore        sem;
+};
+
+struct block_device {
+       struct list_head        bd_hash;
+       atomic_t                bd_count;
+/*     struct address_space    bd_data; */
+       dev_t                   bd_dev;  /* not a kdev_t - it's a search key */
+       atomic_t                bd_openers;
+       const struct block_device_operations *bd_op;
+       struct semaphore        bd_sem; /* open/close mutex */
+};
+
+struct inode {
+       struct list_head        i_hash;
+       struct list_head        i_list;
+       struct list_head        i_dentry;
+       
+       struct list_head        i_dirty_buffers;
+
+       unsigned long           i_ino;
+       atomic_t                i_count;
+       kdev_t                  i_dev;
+       umode_t                 i_mode;
+       nlink_t                 i_nlink;
+       uid_t                   i_uid;
+       gid_t                   i_gid;
+       kdev_t                  i_rdev;
+       loff_t                  i_size;
+       time_t                  i_atime;
+       time_t                  i_mtime;
+       time_t                  i_ctime;
+       unsigned long           i_blksize;
+       unsigned long           i_blocks;
+       unsigned long           i_version;
+       struct semaphore        i_sem;
+       struct semaphore        i_zombie;
+       struct inode_operations *i_op;
+       struct file_operations  *i_fop; /* former ->i_op->default_file_ops */
+       struct super_block      *i_sb;
+       wait_queue_head_t       i_wait;
+       struct file_lock        *i_flock;
+       struct address_space    *i_mapping;
+       struct address_space    i_data; 
+       struct dquot            *i_dquot[MAXQUOTAS];
+       /* These three should probably be a union */
+       struct pipe_inode_info  *i_pipe;
+       struct block_device     *i_bdev;
+       struct char_device      *i_cdev;
+
+       unsigned long           i_dnotify_mask; /* Directory notify events */
+       struct dnotify_struct   *i_dnotify; /* for directory notifications */
+
+       unsigned long           i_state;
+
+       unsigned int            i_flags;
+       unsigned char           i_sock;
+
+       atomic_t                i_writecount;
+       unsigned int            i_attr_flags;
+       __u32                   i_generation;
+       union {
+               struct minix_inode_info         minix_i;
+               struct ext2_inode_info          ext2_i;
+               struct hpfs_inode_info          hpfs_i;
+               struct ntfs_inode_info          ntfs_i;
+               struct msdos_inode_info         msdos_i;
+               struct umsdos_inode_info        umsdos_i;
+               struct iso_inode_info           isofs_i;
+               struct nfs_inode_info           nfs_i;
+               struct sysv_inode_info          sysv_i;
+               struct affs_inode_info          affs_i;
+               struct ufs_inode_info           ufs_i;
+               struct efs_inode_info           efs_i;
+               struct romfs_inode_info         romfs_i;
+               struct shmem_inode_info         shmem_i;
+               struct coda_inode_info          coda_i;
+               struct smb_inode_info           smbfs_i;
+               struct hfs_inode_info           hfs_i;
+               struct adfs_inode_info          adfs_i;
+               struct qnx4_inode_info          qnx4_i;
+               struct reiserfs_inode_info      reiserfs_i;
+               struct bfs_inode_info           bfs_i;
+               struct udf_inode_info           udf_i;
+               struct ncp_inode_info           ncpfs_i;
+               struct proc_inode_info          proc_i;
+               struct socket                   socket_i;
+               struct usbdev_inode_info        usbdev_i;
+               void                            *generic_ip;
+       } u;
+};
+
+struct fown_struct {
+       int pid;                /* pid or -pgrp where SIGIO should be sent */
+       uid_t uid, euid;        /* uid/euid of process setting the owner */
+       int signum;             /* posix.1b rt signal to be delivered on IO */
+};
+
+struct file {
+       struct list_head        f_list;
+       struct dentry           *f_dentry;
+       struct vfsmount         *f_vfsmnt;
+       struct file_operations  *f_op;
+       atomic_t                f_count;
+       unsigned int            f_flags;
+       mode_t                  f_mode;
+       loff_t                  f_pos;
+       unsigned long           f_reada, f_ramax, f_raend, f_ralen, f_rawin;
+       struct fown_struct      f_owner;
+       unsigned int            f_uid, f_gid;
+       int                     f_error;
+
+       unsigned long           f_version;
+
+       /* needed for tty driver, and maybe others */
+       void                    *private_data;
+};
+extern spinlock_t files_lock;
+#define file_list_lock() spin_lock(&files_lock);
+#define file_list_unlock() spin_unlock(&files_lock);
+
+#define get_file(x)    atomic_inc(&(x)->f_count)
+#define file_count(x)  atomic_read(&(x)->f_count)
+
+extern int init_private_file(struct file *, struct dentry *, int);
+
+#define        MAX_NON_LFS     ((1UL<<31) - 1)
+
+#define FL_POSIX       1
+#define FL_FLOCK       2
+#define FL_BROKEN      4       /* broken flock() emulation */
+#define FL_ACCESS      8       /* for processes suspended by mandatory locking */
+#define FL_LOCKD       16      /* lock held by rpc.lockd */
+#define FL_LEASE       32      /* lease held on this file */
+
+/*
+ * The POSIX file lock owner is determined by
+ * the "struct files_struct" in the thread group
+ * (or NULL for no owner - BSD locks).
+ *
+ * Lockd stuffs a "host" pointer into this.
+ */
+typedef struct files_struct *fl_owner_t;
+
+struct file_lock {
+       struct file_lock *fl_next;      /* singly linked list for this inode  */
+       struct list_head fl_link;       /* doubly linked list of all locks */
+       struct list_head fl_block;      /* circular list of blocked processes */
+       fl_owner_t fl_owner;
+       unsigned int fl_pid;
+       wait_queue_head_t fl_wait;
+       struct file *fl_file;
+       unsigned char fl_flags;
+       unsigned char fl_type;
+       loff_t fl_start;
+       loff_t fl_end;
+
+       void (*fl_notify)(struct file_lock *);  /* unblock callback */
+       void (*fl_insert)(struct file_lock *);  /* lock insertion callback */
+       void (*fl_remove)(struct file_lock *);  /* lock removal callback */
+
+       struct fasync_struct *  fl_fasync; /* for lease break notifications */
+
+       union {
+               struct nfs_lock_info    nfs_fl;
+       } fl_u;
+};
+
+/* The following constant reflects the upper bound of the file/locking space */
+#ifndef OFFSET_MAX
+#define INT_LIMIT(x)   (~((x)1 << (sizeof(x)*8 - 1)))
+#define OFFSET_MAX     INT_LIMIT(loff_t)
+#define OFFT_OFFSET_MAX        INT_LIMIT(off_t)
+#endif
+
+extern struct list_head file_lock_list;
+
+#include <linux/fcntl.h>
+
+extern int fcntl_getlk(unsigned int, struct flock *);
+extern int fcntl_setlk(unsigned int, unsigned int, struct flock *);
+
+extern int fcntl_getlk64(unsigned int, struct flock64 *);
+extern int fcntl_setlk64(unsigned int, unsigned int, struct flock64 *);
+
+/* fs/locks.c */
+extern void locks_init_lock(struct file_lock *);
+extern void locks_copy_lock(struct file_lock *, struct file_lock *);
+extern void locks_remove_posix(struct file *, fl_owner_t);
+extern void locks_remove_flock(struct file *);
+extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
+extern int posix_lock_file(struct file *, struct file_lock *, unsigned int);
+extern void posix_block_lock(struct file_lock *, struct file_lock *);
+extern void posix_unblock_lock(struct file_lock *);
+extern int __get_lease(struct inode *inode, unsigned int flags);
+extern time_t lease_get_mtime(struct inode *);
+extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
+extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
+
+struct fasync_struct {
+       int     magic;
+       int     fa_fd;
+       struct  fasync_struct   *fa_next; /* singly linked list */
+       struct  file            *fa_file;
+};
+
+#define FASYNC_MAGIC 0x4601
+
+/* SMP safe fasync helpers: */
+extern int fasync_helper(int, struct file *, int, struct fasync_struct **);
+/* can be called from interrupts */
+extern void kill_fasync(struct fasync_struct **, int, int);
+/* only for net: no internal synchronization */
+extern void __kill_fasync(struct fasync_struct *, int, int);
+
+struct nameidata {
+       struct dentry *dentry;
+       struct vfsmount *mnt;
+       struct qstr last;
+       unsigned int flags;
+       int last_type;
+};
+
+#define DQUOT_USR_ENABLED      0x01            /* User diskquotas enabled */
+#define DQUOT_GRP_ENABLED      0x02            /* Group diskquotas enabled */
+
+struct quota_mount_options
+{
+       unsigned int flags;                     /* Flags for diskquotas on this device */
+       struct semaphore dqio_sem;              /* lock device while I/O in progress */
+       struct semaphore dqoff_sem;             /* serialize quota_off() and quota_on() on device */
+       struct file *files[MAXQUOTAS];          /* fp's to quotafiles */
+       time_t inode_expire[MAXQUOTAS];         /* expiretime for inode-quota */
+       time_t block_expire[MAXQUOTAS];         /* expiretime for block-quota */
+       char rsquash[MAXQUOTAS];                /* for quotas threat root as any other user */
+};
+
+/*
+ *     Umount options
+ */
+
+#define MNT_FORCE      0x00000001      /* Attempt to forcibily umount */
+
+#include <linux/minix_fs_sb.h>
+#include <linux/ext2_fs_sb.h>
+#include <linux/hpfs_fs_sb.h>
+#include <linux/ntfs_fs_sb.h>
+#include <linux/msdos_fs_sb.h>
+#include <linux/iso_fs_sb.h>
+#include <linux/nfs_fs_sb.h>
+#include <linux/sysv_fs_sb.h>
+#include <linux/affs_fs_sb.h>
+#include <linux/ufs_fs_sb.h>
+#include <linux/efs_fs_sb.h>
+#include <linux/romfs_fs_sb.h>
+#include <linux/smb_fs_sb.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/adfs_fs_sb.h>
+#include <linux/qnx4_fs_sb.h>
+#include <linux/reiserfs_fs_sb.h>
+#include <linux/bfs_fs_sb.h>
+#include <linux/udf_fs_sb.h>
+#include <linux/ncp_fs_sb.h>
+#include <linux/usbdev_fs_sb.h>
+
+extern struct list_head super_blocks;
+
+#define sb_entry(list) list_entry((list), struct super_block, s_list)
+struct super_block {
+       struct list_head        s_list;         /* Keep this first */
+       kdev_t                  s_dev;
+       unsigned long           s_blocksize;
+       unsigned char           s_blocksize_bits;
+       unsigned char           s_dirt;
+       unsigned long long      s_maxbytes;     /* Max file size */
+       struct file_system_type *s_type;
+       struct super_operations *s_op;
+       struct dquot_operations *dq_op;
+       unsigned long           s_flags;
+       unsigned long           s_magic;
+       struct dentry           *s_root;
+       struct rw_semaphore     s_umount;
+       struct semaphore        s_lock;
+
+       struct list_head        s_dirty;        /* dirty inodes */
+       struct list_head        s_locked_inodes;/* inodes being synced */
+       struct list_head        s_files;
+
+       struct block_device     *s_bdev;
+       struct list_head        s_mounts;       /* vfsmount(s) of this one */
+       struct quota_mount_options s_dquot;     /* Diskquota specific options */
+
+       union {
+               struct minix_sb_info    minix_sb;
+               struct ext2_sb_info     ext2_sb;
+               struct hpfs_sb_info     hpfs_sb;
+               struct ntfs_sb_info     ntfs_sb;
+               struct msdos_sb_info    msdos_sb;
+               struct isofs_sb_info    isofs_sb;
+               struct nfs_sb_info      nfs_sb;
+               struct sysv_sb_info     sysv_sb;
+               struct affs_sb_info     affs_sb;
+               struct ufs_sb_info      ufs_sb;
+               struct efs_sb_info      efs_sb;
+               struct shmem_sb_info    shmem_sb;
+               struct romfs_sb_info    romfs_sb;
+               struct smb_sb_info      smbfs_sb;
+               struct hfs_sb_info      hfs_sb;
+               struct adfs_sb_info     adfs_sb;
+               struct qnx4_sb_info     qnx4_sb;
+               struct reiserfs_sb_info reiserfs_sb;
+               struct bfs_sb_info      bfs_sb;
+               struct udf_sb_info      udf_sb;
+               struct ncp_sb_info      ncpfs_sb;
+               struct usbdev_sb_info   usbdevfs_sb;
+               void                    *generic_sbp;
+       } u;
+       /*
+        * The next field is for VFS *only*. No filesystems have any business
+        * even looking at it. You had been warned.
+        */
+       struct semaphore s_vfs_rename_sem;      /* Kludge */
+
+       /* The next field is used by knfsd when converting a (inode number based)
+        * file handle into a dentry. As it builds a path in the dcache tree from
+        * the bottom up, there may for a time be a subpath of dentrys which is not
+        * connected to the main tree.  This semaphore ensure that there is only ever
+        * one such free path per filesystem.  Note that unconnected files (or other
+        * non-directories) are allowed, but not unconnected diretories.
+        */
+       struct semaphore s_nfsd_free_path_sem;
+};
+
+/*
+ * VFS helper functions..
+ */
+extern int vfs_create(struct inode *, struct dentry *, int);
+extern int vfs_mkdir(struct inode *, struct dentry *, int);
+extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t);
+extern int vfs_symlink(struct inode *, struct dentry *, const char *);
+extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
+extern int vfs_rmdir(struct inode *, struct dentry *);
+extern int vfs_unlink(struct inode *, struct dentry *);
+extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+
+/*
+ * File types
+ */
+#define DT_UNKNOWN     0
+#define DT_FIFO                1
+#define DT_CHR         2
+#define DT_DIR         4
+#define DT_BLK         6
+#define DT_REG         8
+#define DT_LNK         10
+#define DT_SOCK                12
+#define DT_WHT         14
+
+/*
+ * This is the "filldir" function type, used by readdir() to let
+ * the kernel specify what kind of dirent layout it wants to have.
+ * This allows the kernel to read directories into kernel space or
+ * to have different dirent layouts depending on the binary type.
+ */
+typedef int (*filldir_t)(void *, const char *, int, off_t, ino_t, unsigned);
+
+struct block_device_operations {
+       int (*open) (struct inode *, struct file *);
+       int (*release) (struct inode *, struct file *);
+       int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
+       int (*check_media_change) (kdev_t);
+       int (*revalidate) (kdev_t);
+};
+
+/*
+ * NOTE:
+ * read, write, poll, fsync, readv, writev can be called
+ *   without the big kernel lock held in all filesystems.
+ */
+struct file_operations {
+       struct module *owner;
+       loff_t (*llseek) (struct file *, loff_t, int);
+       ssize_t (*read) (struct file *, char *, size_t, loff_t *);
+       ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
+       int (*readdir) (struct file *, void *, filldir_t);
+       unsigned int (*poll) (struct file *, struct poll_table_struct *);
+       int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
+       int (*mmap) (struct file *, struct vm_area_struct *);
+       int (*open) (struct inode *, struct file *);
+       int (*flush) (struct file *);
+       int (*release) (struct inode *, struct file *);
+       int (*fsync) (struct file *, struct dentry *, int datasync);
+       int (*fasync) (int, struct file *, int);
+       int (*lock) (struct file *, int, struct file_lock *);
+       ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
+       ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
+       ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
+       unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+};
+
+struct inode_operations {
+       int (*create) (struct inode *,struct dentry *,int);
+       struct dentry * (*lookup) (struct inode *,struct dentry *);
+       int (*link) (struct dentry *,struct inode *,struct dentry *);
+       int (*unlink) (struct inode *,struct dentry *);
+       int (*symlink) (struct inode *,struct dentry *,const char *);
+       int (*mkdir) (struct inode *,struct dentry *,int);
+       int (*rmdir) (struct inode *,struct dentry *);
+       int (*mknod) (struct inode *,struct dentry *,int,int);
+       int (*rename) (struct inode *, struct dentry *,
+                       struct inode *, struct dentry *);
+       int (*readlink) (struct dentry *, char *,int);
+       int (*follow_link) (struct dentry *, struct nameidata *);
+       void (*truncate) (struct inode *);
+       int (*permission) (struct inode *, int);
+       int (*revalidate) (struct dentry *);
+       int (*setattr) (struct dentry *, struct iattr *);
+       int (*getattr) (struct dentry *, struct iattr *);
+};
+
+/*
+ * NOTE: write_inode, delete_inode, clear_inode, put_inode can be called
+ * without the big kernel lock held in all filesystems.
+ */
+struct super_operations {
+       void (*read_inode) (struct inode *);
+  
+       /* reiserfs kludge.  reiserfs needs 64 bits of information to
+       ** find an inode.  We are using the read_inode2 call to get
+       ** that information.  We don't like this, and are waiting on some
+       ** VFS changes for the real solution.
+       ** iget4 calls read_inode2, iff it is defined
+       */
+       void (*read_inode2) (struct inode *, void *) ;
+       void (*dirty_inode) (struct inode *);
+       void (*write_inode) (struct inode *, int);
+       void (*put_inode) (struct inode *);
+       void (*delete_inode) (struct inode *);
+       void (*put_super) (struct super_block *);
+       void (*write_super) (struct super_block *);
+       void (*write_super_lockfs) (struct super_block *);
+       void (*unlockfs) (struct super_block *);
+       int (*statfs) (struct super_block *, struct statfs *);
+       int (*remount_fs) (struct super_block *, int *, char *);
+       void (*clear_inode) (struct inode *);
+       void (*umount_begin) (struct super_block *);
+};
+
+/* Inode state bits.. */
+#define I_DIRTY_SYNC           1 /* Not dirty enough for O_DATASYNC */
+#define I_DIRTY_DATASYNC       2 /* Data-related inode changes pending */
+#define I_DIRTY_PAGES          4 /* Data-related inode changes pending */
+#define I_LOCK                 8
+#define I_FREEING              16
+#define I_CLEAR                        32
+
+#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
+
+extern void __mark_inode_dirty(struct inode *, int);
+static inline void mark_inode_dirty(struct inode *inode)
+{
+       __mark_inode_dirty(inode, I_DIRTY);
+}
+
+static inline void mark_inode_dirty_sync(struct inode *inode)
+{
+       __mark_inode_dirty(inode, I_DIRTY_SYNC);
+}
+
+static inline void mark_inode_dirty_pages(struct inode *inode)
+{
+       __mark_inode_dirty(inode, I_DIRTY_PAGES);
+}
+
+struct dquot_operations {
+       void (*initialize) (struct inode *, short);
+       void (*drop) (struct inode *);
+       int (*alloc_block) (const struct inode *, unsigned long, char);
+       int (*alloc_inode) (const struct inode *, unsigned long);
+       void (*free_block) (const struct inode *, unsigned long);
+       void (*free_inode) (const struct inode *, unsigned long);
+       int (*transfer) (struct dentry *, struct iattr *);
+};
+
+struct file_system_type {
+       const char *name;
+       int fs_flags;
+       struct super_block *(*read_super) (struct super_block *, void *, int);
+       struct module *owner;
+       struct vfsmount *kern_mnt; /* For kernel mount, if it's FS_SINGLE fs */
+       struct file_system_type * next;
+};
+
+#define DECLARE_FSTYPE(var,type,read,flags) \
+struct file_system_type var = { \
+       name:           type, \
+       read_super:     read, \
+       fs_flags:       flags, \
+       owner:          THIS_MODULE, \
+}
+
+#define DECLARE_FSTYPE_DEV(var,type,read) \
+       DECLARE_FSTYPE(var,type,read,FS_REQUIRES_DEV)
+
+/* Alas, no aliases. Too much hassle with bringing module.h everywhere */
+#define fops_get(fops) \
+       (((fops) && (fops)->owner)      \
+               ? ( try_inc_mod_count((fops)->owner) ? (fops) : NULL ) \
+               : (fops))
+
+#define fops_put(fops) \
+do {   \
+       if ((fops) && (fops)->owner) \
+               __MOD_DEC_USE_COUNT((fops)->owner);     \
+} while(0)
+
+extern int register_filesystem(struct file_system_type *);
+extern int unregister_filesystem(struct file_system_type *);
+extern struct vfsmount *kern_mount(struct file_system_type *);
+extern int may_umount(struct vfsmount *);
+extern long do_mount(char *, char *, char *, unsigned long, void *);
+
+#define kern_umount mntput
+
+extern int vfs_statfs(struct super_block *, struct statfs *);
+
+/* Return value for VFS lock functions - tells locks.c to lock conventionally
+ * REALLY kosha for root NFS and nfs_lock
+ */ 
+#define LOCK_USE_CLNT 1
+
+#define FLOCK_VERIFY_READ  1
+#define FLOCK_VERIFY_WRITE 2
+
+extern int locks_mandatory_locked(struct inode *);
+extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t);
+
+/*
+ * Candidates for mandatory locking have the setgid bit set
+ * but no group execute bit -  an otherwise meaningless combination.
+ */
+#define MANDATORY_LOCK(inode) \
+       (IS_MANDLOCK(inode) && ((inode)->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+
+static inline int locks_verify_locked(struct inode *inode)
+{
+       if (MANDATORY_LOCK(inode))
+               return locks_mandatory_locked(inode);
+       return 0;
+}
+
+static inline int locks_verify_area(int read_write, struct inode *inode,
+                                   struct file *filp, loff_t offset,
+                                   size_t count)
+{
+       if (inode->i_flock && MANDATORY_LOCK(inode))
+               return locks_mandatory_area(read_write, inode, filp, offset, count);
+       return 0;
+}
+
+static inline int locks_verify_truncate(struct inode *inode,
+                                   struct file *filp,
+                                   loff_t size)
+{
+       if (inode->i_flock && MANDATORY_LOCK(inode))
+               return locks_mandatory_area(
+                       FLOCK_VERIFY_WRITE, inode, filp,
+                       size < inode->i_size ? size : inode->i_size,
+                       (size < inode->i_size ? inode->i_size - size
+                        : size - inode->i_size)
+               );
+       return 0;
+}
+
+extern inline int get_lease(struct inode *inode, unsigned int mode)
+{
+       if (inode->i_flock && (inode->i_flock->fl_flags & FL_LEASE))
+               return __get_lease(inode, mode);
+       return 0;
+}
+
+/* fs/open.c */
+
+asmlinkage long sys_open(const char *, int, int);
+asmlinkage long sys_close(unsigned int);       /* yes, it's really unsigned */
+extern int do_truncate(struct dentry *, loff_t start);
+
+extern struct file *filp_open(const char *, int, int);
+extern struct file * dentry_open(struct dentry *, struct vfsmount *, int);
+extern int filp_close(struct file *, fl_owner_t id);
+extern char * getname(const char *);
+
+/* fs/dcache.c */
+extern void vfs_caches_init(unsigned long);
+
+#define __getname()    kmem_cache_alloc(names_cachep, SLAB_KERNEL)
+#define putname(name)  kmem_cache_free(names_cachep, (void *)(name))
+
+enum {BDEV_FILE, BDEV_SWAP, BDEV_FS, BDEV_RAW};
+extern int register_blkdev(unsigned int, const char *, struct block_device_operations *);
+extern int unregister_blkdev(unsigned int, const char *);
+extern struct block_device *bdget(dev_t);
+extern void bdput(struct block_device *);
+extern struct char_device *cdget(dev_t);
+extern void cdput(struct char_device *);
+extern int blkdev_open(struct inode *, struct file *);
+extern struct file_operations def_blk_fops;
+extern struct file_operations def_fifo_fops;
+extern int ioctl_by_bdev(struct block_device *, unsigned, unsigned long);
+extern int blkdev_get(struct block_device *, mode_t, unsigned, int);
+extern int blkdev_put(struct block_device *, int);
+
+/* fs/devices.c */
+extern const struct block_device_operations *get_blkfops(unsigned int);
+extern int register_chrdev(unsigned int, const char *, struct file_operations *);
+extern int unregister_chrdev(unsigned int, const char *);
+extern int chrdev_open(struct inode *, struct file *);
+extern const char * bdevname(kdev_t);
+extern const char * cdevname(kdev_t);
+extern const char * kdevname(kdev_t);
+extern void init_special_inode(struct inode *, umode_t, int);
+
+/* Invalid inode operations -- fs/bad_inode.c */
+extern void make_bad_inode(struct inode *);
+extern int is_bad_inode(struct inode *);
+
+extern struct file_operations read_fifo_fops;
+extern struct file_operations write_fifo_fops;
+extern struct file_operations rdwr_fifo_fops;
+extern struct file_operations read_pipe_fops;
+extern struct file_operations write_pipe_fops;
+extern struct file_operations rdwr_pipe_fops;
+
+extern int fs_may_remount_ro(struct super_block *);
+
+extern int try_to_free_buffers(struct page *, int);
+extern void refile_buffer(struct buffer_head * buf);
+
+/* reiserfs_writepage needs this */
+extern void set_buffer_async_io(struct buffer_head *bh) ;
+
+#define BUF_CLEAN      0
+#define BUF_LOCKED     1       /* Buffers scheduled for write */
+#define BUF_DIRTY      2       /* Dirty buffers, not yet scheduled for write */
+#define BUF_PROTECTED  3       /* Ramdisk persistent storage */
+#define NR_LIST                4
+
+/*
+ * This is called by bh->b_end_io() handlers when I/O has completed.
+ */
+static inline void mark_buffer_uptodate(struct buffer_head * bh, int on)
+{
+       if (on)
+               set_bit(BH_Uptodate, &bh->b_state);
+       else
+               clear_bit(BH_Uptodate, &bh->b_state);
+}
+
+#define atomic_set_buffer_clean(bh) test_and_clear_bit(BH_Dirty, &(bh)->b_state)
+
+static inline void __mark_buffer_clean(struct buffer_head *bh)
+{
+       refile_buffer(bh);
+}
+
+static inline void mark_buffer_clean(struct buffer_head * bh)
+{
+       if (atomic_set_buffer_clean(bh))
+               __mark_buffer_clean(bh);
+}
+
+#define atomic_set_buffer_protected(bh) test_and_set_bit(BH_Protected, &(bh)->b_state)
+
+static inline void __mark_buffer_protected(struct buffer_head *bh)
+{
+       refile_buffer(bh);
+}
+
+static inline void mark_buffer_protected(struct buffer_head * bh)
+{
+       if (!atomic_set_buffer_protected(bh))
+               __mark_buffer_protected(bh);
+}
+
+extern void FASTCALL(__mark_buffer_dirty(struct buffer_head *bh));
+extern void FASTCALL(mark_buffer_dirty(struct buffer_head *bh));
+
+#define atomic_set_buffer_dirty(bh) test_and_set_bit(BH_Dirty, &(bh)->b_state)
+
+/*
+ * If an error happens during the make_request, this function
+ * has to be recalled. It marks the buffer as clean and not
+ * uptodate, and it notifys the upper layer about the end
+ * of the I/O.
+ */
+static inline void buffer_IO_error(struct buffer_head * bh)
+{
+       mark_buffer_clean(bh);
+       /*
+        * b_end_io has to clear the BH_Uptodate bitflag in the error case!
+        */
+       bh->b_end_io(bh, 0);
+}
+
+extern void buffer_insert_inode_queue(struct buffer_head *, struct inode *);
+static inline void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode)
+{
+       mark_buffer_dirty(bh);
+       buffer_insert_inode_queue(bh, inode);
+}
+
+extern void balance_dirty(kdev_t);
+extern int check_disk_change(kdev_t);
+extern int invalidate_inodes(struct super_block *);
+extern int invalidate_device(kdev_t, int);
+extern void invalidate_inode_pages(struct inode *);
+extern void invalidate_inode_buffers(struct inode *);
+#define invalidate_buffers(dev)        __invalidate_buffers((dev), 0)
+#define destroy_buffers(dev)   __invalidate_buffers((dev), 1)
+extern void __invalidate_buffers(kdev_t dev, int);
+extern void sync_inodes(kdev_t);
+extern void sync_unlocked_inodes(void);
+extern void write_inode_now(struct inode *, int);
+extern void sync_dev(kdev_t);
+extern int fsync_dev(kdev_t);
+extern int fsync_super(struct super_block *);
+extern void sync_inodes_sb(struct super_block *);
+extern int fsync_inode_buffers(struct inode *);
+extern int osync_inode_buffers(struct inode *);
+extern int inode_has_buffers(struct inode *);
+extern void filemap_fdatasync(struct address_space *);
+extern void filemap_fdatawait(struct address_space *);
+extern void sync_supers(kdev_t);
+extern int bmap(struct inode *, int);
+extern int notify_change(struct dentry *, struct iattr *);
+extern int permission(struct inode *, int);
+extern int vfs_permission(struct inode *, int);
+extern int get_write_access(struct inode *);
+extern int deny_write_access(struct file *);
+static inline void put_write_access(struct inode * inode)
+{
+       atomic_dec(&inode->i_writecount);
+}
+static inline void allow_write_access(struct file *file)
+{
+       if (file)
+               atomic_inc(&file->f_dentry->d_inode->i_writecount);
+}
+extern int do_pipe(int *);
+
+extern int open_namei(const char *, int, int, struct nameidata *);
+
+extern int kernel_read(struct file *, unsigned long, char *, unsigned long);
+extern struct file * open_exec(const char *);
+/* fs/dcache.c -- generic fs support functions */
+extern int is_subdir(struct dentry *, struct dentry *);
+extern ino_t find_inode_number(struct dentry *, struct qstr *);
+
+/*
+ * Kernel pointers have redundant information, so we can use a
+ * scheme where we can return either an error code or a dentry
+ * pointer with the same return value.
+ *
+ * This should be a per-architecture thing, to allow different
+ * error and pointer decisions.
+ */
+static inline void *ERR_PTR(long error)
+{
+       return (void *) error;
+}
+
+static inline long PTR_ERR(const void *ptr)
+{
+       return (long) ptr;
+}
+
+static inline long IS_ERR(const void *ptr)
+{
+       return (unsigned long)ptr > (unsigned long)-1000L;
+}
+
+/*
+ * The bitmask for a lookup event:
+ *  - follow links at the end
+ *  - require a directory
+ *  - ending slashes ok even for nonexistent files
+ *  - internal "there are more path compnents" flag
+ */
+#define LOOKUP_FOLLOW          (1)
+#define LOOKUP_DIRECTORY       (2)
+#define LOOKUP_CONTINUE                (4)
+#define LOOKUP_POSITIVE                (8)
+#define LOOKUP_PARENT          (16)
+#define LOOKUP_NOALT           (32)
+/*
+ * Type of the last component on LOOKUP_PARENT
+ */
+enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
+
+/*
+ * "descriptor" for what we're up to with a read for sendfile().
+ * This allows us to use the same read code yet
+ * have multiple different users of the data that
+ * we read from a file.
+ *
+ * The simplest case just copies the data to user
+ * mode.
+ */
+typedef struct {
+       size_t written;
+       size_t count;
+       char * buf;
+       int error;
+} read_descriptor_t;
+
+typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, unsigned long);
+
+/* needed for stackable file system support */
+extern loff_t default_llseek(struct file *file, loff_t offset, int origin);
+
+extern int __user_walk(const char *, unsigned, struct nameidata *);
+extern int path_init(const char *, unsigned, struct nameidata *);
+extern int path_walk(const char *, struct nameidata *);
+extern void path_release(struct nameidata *);
+extern int follow_down(struct vfsmount **, struct dentry **);
+extern int follow_up(struct vfsmount **, struct dentry **);
+extern struct dentry * lookup_one_len(const char *, struct dentry *, int);
+extern struct dentry * lookup_hash(struct qstr *, struct dentry *);
+#define user_path_walk(name,nd)         __user_walk(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd)
+#define user_path_walk_link(name,nd) __user_walk(name, LOOKUP_POSITIVE, nd)
+
+extern void iput(struct inode *);
+extern void force_delete(struct inode *);
+extern struct inode * igrab(struct inode *);
+extern ino_t iunique(struct super_block *, ino_t);
+
+typedef int (*find_inode_t)(struct inode *, unsigned long, void *);
+extern struct inode * iget4(struct super_block *, unsigned long, find_inode_t, void *);
+static inline struct inode *iget(struct super_block *sb, unsigned long ino)
+{
+       return iget4(sb, ino, NULL, NULL);
+}
+
+extern void clear_inode(struct inode *);
+extern struct inode * get_empty_inode(void);
+static inline struct inode * new_inode(struct super_block *sb)
+{
+       struct inode *inode = get_empty_inode();
+       if (inode) {
+               inode->i_sb = sb;
+               inode->i_dev = sb->s_dev;
+       }
+       return inode;
+}
+extern void remove_suid(struct inode *inode);
+
+extern void insert_inode_hash(struct inode *);
+extern void remove_inode_hash(struct inode *);
+extern struct file * get_empty_filp(void);
+extern void file_move(struct file *f, struct list_head *list);
+extern void file_moveto(struct file *new, struct file *old);
+extern struct buffer_head * get_hash_table(kdev_t, int, int);
+extern struct buffer_head * getblk(kdev_t, int, int);
+extern void ll_rw_block(int, int, struct buffer_head * bh[]);
+extern void submit_bh(int, struct buffer_head *);
+extern int is_read_only(kdev_t);
+extern void __brelse(struct buffer_head *);
+static inline void brelse(struct buffer_head *buf)
+{
+       if (buf)
+               __brelse(buf);
+}
+extern void __bforget(struct buffer_head *);
+static inline void bforget(struct buffer_head *buf)
+{
+       if (buf)
+               __bforget(buf);
+}
+extern void set_blocksize(kdev_t, int);
+extern struct buffer_head * bread(kdev_t, int, int);
+extern void wakeup_bdflush(int wait);
+
+extern int brw_page(int, struct page *, kdev_t, int [], int);
+
+typedef int (get_block_t)(struct inode*,long,struct buffer_head*,int);
+
+/* Generic buffer handling for block filesystems.. */
+extern int block_flushpage(struct page *, unsigned long);
+extern int block_symlink(struct inode *, const char *, int);
+extern int block_write_full_page(struct page*, get_block_t*);
+extern int block_read_full_page(struct page*, get_block_t*);
+extern int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*);
+extern int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*,
+                               unsigned long *);
+extern int block_sync_page(struct page *);
+
+int generic_block_bmap(struct address_space *, long, get_block_t *);
+int generic_commit_write(struct file *, struct page *, unsigned, unsigned);
+int block_truncate_page(struct address_space *, loff_t, get_block_t *);
+
+extern int waitfor_one_page(struct page*);
+extern int generic_file_mmap(struct file *, struct vm_area_struct *);
+extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
+extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
+extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *);
+extern void do_generic_file_read(struct file *, loff_t *, read_descriptor_t *, read_actor_t);
+
+extern ssize_t generic_read_dir(struct file *, char *, size_t, loff_t *);
+
+extern struct file_operations generic_ro_fops;
+
+extern int vfs_readlink(struct dentry *, char *, int, const char *);
+extern int vfs_follow_link(struct nameidata *, const char *);
+extern int page_readlink(struct dentry *, char *, int);
+extern int page_follow_link(struct dentry *, struct nameidata *);
+extern struct inode_operations page_symlink_inode_operations;
+
+extern int vfs_readdir(struct file *, filldir_t, void *);
+extern int dcache_readdir(struct file *, void *, filldir_t);
+
+extern struct file_system_type *get_fs_type(const char *name);
+extern struct super_block *get_super(kdev_t);
+extern void put_super(kdev_t);
+static inline int is_mounted(kdev_t dev)
+{
+       struct super_block *sb = get_super(dev);
+       if (sb) {
+               /* drop_super(sb); will go here */
+               return 1;
+       }
+       return 0;
+}
+unsigned long generate_cluster(kdev_t, int b[], int);
+unsigned long generate_cluster_swab32(kdev_t, int b[], int);
+extern kdev_t ROOT_DEV;
+extern char root_device_name[];
+
+
+extern void show_buffers(void);
+extern void mount_root(void);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+extern kdev_t real_root_dev;
+extern int change_root(kdev_t, const char *);
+#endif
+
+extern ssize_t char_read(struct file *, char *, size_t, loff_t *);
+extern ssize_t block_read(struct file *, char *, size_t, loff_t *);
+extern int read_ahead[];
+
+extern ssize_t char_write(struct file *, const char *, size_t, loff_t *);
+extern ssize_t block_write(struct file *, const char *, size_t, loff_t *);
+
+extern int file_fsync(struct file *, struct dentry *, int);
+extern int generic_buffer_fdatasync(struct inode *inode, unsigned long start_idx, unsigned long end_idx);
+extern int generic_osync_inode(struct inode *, int);
+
+extern int inode_change_ok(struct inode *, struct iattr *);
+extern void inode_setattr(struct inode *, struct iattr *);
+
+/*
+ * Common dentry functions for inclusion in the VFS
+ * or in other stackable file systems.  Some of these
+ * functions were in linux/fs/ C (VFS) files.
+ *
+ */
+
+/*
+ * Locking the parent is needed to:
+ *  - serialize directory operations
+ *  - make sure the parent doesn't change from
+ *    under us in the middle of an operation.
+ *
+ * NOTE! Right now we'd rather use a "struct inode"
+ * for this, but as I expect things to move toward
+ * using dentries instead for most things it is
+ * probably better to start with the conceptually
+ * better interface of relying on a path of dentries.
+ */
+static inline struct dentry *lock_parent(struct dentry *dentry)
+{
+       struct dentry *dir = dget(dentry->d_parent);
+
+       down(&dir->d_inode->i_sem);
+       return dir;
+}
+
+static inline struct dentry *get_parent(struct dentry *dentry)
+{
+       return dget(dentry->d_parent);
+}
+
+static inline void unlock_dir(struct dentry *dir)
+{
+       up(&dir->d_inode->i_sem);
+       dput(dir);
+}
+
+/*
+ * Whee.. Deadlock country. Happily there are only two VFS
+ * operations that does this..
+ */
+static inline void double_down(struct semaphore *s1, struct semaphore *s2)
+{
+       if (s1 != s2) {
+               if ((unsigned long) s1 < (unsigned long) s2) {
+                       struct semaphore *tmp = s2;
+                       s2 = s1; s1 = tmp;
+               }
+               down(s1);
+       }
+       down(s2);
+}
+
+/*
+ * Ewwwwwwww... _triple_ lock. We are guaranteed that the 3rd argument is
+ * not equal to 1st and not equal to 2nd - the first case (target is parent of
+ * source) would be already caught, the second is plain impossible (target is
+ * its own parent and that case would be caught even earlier). Very messy.
+ * I _think_ that it works, but no warranties - please, look it through.
+ * Pox on bloody lusers who mandated overwriting rename() for directories...
+ */
+
+static inline void triple_down(struct semaphore *s1,
+                              struct semaphore *s2,
+                              struct semaphore *s3)
+{
+       if (s1 != s2) {
+               if ((unsigned long) s1 < (unsigned long) s2) {
+                       if ((unsigned long) s1 < (unsigned long) s3) {
+                               struct semaphore *tmp = s3;
+                               s3 = s1; s1 = tmp;
+                       }
+                       if ((unsigned long) s1 < (unsigned long) s2) {
+                               struct semaphore *tmp = s2;
+                               s2 = s1; s1 = tmp;
+                       }
+               } else {
+                       if ((unsigned long) s1 < (unsigned long) s3) {
+                               struct semaphore *tmp = s3;
+                               s3 = s1; s1 = tmp;
+                       }
+                       if ((unsigned long) s2 < (unsigned long) s3) {
+                               struct semaphore *tmp = s3;
+                               s3 = s2; s2 = tmp;
+                       }
+               }
+               down(s1);
+       } else if ((unsigned long) s2 < (unsigned long) s3) {
+               struct semaphore *tmp = s3;
+               s3 = s2; s2 = tmp;
+       }
+       down(s2);
+       down(s3);
+}
+
+static inline void double_up(struct semaphore *s1, struct semaphore *s2)
+{
+       up(s1);
+       if (s1 != s2)
+               up(s2);
+}
+
+static inline void triple_up(struct semaphore *s1,
+                            struct semaphore *s2,
+                            struct semaphore *s3)
+{
+       up(s1);
+       if (s1 != s2)
+               up(s2);
+       up(s3);
+}
+
+static inline void double_lock(struct dentry *d1, struct dentry *d2)
+{
+       double_down(&d1->d_inode->i_sem, &d2->d_inode->i_sem);
+}
+
+static inline void double_unlock(struct dentry *d1, struct dentry *d2)
+{
+       double_up(&d1->d_inode->i_sem,&d2->d_inode->i_sem);
+       dput(d1);
+       dput(d2);
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_FS_H */
diff --git a/fs/localfs.c b/fs/localfs.c
new file mode 100644 (file)
index 0000000..4fdc99f
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "fs/localfs.h"
+
+/*
+ * LocalFS is just a shim layer on top of the 
+ * FileSystem Protocol which gives access to FAT32,16,12
+ */
+#define FS_NAME L"vfat"
+
+
+typedef struct {
+       EFI_HANDLE      dev;            /* device we're attached to */
+       EFI_FILE_HANDLE volume;         /* root of volume */
+} localfs_priv_state_t;
+
+#define LOCALFS_F2FD(f)                ((UINTN)(f))
+#define LOCALFS_FD2F(fd)       ((EFI_FILE_HANDLE)(fd))
+
+typedef union {
+       localfs_interface_t     pub_intf;
+       struct {
+               localfs_interface_t     pub_intf;
+               localfs_priv_state_t    priv_data;
+       } localfs_priv;
+} localfs_t;
+
+#define FS_PRIVATE(n)  (&(((localfs_t *)n)->localfs_priv.priv_data))
+
+
+static EFI_GUID        LocalFsProtocol = LOCALFS_PROTOCOL;
+
+/*
+ * let's be clean here
+ */
+typedef union {
+       EFI_HANDLE *dev;
+       localfs_t  *intf;
+} dev_tab_t;
+
+static dev_tab_t *dev_tab;     /* holds all devices we found */
+static UINTN ndev;             /* how many entries in dev_tab */
+
+static EFI_STATUS
+localfs_name(localfs_interface_t *this, CHAR16 *name, UINTN maxlen)
+{
+       if (name == NULL || maxlen < 1) return EFI_INVALID_PARAMETER;
+
+       StrnCpy(name, FS_NAME, maxlen-1);
+
+       name[maxlen-1] = CHAR_NULL;
+
+       return EFI_SUCCESS;
+}
+
+
+static EFI_STATUS
+localfs_open(localfs_interface_t *this, CHAR16 *name, UINTN *fd)
+{
+       localfs_priv_state_t *lfs;
+       EFI_STATUS              status;
+       EFI_FILE_HANDLE         fh;
+
+       if (this == NULL || name == NULL || fd == NULL) return EFI_INVALID_PARAMETER;
+
+       lfs = FS_PRIVATE(this);
+
+       DBG_PRT((L"localfs_open on %s\n", name));
+
+       status = lfs->volume->Open(lfs->volume, &fh, name, EFI_FILE_MODE_READ, 0);
+       if (status == EFI_SUCCESS) {
+               *fd = LOCALFS_F2FD(fh);
+       } 
+       return status;
+}
+
+static EFI_STATUS
+localfs_read(localfs_interface_t *this, UINTN fd, VOID *buf, UINTN *size)
+{
+       localfs_priv_state_t *lfs;
+
+       if (this == NULL || fd == 0 || buf == NULL || size == NULL) return EFI_INVALID_PARAMETER;
+
+       lfs = FS_PRIVATE(this);
+
+       return lfs->volume->Read(LOCALFS_FD2F(fd), size, buf);
+}
+
+static EFI_STATUS
+localfs_close(localfs_interface_t *this, UINTN fd)
+{
+       localfs_priv_state_t *lfs;
+
+       if (this == NULL || fd == 0) return EFI_INVALID_PARAMETER;
+
+       lfs = FS_PRIVATE(this);
+
+       return lfs->volume->Close(LOCALFS_FD2F(fd));
+}
+
+static EFI_STATUS
+localfs_infosize(localfs_interface_t *this, UINTN fd, UINT64 *sz)
+{
+       localfs_priv_state_t *lfs;
+       EFI_FILE_INFO        *info;
+
+       if (this == NULL || fd == 0 || sz == NULL) return EFI_INVALID_PARAMETER;
+
+       lfs = FS_PRIVATE(this);
+
+       info = LibFileInfo(LOCALFS_FD2F(fd));
+       if (info == NULL) return EFI_UNSUPPORTED;
+
+       *sz = info->FileSize;
+
+       FreePool(info);
+
+       return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+localfs_seek(localfs_interface_t *this, UINTN fd, UINT64 newpos)
+{
+       localfs_priv_state_t *lfs;
+
+       if (this == NULL || fd == 0) return EFI_INVALID_PARAMETER;
+
+       lfs = FS_PRIVATE(this);
+
+       return lfs->volume->SetPosition(LOCALFS_FD2F(fd), newpos);
+}
+
+static VOID
+localfs_init_state(localfs_t *localfs, EFI_HANDLE dev, EFI_FILE_HANDLE volume)
+{
+       localfs_priv_state_t *lfs = FS_PRIVATE(localfs);
+
+       /* need to do some init here on localfs_intf */
+       Memset(localfs, 0, sizeof(*localfs));
+
+       localfs->pub_intf.localfs_name        = localfs_name;
+       localfs->pub_intf.localfs_open        = localfs_open;
+       localfs->pub_intf.localfs_read        = localfs_read;
+       localfs->pub_intf.localfs_close       = localfs_close;
+       localfs->pub_intf.localfs_infosize    = localfs_infosize;
+       localfs->pub_intf.localfs_seek        = localfs_seek;
+
+       lfs->dev    = dev;
+       lfs->volume = volume;
+}
+
+static EFI_STATUS
+localfs_install_one(EFI_HANDLE dev, VOID **intf)
+{
+
+       EFI_STATUS              status;
+       localfs_t               *localfs;
+       EFI_FILE_IO_INTERFACE   *volume;
+       EFI_FILE_HANDLE         volume_fh;
+
+       status = BS->HandleProtocol (dev, &LocalFsProtocol, (VOID **)&localfs);
+       if (status == EFI_SUCCESS) {
+               ERR_PRT((L"Warning: found existing %s protocol on device", FS_NAME));
+               goto found;
+       }
+       
+       status = BS->HandleProtocol (dev, &FileSystemProtocol, (VOID **)&volume);
+       if (EFI_ERROR(status)) return EFI_INVALID_PARAMETER;
+
+       status = volume->OpenVolume(volume, &volume_fh);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"cannot open volume"));
+               return status;
+       }
+
+       localfs = (localfs_t *)alloc(sizeof(*localfs), EfiLoaderData);
+       if (localfs == NULL) {
+               ERR_PRT((L"failed to allocate %s", FS_NAME));
+               return EFI_OUT_OF_RESOURCES;
+       }
+       localfs_init_state(localfs, dev, volume_fh);
+
+       status = LibInstallProtocolInterfaces(&dev, &LocalFsProtocol, localfs, NULL);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"Cannot install %s protocol: %r", FS_NAME, status));
+               free(localfs);
+               return status;
+       }
+found:
+       if (intf) *intf = (VOID *)localfs;
+
+       VERB_PRT(3,
+               { EFI_DEVICE_PATH *dp; CHAR16 *str;
+                 dp  = DevicePathFromHandle(dev);
+                 str = DevicePathToStr(dp);
+                 Print(L"attached %s to %s\n", FS_NAME, str);
+                 FreePool(str);
+               });
+
+       return EFI_SUCCESS;
+}
+       
+EFI_STATUS
+localfs_install(VOID)
+{
+       UINTN size = 0;
+       UINTN i;
+       EFI_STATUS status;
+       VOID *intf;
+
+       BS->LocateHandle(ByProtocol, &FileSystemProtocol, NULL, &size, NULL);
+       if (size == 0) return EFI_UNSUPPORTED; /* no device found, oh well */
+
+       DBG_PRT((L"size=%d", size));
+
+       dev_tab = (dev_tab_t *)alloc(size, EfiLoaderData);
+       if (dev_tab == NULL) {
+               ERR_PRT((L"failed to allocate handle table"));
+               return EFI_OUT_OF_RESOURCES;
+       }
+       
+       status = BS->LocateHandle(ByProtocol, &FileSystemProtocol, NULL, &size, (VOID **)dev_tab);
+       if (status != EFI_SUCCESS) {
+               ERR_PRT((L"failed to get handles: %r", status));
+               free(dev_tab);
+               return status;
+       }
+       ndev = size / sizeof(EFI_HANDLE);
+
+       for(i=0; i < ndev; i++) {
+               intf = NULL;
+               localfs_install_one(dev_tab[i].dev, &intf);
+               /* override device handle with interface pointer */
+               dev_tab[i].intf = intf;
+       }
+
+       return EFI_SUCCESS;
+}
+       
+EFI_STATUS
+localfs_uninstall(VOID)
+{
+       
+       localfs_priv_state_t *lfs;
+       EFI_STATUS status;
+       UINTN i;
+
+       for(i=0; i < ndev; i++) {
+               if (dev_tab[i].intf == NULL) continue;
+               lfs = FS_PRIVATE(dev_tab[i].intf);
+               status = BS->UninstallProtocolInterface(lfs->dev, &LocalFsProtocol, dev_tab[i].intf);
+               if (EFI_ERROR(status)) {
+                       ERR_PRT((L"Uninstall %s error: %r", FS_NAME, status));
+                       continue;
+               }
+               VERB_PRT(3,
+                       { EFI_DEVICE_PATH *dp; CHAR16 *str;
+                       dp  = DevicePathFromHandle(lfs->dev);
+                       str = DevicePathToStr(dp);
+                       Print(L"uninstalled %s on %s\n", FS_NAME, str);
+                       FreePool(str);
+                       });
+               free(dev_tab[i].intf);
+       }
+       if (dev_tab) free(dev_tab);
+
+       return EFI_SUCCESS;
+}
+
+
diff --git a/fs/localfs.h b/fs/localfs.h
new file mode 100644 (file)
index 0000000..aa63620
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __LOCALFS_H__
+#define __LOCALFS_H__
+
+INTERFACE_DECL(_localfs_interface_t);
+
+typedef struct _localfs_interface_t {
+       EFI_STATUS (*localfs_name)(struct _localfs_interface_t *this, CHAR16 *name, UINTN maxlen);
+       EFI_STATUS (*localfs_open)(struct _localfs_interface_t *this, CHAR16 *name, UINTN *fd);
+       EFI_STATUS (*localfs_read)(struct _localfs_interface_t *this, UINTN fd, VOID *buf, UINTN *size);
+       EFI_STATUS (*localfs_close)(struct _localfs_interface_t *this, UINTN fd);
+       EFI_STATUS (*localfs_infosize)(struct _localfs_interface_t *this, UINTN fd, UINT64 *size);
+       EFI_STATUS (*localfs_seek)(struct _localfs_interface_t *this, UINTN fd, UINT64 newpos);
+} localfs_interface_t;
+
+#define LOCALFS_PROTOCOL \
+    { 0x3a42ff5d, 0x43c9, 0x4db8, {0x82, 0x4e, 0xb8, 0x5b, 0xab, 0x97, 0x63, 0xcc} }
+
+extern EFI_STATUS localfs_install(VOID);
+extern EFI_STATUS localfs_uninstall(VOID);
+
+#endif /* __LOCALFS_H__ */
diff --git a/fs/netfs.c b/fs/netfs.c
new file mode 100644 (file)
index 0000000..4913785
--- /dev/null
@@ -0,0 +1,790 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "fs/netfs.h"
+
+#include "elilo.h"
+
+#define FS_NAME L"netfs"
+
+#define NETFS_DEFAULT_BUFSIZE          16*MB
+#define NETFS_DEFAULT_BUFSIZE_INC       8*MB
+
+#define NETFS_DEFAULT_SERVER_TYPE      EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP
+#define NETFS_FD_MAX   2
+
+typedef struct _netfs_fd {
+       struct _netfs_fd *next;
+       
+       CHAR8  *netbuf;
+       UINT64 netbuf_maxsize;  /* currently allocated buffer */
+       UINTN  netbuf_size;     /* number of bytes currently used in the buffer */
+       UINT64 netbuf_pos;      /* current position in the buffer */
+       BOOLEAN is_valid;       /* avoid conflicting opens */
+       BOOLEAN netbuf_reuse;
+
+       CHAR16 last_file[FILENAME_MAXLEN];
+} netfs_fd_t;
+
+
+typedef struct {
+       EFI_PXE_BASE_CODE *pxe;
+       EFI_HANDLE        dev;          /* handle to device we're attached to */
+       BOOLEAN           using_pxe;    /* true if downloaded using the PXE protocol vs. regular DHCP */
+
+       EFI_IP_ADDRESS  srv_ip;
+       EFI_IP_ADDRESS  cln_ip;
+       EFI_IP_ADDRESS  gw_ip;
+       EFI_IP_ADDRESS  netmask;
+       UINT8           hw_addr[16];
+
+       netfs_fd_t      fd_tab[NETFS_FD_MAX];
+       netfs_fd_t      *free_fd;
+       UINTN           free_fd_count;
+
+} netfs_priv_state_t;
+
+#define NETFS_F2FD(l,f)                (UINTN)((f)-(l)->fd_tab)
+#define NETFS_FD2F(l,fd)       ((l)->fd_tab+fd)
+#define NETFS_F_INVALID(f)     ((f)->is_valid == FALSE)
+
+
+typedef union {
+       netfs_interface_t pub_intf;
+       struct {
+               netfs_interface_t  pub_intf;
+               netfs_priv_state_t priv_data;
+       } netfs_priv;
+} netfs_t;
+
+#define FS_PRIVATE(n)  (&(((netfs_t *)n)->netfs_priv.priv_data))
+
+typedef union {
+       EFI_HANDLE *dev;
+       netfs_t  *intf;
+} dev_tab_t;
+
+static dev_tab_t *dev_tab;     /* holds all devices we found */
+static UINTN ndev;             /* how many entries in dev_tab */
+
+static EFI_GUID NetFsProtocol = NETFS_PROTOCOL;
+
+
+#if 0
+static EFI_PXE_BASE_CODE_CALLBACK_STATUS
+netfs_callback_func(
+    IN EFI_PXE_BASE_CODE_CALLBACK *this,
+    IN EFI_PXE_BASE_CODE_FUNCTION function,
+    IN BOOLEAN received,
+    IN UINT32 packet_len,
+    IN EFI_PXE_BASE_CODE_PACKET *packet OPTIONAL
+)
+{
+       Print(L"netfs_callback called received=%d packet_len=%d\n", received, packet_len);
+       return EFI_ABORTED;
+}
+
+static EFI_PXE_BASE_CODE_CALLBACK netfs_callback = {
+    EFI_PXE_BASE_CODE_CALLBACK_INTERFACE_REVISION,
+    &netfs_callback_func
+};
+#endif
+
+static netfs_fd_t *
+netfs_fd_alloc(netfs_priv_state_t *nfs, CHAR16 *name)
+{
+       netfs_fd_t *tmp = NULL, *prev = NULL, *match;
+       UINT8 netbuf_reuse = 0;
+
+       if (nfs->free_fd == NULL) {
+               ERR_PRT((L"out of file descriptor"));
+               return NULL;
+       }
+       match = nfs->free_fd; 
+       for (tmp = nfs->free_fd; tmp; tmp = tmp->next) {
+               if (!StrCmp(name, tmp->last_file)) {
+                       DBG_PRT((L"Using cached file %s netbuf_size=%d", tmp->last_file, tmp->netbuf_size));
+                       netbuf_reuse = 1;
+                       match = tmp;
+                       break;
+               }
+               prev = tmp;
+       }
+       /* indicate whether or not we got a match in caching */
+       match->netbuf_reuse = netbuf_reuse;
+
+       if (match == nfs->free_fd) 
+               nfs->free_fd = match->next;     
+       else 
+               prev->next  = match->next;
+
+       nfs->free_fd_count--;
+
+       return match;
+}
+
+static VOID
+netfs_fd_free(netfs_priv_state_t *nfs, netfs_fd_t *f)
+{
+       if (f == NULL) {
+               ERR_PRT((L"invalid fd"));
+               return;
+       }
+       f->next    = nfs->free_fd;
+
+       /* we keep the netbuf, in case we can reuse it */
+       f->is_valid = FALSE;
+
+       nfs->free_fd = f;
+       nfs->free_fd_count++;
+
+       if (nfs->free_fd_count > NETFS_FD_MAX) {
+               ERR_PRT((L"too many free descriptors %d", nfs->free_fd_count));
+       }
+}
+
+
+static INTN
+netbuf_alloc(netfs_fd_t *f)
+{
+       /* we will try to reuse the existing buffer first */
+       if (f->netbuf != 0) return 0;
+
+       f->netbuf_pos     = 0;
+
+       f->netbuf = (CHAR8 *)alloc_pages(EFI_SIZE_TO_PAGES(f->netbuf_maxsize), EfiLoaderData, AllocateAnyPages, 0);
+               
+       return f->netbuf == 0 ? -1 : 0;
+}
+
+static EFI_STATUS
+netfs_name(netfs_interface_t *this, CHAR16 *name, UINTN maxlen)
+{
+       if (name == NULL || maxlen < 1) return EFI_INVALID_PARAMETER;
+
+       StrnCpy(name, FS_NAME, maxlen-1);
+
+       name[maxlen-1] = CHAR_NULL;
+
+       return EFI_SUCCESS;
+}
+
+static  VOID
+netfs_extract_ip(netfs_priv_state_t *nfs)
+{
+       EFI_PXE_BASE_CODE *pxe = nfs->pxe;
+
+       if (pxe->Mode->PxeDiscoverValid) {
+               nfs->using_pxe = TRUE;
+               Memcpy(&nfs->srv_ip, pxe->Mode->PxeReply.Dhcpv4.BootpSiAddr, sizeof(EFI_IP_ADDRESS)); 
+               Memcpy(&nfs->hw_addr, pxe->Mode->PxeReply.Dhcpv4.BootpHwAddr, 16*sizeof(UINT8));
+       } else {
+               Memcpy(&nfs->srv_ip, pxe->Mode->DhcpAck.Dhcpv4.BootpSiAddr, sizeof(EFI_IP_ADDRESS)); 
+               Memcpy(&nfs->hw_addr, pxe->Mode->DhcpAck.Dhcpv4.BootpHwAddr, sizeof(nfs->hw_addr));
+       }
+
+       Memcpy(&nfs->cln_ip, &pxe->Mode->StationIp, sizeof(EFI_IP_ADDRESS)); 
+       Memcpy(&nfs->netmask, &pxe->Mode->SubnetMask, sizeof(EFI_IP_ADDRESS)); 
+
+       /*
+        * the fact that we use index 0, is just a guess
+        */
+       if (pxe->Mode->RouteTableEntries>0) 
+               Memcpy(&nfs->gw_ip, &pxe->Mode->RouteTable[0].GwAddr, sizeof(EFI_IP_ADDRESS)); 
+
+       VERB_PRT(1, Print(L"PXE PxeDiscoverValid: %s\n", pxe->Mode->PxeDiscoverValid?  L"Yes (PXE-aware DHCPD)" : L"No (Regular DHCPD)"));
+#if 0
+       status = BS->HandleProtocol(dev, &PxeCallbackProtocol, (VOID **)&netfs_callback);
+       status = LibInstallProtocolInterfaces(&dev, &PxeCallbackProtocol, &netfs_callback, NULL);
+       Print(L"PXE Callback support : %r\n", status);
+       if (status == EFI_SUCCESS) {
+               BOOLEAN doit = TRUE;
+               status = pxe->SetParameters(pxe, NULL, NULL, NULL, NULL, &doit);
+               Print(L"PXE Callback SetParameters: %r\n", status);
+       }
+#endif
+               /*
+                * XXX: TFTPD server not quite right when using PXE, need to extract bootservers...
+                */
+       VERB_PRT(1, Print(L"Local IP: %d.%d.%d.%d\n",
+               pxe->Mode->StationIp.v4.Addr[0] & 0xff,
+               pxe->Mode->StationIp.v4.Addr[1] & 0xff,
+               pxe->Mode->StationIp.v4.Addr[2] & 0xff,
+               pxe->Mode->StationIp.v4.Addr[3] & 0xff));
+
+       VERB_PRT(1, Print(L"SM: %d.%d.%d.%d\n",
+               pxe->Mode->SubnetMask.v4.Addr[0] & 0xff,
+               pxe->Mode->SubnetMask.v4.Addr[1] & 0xff,
+               pxe->Mode->SubnetMask.v4.Addr[2] & 0xff,
+               pxe->Mode->SubnetMask.v4.Addr[3] & 0xff));
+
+       VERB_PRT(1, Print(L"TFTPD IP: %d.%d.%d.%d\n", 
+               nfs->srv_ip.v4.Addr[0] & 0xff, 
+               nfs->srv_ip.v4.Addr[1] & 0xff, 
+               nfs->srv_ip.v4.Addr[2] & 0xff, 
+               nfs->srv_ip.v4.Addr[3] & 0xff));
+
+       VERB_PRT(1, Print(L"Gateway IP: %d.%d.%d.%d\n",
+                       nfs->gw_ip.v4.Addr[0] & 0xff, 
+                       nfs->gw_ip.v4.Addr[1] & 0xff, 
+                       nfs->gw_ip.v4.Addr[2] & 0xff, 
+                       nfs->gw_ip.v4.Addr[3] & 0xff));
+
+}
+
+static EFI_STATUS
+netfs_start(EFI_PXE_BASE_CODE *pxe)
+{
+       EFI_STATUS status;
+
+       status = pxe->Start(pxe, FALSE);
+       if (EFI_ERROR(status)) return status;
+
+       return pxe->Dhcp(pxe, FALSE);
+}
+
+static EFI_STATUS
+netfs_open(netfs_interface_t *this, CHAR16 *name, UINTN *fd)
+{
+       netfs_priv_state_t *nfs;
+       netfs_fd_t         *f;
+       EFI_STATUS         status;
+       CHAR8              ascii_name[FILENAME_MAXLEN];
+       UINTN              blocksize = 0, prev_netbufsize;
+
+       if (this == NULL || name == NULL || fd == NULL) return EFI_INVALID_PARAMETER;
+
+       nfs = FS_PRIVATE(this);
+
+       if (nfs->pxe == NULL) return EFI_INVALID_PARAMETER;
+
+       /*
+        * Try to start protocol if not already active
+        */
+       if (nfs->pxe->Mode->Started == FALSE) {
+               status = netfs_start(nfs->pxe);
+               if (EFI_ERROR(status)) return  status;
+               netfs_extract_ip(nfs);
+       }
+
+       if ((f=netfs_fd_alloc(nfs, name)) == NULL) return EFI_OUT_OF_RESOURCES;
+
+       if (f->netbuf_reuse) {
+               f->netbuf_pos = 0;
+               f->is_valid = TRUE;
+               *fd = NETFS_F2FD(nfs, f);
+               return EFI_SUCCESS;
+       }
+       f->netbuf_maxsize = NETFS_DEFAULT_BUFSIZE;
+       
+       if (f->netbuf == NULL && netbuf_alloc(f) == -1) {
+               netfs_fd_free(nfs, f);
+               return EFI_OUT_OF_RESOURCES;
+       }
+
+       /* well, we need to download ! */
+
+       U2ascii(name, ascii_name, FILENAME_MAXLEN);
+
+       VERB_PRT(2, Print(L"downloading %a from %d.%d.%d.%d...", ascii_name, 
+                               nfs->srv_ip.v4.Addr[0], 
+                               nfs->srv_ip.v4.Addr[1], 
+                               nfs->srv_ip.v4.Addr[2], 
+                               nfs->srv_ip.v4.Addr[3]));
+retry:
+       f->netbuf_size = f->netbuf_maxsize;
+
+       DBG_PRT((L"\nbefore netbuf:0x%lx netbuf_size=%ld\n", f->netbuf, f->netbuf_size));
+
+       /* 
+        * For EFI versions older than 14.61:
+        *   it seems like there is an EFI bug (or undocumented behavior) when the buffer size
+        *   is too small AND the blocksize parameter is NULL, i.e., used the largest possible.
+        *   In this case, Mtftp() never returns EFI_BUFFER_TOO_SMALL but EFI_TIMEOUT instead.
+        *   This is true for 1.02 and also 1.10 it seems. Here we set it to the minimal value (512).
+        *
+        *   Also it seems like on a READ_FILE which returns EFI_BUFFER_TOO_SMALL, the buffersize
+        *   is NOT updated to reflect the required size for the next attempt.
+        *
+        * For EFI versions 14.61 and higher:
+        *  In case the buffer is too small AND the TFTP server reports the file size (see RFC 2349), 
+        *  the f->netbuf_size will report the exact size for the buffer.
+        */
+       prev_netbufsize = f->netbuf_size;
+
+       status = nfs->pxe->Mtftp(nfs->pxe, EFI_PXE_BASE_CODE_TFTP_READ_FILE, f->netbuf, FALSE,
+                           &(f->netbuf_size), 
+                           blocksize > 0 ? &blocksize : NULL, 
+                           &nfs->srv_ip, 
+                           ascii_name, 
+                           NULL, 
+                           FALSE);
+
+       DBG_PRT((L"after Mftp=%r netbuf:0x%lx netbuf_size=%ld blocksize=%ld\n", 
+               status, 
+               f->netbuf, 
+               f->netbuf_size, 
+               blocksize));
+
+       if (status == EFI_TIMEOUT && blocksize == 0) {
+               /*
+                * XXX: if blocksize is not adjusted we could loop forever here
+                */
+               //blocksize = 512;
+               status = EFI_BUFFER_TOO_SMALL;
+       }
+       /*
+        * check if we need to increase our buffer size
+        */
+       if (status == EFI_BUFFER_TOO_SMALL) {
+               DBG_PRT((L"buffer too small, need netbuf_size=%d", f->netbuf_size));
+               /*
+                * if the TFTP server supports TFTP options, then we should
+                * get the required size. So we test to see if the size
+                * we set has changed. If so, we got the required size.
+                * If not, we increase the buffer size and retry.
+                */
+               if (f->netbuf_size == prev_netbufsize) {
+                       f->netbuf_maxsize += NETFS_DEFAULT_BUFSIZE_INC;
+               } else {
+                       /* we got an answer from the TFTP server, let's try it */
+                       f->netbuf_maxsize = f->netbuf_size;
+               }
+               free(f->netbuf);
+
+               f->netbuf = NULL; /* will force reallocation */
+
+               if (netbuf_alloc(f) == 0) goto retry;
+
+               /* fall through in case of error */
+       }
+
+       if (status == EFI_SUCCESS) {
+               /* start at the beginning of the file */
+               f->netbuf_pos = 0;
+
+               /* cache file name */
+               StrCpy(f->last_file, name);
+
+               f->is_valid = 1;
+
+               *fd = NETFS_F2FD(nfs, f);
+               VERB_PRT(2, Print(L"Done\n"));
+       } else {
+               netfs_fd_free(nfs, f);
+               VERB_PRT(2, Print(L"Failed: %r\n", status));
+       }
+       DBG_PRT((L"File %s netbuf_size=%d: %r", name, f->netbuf_size, status));
+#if 0
+       Print(L"\n---\n");
+       { INTN i; 
+               for(i=0; i < netbuf_size; i++) {
+                       Print(L"%c", (CHAR16)netbuf[i]);
+               }
+       }
+       Print(L"\n---\n");
+#endif
+       return status;
+}
+
+
+static EFI_STATUS
+netfs_read(netfs_interface_t *this, UINTN fd, VOID *buf, UINTN *size)
+{
+       netfs_priv_state_t *nfs;
+       netfs_fd_t         *f;
+       UINTN              count;
+
+       if (this == NULL || fd >= NETFS_FD_MAX || buf == NULL || size == NULL) return EFI_INVALID_PARAMETER;
+
+       nfs = FS_PRIVATE(this);
+       f   = NETFS_FD2F(nfs, fd);
+
+       if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
+
+       count = MIN(*size, f->netbuf_size - f->netbuf_pos);
+
+       if (count) Memcpy(buf, f->netbuf+f->netbuf_pos, count);
+
+       *size = count;
+       f->netbuf_pos += count;
+
+       return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+netfs_close(netfs_interface_t *this, UINTN fd)
+{
+       netfs_priv_state_t *nfs;
+       netfs_fd_t           *f;
+
+       if (this == NULL || fd >= NETFS_FD_MAX) return EFI_INVALID_PARAMETER;
+
+       nfs = FS_PRIVATE(this);
+       f   = NETFS_FD2F(nfs, fd);
+
+       if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
+
+       netfs_fd_free(nfs, f);
+
+       return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+netfs_seek(netfs_interface_t *this, UINTN fd, UINT64 newpos)
+{
+       netfs_priv_state_t *nfs;
+       netfs_fd_t         *f;
+
+       if (this == NULL || fd >= NETFS_FD_MAX) return EFI_INVALID_PARAMETER;
+
+       nfs = FS_PRIVATE(this);
+       f   = NETFS_FD2F(nfs, fd);
+
+       if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
+
+       if (newpos > f->netbuf_size) return EFI_INVALID_PARAMETER;
+
+       f->netbuf_pos = newpos;
+
+       return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+netfs_infosize(netfs_interface_t *this, UINTN fd, UINT64 *sz)
+{
+       netfs_priv_state_t *nfs;
+       netfs_fd_t         *f;
+
+       if (this == NULL || fd >= NETFS_FD_MAX || sz == NULL) return EFI_INVALID_PARAMETER;
+
+       nfs = FS_PRIVATE(this);
+       f   = NETFS_FD2F(nfs, fd);
+
+       if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
+
+       *sz = f->netbuf_size;
+
+       return EFI_SUCCESS;
+}
+
+static INTN
+find_dhcp_option(EFI_PXE_BASE_CODE_PACKET *packet, UINT8 use_ipv6, UINT8 option, CHAR8 *str, INTN *len)
+{
+       INTN i = 0;
+       UINT8 tag, length;
+       UINT8 *opts = packet->Dhcpv4.DhcpOptions;
+
+       *len = 0;
+
+       for(;;) {
+               if (i >= 56) {
+                       DBG_PRT((L"reach end of options (no marker)\n"));
+                       break;
+               }
+               tag = opts[i++];
+
+               if (tag == 0) continue;
+               if (tag == 255) break;
+
+               length = opts[i++];
+
+#if 0
+               { UINT8 l = length, k = 0;
+                       Print(L"found option %d len=%d: ", tag, length);
+                       while (l--) { Print(L"%c(%d)\n", (CHAR16)opts[k], opts[k]); k++; }
+                       Print(L"\n");
+               }
+#endif
+               if (tag == option) {
+                       *len = length;
+                       while (length--) { *str++ = opts[i++]; }
+                       return 0;
+               }
+               i += length;
+       }
+       return -1;
+}
+
+static EFI_STATUS
+netfs_getinfo(netfs_interface_t *this, netfs_info_t *info)
+{
+       netfs_priv_state_t *nfs;
+       CHAR8 str[256];
+       INTN len, r;
+
+       if (this == NULL || info == NULL) return EFI_INVALID_PARAMETER;
+
+       nfs = FS_PRIVATE(this);
+
+       Memcpy(&info->cln_ipaddr, &nfs->cln_ip, sizeof(EFI_IP_ADDRESS));
+       Memcpy(&info->srv_ipaddr, &nfs->srv_ip, sizeof(EFI_IP_ADDRESS));
+       Memcpy(&info->netmask, &nfs->netmask, sizeof(EFI_IP_ADDRESS));
+       Memcpy(&info->gw_ipaddr, &nfs->gw_ip, sizeof(EFI_IP_ADDRESS));
+       Memcpy(&info->hw_addr, &nfs->hw_addr, sizeof(info->hw_addr));
+
+       info->using_pxe  = nfs->using_pxe;
+       info->started    = nfs->pxe->Mode->Started;
+       info->using_ipv6 = nfs->pxe->Mode->UsingIpv6;
+
+       if (nfs->pxe->Mode->UsingIpv6) goto skip_options;
+
+       r = find_dhcp_option(&nfs->pxe->Mode->DhcpAck,nfs->pxe->Mode->UsingIpv6, 15, str,  &len);
+       str[len] = '\0';
+       ascii2U(str, info->domainame, 255);
+
+       VERB_PRT(3, Print(L"domain(15): %a\n", str));
+
+       r = find_dhcp_option(&nfs->pxe->Mode->DhcpAck,nfs->pxe->Mode->UsingIpv6, 12, str,  &len);
+       str[len] = '\0';
+       ascii2U(str, info->hostname, 255);
+
+       VERB_PRT(3, Print(L"hostname(12): %a\n", str));
+
+       /*
+        * extract bootfile name from DHCP exchanges
+        */
+       if (nfs->using_pxe == 0) {
+               ascii2U(nfs->pxe->Mode->DhcpAck.Dhcpv4.BootpBootFile, info->bootfile, NETFS_BOOTFILE_MAXLEN);
+               VERB_PRT(3, Print(L"bootfile: %s\n", info->bootfile));
+       }
+
+skip_options:
+       return EFI_SUCCESS;
+}
+
+static UINT16
+find_pxe_server_type(EFI_PXE_BASE_CODE *pxe)
+{
+       INTN i = 0, max;
+       UINT8 tag, length;
+       UINT8 *opts = pxe->Mode->PxeReply.Dhcpv4.DhcpOptions;
+       UINT16 server_type;
+
+       while(i < 55) {
+               tag    = opts[i];
+               length = opts[i+1];
+
+               DBG_PRT((L"Tag #%d Length %d\n",tag, length));
+
+               if (tag == 43) goto found;
+
+               i += 2 + length;
+       }
+       return NETFS_DEFAULT_SERVER_TYPE;
+found:
+       max = i+2+length;
+       i  += 2;
+       while (i < max) {
+               tag    = opts[i];
+               length = opts[i+1];
+               if (tag == 71) {
+                       server_type =(opts[i+2]<<8) | opts[i+3];
+                       DBG_PRT((L"ServerType: %d\n", server_type));
+                       return server_type;
+               }
+               i+= 2 + length;
+       }
+       return NETFS_DEFAULT_SERVER_TYPE;
+}
+
+static EFI_STATUS
+netfs_query_layer(netfs_interface_t *this, UINT16 server_type, UINT16 layer, UINTN maxlen, CHAR16 *str)
+{
+       netfs_priv_state_t *nfs;
+       EFI_STATUS         status;
+
+       if (this == NULL || str == NULL) return EFI_INVALID_PARAMETER;
+
+       nfs = FS_PRIVATE(this);
+       
+       if (nfs->using_pxe == FALSE) return EFI_UNSUPPORTED;
+
+       if (server_type == 0) server_type = find_pxe_server_type(nfs->pxe);
+
+       status = nfs->pxe->Discover(nfs->pxe, server_type, &layer, FALSE, 0);
+       if(status == EFI_SUCCESS) {
+               ascii2U(nfs->pxe->Mode->PxeReply.Dhcpv4.BootpBootFile, str, maxlen);
+       } 
+       return status;
+}
+
+static VOID
+netfs_init_state(netfs_t *netfs, EFI_HANDLE dev, EFI_PXE_BASE_CODE *pxe)
+{
+       netfs_priv_state_t *nfs = FS_PRIVATE(netfs);
+       UINTN i;
+
+       /* need to do some init here on netfs_intf */
+       Memset(netfs, 0, sizeof(*netfs));
+
+
+       netfs->pub_intf.netfs_name        = netfs_name;
+       netfs->pub_intf.netfs_open        = netfs_open;
+       netfs->pub_intf.netfs_read        = netfs_read;
+       netfs->pub_intf.netfs_close       = netfs_close;
+       netfs->pub_intf.netfs_infosize    = netfs_infosize;
+       netfs->pub_intf.netfs_seek        = netfs_seek;
+       netfs->pub_intf.netfs_query_layer = netfs_query_layer;
+       netfs->pub_intf.netfs_getinfo     = netfs_getinfo;
+
+       nfs->dev = dev;
+       nfs->pxe = pxe;
+
+       /*
+        * we defer DHCP request until it is really necessary (netfs_open)
+        */
+       if (pxe->Mode->Started == TRUE) netfs_extract_ip(nfs);
+
+       Memset(nfs->fd_tab, 0, sizeof(nfs->fd_tab));
+
+       for (i=0; i < NETFS_FD_MAX-1; i++) {
+               nfs->fd_tab[i].next = &nfs->fd_tab[i+1];
+       }
+       /* null on last element is done by memset */
+
+       nfs->free_fd        = nfs->fd_tab;
+       nfs->free_fd_count  = NETFS_FD_MAX;
+}
+
+static EFI_STATUS
+netfs_install_one(EFI_HANDLE dev, VOID **intf)
+{
+
+       EFI_STATUS status;
+       netfs_t *netfs;
+       EFI_PXE_BASE_CODE *pxe;
+
+       status = BS->HandleProtocol (dev, &NetFsProtocol, (VOID **)&netfs);
+       if (status == EFI_SUCCESS) {
+               ERR_PRT((L"Warning: found existing %s protocol on device", FS_NAME));
+               goto found;
+       }
+       
+       status = BS->HandleProtocol (dev, &PxeBaseCodeProtocol, (VOID **)&pxe);
+       if (EFI_ERROR(status)) return EFI_INVALID_PARAMETER;
+
+
+       netfs = (netfs_t *)alloc(sizeof(*netfs), EfiLoaderData);
+       if (netfs == NULL) {
+               ERR_PRT((L"failed to allocate %s", FS_NAME));
+               return EFI_OUT_OF_RESOURCES;
+       }
+
+       netfs_init_state(netfs, dev, pxe);
+
+       status = LibInstallProtocolInterfaces(&dev, &NetFsProtocol, netfs, NULL);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"Cannot install %s protocol: %r", FS_NAME, status));
+               free(netfs);
+               return status;
+       }
+
+found:
+       if (intf) *intf = (VOID *)netfs;
+
+       VERB_PRT(3,
+               { EFI_DEVICE_PATH *dp; CHAR16 *str;
+                 dp  = DevicePathFromHandle(dev);
+                 str = DevicePathToStr(dp);
+                 Print(L"attached %s to %s\n", FS_NAME, str);
+                 FreePool(str);
+               });
+
+       return EFI_SUCCESS;
+}
+       
+EFI_STATUS
+netfs_install(VOID)
+{
+       UINTN size = 0;
+       UINTN i;
+       EFI_STATUS status;
+       VOID *intf;
+
+       BS->LocateHandle(ByProtocol, &PxeBaseCodeProtocol, NULL, &size, NULL);
+       if (size == 0) return EFI_UNSUPPORTED; /* no device found, oh well */
+
+       DBG_PRT((L"size=%d", size));
+
+       dev_tab = (dev_tab_t *)alloc(size, EfiLoaderData);
+       if (dev_tab == NULL) {
+               ERR_PRT((L"failed to allocate handle table"));
+               return EFI_OUT_OF_RESOURCES;
+       }
+       
+       status = BS->LocateHandle(ByProtocol, &PxeBaseCodeProtocol, NULL, &size, (VOID **)dev_tab);
+       if (status != EFI_SUCCESS) {
+               ERR_PRT((L"failed to get handles: %r", status));
+               free(dev_tab);
+               return status;
+       }
+       ndev = size / sizeof(EFI_HANDLE);
+
+       for(i=0; i < ndev; i++) {
+               intf = NULL;
+               netfs_install_one(dev_tab[i].dev, &intf);
+               /* override device handle with interface pointer */
+               dev_tab[i].intf = intf;
+       }
+
+       return EFI_SUCCESS;
+}
+       
+EFI_STATUS
+netfs_uninstall(VOID)
+{
+       
+       netfs_priv_state_t *nfs;
+       EFI_STATUS status;
+       UINTN i;
+
+       for(i=0; i < ndev; i++) {
+               if (dev_tab[i].intf == NULL) continue;
+               nfs = FS_PRIVATE(dev_tab[i].intf);
+               status = BS->UninstallProtocolInterface(nfs->dev, &NetFsProtocol, dev_tab[i].intf);
+               if (EFI_ERROR(status)) {
+                       ERR_PRT((L"Uninstall %s error: %r", FS_NAME, status));
+                       continue;
+               }
+               VERB_PRT(3,
+                       { EFI_DEVICE_PATH *dp; CHAR16 *str;
+                       dp  = DevicePathFromHandle(nfs->dev);
+                       str = DevicePathToStr(dp);
+                       Print(L"uninstalled %s on %s\n", FS_NAME, str);
+                       FreePool(str);
+                       });
+
+               if (nfs->pxe->Mode->Started == TRUE) nfs->pxe->Stop(nfs->pxe);
+
+               free(dev_tab[i].intf);
+       }
+       if (dev_tab) free(dev_tab);
+
+       return EFI_SUCCESS;
+}
diff --git a/fs/netfs.h b/fs/netfs.h
new file mode 100644 (file)
index 0000000..3028689
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __NETLFS_H__
+#define __NETLFS_H__
+
+#include <efi.h>
+#include <efilib.h>
+
+#define NETFS_BOOTFILE_MAXLEN  256
+
+typedef struct {
+       EFI_IP_ADDRESS cln_ipaddr;
+       EFI_IP_ADDRESS srv_ipaddr;
+       EFI_IP_ADDRESS netmask;
+       EFI_IP_ADDRESS gw_ipaddr;
+       UINT8           hw_addr[16];
+       CHAR16          hostname[255];  /* 255 limitation of DHCP protocol */
+       CHAR16          domainame[255]; /* 255 limitation of DHCP protocol */
+       CHAR16          bootfile[NETFS_BOOTFILE_MAXLEN];  /* name of file downloaded (BOOTP/DHCP) */
+       BOOLEAN         using_pxe;
+       BOOLEAN         started;
+       BOOLEAN         using_ipv6;
+} netfs_info_t;
+
+
+INTERFACE_DECL(_netfs_interface_t);
+
+typedef struct _netfs_interface_t {
+       EFI_STATUS (*netfs_name)(struct _netfs_interface_t *this, CHAR16 *name, UINTN maxlen);
+       EFI_STATUS (*netfs_open)(struct _netfs_interface_t *this, CHAR16 *name, UINTN *fd);
+       EFI_STATUS (*netfs_read)(struct _netfs_interface_t *this, UINTN fd, VOID *buf, UINTN *size);
+       EFI_STATUS (*netfs_close)(struct _netfs_interface_t *this, UINTN fd);
+       EFI_STATUS (*netfs_infosize)(struct _netfs_interface_t *this, UINTN fd, UINT64 *size);
+       EFI_STATUS (*netfs_seek)(struct _netfs_interface_t *this, UINTN fd, UINT64 newpos);
+       EFI_STATUS (*netfs_query_layer)(struct _netfs_interface_t *this, UINT16 server_type, UINT16 layer, UINTN maxlen, CHAR16 *str);
+       EFI_STATUS (*netfs_getinfo)(struct _netfs_interface_t *this, netfs_info_t *info);
+} netfs_interface_t;
+
+#define NETFS_PROTOCOL \
+    { 0x6746de4f, 0xcc1e, 0x4c5f, {0xb7, 0xfb, 0x85, 0x6a, 0x5d, 0x69, 0x0f, 0x06} }
+
+extern EFI_STATUS netfs_install(VOID);
+extern EFI_STATUS netfs_uninstall(VOID);
+
+#endif /* __NETFS_H__ */
diff --git a/getopt.c b/getopt.c
new file mode 100644 (file)
index 0000000..8a70616
--- /dev/null
+++ b/getopt.c
@@ -0,0 +1,99 @@
+/*
+ * Simplistic getopt() function for EFI
+ *
+ * This function provides the basic functionality of the POSIX getopt() function.
+ * No long options are supported.
+ *
+ * This code  is meant for EFI programs and therefore deals with Unicode characters.
+ *
+ * Copyright (C) 2000 Hewlett-Packard Co
+ * Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * ELILO is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ELILO is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ELILO; see the file COPYING.  If not, write to the Free
+ * Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+
+#include <efi.h>
+#include <efilib.h>
+
+
+#define DASH   (CHAR16)'-'
+#define COLON  (CHAR16)':'
+#define EOS    (CHAR16)'\0'
+#define BADCH  (INTN)'?'
+#define BADARG (INTN)':'
+
+extern CHAR16 * StrChr(IN const CHAR16 *s, INT16 c);
+
+CHAR16 *Optarg;
+INTN    Optind = 1;
+INTN    Optopt;
+
+/*
+ * This simple version of getopt supports:
+ *     - option with no argument (no :)
+ *     - option with REQUIRED argument (single :)
+ * it does not support:
+ *     - long options
+ *     - optional arguments to options
+ *     - optreset
+ */
+INTN
+Getopt(INTN argc, CHAR16 *const argv[], const CHAR16 *optstring)
+{
+       static CHAR16 *cur_chr = NULL;
+       CHAR16 *opt;
+
+       if (Optind >= argc) { /* no option or end of argument list */
+               cur_chr = NULL;
+               return -1;
+       }
+       if (cur_chr == NULL || *cur_chr == EOS) {
+               cur_chr = argv[Optind];
+               if (*cur_chr++ != DASH) { /* missing DASH */
+                       cur_chr = NULL;
+                       return -1;
+               }
+               if (*cur_chr == DASH) {
+                       cur_chr = NULL;
+                       Optind++;
+                       return -1; /* -- case, we're done */
+               }
+       }
+       Optopt = *cur_chr++;
+       opt = StrChr(optstring, Optopt);
+       if (!opt) {
+               Print(L"%s: illegal option -- %c\n", argv[0], Optopt);
+               if (*cur_chr == EOS) Optind++;
+               return BADCH;
+       } 
+       if (*(opt+1) != COLON) {
+               Optarg = NULL;
+               if (*cur_chr == EOS) Optind++;
+       } else {
+               if (*cur_chr) {
+                       Optarg = cur_chr;
+               } else if ( ++Optind >= argc ) {
+                          Print(L"%s: option `%s' requires an argument\n", argv[0], argv[Optind-1]),
+                          cur_chr = NULL;
+                          return BADARG;
+               } else {
+                       Optarg = argv[Optind];
+               }
+               Optind++;
+       }
+       return Optopt;
+}
diff --git a/getopt.h b/getopt.h
new file mode 100644 (file)
index 0000000..fe373db
--- /dev/null
+++ b/getopt.h
@@ -0,0 +1,33 @@
+/*
+ * Simplistic getopt() function header file for EFI
+ *
+ * Copyright (C) 2000 Hewlett-Packard Co
+ * Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * ELILO is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ELILO is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ELILO; see the file COPYING.  If not, write to the Free
+ * Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+
+
+#ifndef __EFI_GETOPT_H__
+#define __EFI_GETOPT_H__
+
+extern CHAR16 *Optarg;
+extern INTN Optind, Optopt;
+
+extern INTN Getopt(INTN argc, CHAR16 *const argv[], const CHAR16 *optstring);
+
+#endif /* __EFI_GETOPT_H__ */
diff --git a/glue_ext2fs.c b/glue_ext2fs.c
new file mode 100644 (file)
index 0000000..d9a14fe
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+#include <efi.h>
+#include <efilib.h>
+
+#include "glue_ext2fs.h"
+#include "fs/ext2fs.h"
+#include "strops.h"
+
+
+static INTN glue(fileops_t *this, VOID *intf);
+
+/* object exported to fileops */
+
+fileops_fs_t ext2fs_glue = { EXT2FS_PROTOCOL , glue, ext2fs_install, ext2fs_uninstall};
+
+static EFI_STATUS
+ext2fs_infosize(ext2fs_interface_t *this, fops_fd_t fd, UINT64 *sz)
+{
+       ext2fs_stat_t st;
+       EFI_STATUS status;
+
+       if (this == NULL || sz == NULL) return EFI_INVALID_PARAMETER;
+
+       status = this->ext2fs_fstat(this, fd, &st);
+       if (status != EFI_SUCCESS) return status;
+
+       *sz  = (UINT64)st.st_size;
+
+       return EFI_SUCCESS;
+}
+
+static INTN
+glue(fileops_t *fp, VOID *intf)
+{
+       ext2fs_interface_t *ext2fs = (ext2fs_interface_t *)intf;
+
+       /* record underlying interface */
+       fp->intf = intf;
+
+       fp->open     = (fops_open_t)ext2fs->ext2fs_open;
+       fp->read     = (fops_read_t)ext2fs->ext2fs_read;
+       fp->close    = (fops_close_t)ext2fs->ext2fs_close;
+       fp->infosize = (fops_infosize_t)ext2fs_infosize; /* local override */
+       fp->seek     = (fops_seek_t)ext2fs->ext2fs_seek;
+
+       /* fill out the name of the underlying file system */
+       ext2fs->ext2fs_name(ext2fs, fp->name, FILEOPS_NAME_MAXLEN);
+
+       return 0;
+}
diff --git a/glue_ext2fs.h b/glue_ext2fs.h
new file mode 100644 (file)
index 0000000..7b20ac8
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __GLUE_EXT2FS_H__
+#define __GLUE_EXT2FS_H__
+
+#include "fileops.h"
+
+extern fileops_fs_t ext2fs_glue;
+
+#endif /* __GLUE_EXT2FS_H__ */
diff --git a/glue_localfs.c b/glue_localfs.c
new file mode 100644 (file)
index 0000000..73aa13d
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+#include <efi.h>
+#include <efilib.h>
+
+#include "fs/localfs.h"
+#include "glue_localfs.h"
+#include "strops.h"
+#include "elilo.h"
+
+static INTN glue(fileops_t *this, VOID *intf);
+
+/* object exported to fileops */
+
+fileops_fs_t localfs_glue = { LOCALFS_PROTOCOL, glue, localfs_install, localfs_uninstall};
+
+
+static CHAR16 localfs_default_path[FILENAME_MAXLEN];
+
+/*
+ * remove /.\ pattern from path name:
+ *
+ * Example 1:  I am in fs0:\efi\debian, which contains elilo.efi, and I
+ * have typed 'elilo' at the efishell prompt.  set_default_path() gets
+ * called with string "\EFI\debian/.\elilo.efi". The final path name
+ * must then be set to "\EFI\debian\".
+ * 
+ * Example 2:  I am in fs0:\ and type 'efi\debian\elilo' at the shell
+ * prompt.  set_default_path() is called with "\/.\efi\debian\elilo.efi",
+ * the path must then be set to "\efi\debian\".
+ *
+ * Example 3:  I am in fs0:\efi and type '\efi\debian\elilo'.
+ * set_default_path() is called with "\efi\debian\elilo.efi", the
+ * path is "\efi\debian".
+ */
+static VOID
+set_default_path(CHAR16 *sptr)
+{
+#define is_sep(h)      (h == CHAR_SLASH || h == CHAR_BACKSLASH)
+       CHAR16 *dptr, *last_sep = NULL;
+       UINTN len = FILENAME_MAXLEN - 1;
+       UINTN last_was_sep = 0;
+       CHAR16 c;
+
+       dptr = localfs_default_path;
+
+       while (len-- && *sptr) {
+               c = sptr[0];
+
+               if (is_sep(c)) {
+                       if (last_was_sep) {
+                               sptr++;
+                               continue;
+                       }
+                       c = CHAR_BACKSLASH;
+                       last_was_sep = 1;
+                       last_sep = dptr;
+               } else {
+                       last_was_sep = 0;
+               }
+               *dptr++ = c;
+               sptr++;
+       }
+       if (last_sep)
+               *++last_sep = CHAR_NULL;
+       else
+               *dptr = CHAR_NULL;
+
+       DBG_PRT((L"localfs_default_path=%s\n", localfs_default_path));
+}
+
+
+/*
+ * The following glue functions are the only ones which need
+ * to know about the way the underlying interface is working
+ */
+#define LOCALFS_DEFAULT_KERNEL L"vmlinux"
+#define LOCALFS_DEFAULT_CONFIG L"elilo.conf"
+static EFI_STATUS
+localfs_setdefaults(VOID *this, CHAR16 *config, CHAR16 *kname, UINTN maxlen, CHAR16 *devpath)
+{
+       StrnCpy(kname, LOCALFS_DEFAULT_KERNEL, maxlen-1);
+       kname[maxlen-1] = CHAR_NULL;
+
+       StrnCpy(config, LOCALFS_DEFAULT_CONFIG, maxlen-1);
+       config[maxlen-1] = CHAR_NULL;
+
+       set_default_path(devpath);
+
+       return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+localfs_getdefault_path(CHAR16 *path, UINTN maxlen)
+{
+       if (maxlen <= StrLen(localfs_default_path)) return EFI_BUFFER_TOO_SMALL;
+
+       StrCpy(path, localfs_default_path);
+       return EFI_SUCCESS;
+}
+
+/*
+ * If the supplied path is a relative path, then prepend the path to
+ * the elilo.efi executable.  This ensures that elilo will look in
+ * its own directory for its config file, kernel images, etc, rather
+ * than the root directory of the disk.  Also * convert forward slashes
+ * into backward slashes.
+ */
+static EFI_STATUS 
+glue_open(VOID *intf, CHAR16 *name, fops_fd_t *fd)
+{
+       CHAR16 *p;
+       localfs_interface_t *localfs = (localfs_interface_t *)intf;
+       CHAR16 fullname[FILENAME_MAXLEN];
+
+       /*
+        * XXX: modification to passed argument (name)
+        */
+       for (p= name; *p != CHAR_NULL; p++) {
+               if (*p == CHAR_SLASH) *p = CHAR_BACKSLASH;
+       }
+       if (name[0] != CHAR_BACKSLASH && localfs_default_path[0] != CHAR_NULL) {
+               if (StrLen(localfs_default_path) + StrLen(name) + 1 >= FILENAME_MAXLEN)
+                       return EFI_INVALID_PARAMETER;
+
+               StrCpy(fullname, localfs_default_path);
+               StrCat(fullname, name);
+               name = fullname;
+       }
+       return localfs->localfs_open(intf, name, fd);
+}
+
+static INTN
+glue(fileops_t *fp, VOID *intf)
+{
+       localfs_interface_t *localfs = (localfs_interface_t *)intf;
+
+       fp->open            = glue_open;
+       fp->read            = (fops_read_t)localfs->localfs_read;
+       fp->close           = (fops_close_t)localfs->localfs_close;
+       fp->infosize        = (fops_infosize_t)localfs->localfs_infosize;
+       fp->seek            = (fops_seek_t)localfs->localfs_seek;
+       fp->setdefaults     = (fops_setdefaults_t)localfs_setdefaults;
+       fp->getdefault_path = (fops_getdefault_path_t)localfs_getdefault_path;
+       fp->intf            = intf;
+
+       /* fill out the name of the underlying file system */
+       localfs->localfs_name(localfs, fp->name, FILEOPS_NAME_MAXLEN);
+
+       return 0;
+}
diff --git a/glue_localfs.h b/glue_localfs.h
new file mode 100644 (file)
index 0000000..f27cb15
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __GLUE_LOCALFS_H__
+#define __GLUE_LOCALFS_H__
+
+#include "fileops.h"
+
+extern fileops_fs_t localfs_glue;
+
+#endif /* __GLUE_LOCALFS_H__ */
diff --git a/glue_netfs.c b/glue_netfs.c
new file mode 100644 (file)
index 0000000..757fc8b
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+#include <efi.h>
+#include <efilib.h>
+
+#include "glue_netfs.h"
+#include "fs/netfs.h"
+#include "strops.h"
+
+#include "elilo.h"
+#include "vars.h"
+
+/*
+ * disable this if you only want the default config file (elilo.conf)
+ * and not the ip-address based first file attempt
+ */
+
+static INTN glue(fileops_t *this, VOID *intf);
+
+/* object exported to fileops */
+fileops_fs_t netfs_glue = { NETFS_PROTOCOL  , glue, netfs_install, netfs_uninstall};
+       
+
+#define NETFS_DEFAULT_KERNEL           L"vmlinux"
+#define NETFS_DEFAULT_CONFIG           L"elilo.conf"
+#define NETFS_DEFAULT_SERVER_TYPE      EFI_PXE_BASE_CODE_BOOT_TYPE_REDHAT_BOOT
+
+
+static CHAR16 netfs_default_path[FILENAME_MAXLEN];
+
+
+/*
+ * Pxe Discovery protocol layers
+ * Layer 0 is used to download the boot loader
+ */
+#define NETFS_CONFIG_LAYER     1
+#define NETFS_KERNEL_LAYER     2
+
+static CHAR16 *hexa=L"0123456789ABCDEF";
+
+static VOID
+convert_ip2hex(UINT8 *ip, INTN l, CHAR16 *str)
+{
+       UINTN i;
+
+       for(i=0; i < l; i++) {
+               str[2*i]   = hexa[(ip[i] & 0xf0)>>4];
+               str[2*i+1] = hexa[ip[i] & 0x0f];
+       }
+}
+
+static VOID
+convert_ip2decstr(UINT8 *ip, INTN l, CHAR16 *str)
+{
+       UINTN i, j;
+       UINTN v, val;
+
+       for(i=0, j=0; i < l; i++) {
+               val = ip[i];
+               v = val / 100;
+               if (v) {
+                       str[j++] = L'0'+v;
+               }
+               val = val % 100;
+               v = val / 10;
+               if (v || ip[i] >= 100) {
+                       str[j++] = L'0'+v;
+               }
+
+               v = val % 10;
+               str[j++] = L'0'+v;
+               if (i < l-1) str[j++] = L'.';
+       }
+       str[j] = CHAR_NULL;
+}
+
+static int
+netfs_set_default_path(netfs_interface_t *netfs, netfs_info_t *info)
+{
+       INTN len;
+
+       StrnCpy(netfs_default_path, info->bootfile, FILENAME_MAXLEN);
+
+       len = StrLen(netfs_default_path) - 1;
+
+       while (len >= 0) {
+               if (netfs_default_path[len] == CHAR_SLASH || netfs_default_path[len] == CHAR_BACKSLASH) break;
+               len--;
+       }
+       netfs_default_path[len+1] = CHAR_NULL;
+       DBG_PRT((L"netfs_default_path=%s\n", netfs_default_path));
+
+       return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+netfs_setdefaults(VOID *intf, CHAR16 *config, CHAR16 *kname, UINTN maxlen, CHAR16 *devpath)
+{
+       netfs_interface_t *netfs = (netfs_interface_t *)intf;
+       netfs_info_t info;
+       EFI_STATUS status;
+       UINT8 *ipaddr;
+       UINTN m;
+       CHAR16 ip_var[64], str[64];
+       UINT8 *ip;
+
+       if (config == NULL || kname == NULL || maxlen < 1) return EFI_INVALID_PARAMETER;
+
+       netfs->netfs_getinfo(netfs, &info);
+
+       m      = info.using_ipv6 ? 16 : 4;
+       ipaddr = info.using_ipv6 ? info.cln_ipaddr.v6.Addr: info.cln_ipaddr.v4.Addr;
+
+       convert_ip2decstr(ipaddr, m, ip_var);
+       set_var(VAR_NETFS_IPADDR, ip_var);
+
+       ip = info.using_ipv6 ? info.netmask.v6.Addr: info.netmask.v4.Addr;
+       convert_ip2decstr(ip, m, str);
+       set_var(VAR_NETFS_NETMASK, str);
+
+       ip = info.using_ipv6 ? info.gw_ipaddr.v6.Addr: info.gw_ipaddr.v4.Addr;
+       convert_ip2decstr(ip, m, str);
+       set_var(VAR_NETFS_GATEWAY, str);
+
+       set_var(VAR_NETFS_HOSTNAME, info.hostname);
+       set_var(VAR_NETFS_DOMAINAME, info.domainame);
+
+       if (info.using_pxe) {
+               status = netfs->netfs_query_layer(netfs, 0, NETFS_CONFIG_LAYER, maxlen, config);
+               if (EFI_ERROR(status)) {
+                       StrnCpy(config, NETFS_DEFAULT_CONFIG, maxlen-1);
+                       config[maxlen-1] = CHAR_NULL;
+               }
+
+               status = netfs->netfs_query_layer(netfs, 0, NETFS_KERNEL_LAYER, maxlen, kname);
+               if (EFI_ERROR(status)) {
+                       StrnCpy(kname, NETFS_DEFAULT_KERNEL, maxlen-1);
+                       kname[maxlen-1] = CHAR_NULL;
+               }
+       } else {
+#ifdef ENABLE_MACHINE_SPECIFIC_NETCONFIG
+               /*
+                * will try a machine specific file first.
+                * the file is constructed based on the IP(v4) address
+                */
+               convert_ip2hex(ipaddr, m, config);
+
+               config[8]  = L'.';
+               config[9]  = L'c';
+               config[10] = L'o';
+               config[11] = L'n';
+               config[12] = L'f';
+               config[13] = CHAR_NULL;
+#else
+               StrnCpy(config, NETFS_DEFAULT_CONFIG, maxlen-1);
+               config[maxlen-1] = CHAR_NULL;
+#endif
+               StrnCpy(kname, NETFS_DEFAULT_KERNEL, maxlen-1);
+               kname[maxlen-1] = CHAR_NULL;
+
+               /*
+                * extract bootloader path prefix to be used for 
+                * the config file (and possibly the other files we 
+                * need to download)
+                */
+               netfs_set_default_path(netfs, &info);
+       }
+       return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+netfs_getdefault_path(CHAR16 *path, UINTN maxlen)
+{
+       if (maxlen <= StrLen(netfs_default_path)) return EFI_BUFFER_TOO_SMALL;
+
+       StrCpy(path, netfs_default_path);
+       return EFI_SUCCESS;
+}
+
+
+static EFI_STATUS 
+glue_open(VOID *intf, CHAR16 *name, fops_fd_t *fd)
+{
+       netfs_interface_t *netfs = (netfs_interface_t *)intf;
+       CHAR16 fullname[FILENAME_MAXLEN];
+
+       if (name[0] != CHAR_SLASH && name[0] != CHAR_BACKSLASH && netfs_default_path[0] != CHAR_NULL) {
+               if (StrLen(netfs_default_path) + StrLen(name) + 1 >= FILENAME_MAXLEN)
+                       return EFI_INVALID_PARAMETER;
+
+               StrCpy(fullname, netfs_default_path);
+               StrCat(fullname, name);
+               name = fullname;
+       }
+       return netfs->netfs_open(intf, name, fd);
+}
+
+static INTN
+glue(fileops_t *fp, VOID *intf)
+{
+       netfs_interface_t *netfs = (netfs_interface_t *)intf;
+
+       /* record underlying interface */
+       fp->intf        = intf;
+
+       fp->open            = glue_open;
+       fp->read            = (fops_read_t)netfs->netfs_read;
+       fp->close           = (fops_close_t)netfs->netfs_close;
+       fp->infosize        = (fops_infosize_t)netfs->netfs_infosize;
+       fp->seek            = (fops_seek_t)netfs->netfs_seek;
+       fp->setdefaults     = (fops_setdefaults_t)netfs_setdefaults;
+       fp->getdefault_path = (fops_getdefault_path_t)netfs_getdefault_path;
+       fp->intf            = intf;
+
+       /* fill out the name of the underlying file system */
+       netfs->netfs_name(netfs, fp->name, FILEOPS_NAME_MAXLEN);
+
+       return 0;
+}
diff --git a/glue_netfs.h b/glue_netfs.h
new file mode 100644 (file)
index 0000000..8176cde
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __GLUE_NETFS_H__
+#define __GLUE_NETFS_H__
+
+#include "fileops.h"
+
+extern fileops_fs_t netfs_glue;
+
+#endif /* __GLUE_NETFS_H__ */
diff --git a/gnu-efi-3.0a-ia32.patch b/gnu-efi-3.0a-ia32.patch
new file mode 100644 (file)
index 0000000..6e3baf1
--- /dev/null
@@ -0,0 +1,19 @@
+diff -urN gnu-efi-3.0a/gnuefi/elf_ia32_efi.lds gnu-efi-3.0a-ia32/gnuefi/elf_ia32_efi.lds
+--- gnu-efi-3.0a/gnuefi/elf_ia32_efi.lds       2002-02-22 15:43:28.000000000 -0800
++++ gnu-efi-3.0a-ia32/gnuefi/elf_ia32_efi.lds  2003-08-21 13:36:51.000000000 -0700
+@@ -17,6 +17,7 @@
+    *(.rodata*)
+    *(.data)
+    *(.data1)
++   *(.data.*)
+    *(.sdata)
+    *(.got.plt)
+    *(.got)
+@@ -34,6 +35,7 @@
+   .rel :
+   {
+     *(.rel.data)
++    *(.rel.data.*)
+     *(.rel.got)
+     *(.rel.stab)
+   }
diff --git a/ia32/Makefile b/ia32/Makefile
new file mode 100644 (file)
index 0000000..1b1c16c
--- /dev/null
@@ -0,0 +1,51 @@
+#
+#  Copyright (C) 2001-2003 Hewlett-Packard Co.
+#      Contributed by Stephane Eranian <eranian@hpl.hp.com>
+#
+# This file is part of the ELILO, the EFI Linux boot loader.
+#
+#  ELILO is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2, or (at your option)
+#  any later version.
+#
+#  ELILO is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with ELILO; see the file COPYING.  If not, write to the Free
+#  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+#  02111-1307, USA.
+#
+# Please check out the elilo.txt for complete documentation on how
+# to use this program.
+#
+
+include ../Make.defaults
+include ../Make.rules
+
+TOPDIR=$(CDIR)/..
+
+FILES=system.o config.o
+
+TARGET=sysdeps.o
+
+all: $(TARGET)
+
+system.o: rmswitch.h
+
+rmswitch.h: bin_to_h.c rmswitch.S
+       $(CC) -o bin_to_h bin_to_h.c
+       $(AS) -o rmswitch.o rmswitch.S
+       $(LD) -Ttext 0x0 -s --oformat binary -o rmswitch rmswitch.o
+       ./bin_to_h <rmswitch >rmswitch.h
+
+$(TARGET): $(FILES)
+       $(LD) -r -o $@ $(FILES)
+
+clean:
+       $(RM) -f $(TARGET) $(FILES)
+       $(RM) -f bin_to_h.o bin_to_h
+       $(RM) -f rmswitch.h rmswitch.o rmswitch
diff --git a/ia32/bin_to_h.c b/ia32/bin_to_h.c
new file mode 100644 (file)
index 0000000..2d1dd1b
--- /dev/null
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(void)
+{
+       unsigned n = 0;
+       int c;
+
+       printf("UINT8 rmswitch_image[] = {\n");
+
+       while ((c = getchar()) != EOF) {
+               printf("0x%02x,%s",
+                       c & 0xFF,
+                       (++n & 0x07) ? " " : "\n");
+       }
+
+       if (n & 0x07) {
+               printf("\n");
+       }
+
+       printf(
+               "};\n"
+               "UINTN rmswitch_size = sizeof rmswitch_image;\n");
+
+       return 0;
+}
diff --git a/ia32/config.c b/ia32/config.c
new file mode 100644 (file)
index 0000000..4bf25d0
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *     Contributed by Chris Ahna <christopher.j.ahna@intel.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "config.h"
+#include "private.h"
+
+typedef struct {
+       UINTN legacy_free_boot;
+} ia32_global_config_t;
+
+
+static ia32_global_config_t ia32_gconf;
+
+static config_option_t sysdeps_global_options[]={
+       {OPT_BOOL,      OPT_GLOBAL, L"legacy-free",     NULL,   NULL,   &ia32_gconf.legacy_free_boot}
+};
+
+
+/*
+ * IA-32 operations that need to be done only once and just before 
+ * entering the main loop of the loader
+ * Return:
+ *      0 if sucessful
+ *     -1 otherwise (will abort execution)
+ */
+INTN
+sysdeps_preloop_actions(EFI_HANDLE dev, CHAR16 **argv, INTN argc, INTN index, EFI_HANDLE image)
+{
+       return 0;
+}
+       
+#define IA32_CMDLINE_OPTIONS   L""
+
+CHAR16 *
+sysdeps_get_cmdline_opts(VOID)
+{
+       return IA32_CMDLINE_OPTIONS;
+}
+
+INTN
+sysdeps_getopt(INTN c, INTN optind, CHAR16 *optarg)
+{
+       return -1;
+}
+
+VOID
+sysdeps_print_cmdline_opts(VOID)
+{
+}
+
+
+INTN
+ia32_use_legacy_free_boot(VOID)
+{
+       return ia32_gconf.legacy_free_boot ? 1 : 0;
+}
+
+INTN
+sysdeps_register_options(VOID)
+{
+       INTN ret;
+
+       ret = register_config_options(sysdeps_global_options, 
+                                     sizeof(sysdeps_global_options)/sizeof(config_option_t),
+                                     OPTIONS_GROUP_GLOBAL);
+#if 0
+       /* no per image options yet */
+       if (ret == -1 ) return ret;
+
+       ret = register_config_options(sysdeps_image_options, 
+                                     sizeof(sysdeps_image_options)/sizeof(config_option_t),
+                                     OPTIONS_GROUP_IMAGE);
+#endif
+
+       return ret;
+}
diff --git a/ia32/private.h b/ia32/private.h
new file mode 100644 (file)
index 0000000..3813858
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __ELILO_PRIVATE_IA32_H__
+#define __ELILO_PRIVATE_IA32_H__
+
+#endif /* __ELILO_PRIVATE_IA32_H__ */
+
diff --git a/ia32/rmswitch.S b/ia32/rmswitch.S
new file mode 100644 (file)
index 0000000..24fd402
--- /dev/null
@@ -0,0 +1,118 @@
+#
+# Switch from protected mode to real mode and jump to setup.S
+# image located at %cx:0.
+#
+# This module must be placed into physical memory at 0:7C00h.
+# EFI has some real mode thunking code at 2000:0h.
+#
+# Processor and non-maskable interrupts should be disabled
+# before control is passed to this module.
+#
+
+.global _start
+
+.code32
+.text
+_start:
+       #
+       # Load identity mapped GDT & real mode IDT.
+       # Add 7C00h to the addresses since this is linked to start
+       # at 0h and it is being placed at 7C00h.
+       #
+
+       lgdt    %cs:gdt_48 + 0x7C00
+       lidt    %cs:idt_48 + 0x7C00
+
+       #
+       # Turn off PG bit in CR0 and set CR3 to zero.
+       #
+
+       movl    %cr0, %eax
+       andl    $0x7FFFFFFF, %eax
+       movl    %eax, %cr0
+
+       xorl    %eax, %eax
+       movl    %eax, %cr3
+
+       #
+       # Reload CS.
+       # Now we add 7B00h because we need to force the segment
+       # address and selector to be the same.
+       #
+
+       .byte   0xEA
+       .long   pm_reload + 0x7B00
+       .word   0x10
+
+pm_reload:
+
+.code16
+
+       #
+       # Reload DS, ES, FS, GS & SS.
+       #
+
+       movw    $0x18, %ax
+       movw    %ax, %ds
+       movw    %ax, %es
+       movw    %ax, %fs
+       movw    %ax, %gs
+       movw    %ax, %ss
+
+       #
+       # Switch to real mode.  Clear PE bit in CR0.
+       #
+
+       movl    %cr0, %eax
+       andl    $0xFFFFFFFE, %eax
+       movl    %eax, %cr0
+
+       #
+       # Reload CS.
+       #
+
+       .byte   0xEA
+       .word   rm_reload + 0x7C00
+       .word   0
+
+rm_reload:
+
+       #
+       # Reload SS & SP.
+       #
+
+       xorw    %ax, %ax
+       movw    %ax, %ss
+       movw    $0x7BFE, %sp
+
+       #
+       # Start running setup.S
+       #
+
+       .byte   0xEA
+       .word   0
+       .word   0x9020
+
+       #
+       # GDT & IDT stuff for switching into real mode.
+       #
+
+gdt:   .word   0, 0, 0, 0              # unused (00h)
+       .word   0, 0, 0, 0              # dummy (08h)
+       .word   0xFFFF, 0x100           # code (10h)
+       .word   0x9A00, 0
+       .word   0xFFFF, 0x180           # data (18h)
+       .word   0x9200, 0
+
+gdt_48:        .word   0x08 * 0x400
+       .long   gdt + 0x7C00
+
+idt_48:        .word   0x400
+       .long   0
+
+       #
+       # Be careful not to exceed 1F0h or the the bootsect.S
+       # parameters will be lost!
+       #
+
+.end
diff --git a/ia32/sysdeps.h b/ia32/sysdeps.h
new file mode 100644 (file)
index 0000000..d6f3324
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *     Contributed by Mike Johnston <johnston@intel.com>
+ *     Contributed by Chris Ahna <christopher.j.ahna@intel.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+/*
+ * This file is used to define all the IA32-specific data structures
+ * and constant used by the generic ELILO
+ */
+#ifndef __ELILO_SYSDEPS_IA32_H__
+#define __ELILO_SYSDEPS_IA32_H__
+
+#define ELILO_ARCH     "IA-32" /* ASCII string */
+
+/* for now use library versions */
+#define Memset(a,v,n)  SetMem((a),(n),(v))
+#define Memcpy(a,b,n)  CopyMem((a),(b),(n))
+
+/*
+ * This version must match the one in the kernel.
+ *
+ * This table was put together using information from the
+ * following Linux kernel source files:
+ *   linux/include/tty.h
+ *   linux/arch/i386/kernel/setup.c
+ *   linux/arch/i386/boot/bootsect.S
+ *   linux/arch/i386/boot/setup.S
+ *   linux/arch/i386/boot/video.S
+ *
+ * New fields in this structure for EFI and ELILO are:
+ *   efi_loader_sig
+ *   efi_st_addr
+ *
+ * A new bit, LDRFLAG_BOOT_PARAM_RELOC, in the loader_flags
+ * field is also defined in this file.
+ */
+typedef struct efi_ia32_boot_params {
+       UINT32 size;
+       UINT32 command_line;
+       UINT32 efi_sys_tbl;
+       UINT32 efi_mem_map;
+       UINT32 efi_mem_map_size;
+       UINT32 efi_mem_desc_size;
+       UINT32 efi_mem_desc_version;
+       UINT32 initrd_start;
+       UINT32 initrd_size;
+       UINT32 loader_start; 
+       UINT32 loader_size;
+       UINT32 kernel_start;
+       UINT32 kernel_size;
+       UINT16 num_cols;        
+       UINT16 num_rows;
+       UINT16 orig_x;
+       UINT16 orig_y;  
+} efi_ia32_boot_params_t;
+
+extern efi_ia32_boot_params_t efi_ia32_bp;
+#pragma pack(1)
+typedef union ia32_boot_params {
+       UINT8 raw[0x2000];
+       struct {
+/* Cursor position before passing control to kernel. */
+/* 0x00 */     UINT8 orig_cursor_col;          /* LDR */
+/* 0x01 */     UINT8 orig_cursor_row;          /* LDR */
+
+/* Available contiguous extended memory in KB. */
+/* 0x02 */     UINT16 ext_mem_k;               /* LDR */
+
+/* Video page, mode and screen width before passing control to kernel. */
+/* 0x04 */     UINT16 orig_video_page;         /* LDR */
+/* 0x06 */     UINT8 orig_video_mode;          /* LDR */
+/* 0x07 */     UINT8 orig_video_cols;          /* LDR */
+
+/* 0x08 */     UINT16 unused_1;                /* unused */
+
+/* %%TBD */
+/* 0x0A */     UINT16 orig_ega_bx;             /* LDR */
+
+/* 0x0C */     UINT16 unused_2;                /* unused */
+
+/* Screen height before passing control to kernel. */
+/* 0x0E */     UINT8 orig_video_rows;          /* LDR */
+
+/* %%TBD */
+/* 0x0F */     UINT8 is_vga;                   /* LDR */
+/* 0x10 */     UINT16 orig_video_points;       /* LDR */
+
+/* %%TBD */
+/* 0x12 */     UINT16 lfb_width;               /* LDR */
+/* 0x14 */     UINT16 lfb_height;              /* LDR */
+/* 0x16 */     UINT16 lfb_depth;               /* LDR */
+/* 0x18 */     UINT32 lfb_base;                /* LDR */
+/* 0x1C */     UINT32 lfb_size;                /* LDR */
+
+/* Offset of command line (from start of ia32_boot_param struct). */
+/* The command line magik number must be set for the kernel setup */
+/* code to use the command line offset. */
+/* 0x20 */     UINT16 cmdline_magik;           /* LDR */
+#define CMDLINE_MAGIK          0xA33F
+/* 0x22 */     UINT16 cmdline_offset;          /* LDR */
+
+/* %%TBD */
+/* 0x24 */     UINT16 lfb_line_len;            /* LDR */
+
+/* %%TBD */
+/* 0x26 */     UINT8 lfb_red_size;             /* LDR */
+/* 0x27 */     UINT8 lfb_red_pos;              /* LDR */
+/* 0x28 */     UINT8 lfb_green_size;           /* LDR */
+/* 0x29 */     UINT8 lfb_green_pos;            /* LDR */
+/* 0x2A */     UINT8 lfb_blue_size;            /* LDR */
+/* 0x2B */     UINT8 lfb_blue_pos;             /* LDR */
+/* 0x2C */     UINT8 lfb_rsvd_size;            /* LDR */
+/* 0x2D */     UINT8 lfb_rsvd_pos;             /* LDR */
+
+/* %%TBD */
+/* 0x2E */     UINT16 vesa_seg;                /* LDR */
+/* 0x30 */     UINT16 vesa_off;                /* LDR */
+
+/* %%TBD */
+/* 0x32 */     UINT16 lfb_pages;               /* LDR */
+/* 0x34 */     UINT8 lfb_reserved[0x0C];       /* reserved */
+
+/* %%TBD */
+/* 0x40 */     UINT16 apm_bios_ver;            /* LDR */
+#define NO_APM_BIOS            0x0000
+
+/* %%TBD */
+/* 0x42 */     UINT16 bios_code_seg;           /* LDR */
+/* 0x44 */     UINT32 bios_entry_point;        /* LDR */
+/* 0x48 */     UINT16 bios_code_seg16;         /* LDR */
+/* 0x4A */     UINT16 bios_data_seg;           /* LDR */
+
+/* %%TBD */
+/* 0x4C */     UINT16 apm_bios_flags;          /* LDR */
+#define NO_32BIT_APM_MASK      0xFFFD
+
+/* %%TBD */
+/* 0x4E */     UINT32 bios_code_len;           /* LDR */
+/* 0x52 */     UINT16 bios_data_len;           /* LDR */
+
+/* 0x54 */     UINT8 unused_3[0x2C];           /* unused */
+
+/* %%TBD */
+/* 0x80 */     UINT8 hd0_info[0x10];           /* LDR */
+/* 0x90 */     UINT8 hd1_info[0x10];           /* LDR */
+
+/* %%TBD */
+/* 0xA0 */     UINT16 mca_info_len;            /* LDR */
+/* 0xA2 */     UINT8 mca_info_buf[0x10];       /* LDR */
+
+/* 0xB2 */     UINT8 unused_4[0x10E];          /* unused */
+
+/* EFI boot loader signature. */
+/* 0x1C0 */    UINT8 efi_loader_sig[4];        /* LDR */
+#define EFI_LOADER_SIG         "EFIL"
+
+/* Address of the EFI system table. */
+/* 0x1C4 */    UINT32 efi_sys_tbl;             /* LDR */
+
+/* EFI memory descriptor size. */
+/* 0x1C8 */    UINT32 efi_mem_desc_size;       /* LDR */
+
+/* EFI memory descriptor version. */
+/* 0x1CC */    UINT32 efi_mem_desc_ver;        /* LDR */
+
+/* Address & size of EFI memory map. */
+/* 0x1D0 */    UINT32 efi_mem_map;             /* LDR */
+/* 0x1D4 */    UINT32 efi_mem_map_size;        /* LDR */
+
+/* Address & size of loader. */
+/* 0x1D8 */    UINT32 loader_start;            /* LDR */
+/* 0x1DC */    UINT32 loader_size;             /* LDR */
+
+/* Available contiguous extended memory in KB. */
+/* 0x1E0 */    UINT32 alt_mem_k;               /* LDR */
+
+/* 0x1E4 */    UINT8 unused_5[0x0D];           /* unused */
+
+/* Size of setup code in sectors (1 sector == 512 bytes). */
+/* 0x1F1 */    UINT8 setup_sectors;            /* BLD */
+
+/* %%TBD */
+/* 0x1F2 */    UINT16 mount_root_rdonly;       /* BLD */
+
+/* %%TBD */
+/* 0x1F4 */    UINT16 sys_size;                /* BLD */
+
+/* %%TBD */
+/* 0x1F6 */    UINT16 swap_dev;                /* BLD */
+
+/* %%TBD */
+/* 0x1F8 */    UINT16 ramdisk_flags;           /* BLD */
+#define RAMDISK_PROMPT         0x8000
+#define RAMDISK_LOAD           0x4000
+
+/* %%TBD */
+/* 0x1FA */    UINT16 video_mode_flag;         /* BLD */
+
+/* %%TBD */
+/* 0x1FC */    UINT16 orig_root_dev;           /* BLD */
+
+/* 0x1FE */    UINT8 unused_6;                 /* unused */
+
+/* %%TBD */
+/* 0x1FF */    UINT8 aux_dev_info;             /* LDR */
+#define NO_MOUSE               0x00
+#define FOUND_MOUSE            0xAA
+
+/* Jump past setup data (not used in EFI). */
+/* 0x200 */    UINT16 jump;                    /* BLD */
+
+/* Setup data signature. */
+/* 0x202 */    UINT8 setup_sig[4];             /* BLD */
+#define SETUP_SIG              "HdrS"
+
+/* %%TBD */
+/* 0x206 */    UINT8 hdr_minor;                /* BLD */
+/* 0x207 */    UINT8 hdr_major;                /* BLD */
+
+/* %%TBD */
+/* 0x208 */    UINT32 rm_switch;               /* LDD */
+
+/* %%TBD */
+/* 0x20C */    UINT16 start_sys_seg;           /* BLD */
+
+/* %%TBD */
+/* 0x20E */    UINT16 kernel_verstr_offset;    /* BLD */
+
+/* Loader type & version. */
+/* 0x210 */    UINT8 loader_type;              /* LDR */
+#define LDRTYPE_ELILO                  0x50    /* 5?h == elilo */
+                                               /* ?0h == revision */
+
+/* 0x211 */    UINT8 loader_flags;             /* BLD and LDR */
+#define LDRFLAG_CAN_USE_HEAP           0x80
+#define LDRFLAG_BOOT_PARAM_RELOC       0x40
+
+/* %%TBD */
+/* 0x212 */    UINT16 setup_move_size;         /* BLD */
+
+/* %%TBD */
+/* 0x214 */    UINT32 kernel_start;            /* LDR */
+
+/* %%TBD */
+/* 0x218 */    UINT32 initrd_start;            /* LDR */
+/* 0x21C */    UINT32 initrd_size;             /* LDR */
+
+/* %%TBD */
+/* 0x220 */    UINT32 bootsect_helper;         /* BLD */
+
+/* %%TBD */
+/* 0x224 */    UINT16 heap_end_ptr;            /* LDR */
+
+/* %%TBD */
+/* 0x226 */    UINT32 base_mem_size;           /* LDR */
+       } s;
+} boot_params_t;
+#pragma pack()
+
+/*
+ * The stuff below here is for jumping to the kernel.
+ */
+
+/*
+ * Some macros to copy and set memory after EFI has been
+ * stopped.
+ */
+
+#define MEMCPY(to, from, cnt) { \
+       UINT8 *t = (UINT8 *)(to); \
+       UINT8 *f = (UINT8 *)(from); \
+       UINTN n = cnt; \
+       if (t && f && n) { \
+               while (n--) { \
+                       *t++ = *f++; \
+               } \
+       } \
+}
+
+#define MEMSET(ptr, size, val) { \
+       UINT8 *p = (UINT8 *)(ptr); \
+       UINTN n = (UINTN)(size); \
+       UINT8 v = (UINT8)(val); \
+       if (p && n) { \
+               while (n--) { \
+                       *p++ = v; \
+               } \
+       } \
+}
+
+/*
+ * Descriptor table pointer format.
+ */
+#pragma pack(1)
+typedef struct {
+       UINT16 limit;
+       UINT32 base;
+} dt_addr_t;
+#pragma pack()
+
+extern UINTN high_base_mem;
+extern UINTN high_ext_mem;
+
+extern boot_params_t *param_start;
+extern UINTN param_size;
+
+extern VOID *kernel_start;
+extern UINTN kernel_size;
+
+extern VOID *initrd_start;
+extern UINTN initrd_size;
+
+extern dt_addr_t gdt_addr;
+extern dt_addr_t idt_addr;
+
+extern UINT16 init_gdt[];
+extern UINTN sizeof_init_gdt;
+
+extern UINT8 rmswitch_image[];
+extern UINTN rmswitch_size;
+
+extern INTN ia32_use_legacy_free_boot();
+
+/*
+ * How to jump to kernel code
+ */
+
+static inline void
+start_kernel(VOID *kentry, boot_params_t *bp)
+{
+       /*
+        * Disable interrupts.
+        */
+
+       asm volatile ( "cli" : : );
+
+       /*
+        * Relocate initrd, if present.
+        */
+
+       if (bp->s.initrd_start) {
+               /* %%TBD */
+               MEMCPY(15 * 1024 * 1024, bp->s.initrd_start, bp->s.initrd_size);
+               bp->s.initrd_start = 15 * 1024 * 1024;
+       }
+
+       /*
+        * Copy boot sector, setup data and command line
+        * to final resting place.  We need to copy
+        * BOOT_PARAM_MEMSIZE bytes.
+        */
+
+       MEMCPY(high_base_mem, bp, 0x4000);
+
+       /* 
+        * initialize efi ia32 boot params and place them at 1kb up from
+        * the start of the boot command line param.  This results in the 
+        * efi ia32 boot params to be copied to 0x00104c00.  See bootparams.c
+        * for details on how this is arranged.  EFI enabled 
+        * kernels will look for the efi boot params here to know if the
+        * kernel is booting on an EFI platform or legacy BIOS based platfrom
+        */
+
+       efi_ia32_bp.initrd_start = bp->s.initrd_start;
+       efi_ia32_bp.initrd_size = bp->s.initrd_size;
+
+       MEMCPY(high_base_mem + 0x4000 - 0x0400, &efi_ia32_bp, sizeof(efi_ia32_bp));
+       /*
+        * Initialize Linux GDT.
+        */
+
+       MEMSET(gdt_addr.base, gdt_addr.limit, 0);
+       MEMCPY(gdt_addr.base, init_gdt, sizeof_init_gdt);
+
+       if (! ia32_use_legacy_free_boot()) {
+
+               /*
+                * Copy our real mode transition code to 0x7C00.
+                */
+
+               MEMCPY(0x7C00, rmswitch_image, rmswitch_size);
+
+               asm volatile ( "movl $0x7C00, %%ebx" : : );
+               asm volatile ( "jmp *%%ebx" : : );
+       }
+
+       /*
+        * Load descriptor table pointers.
+        */
+
+       asm volatile ( "lidt %0" : : "m" (idt_addr) );
+       asm volatile ( "lgdt %0" : : "m" (gdt_addr) );
+
+       /*
+        * ebx := 0  (%%TBD - do not know why, yet)
+        * ecx := kernel entry point
+        * esi := address of boot sector and setup data
+        */
+
+       asm volatile ( "movl %0, %%esi" : : "m" (high_base_mem) );
+       asm volatile ( "movl %0, %%ecx" : : "m" (kentry) );
+       asm volatile ( "xorl %%ebx, %%ebx" : : );
+
+       /*
+        * Jump to kernel entry point.
+        */
+
+
+       asm volatile ( "jmp *%%ecx" : : );
+}
+
+typedef struct sys_img_options {
+       UINT8 nothing_yet;
+} sys_img_options_t;
+
+#endif /* __ELILO_SYSDEPS_IA32_H__ */
diff --git a/ia32/system.c b/ia32/system.c
new file mode 100644 (file)
index 0000000..2e8b8e4
--- /dev/null
@@ -0,0 +1,798 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *     Contributed by Mike Johnston <johnston@intel.com>
+ *     Contributed by Chris Ahna <christopher.j.ahna@intel.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+/*
+ * this file contains all the IA-32 specific code expected by generic loader
+ */
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "loader.h"
+
+#include "rmswitch.h"
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* extern loader_ops_t plain_loader, gzip_loader; */
+
+efi_ia32_boot_params_t efi_ia32_bp;
+
+
+/*
+ * Descriptor table base addresses & limits for Linux startup.
+ */
+
+dt_addr_t gdt_addr = { 0x800, 0x94000 };
+dt_addr_t idt_addr = { 0, 0 };
+
+/*
+ * Initial GDT layout for Linux startup.
+ */
+
+UINT16 init_gdt[] = {
+       /* gdt[0]: dummy */
+       0, 0, 0, 0, 
+       
+       /* gdt[1]: unused */
+       0, 0, 0, 0,
+
+       /* gdt[2]: code */
+       0xFFFF,         /* 4Gb - (0x100000*0x1000 = 4Gb) */
+       0x0000,         /* base address=0 */
+       0x9A00,         /* code read/exec */
+       0x00CF,         /* granularity=4096, 386 (+5th nibble of limit) */
+
+       /* gdt[3]: data */
+       0xFFFF,         /* 4Gb - (0x100000*0x1000 = 4Gb) */
+       0x0000,         /* base address=0 */
+       0x9200,         /* data read/write */
+       0x00CF,         /* granularity=4096, 386 (+5th nibble of limit) */
+};
+
+UINTN sizeof_init_gdt = sizeof init_gdt;
+
+
+/*
+ * Highest available base memory address.
+ *
+ * For traditional kernels and loaders this is always at 0x90000.
+ * For updated kernels and loaders this is computed by taking the
+ * highest available base memory address and rounding down to the
+ * nearest 64 kB boundary and then subtracting 64 kB.
+ *
+ * A non-compressed kernel is automatically assumed to be an updated
+ * kernel.  A compressed kernel that has bit 6 (0x40) set in the
+ * loader_flags field is also assumed to be an updated kernel.
+ */
+
+UINTN high_base_mem = 0x90000;
+
+/*
+ * Highest available extended memory address.
+ *
+ * This is computed by taking the highest available extended memory
+ * address and rounding down to the nearest EFI_PAGE_SIZE (usually
+ * 4 kB) boundary.  The ia32 Linux kernel can only support up to
+ * 2 GB (AFAIK).
+ */
+
+UINTN high_ext_mem = 32 * 1024 * 1024;
+
+/*
+ * Starting location and size of runtime memory blocks.
+ */
+
+boot_params_t *param_start = NULL;
+UINTN param_size = 0;
+
+VOID *kernel_start = (VOID *)0x100000; /* 1M */
+UINTN kernel_size = 0x200000;          /* 2M (largest x86 kernel image) */
+
+VOID *initrd_start = NULL;
+UINTN initrd_size = 0;
+
+/*
+ * Boot parameters can be relocated if TRUE.
+ * Boot parameters must be placed at 0x90000 if FALSE.
+ *
+ * This will be set to TRUE if bit 6 (0x40) is set in the loader_flags
+ * field in a compressed x86 boot format kernel.  This will also be set
+ * to TRUE if the kernel is an uncompressed ELF32 image.
+ *
+ * To remote boot w/ the universal network driver and a 16-bit UNDI
+ * this must be set to TRUE.
+ */
+
+BOOLEAN can_reloc_boot_params = FALSE;
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+static INTN
+probe_bzImage_boot(CHAR16 *kname)
+{
+       EFI_STATUS efi_status;
+       UINTN size;
+       fops_fd_t fd;
+       UINT8 bootsect[512];
+
+       DBG_PRT((L"probe_bzImage_boot()\n"));
+
+       if (!kname) {
+               ERR_PRT((L"kname == %xh", kname));
+               free_kmem();
+               return -1;
+       }
+
+       /*
+        * Open kernel image.
+        */
+
+       DBG_PRT((L"opening %s...\n", kname));
+
+       efi_status = fops_open(kname, &fd);
+
+       if (EFI_ERROR(efi_status)) {
+               ERR_PRT((L"Could not open %s.", kname));
+               free_kmem();
+               return -1;
+       }
+
+       /*
+        * Read boot sector.
+        */
+
+       DBG_PRT((L"\nreading boot sector...\n"));
+
+       size = sizeof bootsect;
+       efi_status = fops_read(fd, bootsect, &size);
+
+       if (EFI_ERROR(efi_status) || size != sizeof bootsect) {
+               ERR_PRT((L"Could not read boot sector from %s.", kname));
+               fops_close(fd);
+               free_kmem();
+               return -1;
+       }
+
+       /*
+        * Verify boot sector signature.
+        */
+
+       if (bootsect[0x1FE] != 0x55 || bootsect[0x1FF] != 0xAA) {
+               ERR_PRT((L"%s is not a bzImage kernel image.\n", kname));
+               fops_close(fd);
+               free_kmem();
+               return -1;
+       }
+
+       /*
+        * Check for out of range setup data size.
+        * Will almost always be 7, but we will accept 1 to 64.
+        */
+
+       DBG_PRT((L"bootsect[1F1h] == %d setup sectors\n", bootsect[0x1F1]));
+
+       if (bootsect[0x1F1] < 1 || bootsect[0x1F1] > 64) {
+               ERR_PRT((L"%s is not a valid bzImage kernel image.",
+                       kname));
+
+               fops_close(fd);
+               free_kmem();
+               return -1;
+       }
+
+       /*
+        * Allocate and read setup data.
+        */
+
+       DBG_PRT((L"reading setup data...\n"));
+
+       param_size = (bootsect[0x1F1] + 1) * 512;
+       //param_start = alloc(param_size, EfiBootServicesData);
+       param_start = alloc(param_size, EfiLoaderData);
+
+       DBG_PRT((L"param_size=%d param_start=%x", param_size, param_start));
+
+       if (!param_start) {
+               ERR_PRT((L"Could not allocate %d bytes of setup data.",
+                       param_size));
+
+               fops_close(fd);
+               free_kmem();
+               return -1;
+       }
+
+       CopyMem(param_start, bootsect, sizeof bootsect);
+
+       size = param_size - 512;
+       efi_status = fops_read(fd, ((UINT8 *)param_start) + 512, &size);
+
+       if (EFI_ERROR(efi_status) || size != param_size - 512) {
+               ERR_PRT((L"Could not read %d bytes of setup data.",
+                       param_size - 512));
+
+               free(param_start);
+               param_start = NULL;
+               param_size = 0;
+               fops_close(fd);
+               free_kmem();
+               return -1;
+       }
+
+       /*
+        * Check for setup data signature.
+        */
+
+       { UINT8 *c = ((UINT8 *)param_start)+514;
+       DBG_PRT((L"param_start(c=%x): %c-%c-%c-%c", c, (CHAR16)c[0],(CHAR16) c[1], (CHAR16)c[2], (CHAR16)c[3]));
+       }
+       if (CompareMem(((UINT8 *)param_start) + 514, "HdrS", 4)) {
+               ERR_PRT((L"%s does not have a setup signature.",
+                       kname));
+
+               free(param_start);
+               param_start = NULL;
+               param_size = 0;
+               fops_close(fd);
+               free_kmem();
+               return -1;
+       }
+
+       /*
+        * Allocate memory for kernel.
+        */
+
+       if (alloc_kmem(kernel_start, EFI_SIZE_TO_PAGES(kernel_size))) {
+               ERR_PRT((L"Could not allocate kernel memory."));
+               return -1;
+       } else {
+               VERB_PRT(3, Print(L"kernel_start: 0x%x  kernel_size: %d\n", kernel_start, kernel_size));
+       }
+
+       /*
+        * Now read the rest of the kernel image into memory.
+        */
+
+       DBG_PRT((L"reading kernel image...\n"));
+
+       size = kernel_size;
+
+       efi_status = fops_read(fd, kernel_start, &size);
+
+       if (EFI_ERROR(efi_status) || size < 0x10000) {
+               ERR_PRT((L"Error reading kernel image %s.", kname));
+               free(param_start);
+               param_start = NULL;
+               param_size = 0;
+               fops_close(fd);
+               free_kmem();
+               return -1;
+       }
+
+       DBG_PRT((L"kernel image read:  %d bytes, %d Kbytes\n", size, size / 1024));
+
+       /*
+        * Boot sector, setup data and kernel image loaded.
+        */
+
+       fops_close(fd);
+       return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+static INTN
+load_bzImage_boot(CHAR16 *kname, kdesc_t *kd)
+{
+       DBG_PRT((L"load_bzImage_boot()\n"));
+
+       if (!kname || !kd) {
+               ERR_PRT((L"kname=0x%x  kd=0x%x", kname, kd));
+
+               free(param_start);
+               param_start = NULL;
+               param_size = 0;
+               free_kmem();
+               return -1;
+       }
+
+       kd->kstart = kd->kentry = kernel_start;
+       kd->kend = ((UINT8 *)kd->kstart) + kernel_size;
+
+       DBG_PRT((L"kstart=0x%x  kentry=0x%x  kend=0x%x\n", kd->kstart, kd->kentry, kd->kend));
+
+       return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+static loader_ops_t loader_bzImage_boot = {
+       NULL,
+       L"loader_bzImage_boot",
+       &probe_bzImage_boot,
+       &load_bzImage_boot
+};
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+INTN
+sysdeps_init(EFI_HANDLE dev)
+{
+
+       DBG_PRT((L"sysdeps_init()\n"));
+
+       /*
+        * Register our loader(s)...
+        */
+
+       loader_register(&loader_bzImage_boot);
+       /* loader_register(&plain_loader); */   
+       /* loader_register(&gzip_loader); */
+
+
+       return 0;
+}
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ * initrd_get_addr()
+ *     Compute a starting address for the initial RAMdisk image.
+ *     For now, this image is placed immediately after the end of
+ *     the kernel memory.  Inside the start_kernel() code, the
+ *     RAMdisk image will be relocated to the top of available
+ *     extended memory.
+ */
+INTN
+sysdeps_initrd_get_addr(kdesc_t *kd, memdesc_t *imem)
+{
+       DBG_PRT((L"initrd_get_addr()\n"));
+
+       if (!kd || !imem) {
+               ERR_PRT((L"kd=0x%x imem=0x%x", kd, imem));
+               return -1;
+       }
+
+       VERB_PRT(3, Print(L"kstart=0x%x  kentry=0x%x  kend=0x%x\n", 
+               kd->kstart, kd->kentry, kd->kend));
+
+       imem->start_addr = kd->kend;
+
+       VERB_PRT(3, Print(L"initrd start_addr=0x%x pgcnt=%d\n", imem->start_addr, imem->pgcnt));
+
+       return 0;
+}
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+VOID
+sysdeps_free_boot_params(boot_params_t *bp)
+{
+       mmap_desc_t md;
+
+       ZeroMem(&md, sizeof md);
+       md.md = (VOID *)bp->s.efi_mem_map;
+       free_memmap(&md);
+}
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ * IA-32 specific boot parameters initialization routine
+ */
+INTN
+sysdeps_create_boot_params(
+       boot_params_t *bp,
+       CHAR8 *cmdline,
+       memdesc_t *initrd,
+       UINTN *cookie)
+{
+       mmap_desc_t mdesc;
+       EFI_STATUS efi_status;
+       UINTN rows, cols;
+       UINT8 row, col;
+       UINT8 mode;
+       UINT16 hdr_version;
+
+       DBG_PRT((L"fill_boot_params()\n"));
+
+       if (!bp || !cmdline || !initrd || !cookie) {
+               ERR_PRT((L"bp=0x%x  cmdline=0x%x  initrd=0x%x cookie=0x%x",
+                       bp, cmdline, initrd, cookie));
+
+               free(param_start);
+               param_start = NULL;
+               param_size = 0;
+               free_kmem();
+               return -1;
+       }
+
+       /*
+        * Copy temporary boot sector and setup data storage to
+        * elilo allocated boot parameter storage.  We only need
+        * the first two sectors (1K).  The rest of the storage
+        * can be used by the command line.
+        */
+
+       CopyMem(bp, param_start, 0x2000);
+
+       free(param_start);
+       param_start = NULL;
+       param_size = 0;
+
+       /*
+        * Save off our header revision information.
+        */
+
+       hdr_version = (bp->s.hdr_major << 8) | bp->s.hdr_minor;
+
+       /*
+        * Clear out unused memory in boot sector image.
+        */
+
+       bp->s.unused_1 = 0;
+       bp->s.unused_2 = 0;
+       ZeroMem(bp->s.unused_3, sizeof bp->s.unused_3);
+       ZeroMem(bp->s.unused_4, sizeof bp->s.unused_4);
+       ZeroMem(bp->s.unused_5, sizeof bp->s.unused_5);
+       bp->s.unused_6 = 0;
+
+       /*
+        * Tell kernel this was loaded by an advanced loader type.
+        * If this field is zero, the initrd_start and initrd_size
+        * fields are ignored by the kernel.
+        */
+
+       bp->s.loader_type = LDRTYPE_ELILO;
+
+       /*
+        * Setup command line information.
+        */
+
+       bp->s.cmdline_magik = CMDLINE_MAGIK;
+       bp->s.cmdline_offset = (UINT8 *)cmdline - (UINT8 *)bp;
+
+       /*
+        * Setup hard drive parameters.
+        * %%TBD - It should be okay to zero fill the hard drive
+        * info buffers.  The kernel should do its own detection.
+        */
+
+       ZeroMem(bp->s.hd0_info, sizeof bp->s.hd0_info);
+       ZeroMem(bp->s.hd1_info, sizeof bp->s.hd1_info);
+
+#if 0
+       CopyMem(bp->s.hd0_info, *((VOID **)(0x41 * 4)),
+               sizeof bp->s.hd0_info);
+
+       CopyMem(bp->s.hd1_info, *((VOID **)(0x46 * 4)),
+               sizeof bp->s.hd1_info);
+#endif
+
+       /*
+        * Memory info.
+        */
+
+       bp->s.alt_mem_k = high_ext_mem / 1024;
+
+       if (bp->s.alt_mem_k <= 65535) {
+               bp->s.ext_mem_k = (UINT16)bp->s.alt_mem_k;
+       } else {
+               bp->s.ext_mem_k = 65535;
+       }
+
+       if (hdr_version < 0x0202)
+               bp->s.base_mem_size = high_base_mem;
+
+       /*
+        * Initial RAMdisk and root device stuff.
+        */
+
+       DBG_PRT((L"initrd->start_addr=0x%x  initrd->pgcnt=%d\n",
+               initrd->start_addr, initrd->pgcnt));
+
+       /* These RAMdisk flags are not needed, just zero them. */
+       bp->s.ramdisk_flags = 0;
+
+       if (initrd->start_addr && initrd->pgcnt) {
+               /* %%TBD - This will probably have to be changed. */
+               bp->s.initrd_start = (UINT32)initrd->start_addr;
+               bp->s.initrd_size = (UINT32)(initrd->pgcnt * EFI_PAGE_SIZE);
+
+               /*
+                * This is the RAMdisk root device for RedHat 2.2.x
+                * kernels (major 0x01, minor 0x00).
+                * %%TBD - Will this work for other distributions and
+                * 2.3.x and 2.4.x kernels?  I do not know, yet.
+                */
+
+               bp->s.orig_root_dev = 0x0100;
+       } else {
+               bp->s.initrd_start = 0;
+               bp->s.initrd_size = 0;
+
+               /* Do not change the root device if there is no RAMdisk. */
+               /* bp->s.orig_root_dev = 0; */
+       }
+
+       /*
+        * APM BIOS info.
+        */
+
+/* %%TBD - How to do Int 15h calls to get this info? */
+       bp->s.apm_bios_ver = NO_APM_BIOS;
+       bp->s.bios_code_seg = 0;
+       bp->s.bios_entry_point = 0;
+       bp->s.bios_code_seg16 = 0;
+       bp->s.bios_data_seg = 0;
+       bp->s.apm_bios_flags = 0;
+       bp->s.bios_code_len = 0;
+       bp->s.bios_data_len = 0;
+
+       /*
+        * MCA BIOS info (misnomer).
+        */
+
+/* %%TBD - How to do Int 15h call to get this info? */
+       bp->s.mca_info_len = 0;
+       ZeroMem(bp->s.mca_info_buf, sizeof bp->s.mca_info_buf);
+
+       /*
+        * Pointing device presence.
+        */
+
+/* %%TBD - How to do Int 11h call to get this info? */
+       bp->s.aux_dev_info = NO_MOUSE;
+
+       /*
+        * EFI loader signature and address of EFI system table.
+        */
+
+       CopyMem(bp->s.efi_loader_sig, EFI_LOADER_SIG, 4);
+       bp->s.efi_sys_tbl = 0; /* %%TBD */
+
+       /*
+        * Kernel entry point.
+        */
+
+       bp->s.kernel_start = (UINT32)kernel_start;
+
+       /*
+        * When changing stuff in the parameter structure compare
+        * the offsets of the fields with the offsets used in the
+        * boot sector and setup source files.
+        *   arch/i386/boot/bootsect.S
+        *   arch/i386/boot/setup.S
+        *   arch/i386/kernel/setup.c
+        */
+
+#define CHECK_OFFSET(n, o, f) \
+{ \
+       UINTN p = (UINT8 *)&bp->s.n - (UINT8 *)bp; \
+       UINTN q = (UINTN)(o); \
+       if (p != q) { \
+               test |= 1; \
+               Print(L"%20a:  %3xh  %3xh  ", #n, p, q); \
+               if (*f) { \
+                       Print(f, bp->s.n); \
+               } \
+               Print(L"\n"); \
+       } \
+}
+
+#define WAIT_FOR_KEY() \
+{ \
+       EFI_INPUT_KEY key; \
+       while (ST->ConIn->ReadKeyStroke(ST->ConIn, &key) != EFI_SUCCESS) { \
+               ; \
+       } \
+}
+
+       {
+               UINTN test = 0;
+
+               CHECK_OFFSET(orig_cursor_col, 0x00, L"%xh");
+               CHECK_OFFSET(orig_cursor_row, 0x01, L"%xh");
+               CHECK_OFFSET(ext_mem_k, 0x02, L"%xh");
+               CHECK_OFFSET(orig_video_page, 0x04, L"%xh");
+               CHECK_OFFSET(orig_video_mode, 0x06, L"%xh");
+               CHECK_OFFSET(orig_video_cols, 0x07, L"%xh");
+               CHECK_OFFSET(orig_ega_bx, 0x0A, L"%xh");
+               CHECK_OFFSET(orig_video_rows, 0x0E, L"%xh");
+               CHECK_OFFSET(is_vga, 0x0F, L"%xh");
+               CHECK_OFFSET(orig_video_points, 0x10, L"%xh");
+               CHECK_OFFSET(lfb_width, 0x12, L"%xh");
+               CHECK_OFFSET(lfb_height, 0x14, L"%xh");
+               CHECK_OFFSET(lfb_depth, 0x16, L"%xh");
+               CHECK_OFFSET(lfb_base, 0x18, L"%xh");
+               CHECK_OFFSET(lfb_size, 0x1C, L"%xh");
+               CHECK_OFFSET(cmdline_magik, 0x20, L"%xh");
+               CHECK_OFFSET(cmdline_offset, 0x22, L"%xh");
+               CHECK_OFFSET(lfb_line_len, 0x24, L"%xh");
+               CHECK_OFFSET(lfb_red_size, 0x26, L"%xh");
+               CHECK_OFFSET(lfb_red_pos, 0x27, L"%xh");
+               CHECK_OFFSET(lfb_green_size, 0x28, L"%xh");
+               CHECK_OFFSET(lfb_green_pos, 0x29, L"%xh");
+               CHECK_OFFSET(lfb_blue_size, 0x2A, L"%xh");
+               CHECK_OFFSET(lfb_blue_pos, 0x2B, L"%xh");
+               CHECK_OFFSET(lfb_rsvd_size, 0x2C, L"%xh");
+               CHECK_OFFSET(lfb_rsvd_pos, 0x2D, L"%xh");
+               CHECK_OFFSET(vesa_seg, 0x2E, L"%xh");
+               CHECK_OFFSET(vesa_off, 0x30, L"%xh");
+               CHECK_OFFSET(lfb_pages, 0x32, L"%xh");
+               CHECK_OFFSET(lfb_reserved, 0x34, L"");
+               CHECK_OFFSET(apm_bios_ver, 0x40, L"%xh");
+               CHECK_OFFSET(bios_code_seg, 0x42, L"%xh");
+               CHECK_OFFSET(bios_entry_point, 0x44, L"%xh");
+               CHECK_OFFSET(bios_code_seg16, 0x48, L"%xh");
+               CHECK_OFFSET(bios_data_seg, 0x4A, L"%xh");
+               CHECK_OFFSET(apm_bios_flags, 0x4C, L"%xh");
+               CHECK_OFFSET(bios_code_len, 0x4E, L"%xh");
+               CHECK_OFFSET(bios_data_len, 0x52, L"%xh");
+               CHECK_OFFSET(hd0_info, 0x80, L"");
+               CHECK_OFFSET(hd1_info, 0x90, L"");
+               CHECK_OFFSET(mca_info_len, 0xA0, L"%xh");
+               CHECK_OFFSET(mca_info_buf, 0xA2, L"");
+               CHECK_OFFSET(efi_loader_sig, 0x1C0, L"'%-4.4a'");
+               CHECK_OFFSET(efi_sys_tbl, 0x1C4, L"%xh");
+               CHECK_OFFSET(efi_mem_desc_size, 0x1C8, L"%xh");
+               CHECK_OFFSET(efi_mem_desc_ver, 0x1CC, L"%xh");
+               CHECK_OFFSET(efi_mem_map, 0x1D0, L"%xh");
+               CHECK_OFFSET(efi_mem_map_size, 0x1D4, L"%xh");
+               CHECK_OFFSET(loader_start, 0x1D8, L"%xh");
+               CHECK_OFFSET(loader_size, 0x1DC, L"%xh");
+               CHECK_OFFSET(alt_mem_k, 0x1E0, L"%xh");
+               CHECK_OFFSET(setup_sectors, 0x1F1, L"%xh");
+               CHECK_OFFSET(mount_root_rdonly, 0x1F2, L"%xh");
+               CHECK_OFFSET(sys_size, 0x1F4, L"%xh");
+               CHECK_OFFSET(swap_dev, 0x1F6, L"%xh");
+               CHECK_OFFSET(ramdisk_flags, 0x1F8, L"%xh");
+               CHECK_OFFSET(video_mode_flag, 0x1FA, L"%xh");
+               CHECK_OFFSET(orig_root_dev, 0x1FC, L"%xh");
+               CHECK_OFFSET(aux_dev_info, 0x1FF, L"%xh");
+               CHECK_OFFSET(jump, 0x200, L"%xh");
+               CHECK_OFFSET(setup_sig, 0x202, L"'%-4.4a'");
+               CHECK_OFFSET(hdr_minor, 0x206, L"%xh");
+               CHECK_OFFSET(hdr_major, 0x207, L"%xh");
+               CHECK_OFFSET(rm_switch, 0x208, L"%xh");
+               CHECK_OFFSET(start_sys_seg, 0x20C, L"%xh");
+               CHECK_OFFSET(kernel_verstr_offset, 0x20E, L"%xh");
+               CHECK_OFFSET(loader_type, 0x210, L"%xh");
+               CHECK_OFFSET(loader_flags, 0x211, L"%xh");
+               CHECK_OFFSET(setup_move_size, 0x212, L"%xh");
+               CHECK_OFFSET(kernel_start, 0x214, L"%xh");
+               CHECK_OFFSET(initrd_start, 0x218, L"%xh");
+               CHECK_OFFSET(initrd_size, 0x21C, L"%xh");
+               CHECK_OFFSET(bootsect_helper, 0x220, L"%xh");
+               CHECK_OFFSET(heap_end_ptr, 0x224, L"%xh");
+               CHECK_OFFSET(base_mem_size, 0x226, L"%xh");
+
+               if (test) {
+                       ERR_PRT((L"Boot sector and/or setup parameter alignment error."));
+                       free_kmem();
+                       return -1;
+               }
+       }
+
+       /*
+        * Get video information.
+        * Do this last so that any other cursor positioning done
+        * in the fill routine gets accounted for.
+        */
+
+       efi_status = ST->ConOut->QueryMode(
+               ST->ConOut,
+               ST->ConOut->Mode->Mode,
+               &cols,
+               &rows);
+
+       if (EFI_ERROR(efi_status)) {
+               ERR_PRT((L"QueryMode failed.  Fake it."));
+
+               mode = 3;
+               rows = 25;
+               cols = 80;
+               row = 24;
+               col = 0;
+       } else {
+               mode = (UINT8)ST->ConOut->Mode->Mode;
+               col = (UINT8)ST->ConOut->Mode->CursorColumn;
+               row = (UINT8)ST->ConOut->Mode->CursorRow;
+       }
+
+       bp->s.orig_cursor_col = col;
+       bp->s.orig_cursor_row = row;
+       bp->s.orig_video_page = 0;
+       bp->s.orig_video_mode = mode;
+       bp->s.orig_video_cols = (UINT8)cols;
+       bp->s.orig_video_rows = (UINT8)rows;
+
+/* %%TBD - How to do Int 10h calls to get video info? */
+       bp->s.orig_ega_bx = 0;
+       bp->s.is_vga = 0;
+       bp->s.orig_video_points = 0;
+
+/* %%TBD - How to do Int 10h calls to get frame buffer info? */
+       bp->s.lfb_width = 0;
+       bp->s.lfb_height = 0;
+       bp->s.lfb_depth = 0;
+       bp->s.lfb_base = 0;
+       bp->s.lfb_size = 0;
+       bp->s.lfb_line_len = 0;
+       bp->s.lfb_red_size = 0;
+       bp->s.lfb_red_pos = 0;
+       bp->s.lfb_green_size = 0;
+       bp->s.lfb_green_pos = 0;
+       bp->s.lfb_blue_size = 0;
+       bp->s.lfb_blue_pos = 0;
+       bp->s.lfb_rsvd_size = 0;
+       bp->s.lfb_rsvd_pos = 0;
+       bp->s.lfb_pages = 0;
+       bp->s.vesa_seg = 0;
+       bp->s.vesa_off = 0;
+
+       /*
+        * Get memory map description and cookie for ExitBootServices()
+        */
+
+       if (get_memmap(&mdesc)) {
+               ERR_PRT((L"Could not get memory map."));
+               free_kmem();
+               return -1;
+       }
+
+       *cookie = mdesc.cookie;
+       bp->s.efi_mem_map = (UINTN)mdesc.md;
+       bp->s.efi_mem_map_size = mdesc.map_size;
+       bp->s.efi_mem_desc_size = mdesc.desc_size;
+       bp->s.efi_mem_desc_ver = mdesc.desc_version;
+       bp->s.efi_sys_tbl = (UINTN)systab;
+       
+       /*
+        * my_ia32_boot_params and get ready to slap them into 0x00104c00
+        */
+
+       efi_ia32_bp.size= sizeof(efi_ia32_bp);
+       efi_ia32_bp.command_line = (UINT32) cmdline;
+       efi_ia32_bp.efi_sys_tbl = bp->s.efi_sys_tbl;
+       efi_ia32_bp.efi_mem_map = bp->s.efi_mem_map;
+       efi_ia32_bp.efi_mem_map_size = bp->s.efi_mem_map_size;
+       efi_ia32_bp.efi_mem_desc_size = bp->s.efi_mem_desc_size;
+       efi_ia32_bp.efi_mem_desc_version = bp->s.efi_mem_desc_ver;
+       efi_ia32_bp.initrd_start = (UINTN)initrd->start_addr;
+       efi_ia32_bp.initrd_size = initrd->pgcnt * EFI_PAGE_SIZE;
+       efi_ia32_bp.loader_start = 0;
+       efi_ia32_bp.loader_size = 0;
+       efi_ia32_bp.kernel_start = bp->s.kernel_start;
+       efi_ia32_bp.kernel_size = kernel_size;
+       efi_ia32_bp.num_cols = cols;
+       efi_ia32_bp.num_rows = rows;
+       efi_ia32_bp.orig_x = col;
+       efi_ia32_bp.orig_y = row;
+
+
+       return 0;
+}
diff --git a/ia64/Makefile b/ia64/Makefile
new file mode 100644 (file)
index 0000000..a243d0f
--- /dev/null
@@ -0,0 +1,42 @@
+#
+#  Copyright (C) 2001-2003 Hewlett-Packard Co.
+#      Contributed by Stephane Eranian <eranian@hpl.hp.com>
+#
+# This file is part of the ELILO, the EFI Linux boot loader.
+#
+#  ELILO is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2, or (at your option)
+#  any later version.
+#
+#  ELILO is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with ELILO; see the file COPYING.  If not, write to the Free
+#  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+#  02111-1307, USA.
+#
+# Please check out the elilo.txt for complete documentation on how
+# to use this program.
+#
+
+include ../Make.defaults
+include ../Make.rules
+
+TOPDIR=$(CDIR)/..
+
+FILES=system.o config.o fpswa.o plain_loader.o gzip_loader.o \
+      gzip.o memset.o memcpy.o setjmp.o longjmp.o
+
+TARGET=sysdeps.o
+
+all: $(TARGET)
+
+$(TARGET): $(FILES)
+       $(LD) -o $@ -r $(FILES)
+
+clean:
+       $(RM) -f $(TARGET) $(FILES)
diff --git a/ia64/config.c b/ia64/config.c
new file mode 100644 (file)
index 0000000..2c86cb1
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "config.h"
+#include "private.h"
+#include "sysdeps.h"
+#include "getopt.h"
+
+typedef struct {
+       CHAR16  fpswa[FILENAME_MAXLEN];
+       CHAR16  cmd_fpswa[FILENAME_MAXLEN];
+       UINTN allow_relocation;
+} ia64_global_config_t;
+
+#define ia64_opt_offsetof(option)      (&((sys_img_options_t *)(0x0))->option)
+
+static ia64_global_config_t ia64_gconf;
+
+/*
+ * No IA-64 specific options at this point
+ * The last entry in each table MUST be use the OPT_NULL type to terminate
+ * the chain.
+ */
+config_option_t sysdeps_global_options[]={
+       {OPT_FILE,      OPT_GLOBAL, L"fpswa",           NULL,   NULL,   ia64_gconf.fpswa},
+       {OPT_BOOL,      OPT_GLOBAL, L"relocatable",     NULL,   NULL,   &ia64_gconf.allow_relocation},
+};
+
+config_option_t sysdeps_image_options[]={
+       {OPT_BOOL,      OPT_IMAGE_SYS,  L"relocatable", NULL,   NULL,   ia64_opt_offsetof(allow_relocation)},
+};
+
+
+/*
+ * IA-64 operations that need to be done only once and just before 
+ * entering the main loop of the loader
+ * Return:
+ *      0 if sucessful
+ *     -1 otherwise (will abort execution)
+ */
+INTN
+sysdeps_preloop_actions(EFI_HANDLE dev, CHAR16 **argv, INTN argc, INTN index, EFI_HANDLE image)
+{
+       /* 
+        * we have separate string to make sure that the command line take precedence over
+        * the config file
+        */
+       if (ia64_gconf.cmd_fpswa[0] != CHAR_NULL) {
+               check_fpswa(image, dev, ia64_gconf.cmd_fpswa);
+       } else if (ia64_gconf.fpswa[0] != CHAR_NULL) 
+               check_fpswa(image, dev, ia64_gconf.fpswa);
+       else
+               check_fpswa(image, dev, NULL);
+
+       return 0;
+}
+
+/*
+ * Return:
+ *     1: if image or global configuration allows relocation
+ *     0: otherwise
+ *
+ * It is written has a function rather than a macro to avoid
+ * exposing config data structure to the rest of the code in ia64
+ */
+INTN
+ia64_can_relocate(VOID)
+{
+       return ia64_gconf.allow_relocation == TRUE
+           || (elilo_opt.sys_img_opts && elilo_opt.sys_img_opts->allow_relocation ==TRUE) ? 1 : 0;
+}
+
+#define IA64_CMDLINE_OPTIONS   L"rF:"
+
+CHAR16 *
+sysdeps_get_cmdline_opts(VOID)
+{
+       return IA64_CMDLINE_OPTIONS;
+}
+
+INTN
+sysdeps_getopt(INTN c, INTN optind, CHAR16 *optarg)
+{
+       INTN ret = 0; /* let's be optimistic ! */
+
+       /*
+        * XXX: for now these command line options have to be global
+        */
+       switch(c) {
+               case L'r':
+                       ia64_gconf.allow_relocation = 1;
+                       break;
+               case L'F':
+                       if (StrLen(Optarg) >= FILENAME_MAXLEN) {
+                               Print(L"FPSWA filename is limited to %d characters\n", FILENAME_MAXLEN);
+                               return -1;
+                       }
+                       StrCpy(ia64_gconf.cmd_fpswa, Optarg);
+                       break;
+               default:
+                       ret = -1;
+       }
+       return ret;
+}
+
+VOID
+sysdeps_print_cmdline_opts(VOID)
+{
+       Print(L"-r        kernel image can be relocated if load address inexistent\n");
+       Print(L"-F file   name of a specific FPSWA EFI driver to load\n");
+}
+
+INTN
+sysdeps_register_options(VOID)
+{
+       INTN ret;
+
+       ret = register_config_options(sysdeps_global_options, 
+                                     sizeof(sysdeps_global_options)/sizeof(config_option_t),
+                                     OPTIONS_GROUP_GLOBAL);
+       if (ret == -1 ) return ret;
+
+       ret = register_config_options(sysdeps_image_options, 
+                                     sizeof(sysdeps_image_options)/sizeof(config_option_t),
+                                     OPTIONS_GROUP_IMAGE);
+
+       return ret;
+}
diff --git a/ia64/fpswa.c b/ia64/fpswa.c
new file mode 100644 (file)
index 0000000..a19d7cb
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "fileops.h"
+
+typedef struct {
+               UINT32  revision;
+               UINT32  reserved;
+               VOID    *fpswa;
+} fpswa_interface_t;
+
+INTN
+query_fpswa(VOID **fpswa)
+{
+       EFI_HANDLE fpswa_image;
+       UINTN size;
+       EFI_STATUS status;
+       EFI_GUID FpswaProtocol = FPSWA_PROTOCOL;
+
+       DBG_PRT((L"Querying FpswaProtocol"));
+
+       size   = sizeof(EFI_HANDLE);
+
+       status = BS->LocateHandle(ByProtocol, &FpswaProtocol, NULL, &size, &fpswa_image);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"boot_params could not locate FPSWA driver", status));
+               return -1;
+        }
+       status = BS->HandleProtocol(fpswa_image, &FpswaProtocol, fpswa);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"boot_params FpswaProtocol not able find the interface"));
+               return -1;
+       } 
+       VERB_PRT(3, Print(L"FpswaProtocol = 0x%lx revision=%x\n", *fpswa,
+                               ((fpswa_interface_t *)*fpswa)->revision));
+       return 0;
+}
+
+
+static INTN
+do_check_fpswa(EFI_HANDLE image, EFI_HANDLE dev, CHAR16 *fpswa_file)
+{
+       EFI_STATUS status;
+       EFI_HANDLE handle;
+       EFI_DEVICE_PATH *dp;
+
+
+       dp = FileDevicePath(dev, fpswa_file);
+       if (dp == NULL) {
+               ERR_PRT((L"Cannot create FilePath for %s", fpswa_file));
+               return -1;
+       }
+       status = BS->LoadImage(0, image, dp, NULL, 0, &handle);
+       if (EFI_ERROR(status)) {
+               VERB_PRT(3, Print(L"..not found\n"));
+               FreePool(dp);
+               return -1;
+       }
+       VERB_PRT(3, Print(L"..starting.."));
+
+       status = BS->StartImage(handle, 0, 0);
+       if (EFI_ERROR(status)) {
+               VERB_PRT(3, Print(L"failed (%r)\n", status));
+               /* 
+                * StartImage() automatically unloads if error 
+                * FPSWA init code will automatically abort if newer revision
+                * is already installed
+                */     
+       } else {
+               VERB_PRT(3, Print(L"..ok\n"));
+       }
+       FreePool(dp);
+
+       return 0;
+}
+
+/*
+ * If the caller specifies a fpswa filename, then it used instead of the
+ * defaults.
+ * Return:
+ *     0 : indicates that one fpswa driver was loaded, i.e. an update could be done
+ *     -1: no update was found that would have a more recent version of the driver. This is 
+ *         not a fatal return value.
+ */
+INTN
+check_fpswa(EFI_HANDLE image, EFI_HANDLE dev, CHAR16 *fpswa_file)
+{
+       /*
+        * we must use \\ here as this is given to LoadImage() directly
+        *
+        * The FPSWA driver MUST be called fpswa.efi and the FPSWA document
+        * (see developer.intel.com/design/itanium) stipulates that the 
+        * file must be placed in \EFI\Intel Firmware\ (no mention of which
+        * EFI system partition). So elilo will check on all accessible
+        * Fat32+ partition for the existence of this directory and file.
+        */
+       static CHAR16 *fpswa_filenames[] ={
+               L"\\efi\\intel firmware\\fpswa.efi",
+#if 0
+               L"\\fpswa.efi",
+               L"\\fw\\fpswa.efi",
+               L"\\efi\\fpswa.efi",
+               L"\\efi\\tools\\fpswa.efi",
+               L"\\fpswa.efi",
+               L"fpswa.efi",
+#endif
+       };
+       UINTN j, count = sizeof(fpswa_filenames)/sizeof(CHAR16 *);
+       INTN cookie;
+       CHAR16 devname[FILENAME_MAXLEN];
+       
+       if (fpswa_file) {
+               INTN r;
+               devname[0] = CHAR_NULL;
+               r = fops_split_path(fpswa_file, devname);
+               if (r == -1) {
+                       ERR_PRT((L"FPSWA driver filename too long %s", fpswa_file));
+                       return -1;
+               }
+               if (devname[0] != CHAR_NULL) {
+                       if (fops_get_device_handle(devname, &dev) != EFI_SUCCESS) {
+                               ERR_PRT((L"cannot find device %s for FPSWA driver", devname));
+                               return -1;
+                       }
+               }
+               return do_check_fpswa(image, dev, fpswa_file);
+       }
+
+       cookie = 0;
+       while (fops_get_next_device(cookie, L"vfat", FILENAME_MAXLEN, &cookie, devname, &dev) == EFI_SUCCESS) {
+               for (j = 0; j < count; j++) {
+                       VERB_PRT(3, Print(L"Trying FPSWA driver %s:%s..", devname, fpswa_filenames[j]));
+                       /*
+                        * we need to do all choices to make sure we pickup
+                        * the latest version.
+                        */
+                       do_check_fpswa(image, dev, fpswa_filenames[j]);
+               }
+       }
+       return -1;
+}
diff --git a/ia64/gzip.c b/ia64/gzip.c
new file mode 100644 (file)
index 0000000..bb5a065
--- /dev/null
@@ -0,0 +1,651 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ *  Copyright (C) 2001 Silicon Graphics, Inc.
+ *      Contributed by Brent Casavant <bcasavan@sgi.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elf.h"
+#include "elilo.h"
+
+#include "gzip.h"
+
+#include "private.h"
+#include "setjmp.h"
+
+#define memzero(s, n)  Memset((VOID *)(s), 0, (n))
+#define memcpy(a,b,n)  Memcpy((VOID *)(a),(b),(n))
+
+/* size of output buffer */
+#define WSIZE 0x8000           /* Window size must be at least 32k, */
+                               /* and a power of two */
+/* size of input buffer */
+#define INBUFSIZE 0x8000
+
+/*
+ * gzip declarations
+ */
+
+#define OF(args)  args
+#define FUNC_STATIC static
+
+typedef unsigned char  uch;
+typedef unsigned short ush;
+typedef unsigned long  ulg;
+
+
+typedef struct segment {
+       unsigned long addr;     /* start address */
+               unsigned long offset;   /* file offset   */
+               unsigned long size;     /* file size     */
+       unsigned long bss_sz;   /* BSS size      */
+       UINT8   flags;  /* indicates whether to load or not */
+} segment_t;
+
+#define CHUNK_FL_VALID         0x1
+#define CHUNK_FL_LOAD          0x2
+
+#define CHUNK_CAN_LOAD(n)      chunks[(n)].flags |= CHUNK_FL_LOAD
+#define CHUNK_NO_LOAD(n)       chunks[(n)].flags &= ~CHUNK_FL_LOAD
+#define CHUNK_IS_LOAD(n)       (chunks[(n)].flags & CHUNK_FL_LOAD)
+
+#define CHUNK_VALIDATE(n)      chunks[(n)].flags |= CHUNK_FL_VALID
+#define CHUNK_INVALIDATE(n)    chunks[(n)].flags = 0
+#define CHUNK_IS_VALID(n)      (chunks[(n)].flags & CHUNK_FL_VALID)
+
+/*
+ * static parameters to gzip helper functions
+ * we cannot use paramters because API was not
+ * designed that way
+ */
+static segment_t *chunks;      /* holds the list of segments */
+static segment_t *cur_chunk;
+static UINTN nchunks;
+static UINTN chunk;                 /* current segment */
+static UINTN input_fd;
+static VOID *kernel_entry, *kernel_base, *kernel_end;
+
+static uch *inbuf;             /* input buffer (compressed data) */
+static uch *window;                    /* output buffer (uncompressed data) */
+static unsigned long file_offset;      /* position in the file */
+
+static unsigned insize = 0;  /* valid bytes in inbuf */
+static unsigned inptr  = 0;   /* index of next byte to be processed in inbuf */
+static unsigned outcnt = 0;  /* bytes in output buffer */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ASCII text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
+#define RESERVED     0xC0 /* bit 6,7:   reserved */
+
+#define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())
+
+/* Diagnostic functions */
+#ifdef INFLATE_DEBUG
+#  define Assert(cond,msg) {if(!(cond)) error(msg);}
+int stderr;
+#  define Trace(x) Print(L"line %d:\n", __LINE__);
+#  define Tracev(x) {if (verbose) Print(L"line %d:\n", __LINE__) ;}
+#  define Tracevv(x) {if (verbose>1) Print(L"line %d:\n", __LINE__)  ;}
+#  define Tracec(c,x) {if (verbose && (c))  Print(L"line %d:\n", __LINE__) ;}
+#  define Tracecv(c,x) {if (verbose>1 && (c))  Print(L"line %d:\n", __LINE__) ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+static int  fill_inbuf(void);
+static void flush_window(void);
+static void error(char *m);
+static long bytes_out;
+
+static void error(char *m);
+
+static jmp_buf jbuf;
+static int error_return;
+static UINTN elf_is_big_endian;        /* true if ELF file is big endian */
+
+static void *
+gzip_malloc(int size)
+{
+       return (void *)alloc(size, 0);
+}
+
+static void
+gzip_free(void *where)
+{      
+       return free(where);
+}
+
+#include "inflate.c"
+
+/*
+ * Fill the input buffer and return the first byte in it. This is called
+ * only when the buffer is empty and at least one byte is really needed.
+ */
+int
+fill_inbuf(void)
+{
+       INTN expected, nread;
+       EFI_STATUS status;
+
+       expected = nread = INBUFSIZE;
+
+       status = fops_read(input_fd, inbuf, &nread);
+       if (EFI_ERROR(status)) {
+               error("elilo: Read failed");
+       }
+       DBG_PRT((L"%s : read %d bytes of %d bytes\n", LD_NAME, nread, expected));
+
+       insize = nread;
+       inptr = 1;
+
+       return inbuf[0];
+}
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+
+/*
+ * Run a set of bytes through the crc shift register.  If s is a NULL
+ * pointer, then initialize the crc shift register contents instead.
+ * Return the current crc in either case.
+ *
+ * Input:
+ *     S       pointer to bytes to pump through.
+ *     N       number of bytes in S[].
+ */
+unsigned long
+updcrc(unsigned char *s, unsigned n)
+{
+       register unsigned long c;
+       /* crc is defined in inflate.c */
+
+       if (!s) {
+               c = 0xffffffffL;
+       } else {
+               c = crc;
+               while (n--) {
+                       c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8);
+               }
+       }
+       crc = c;
+       return c ^ 0xffffffffUL;       /* (instead of ~c for 64-bit machines) */
+}
+
+
+/*
+ * Clear input and output buffers
+ */
+void
+clear_bufs(void)
+{
+       outcnt = 0;
+       inptr = 0;
+       chunk = 0;
+       cur_chunk = NULL;
+       file_offset = 0;
+}
+
+
+static inline UINT64 
+bswap64(UINT64 v)
+{
+        if(elf_is_big_endian) v = __ia64_swab64(v);
+        return v;
+}
+
+static inline UINT32
+bswap32(UINT32 v)
+{
+        if(elf_is_big_endian) v = __ia64_swab32(v);
+        return v;
+}
+
+static inline UINT16
+bswap16(UINT16 v)
+{
+        if(elf_is_big_endian) v = __ia64_swab16(v);
+        return v;
+}
+
+static INTN
+is_valid_header(Elf64_Ehdr *ehdr)
+{
+       UINT16 type, machine;
+
+        if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) {
+               type    = __ia64_swab16(ehdr->e_type);
+               machine = __ia64_swab16(ehdr->e_machine);
+       } else {
+               type    = ehdr->e_type;
+               machine = ehdr->e_machine;
+       }
+       VERB_PRT(3, Print(L"class=%d type=%d data=%d machine=%d\n", 
+               ehdr->e_ident[EI_CLASS],
+               type,
+               ehdr->e_ident[EI_DATA],
+               machine));
+
+       return    ehdr->e_ident[EI_MAG0]  == 0x7f 
+              && ehdr->e_ident[EI_MAG1]  == 'E'
+              && ehdr->e_ident[EI_MAG2]  == 'L'
+              && ehdr->e_ident[EI_MAG3]  == 'F'
+              && ehdr->e_ident[EI_CLASS] == ELFCLASS64 
+              && type                    == ET_EXEC    /* must be executable */
+              && machine                 == EM_IA_64 ? 0 : -1;
+}
+
+/*
+ * will invalidate loadble segments which overlap with others
+ */ 
+void
+check_overlap(int i)
+{
+       int j;
+       unsigned long iend = chunks[i].addr + chunks[i].size;
+
+       for(j=0; j < nchunks; j++) {
+               if (j ==i) continue;
+               if (chunks[i].addr >= chunks[j].addr && iend < (chunks[j].addr + chunks[j].size)) {
+                       DBG_PRT((L"%s : segment %d fully included in segment %d\n", LD_NAME, i, j));
+                       CHUNK_INVALIDATE(i); /* nullyify segment */
+                       break;
+               }
+       }
+}
+
+void
+analyze_chunks(void)
+{
+       INTN i;
+
+       for(i=0; i < nchunks; i++) {
+               if (CHUNK_IS_VALID(i) && !CHUNK_IS_LOAD(i)) check_overlap(i);
+       }
+}
+
+
+/*
+ * The decompression code calls this function after decompressing the
+ * first block of the object file.  The first block must contain all
+ * the relevant header information.
+ */
+int
+first_block (const char *buf, long blocksize)
+{
+       Elf64_Ehdr *elf;
+       Elf64_Phdr *phdrs;
+       UINTN total_size, pages;
+       UINTN low_addr, max_addr;
+       UINTN load_offset = 0;
+       UINTN offs = 0;
+       UINT16 phnum;
+       UINTN paddr, memsz;
+       INTN i;
+
+       elf  = (Elf64_Ehdr *)buf;
+       
+       if (is_valid_header(elf) == -1) return -1;
+
+       /* determine file endianess */
+        elf_is_big_endian = elf->e_ident[EI_DATA] == ELFDATA2MSB ? 1 : 0;
+
+       
+       offs  = bswap64(elf->e_phoff);
+       phnum = bswap16(elf->e_phnum);
+
+       VERB_PRT(3, { 
+                       Print(L"ELF file is %s\n", elf_is_big_endian ? L"big endian" : L"little endian");
+                       Print(L"Entry point 0x%lx\n", bswap64(elf->e_entry));
+                       Print(L"%d program headers\n", phnum);
+                       Print(L"%d segment headers\n", bswap16(elf->e_shnum));
+                  });
+
+
+       /* XXX: need to check on this */
+       if (offs + phnum * sizeof(*phdrs) > (unsigned) blocksize) {
+               ERR_PRT((L"%s : ELF program headers not in first block (%ld)\n", LD_NAME, offs));
+               return -1;
+       }
+
+       kernel_entry = (void *)bswap64(elf->e_entry);
+
+       if (((UINTN)kernel_entry >> 61) != 0) {
+               ERR_PRT((L"%s:  <<ERROR>> entry point is a virtual address 0x%lx : not supported anymore\n", LD_NAME, kernel_entry));
+       }
+
+       phdrs = (Elf64_Phdr *) (buf + offs);
+
+       low_addr = ~0;
+       max_addr = 0;
+
+       /*
+        * allocate chunk table
+        * Convention: a segment that does not need loading will
+        * have chunk[].addr = 0.
+        */
+       chunks = (void *)alloc(sizeof(struct segment)*phnum, 0);
+       if (chunks == NULL) {
+               ERR_PRT((L"%s : failed alloc chunks %r\n", LD_NAME));
+               return -1;
+       }
+       nchunks = phnum;
+       /*
+        * find lowest and higest virtual addresses
+        * don't assume FULLY sorted !
+        */
+       for (i = 0; i < phnum; ++i) {
+
+               /* 
+                * record chunk no matter what because no load may happen
+                * anywhere in archive, not just as the last segment
+                */
+               paddr = bswap64(phdrs[i].p_paddr);
+               memsz = bswap64(phdrs[i].p_memsz),
+
+               chunks[i].addr   = paddr;
+               chunks[i].offset = bswap64(phdrs[i].p_offset);
+               chunks[i].size   = bswap64(phdrs[i].p_filesz);
+               chunks[i].bss_sz = bswap64(phdrs[i].p_memsz) - bswap64(phdrs[i].p_filesz);
+
+               CHUNK_VALIDATE(i);
+
+               if (bswap32(phdrs[i].p_type) != PT_LOAD) {
+                       CHUNK_NO_LOAD(i); /* mark no load chunk */
+                       DBG_PRT((L"%s : skipping segment %ld\n", LD_NAME, i));
+                       continue;
+               }
+
+               CHUNK_CAN_LOAD(i); /* mark no load chunk */
+
+               VERB_PRT(3, 
+               Print(L"\n%s : segment %ld vaddr [0x%lx-0x%lx] offset %ld filesz %ld memsz=%ld bss_sz=%ld\n",
+                               LD_NAME,
+                               1+i, 
+                               chunks[i].addr, 
+                               chunks[i].addr+bswap64(phdrs[i].p_filesz), 
+                               chunks[i].offset, 
+                               chunks[i].size,
+                               memsz,
+                               chunks[i].bss_sz));
+               
+               if (paddr < low_addr) low_addr = paddr;
+
+               if (paddr + memsz > max_addr) max_addr = paddr + memsz;
+       }
+
+       if (low_addr & (EFI_PAGE_SIZE - 1)) {
+               ERR_PRT((L"%s : low_addr not page aligned 0x%lx\n", LD_NAME, low_addr));
+               goto error;
+       }
+
+       analyze_chunks();
+
+       DBG_PRT((L"%s : %d program headers entry=0x%lx\nlowest_addr=0x%lx highest_addr=0x%lx\n", 
+                       LD_NAME,
+                       phnum, kernel_entry, low_addr, max_addr));
+
+       total_size = (UINTN)max_addr - (UINTN)low_addr;
+       pages = EFI_SIZE_TO_PAGES(total_size);
+
+       /*
+        * Record end of kernel for initrd
+        */
+       kernel_base = (void *)low_addr;
+       kernel_end  = (void *)(low_addr + (pages << EFI_PAGE_SHIFT));
+
+       /* allocate memory for the kernel */
+       if (alloc_kmem((void *)low_addr, pages) == -1) {
+               VOID *new_addr;
+
+               ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", LD_NAME, pages, low_addr));
+
+               if (ia64_can_relocate() == 0) {
+                       ERR_PRT((L"relocation is disabled, cannot load kernel"));
+                       goto error;
+               }
+
+               /*
+                * could not allocate at requested spot, try to find a
+                * suitable location to relocate the kernel
+                *
+                * The maximum sized Itanium TLB translation entry is 256 MB.
+                * If we relocate the kernel by this amount we know for sure
+                * that alignment constraints will be satisified, regardless
+                * of the kernel used.
+                */
+               VERB_PRT(1, Print(L"Attempting to relocate kernel.\n"));
+
+               if (find_kernel_memory((VOID*) low_addr, (VOID*) max_addr, 256*MB, &new_addr) == -1) {
+                       ERR_PRT((L"%s : find_kernel_memory(0x%lx, 0x%lx, 0x%lx, 0x%lx) failed\n", LD_NAME, low_addr, max_addr, 256*MB, &load_offset));
+                       goto error;
+               }
+               /* unsigned arithmetic */
+                load_offset = (UINTN) (new_addr - ROUNDDOWN((UINTN) low_addr,256*MB));
+
+               ERR_PRT((L"low_addr=0x%lx new_addr=0x%lx offset=0x%lx", low_addr, new_addr, load_offset));
+
+               /*
+                * correct various addresses for non-zero load_offset
+                */
+               kernel_base = (void *) ((UINTN) kernel_base + load_offset);
+               kernel_end  = (void *) ((UINTN) kernel_end + load_offset);
+               kernel_entry = (void*) ((UINTN) kernel_entry + load_offset);
+
+               for (i = 0; i < phnum; ++i) {
+                       chunks[i].addr += load_offset;
+                       phdrs[i].p_paddr = (Elf64_Addr) ((UINT64) phdrs[i].p_paddr + load_offset);
+               }
+
+               /*
+                * try one last time to get memory for the kernel
+                */
+               if (alloc_kmem((void *)low_addr+load_offset, pages) == -1) {
+                       ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", LD_NAME, pages, low_addr+load_offset));
+                       ERR_PRT((L"Relocation by 0x%lx bytes failed.\n", load_offset));
+                       goto error;
+               }
+       }
+       return 0;
+error:
+       if (chunks) free(chunks);
+       return -1;
+}
+
+/*
+ * Determine which chunk in the Elf file will be coming out of the expand
+ * code next.
+ */
+static void
+nextchunk(void)
+{
+       int i;
+       segment_t *cp;
+
+       cp = NULL;
+       for(i=0; i < nchunks; i++) {
+
+               if (!CHUNK_IS_VALID(i) || !CHUNK_IS_LOAD(i)) continue;
+
+               if (file_offset > chunks[i].offset) continue;
+
+               if (cp == NULL || chunks[i].offset < cp->offset) cp = &chunks[i];
+       }
+       cur_chunk = cp;
+}
+
+
+/*
+ * Write the output window window[0..outcnt-1] holding uncompressed
+ * data and update crc.
+ */
+void
+flush_window(void)
+{
+       static const CHAR8 helicopter[4] = { '|' , '/' , '-' , '\\' };
+       static UINTN heli_count;
+       struct segment *cp;
+       char    *src, *dst;
+       long    cnt;
+
+       if (!outcnt) return;
+
+       DBG_PRT((L"%s : flush_window outnct=%d file_offset=%ld\n", LD_NAME, outcnt, file_offset));
+
+       Print(L"%c\b",helicopter[heli_count++%4]);
+
+       updcrc(window, outcnt);
+
+       /*
+        * first time, we extract the headers
+        */
+       if (!bytes_out) {
+               if (first_block(window, outcnt) < 0) error("invalid exec header"); 
+               nextchunk();
+       }
+
+       bytes_out += outcnt;
+       src = window;
+tail:
+       /* check if user wants to abort */
+       if (check_abort() == EFI_SUCCESS) goto load_abort;
+
+       cp = cur_chunk;
+       if (cp == NULL || file_offset + outcnt <= cp->offset) {
+               file_offset += outcnt;
+               return;
+       }
+
+       // Does this window begin before the current chunk?
+       if (file_offset < cp->offset) {
+               unsigned long skip = cp->offset - file_offset;
+
+               src         += skip;
+               file_offset += skip;
+               outcnt      -= skip;
+       }
+       dst = (char *)cp->addr + (file_offset - cp->offset);
+
+       cnt = cp->offset + cp->size - file_offset;
+
+       if (cnt > outcnt) cnt = outcnt;
+
+       Memcpy(dst, src, cnt);
+
+       file_offset += cnt;
+       outcnt      -= cnt;
+       src         += cnt;
+
+       /* See if we are at the end of this chunk */
+       if (file_offset == cp->offset + cp->size) {
+               if (cp->bss_sz) {
+                       dst = (char *)cp->addr + cp->size;
+                       Memset(dst, 0, cp->bss_sz);
+               }
+               nextchunk();
+               /* handle remaining bytes */
+               if (outcnt) goto tail; 
+       }
+       return;
+load_abort:
+       free_kmem();
+       error_return = ELILO_LOAD_ABORTED;
+       longjmp(jbuf, 1);
+}
+
+static void
+error(char *x)
+{
+       ERR_PRT((L"%s : %a", LD_NAME, x));
+       /* will eventually exit with error from gunzip() */
+       longjmp(jbuf,1);
+}
+
+INT32
+decompress_kernel(VOID)
+{
+       INT32 ret;
+
+       clear_bufs();
+       makecrc();
+       Print(L"Uncompressing Linux... ");
+       ret = gunzip();
+       if (ret == 0) Print(L"done\n");
+       return ret == 0 ? 0 : -1;
+}
+
+int
+gunzip_kernel(fops_fd_t fd, kdesc_t *kd)
+{
+       int ret = -1;
+
+       error_return = ELILO_LOAD_ERROR;
+       
+       window = (void *)alloc(WSIZE, 0);
+       if (window == NULL) {
+               ERR_PRT((L"%s : allocate output window failed\n", LD_NAME));
+               return -1;
+       }
+
+       inbuf = (void *)alloc(INBUFSIZE, 0);
+       if (inbuf == NULL) {
+               ERR_PRT((L"%s : allocate input window failedr\n", LD_NAME));
+               goto error;
+       }
+
+       input_fd   = fd;
+       insize     = 0;
+       bytes_out  = 0;
+
+       if (setjmp(jbuf) == 1) goto error;
+
+
+       ret = decompress_kernel();
+
+error:
+       if (window) free(window);
+       if (inbuf) free(inbuf);
+
+       if (ret == 0) {
+               kd->kentry = kernel_entry;
+               kd->kend   = kernel_end;
+               kd->kstart = kernel_base;
+               error_return = ELILO_LOAD_SUCCESS;
+       }
+       return error_return;
+}
diff --git a/ia64/gzip.h b/ia64/gzip.h
new file mode 100644 (file)
index 0000000..69749df
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __GZIP_H__
+#define __GZIP_H__
+
+
+int gzip_probe(unsigned char *, unsigned long);
+int gunzip_kernel(fops_fd_t, kdesc_t *);
+
+#define LD_NAME                L"gzip_ia64"
+
+#endif /* __GZIP_H__ */
diff --git a/ia64/gzip_loader.c b/ia64/gzip_loader.c
new file mode 100644 (file)
index 0000000..4e06db4
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "loader.h"
+#include "gzip.h"
+
+static INTN
+gzip_probe_format(CHAR16 *kname)
+{
+       UINT8 buf[4];
+       EFI_STATUS status;
+       INTN ret = -1;
+       UINTN size;
+       fops_fd_t fd;
+
+       status = fops_open(kname, &fd);
+       if (EFI_ERROR(status)) return -1;
+
+       size = sizeof(buf);
+       status = fops_read(fd, buf, &size);
+
+       if (EFI_ERROR(status) || size != sizeof(buf)) goto error;
+
+       ret = gzip_probe(buf, sizeof(buf));
+error:
+       fops_close(fd);
+       return ret;
+}
+
+
+static INTN
+gzip_load_kernel(CHAR16 *kname, kdesc_t *kd)
+{
+       EFI_STATUS status;
+       INT32 ret;
+       fops_fd_t fd;
+
+       status = fops_open(kname, &fd);
+       if (EFI_ERROR(status)) return ELILO_LOAD_ERROR;
+
+       ret = gunzip_kernel(fd, kd);
+       
+       fops_close(fd);
+
+       return  ret; /* could be success, error, or abort */
+}
+
+loader_ops_t gzip_loader={
+       NULL,
+       LD_NAME,
+       gzip_probe_format,
+       gzip_load_kernel
+};
diff --git a/ia64/inflate.c b/ia64/inflate.c
new file mode 100644 (file)
index 0000000..8273c98
--- /dev/null
@@ -0,0 +1,1204 @@
+#define DEBG(x)
+#define DEBG1(x)
+/* inflate.c -- Not copyrighted 1992 by Mark Adler
+   version c10p1, 10 January 1993 */
+
+/* 
+ * Adapted for booting Linux by Hannu Savolainen 1993
+ * based on gzip-1.0.3 
+ *
+ * Nicolas Pitre <nico@cam.org>, 1999/04/14 :
+ *   Little mods for all variable to reside either into rodata or bss segments
+ *   by marking constant variables with 'const' and initializing all the others
+ *   at run-time only.  This allows for the kernel uncompressor to run
+ *   directly from Flash or ROM memory on embeded systems.
+ */
+
+/*
+   Inflate deflated (PKZIP's method 8 compressed) data.  The compression
+   method searches for as much of the current string of bytes (up to a
+   length of 258) in the previous 32 K bytes.  If it doesn't find any
+   matches (of at least length 3), it codes the next byte.  Otherwise, it
+   codes the length of the matched string and its distance backwards from
+   the current position.  There is a single Huffman code that codes both
+   single bytes (called "literals") and match lengths.  A second Huffman
+   code codes the distance information, which follows a length code.  Each
+   length or distance code actually represents a base value and a number
+   of "extra" (sometimes zero) bits to get to add to the base value.  At
+   the end of each deflated block is a special end-of-block (EOB) literal/
+   length code.  The decoding process is basically: get a literal/length
+   code; if EOB then done; if a literal, emit the decoded byte; if a
+   length then get the distance and emit the referred-to bytes from the
+   sliding window of previously emitted data.
+
+   There are (currently) three kinds of inflate blocks: stored, fixed, and
+   dynamic.  The compressor deals with some chunk of data at a time, and
+   decides which method to use on a chunk-by-chunk basis.  A chunk might
+   typically be 32 K or 64 K.  If the chunk is incompressible, then the
+   "stored" method is used.  In this case, the bytes are simply stored as
+   is, eight bits per byte, with none of the above coding.  The bytes are
+   preceded by a count, since there is no longer an EOB code.
+
+   If the data is compressible, then either the fixed or dynamic methods
+   are used.  In the dynamic method, the compressed data is preceded by
+   an encoding of the literal/length and distance Huffman codes that are
+   to be used to decode this block.  The representation is itself Huffman
+   coded, and so is preceded by a description of that code.  These code
+   descriptions take up a little space, and so for small blocks, there is
+   a predefined set of codes, called the fixed codes.  The fixed method is
+   used if the block codes up smaller that way (usually for quite small
+   chunks), otherwise the dynamic method is used.  In the latter case, the
+   codes are customized to the probabilities in the current block, and so
+   can code it much better than the pre-determined fixed codes.
+   The Huffman codes themselves are decoded using a multi-level table
+   lookup, in order to maximize the speed of decoding plus the speed of
+   building the decoding tables.  See the comments below that precede the
+   lbits and dbits tuning parameters.
+ */
+
+
+/*
+   Notes beyond the 1.93a appnote.txt:
+
+   1. Distance pointers never point before the beginning of the output
+      stream.
+   2. Distance pointers can point back across blocks, up to 32k away.
+   3. There is an implied maximum of 7 bits for the bit length table and
+      15 bits for the actual data.
+   4. If only one code exists, then it is encoded using one bit.  (Zero
+      would be more efficient, but perhaps a little confusing.)  If two
+      codes exist, they are coded using one bit each (0 and 1).
+   5. There is no way of sending zero distance codes--a dummy must be
+      sent if there are none.  (History: a pre 2.0 version of PKZIP would
+      store blocks with no distance codes, but this was discovered to be
+      too harsh a criterion.)  Valid only for 1.93a.  2.04c does allow
+      zero distance codes, which is sent as one code of zero bits in
+      length.
+   6. There are up to 286 literal/length codes.  Code 256 represents the
+      end-of-block.  Note however that the static length tree defines
+      288 codes just to fill out the Huffman codes.  Codes 286 and 287
+      cannot be used though, since there is no length base or extra bits
+      defined for them.  Similarly, there are up to 30 distance codes.
+      However, static trees define 32 codes (all 5 bits) to fill out the
+      Huffman codes, but the last two had better not show up in the data.
+   7. Unzip can check dynamic Huffman blocks for complete code sets.
+      The exception is that a single code would not be complete (see #4).
+   8. The five bits following the block type is really the number of
+      literal codes sent minus 257.
+   9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+      (1+6+6).  Therefore, to output three times the length, you output
+      three codes (1+1+1), whereas to output four times the same length,
+      you only need two codes (1+3).  Hmm.
+  10. In the tree reconstruction algorithm, Code = Code + Increment
+      only if BitLength(i) is not zero.  (Pretty obvious.)
+  11. Correction: 4 Bits: # of Bit Length codes - 4     (4 - 19)
+  12. Note: length code 284 can represent 227-258, but length code 285
+      really is 258.  The last length deserves its own, short code
+      since it gets used a lot in very redundant files.  The length
+      258 is special since 258 - 3 (the min match length) is 255.
+  13. The literal/length and distance code bit lengths are read as a
+      single stream of lengths.  It is possible (and advantageous) for
+      a repeat code (16, 17, or 18) to go across the boundary between
+      the two sets of lengths.
+ */
+
+#ifdef RCSID
+static char rcsid[] = "#Id: inflate.c,v 0.14 1993/06/10 13:27:04 jloup Exp #";
+#endif
+
+#ifndef FUNC_STATIC
+
+#if defined(STDC_HEADERS) || defined(HAVE_STDLIB_H)
+#  include <sys/types.h>
+#  include <stdlib.h>
+#endif
+
+#include "gzip.h"
+#define FUNC_STATIC
+#endif /* !FUNC_STATIC */
+       
+#define slide window
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+   that have 16-bit pointers (e.g. PC's in the small or medium model).
+   Valid extra bits are 0..13.  e == 15 is EOB (end of block), e == 16
+   means that v is a literal, 16 < e < 32 means that v is a pointer to
+   the next table, which codes e - 16 bits, and lastly e == 99 indicates
+   an unused code.  If a code with e == 99 is looked up, this implies an
+   error in the data. */
+struct huft {
+  uch e;                /* number of extra bits or operation */
+  uch b;                /* number of bits in this code or subcode */
+  union {
+    ush n;              /* literal, length base, or distance base */
+    struct huft *t;     /* pointer to next level of table */
+  } v;
+};
+
+
+/* Function prototypes */
+FUNC_STATIC int huft_build OF((unsigned *, unsigned, unsigned, 
+               const ush *, const ush *, struct huft **, int *));
+FUNC_STATIC int huft_free OF((struct huft *));
+FUNC_STATIC int inflate_codes OF((struct huft *, struct huft *, int, int));
+FUNC_STATIC int inflate_stored OF((void));
+FUNC_STATIC int inflate_fixed OF((void));
+FUNC_STATIC int inflate_dynamic OF((void));
+FUNC_STATIC int inflate_block OF((int *));
+FUNC_STATIC int inflate OF((void));
+
+
+/* The inflate algorithm uses a sliding 32 K byte window on the uncompressed
+   stream to find repeated byte strings.  This is implemented here as a
+   circular buffer.  The index is updated simply by incrementing and then
+   ANDing with 0x7fff (32K-1). */
+/* It is left to other modules to supply the 32 K area.  It is assumed
+   to be usable as if it were declared "uch slide[32768];" or as just
+   "uch *slide;" and then malloc'ed in the latter case.  The definition
+   must be in unzip.h, included above. */
+/* unsigned wp;             current position in slide */
+#define wp outcnt
+#define flush_output(w) (wp=(w),flush_window())
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+static const unsigned border[] = {    /* Order of the bit length code lengths */
+        16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+static const ush cplens[] = {         /* Copy lengths for literal codes 257..285 */
+        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+        /* note: see note #13 above about the 258 in this list. */
+static const ush cplext[] = {         /* Extra bits for literal codes 257..285 */
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */
+static const ush cpdist[] = {         /* Copy offsets for distance codes 0..29 */
+        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+        8193, 12289, 16385, 24577};
+static const ush cpdext[] = {         /* Extra bits for distance codes */
+        0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+        7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+        12, 12, 13, 13};
+
+
+
+/* Macros for inflate() bit peeking and grabbing.
+   The usage is:
+   
+        NEEDBITS(j)
+        x = b & mask_bits[j];
+        DUMPBITS(j)
+
+   where NEEDBITS makes sure that b has at least j bits in it, and
+   DUMPBITS removes the bits from b.  The macros use the variable k
+   for the number of bits in b.  Normally, b and k are register
+   variables for speed, and are initialized at the beginning of a
+   routine that uses these macros from a global bit buffer and count.
+
+   If we assume that EOB will be the longest code, then we will never
+   ask for bits with NEEDBITS that are beyond the end of the stream.
+   So, NEEDBITS should not read any more bytes than are needed to
+   meet the request.  Then no bytes need to be "returned" to the buffer
+   at the end of the last block.
+
+   However, this assumption is not true for fixed blocks--the EOB code
+   is 7 bits, but the other literal/length codes can be 8 or 9 bits.
+   (The EOB code is shorter than other codes because fixed blocks are
+   generally short.  So, while a block always has an EOB, many other
+   literal/length codes have a significantly lower probability of
+   showing up at all.)  However, by making the first table have a
+   lookup of seven bits, the EOB code will be found in that first
+   lookup, and so will not require that too many bits be pulled from
+   the stream.
+ */
+
+FUNC_STATIC ulg bb;                         /* bit buffer */
+FUNC_STATIC unsigned bk;                    /* bits in bit buffer */
+
+FUNC_STATIC const ush mask_bits[] = {
+    0x0000,
+    0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+    0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+#define NEXTBYTE()  (uch)get_byte()
+#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<<k;k+=8;}}
+#define DUMPBITS(n) {b>>=(n);k-=(n);}
+
+
+/*
+   Huffman code decoding is performed using a multi-level table lookup.
+   The fastest way to decode is to simply build a lookup table whose
+   size is determined by the longest code.  However, the time it takes
+   to build this table can also be a factor if the data being decoded
+   is not very long.  The most common codes are necessarily the
+   shortest codes, so those codes dominate the decoding time, and hence
+   the speed.  The idea is you can have a shorter table that decodes the
+   shorter, more probable codes, and then point to subsidiary tables for
+   the longer codes.  The time it costs to decode the longer codes is
+   then traded against the time it takes to make longer tables.
+
+   This results of this trade are in the variables lbits and dbits
+   below.  lbits is the number of bits the first level table for literal/
+   length codes can decode in one step, and dbits is the same thing for
+   the distance codes.  Subsequent tables are also less than or equal to
+   those sizes.  These values may be adjusted either when all of the
+   codes are shorter than that, in which case the longest code length in
+   bits is used, or when the shortest code is *longer* than the requested
+   table size, in which case the length of the shortest code in bits is
+   used.
+
+   There are two different values for the two tables, since they code a
+   different number of possibilities each.  The literal/length table
+   codes 286 possible values, or in a flat code, a little over eight
+   bits.  The distance table codes 30 possible values, or a little less
+   than five bits, flat.  The optimum values for speed end up being
+   about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+   The optimum values may differ though from machine to machine, and
+   possibly even between compilers.  Your mileage may vary.
+ */
+
+
+FUNC_STATIC const int lbits = 9;          /* bits in base literal/length lookup table */
+FUNC_STATIC const int dbits = 6;          /* bits in base distance lookup table */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+#define BMAX 16         /* maximum bit length of any code (16 for explode) */
+#define N_MAX 288       /* maximum number of codes in any set */
+
+
+FUNC_STATIC unsigned hufts;         /* track memory usage */
+
+
+FUNC_STATIC int huft_build(b, n, s, d, e, t, m)
+unsigned *b;            /* code lengths in bits (all assumed <= BMAX) */
+unsigned n;             /* number of codes (assumed <= N_MAX) */
+unsigned s;             /* number of simple-valued codes (0..s-1) */
+const ush *d;                 /* list of base values for non-simple codes */
+const ush *e;                 /* list of extra bits for non-simple codes */
+struct huft **t;        /* result: starting table */
+int *m;                 /* maximum lookup bits, returns actual */
+/* Given a list of code lengths and a maximum table size, make a set of
+   tables to decode that set of codes.  Return zero on success, one if
+   the given code set is incomplete (the tables are still built in this
+   case), two if the input is invalid (all zero length codes or an
+   oversubscribed set of lengths), and three if not enough memory. */
+{
+  unsigned a;                   /* counter for codes of length k */
+  unsigned c[BMAX+1];           /* bit length count table */
+  unsigned f;                   /* i repeats in table every f entries */
+  int g;                        /* maximum code length */
+  int h;                        /* table level */
+  register unsigned i;          /* counter, current code */
+  register unsigned j;          /* counter */
+  register int k;               /* number of bits in current code */
+  int l;                        /* bits per table (returned in m) */
+  register unsigned *p;         /* pointer into c[], b[], or v[] */
+  register struct huft *q;      /* points to current table */
+  struct huft r;                /* table entry for structure assignment */
+  struct huft *u[BMAX];         /* table stack */
+  unsigned v[N_MAX];            /* values in order of bit length */
+  register int w;               /* bits before this table == (l * h) */
+  unsigned x[BMAX+1];           /* bit offsets, then code stack */
+  unsigned *xp;                 /* pointer into x */
+  int y;                        /* number of dummy codes added */
+  unsigned z;                   /* number of entries in current table */
+
+DEBG("huft1 ");
+
+  /* Generate counts for each bit length */
+  memzero(c, sizeof(c));
+
+  p = b;  i = n;
+  do {
+    Tracecv(*p, (stderr, (n-i >= ' ' && n-i <= '~' ? "%c %d\n" : "0x%x %d\n"), 
+           n-i, *p));
+    c[*p]++;                    /* assume all entries <= BMAX */
+    p++;                      /* Can't combine with above line (Solaris bug) */
+  } while (--i);
+  if (c[0] == n)                /* null input--all zero length codes */
+  {
+    *t = (struct huft *)NULL;
+    *m = 0;
+    return 0;
+  }
+
+DEBG("huft2 ");
+
+  /* Find minimum and maximum length, bound *m by those */
+  l = *m;
+  for (j = 1; j <= BMAX; j++)
+    if (c[j])
+      break;
+  k = j;                        /* minimum code length */
+  if ((unsigned)l < j)
+    l = j;
+  for (i = BMAX; i; i--)
+    if (c[i])
+      break;
+  g = i;                        /* maximum code length */
+  if ((unsigned)l > i)
+    l = i;
+  *m = l;
+
+DEBG("huft3 ");
+
+  /* Adjust last length count to fill out codes, if needed */
+  for (y = 1 << j; j < i; j++, y <<= 1)
+    if ((y -= c[j]) < 0)
+      return 2;                 /* bad input: more codes than bits */
+  if ((y -= c[i]) < 0)
+    return 2;
+  c[i] += y;
+
+DEBG("huft4 ");
+
+  /* Generate starting offsets into the value table for each length */
+  x[1] = j = 0;
+  p = c + 1;  xp = x + 2;
+  while (--i) {                 /* note that i == g from above */
+    *xp++ = (j += *p++);
+  }
+
+DEBG("huft5 ");
+
+  /* Make a table of values in order of bit lengths */
+  p = b;  i = 0;
+  do {
+    if ((j = *p++) != 0)
+      v[x[j]++] = i;
+  } while (++i < n);
+
+DEBG("h6 ");
+
+  /* Generate the Huffman codes and for each, make the table entries */
+  x[0] = i = 0;                 /* first Huffman code is zero */
+  p = v;                        /* grab values in bit order */
+  h = -1;                       /* no tables yet--level -1 */
+  w = -l;                       /* bits decoded == (l * h) */
+  u[0] = (struct huft *)NULL;   /* just to keep compilers happy */
+  q = (struct huft *)NULL;      /* ditto */
+  z = 0;                        /* ditto */
+DEBG("h6a ");
+
+  /* go through the bit lengths (k already is bits in shortest code) */
+  for (; k <= g; k++)
+  {
+DEBG("h6b ");
+    a = c[k];
+    while (a--)
+    {
+DEBG("h6b1 ");
+      /* here i is the Huffman code of length k bits for value *p */
+      /* make tables up to required level */
+      while (k > w + l)
+      {
+DEBG1("1 ");
+        h++;
+        w += l;                 /* previous table always l bits */
+
+        /* compute minimum size table less than or equal to l bits */
+        z = (z = g - w) > (unsigned)l ? l : z;  /* upper limit on table size */
+        if ((f = 1 << (j = k - w)) > a + 1)     /* try a k-w bit table */
+        {                       /* too few codes for k-w bit table */
+DEBG1("2 ");
+          f -= a + 1;           /* deduct codes from patterns left */
+          xp = c + k;
+          while (++j < z)       /* try smaller tables up to z bits */
+          {
+            if ((f <<= 1) <= *++xp)
+              break;            /* enough codes to use up j bits */
+            f -= *xp;           /* else deduct codes from patterns */
+          }
+        }
+DEBG1("3 ");
+        z = 1 << j;             /* table entries for j-bit table */
+
+        /* allocate and link in new table */
+        if ((q = (struct huft *)gzip_malloc((z + 1)*sizeof(struct huft))) ==
+            (struct huft *)NULL)
+        {
+          if (h)
+            huft_free(u[0]);
+          return 3;             /* not enough memory */
+        }
+DEBG1("4 ");
+        hufts += z + 1;         /* track memory usage */
+        *t = q + 1;             /* link to list for huft_free() */
+        *(t = &(q->v.t)) = (struct huft *)NULL;
+        u[h] = ++q;             /* table starts after link */
+
+DEBG1("5 ");
+        /* connect to last table, if there is one */
+        if (h)
+        {
+          x[h] = i;             /* save pattern for backing up */
+          r.b = (uch)l;         /* bits to dump before this table */
+          r.e = (uch)(16 + j);  /* bits in this table */
+          r.v.t = q;            /* pointer to this table */
+          j = i >> (w - l);     /* (get around Turbo C bug) */
+          u[h-1][j] = r;        /* connect to last table */
+        }
+DEBG1("6 ");
+      }
+DEBG("h6c ");
+
+      /* set up table entry in r */
+      r.b = (uch)(k - w);
+      if (p >= v + n)
+        r.e = 99;               /* out of values--invalid code */
+      else if (*p < s)
+      {
+        r.e = (uch)(*p < 256 ? 16 : 15);    /* 256 is end-of-block code */
+        r.v.n = (ush)(*p);             /* simple code is just the value */
+       p++;                           /* one compiler does not like *p++ */
+      }
+      else
+      {
+        r.e = (uch)e[*p - s];   /* non-simple--look up in lists */
+        r.v.n = d[*p++ - s];
+      }
+DEBG("h6d ");
+
+      /* fill code-like entries with r */
+      f = 1 << (k - w);
+      for (j = i >> w; j < z; j += f)
+        q[j] = r;
+
+      /* backwards increment the k-bit code i */
+      for (j = 1 << (k - 1); i & j; j >>= 1)
+        i ^= j;
+      i ^= j;
+
+      /* backup over finished tables */
+      while ((i & ((1 << w) - 1)) != x[h])
+      {
+        h--;                    /* don't need to update q */
+        w -= l;
+      }
+DEBG("h6e ");
+    }
+DEBG("h6f ");
+  }
+
+DEBG("huft7 ");
+
+  /* Return true (1) if we were given an incomplete table */
+  return y != 0 && g != 1;
+}
+
+
+
+FUNC_STATIC int huft_free(t)
+struct huft *t;         /* table to free */
+/* Free the malloc'ed tables built by huft_build(), which makes a linked
+   list of the tables it made, with the links in a dummy first entry of
+   each table. */
+{
+  register struct huft *p, *q;
+
+
+  /* Go through linked list, freeing from the malloced (t[-1]) address. */
+  p = t;
+  while (p != (struct huft *)NULL)
+  {
+    q = (--p)->v.t;
+    gzip_free((char*)p);
+    p = q;
+  } 
+  return 0;
+}
+
+
+FUNC_STATIC int inflate_codes(tl, td, bl, bd)
+struct huft *tl, *td;   /* literal/length and distance decoder tables */
+int bl, bd;             /* number of bits decoded by tl[] and td[] */
+/* inflate (decompress) the codes in a deflated (compressed) block.
+   Return an error code or zero if it all goes ok. */
+{
+  register unsigned e;  /* table entry flag/number of extra bits */
+  unsigned n, d;        /* length and index for copy */
+  unsigned w;           /* current window position */
+  struct huft *t;       /* pointer to table entry */
+  unsigned ml, md;      /* masks for bl and bd bits */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+
+  /* make local copies of globals */
+  b = bb;                       /* initialize bit buffer */
+  k = bk;
+  w = wp;                       /* initialize window position */
+
+  /* inflate the coded data */
+  ml = mask_bits[bl];           /* precompute masks for speed */
+  md = mask_bits[bd];
+  for (;;)                      /* do until end of block */
+  {
+    NEEDBITS((unsigned)bl)
+    if ((e = (t = tl + ((unsigned)b & ml))->e) > 16)
+      do {
+        if (e == 99)
+          return 1;
+        DUMPBITS(t->b)
+        e -= 16;
+        NEEDBITS(e)
+      } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
+    DUMPBITS(t->b)
+    if (e == 16)                /* then it's a literal */
+    {
+      slide[w++] = (uch)t->v.n;
+      Tracevv((stderr, "%c", slide[w-1]));
+      if (w == WSIZE)
+      {
+        flush_output(w);
+        w = 0;
+      }
+    }
+    else                        /* it's an EOB or a length */
+    {
+      /* exit if end of block */
+      if (e == 15)
+        break;
+
+      /* get length of block to copy */
+      NEEDBITS(e)
+      n = t->v.n + ((unsigned)b & mask_bits[e]);
+      DUMPBITS(e);
+
+      /* decode distance of block to copy */
+      NEEDBITS((unsigned)bd)
+      if ((e = (t = td + ((unsigned)b & md))->e) > 16)
+        do {
+          if (e == 99)
+            return 1;
+          DUMPBITS(t->b)
+          e -= 16;
+          NEEDBITS(e)
+        } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
+      DUMPBITS(t->b)
+      NEEDBITS(e)
+      d = w - t->v.n - ((unsigned)b & mask_bits[e]);
+      DUMPBITS(e)
+      Tracevv((stderr,"\\[%d,%d]", w-d, n));
+
+      /* do the copy */
+      do {
+        n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e);
+#if !defined(NOMEMCPY) && !defined(INFLATE_DEBUG)
+        if (w - d >= e)         /* (this test assumes unsigned comparison) */
+        {
+          memcpy(slide + w, slide + d, e);
+          w += e;
+          d += e;
+        }
+        else                      /* do it slow to avoid memcpy() overlap */
+#endif /* !NOMEMCPY */
+          do {
+            slide[w++] = slide[d++];
+           Tracevv((stderr, "%c", slide[w-1]));
+          } while (--e);
+        if (w == WSIZE)
+        {
+          flush_output(w);
+          w = 0;
+        }
+      } while (n);
+    }
+  }
+
+
+  /* restore the globals from the locals */
+  wp = w;                       /* restore global window pointer */
+  bb = b;                       /* restore global bit buffer */
+  bk = k;
+
+  /* done */
+  return 0;
+}
+
+
+
+FUNC_STATIC int inflate_stored()
+/* "decompress" an inflated type 0 (stored) block. */
+{
+  unsigned n;           /* number of bytes in block */
+  unsigned w;           /* current window position */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+DEBG("<stor");
+
+  /* make local copies of globals */
+  b = bb;                       /* initialize bit buffer */
+  k = bk;
+  w = wp;                       /* initialize window position */
+
+
+  /* go to byte boundary */
+  n = k & 7;
+  DUMPBITS(n);
+
+
+  /* get the length and its complement */
+  NEEDBITS(16)
+  n = ((unsigned)b & 0xffff);
+  DUMPBITS(16)
+  NEEDBITS(16)
+  if (n != (unsigned)((~b) & 0xffff))
+    return 1;                   /* error in compressed data */
+  DUMPBITS(16)
+
+
+  /* read and output the compressed data */
+  while (n--)
+  {
+    NEEDBITS(8)
+    slide[w++] = (uch)b;
+    if (w == WSIZE)
+    {
+      flush_output(w);
+      w = 0;
+    }
+    DUMPBITS(8)
+  }
+
+
+  /* restore the globals from the locals */
+  wp = w;                       /* restore global window pointer */
+  bb = b;                       /* restore global bit buffer */
+  bk = k;
+
+  DEBG(">");
+  return 0;
+}
+
+
+
+FUNC_STATIC int inflate_fixed()
+/* decompress an inflated type 1 (fixed Huffman codes) block.  We should
+   either replace this with a custom decoder, or at least precompute the
+   Huffman tables. */
+{
+  int i;                /* temporary variable */
+  struct huft *tl;      /* literal/length code table */
+  struct huft *td;      /* distance code table */
+  int bl;               /* lookup bits for tl */
+  int bd;               /* lookup bits for td */
+  unsigned l[288];      /* length list for huft_build */
+
+DEBG("<fix");
+
+  /* set up literal table */
+  for (i = 0; i < 144; i++)
+    l[i] = 8;
+  for (; i < 256; i++)
+    l[i] = 9;
+  for (; i < 280; i++)
+    l[i] = 7;
+  for (; i < 288; i++)          /* make a complete, but wrong code set */
+    l[i] = 8;
+  bl = 7;
+  if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0)
+    return i;
+
+
+  /* set up distance table */
+  for (i = 0; i < 30; i++)      /* make an incomplete code set */
+    l[i] = 5;
+  bd = 5;
+  if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1)
+  {
+    huft_free(tl);
+
+    DEBG(">");
+    return i;
+  }
+
+
+  /* decompress until an end-of-block code */
+  if (inflate_codes(tl, td, bl, bd))
+    return 1;
+
+
+  /* free the decoding tables, return */
+  huft_free(tl);
+  huft_free(td);
+  return 0;
+}
+
+
+
+FUNC_STATIC int inflate_dynamic()
+/* decompress an inflated type 2 (dynamic Huffman codes) block. */
+{
+  int i;                /* temporary variables */
+  unsigned j;
+  unsigned l;           /* last length */
+  unsigned m;           /* mask for bit lengths table */
+  unsigned n;           /* number of lengths to get */
+  struct huft *tl;      /* literal/length code table */
+  struct huft *td;      /* distance code table */
+  int bl;               /* lookup bits for tl */
+  int bd;               /* lookup bits for td */
+  unsigned nb;          /* number of bit length codes */
+  unsigned nl;          /* number of literal/length codes */
+  unsigned nd;          /* number of distance codes */
+#ifdef PKZIP_BUG_WORKAROUND
+  unsigned ll[288+32];  /* literal/length and distance code lengths */
+#else
+  unsigned ll[286+30];  /* literal/length and distance code lengths */
+#endif
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+DEBG("<dyn");
+
+  /* make local bit buffer */
+  b = bb;
+  k = bk;
+
+
+  /* read in table lengths */
+  NEEDBITS(5)
+  nl = 257 + ((unsigned)b & 0x1f);      /* number of literal/length codes */
+  DUMPBITS(5)
+  NEEDBITS(5)
+  nd = 1 + ((unsigned)b & 0x1f);        /* number of distance codes */
+  DUMPBITS(5)
+  NEEDBITS(4)
+  nb = 4 + ((unsigned)b & 0xf);         /* number of bit length codes */
+  DUMPBITS(4)
+#ifdef PKZIP_BUG_WORKAROUND
+  if (nl > 288 || nd > 32)
+#else
+  if (nl > 286 || nd > 30)
+#endif
+    return 1;                   /* bad lengths */
+
+DEBG("dyn1 ");
+
+  /* read in bit-length-code lengths */
+  for (j = 0; j < nb; j++)
+  {
+    NEEDBITS(3)
+    ll[border[j]] = (unsigned)b & 7;
+    DUMPBITS(3)
+  }
+  for (; j < 19; j++)
+    ll[border[j]] = 0;
+
+DEBG("dyn2 ");
+
+  /* build decoding table for trees--single level, 7 bit lookup */
+  bl = 7;
+  if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0)
+  {
+    if (i == 1)
+      huft_free(tl);
+    return i;                   /* incomplete code set */
+  }
+
+DEBG("dyn3 ");
+
+  /* read in literal and distance code lengths */
+  n = nl + nd;
+  m = mask_bits[bl];
+  i = l = 0;
+  while ((unsigned)i < n)
+  {
+    NEEDBITS((unsigned)bl)
+    j = (td = tl + ((unsigned)b & m))->b;
+    DUMPBITS(j)
+    j = td->v.n;
+    if (j < 16)                 /* length of code in bits (0..15) */
+      ll[i++] = l = j;          /* save last length in l */
+    else if (j == 16)           /* repeat last length 3 to 6 times */
+    {
+      NEEDBITS(2)
+      j = 3 + ((unsigned)b & 3);
+      DUMPBITS(2)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = l;
+    }
+    else if (j == 17)           /* 3 to 10 zero length codes */
+    {
+      NEEDBITS(3)
+      j = 3 + ((unsigned)b & 7);
+      DUMPBITS(3)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = 0;
+      l = 0;
+    }
+    else                        /* j == 18: 11 to 138 zero length codes */
+    {
+      NEEDBITS(7)
+      j = 11 + ((unsigned)b & 0x7f);
+      DUMPBITS(7)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = 0;
+      l = 0;
+    }
+  }
+
+DEBG("dyn4 ");
+
+  /* free decoding table for trees */
+  huft_free(tl);
+
+DEBG("dyn5 ");
+
+  /* restore the global bit buffer */
+  bb = b;
+  bk = k;
+
+DEBG("dyn5a ");
+
+  /* build the decoding tables for literal/length and distance codes */
+  bl = lbits;
+  if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0)
+  {
+DEBG("dyn5b ");
+    if (i == 1) {
+      error(" incomplete literal tree\n");
+      huft_free(tl);
+    }
+    return i;                   /* incomplete code set */
+  }
+DEBG("dyn5c ");
+  bd = dbits;
+  if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0)
+  {
+DEBG("dyn5d ");
+    if (i == 1) {
+      error(" incomplete distance tree\n");
+#ifdef PKZIP_BUG_WORKAROUND
+      i = 0;
+    }
+#else
+      huft_free(td);
+    }
+    huft_free(tl);
+    return i;                   /* incomplete code set */
+#endif
+  }
+
+DEBG("dyn6 ");
+
+  /* decompress until an end-of-block code */
+  if (inflate_codes(tl, td, bl, bd))
+    return 1;
+
+DEBG("dyn7 ");
+
+  /* free the decoding tables, return */
+  huft_free(tl);
+  huft_free(td);
+
+  DEBG(">");
+  return 0;
+}
+
+
+
+FUNC_STATIC int inflate_block(e)
+int *e;                 /* last block flag */
+/* decompress an inflated block */
+{
+  unsigned t;           /* block type */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+  DEBG("<blk");
+
+  /* make local bit buffer */
+  b = bb;
+  k = bk;
+
+
+  /* read in last block bit */
+  NEEDBITS(1)
+  *e = (int)b & 1;
+  DUMPBITS(1)
+
+
+  /* read in block type */
+  NEEDBITS(2)
+  t = (unsigned)b & 3;
+  DUMPBITS(2)
+
+
+  /* restore the global bit buffer */
+  bb = b;
+  bk = k;
+
+  /* inflate that block type */
+  if (t == 2)
+    return inflate_dynamic();
+  if (t == 0)
+    return inflate_stored();
+  if (t == 1)
+    return inflate_fixed();
+
+  DEBG(">");
+
+  /* bad block type */
+  return 2;
+}
+
+
+
+FUNC_STATIC int inflate()
+/* decompress an inflated entry */
+{
+  int e;                /* last block flag */
+  int r;                /* result code */
+  unsigned h;           /* maximum struct huft's malloc'ed */
+
+  /* initialize window, bit buffer */
+  wp = 0;
+  bk = 0;
+  bb = 0;
+
+
+  /* decompress until the last block */
+  h = 0;
+  do {
+    hufts = 0;
+    if ((r = inflate_block(&e)) != 0) {
+      return r;
+    }
+    if (hufts > h)
+      h = hufts;
+  } while (!e);
+
+  /* Undo too much lookahead. The next read will be byte aligned so we
+   * can discard unused bits in the last meaningful byte.
+   */
+  while (bk >= 8) {
+    bk -= 8;
+    inptr--;
+  }
+
+  /* flush out slide */
+  flush_output(wp);
+
+
+  /* return success */
+#ifdef INFLATE_DEBUG
+#ifdef EFI_COMPILE
+  Print(L"<%d> ", h);
+#else
+  printf("<%d> ", h);
+#endif
+#endif /* INFLATE_DEBUG */
+  return 0;
+}
+
+/**********************************************************************
+ *
+ * The following are support routines for inflate.c
+ *
+ **********************************************************************/
+
+static ulg crc_32_tab[256];
+static ulg crc;                /* initialized in makecrc() so it'll reside in bss */
+#define CRC_VALUE (crc ^ 0xffffffffUL)
+
+/*
+ * Code to compute the CRC-32 table. Borrowed from 
+ * gzip-1.0.3/makecrc.c.
+ */
+
+static void
+makecrc(void)
+{
+/* Not copyrighted 1990 Mark Adler     */
+
+  unsigned long c;      /* crc shift register */
+  unsigned long e;      /* polynomial exclusive-or pattern */
+  int i;                /* counter for all possible eight bit values */
+  int k;                /* byte being shifted into crc apparatus */
+
+  /* terms of polynomial defining this crc (except x^32): */
+  static const int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+  /* Make exclusive-or pattern from polynomial */
+  e = 0;
+  for (i = 0; i < sizeof(p)/sizeof(int); i++)
+    e |= 1L << (31 - p[i]);
+
+  crc_32_tab[0] = 0;
+
+  for (i = 1; i < 256; i++)
+  {
+    c = 0;
+    for (k = i | 256; k != 1; k >>= 1)
+    {
+      c = c & 1 ? (c >> 1) ^ e : c >> 1;
+      if (k & 1)
+        c ^= e;
+    }
+    crc_32_tab[i] = c;
+  }
+
+  /* this is initialized here so this code could reside in ROM */
+  crc = (ulg)0xffffffffUL; /* shift register contents */
+}
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ASCII text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
+#define RESERVED     0xC0 /* bit 6,7:   reserved */
+
+/*
+ * check for valid gzip signature
+ * return:
+ *     0 : valid gzip archive
+ *     -1: invalid gzip archive
+ */
+int
+gzip_probe(uch *buf, unsigned long size)
+{
+    if (size < 4) return -1;
+
+    if (buf[0] != 037 ||
+       ((buf[1] != 0213) && (buf[1] != 0236))) return -1;
+
+    /* We only support method #8, DEFLATED */
+    if (buf[2] != 8) return -1;
+    
+    if ((buf[3] & ENCRYPTED) != 0) return -1;
+
+    if ((buf[3] & CONTINUATION) != 0) return -1;
+
+    if ((buf[3] & RESERVED) != 0) return -1;
+
+    return 0;
+}
+
+
+/*
+ * Do the uncompression!
+ */
+static int gunzip(void)
+{
+    uch flags;
+    unsigned char magic[2]; /* magic header */
+    char method;
+    ulg orig_crc = 0;       /* original crc */
+    ulg orig_len = 0;       /* original uncompressed length */
+    int res;
+
+    magic[0] = (unsigned char)get_byte();
+    magic[1] = (unsigned char)get_byte();
+    method = (unsigned char)get_byte();
+
+    if (magic[0] != 037 ||
+       ((magic[1] != 0213) && (magic[1] != 0236))) {
+           error("bad gzip magic numbers");
+           return -1;
+    }
+
+    /* We only support method #8, DEFLATED */
+    if (method != 8)  {
+           error("internal error, invalid method");
+           return -1;
+    }
+
+    flags  = (uch)get_byte();
+    if ((flags & ENCRYPTED) != 0) {
+           error("Input is encrypted\n");
+           return -1;
+    }
+    if ((flags & CONTINUATION) != 0) {
+           error("Multi part input\n");
+           return -1;
+    }
+    if ((flags & RESERVED) != 0) {
+           error("Input has invalid flags\n");
+           return -1;
+    }
+    (ulg)get_byte();   /* Get timestamp */
+    ((ulg)get_byte()) << 8;
+    ((ulg)get_byte()) << 16;
+    ((ulg)get_byte()) << 24;
+
+    (void)get_byte();  /* Ignore extra flags for the moment */
+    (void)get_byte();  /* Ignore OS type for the moment */
+
+    if ((flags & EXTRA_FIELD) != 0) {
+           unsigned len = (unsigned)get_byte();
+           len |= ((unsigned)get_byte())<<8;
+           while (len--) (void)get_byte();
+    }
+
+    /* Get original file name if it was truncated */
+    if ((flags & ORIG_NAME) != 0) {
+           /* Discard the old name */
+           while (get_byte() != 0) /* null */ ;
+    } 
+
+    /* Discard file comment if any */
+    if ((flags & COMMENT) != 0) {
+           while (get_byte() != 0) /* null */ ;
+    }
+
+    /* Decompress */
+    if ((res = inflate())) {
+           switch (res) {
+           case 0:
+                   break;
+           case 1:
+                   error("invalid compressed format (err=1)");
+                   break;
+           case 2:
+                   error("invalid compressed format (err=2)");
+                   break;
+           case 3:
+                   error("out of memory");
+                   break;
+           default:
+                   error("invalid compressed format (other)");
+           }
+           return -1;
+    }
+           
+    /* Get the crc and original length */
+    /* crc32  (see algorithm.doc)
+     * uncompressed input size modulo 2^32
+     */
+    orig_crc = (ulg) get_byte();
+    orig_crc |= (ulg) get_byte() << 8;
+    orig_crc |= (ulg) get_byte() << 16;
+    orig_crc |= (ulg) get_byte() << 24;
+    
+    orig_len = (ulg) get_byte();
+    orig_len |= (ulg) get_byte() << 8;
+    orig_len |= (ulg) get_byte() << 16;
+    orig_len |= (ulg) get_byte() << 24;
+    
+    /* Validate decompression */
+    if (orig_crc != CRC_VALUE) {
+           error("crc error");
+           return -1;
+    }
+    if (orig_len != bytes_out) {
+           error("length error");
+           return -1;
+    }
+    return 0;
+}
+
+
diff --git a/ia64/longjmp.S b/ia64/longjmp.S
new file mode 100644 (file)
index 0000000..8b66893
--- /dev/null
@@ -0,0 +1,162 @@
+/* Copyright (C) 1999, 2000, 2001, 2002  Free Software Foundation, Inc.
+   Contributed by David Mosberger-Tang <davidm@hpl.hp.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.
+
+   Note that __sigsetjmp() did NOT flush the register stack.  Instead,
+   we do it here since __longjmp() is usually much less frequently
+   invoked than __sigsetjmp(). The only difficulty is that __sigsetjmp()
+   didn't (and wouldn't be able to) save ar.rnat either.  This is a problem
+   because if we're not careful, we could end up loading random NaT bits.
+   There are two cases:
+
+       (i)  ar.bsp < ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+               ar.rnat contains the desired bits---preserve ar.rnat
+               across loadrs and write to ar.bspstore
+
+       (ii) ar.bsp >= ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+               The desired ar.rnat is stored in
+               ia64_rse_rnat_addr(jmpbuf.ar_bsp).  Load those
+               bits into ar.rnat after setting ar.bspstore. */
+
+
+
+#      define  pPos    p6      /* is rotate count positive? */
+#      define  pNeg    p7      /* is rotate count negative? */
+
+
+       /* __longjmp(__jmp_buf buf, int val) */
+
+       .text 
+       .global longjmp
+       .proc longjmp
+longjmp:
+       alloc r8=ar.pfs,2,1,0,0
+       mov r27=ar.rsc
+       add r2=0x98,in0         // r2 <- &jmpbuf.orig_jmp_buf_addr
+       ;;
+       ld8 r8=[r2],-16         // r8 <- orig_jmp_buf_addr
+       mov r10=ar.bsp
+       and r11=~0x3,r27        // clear ar.rsc.mode
+       ;;
+       flushrs                 // flush dirty regs to backing store (must be first in insn grp)
+       ld8 r23=[r2],8          // r23 <- jmpbuf.ar_bsp
+       sub r8=r8,in0           // r8 <- &orig_jmpbuf - &jmpbuf
+       ;;
+       ld8 r25=[r2]            // r25 <- jmpbuf.ar_unat
+       extr.u r8=r8,3,6        // r8 <- (&orig_jmpbuf - &jmpbuf)/8 & 0x3f
+       ;;
+       cmp.lt pNeg,pPos=r8,r0
+       mov r2=in0
+       ;;
+(pPos) mov r16=r8
+(pNeg) add r16=64,r8
+(pPos) sub r17=64,r8
+(pNeg) sub r17=r0,r8
+       ;;
+       mov ar.rsc=r11          // put RSE in enforced lazy mode
+       shr.u r8=r25,r16
+       add r3=8,in0            // r3 <- &jmpbuf.r1
+       shl r9=r25,r17
+       ;;
+       or r25=r8,r9
+       ;;
+       mov r26=ar.rnat
+       mov ar.unat=r25         // setup ar.unat (NaT bits for r1, r4-r7, and r12)
+       ;;
+       ld8.fill.nta sp=[r2],16 // r12 (sp)
+       ld8.fill.nta gp=[r3],16         // r1 (gp)
+       dep r11=-1,r23,3,6      // r11 <- ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+       ;;
+       ld8.nta r16=[r2],16             // caller's unat
+       ld8.nta r17=[r3],16             // fpsr
+       ;;
+       ld8.fill.nta r4=[r2],16 // r4
+       ld8.fill.nta r5=[r3],16         // r5 (gp)
+       cmp.geu p8,p0=r10,r11   // p8 <- (ar.bsp >= jmpbuf.ar_bsp)
+       ;;
+       ld8.fill.nta r6=[r2],16 // r6
+       ld8.fill.nta r7=[r3],16         // r7
+       ;;
+       mov ar.unat=r16                 // restore caller's unat
+       mov ar.fpsr=r17                 // restore fpsr
+       ;;
+       ld8.nta r16=[r2],16             // b0
+       ld8.nta r17=[r3],16             // b1
+       ;;
+(p8)   ld8 r26=[r11]           // r26 <- *ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+       mov ar.bspstore=r23     // restore ar.bspstore
+       ;;
+       ld8.nta r18=[r2],16             // b2
+       ld8.nta r19=[r3],16             // b3
+       ;;
+       ld8.nta r20=[r2],16             // b4
+       ld8.nta r21=[r3],16             // b5
+       ;;
+       ld8.nta r11=[r2],16             // ar.pfs
+       ld8.nta r22=[r3],56             // ar.lc
+       ;;
+       ld8.nta r24=[r2],32             // pr
+       mov b0=r16
+       ;;
+       ldf.fill.nta f2=[r2],32
+       ldf.fill.nta f3=[r3],32
+       mov b1=r17
+       ;;
+       ldf.fill.nta f4=[r2],32
+       ldf.fill.nta f5=[r3],32
+       mov b2=r18
+       ;;
+       ldf.fill.nta f16=[r2],32
+       ldf.fill.nta f17=[r3],32
+       mov b3=r19
+       ;;
+       ldf.fill.nta f18=[r2],32
+       ldf.fill.nta f19=[r3],32
+       mov b4=r20
+       ;;
+       ldf.fill.nta f20=[r2],32
+       ldf.fill.nta f21=[r3],32
+       mov b5=r21
+       ;;
+       ldf.fill.nta f22=[r2],32
+       ldf.fill.nta f23=[r3],32
+       mov ar.lc=r22
+       ;;
+       ldf.fill.nta f24=[r2],32
+       ldf.fill.nta f25=[r3],32
+       cmp.eq p8,p9=0,in1
+       ;;
+       ldf.fill.nta f26=[r2],32
+       ldf.fill.nta f27=[r3],32
+       mov ar.pfs=r11
+       ;;
+       ldf.fill.nta f28=[r2],32
+       ldf.fill.nta f29=[r3],32
+       ;;
+       ldf.fill.nta f30=[r2]
+       ldf.fill.nta f31=[r3]
+(p8)   mov r8=1
+
+       mov ar.rnat=r26         // restore ar.rnat
+       ;;
+       mov ar.rsc=r27          // restore ar.rsc
+(p9)   mov r8=in1
+
+       invala                  // virt. -> phys. regnum mapping may change
+       mov pr=r24,-1
+       br.ret.dptk.few rp
+       .endp __longjmp
diff --git a/ia64/memcpy.S b/ia64/memcpy.S
new file mode 100644 (file)
index 0000000..aea7da1
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ *
+ * This file is derived from the Linux/ia64 kernel source code
+ */
+
+/*
+ *
+ * Optimized version of the standard memcpy() function
+ *
+ * Inputs:
+ *     in0:    destination address
+ *     in1:    source address
+ *     in2:    number of bytes to copy
+ * Output:
+ *     no return value
+ *
+ * Copyright (C) 2000 Hewlett-Packard Co
+ * Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com>
+ * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+
+/* be pessimistic for now... */
+#define CONFIG_ITANIUM_B0_SPECIFIC 1
+
+#if defined(CONFIG_ITANIUM_B0_SPECIFIC) || defined(CONFIG_ITANIUM_B1_SPECIFIC)
+# define BRP(args...)  nop.b 0
+#else
+# define BRP(args...)  brp.loop.imp args
+#endif
+
+       .text 
+       // FALL THROUGH
+       .global Memcpy
+       .proc Memcpy
+Memcpy:
+#      define MEM_LAT  21              /* latency to memory */
+
+#      define dst      r2
+#      define src      r3
+#      define retval   r8
+#      define saved_pfs r9
+#      define saved_lc r10
+#      define saved_pr r11
+#      define cnt      r16
+#      define src2     r17
+#      define t0       r18
+#      define t1       r19
+#      define t2       r20
+#      define t3       r21
+#      define t4       r22
+#      define src_end  r23
+
+#      define N        (MEM_LAT + 4)
+#      define Nrot     ((N + 7) & ~7)
+
+       /*
+        * First, check if everything (src, dst, len) is a multiple of eight.  If
+        * so, we handle everything with no taken branches (other than the loop
+        * itself) and a small icache footprint.  Otherwise, we jump off to
+        * the more general copy routine handling arbitrary
+        * sizes/alignment etc.
+        */
+       .prologue
+       .save ar.pfs, saved_pfs
+       alloc saved_pfs=ar.pfs,3,Nrot,0,Nrot
+       .save ar.lc, saved_lc
+       mov saved_lc=ar.lc
+       or t0=in0,in1
+       ;;
+
+       or t0=t0,in2
+       .save pr, saved_pr
+       mov saved_pr=pr
+
+       .body
+
+       cmp.eq p6,p0=in2,r0     // zero length?
+       mov retval=in0          // return dst
+(p6)   br.ret.spnt.many rp     // zero length, return immediately
+       ;;
+
+       mov dst=in0             // copy because of rotation
+       shr.u cnt=in2,3         // number of 8-byte words to copy
+       mov pr.rot=1<<16
+       ;;
+
+       adds cnt=-1,cnt         // br.ctop is repeat/until
+       cmp.gtu p7,p0=16,in2    // copying less than 16 bytes?
+       mov ar.ec=N
+       ;;
+
+       and t0=0x7,t0
+       mov ar.lc=cnt
+       ;;
+       cmp.ne p6,p0=t0,r0
+
+       mov src=in1             // copy because of rotation
+(p7)   br.cond.spnt.few memcpy_short
+(p6)   br.cond.spnt.few memcpy_long
+       ;;
+       nop.m   0
+       ;;
+       nop.m   0
+       nop.i   0
+       ;;
+       nop.m   0
+       ;;
+       .rotr val[N]
+       .rotp p[N]
+       .align 32
+1: { .mib
+(p[0]) ld8 val[0]=[src],8
+       nop.i 0
+       BRP(1b, 2f)
+}
+2: { .mfb
+(p[N-1])st8 [dst]=val[N-1],8
+       nop.f 0
+       br.ctop.dptk.few 1b
+}
+       ;;
+       mov ar.lc=saved_lc
+       mov pr=saved_pr,-1
+       mov ar.pfs=saved_pfs
+       br.ret.sptk.many rp
+
+       /*
+        * Small (<16 bytes) unaligned copying is done via a simple byte-at-the-time
+        * copy loop.  This performs relatively poorly on Itanium, but it doesn't
+        * get used very often (gcc inlines small copies) and due to atomicity
+        * issues, we want to avoid read-modify-write of entire words.
+        */
+       .align 32
+memcpy_short:
+       adds cnt=-1,in2         // br.ctop is repeat/until
+       mov ar.ec=MEM_LAT
+       BRP(1f, 2f)
+       ;;
+       mov ar.lc=cnt
+       ;;
+       nop.m   0                       
+       ;;
+       nop.m   0
+       nop.i   0
+       ;;
+       nop.m   0
+       ;;
+       nop.m   0
+       ;;
+       /*
+        * It is faster to put a stop bit in the loop here because it makes
+        * the pipeline shorter (and latency is what matters on short copies).
+        */
+       .align 32
+1: { .mib
+(p[0]) ld1 val[0]=[src],1
+       nop.i 0
+       BRP(1b, 2f)
+} ;;
+2: { .mfb
+(p[MEM_LAT-1])st1 [dst]=val[MEM_LAT-1],1
+       nop.f 0
+       br.ctop.dptk.few 1b
+} ;;
+       mov ar.lc=saved_lc
+       mov pr=saved_pr,-1
+       mov ar.pfs=saved_pfs
+       br.ret.sptk.many rp
+
+       /*
+        * Large (>= 16 bytes) copying is done in a fancy way.  Latency isn't
+        * an overriding concern here, but throughput is.  We first do
+        * sub-word copying until the destination is aligned, then we check
+        * if the source is also aligned.  If so, we do a simple load/store-loop
+        * until there are less than 8 bytes left over and then we do the tail,
+        * by storing the last few bytes using sub-word copying.  If the source
+        * is not aligned, we branch off to the non-congruent loop.
+        *
+        *   stage:   op:
+        *         0  ld
+        *         :
+        * MEM_LAT+3  shrp
+        * MEM_LAT+4  st
+        *
+        * On Itanium, the pipeline itself runs without stalls.  However,  br.ctop
+        * seems to introduce an unavoidable bubble in the pipeline so the overall
+        * latency is 2 cycles/iteration.  This gives us a _copy_ throughput
+        * of 4 byte/cycle.  Still not bad.
+        */
+#      undef N
+#      undef Nrot
+#      define N        (MEM_LAT + 5)           /* number of stages */
+#      define Nrot     ((N+1 + 2 + 7) & ~7)    /* number of rotating regs */
+
+#define LOG_LOOP_SIZE  6
+
+memcpy_long:
+       alloc t3=ar.pfs,3,Nrot,0,Nrot   // resize register frame
+       and t0=-8,src           // t0 = src & ~7
+       and t2=7,src            // t2 = src & 7
+       ;;
+       ld8 t0=[t0]             // t0 = 1st source word
+       adds src2=7,src         // src2 = (src + 7)
+       sub t4=r0,dst           // t4 = -dst
+       ;;
+       and src2=-8,src2        // src2 = (src + 7) & ~7
+       shl t2=t2,3             // t2 = 8*(src & 7)
+       shl t4=t4,3             // t4 = 8*(dst & 7)
+       ;;
+       ld8 t1=[src2]           // t1 = 1st source word if src is 8-byte aligned, 2nd otherwise
+       sub t3=64,t2            // t3 = 64-8*(src & 7)
+       shr.u t0=t0,t2
+       ;;
+       add src_end=src,in2
+       shl t1=t1,t3
+       mov pr=t4,0x38          // (p5,p4,p3)=(dst & 7)
+       ;;
+       or t0=t0,t1
+       mov cnt=r0
+       adds src_end=-1,src_end
+       ;;
+(p3)   st1 [dst]=t0,1
+(p3)   shr.u t0=t0,8
+(p3)   adds cnt=1,cnt
+       ;;
+(p4)   st2 [dst]=t0,2
+(p4)   shr.u t0=t0,16
+(p4)   adds cnt=2,cnt
+       ;;
+(p5)   st4 [dst]=t0,4
+(p5)   adds cnt=4,cnt
+       and src_end=-8,src_end  // src_end = last word of source buffer
+       ;;
+
+       // At this point, dst is aligned to 8 bytes and there at least 16-7=9 bytes left to copy:
+
+1:{    add src=cnt,src                 // make src point to remainder of source buffer
+       sub cnt=in2,cnt                 // cnt = number of bytes left to copy
+       mov t4=ip
+  }    ;;
+       and src2=-8,src                 // align source pointer
+       adds t4=memcpy_loops-1b,t4
+       mov ar.ec=N
+
+       and t0=7,src                    // t0 = src & 7
+       shr.u t2=cnt,3                  // t2 = number of 8-byte words left to copy
+       shl cnt=cnt,3                   // move bits 0-2 to 3-5
+       ;;
+
+       .rotr val[N+1], w[2]
+       .rotp p[N]
+
+       cmp.ne p6,p0=t0,r0              // is src aligned, too?
+       shl t0=t0,LOG_LOOP_SIZE         // t0 = 8*(src & 7)
+       adds t2=-1,t2                   // br.ctop is repeat/until
+       ;;
+       add t4=t0,t4
+       mov pr=cnt,0x38                 // set (p5,p4,p3) to # of bytes last-word bytes to copy
+       mov ar.lc=t2
+       ;;
+       nop.m   0                       
+       ;;
+       nop.m   0
+       nop.i   0
+       ;;
+       nop.m   0
+       ;;
+(p6)   ld8 val[1]=[src2],8             // prime the pump...
+       mov b6=t4
+       br.sptk.few b6
+       ;;
+
+memcpy_tail:
+       // At this point, (p5,p4,p3) are set to the number of bytes left to copy (which is
+       // less than 8) and t0 contains the last few bytes of the src buffer:
+(p5)   st4 [dst]=t0,4
+(p5)   shr.u t0=t0,32
+       mov ar.lc=saved_lc
+       ;;
+(p4)   st2 [dst]=t0,2
+(p4)   shr.u t0=t0,16
+       mov ar.pfs=saved_pfs
+       ;;
+(p3)   st1 [dst]=t0
+       mov pr=saved_pr,-1
+       br.ret.sptk.many rp
+
+///////////////////////////////////////////////////////
+       .align 64
+
+#define COPY(shift,index)                                                                      \
+ 1: { .mib                                                                                     \
+       (p[0])          ld8 val[0]=[src2],8;                                                    \
+       (p[MEM_LAT+3])  shrp w[0]=val[MEM_LAT+3],val[MEM_LAT+4-index],shift;                    \
+                       BRP(1b, 2f)                                                             \
+    };                                                                                         \
+ 2: { .mfb                                                                                     \
+       (p[MEM_LAT+4])  st8 [dst]=w[1],8;                                                       \
+                       nop.f 0;                                                                \
+                       br.ctop.dptk.few 1b;                                                    \
+    };                                                                                         \
+                       ;;                                                                      \
+                       ld8 val[N-1]=[src_end]; /* load last word (may be same as val[N]) */    \
+                       ;;                                                                      \
+                       shrp t0=val[N-1],val[N-index],shift;                                    \
+                       br memcpy_tail
+memcpy_loops:
+       COPY(0, 1) /* no point special casing this---it doesn't go any faster without shrp */
+       COPY(8, 0)
+       COPY(16, 0)
+       COPY(24, 0)
+       COPY(32, 0)
+       COPY(40, 0)
+       COPY(48, 0)
+       COPY(56, 0)
+       .endp Memcpy
+
diff --git a/ia64/memset.S b/ia64/memset.S
new file mode 100644 (file)
index 0000000..b72554b
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ *  Copyright (C) 1999-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ *
+ * This code is derived from the Linux/ia64 source code.
+ */
+
+/*
+ *
+ * Optimized version of the standard memset() function
+ *
+ * Return: none
+ *
+ * Inputs:
+ *     in0:    address of buffer
+ *     in1:    byte value to use for storing
+ *     in2:    length of the buffer
+ *
+ */
+
+// arguments
+//
+#define buf            r32
+#define val            r33
+#define len            r34
+
+//
+// local registers
+//
+#define saved_pfs      r14
+#define cnt            r18
+#define buf2           r19
+#define saved_lc       r20
+#define tmp            r21
+       .text
+       .global Memset
+       .proc Memset
+Memset:
+       .prologue
+       .save ar.pfs, saved_pfs
+       alloc saved_pfs=ar.pfs,3,0,0,0  // cnt is sink here
+       cmp.eq p8,p0=r0,len     // check for zero length
+       .save ar.lc, saved_lc
+       mov saved_lc=ar.lc      // preserve ar.lc (slow)
+       ;;
+
+       .body
+
+       adds tmp=-1,len         // br.ctop is repeat/until
+       tbit.nz p6,p0=buf,0     // odd alignment
+(p8)   br.ret.spnt.few rp
+
+       cmp.lt p7,p0=16,len     // if len > 16 then long memset
+       mux1 val=val,@brcst     // prepare value
+(p7)   br.cond.dptk.few long_memset
+       ;;
+       mov ar.lc=tmp           // initialize lc for small count
+       ;;                      // avoid RAW and WAW on ar.lc
+1:                             // worst case 15 cyles, avg 8 cycles
+       st1 [buf]=val,1
+       br.cloop.dptk.few 1b
+       ;;                              // avoid RAW on ar.lc
+       mov ar.lc=saved_lc
+       mov ar.pfs=saved_pfs
+       br.ret.sptk.few rp      // end of short memset
+
+       // at this point we know we have more than 16 bytes to copy
+       // so we focus on alignment
+long_memset:
+(p6)   st1 [buf]=val,1         // 1-byte aligned
+(p6)   adds len=-1,len;;       // sync because buf is modified
+       tbit.nz p6,p0=buf,1
+       ;;
+(p6)   st2 [buf]=val,2         // 2-byte aligned
+(p6)   adds len=-2,len;;
+       tbit.nz p6,p0=buf,2
+       ;;
+(p6)   st4 [buf]=val,4         // 4-byte aligned
+(p6)   adds len=-4,len;;
+       tbit.nz p6,p0=buf,3
+       ;;
+(p6)   st8 [buf]=val,8         // 8-byte aligned
+(p6)   adds len=-8,len;;
+       shr.u cnt=len,4         // number of 128-bit (2x64bit) words
+       ;;
+       cmp.eq p6,p0=r0,cnt
+       adds tmp=-1,cnt
+(p6)   br.cond.dpnt.few .dotail // we have less than 16 bytes left
+       ;;
+       adds buf2=8,buf         // setup second base pointer
+       mov ar.lc=tmp
+       ;;
+2:                             // 16bytes/iteration
+       st8 [buf]=val,16
+       st8 [buf2]=val,16
+       br.cloop.dptk.few 2b
+       ;;
+.dotail:                       // tail correction based on len only
+       tbit.nz p6,p0=len,3
+       ;;
+(p6)   st8 [buf]=val,8         // at least 8 bytes
+       tbit.nz p6,p0=len,2
+       ;;
+(p6)   st4 [buf]=val,4         // at least 4 bytes
+       tbit.nz p6,p0=len,1
+       ;;
+(p6)   st2 [buf]=val,2         // at least 2 bytes
+       tbit.nz p6,p0=len,0
+       mov ar.lc=saved_lc
+       ;;
+(p6)   st1 [buf]=val           // only 1 byte left
+       br.ret.dptk.few rp
+       .endp Memset
diff --git a/ia64/plain_loader.c b/ia64/plain_loader.c
new file mode 100644 (file)
index 0000000..92d009e
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ *  Copyright (C) 2001 Silicon Graphics, Inc.
+ *      Contributed by Brent Casavant <bcasavan@sgi.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "loader.h"
+#include "elf.h"
+#include "private.h"
+
+#define LD_NAME L"plain_elf64"
+
+#define PLAIN_MIN_BLOCK_SIZE   sizeof(Elf64_Ehdr) /* see load_elf() for details */
+
+#define SKIPBUFSIZE    2048    /* minimal default size of the skip buffer */
+static CHAR8 *skip_buffer;     /* used to skip over unneeded data */
+static UINTN skip_bufsize;
+static UINTN elf_is_big_endian;        /* true if ELF file is big endian */
+
+static inline UINT64 
+bswap64(UINT64 v)
+{
+        if(elf_is_big_endian) v = __ia64_swab64(v);
+        return v;
+}
+
+static inline UINT32
+bswap32(UINT32 v)
+{
+        if(elf_is_big_endian) v = __ia64_swab32(v);
+        return v;
+}
+
+static inline UINT16
+bswap16(UINT16 v)
+{
+        if(elf_is_big_endian) v = __ia64_swab16(v);
+        return v;
+}
+
+static INTN
+is_valid_header(Elf64_Ehdr *ehdr)
+{
+       UINT16 type, machine;
+
+        if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) {
+               type    = __ia64_swab16(ehdr->e_type);
+               machine = __ia64_swab16(ehdr->e_machine);
+       } else {
+               type    = ehdr->e_type;
+               machine = ehdr->e_machine;
+       }
+       DBG_PRT((L"class=%d type=%d data=%d machine=%d\n", 
+               ehdr->e_ident[EI_CLASS],
+               type,
+               ehdr->e_ident[EI_DATA],
+               machine));
+
+       return    ehdr->e_ident[EI_MAG0]  == 0x7f 
+              && ehdr->e_ident[EI_MAG1]  == 'E'
+              && ehdr->e_ident[EI_MAG2]  == 'L'
+              && ehdr->e_ident[EI_MAG3]  == 'F'
+              && ehdr->e_ident[EI_CLASS] == ELFCLASS64 
+              && type                    == ET_EXEC    /* must be executable */
+              && machine                 == EM_IA_64 ? 0 : -1;
+}
+
+static INTN
+plain_probe(CHAR16 *kname)
+{
+       Elf64_Ehdr ehdr;
+       EFI_STATUS status;
+       INTN ret = -1;
+       fops_fd_t fd;
+       UINTN size = sizeof(ehdr);
+
+       status = fops_open(kname, &fd);
+       if (EFI_ERROR(status)) return -1;
+
+       status = fops_read(fd, &ehdr, &size);
+
+       if (EFI_ERROR(status) || size != sizeof(ehdr)) goto error;
+
+       ret = is_valid_header(&ehdr);
+error:
+       fops_close(fd);
+       return ret;
+}
+
+/*
+ * move skip bytes forward in the file
+ * this is required because we cannot assume fileops has
+ * seek() capabilities.
+ */
+static INTN
+skip_bytes(fops_fd_t fd, UINTN curpos, UINTN newpos)
+{
+       EFI_STATUS status;
+       UINTN n, skip;
+
+       skip = newpos - curpos;
+       /* check if seek capability exists */   
+
+       status = fops_seek(fd, newpos);
+       if (status == EFI_SUCCESS) return 0;
+
+       if (status != EFI_UNSUPPORTED) goto error;
+
+       /* unsupported case */
+
+       if (skip_buffer == NULL) {
+               skip_bufsize = MAX(skip, SKIPBUFSIZE);
+               skip_buffer= (CHAR8 *)alloc(skip_bufsize, EfiLoaderData);
+               if (skip_buffer == NULL) return -1;
+       }
+       while (skip) {
+               n = skip > skip_bufsize? skip_bufsize : skip;
+
+               status = fops_read(fd, skip_buffer, &n);
+               if (EFI_ERROR(status)) goto error;
+
+               skip -=n;
+       }
+       return 0;
+
+error:
+       ERR_PRT((L"%s : cannot skip %d bytes\n", LD_NAME, n));
+       return -1;
+}
+
+static INTN
+load_elf(fops_fd_t fd, kdesc_t *kd)
+{
+       Elf64_Ehdr ehdr;
+       Elf64_Phdr *phdrs;
+       EFI_STATUS status;
+       INTN ret = ELILO_LOAD_ERROR;
+       UINTN i, total_size = 0;
+       UINTN pages, size, bss_sz, osize;
+       UINTN offs = 0;
+       VOID *low_addr = (VOID *)~0;
+       VOID *max_addr = (VOID *)0;
+       UINTN load_offset = 0;
+       UINTN paddr, memsz, filesz, poffs;
+       UINT16 phnum;
+
+       Print(L"Loading Linux... ");
+
+       size = sizeof(ehdr);
+
+       status = fops_read(fd, &ehdr, &size);
+       if (EFI_ERROR(status) ||size < sizeof(ehdr)) return ELILO_LOAD_ERROR;
+
+       offs += size;
+
+       /*
+        * do some sanity checking on the file
+        */
+       if (is_valid_header(&ehdr) == -1) {
+               ERR_PRT((L"%s : not an elf 64-bit file\n", LD_NAME));
+               return ELILO_LOAD_ERROR;
+       }        
+
+       /* determine file endianess */
+        elf_is_big_endian = ehdr.e_ident[EI_DATA] == ELFDATA2MSB ? 1 : 0;
+
+       VERB_PRT(3, { 
+                       Print(L"ELF file is %s\n", elf_is_big_endian ? L"big endian" : L"little endian");
+                       Print(L"Entry point 0x%lx\n", bswap64(ehdr.e_entry));
+                       Print(L"%d program headers\n", bswap16(ehdr.e_phnum));
+                       Print(L"%d segment headers\n", bswap16(ehdr.e_shnum));
+                  });
+
+       phnum = bswap16(ehdr.e_phnum);
+
+       if (skip_bytes(fd, offs, bswap64(ehdr.e_phoff)) != 0) {
+               ERR_PRT((L"%s : skip tp %ld for phdrs failed", LD_NAME, offs));
+               return ELILO_LOAD_ERROR;
+       }
+       offs  = bswap64(ehdr.e_phoff);
+
+       size = osize = phnum*sizeof(Elf64_Phdr);
+
+       DBG_PRT((L"%s : phdrs allocate %d bytes sizeof=%d entsize=%d\n", LD_NAME, size,sizeof(Elf64_Phdr), bswap16(ehdr.e_phentsize)));
+
+       phdrs = (Elf64_Phdr *)alloc(size, 0);
+       if (phdrs == NULL) {
+               ERR_PRT((L"%s : allocate phdrs failed", LD_NAME));
+               return ELILO_LOAD_ERROR;
+       }
+
+       status = fops_read(fd, phdrs, &size);
+       if (EFI_ERROR(status) || size != osize) {
+               ERR_PRT((L"%s : load phdrs failed", LD_NAME, status));
+               goto out;
+       }
+       offs += size;
+       /*
+        * First pass to figure out:
+        *      - lowest physical address
+        *      - total memory footprint
+        */
+       for (i = 0; i < phnum; i++) {
+
+               paddr = bswap64(phdrs[i].p_paddr);
+               memsz = bswap64(phdrs[i].p_memsz);
+
+               DBG_PRT((L"Phdr %d paddr [0x%lx-0x%lx] offset %ld"
+                          " filesz %ld memsz=%ld bss_sz=%ld p_type=%d\n",
+                          1+i, 
+                          paddr, 
+                          paddr+bswap64(phdrs[i].p_filesz), 
+                          bswap64(phdrs[i].p_offset), 
+                          bswap64(phdrs[i].p_filesz), 
+                          memsz, 
+                          memsz - bswap64(phdrs[i].p_filesz), bswap32(phdrs[i].p_type)));
+       
+               if (bswap32(phdrs[i].p_type) != PT_LOAD) continue;
+
+
+               if (paddr < (UINTN)low_addr) low_addr = (VOID *)paddr;
+
+               if (paddr + memsz > (UINTN)max_addr) 
+                       max_addr = (VOID *)paddr + memsz;
+       }
+
+       if ((UINTN)low_addr & (EFI_PAGE_SIZE - 1)) {
+               ERR_PRT((L"%s : kernel low address 0x%lx not page aligned\n", LD_NAME, low_addr));
+               goto out;
+       }
+
+       /* how many bytes are needed to hold the kernel */
+       total_size = (UINTN)max_addr - (UINTN)low_addr;
+
+       /* round up to get required number of pages */
+       pages = EFI_SIZE_TO_PAGES(total_size);
+
+       /* keep track of location where kernel ends for
+        * the initrd ramdisk (it will be put right after the kernel) 
+        */
+       kd->kstart = low_addr;
+       kd->kend   = low_addr+ (pages << EFI_PAGE_SHIFT);
+
+       /*
+        * that's the kernel entry point (virtual address)
+        */
+       kd->kentry = (VOID *)bswap64(ehdr.e_entry);
+       
+       if (((UINTN)kd->kentry >> 61) != 0) {
+               ERR_PRT((L"%s:  <<ERROR>> entry point is a virtual address 0x%lx : not supported anymore\n", LD_NAME, kd->kentry));
+       }
+
+       VERB_PRT(3, {
+               Print(L"Lowest PhysAddr: 0x%lx\nTotalMemSize:%d bytes (%d pages)\n",
+                       low_addr, total_size, pages);
+               Print(L"Kernel entry @ 0x%lx\n", kd->kentry);
+       });
+
+       /*
+        * now allocate memory for the kernel at the exact requested spot
+        */
+       if (alloc_kmem(low_addr, pages) == -1) {
+               VOID *new_addr;
+
+               ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", LD_NAME, pages, low_addr));
+
+               if (ia64_can_relocate() == 0) {
+                       ERR_PRT((L"relocation is disabled, cannot load kernel"));
+                       goto out;
+               }
+
+               /*
+                * could not allocate at requested spot, try to find a
+                * suitable location to relocate the kernel
+                *
+                * The maximum sized Itanium TLB translation entry is 256 MB.
+                * If we relocate the kernel by this amount we know for sure
+                * that alignment constraints will be satisified, regardless
+                * of the kernel used.
+                */
+               Print(L"Attempting to relocate kernel.\n");
+               if (find_kernel_memory(low_addr, max_addr, 256*MB, &new_addr) == -1) {
+                       ERR_PRT((L"%s : find_kernel_memory(0x%lx, 0x%lx, 0x%lx, 0x%lx) failed\n", LD_NAME, low_addr, max_addr, 256*MB, &load_offset));
+                       goto out;
+               }
+               /* unsigned arithmetic */
+                load_offset = (UINTN) (new_addr - ROUNDDOWN((UINTN) low_addr,256*MB));
+
+               VERB_PRT(3, Print(L"low_addr=0x%lx new_addr=0x%lx offset=0x%lx", low_addr, new_addr, load_offset));
+
+               /*
+                * correct various addesses for non-zero load_offset
+                */
+               low_addr = (VOID*) ((UINTN) low_addr + load_offset);
+               max_addr = (VOID*) ((UINTN) max_addr + load_offset);
+               kd->kstart = (VOID *) ((UINTN) kd->kstart + load_offset);
+               kd->kend = (VOID *) ((UINTN) kd->kend + load_offset);
+               kd->kentry = (VOID *) ((UINTN) kd->kentry + load_offset);
+
+               /*
+                * try one last time to get memory for the kernel
+                */
+               if (alloc_kmem(low_addr, pages) == -1) {
+                       ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", LD_NAME, pages, low_addr));
+                       ERR_PRT((L"Relocation by 0x%lx bytes failed.\n", load_offset));
+                       goto out;
+               }
+       }
+
+       VERB_PRT(1, Print(L"Press any key to interrupt\n"));
+
+       /* Second pass:
+        * Walk through the program headers
+        * and actually load data into physical memory
+        */
+       for (i = 0; i < phnum; i++) {
+
+               /*
+                * Check for pure loadable segment; ignore if not loadable
+                */
+               if (bswap32(phdrs[i].p_type) != PT_LOAD) continue;
+
+               poffs = bswap64(phdrs[i].p_offset);
+
+               size = poffs - offs;
+
+               VERB_PRT(3, Print(L"\noff=%ld poffs=%ld size=%ld\n", offs, poffs, size));
+
+               filesz = bswap64(phdrs[i].p_filesz);
+                /*
+                 * correct p_paddr for non-zero load offset
+                 */
+               phdrs[i].p_paddr = (Elf64_Addr) ((UINTN) bswap64(phdrs[i].p_paddr) + load_offset);
+
+               /*
+                * Move to the right position
+                */
+               if (size && skip_bytes(fd, offs, poffs) != 0) goto out_kernel;
+
+               /*
+                * Keep track of current position in file
+                */
+               offs += size;
+
+               /*
+                * How many BSS bytes to clear
+                */
+               bss_sz = bswap64(phdrs[i].p_memsz) - filesz;
+
+               VERB_PRT(4, {
+                       Print(L"\nHeader #%d\n", i);
+                       Print(L"offset %ld\n", poffs);
+                       Print(L"Phys addr 0x%lx\n", phdrs[i].p_paddr); /* already endian adjusted */
+                       Print(L"BSS size %ld bytes\n", bss_sz);
+                       Print(L"skip=%ld offs=%ld\n", size, offs);
+               });
+
+               /*
+                * Read actual segment into memory
+                */
+               ret = read_file(fd, filesz, (CHAR8 *)phdrs[i].p_paddr);
+               if (ret == ELILO_LOAD_ABORTED) goto load_abort;
+               if (ret == ELILO_LOAD_ERROR) goto out;
+
+               /*
+                * update file position
+                */
+               offs += filesz;
+
+               /*
+                * Clear bss section
+                */
+               if (bss_sz) Memset((VOID *) phdrs[i].p_paddr+filesz, 0, bss_sz);
+       }
+
+       free(phdrs);
+
+       Print(L"..done\n");
+       return ELILO_LOAD_SUCCESS;
+
+load_abort:
+       Print(L"..Aborted\n");
+       ret = ELILO_LOAD_ABORTED;
+out_kernel:
+       /* free kernel memory */
+       free_kmem();
+out:
+       free(phdrs);
+       return ret;
+}
+
+static INTN
+plain_load_kernel(CHAR16 *kname, kdesc_t *kd)
+{      
+       INTN ret;
+       fops_fd_t fd;
+       EFI_STATUS status;
+
+       /*
+        * Moving the open here simplifies the load_elf() error handling
+        */
+       status = fops_open(kname, &fd);
+       if (EFI_ERROR(status)) return ELILO_LOAD_ERROR;
+
+       Print(L"Loading %s...", kname);
+
+       ret = load_elf(fd, kd);
+
+       fops_close(fd);
+
+       /*
+        * if the skip buffer was ever used, free it
+        */
+       if (skip_buffer) {
+               free(skip_buffer);
+               /* in case we come back */
+               skip_buffer = NULL;
+       }
+       return ret;
+}
+
+loader_ops_t plain_loader={
+       NULL,
+       LD_NAME,
+       plain_probe,
+       plain_load_kernel
+};
diff --git a/ia64/private.h b/ia64/private.h
new file mode 100644 (file)
index 0000000..bb09faf
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __ELILO_PRIVATE_IA64_H__
+#define __ELILO_PRIVATE_IA64_H__
+
+extern INTN check_fpswa(EFI_HANDLE, EFI_HANDLE, CHAR16 *);
+extern INTN query_fpswa(VOID **);
+
+extern INTN ia64_can_relocate();
+
+#endif /* __ELILO_PRIVATE_IA64_H__ */
+
diff --git a/ia64/setjmp.S b/ia64/setjmp.S
new file mode 100644 (file)
index 0000000..ce7e67c
--- /dev/null
@@ -0,0 +1,170 @@
+/* Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+   Contributed by David Mosberger-Tang <davidm@hpl.hp.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.
+
+   The layout of the jmp_buf is as follows.  This is subject to change
+   and user-code should never depend on the particular layout of
+   jmp_buf!
+
+
+       offset: description:
+       ------- ------------
+       0x000   stack pointer (r12)     ; unchangeable (see _JMPBUF_UNWINDS)
+       0x008   r1 (gp)
+       0x010   caller's unat
+       0x018   fpsr
+       0x020   r4
+       0x028   r5
+       0x030   r6
+       0x038   r7
+       0x040   rp (b0)
+       0x048   b1
+       0x050   b2
+       0x058   b3
+       0x060   b4
+       0x068   b5
+       0x070   ar.pfs
+       0x078   ar.lc
+       0x080   pr
+       0x088   ar.bsp                  ; unchangeable (see __longjmp.S)
+       0x090   ar.unat
+       0x098   &__jmp_buf      ; address of the jmpbuf (needed to locate NaT bits in unat)
+       0x0a0    f2
+       0x0b0    f3
+       0x0c0    f4
+       0x0d0    f5
+       0x0e0   f16
+       0x0f0   f17
+       0x100   f18
+       0x110   f19
+       0x120   f20
+       0x130   f21
+       0x130   f22
+       0x140   f23
+       0x150   f24
+       0x160   f25
+       0x170   f26
+       0x180   f27
+       0x190   f28
+       0x1a0   f29
+       0x1b0   f30
+       0x1c0   f31 */
+
+
+       /* The following two entry points are the traditional entry points: */
+
+       .text
+       .global setjmp
+       .proc setjmp
+setjmp:
+       alloc r8=ar.pfs,2,0,0,0
+       mov in1=1
+       br.cond.sptk.many __sigsetjmp
+       .endp setjmp
+
+       /* __sigsetjmp(__jmp_buf buf, int savemask) */
+
+__sigsetjmp:
+       //.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2)
+       alloc loc1=ar.pfs,2,2,2,0
+       mov r16=ar.unat
+       ;;
+       mov r17=ar.fpsr
+       mov r2=in0
+       add r3=8,in0
+       ;;
+       st8.spill.nta [r2]=sp,16        // r12 (sp)
+       st8.spill.nta [r3]=gp,16        // r1 (gp)
+       ;;
+       st8.nta [r2]=r16,16             // save caller's unat
+       st8.nta [r3]=r17,16             // save fpsr
+       add r8=0xa0,in0
+       ;;
+       st8.spill.nta [r2]=r4,16        // r4
+       st8.spill.nta [r3]=r5,16        // r5
+       add r9=0xb0,in0
+       ;;
+       stf.spill.nta [r8]=f2,32
+       stf.spill.nta [r9]=f3,32
+       mov loc0=rp
+       .body
+       ;;
+       stf.spill.nta [r8]=f4,32
+       stf.spill.nta [r9]=f5,32
+       mov r17=b1
+       ;;
+       stf.spill.nta [r8]=f16,32
+       stf.spill.nta [r9]=f17,32
+       mov r18=b2
+       ;;
+       stf.spill.nta [r8]=f18,32
+       stf.spill.nta [r9]=f19,32
+       mov r19=b3
+       ;;
+       stf.spill.nta [r8]=f20,32
+       stf.spill.nta [r9]=f21,32
+       mov r20=b4
+       ;;
+       stf.spill.nta [r8]=f22,32
+       stf.spill.nta [r9]=f23,32
+       mov r21=b5
+       ;;
+       stf.spill.nta [r8]=f24,32
+       stf.spill.nta [r9]=f25,32
+       mov r22=ar.lc
+       ;;
+       stf.spill.nta [r8]=f26,32
+       stf.spill.nta [r9]=f27,32
+       mov r24=pr
+       ;;
+       stf.spill.nta [r8]=f28,32
+       stf.spill.nta [r9]=f29,32
+       ;;
+       stf.spill.nta [r8]=f30
+       stf.spill.nta [r9]=f31
+
+       st8.spill.nta [r2]=r6,16        // r6
+       st8.spill.nta [r3]=r7,16        // r7
+       ;;
+       mov r23=ar.bsp
+       mov r25=ar.unat
+       mov out0=in0
+
+       st8.nta [r2]=loc0,16            // b0
+       st8.nta [r3]=r17,16             // b1
+       mov out1=in1
+       ;;
+       st8.nta [r2]=r18,16             // b2
+       st8.nta [r3]=r19,16             // b3
+       ;;
+       st8.nta [r2]=r20,16             // b4
+       st8.nta [r3]=r21,16             // b5
+       ;;
+       st8.nta [r2]=loc1,16            // ar.pfs
+       st8.nta [r3]=r22,16             // ar.lc
+       ;;
+       st8.nta [r2]=r24,16             // pr
+       st8.nta [r3]=r23,16             // ar.bsp
+       ;;
+       st8.nta [r2]=r25                // ar.unat
+       st8.nta [r3]=in0                // &__jmp_buf
+       mov r8=0
+       mov rp=loc0
+       mov ar.pfs=loc1
+       br.ret.sptk.many rp
+
+       .endp __sigsetjmp
diff --git a/ia64/setjmp.h b/ia64/setjmp.h
new file mode 100644 (file)
index 0000000..91bbfc1
--- /dev/null
@@ -0,0 +1,28 @@
+/* Define the machine-dependent type `jmp_buf'.  Linux/IA-64 version.
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+   Contributed by David Mosberger-Tang <davidm@hpl.hp.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* User code must not depend on the internal representation of jmp_buf. */
+
+#define _JBLEN 70
+
+/* the __jmp_buf element type should be __float80 per ABI... */
+typedef long jmp_buf[_JBLEN] __attribute__ ((aligned (16))); /* guarantees 128-bit alignment! */
+
+extern int setjmp (jmp_buf __env);
+extern void longjmp (jmp_buf __env, int __val);
diff --git a/ia64/sysdeps.h b/ia64/sysdeps.h
new file mode 100644 (file)
index 0000000..ab447fb
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+/*
+ * This file is used to define all the IA64-specific data structures, functions, 
+ * and constants used by the generic ELILO. 
+ *
+ * For things specific to this platform use private.h instead
+ */
+#ifndef __ELILO_SYSDEPS_IA64_H__
+#define __ELILO_SYSDEPS_IA64_H__
+
+#define ELILO_ARCH     "IA-64" /* ASCII string ! */
+
+/* in respective assembly files */
+extern VOID Memset(VOID *, INTN, UINTN);
+extern VOID Memcpy(VOID *, VOID *, UINTN);
+
+extern VOID sysdep_register_options(VOID);
+
+/*
+ * This version must match the one in the kernel
+ */
+typedef struct ia64_boot_params {
+       /*
+        * The following three pointers MUST point to memory that is marked
+        * as EfiRuntimeServicesData so that the kernel doesn't think the
+        * underlying memory is free.
+        */
+       UINTN command_line;             /* physical address of command line arguments */
+       UINTN efi_systab;               /* physical address of EFI system table */
+       UINTN efi_memmap;               /* physical address of EFI memory map */
+       UINTN efi_memmap_size;          /* size of EFI memory map */
+       UINTN efi_memdesc_size; /* size of an EFI memory map descriptor */
+       UINT32 efi_memdesc_version;     /* descriptor version */
+       struct {
+               UINT16 num_cols;        /* number of columns on console output device */
+               UINT16 num_rows;        /* number of rows on console output device */
+               UINT16 orig_x;          /* cursor's x position */
+               UINT16 orig_y;          /* cursor's y position */
+       } console_info;
+       UINTN fpswa;                    /* physical address of fpswa interface */
+       UINTN initrd_start;             /* virtual address where the initial ramdisk begins */
+       UINTN initrd_size;              /* how big is the initial ramdisk */
+
+       UINTN loader_addr;              /* start address of boot loader */
+       UINTN loader_size;              /* size of loader code & data */
+} boot_params_t;
+
+typedef struct sys_img_options {
+       UINT8 dummy;                    /* forces non-zero offset for first field */
+       UINT8 allow_relocation;         /* allow kernel relocation on allocation error */
+} sys_img_options_t;
+
+/*
+ * How to jump to kernel code
+ */
+static inline void
+start_kernel(VOID *kentry, VOID *bp)
+{
+        asm volatile ("mov r28=%1; br.sptk.few %0" :: "b"(kentry),"r"(bp));
+}
+
+static inline const UINT64
+__ia64_swab64 (UINT64 x)
+{
+       UINT64 result;
+
+       asm volatile ("mux1 %0=%1,@rev" : "=r" (result) : "r" (x));
+       return result;
+}
+
+static inline const UINT32
+__ia64_swab32 (UINT32 x)
+{
+       return __ia64_swab64(x) >> 32;
+}
+
+static inline const UINT16
+__ia64_swab16(UINT16 x)
+{
+       return __ia64_swab64(x) >> 48;
+}
+
+#endif /* __ELILO_SYSDEPS_IA64_H__ */
diff --git a/ia64/system.c b/ia64/system.c
new file mode 100644 (file)
index 0000000..388d435
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+/*
+ * this file contains all the IA-64 specific code expected by generic loader
+ */
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "loader.h"
+#include "private.h"
+
+extern loader_ops_t plain_loader, gzip_loader;
+
+/*
+ * IA-64 specific boot paramters initialization routine
+ */
+INTN
+sysdeps_create_boot_params(boot_params_t *bp, CHAR8 *cmdline, memdesc_t *initrd, UINTN *cookie)
+{
+       UINTN cols, rows;
+       SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
+       EFI_STATUS status;
+       mmap_desc_t mdesc;
+
+       /*
+        * retrieve address of FPSWA interface
+        * if not found, argument is not touched
+        * will be 0 because of Memset()
+        */
+       query_fpswa((VOID **)&bp->fpswa);
+
+       if (get_memmap(&mdesc) == -1) return -1;
+
+       DBG_PRT((L"Got memory map @ 0x%lx (%d bytes)", mdesc.md, mdesc.map_size));
+
+       bp->efi_systab          = (UINTN)systab;
+       bp->efi_memmap          = (UINTN)mdesc.md;
+       bp->efi_memmap_size     = mdesc.map_size;
+       bp->efi_memdesc_size    = mdesc.desc_size;
+       bp->efi_memdesc_version = mdesc.desc_version;
+       bp->command_line        = (UINTN)cmdline;
+       bp->initrd_start        = (UINTN) initrd->start_addr;
+       bp->initrd_size         = initrd->pgcnt << EFI_PAGE_SHIFT;
+
+       /* fetch console parameters: */
+       conout = systab->ConOut;
+       status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"boot_params QueryMode failed %r", status));
+               goto error;
+       }
+       DBG_PRT((L"Got console info: cols=%d rows=%d x=%d y=%d",
+             cols, rows, conout->Mode->CursorColumn, conout->Mode->CursorRow));
+
+       bp->console_info.num_cols = cols;
+       bp->console_info.num_rows = rows;
+       bp->console_info.orig_x = conout->Mode->CursorColumn;
+       bp->console_info.orig_y = conout->Mode->CursorRow;
+
+       *cookie = mdesc.cookie;
+
+       return 0;
+error:
+       /* free descriptors' memory */
+       free_memmap(&mdesc);
+
+       return -1;
+}
+
+VOID
+sysdeps_free_boot_params(boot_params_t *bp)
+{
+       mmap_desc_t md;
+
+       Memset(&md, 0, sizeof(md));
+
+       md.md = (VOID *)bp->efi_memmap;
+
+       free_memmap(&md);
+}
+
+INTN
+sysdeps_init(EFI_HANDLE dev)
+{
+       loader_register(&plain_loader);
+       loader_register(&gzip_loader);
+
+       return 0;
+}
+
+INTN
+sysdeps_initrd_get_addr(kdesc_t *kd, memdesc_t *imem)
+{
+       /*
+        * We currently place the initrd at the next page aligned boundary
+        * after the kernel. 
+        *
+        * Current kernel implementation requires this (see arch/ia64/kernel/setup.c).
+        *
+        * IMPORTANT: EFI & kernel page sizes may differ. We have no way
+        * of guessing what size the kernel uses. It is the responsibility
+        * of the kernel to adjust.
+        *
+        */
+#if 0
+       imem->start_addr = (VOID *)ROUNDUP((UINTN)kd->kend, EFI_PAGE_SIZE);
+#else
+       imem->start_addr = 0; /* let the allocator decide */
+#endif
+
+       return 0;
+}
+
diff --git a/initrd.c b/initrd.c
new file mode 100644 (file)
index 0000000..92b6f44
--- /dev/null
+++ b/initrd.c
@@ -0,0 +1,112 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+
+/*
+ * This function allocates memory for the initial ramdisk (initrd) and loads it to memory
+ * OUTPUTS:
+ *     - ELILO_LOAD_SUCCESS: if everything works
+ *     - ELILO_LOAD_ABORTED: in case the user decided to abort loading
+ *     - ELILO_LOAD_ERROR: there was an error during alloc/read
+ *
+ * Adapted from Bill Nottingham <notting@redhat.com>  patch for ELI.
+ */
+INTN
+load_initrd(CHAR16 *filename, memdesc_t *initrd)
+{
+       EFI_STATUS status;
+       VOID *start_addr = initrd->start_addr;
+       UINT64 size = 0;
+       UINTN pgcnt;
+       fops_fd_t fd;
+       INTN ret  = ELILO_LOAD_ERROR;
+       
+
+       if (filename == NULL || filename[0] == 0) return -1;
+
+       /* Open the file */
+       status = fops_open(filename, &fd);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"Open initrd file %s failed: %r", filename, status));
+               return -1;
+       }
+
+       DBG_PRT((L"initrd_open %s worked", filename));
+
+       /* warning: this function allocates memory  */
+       status = fops_infosize(fd, &size);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"Couldn't read initrd file %s info %r",filename, status));
+               goto error;
+       }
+       
+       /* round up to get required number of pages (4KB) */
+       initrd->pgcnt = pgcnt = EFI_SIZE_TO_PAGES(size);
+
+
+
+       start_addr = alloc_pages(pgcnt, EfiLoaderData, start_addr ? AllocateAddress : AllocateAnyPages, start_addr);
+       if (start_addr == NULL) {
+               ERR_PRT((L"Failed to allocate %d pages for initrd", pgcnt));
+               goto error;
+       }
+       VERB_PRT(2, Print(L"initrd: total_size: %ld bytes base: 0x%lx pages %d\n", 
+                       size, (UINT64)start_addr, pgcnt));
+
+       Print(L"Loading initrd %s...", filename);
+
+       ret = read_file(fd, size, start_addr);  
+
+       fops_close(fd);
+
+       if (ret != ELILO_LOAD_SUCCESS) {
+               ERR_PRT((L"read initrd(%s) failed: %d", filename, ret));
+               goto error;
+       }
+
+       Print(L"done\n");
+
+       initrd->start_addr = start_addr;
+
+       return ELILO_LOAD_SUCCESS;
+
+error:
+       if (start_addr) free(start_addr);
+
+       /*
+        * make sure nothing is passed to kernel
+        * in case of error.
+        */
+       initrd->start_addr = 0;
+       initrd->pgcnt      = 0;
+
+       return ret;
+}
+
+
diff --git a/loader.c b/loader.c
new file mode 100644 (file)
index 0000000..290531c
--- /dev/null
+++ b/loader.c
@@ -0,0 +1,68 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+#include "loader.h"
+
+extern loader_ops_t plain_loader;
+extern loader_ops_t gzip_loader;
+
+static loader_ops_t *ldops_list;
+
+loader_ops_t *
+loader_probe(CHAR16 *kname)
+{
+       loader_ops_t *ops;
+
+       for (ops= ldops_list; ops; ops = ops->next) {
+               if (ops->ld_probe(kname) == 0) {
+                       return ops;
+               }
+       }
+       return NULL;
+}
+
+INTN
+loader_register(loader_ops_t *ldops)
+{
+       if (ldops == NULL) return -1;
+
+       /* cheap sanity check */
+       if (ldops->next) {
+               ERR_PRT((L"loader %s is already registered", ldops->ld_name));
+               return -1;
+       }
+
+       ldops->next = ldops_list;
+       ldops_list  = ldops;
+
+       VERB_PRT(3, Print(L"New loader registered: %s\n", ldops->ld_name));
+
+       return 0;
+}
+
diff --git a/loader.h b/loader.h
new file mode 100644 (file)
index 0000000..0e127c7
--- /dev/null
+++ b/loader.h
@@ -0,0 +1,41 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __LOADER_H__
+#define __LOADER_H__
+
+#include "fileops.h"
+
+typedef struct __loader_ops_t {
+       struct __loader_ops_t *next;
+       CHAR16 *ld_name;
+       INTN (*ld_probe)(CHAR16 *kname);
+       INTN (*ld_load_kernel)(CHAR16 *kname, kdesc_t *kd);
+} loader_ops_t;
+
+extern loader_ops_t *loader_probe(CHAR16 *kname);
+extern INTN loader_register(loader_ops_t *ldops);
+
+#endif /* __LOADER_H__ */
diff --git a/strops.c b/strops.c
new file mode 100644 (file)
index 0000000..b47a58e
--- /dev/null
+++ b/strops.c
@@ -0,0 +1,165 @@
+/*
+ *  Copyright (C) 1999-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+//#define CHAR_NULL    (CHAR16)'\0'
+
+CHAR16 *
+StrChr(IN const CHAR16 *s, IN const CHAR16 c)
+{
+        for(; *s != c; ++s)  if (*s == CHAR_NULL) return NULL;
+
+        return (CHAR16 *)s;
+}
+
+CHAR16 *
+StrnCpy(OUT CHAR16 *dst, IN const CHAR16 *src, IN UINTN size)
+{
+       CHAR16 *res = dst;
+
+       while (size-- && (*dst++ = *src++) != CHAR_NULL);
+       /*
+        * does the null padding
+        */
+       while (size-- > 0) *dst++ = CHAR_NULL;
+
+       return res;
+}
+
+CHAR8 *
+StrnXCpy(OUT CHAR8 *dst, IN const CHAR16 *src, IN UINTN size)
+{
+       CHAR8 *res = dst;
+
+       while (size-- && (*dst++ = (CHAR8)*src++) != '\0');
+       /*
+        * does the null padding
+        */
+       while (size-- > 0) *dst++ = '\0';
+
+       return res;
+}
+       
+VOID
+U2ascii(CHAR16 *in, CHAR8 *out, UINTN maxlen)
+{
+       while(maxlen-- > 1 && (*out++ = *in++));
+       *out = '\0';
+}
+       
+CHAR8 *
+strncpya(OUT CHAR8 *dst, IN const CHAR8 *src, IN UINTN size)
+{
+       CHAR8 *res = dst;
+
+       while (size-- && (*dst++ = *src++) != '\0');
+       /*
+        * does the null padding
+        */
+       while (size-- > 0) *dst++ = '\0';
+
+       return res;
+}
+
+CHAR8 *
+strcpya(CHAR8 *dst, const CHAR8 *src)
+{
+       CHAR8 *tmp = dst;
+
+    while (*src) {
+        *(dst++) = *(src++);
+    }
+    *dst = 0;
+
+    return tmp;
+
+}
+
+CHAR8 *
+strchra(IN const CHAR8 *s, IN const CHAR8 c)
+{
+        for(; *s != c; ++s)   
+                if (*s == 0) return NULL;
+        return (CHAR8 *)s;
+}
+
+CHAR8 *
+strcata(IN CHAR8 *dst,IN CHAR8 *src)
+{   
+    return strcpya(dst+strlena(dst), src);
+}
+
+CHAR8 *
+strrchra(IN const CHAR8 *s, const INTN c)
+{
+  CHAR8 *found, *p, ch = (CHAR8)c;
+
+  /* Since strchr is fast, we use it rather than the obvious loop.  */
+
+  if (ch == '\0') return strchra(s, '\0');
+
+  found = NULL;
+  while ((p = strchra(s, ch)) != NULL)
+    {
+      found = p;
+      s = p + 1;
+    }
+
+  return (CHAR8 *) found;
+}
+
+CHAR8 *
+strtok_simple(CHAR8 *in, CHAR8 c)
+{
+       static CHAR8 *last;
+       CHAR8 *tmp;
+
+       if (in == NULL) in = last;
+
+       if (in == NULL) return NULL;
+
+       if (*in == c) in++;
+
+       tmp = strchra(in, c);
+       if (tmp) {
+               *tmp = '\0';
+               last = tmp+1;
+       } else {
+               last = NULL;
+       }
+       return in;
+}
+       
+VOID
+ascii2U(CHAR8 *in, CHAR16 *out, UINTN maxlen)
+{
+       while(maxlen-- > 1 && (*out++ = *in++));
+
+       *out = (CHAR16)0;
+}
+
+
diff --git a/strops.h b/strops.h
new file mode 100644 (file)
index 0000000..1084861
--- /dev/null
+++ b/strops.h
@@ -0,0 +1,41 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __STROPS_H__
+#define __STROPS_H__
+
+extern CHAR16 *StrChr(IN const CHAR16 *s, const CHAR16 c);
+extern CHAR16 *StrnCpy(OUT CHAR16 *dst, IN const CHAR16 *src, UINTN count);
+extern CHAR8 *StrnXCpy(OUT CHAR8 *dst, IN const CHAR16 *src, UINTN count);
+
+extern CHAR8 *strtok_simple(CHAR8 *in, CHAR8 c);
+extern CHAR8 *strrchra(IN const CHAR8 *s, const INTN c);
+extern CHAR8 *strcata(IN CHAR8 *dst,IN CHAR8 *src);
+extern CHAR8 *strchra(IN const CHAR8 *s, IN const CHAR8 c);
+extern CHAR8 *strcpya(CHAR8 *dst, const CHAR8 *src);
+extern CHAR8 *strncpya(OUT CHAR8 *dst, IN const CHAR8 *src, IN UINTN size);
+extern VOID  U2ascii(CHAR16 *in, CHAR8 *out, UINTN maxlen);
+
+#endif /* __LOADER_H__ */
diff --git a/sysdeps.h b/sysdeps.h
new file mode 100644 (file)
index 0000000..b72d106
--- /dev/null
+++ b/sysdeps.h
@@ -0,0 +1,35 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __ELILO_SYSDEPS_H__
+#define __ELILO_SYSDEPS_H__
+
+#ifdef CONFIG_ia64
+#include "ia64/sysdeps.h"
+#elif defined CONFIG_ia32
+#include "ia32/sysdeps.h"
+#endif
+
+#endif /* __ELILO_SYSDEPS_H__ */
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644 (file)
index 0000000..8858224
--- /dev/null
@@ -0,0 +1,47 @@
+#
+#  Copyright (C) 2001-2003 Hewlett-Packard Co.
+#      Contributed by Stephane Eranian <eranian@hpl.hp.com>
+#
+# This file is part of the ELILO, the EFI Linux boot loader.
+#
+#  ELILO is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2, or (at your option)
+#  any later version.
+#
+#  ELILO is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with ELILO; see the file COPYING.  If not, write to the Free
+#  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+#  02111-1307, USA.
+#
+# Please check out the elilo.txt for complete documentation on how
+# to use this program.
+#
+
+include ../Make.defaults
+include ../Make.rules
+
+TOPDIR=$(CDIR)/..
+
+FILES=eliloalt.o
+TARGET=eliloalt
+
+all: $(TARGET)
+
+#
+# redefine local rule (we build a Linux/ia64 binary here)
+#
+%.o: %.c 
+       $(CC) $(OPTIMFLAGS) $(DEBUGFLAGS) -c $< -o $@
+
+$(TARGET):  %:%.o 
+       $(CC) -o $@ $(OPTIMFLAGS) $(DEBUGFLAGS) $^
+
+clean:
+       $(RM) -f $(TARGET) $(FILES)
+
diff --git a/tools/eliloalt.c b/tools/eliloalt.c
new file mode 100644 (file)
index 0000000..1c9709a
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * eliloalt.c
+ *
+ * Copyright (C) 2002-2003 Hewlett-Packard Co
+ * Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ */
+
+/*
+ * This program is used to set the EliloAlt EFI variable to influence
+ * how elilo will behave at the next reboot. This variable is used
+ * to boot a certain kernel/configuration only once (debug, for instance).
+ *
+ * This is is supposed to be installed in /usr/sbin/eliloalt and must only
+ * be run by root.
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <dirent.h>
+
+
+#define ELILOALT_VERSION       "0.02"
+
+#define ELILO_ALT_NAME "EliloAlt"
+#define EFIVAR_DIR     "/proc/efi/vars"
+#define ELILO_ALTVAR   EFIVAR_DIR"/"ELILO_ALT_NAME"-00000000-0000-0000-0000-000000000000"
+
+#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
+#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
+#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
+
+typedef unsigned long efi_status_t;
+typedef uint8_t  efi_bool_t;
+typedef uint16_t efi_char16_t;         /* UNICODE character */
+
+/*
+ * EFI GUID type definition
+ */
+typedef struct {
+       uint32_t data1;
+       uint16_t data2;
+       uint16_t data3;
+       uint8_t  data4[8];
+} efi_guid_t;
+
+/*
+ * EFI variable structure 
+ */
+typedef struct _efi_variable_t {
+        efi_char16_t   variablename[1024/sizeof(efi_char16_t)];
+        efi_guid_t     vendorguid;
+        uint64_t       datasize;
+        uint8_t         data[1024];
+       efi_status_t    status;
+        uint32_t        attributes;
+} __attribute__((packed)) efi_variable_t;
+
+static char *elilo_alt_name = ELILO_ALT_NAME;
+
+static struct option cmd_options[]={
+       { "version", 0, 0, 1},
+       { "help", 0, 0, 2},
+       { "delete", 0, 0, 3},
+       { "print", 0, 0, 4},
+       { "set", 1, 0, 5},
+       { 0, 0, 0, 0}
+};
+
+static void fatal_error(char *fmt,...) __attribute__((noreturn));
+
+static void
+fatal_error(char *fmt, ...) 
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+
+       exit(1);
+}
+
+
+static void
+usage(char **argv)
+{
+       printf("Usage: %s [OPTIONS] cmdline\n", argv[0]);
+
+       printf( "-h, --help\t\tdisplay this help and exit\n"
+               "--version\t\toutput version information and exit\n"
+               "-s, --set cmdline\tset elilo alternate variable to cmdline\n"
+               "-p, --print\t\tprint elilo alternate variable\n"
+               "-d, --delete\t\tprint elilo alternate variable\n"
+       );
+}
+
+static char *
+check_proc_efi(int find_entry)
+{
+       DIR *efi_vars;
+       struct dirent *entry;
+       static char name[1024];
+
+       if (getuid() != 0) {
+               fatal_error("This program must be run as root\n");
+       }
+       efi_vars = opendir(EFIVAR_DIR);
+       if (efi_vars == NULL) {
+               fatal_error("Cannot access %s\n", EFIVAR_DIR);
+       }
+       if (!find_entry) {
+               closedir(efi_vars);
+               return NULL;
+       }
+       /* Find one entry we can open */
+       while ((entry = readdir(efi_vars)) != NULL) {
+               if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
+                       break;
+       }
+       if (entry == NULL) {
+               fatal_error("Cannot find entry in %s\n", EFIVAR_DIR);
+       }
+       sprintf(name, "%s/%s", EFIVAR_DIR, entry->d_name);
+       closedir(efi_vars);
+       return name;
+}
+
+static void
+delete_var(void)
+{
+       efi_variable_t var;
+       int fd, r, i;
+
+       check_proc_efi(0);
+
+       fd = open(ELILO_ALTVAR, O_WRONLY);
+       if (fd == -1) {
+               fatal_error("variable not defined\n");
+       }
+
+       memset(&var, 0, sizeof(var));
+
+       for (i=0; i < sizeof(elilo_alt_name); i++) {
+               var.variablename[i] = (efi_char16_t)elilo_alt_name[i];
+       }
+
+       /* 
+        * we use NULL GUID so no need to initialize it now memset() did it
+        * writing with a datasize=0 will effectively delete the variable.
+        */
+       
+       r = write(fd, &var, sizeof(var));
+       if (r != sizeof(var)) {
+               fatal_error("Variable %s defined but invalid content\n", ELILO_ALTVAR);
+       }
+       close(fd);
+}
+
+
+static void
+print_var(void)
+{
+       efi_variable_t var;
+       int fd, r, i;
+
+
+       check_proc_efi(0);
+
+       fd = open(ELILO_ALTVAR, O_RDONLY);
+       if (fd == -1) {
+               fatal_error("variable not defined\n");
+       }
+
+       memset(&var, 0, sizeof(var));
+
+       r = read(fd, &var, sizeof(var));
+       if (r != sizeof(var)) {
+               fatal_error("Variable %s defined but invalid content\n", ELILO_ALTVAR);
+       }
+       printf("EliloAlt=\"");
+       for(i=0; i < var.datasize; i+=1){
+               printf("%c", var.data[i]);
+       }
+       printf("\"\n");
+       close(fd);
+}
+
+static void
+set_var(char *cmdline)
+{
+       efi_variable_t var;
+       int fd, r, i, j, l;
+       char *name;
+
+       name = check_proc_efi(1);
+
+       if (cmdline == NULL) {
+               fatal_error("invalid cmdline argument\n");
+       }
+
+       l = strlen(cmdline);
+
+       if (l >= 1024) {
+               fatal_error("Variable content is too long, must be <= 512 characters\n");
+       }
+
+       fd = open(name, O_WRONLY);
+       if (fd == -1) {
+               fatal_error("can't open %s: %s\n", ELILO_ALTVAR, strerror(errno));
+       }
+
+       memset(&var, 0, sizeof(var));
+
+       for (i=0; i < sizeof(elilo_alt_name); i++) {
+               var.variablename[i] = (efi_char16_t)elilo_alt_name[i];
+       }
+
+       for (i=0, j=0; i < l; i++, j+=2) {
+               var.data[j] = (efi_char16_t)cmdline[i];
+       }
+       /* +2 = include char16 for null termination */
+       var.datasize   = j+2;
+
+       var.attributes =  EFI_VARIABLE_NON_VOLATILE 
+                       | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
+
+       /* 
+        * we use NULL GUID so no need to initialize it now memset() did it
+        * writing with a datasize=0 will effectively delete the variable.
+        */
+       
+       r = write(fd, &var, sizeof(var));
+       if (r != sizeof(var)) {
+               fatal_error("Variable %s defined but invalid content %d\n", ELILO_ALTVAR, r);
+       }
+       close(fd);
+
+}
+
+int
+main(int argc, char **argv)
+{
+       int c;
+
+       while ((c=getopt_long(argc, argv,"hdps:", cmd_options, 0)) != -1) {
+               switch(c) {
+                       case   0: continue; /* fast path for options */
+                       case   1:
+                               printf("Version %s Date: %s\n", ELILOALT_VERSION, __DATE__);
+                               exit(0);
+                       case   2:
+                       case 'h':
+                               usage(argv);
+                               exit(0);
+                       case   3:
+                       case 'd':
+                               delete_var();
+                               exit(0);
+                       case   4:
+                       case 'p':
+                               print_var();
+                               exit(0);
+                       case   5:
+                       case 's':
+                               set_var(optarg);
+                               exit(0);
+                       default:
+                               fatal_error("Unknown option\n");
+               }
+       }
+       print_var();
+       return 0;
+}
diff --git a/util.c b/util.c
new file mode 100644 (file)
index 0000000..85babf4
--- /dev/null
+++ b/util.c
@@ -0,0 +1,503 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ *  Copyright (C) 2001 Silicon Graphics, Inc.
+ *     Contributed by Brent Casavant <bcasavan@sgi.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "elilo.h"
+
+#define TENTH_SEC              1000000         /* 1/10th second in 100ns unit */
+#define READ_BLOCK_SIZE                (4*EFI_PAGE_SIZE) /* block size for read_file */
+
+#define is_cr(k)               (((k)==CHAR_LINEFEED)||((k)==CHAR_CARRIAGE_RETURN))
+#define CHAR_SPACE             L' '
+
+static INTN
+read_keypress(EFI_INPUT_KEY *key)
+{
+       return systab->ConIn->ReadKeyStroke(systab->ConIn, key);
+}
+
+
+EFI_STATUS
+check_abort(VOID)
+{
+       EFI_INPUT_KEY key;
+
+       return read_keypress(&key);
+}
+
+inline VOID
+reset_input(VOID)
+{
+       systab->ConIn->Reset(systab->ConIn, 1);
+}
+
+#if 0
+INTN
+wait_keypress_abort(VOID)
+{
+       SIMPLE_INPUT_INTERFACE *conin = systab->ConIn;
+       EFI_INPUT_KEY key;
+       EFI_STATUS status;
+
+       reset_input();
+
+       Print(L"Hit ENTER to continue or ANY other key to cancel");
+
+       /* cleanup buffer first */
+       while (conin->ReadKeyStroke(conin, &key) == EFI_SUCCESS);
+
+       while ((status=conin->ReadKeyStroke(conin, &key)) == EFI_NOT_READY );
+
+       if (EFI_ERROR(status)) return ELILO_LOAD_ERROR;
+
+       Print(L"\n");
+
+       return is_cr(key.UnicodeChar) ? ELILO_LOAD_SUCCESS: ELILO_BOOT_ABORTED;
+}
+#endif
+
+/*
+ * wait for timeout to expire or keypress
+ * Return:
+ *     0 : timeout expired
+ *     1 : a key was pressed (still input stream to process)
+ *     -1: an error occured
+ */
+INTN
+wait_timeout(UINTN timeout)
+{
+               EFI_STATUS status;
+       EFI_EVENT timer;
+       EFI_EVENT list[2];
+       UINTN idx;
+
+
+       if (timeout == 0) return 0;
+
+       /* Create a timeout timer */
+       status = BS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &timer);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L" waitkey CreateEvent failed %r", status));
+               return -1;
+       }
+       /* In 100ns increments */
+       status = BS->SetTimer(timer, TimerPeriodic, TENTH_SEC);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"waitkey SetTimer failed %r", status));
+               return -1;
+       }
+
+       list[0] = timer;
+       list[1] = systab->ConIn->WaitForKey;
+
+       do {
+               status = BS->WaitForEvent(2, list, &idx);
+               if (EFI_ERROR(status)) {
+                       ERR_PRT((L"waitkey WaitForEvent failed %r", status));
+                       return -1;
+               }
+
+       } while (timeout-- && idx == 0);        
+
+       /*
+        * SetTimer(timer, TimerCancel, 0) is causing problems on IA-32 and gcc3
+        * I do not know why it dies with EFI12.35. So let's fake a key stroke.
+        */
+       status = BS->SetTimer(timer, TimerCancel, 0);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"waitkey SetTimer(TimerCancel) failed %r", status));
+               return -1;
+       }
+
+       BS->CloseEvent(timer);
+
+       return idx ? 1 : 0;
+}
+
+INTN
+argify(CHAR16 *buf, UINTN len, CHAR16 **argv)   
+{
+
+        UINTN     i=0, j=0;
+        CHAR16   *p = buf;
+       
+        if (buf == 0) { 
+               argv[0] = NULL;
+               return 0;
+       }
+       /* len represents the number of bytes, not the number of 16 bytes chars */
+       len = len >> 1;
+
+       /*
+        * Here we use CHAR_NULL as the terminator rather than the length
+        * because it seems like the EFI shell return rather bogus values for it.
+        * Apparently, we are guaranteed to find the '\0' character in the buffer
+        * where the real input arguments stop, so we use it instead.
+        */
+       for(;;) {
+               while (buf[i] == CHAR_SPACE && buf[i] != CHAR_NULL && i < len) i++;
+
+               if (buf[i] == CHAR_NULL || i == len) goto end;
+
+               p = buf+i;
+               i++;
+
+               while (buf[i] != CHAR_SPACE && buf[i] != CHAR_NULL && i < len) i++;
+
+               argv[j++] = p;
+
+               if (buf[i] == CHAR_NULL) goto end;
+
+               buf[i]  = CHAR_NULL;
+
+               if (i == len)  goto end;
+
+               i++;
+
+               if (j == MAX_ARGS-1) {
+                       ERR_PRT((L"too many arguments (%d) truncating", j));
+                       goto end;
+               }
+       }
+end:
+#if 0
+       if (i != len) {
+               ERR_PRT((L"ignoring trailing %d characters on command line", len-i));
+       }
+#endif
+        argv[j] = NULL;
+       return j;
+}
+
+VOID
+unargify(CHAR16 **argv, CHAR16 **args)
+{
+       if ( *argv == 0 ) {
+               *args = L"";
+               return;
+       }
+       *args = *argv;
+       while ( argv[1] ) {
+               (*argv)[StrLen(*argv)] = CHAR_SPACE;
+               argv++;
+       }
+}
+
+VOID
+split_args(CHAR16 *buffer, CHAR16 *kname, CHAR16 *args)
+{
+       CHAR16 *tmp;
+
+       /* find beginning of kernel name */
+       while (*buffer && *buffer == CHAR_SPACE) buffer++;
+
+       tmp = buffer;
+       
+       /* scan through kernel name */  
+       while (*buffer && *buffer != CHAR_SPACE) buffer++;
+
+       if (*buffer) {
+               *buffer++ = CHAR_NULL;
+               StrCpy(kname, tmp);
+       }
+
+       /* skip space between kernel and args */        
+       while (*buffer && *buffer == CHAR_SPACE) buffer++;
+
+       StrCpy(args, buffer);
+}
+
+INTN
+read_file(UINTN fd, UINTN total_size, CHAR8 *buffer)
+{
+       INTN size, j=0;
+       EFI_STATUS status;
+       CHAR16 helicopter[4] = { L'|' , L'/' , L'-' , L'\\' };
+       INTN ret = ELILO_LOAD_SUCCESS;
+       UINTN sum = 0;
+       /*
+        * We load by chunks rather than a single big read because
+        * early versions of EFI had troubles loading files
+        * from floppies in a single big request.  Breaking
+        * the read down into chunks of 4KB fixed that
+        * problem. While this problem has been fixed, we still prefer
+        * this method because it tells us whether or not we're making
+        * forward progress.
+        */
+
+       while (total_size > 0) {
+               size = total_size < READ_BLOCK_SIZE? total_size : READ_BLOCK_SIZE;
+
+               status = fops_read(fd, buffer, &size);
+               if (EFI_ERROR(status)) {
+                       ERR_PRT((L"read_file failed %r", status));
+                       return ELILO_LOAD_ERROR;
+               }
+               sum += size;
+
+               Print(L"%c\b",helicopter[j++%4]);
+
+               buffer     += size;
+               total_size -= size; 
+
+               if (check_abort() == EFI_SUCCESS) {
+                       ret = ELILO_LOAD_ABORTED;
+                       break;
+               }
+       }
+       return ret;
+}
+
+INTN
+get_memmap(mmap_desc_t *desc)
+{
+#define        ELILO_MEMMAP_SIZE_DEFAULT       EFI_PAGE_SIZE
+#define        ELILO_MEMMAP_INC                (sizeof(EFI_MEMORY_DESCRIPTOR)<<1)
+
+       EFI_STATUS status;
+
+       desc->map_size = ELILO_MEMMAP_SIZE_DEFAULT;
+
+       for(;;) {
+               desc->md = (EFI_MEMORY_DESCRIPTOR *)alloc(desc->map_size, EfiLoaderData);
+
+               if (desc->md == NULL) {
+                       ERR_PRT((L"failed to allocate memory map buffer"));
+                       return -1;
+               }
+               status = (*BS->GetMemoryMap)(&desc->map_size, desc->md, &desc->cookie, &desc->desc_size, &desc->desc_version);
+               if (status == EFI_SUCCESS) break;
+
+               free(desc->md);
+
+               if (status != EFI_BUFFER_TOO_SMALL) {
+                       ERR_PRT((L"failed to obtain memory map %r"));
+                       return -1;
+               }
+               desc->map_size += ELILO_MEMMAP_INC;
+       }
+       DBG_PRT((L"final get_memmap map_size=%ld", desc->map_size));
+
+       return 0;
+}
+
+#if 0
+INTN
+get_memmap(mmap_desc_t *desc)
+{
+       EFI_STATUS status;
+
+       /* will get the right size in return */
+       desc->map_size = 0;
+
+       status = BS->GetMemoryMap(&desc->map_size, desc->md, &desc->cookie, &desc->desc_size, &desc->desc_version);
+       if (status != EFI_BUFFER_TOO_SMALL) return -1;
+
+       desc->md = (EFI_MEMORY_DESCRIPTOR *)alloc(desc->map_size, EfiLoaderData);
+       if (desc->md == NULL) {
+               ERR_PRT((L"failed to allocate memory map buffer"));
+               return -1;
+       }
+
+
+       status = BS->GetMemoryMap(&desc->map_size, desc->md, &desc->cookie, &desc->desc_size, &desc->desc_version);
+       if (EFI_ERROR(status)) {
+               ERR_PRT((L"failed to obtain memory map %d: %r", desc->map_size, status));
+               free(desc->md);
+               return -1;
+       }
+       DBG_PRT((L"final get_memmap map_size=%d", desc->map_size));
+
+       return 0;
+}
+#endif
+
+
+VOID
+free_memmap(mmap_desc_t *desc)
+{
+       if (desc->md) {
+               free(desc->md);
+               desc->md = NULL;
+       }
+}
+
+VOID
+print_memmap(mmap_desc_t *desc)
+{
+       EFI_MEMORY_DESCRIPTOR *md;
+       UINTN desc_size;
+       VOID *p;
+       VOID *md_end;
+       INT8 printed;
+       UINTN ntypes;
+       CHAR16* str;
+
+       static CHAR16 *memtypes[]={
+               L"ReservedMemoryType",
+               L"LoaderCode",
+               L"LoaderData",
+               L"BootServicesCode",
+               L"BootServicesData",
+               L"RuntimeServicesCode",
+               L"RuntimeServicesData",
+               L"ConventionalMemory",
+               L"UnusableMemory",
+               L"ACPIReclaimMemory",
+               L"ACPIMemoryNVS",
+               L"MemoryMappedIO",
+               L"MemoryMappedIOPortSpace",
+               L"PalCode"
+       };
+
+
+       md_end = ((VOID *)desc->md)+desc->map_size;
+       desc_size = desc->desc_size;
+
+       ntypes = sizeof(memtypes)/sizeof(CHAR16 *);
+
+       for(p = desc->md; p < md_end; p += desc_size) {
+               md = p;
+
+               str = md->Type < ntypes ? memtypes[md->Type] : L"Unknown";
+
+               Print(L"%24s %lx-%lx %8lx", str, md->PhysicalStart,
+                               md->PhysicalStart+(md->NumberOfPages<<EFI_PAGE_SHIFT),
+                               md->NumberOfPages);
+
+               printed=0;
+#define P_FLG(f)       { \
+       Print(L" %s %s", printed ? L"|":L"", f); \
+       printed=1; \
+}
+
+               if (md->Attribute & EFI_MEMORY_UC) {
+                       P_FLG(L"UC");
+               }
+               if (md->Attribute & EFI_MEMORY_WC) {
+                       P_FLG(L"WC");
+               }
+               if (md->Attribute & EFI_MEMORY_WT) {
+                       P_FLG(L"WT");
+               }
+               if (md->Attribute & EFI_MEMORY_WB) {
+                       P_FLG(L"WB");
+               }
+               if (md->Attribute & EFI_MEMORY_UCE) {
+                       P_FLG(L"UCE");
+               }
+               if (md->Attribute & EFI_MEMORY_WP) {
+                       P_FLG(L"WP");
+               }
+               if (md->Attribute & EFI_MEMORY_RP) {
+                       P_FLG(L"RP");
+               }
+               if (md->Attribute & EFI_MEMORY_XP) {
+                       P_FLG(L"XP");
+               }
+               if (md->Attribute & EFI_MEMORY_RUNTIME) {
+                       P_FLG(L"RT");
+               }
+               Print(L"\n");
+       }
+}
+
+INTN
+find_kernel_memory(VOID* low_addr, VOID* max_addr, UINTN alignment, VOID** start)
+{       
+#define HIGHEST_ADDR (VOID*)(~0)
+        mmap_desc_t mdesc;
+        EFI_MEMORY_DESCRIPTOR *md;
+       UINT64 size;
+       VOID *p, *addr;
+       VOID *desc_end, *md_end, *best_addr = HIGHEST_ADDR;
+
+       /*
+        * first get up-to-date memory map
+        *
+        * XXX: is there a danger of not seeing the latest version if interrupted
+        * during our scan ?
+        *
+        */
+        if (get_memmap(&mdesc) == -1) {
+                ERR_PRT((L"find_kernel_memory :GetMemoryMap() failed"));
+                return -1;
+        }
+
+       desc_end = ((VOID *)mdesc.md) + mdesc.map_size;
+       size     = max_addr - low_addr;
+        /*
+        * Find memory which covers the desired range
+        */
+       for(p = mdesc.md; p < desc_end; p += mdesc.desc_size) {
+               md = p;
+
+               /*
+                * restrict to decent memory types. 
+                *
+                * the EFI memory map report where memory is and how it is currently used
+                * using types.
+                *
+                * EfiLoaderData which is used by the AllocatePages() cannot be used
+                * here because it may hold some valid information. Same thing for most
+                * of the memory types with the exception of EfiConventional which 
+                * can be assumed as being free to use. 
+                */
+               if (md->Type != EfiConventionalMemory) continue;
+
+               /* 
+                * compute aligned address and upper boundary for range
+                */
+               md_end = (VOID*)(md->PhysicalStart + md->NumberOfPages * EFI_PAGE_SIZE);        
+               addr   = (VOID*)ROUNDUP(md->PhysicalStart, alignment);
+
+               /*
+                * need to check if:
+                * - aligned address still in the range
+                * - the range [addr-addr+size) still fits into memory range
+                * if so we have a match. We do not assume that the memory ranges
+                * are sorted by EFI, therefore we must record the match and only
+                * keep the lowest possible one.
+                */
+               if (addr < best_addr && addr < md_end && addr+size <= md_end) best_addr = addr;
+        }
+        if (best_addr == HIGHEST_ADDR) {
+                free_memmap(&mdesc);
+                ERR_PRT((L"Could not find memory suitable for loading image"));
+                return -1;
+        }
+
+        *start = best_addr;
+
+        free_memmap(&mdesc);
+
+        return 0;
+}
+
diff --git a/vars.c b/vars.c
new file mode 100644 (file)
index 0000000..a709007
--- /dev/null
+++ b/vars.c
@@ -0,0 +1,130 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ *  Copyright (C) 2001 Silicon Graphics, Inc.
+ *     Contributed by Brent Casavant <bcasavan@sgi.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  ELILO is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  ELILO is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with ELILO; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+#include <efi.h>
+#include <efilib.h>
+
+/*
+ * A variable name is 1 character long and case sensitive. So 
+ * we actually have 52 (26*2) possible variables.
+ */
+#define MAX_VARIABLES          (26<<1)
+#define MAX_VARIABLE_LENGTH    128
+#define VAR_IDX(a)             (((a) >= 'a' && (a) <= 'z') ? 26-'a'+(a) : (a)-'A')
+#define IDX_VAR(i)             ((i) < 26 ? 'A'+(i) : 'a'+ ((i)-26))
+
+typedef struct {
+       CHAR16  value[MAX_VARIABLE_LENGTH];
+} elilo_var_t;
+
+static elilo_var_t vars[MAX_VARIABLES];        /* set of variables */
+
+INTN
+set_var(CHAR16 v, CHAR16 *value)
+{
+       /* invalid variable name */
+       if (v < 'A' || (v > 'Z' && v < 'a') || v > 'z') return -1;
+
+       StrCpy(vars[VAR_IDX(v)].value, value);
+       return 0;
+}
+
+CHAR16 *
+get_var(CHAR16 v)
+{
+       /* invalid variable name */
+       if (v < L'A' || (v > L'Z' && v < L'a') || v > L'z') return NULL;
+
+       return vars[VAR_IDX(v)].value;
+}
+
+
+VOID
+print_vars(VOID)
+{
+       INTN i;
+       UINTN cnt = 0;
+
+       for(i=0; i < MAX_VARIABLES; i++) {
+               if (vars[i].value[0]) {
+                       cnt++;
+                       Print(L"%c = \"%s\"\n", IDX_VAR(i), vars[i].value);
+               }
+       }
+       if (cnt == 0) Print(L"no variable defined\n");
+}
+
+
+INTN
+subst_vars(CHAR16 *in, CHAR16 *out, INTN maxlen)
+{
+       /* 
+        * we cannot use \\ for the despecialization character because
+        * it is also used as a path separator in EFI.
+        */
+#define DSPEC_CHAR     L'&'
+       INTN i, l, j, cnt;
+       INTN m = 0, d = 0;
+       CHAR16 *val;
+
+       if (in == NULL || out == NULL || maxlen <= 1) return -1;
+
+       l = StrLen(in);
+
+       maxlen--;
+
+       for (i=0, j=0;i < l; i++) {
+               cnt = 1;
+               val = in+i;
+
+               if (*val == DSPEC_CHAR  && d == 0) {
+                       d = 1; 
+                       continue;
+               }
+               if(m == 1) {
+                   m = 0;
+                   val = get_var(*val);
+
+                   if (val == NULL) continue;
+
+                   cnt = StrLen(val);
+
+               } else if (*val == L'%' && d == 0) {
+                       m = 1;
+                       continue;
+               }
+               d = 0;
+               while (j < maxlen && cnt) {
+                       out[j++] = *val++;
+                       cnt--;
+               }
+               if (j == maxlen) break;
+       }
+       out[j] = CHAR_NULL;
+
+       return 0;
+}
diff --git a/vars.h b/vars.h
new file mode 100644 (file)
index 0000000..48d2fea
--- /dev/null
+++ b/vars.h
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (C) 2001-2003 Hewlett-Packard Co.
+ *     Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ *  Copyright (C) 2001 Silicon Graphics, Inc.
+ *      Contributed by Brent Casavant <bcasavan@sgi.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ *  GNU EFI is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  GNU EFI is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU EFI; see the file COPYING.  If not, write to the Free
+ *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ */
+
+#ifndef __ELILO_VARS_H__
+#define __ELILO_VARS_H__
+/*
+ * This file contains the list of defined variables.
+ * It is expected that every module which uses a variable add its entry
+ * here.
+ * The syntax for the name is: VAR_modulename_meaning L'X'
+ * where:
+ *     - modulename: a string representing the module that uses the variable
+ *     - meaning   : a string representing the meaning of the variable for the module
+ *     - X         : the variable name [A-Za-z]
+ */
+
+/* from glue_netfs.c */
+#define VAR_NETFS_IPADDR       L'I'    /* the IP address obtained by DHCP/PXE */
+#define VAR_NETFS_NETMASK      L'M'    /* the netmask obtained by DHCP/PXE */
+#define VAR_NETFS_GATEWAY      L'G'    /* the gateway obtained by DHCP/PXE */
+#define VAR_NETFS_HOSTNAME     L'H'    /* the hostname obtained by DHCP/PXE */
+#define VAR_NETFS_DOMAINAME    L'D'    /* the domain name obtained by DHCP/PXE */
+
+extern INTN set_var(CHAR16 v, CHAR16 *value);
+extern CHAR16 * get_var(CHAR16 v);
+extern VOID print_vars(VOID);
+extern INTN subst_vars(CHAR16 *in, CHAR16 *out, INTN maxlen);
+
+#endif /* __ELILO_VARS_H__ */
+