commit fb6ce0d5962fab9975440f61a502d2c230364d9a Author: Stephane Eranian Date: Mon Feb 23 08:11:30 2004 -0500 release 3.4 https://sourceforge.net/projects/elilo/files/elilo/elilo-3.4/ 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 0000000..3f8cd33 Binary files /dev/null and b/elilo-ia32.efi differ diff --git a/elilo-ia64.efi b/elilo-ia64.efi new file mode 100755 index 0000000..6022fed Binary files /dev/null and b/elilo-ia64.efi differ diff --git a/elilo.c b/elilo.c new file mode 100644 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__ */ +