From fb6ce0d5962fab9975440f61a502d2c230364d9a Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Mon, 23 Feb 2004 08:11:30 -0500 Subject: [PATCH] release 3.4 https://sourceforge.net/projects/elilo/files/elilo/elilo-3.4/ --- ChangeLog | 338 ++++ LIMITATIONS | 3 + Make.defaults | 131 ++ Make.rules | 35 + Makefile | 103 ++ README | 74 + README.gnu-efi | 31 + TODO | 18 + alloc.c | 240 +++ alternate.c | 118 ++ bootparams.c | 123 ++ chooser.c | 114 ++ chooser.h | 60 + choosers/Makefile | 58 + choosers/simple.c | 410 +++++ choosers/simple.h | 35 + choosers/textmenu.c | 528 ++++++ choosers/textmenu.h | 35 + config.c | 1171 +++++++++++++ config.h | 56 + devschemes/Makefile | 46 + devschemes/simple.c | 152 ++ docs/devschemes.txt | 106 ++ docs/edd30.txt | 102 ++ docs/elilo.txt | 478 ++++++ docs/eliloalt.txt | 77 + docs/elilovars.txt | 53 + docs/fpswa.txt | 22 + docs/netbooting.txt | 396 +++++ docs/simple_chooser.txt | 156 ++ docs/textmenu_chooser.txt | 61 + elf.h | 632 +++++++ elilo-ia32.efi | Bin 0 -> 159859 bytes elilo-ia64.efi | Bin 0 -> 348238 bytes elilo.c | 654 ++++++++ elilo.h | 201 +++ elilo_debug.h | 44 + examples/netboot/dhcpd-pxe.conf | 188 +++ examples/netboot/dhcpd.conf | 14 + examples/textmenu_chooser/elilo-textmenu.conf | 45 + examples/textmenu_chooser/general.msg | 25 + examples/textmenu_chooser/params.msg | 25 + .../textmenu_chooser/textmenu-message.msg | 25 + fileops.c | 628 +++++++ fileops.h | 123 ++ fs/Makefile | 70 + fs/ext2_fs.h | 626 +++++++ fs/ext2_fs_i.h | 42 + fs/ext2_fs_sb.h | 61 + fs/ext2_private.h | 70 + fs/ext2fs.c | 1004 +++++++++++ fs/ext2fs.h | 63 + fs/fs.h | 1480 +++++++++++++++++ fs/localfs.c | 297 ++++ fs/localfs.h | 46 + fs/netfs.c | 790 +++++++++ fs/netfs.h | 68 + getopt.c | 99 ++ getopt.h | 33 + glue_ext2fs.c | 73 + glue_ext2fs.h | 33 + glue_localfs.c | 173 ++ glue_localfs.h | 33 + glue_netfs.c | 242 +++ glue_netfs.h | 33 + gnu-efi-3.0a-ia32.patch | 19 + ia32/Makefile | 51 + ia32/bin_to_h.c | 27 + ia32/config.c | 103 ++ ia32/private.h | 30 + ia32/rmswitch.S | 118 ++ ia32/sysdeps.h | 440 +++++ ia32/system.c | 798 +++++++++ ia64/Makefile | 42 + ia64/config.c | 154 ++ ia64/fpswa.c | 167 ++ ia64/gzip.c | 651 ++++++++ ia64/gzip.h | 35 + ia64/gzip_loader.c | 79 + ia64/inflate.c | 1204 ++++++++++++++ ia64/longjmp.S | 162 ++ ia64/memcpy.S | 339 ++++ ia64/memset.S | 133 ++ ia64/plain_loader.c | 453 +++++ ia64/private.h | 35 + ia64/setjmp.S | 170 ++ ia64/setjmp.h | 28 + ia64/sysdeps.h | 107 ++ ia64/system.c | 137 ++ initrd.c | 112 ++ loader.c | 68 + loader.h | 41 + strops.c | 165 ++ strops.h | 41 + sysdeps.h | 35 + tools/Makefile | 47 + tools/eliloalt.c | 298 ++++ util.c | 503 ++++++ vars.c | 130 ++ vars.h | 55 + 100 files changed, 20247 insertions(+) create mode 100644 ChangeLog create mode 100644 LIMITATIONS create mode 100644 Make.defaults create mode 100644 Make.rules create mode 100644 Makefile create mode 100644 README create mode 100644 README.gnu-efi create mode 100644 TODO create mode 100644 alloc.c create mode 100644 alternate.c create mode 100644 bootparams.c create mode 100644 chooser.c create mode 100644 chooser.h create mode 100644 choosers/Makefile create mode 100644 choosers/simple.c create mode 100644 choosers/simple.h create mode 100644 choosers/textmenu.c create mode 100644 choosers/textmenu.h create mode 100644 config.c create mode 100644 config.h create mode 100644 devschemes/Makefile create mode 100644 devschemes/simple.c create mode 100644 docs/devschemes.txt create mode 100644 docs/edd30.txt create mode 100644 docs/elilo.txt create mode 100644 docs/eliloalt.txt create mode 100644 docs/elilovars.txt create mode 100644 docs/fpswa.txt create mode 100644 docs/netbooting.txt create mode 100644 docs/simple_chooser.txt create mode 100644 docs/textmenu_chooser.txt create mode 100644 elf.h create mode 100755 elilo-ia32.efi create mode 100755 elilo-ia64.efi create mode 100644 elilo.c create mode 100644 elilo.h create mode 100644 elilo_debug.h create mode 100644 examples/netboot/dhcpd-pxe.conf create mode 100644 examples/netboot/dhcpd.conf create mode 100644 examples/textmenu_chooser/elilo-textmenu.conf create mode 100644 examples/textmenu_chooser/general.msg create mode 100644 examples/textmenu_chooser/params.msg create mode 100644 examples/textmenu_chooser/textmenu-message.msg create mode 100644 fileops.c create mode 100644 fileops.h create mode 100644 fs/Makefile create mode 100644 fs/ext2_fs.h create mode 100644 fs/ext2_fs_i.h create mode 100644 fs/ext2_fs_sb.h create mode 100644 fs/ext2_private.h create mode 100644 fs/ext2fs.c create mode 100644 fs/ext2fs.h create mode 100644 fs/fs.h create mode 100644 fs/localfs.c create mode 100644 fs/localfs.h create mode 100644 fs/netfs.c create mode 100644 fs/netfs.h create mode 100644 getopt.c create mode 100644 getopt.h create mode 100644 glue_ext2fs.c create mode 100644 glue_ext2fs.h create mode 100644 glue_localfs.c create mode 100644 glue_localfs.h create mode 100644 glue_netfs.c create mode 100644 glue_netfs.h create mode 100644 gnu-efi-3.0a-ia32.patch create mode 100644 ia32/Makefile create mode 100644 ia32/bin_to_h.c create mode 100644 ia32/config.c create mode 100644 ia32/private.h create mode 100644 ia32/rmswitch.S create mode 100644 ia32/sysdeps.h create mode 100644 ia32/system.c create mode 100644 ia64/Makefile create mode 100644 ia64/config.c create mode 100644 ia64/fpswa.c create mode 100644 ia64/gzip.c create mode 100644 ia64/gzip.h create mode 100644 ia64/gzip_loader.c create mode 100644 ia64/inflate.c create mode 100644 ia64/longjmp.S create mode 100644 ia64/memcpy.S create mode 100644 ia64/memset.S create mode 100644 ia64/plain_loader.c create mode 100644 ia64/private.h create mode 100644 ia64/setjmp.S create mode 100644 ia64/setjmp.h create mode 100644 ia64/sysdeps.h create mode 100644 ia64/system.c create mode 100644 initrd.c create mode 100644 loader.c create mode 100644 loader.h create mode 100644 strops.c create mode 100644 strops.h create mode 100644 sysdeps.h create mode 100644 tools/Makefile create mode 100644 tools/eliloalt.c create mode 100644 util.c create mode 100644 vars.c create mode 100644 vars.h diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..07aba8e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,338 @@ +2003-08-20 Stephane Eranian + * released 3.4 +2003-08-19 Stephane Eranian + * integrated ia32 updates from Matt + Tolentino +2003-08-13 Stephane Eranian + * 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 + * 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 + * 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 + * added check on dev_tab in fs/*fs.c:*_uninstall() +2003-02-07 Stephane Eranian + * 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 + * 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 + * fix bug in glue_netfs.c convert_ip2decstr() which caused some IP + addresses to be incorrectly converted to strings. +2002-11-01 Stephane Eranian + * fix bug in -r option for IA64. There is no argument to this option. +2002-10-15 Stephane Eranian + * 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 + * applied patch from Andreas Schwab to eliloalt.c. + eliloalt dynamically selects a variable in /proc/efi/vars. +2002-09-12 Stephane Eranian + * removed extra free() from fs/ext2fs.c:ext2fs_init_state(). + Bug report and fix by NOMURA Jun'ichi + * 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 + * on IA-64 forbid the use of f32-f127 by the compiler (EFI spec) +2002-09-10 Stephane Eranian + * fix a bug in argify() that was causing an EFI assertion + when aborting at the elilo prompt when netbooted. +2002-08-26 Stephane Eranian + * 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 + * added fpswa.txt in the docs directory + * updated elilo.txt +2002-08-15 Stephane Eranian + * 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 + * Changed the despecialization character for the variables from \\ to & + to avoid conflicts with \\ as a path separator +2002-06-11 Stephane Eranian + * fixed the return value in efi_main(). elilo was always returning + success even in case of failure. Bug reported by Egan Ford + * applied patch from Richard Hirst to fix an + initialization bug in choosers/textmenu.c + * applied patch from Richard Hirst 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 + * 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 + * 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 and Chris Ahna + * major update to ia32 support: can now boot 2.4.x, and 2.2.x kernels +2002-02-20 Stephane Eranian + * fixed missing netfs_fd_free() in case of file not found in netfs.c +2002-02-19 Stephane Eranian + * 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 + * added support for message= option to simple chooser + * added support for description= option to simple chooser +2002-02-13 Stephane Eranian + * 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 () + * 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 + * cleaned up alternate.c + * added support for ctrl-U (clear line) in chooser/simple.c + +2002-01-25 Stephane Eranian + * added support for architecture specific config file (elilo-ia64.conf, elilo-ia32.conf). + +2002-01-13 Stephane Eranian + * removed call to Reset() in ext2fs.c + +2001-08-17 Stephane Eranian + * 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 + * fix a bug in config.c:find_option() where it would do + a strXcmp() on a NULL string. + +2001-08-01 Stephane Eranian + * 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 . + * added slash/backslash conversion for filenames on vfat filesystems. + +2001-07-23 Stephane Eranian + * 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 + * 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 + * incorporated rewritten gzip.c:flush_window() from Tony Luck + * 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 . + +2001-04-06 Stephane Eranian + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + * 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 ) + * 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 + + * 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 + + * 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 + + * 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 index 0000000..2381a6a --- /dev/null +++ b/LIMITATIONS @@ -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 index 0000000..523f0fc --- /dev/null +++ b/Make.defaults @@ -0,0 +1,131 @@ +# +# Copyright (C) 2001-2003 Hewlett-Packard Co. +# Contributed by Stephane Eranian +# +# 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 index 0000000..2f4ade2 --- /dev/null +++ b/Make.rules @@ -0,0 +1,35 @@ +# +# Copyright (C) 2001-2003 Hewlett-Packard Co. +# Contributed by Stephane Eranian +# +# 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 index 0000000..d8ec67d --- /dev/null +++ b/Makefile @@ -0,0 +1,103 @@ +# +# Copyright (C) 2001-2003 Hewlett-Packard Co. +# Contributed by Stephane Eranian +# +# 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 index 0000000..29724e7 --- /dev/null +++ b/README @@ -0,0 +1,74 @@ + ELILO: the IA-32 and IA-64 Linux Loader + --------------------------------------- + Stephane Eranian + + 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 index 0000000..1653032 --- /dev/null +++ b/README.gnu-efi @@ -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 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 index 0000000..373fbdc --- /dev/null +++ b/alloc.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..df138ad --- /dev/null +++ b/alternate.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..a6c9d7c --- /dev/null +++ b/bootparams.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..b257aba --- /dev/null +++ b/chooser.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..a63d9e2 --- /dev/null +++ b/chooser.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * 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 index 0000000..31b10d0 --- /dev/null +++ b/choosers/Makefile @@ -0,0 +1,58 @@ +# +# Copyright (C) 2001-2003 Hewlett-Packard Co. +# Contributed by Stephane Eranian +# +# This file is part of the ELILO, the EFI Linux boot loader. +# +# ELILO is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# ELILO is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ELILO; see the file COPYING. If not, write to the Free +# Software Foundation, 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# Please check out the elilo.txt for complete documentation on how +# to use this program. +# + +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 index 0000000..9b0725e --- /dev/null +++ b/choosers/simple.c @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..5cf109d --- /dev/null +++ b/choosers/simple.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * 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 index 0000000..9379b0e --- /dev/null +++ b/choosers/textmenu.c @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Richard Hirst + * + * 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 +#include + +#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 index 0000000..4b50b01 --- /dev/null +++ b/choosers/textmenu.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Richard Hirst + * + * 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 index 0000000..ac789b9 --- /dev/null +++ b/config.c @@ -0,0 +1,1171 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + * + * Portions of this file are derived from the LILO/x86 + * Copyright 1992-1997 Werner Almesberger. + */ + +#include +#include +#include + +#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)¤t_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 index 0000000..7f4a8c9 --- /dev/null +++ b/config.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..6ac05af --- /dev/null +++ b/devschemes/Makefile @@ -0,0 +1,46 @@ +# +# Copyright (C) 2001-2003 Hewlett-Packard Co. +# Contributed by Stephane Eranian +# +# This file is part of the ELILO, the EFI Linux boot loader. +# +# ELILO is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# ELILO is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ELILO; see the file COPYING. If not, write to the Free +# Software Foundation, 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# Please check out the elilo.txt for complete documentation on how +# to use this program. +# + +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 index 0000000..22b0d06 --- /dev/null +++ b/devschemes/simple.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Look at the README.devschemes for more explanations on how + * to use devschemes. + */ + +#include +#include + +#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 index 0000000..a7dc06e --- /dev/null +++ b/docs/devschemes.txt @@ -0,0 +1,106 @@ + +Some explanations of what the devschemes are for: +------------------------------------------------- +Copyright (c) 2001-2003 Hewlett-Packard Co +Contributed by Stephane Eranian + + + 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 index 0000000..9af06f0 --- /dev/null +++ b/docs/edd30.txt @@ -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 index 0000000..fdc1131 --- /dev/null +++ b/docs/elilo.txt @@ -0,0 +1,478 @@ + -------------------------------------------------------------------- + ELILO.EFI: Linux boot loader for EFI/IA-64 and EFI/IA-32 based systems + -------------------------------------------------------------------- + Stephane Eranian + + 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 + David Mosberger + Johannes Erdfelt + Richard Hirst + Chris Ahna + Mike Johnston + +X/ Bug reports + ----------- + + You can submit bugs to 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 index 0000000..e94adeb --- /dev/null +++ b/docs/eliloalt.txt @@ -0,0 +1,77 @@ +Information related to the eliloalt Linux tool +--------------------------------------------- +(c) 2002-2003 Hewlett Packard Co + Contributed by Stephane Eranian + + 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 index 0000000..9350aa4 --- /dev/null +++ b/docs/elilovars.txt @@ -0,0 +1,53 @@ +Information related to variable support in ELILO +------------------------------------------------ +(c) 2002-2003 Hewlett Packard Co + Contributed by Stephane Eranian + + 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 index 0000000..2945e4e --- /dev/null +++ b/docs/fpswa.txt @@ -0,0 +1,22 @@ +Information related to the FPSWA driver for EFI/ia64 platforms +-------------------------------------------------------------- +(c) 2002-2003 Hewlett Packard Co + Contributed by Stephane Eranian + +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 index 0000000..2f51740 --- /dev/null +++ b/docs/netbooting.txt @@ -0,0 +1,396 @@ +How to netboot using ELILO +-------------------------- + +Copyright (C) 2002-2003 Hewlett-Packard Co. +Contributed by Stephane Eranian + +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=:::::: + + To designate the NFS server, you must use the "nfsroot=" option. It has the + following syntax: + nfsroot=[:][,] + + 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 index 0000000..49e6de5 --- /dev/null +++ b/docs/simple_chooser.txt @@ -0,0 +1,156 @@ +Information about the simple chooser +-------------------------------------- +Copyright (C) 2002-2003 Hewlett-Packard Co. +Contributed by Stephane Eranian + +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 index 0000000..5ad4caf --- /dev/null +++ b/docs/textmenu_chooser.txt @@ -0,0 +1,61 @@ +Information about the textmenu chooser +-------------------------------------- +Copyright (C) 2002-2003 Hewlett-Packard Co. +Contributed by + +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 index 0000000..ad367f5 --- /dev/null +++ b/elf.h @@ -0,0 +1,632 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * 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 . + */ + +#include + +/* 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 index 0000000000000000000000000000000000000000..3f8cd331af2ca272f7050217890bfe804ea00b9e GIT binary patch literal 159859 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 bg9qZMnj3hMgl`C{;3DFosFS4m5rzK`0Rz9U literal 0 HcmV?d00001 diff --git a/elilo.c b/elilo.c new file mode 100644 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 . + * Contributed by Stephane Eranian + * + * Copyright (C) 1999-2000 VA Linux Systems + * Contributed by Johannes Erdfelt . + * + * 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 +#include + +#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 index 0000000..9d28617 --- /dev/null +++ b/elilo.h @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * Copyright (C) 2001 Silicon Graphics, Inc. + * Contributed by Brent Casavant + * + * 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 + +#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 index 0000000..bdea9e2 --- /dev/null +++ b/elilo_debug.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * 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 index 0000000..e14a713 --- /dev/null +++ b/examples/netboot/dhcpd-pxe.conf @@ -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 or for menu. Press 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 index 0000000..1d2aece --- /dev/null +++ b/examples/netboot/dhcpd.conf @@ -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 index 0000000..c46a8d0 --- /dev/null +++ b/examples/textmenu_chooser/elilo-textmenu.conf @@ -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 index 0000000..7ec35ff --- /dev/null +++ b/examples/textmenu_chooser/general.msg @@ -0,0 +1,25 @@ + ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß +10 + 7fÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ 74General Screen7f ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ70¿10 + 7f³70 ³10 + 7f³70 This is the general screen. You entered by pressing F1 ³10 + 7f³70 ³10 + 7f³70 Press any key to return to main screen ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 Help: [71F170-General] [71F270-Params] ³10 + 7fÀ70ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ10 diff --git a/examples/textmenu_chooser/params.msg b/examples/textmenu_chooser/params.msg new file mode 100644 index 0000000..cac9e39 --- /dev/null +++ b/examples/textmenu_chooser/params.msg @@ -0,0 +1,25 @@ + ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß +10 + 7fÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ 74Params Screen7f ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ70¿10 + 7f³70 ³10 + 7f³70 This is the params screen. You entered by pressing F2 ³10 + 7f³70 ³10 + 7f³70 Press any key to return to main screen ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 Help: [71F170-General] [71F270-Params] ³10 + 7fÀ70ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ10 diff --git a/examples/textmenu_chooser/textmenu-message.msg b/examples/textmenu_chooser/textmenu-message.msg new file mode 100644 index 0000000..f63dfdb --- /dev/null +++ b/examples/textmenu_chooser/textmenu-message.msg @@ -0,0 +1,25 @@ + ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß +10 + 7fÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ 74Install and Recovery Disk7f ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ70¿10 + 7f³70 This CD demonstrates the elilo textmenu chooser. You can select an entry ³10 + 7f³70 from the menu with the curosr keys, enter exptra parameters at the propmt, ³10 + 7f³70 and press to start the process. Help can be made available via ³10 + 7f³70 the function keys. On a serial console, use Ctrl-F followed by the ³10 + 7f³70 relevant function key number. ³10 + 7f³70 ³10 + 7f³70 7eBeware that this is an install disk, and misuse can result in the loss of 70³10 + 7f³70 7eany data currently on your disks. 70³10 + 7f³70 ³10 + 7f³70 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ7f¿70 ³10 + 7f³70 ³70 This is where the menu goes 7f³70 ³10 + 7f³70 ³701e (active colour) 7f³70 ³10 + 7f³70 ³70 (inactive colour) 7f³70 ³10 + 7f³70 ³70 1e7f³70 ³10 + 7f³70 À7fÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 ³10 + 7f³70 Boot: 35 70 ³10 + 7f³70 ³10 + 7f³70 Help: [71F170-General] [71F270-Params] ³10 + 7fÀ70ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ10 diff --git a/fileops.c b/fileops.c new file mode 100644 index 0000000..674fc5c --- /dev/null +++ b/fileops.c @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..0150f35 --- /dev/null +++ b/fileops.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * 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 index 0000000..1829d96 --- /dev/null +++ b/fs/Makefile @@ -0,0 +1,70 @@ +# +# Copyright (C) 2001-2003 Hewlett-Packard Co. +# Contributed by Stephane Eranian +# +# This file is part of the ELILO, the EFI Linux boot loader. +# +# ELILO is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# ELILO is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ELILO; see the file COPYING. If not, write to the Free +# Software Foundation, 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# Please check out the elilo.txt for complete documentation on how +# to use this program. +# + +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 index 0000000..a2d5458 --- /dev/null +++ b/fs/ext2_fs.h @@ -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 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 index 0000000..72bcd5c --- /dev/null +++ b/fs/ext2_fs_i.h @@ -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 index 0000000..2e72dfb --- /dev/null +++ b/fs/ext2_fs_sb.h @@ -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 index 0000000..c7c44b1 --- /dev/null +++ b/fs/ext2_private.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..4eaea27 --- /dev/null +++ b/fs/ext2fs.c @@ -0,0 +1,1004 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + * + * 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 +#include + +#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 index 0000000..938c6be --- /dev/null +++ b/fs/ext2fs.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ +#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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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<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 +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 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 +#include + +/* + * 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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000..4fdc99f --- /dev/null +++ b/fs/localfs.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..aa63620 --- /dev/null +++ b/fs/localfs.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..4913785 --- /dev/null +++ b/fs/netfs.c @@ -0,0 +1,790 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..3028689 --- /dev/null +++ b/fs/netfs.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#ifndef __NETLFS_H__ +#define __NETLFS_H__ + +#include +#include + +#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 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 + * + * 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 +#include + + +#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 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 + * + * 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 index 0000000..d9a14fe --- /dev/null +++ b/glue_ext2fs.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ +#include +#include + +#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 index 0000000..7b20ac8 --- /dev/null +++ b/glue_ext2fs.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..73aa13d --- /dev/null +++ b/glue_localfs.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ +#include +#include + +#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 index 0000000..f27cb15 --- /dev/null +++ b/glue_localfs.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..757fc8b --- /dev/null +++ b/glue_netfs.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ +#include +#include + +#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 index 0000000..8176cde --- /dev/null +++ b/glue_netfs.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..6e3baf1 --- /dev/null +++ b/gnu-efi-3.0a-ia32.patch @@ -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 index 0000000..1b1c16c --- /dev/null +++ b/ia32/Makefile @@ -0,0 +1,51 @@ +# +# Copyright (C) 2001-2003 Hewlett-Packard Co. +# Contributed by Stephane Eranian +# +# This file is part of the ELILO, the EFI Linux boot loader. +# +# ELILO is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# ELILO is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ELILO; see the file COPYING. If not, write to the Free +# Software Foundation, 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# Please check out the elilo.txt for complete documentation on how +# to use this program. +# + +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.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 index 0000000..2d1dd1b --- /dev/null +++ b/ia32/bin_to_h.c @@ -0,0 +1,27 @@ +#include +#include + +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 index 0000000..4bf25d0 --- /dev/null +++ b/ia32/config.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * Contributed by Chris Ahna + * + * 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 +#include + +#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 index 0000000..3813858 --- /dev/null +++ b/ia32/private.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..24fd402 --- /dev/null +++ b/ia32/rmswitch.S @@ -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 index 0000000..d6f3324 --- /dev/null +++ b/ia32/sysdeps.h @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * Contributed by Mike Johnston + * Contributed by Chris Ahna + * + * 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 index 0000000..2e8b8e4 --- /dev/null +++ b/ia32/system.c @@ -0,0 +1,798 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * Contributed by Mike Johnston + * Contributed by Chris Ahna + * + * 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 +#include + +#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 index 0000000..a243d0f --- /dev/null +++ b/ia64/Makefile @@ -0,0 +1,42 @@ +# +# Copyright (C) 2001-2003 Hewlett-Packard Co. +# Contributed by Stephane Eranian +# +# This file is part of the ELILO, the EFI Linux boot loader. +# +# ELILO is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# ELILO is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ELILO; see the file COPYING. If not, write to the Free +# Software Foundation, 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# Please check out the elilo.txt for complete documentation on how +# to use this program. +# + +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 index 0000000..2c86cb1 --- /dev/null +++ b/ia64/config.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..a19d7cb --- /dev/null +++ b/ia64/fpswa.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..bb5a065 --- /dev/null +++ b/ia64/gzip.c @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * Copyright (C) 2001 Silicon Graphics, Inc. + * Contributed by Brent Casavant + * + * 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 +#include + +#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: <> 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 index 0000000..69749df --- /dev/null +++ b/ia64/gzip.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..4e06db4 --- /dev/null +++ b/ia64/gzip_loader.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..8273c98 --- /dev/null +++ b/ia64/inflate.c @@ -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 , 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 +# include +#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())<>=(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(""); + 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(" 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(" 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(""); + + /* 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 index 0000000..8b66893 --- /dev/null +++ b/ia64/longjmp.S @@ -0,0 +1,162 @@ +/* Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + Note that __sigsetjmp() did NOT flush the register stack. Instead, + we do it here since __longjmp() is usually much less frequently + invoked than __sigsetjmp(). The only difficulty is that __sigsetjmp() + didn't (and wouldn't be able to) save ar.rnat either. This is a problem + because if we're not careful, we could end up loading random NaT bits. + There are two cases: + + (i) ar.bsp < ia64_rse_rnat_addr(jmpbuf.ar_bsp) + ar.rnat contains the desired bits---preserve ar.rnat + across loadrs and write to ar.bspstore + + (ii) ar.bsp >= ia64_rse_rnat_addr(jmpbuf.ar_bsp) + The desired ar.rnat is stored in + ia64_rse_rnat_addr(jmpbuf.ar_bsp). Load those + bits into ar.rnat after setting ar.bspstore. */ + + + +# define pPos p6 /* is rotate count positive? */ +# define pNeg p7 /* is rotate count negative? */ + + + /* __longjmp(__jmp_buf buf, int val) */ + + .text + .global longjmp + .proc longjmp +longjmp: + alloc r8=ar.pfs,2,1,0,0 + mov r27=ar.rsc + add r2=0x98,in0 // r2 <- &jmpbuf.orig_jmp_buf_addr + ;; + ld8 r8=[r2],-16 // r8 <- orig_jmp_buf_addr + mov r10=ar.bsp + and r11=~0x3,r27 // clear ar.rsc.mode + ;; + flushrs // flush dirty regs to backing store (must be first in insn grp) + ld8 r23=[r2],8 // r23 <- jmpbuf.ar_bsp + sub r8=r8,in0 // r8 <- &orig_jmpbuf - &jmpbuf + ;; + ld8 r25=[r2] // r25 <- jmpbuf.ar_unat + extr.u r8=r8,3,6 // r8 <- (&orig_jmpbuf - &jmpbuf)/8 & 0x3f + ;; + cmp.lt pNeg,pPos=r8,r0 + mov r2=in0 + ;; +(pPos) mov r16=r8 +(pNeg) add r16=64,r8 +(pPos) sub r17=64,r8 +(pNeg) sub r17=r0,r8 + ;; + mov ar.rsc=r11 // put RSE in enforced lazy mode + shr.u r8=r25,r16 + add r3=8,in0 // r3 <- &jmpbuf.r1 + shl r9=r25,r17 + ;; + or r25=r8,r9 + ;; + mov r26=ar.rnat + mov ar.unat=r25 // setup ar.unat (NaT bits for r1, r4-r7, and r12) + ;; + ld8.fill.nta sp=[r2],16 // r12 (sp) + ld8.fill.nta gp=[r3],16 // r1 (gp) + dep r11=-1,r23,3,6 // r11 <- ia64_rse_rnat_addr(jmpbuf.ar_bsp) + ;; + ld8.nta r16=[r2],16 // caller's unat + ld8.nta r17=[r3],16 // fpsr + ;; + ld8.fill.nta r4=[r2],16 // r4 + ld8.fill.nta r5=[r3],16 // r5 (gp) + cmp.geu p8,p0=r10,r11 // p8 <- (ar.bsp >= jmpbuf.ar_bsp) + ;; + ld8.fill.nta r6=[r2],16 // r6 + ld8.fill.nta r7=[r3],16 // r7 + ;; + mov ar.unat=r16 // restore caller's unat + mov ar.fpsr=r17 // restore fpsr + ;; + ld8.nta r16=[r2],16 // b0 + ld8.nta r17=[r3],16 // b1 + ;; +(p8) ld8 r26=[r11] // r26 <- *ia64_rse_rnat_addr(jmpbuf.ar_bsp) + mov ar.bspstore=r23 // restore ar.bspstore + ;; + ld8.nta r18=[r2],16 // b2 + ld8.nta r19=[r3],16 // b3 + ;; + ld8.nta r20=[r2],16 // b4 + ld8.nta r21=[r3],16 // b5 + ;; + ld8.nta r11=[r2],16 // ar.pfs + ld8.nta r22=[r3],56 // ar.lc + ;; + ld8.nta r24=[r2],32 // pr + mov b0=r16 + ;; + ldf.fill.nta f2=[r2],32 + ldf.fill.nta f3=[r3],32 + mov b1=r17 + ;; + ldf.fill.nta f4=[r2],32 + ldf.fill.nta f5=[r3],32 + mov b2=r18 + ;; + ldf.fill.nta f16=[r2],32 + ldf.fill.nta f17=[r3],32 + mov b3=r19 + ;; + ldf.fill.nta f18=[r2],32 + ldf.fill.nta f19=[r3],32 + mov b4=r20 + ;; + ldf.fill.nta f20=[r2],32 + ldf.fill.nta f21=[r3],32 + mov b5=r21 + ;; + ldf.fill.nta f22=[r2],32 + ldf.fill.nta f23=[r3],32 + mov ar.lc=r22 + ;; + ldf.fill.nta f24=[r2],32 + ldf.fill.nta f25=[r3],32 + cmp.eq p8,p9=0,in1 + ;; + ldf.fill.nta f26=[r2],32 + ldf.fill.nta f27=[r3],32 + mov ar.pfs=r11 + ;; + ldf.fill.nta f28=[r2],32 + ldf.fill.nta f29=[r3],32 + ;; + ldf.fill.nta f30=[r2] + ldf.fill.nta f31=[r3] +(p8) mov r8=1 + + mov ar.rnat=r26 // restore ar.rnat + ;; + mov ar.rsc=r27 // restore ar.rsc +(p9) mov r8=in1 + + invala // virt. -> phys. regnum mapping may change + mov pr=r24,-1 + br.ret.dptk.few rp + .endp __longjmp diff --git a/ia64/memcpy.S b/ia64/memcpy.S new file mode 100644 index 0000000..aea7da1 --- /dev/null +++ b/ia64/memcpy.S @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + * + * This 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 + * Copyright (C) 2000 David Mosberger-Tang + */ + +/* 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 index 0000000..b72554b --- /dev/null +++ b/ia64/memset.S @@ -0,0 +1,133 @@ +/* + * Copyright (C) 1999-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + * + * This code is derived from the Linux/ia64 source code. + */ + +/* + * + * Optimized version of the standard memset() function + * + * Return: none + * + * Inputs: + * in0: address of buffer + * in1: byte value to use for storing + * in2: length of the buffer + * + */ + +// arguments +// +#define buf r32 +#define val r33 +#define len r34 + +// +// local registers +// +#define saved_pfs r14 +#define cnt r18 +#define buf2 r19 +#define saved_lc r20 +#define tmp r21 + .text + .global Memset + .proc Memset +Memset: + .prologue + .save ar.pfs, saved_pfs + alloc saved_pfs=ar.pfs,3,0,0,0 // cnt is sink here + cmp.eq p8,p0=r0,len // check for zero length + .save ar.lc, saved_lc + mov saved_lc=ar.lc // preserve ar.lc (slow) + ;; + + .body + + adds tmp=-1,len // br.ctop is repeat/until + tbit.nz p6,p0=buf,0 // odd alignment +(p8) br.ret.spnt.few rp + + cmp.lt p7,p0=16,len // if len > 16 then long memset + mux1 val=val,@brcst // prepare value +(p7) br.cond.dptk.few long_memset + ;; + mov ar.lc=tmp // initialize lc for small count + ;; // avoid RAW and WAW on ar.lc +1: // worst case 15 cyles, avg 8 cycles + st1 [buf]=val,1 + br.cloop.dptk.few 1b + ;; // avoid RAW on ar.lc + mov ar.lc=saved_lc + mov ar.pfs=saved_pfs + br.ret.sptk.few rp // end of short memset + + // at this point we know we have more than 16 bytes to copy + // so we focus on alignment +long_memset: +(p6) st1 [buf]=val,1 // 1-byte aligned +(p6) adds len=-1,len;; // sync because buf is modified + tbit.nz p6,p0=buf,1 + ;; +(p6) st2 [buf]=val,2 // 2-byte aligned +(p6) adds len=-2,len;; + tbit.nz p6,p0=buf,2 + ;; +(p6) st4 [buf]=val,4 // 4-byte aligned +(p6) adds len=-4,len;; + tbit.nz p6,p0=buf,3 + ;; +(p6) st8 [buf]=val,8 // 8-byte aligned +(p6) adds len=-8,len;; + shr.u cnt=len,4 // number of 128-bit (2x64bit) words + ;; + cmp.eq p6,p0=r0,cnt + adds tmp=-1,cnt +(p6) br.cond.dpnt.few .dotail // we have less than 16 bytes left + ;; + adds buf2=8,buf // setup second base pointer + mov ar.lc=tmp + ;; +2: // 16bytes/iteration + st8 [buf]=val,16 + st8 [buf2]=val,16 + br.cloop.dptk.few 2b + ;; +.dotail: // tail correction based on len only + tbit.nz p6,p0=len,3 + ;; +(p6) st8 [buf]=val,8 // at least 8 bytes + tbit.nz p6,p0=len,2 + ;; +(p6) st4 [buf]=val,4 // at least 4 bytes + tbit.nz p6,p0=len,1 + ;; +(p6) st2 [buf]=val,2 // at least 2 bytes + tbit.nz p6,p0=len,0 + mov ar.lc=saved_lc + ;; +(p6) st1 [buf]=val // only 1 byte left + br.ret.dptk.few rp + .endp Memset diff --git a/ia64/plain_loader.c b/ia64/plain_loader.c new file mode 100644 index 0000000..92d009e --- /dev/null +++ b/ia64/plain_loader.c @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * Copyright (C) 2001 Silicon Graphics, Inc. + * Contributed by Brent Casavant + * + * 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 +#include + +#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: <> 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 index 0000000..bb09faf --- /dev/null +++ b/ia64/private.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..ce7e67c --- /dev/null +++ b/ia64/setjmp.S @@ -0,0 +1,170 @@ +/* Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + The layout of the jmp_buf is as follows. This is subject to change + and user-code should never depend on the particular layout of + jmp_buf! + + + offset: description: + ------- ------------ + 0x000 stack pointer (r12) ; unchangeable (see _JMPBUF_UNWINDS) + 0x008 r1 (gp) + 0x010 caller's unat + 0x018 fpsr + 0x020 r4 + 0x028 r5 + 0x030 r6 + 0x038 r7 + 0x040 rp (b0) + 0x048 b1 + 0x050 b2 + 0x058 b3 + 0x060 b4 + 0x068 b5 + 0x070 ar.pfs + 0x078 ar.lc + 0x080 pr + 0x088 ar.bsp ; unchangeable (see __longjmp.S) + 0x090 ar.unat + 0x098 &__jmp_buf ; address of the jmpbuf (needed to locate NaT bits in unat) + 0x0a0 f2 + 0x0b0 f3 + 0x0c0 f4 + 0x0d0 f5 + 0x0e0 f16 + 0x0f0 f17 + 0x100 f18 + 0x110 f19 + 0x120 f20 + 0x130 f21 + 0x130 f22 + 0x140 f23 + 0x150 f24 + 0x160 f25 + 0x170 f26 + 0x180 f27 + 0x190 f28 + 0x1a0 f29 + 0x1b0 f30 + 0x1c0 f31 */ + + + /* The following two entry points are the traditional entry points: */ + + .text + .global setjmp + .proc setjmp +setjmp: + alloc r8=ar.pfs,2,0,0,0 + mov in1=1 + br.cond.sptk.many __sigsetjmp + .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 index 0000000..91bbfc1 --- /dev/null +++ b/ia64/setjmp.h @@ -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 . + + 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 index 0000000..ab447fb --- /dev/null +++ b/ia64/sysdeps.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +/* + * This 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 index 0000000..388d435 --- /dev/null +++ b/ia64/system.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +/* + * this file contains all the IA-64 specific code expected by generic loader + */ +#include +#include + +#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 index 0000000..92b6f44 --- /dev/null +++ b/initrd.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 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 index 0000000..290531c --- /dev/null +++ b/loader.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +#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 index 0000000..0e127c7 --- /dev/null +++ b/loader.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..b47a58e --- /dev/null +++ b/strops.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 1999-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#include +#include + +//#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 index 0000000..1084861 --- /dev/null +++ b/strops.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..b72d106 --- /dev/null +++ b/sysdeps.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + */ + +#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 index 0000000..8858224 --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,47 @@ +# +# Copyright (C) 2001-2003 Hewlett-Packard Co. +# Contributed by Stephane Eranian +# +# This file is part of the ELILO, the EFI Linux boot loader. +# +# ELILO is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# ELILO is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ELILO; see the file COPYING. If not, write to the Free +# Software Foundation, 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# Please check out the elilo.txt for complete documentation on how +# to use this program. +# + +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 index 0000000..1c9709a --- /dev/null +++ b/tools/eliloalt.c @@ -0,0 +1,298 @@ +/* + * eliloalt.c + * + * Copyright (C) 2002-2003 Hewlett-Packard Co + * Contributed by Stephane Eranian + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * ELILO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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 index 0000000..85babf4 --- /dev/null +++ b/util.c @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * Copyright (C) 2001 Silicon Graphics, Inc. + * Contributed by Brent Casavant + * + * 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 +#include + +#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<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 index 0000000..a709007 --- /dev/null +++ b/vars.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * Copyright (C) 2001 Silicon Graphics, Inc. + * Contributed by Brent Casavant + * + * 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 +#include + +/* + * 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 index 0000000..48d2fea --- /dev/null +++ b/vars.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Contributed by Stephane Eranian + * + * Copyright (C) 2001 Silicon Graphics, Inc. + * Contributed by Brent Casavant + * + * 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__ */ +