diff --git a/ChangeLog b/ChangeLog index 07aba8e..55dad01 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2004-02-19 Brett Johnson + * Fixed bug where default image initrd would carry over to another + image that was selected interactively (iff the newly selected image + did not have an initrd). + * Added support for subnet-specific config files in netfs. +2004-02-17 Brett Johnson + * integrated ia32 compressed kernel support from Matt Tolentino + 2003-08-20 Stephane Eranian * released 3.4 2003-08-19 Stephane Eranian diff --git a/Make.defaults b/Make.defaults index 523f0fc..48baaea 100644 --- a/Make.defaults +++ b/Make.defaults @@ -68,7 +68,7 @@ CPPFLAGS = -DCONFIG_$(ARCH) OPTIMFLAGS = -O2 DEBUGFLAGS = -Wall CFLAGS = $(OPTIMFLAGS) -fpic -fshort-wchar $(DEBUGFLAGS) -LDFLAGS = -nostdlib +LDFLAGS = -nostdlib -znocombreloc INSTALL = install ifeq ($(CONFIG_machspec_netconfig),y) @@ -121,7 +121,8 @@ CFLAGS += -mfixed-range=f32-f127 else ifeq ($(ARCH),ia32) prefix = - CC = $(prefix)gcc3 +# CC = $(prefix)gcc3 + CC = $(prefix)gcc AS = $(prefix)as LD = $(prefix)ld AR = $(prefix)ar diff --git a/TODO b/TODO index d996a16..fb2696a 100644 --- a/TODO +++ b/TODO @@ -14,5 +14,3 @@ Some of the things TO DO: - 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/choosers/simple.c b/choosers/simple.c index 9b0725e..0af1fa7 100644 --- a/choosers/simple.c +++ b/choosers/simple.c @@ -346,6 +346,7 @@ restart: ret = wait_timeout(elilo_opt.delay); if (ret != 0) { elilo_opt.prompt = 1; + elilo_opt.initrd[0] = CHAR_NULL; elilo_opt.timeout = ELILO_TIMEOUT_INFINITY; goto restart; } diff --git a/choosers/textmenu.c b/choosers/textmenu.c index 9379b0e..aaf2a4b 100644 --- a/choosers/textmenu.c +++ b/choosers/textmenu.c @@ -459,6 +459,7 @@ restart: ret = wait_timeout(elilo_opt.delay); if (ret != 0) { elilo_opt.prompt = 1; + elilo_opt.initrd[0] = CHAR_NULL; elilo_opt.timeout = ELILO_TIMEOUT_INFINITY; goto restart; } diff --git a/config.c b/config.c index ac789b9..c556484 100644 --- a/config.c +++ b/config.c @@ -843,7 +843,7 @@ get_config_file(VOID) } EFI_STATUS -read_config(CHAR16 *filename, INTN retry) +read_config(CHAR16 *filename) { EFI_STATUS status; INTN ret; @@ -856,49 +856,8 @@ read_config(CHAR16 *filename, INTN retry) 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; - } + VERB_PRT(3, Print(L"cannot open config file %s\n", filename)); + return status; } /* * start numbering at line 1 diff --git a/docs/netbooting.txt b/docs/netbooting.txt index 2f51740..130cc11 100644 --- a/docs/netbooting.txt +++ b/docs/netbooting.txt @@ -126,7 +126,21 @@ only on two very common cases: This filename is an opportunity to specify a machine specific configuration file. - 2) elilo-ia32.config or elilo-ia64.conf + 2) AA[BB[CC]][-ia32|ia64].conf + As of version 3.5, elilo will also look for IPv4 class A,B,C + subnet-specific versions of the config file. This is useful when you + want to have a common config file for all machines connected to a + particular subnet. + + For example, if your IP address is 10.0.0.1 (0A000001 in hex), elilo + will look first for 0A000001.conf, then 0A0000.conf, then 0A00.conf, + and finally 0A.conf. + + Elilo will also try architecture-specific versions of subnet-specific + config files first (So for example, on an Itanium system, + "0A0000-ia64.conf" will be tried before "0A0000.conf") + + 3) elilo-ia32.config or elilo-ia64.conf Depending on the machine (client side) architecture elilo will try the IA-32 or IA-64 file. @@ -135,7 +149,7 @@ only on two very common cases: 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 + 4) 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). diff --git a/elilo-ia32.efi b/elilo-ia32.efi old mode 100755 new mode 100644 index 3f8cd33..b2ca1c8 Binary files a/elilo-ia32.efi and b/elilo-ia32.efi differ diff --git a/elilo-ia64.efi b/elilo-ia64.efi old mode 100755 new mode 100644 index 6022fed..9022ad4 Binary files a/elilo-ia64.efi and b/elilo-ia64.efi differ diff --git a/elilo.c b/elilo.c index 0959c71..19731c9 100644 --- a/elilo.c +++ b/elilo.c @@ -368,8 +368,8 @@ efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *system_tab) CHAR16 dpath[FILENAME_MAXLEN]; CHAR16 *devpath; - //elilo_opt.verbose=3; - //elilo_opt.debug=1; + elilo_opt.verbose=0; + elilo_opt.debug=0; /* initialize global variable */ systab = system_tab; @@ -548,23 +548,37 @@ efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *system_tab) /* * set per fileops defaults files for configuration and kernel */ - fops_setdefaults(elilo_opt.default_config, elilo_opt.default_kernel, FILENAME_MAXLEN, devpath); + fops_setdefaults(elilo_opt.default_configs, 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)); + elilo_opt.default_configs[0].fname, 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); + ptr = elilo_opt.config[0] == CHAR_NULL ? (retry=1,elilo_opt.default_configs[0].fname) : (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); + ret = read_config(ptr); + VERB_PRT(1,Print(L"read_config=%r\n", ret)); + + /* Only try the default config filenames if user did not specify a + * config filename on the command line */ + if (elilo_opt.config[0] == CHAR_NULL) { + while ((ret != EFI_SUCCESS) && + (retry < MAX_DEFAULT_CONFIGS) && + (elilo_opt.default_configs[retry].fname[0] != CHAR_NULL)) { + + ptr = elilo_opt.default_configs[retry].fname; + ret = read_config(ptr); + VERB_PRT(1,Print(L"read_config=%r\n", ret)); + retry += 1; + } + } /* * when the config file is not found, we fail only if: * - the user did not specified interactive mode diff --git a/elilo.h b/elilo.h index 9d28617..7f0d768 100644 --- a/elilo.h +++ b/elilo.h @@ -60,11 +60,17 @@ #define CMDLINE_MAXLEN 512 /* needed by ia32 */ #define FILENAME_MAXLEN 256 #define MAX_ARGS 256 +/* Just pick an arbitrary number that's high enough for now :o) */ +#define MAX_DEFAULT_CONFIGS 16 typedef struct { UINT8 nothing_yet; } image_opt_t; +typedef struct config_file { + CHAR16 fname[FILENAME_MAXLEN]; +} config_file_t; + typedef struct { /* * list of options controllable from both the command line @@ -91,7 +97,8 @@ typedef struct { sys_img_options_t *sys_img_opts; /* architecture depdendent per image options */ CHAR16 default_kernel[FILENAME_MAXLEN]; - CHAR16 default_config[FILENAME_MAXLEN]; + /* CHAR16 default_config[FILENAME_MAXLEN]; */ + config_file_t default_configs[MAX_DEFAULT_CONFIGS]; CHAR16 config[FILENAME_MAXLEN]; /* name of config file */ CHAR16 chooser[FILENAME_MAXLEN]; /* image chooser to use */ @@ -160,7 +167,7 @@ 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 EFI_STATUS read_config(CHAR16 *); extern VOID print_config_options(VOID); extern INTN find_label(CHAR16 *, CHAR16 *, CHAR16 *, CHAR16 *); extern VOID print_label_list(VOID); diff --git a/fileops.c b/fileops.c index 674fc5c..1678f0f 100644 --- a/fileops.c +++ b/fileops.c @@ -345,12 +345,37 @@ fops_seek(fops_fd_t fd, UINT64 newpos) } EFI_STATUS -fops_setdefaults(CHAR16 *config, CHAR16 *kname, UINTN maxlen, CHAR16 *devpath) +fops_setdefaults(struct config_file *defconf, CHAR16 *kname, UINTN maxlen, CHAR16 *devpath) { -#define FILEOPS_DEFAULT_KERNEL L"vmlinux" -#define FILEOPS_DEFAULT_CONFIG L"elilo.conf" + INTN i; - if (config == NULL || kname == NULL) return EFI_INVALID_PARAMETER; +/* + * 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 FILEOPS_ARCH_DEFAULT_CONFIG L"elilo-ia64.conf" +#elif defined (CONFIG_ia32) +#define FILEOPS_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 FILEOPS_DEFAULT_CONFIG L"elilo.conf" + +#define FILEOPS_DEFAULT_KERNEL L"vmlinux" + +#ifdef ELILO_DEBUG + if (defconf == NULL || kname == NULL) return EFI_INVALID_PARAMETER; +#endif + + for (i=0; ifops == NULL) { if (boot_dev == NULL) @@ -360,13 +385,30 @@ fops_setdefaults(CHAR16 *config, CHAR16 *kname, UINTN maxlen, CHAR16 *devpath) 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); + else { + boot_dev->fops->setdefaults(boot_dev->fops->intf, defconf, kname, maxlen, devpath); + } + i=0; while (i= MAX_DEFAULT_CONFIGS) { + Print(L"ERROR: i = %d, MAX_DEFAULT_CONFIGS is not large enough\n", i); + return EFI_INVALID_PARAMETER; + } +//#endif + StrnCpy(defconf[i].fname, FILEOPS_ARCH_DEFAULT_CONFIG, maxlen-1); + StrnCpy(defconf[i+1].fname, FILEOPS_DEFAULT_CONFIG, maxlen-1); + +//#ifdef ELILO_DEBUG + VERB_PRT(3,Print(L"Default config filename list:\n")); + for (i=0; inetfs_query_layer(netfs, 0, NETFS_CONFIG_LAYER, maxlen, config); + status = netfs->netfs_query_layer(netfs, 0, NETFS_CONFIG_LAYER, maxlen, config[0].fname); if (EFI_ERROR(status)) { - StrnCpy(config, NETFS_DEFAULT_CONFIG, maxlen-1); - config[maxlen-1] = CHAR_NULL; + StrnCpy(config[0].fname, NETFS_DEFAULT_CONFIG, maxlen-1); + config[0].fname[maxlen-1] = CHAR_NULL; } status = netfs->netfs_query_layer(netfs, 0, NETFS_KERNEL_LAYER, maxlen, kname); @@ -162,21 +162,44 @@ netfs_setdefaults(VOID *intf, CHAR16 *config, CHAR16 *kname, UINTN maxlen, CHAR1 } } 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; +# if defined(CONFIG_ia64) +# define CONFIG_ARCH_EXTENSION L"-ia64.conf\0" +# elif defined (CONFIG_ia32) +# define CONFIG_ARCH_EXTENSION L"-ia64.conf\0" +# else +# error "You need to specfy your default arch config file" +# endif + +# define CONFIG_EXTENSION L".conf\0" + /* + * will try machine/subnet specific files first. + * the filenames are constructed based on the IP(v4) address + */ + convert_ip2hex(ipaddr, m, str); + StrnCpy(config[0].fname, str, maxlen-1); + StrnCpy(config[0].fname+8, CONFIG_EXTENSION, 6); + + StrnCpy(config[1].fname, str, maxlen-1); + StrnCpy(config[1].fname+6, CONFIG_ARCH_EXTENSION, 11); + + StrnCpy(config[2].fname, str, maxlen-1); + StrnCpy(config[2].fname+6, CONFIG_EXTENSION, 6); + + StrnCpy(config[3].fname, str, maxlen-1); + StrnCpy(config[3].fname+4, CONFIG_ARCH_EXTENSION, 11); + + StrnCpy(config[4].fname, str, maxlen-1); + StrnCpy(config[4].fname+4, CONFIG_EXTENSION, 6); + + StrnCpy(config[5].fname, str, maxlen-1); + StrnCpy(config[5].fname+2, CONFIG_ARCH_EXTENSION, 11); + + StrnCpy(config[6].fname, str, maxlen-1); + StrnCpy(config[6].fname+2, CONFIG_EXTENSION, 6); #else - StrnCpy(config, NETFS_DEFAULT_CONFIG, maxlen-1); - config[maxlen-1] = CHAR_NULL; + StrnCpy(config[0].fname, NETFS_DEFAULT_CONFIG, maxlen-1); + config[0].fname[maxlen-1] = CHAR_NULL; #endif StrnCpy(kname, NETFS_DEFAULT_KERNEL, maxlen-1); kname[maxlen-1] = CHAR_NULL; diff --git a/gnu-efi-3.0a-ia32.patch b/gnu-efi-3.0a-ia32.patch deleted file mode 100644 index 6e3baf1..0000000 --- a/gnu-efi-3.0a-ia32.patch +++ /dev/null @@ -1,19 +0,0 @@ -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 index 1b1c16c..353ae67 100644 --- a/ia32/Makefile +++ b/ia32/Makefile @@ -28,7 +28,7 @@ include ../Make.rules TOPDIR=$(CDIR)/.. -FILES=system.o config.o +FILES=system.o config.o bzimage.o plain_loader.o gzip_loader.o gzip.o TARGET=sysdeps.o diff --git a/ia32/bzimage.c b/ia32/bzimage.c new file mode 100644 index 0000000..aac1d37 --- /dev/null +++ b/ia32/bzimage.c @@ -0,0 +1,224 @@ +/* + * 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. + */ + +#include +#include + +#include "elilo.h" +#include "loader.h" + +boot_params_t *param_start = NULL; +UINTN param_size = 0; + +UINTN kernel_size = 0x200000; /* 2M (largest x86 bzImage kernel image) */ + +static INTN +bzImage_probe(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, 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 +bzImage_load(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; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +loader_ops_t bzimage_loader = { + NULL, + L"bzImage_loader", + &bzImage_probe, + &bzImage_load +}; diff --git a/ia32/gzip.c b/ia32/gzip.c new file mode 100644 index 0000000..fdd7284 --- /dev/null +++ b/ia32/gzip.c @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2001-2002 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" + +#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 int error_return; + +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 INTN +is_valid_header(Elf32_Ehdr *ehdr) +{ + UINT16 type, machine; + + 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] == ELFCLASS32 + && type == ET_EXEC /* must be executable */ + && machine == EM_386 ? 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) +{ + Elf32_Ehdr *elf; + Elf32_Phdr *phdrs; + UINTN total_size, pages; + UINTN low_addr, max_addr; + UINTN offs = 0; + UINT16 phnum; + UINTN paddr, memsz; + INTN i; + + elf = (Elf32_Ehdr *)buf; + + if (is_valid_header(elf) == -1) + return -1; + + offs = elf->e_phoff; + phnum = elf->e_phnum; + + VERB_PRT(3, { + Print(L"Entry point 0x%lx\n", elf->e_entry); + Print(L"%d program headers\n", phnum); + Print(L"%d segment headers\n", elf->e_shnum); + }); + + 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 *)(elf->e_entry & PADDR_MASK); + + phdrs = (Elf32_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 = (phdrs[i].p_paddr & PADDR_MASK); + memsz = phdrs[i].p_memsz, + + chunks[i].addr = paddr; + chunks[i].offset = phdrs[i].p_offset; + chunks[i].size = phdrs[i].p_filesz; + chunks[i].bss_sz = phdrs[i].p_memsz - phdrs[i].p_filesz; + + CHUNK_VALIDATE(i); + + if (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+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) { + ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", + LD_NAME, pages, low_addr)); + ERR_PRT((L"%s : Could not load kernel at 0x%lx\n", LD_NAME, low_addr)); + ERR_PRT((L"%s : Bailing\n", LD_NAME)); + 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; +} + +static void +error(char *x) +{ + ERR_PRT((L"%s : %a", LD_NAME, x)); + /* will eventually exit with error from gunzip() */ +} + +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; + + 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/ia32/gzip.h b/ia32/gzip.h new file mode 100644 index 0000000..296bf3c --- /dev/null +++ b/ia32/gzip.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2001-2002 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_ia32" + +#endif /* __GZIP_H__ */ diff --git a/ia32/gzip_loader.c b/ia32/gzip_loader.c new file mode 100644 index 0000000..687b301 --- /dev/null +++ b/ia32/gzip_loader.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2001-2002 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/ia32/inflate.c b/ia32/inflate.c new file mode 100644 index 0000000..5532250 --- /dev/null +++ b/ia32/inflate.c @@ -0,0 +1,1205 @@ +#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) +{ + /* FIXME this is wrong here! FIXME!!!!*/ + 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/ia32/plain_loader.c b/ia32/plain_loader.c new file mode 100644 index 0000000..ac4c573 --- /dev/null +++ b/ia32/plain_loader.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2001-2002 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_elf32" + +static INTN +is_valid_header(Elf32_Ehdr *ehdr) +{ + UINT16 type, machine; + + 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] == ELFCLASS32 + && type == ET_EXEC /* must be executable */ + && machine == EM_386 ? 0 : -1; +} + +static INTN +plain_probe(CHAR16 *kname) +{ + Elf32_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; +} + + +static INTN +load_elf(fops_fd_t fd, kdesc_t *kd) +{ + Elf32_Ehdr ehdr; + Elf32_Phdr *phdrs; + EFI_STATUS status; + INTN ret = ELILO_LOAD_ERROR; + UINTN i, total_size = 0; + UINTN pages, size, bss_sz, osize; + VOID *low_addr = (VOID *)~0; + VOID *max_addr = (VOID *)0; + UINTN paddr, memsz, filesz; + 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; + + if (is_valid_header(&ehdr) == -1) { + ERR_PRT((L"%s : not a 32-bit ELF image\n", LD_NAME)); + return ELILO_LOAD_ERROR; + } + VERB_PRT(3, { + Print(L"ELF Header information: \n"); + Print(L"\tEntry point 0x%x\n", (ehdr.e_entry & PADDR_MASK)); + Print(L"\t%d program headers\n", ehdr.e_phnum); + Print(L"\t%d segment headers\n", ehdr.e_shnum); + }); + + phnum = ehdr.e_phnum; + + if (fops_seek(fd, ehdr.e_phoff) < 0) { + ERR_PRT((L"%s : seek to %d for phdrs failed", LD_NAME, ehdr.e_phoff)); + return ELILO_LOAD_ERROR; + } + size = osize = (phnum * sizeof(Elf32_Phdr)); + + DBG_PRT((L"%s : allocate %d bytes for %d pheaders each of size:%d phentsize=%d\n", + LD_NAME, size, phnum, sizeof(Elf32_Phdr), ehdr.e_phentsize)); + + phdrs = (Elf32_Phdr *)alloc(size, 0); + if (phdrs == NULL) { + ERR_PRT((L"%s : allocate for phdrs failed", LD_NAME)); + return ELILO_LOAD_ERROR; + } + status = fops_read(fd, phdrs, &size); + if (EFI_ERROR(status) || size != osize) { + ERR_PRT((L"%s : phdr load failed", LD_NAME, status)); + goto out; + } + /* + * First pass to figure out total memory footprint + */ + for (i = 0; i < phnum; i++) { + + paddr = (phdrs[i].p_paddr & PADDR_MASK); + memsz = phdrs[i].p_memsz; + + DBG_PRT((L"Phdr %d paddr [0x%x-0x%x] offset 0x%x" + " filesz 0x%x memsz=0x%x bss_sz=0x%x p_type=0x%x\n", + 1+i, paddr, paddr+phdrs[i].p_filesz, phdrs[i].p_offset, + phdrs[i].p_filesz, memsz, + (memsz - phdrs[i].p_filesz), phdrs[i].p_type)); + + if (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%x 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 starts and ends */ + kd->kstart = low_addr; + kd->kend = (low_addr + (pages << EFI_PAGE_SHIFT)); + kd->kentry = (VOID *)(ehdr.e_entry & PADDR_MASK); + + VERB_PRT(3, { + Print(L"Lowest PhysAddr: 0x%x\nTotalMemSize:%d bytes (%d pages)\n", + low_addr, total_size, pages); + Print(L"Kernel entry @ 0x%x\n", kd->kentry); + }); + + /* now allocate memory for the kernel at the exact requested spot */ + 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"%s : Could not alloc %d pages for the kernel at 0x%lx " + " and relocation is not not been implemented!\n", + LD_NAME, pages, low_addr)); + goto load_abort; + } + /* Pure paranoia. Clear the memory first. Just in case... */ + Memset(low_addr, 0, (pages << EFI_PAGE_SHIFT)); + + VERB_PRT(1, Print(L"Press any key to interrupt\n")); + + /* + * 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 (phdrs[i].p_type != PT_LOAD) + continue; + + VERB_PRT(3, Print(L"poffs: 0x%x (phdrs[%d].p_offset)\n", + phdrs[i].p_offset, i)); + + filesz = phdrs[i].p_filesz; + low_addr = (VOID *)((UINTN) phdrs[i].p_paddr & PADDR_MASK); + + /* Move to the right position */ + if (fops_seek(fd, phdrs[i].p_offset) < 0) + goto out_kernel; + + /* How many BSS bytes to clear */ + bss_sz = phdrs[i].p_memsz - filesz; + + VERB_PRT(4, { + Print(L"\nHeader #%d\n", i); + Print(L"Offset in file 0x%x\n", phdrs[i].p_offset); + Print(L"Physical addr 0x%x\n", low_addr); + Print(L"BSS size 0x%x bytes\n", bss_sz); + }); + + /* + * Read actual segment into memory + */ + ret = fops_read(fd, low_addr, &filesz); + if (ret == ELILO_LOAD_ABORTED) goto load_abort; + if (ret == ELILO_LOAD_ERROR) goto out; + + /* + * Clear bss section + */ + if (bss_sz) + Memset((VOID *)low_addr+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); + return ret; +} + +loader_ops_t plain_loader={ + NULL, + LD_NAME, + plain_probe, + plain_load_kernel +}; diff --git a/ia32/sysdeps.h b/ia32/sysdeps.h index d6f3324..0cec75a 100644 --- a/ia32/sysdeps.h +++ b/ia32/sysdeps.h @@ -33,6 +33,7 @@ #define __ELILO_SYSDEPS_IA32_H__ #define ELILO_ARCH "IA-32" /* ASCII string */ +#define PADDR_MASK 0xfffffff /* for now use library versions */ #define Memset(a,v,n) SetMem((a),(n),(v)) @@ -56,28 +57,7 @@ * 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]; @@ -275,7 +255,9 @@ typedef union ia32_boot_params { /* 0x224 */ UINT16 heap_end_ptr; /* LDR */ /* %%TBD */ -/* 0x226 */ UINT32 base_mem_size; /* LDR */ +/* 0x226 */ UINT16 unused_7; /* LDR */ + +/* 0x228 */ UINT32 cmdline_addr; /* LDR */ } s; } boot_params_t; #pragma pack() @@ -354,7 +336,6 @@ start_kernel(VOID *kentry, boot_params_t *bp) /* * Disable interrupts. */ - asm volatile ( "cli" : : ); /* @@ -362,11 +343,9 @@ start_kernel(VOID *kentry, boot_params_t *bp) */ 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 @@ -375,20 +354,6 @@ start_kernel(VOID *kentry, boot_params_t *bp) 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. */ @@ -429,7 +394,6 @@ start_kernel(VOID *kentry, boot_params_t *bp) * Jump to kernel entry point. */ - asm volatile ( "jmp *%%ecx" : : ); } diff --git a/ia32/system.c b/ia32/system.c index 2e8b8e4..7c2228a 100644 --- a/ia32/system.c +++ b/ia32/system.c @@ -26,30 +26,23 @@ */ /* - * this file contains all the IA-32 specific code expected by generic loader + * 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; - +extern loader_ops_t bzimage_loader, plain_loader, gzip_loader; /* * Descriptor table base addresses & limits for Linux startup. */ dt_addr_t gdt_addr = { 0x800, 0x94000 }; -dt_addr_t idt_addr = { 0, 0 }; +dt_addr_t idt_addr = { 0, 0 }; /* * Initial GDT layout for Linux startup. @@ -77,7 +70,6 @@ UINT16 init_gdt[] = { UINTN sizeof_init_gdt = sizeof init_gdt; - /* * Highest available base memory address. * @@ -98,266 +90,33 @@ UINTN high_base_mem = 0x90000; * * 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). + * 4 kB) boundary. + * This is only used for backward compatibility. */ 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; - +/* This starting address will hold true for all of the loader types for now */ 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); */ - - + loader_register(&bzimage_loader); + loader_register(&plain_loader); + loader_register(&gzip_loader); return 0; } -/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* * initrd_get_addr() * Compute a starting address for the initial RAMdisk image. @@ -381,13 +140,12 @@ sysdeps_initrd_get_addr(kdesc_t *kd, memdesc_t *imem) imem->start_addr = kd->kend; - VERB_PRT(3, Print(L"initrd start_addr=0x%x pgcnt=%d\n", imem->start_addr, imem->pgcnt)); + 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) { @@ -398,8 +156,6 @@ sysdeps_free_boot_params(boot_params_t *bp) free_memmap(&md); } -/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* * IA-32 specific boot parameters initialization routine */ @@ -423,9 +179,11 @@ sysdeps_create_boot_params( 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; + if (param_start != NULL) { + free(param_start); + param_start = NULL; + param_size = 0; + } free_kmem(); return -1; } @@ -436,29 +194,27 @@ sysdeps_create_boot_params( * 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; - + if (param_start != NULL) { + 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; + bp->s.unused_7 = 0; /* * Tell kernel this was loaded by an advanced loader type. @@ -475,6 +231,12 @@ sysdeps_create_boot_params( bp->s.cmdline_magik = CMDLINE_MAGIK; bp->s.cmdline_offset = (UINT8 *)cmdline - (UINT8 *)bp; + /* + * Clear out the cmdline_addr field so the kernel can find + * the cmdline. + */ + bp->s.cmdline_addr = 0x0; + /* * Setup hard drive parameters. * %%TBD - It should be okay to zero fill the hard drive @@ -484,28 +246,16 @@ sysdeps_create_boot_params( 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) { + if (bp->s.alt_mem_k <= 65535) bp->s.ext_mem_k = (UINT16)bp->s.alt_mem_k; - } else { + 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. @@ -525,24 +275,17 @@ sysdeps_create_boot_params( /* * 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; @@ -555,29 +298,22 @@ sysdeps_create_boot_params( /* * 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. + * Pointing device presence. The kernel will detect this. */ - -/* %%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. + * EFI loader signature */ - 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; /* @@ -587,6 +323,7 @@ sysdeps_create_boot_params( * arch/i386/boot/bootsect.S * arch/i386/boot/setup.S * arch/i386/kernel/setup.c + * include/asm-i386/setup.h (2.5/2.6) */ #define CHECK_OFFSET(n, o, f) \ @@ -610,7 +347,6 @@ sysdeps_create_boot_params( ; \ } \ } - { UINTN test = 0; @@ -688,7 +424,7 @@ sysdeps_create_boot_params( 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"); + CHECK_OFFSET(cmdline_addr, 0x228, L"%xh"); if (test) { ERR_PRT((L"Boot sector and/or setup parameter alignment error.")); @@ -711,7 +447,6 @@ sysdeps_create_boot_params( if (EFI_ERROR(efi_status)) { ERR_PRT((L"QueryMode failed. Fake it.")); - mode = 3; rows = 25; cols = 80; @@ -730,12 +465,10 @@ sysdeps_create_boot_params( 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; + bp->s.orig_video_points = 16; -/* %%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; @@ -763,7 +496,6 @@ sysdeps_create_boot_params( free_kmem(); return -1; } - *cookie = mdesc.cookie; bp->s.efi_mem_map = (UINTN)mdesc.md; bp->s.efi_mem_map_size = mdesc.map_size; @@ -771,28 +503,5 @@ sysdeps_create_boot_params( 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; }