|  | /* common.c - miscellaneous shared variables and routines */ | 
|  | /* | 
|  | *  GRUB  --  GRand Unified Bootloader | 
|  | *  Copyright (C) 1996  Erich Boleyn  <erich@uruk.org> | 
|  | *  Copyright (C) 1999, 2000  Free Software Foundation, Inc. | 
|  | * | 
|  | *  This program is free software; you can redistribute it and/or modify | 
|  | *  it under the terms of the GNU General Public License as published by | 
|  | *  the Free Software Foundation; either version 2 of the License, or | 
|  | *  (at your option) any later version. | 
|  | * | 
|  | *  This program is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | *  GNU General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU General Public License | 
|  | *  along with this program; if not, write to the Free Software | 
|  | *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | */ | 
|  |  | 
|  | #include <shared.h> | 
|  |  | 
|  | #ifdef SUPPORT_DISKLESS | 
|  | # include <etherboot.h> | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | *  Shared BIOS/boot data. | 
|  | */ | 
|  |  | 
|  | struct multiboot_info mbi; | 
|  | unsigned long saved_drive; | 
|  | unsigned long saved_partition; | 
|  | #ifndef STAGE1_5 | 
|  | unsigned long saved_mem_upper; | 
|  |  | 
|  | /* This saves the maximum size of extended memory (in KB).  */ | 
|  | unsigned long extended_memory; | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | *  Error code stuff. | 
|  | */ | 
|  |  | 
|  | grub_error_t errnum = ERR_NONE; | 
|  |  | 
|  | #ifndef STAGE1_5 | 
|  |  | 
|  | char *err_list[] = | 
|  | { | 
|  | [ERR_NONE] = 0, | 
|  | [ERR_BAD_ARGUMENT] = "Invalid argument", | 
|  | [ERR_BAD_FILENAME] = | 
|  | "Filename must be either an absolute pathname or blocklist", | 
|  | [ERR_BAD_FILETYPE] = "Bad file or directory type", | 
|  | [ERR_BAD_GZIP_DATA] = "Bad or corrupt data while decompressing file", | 
|  | [ERR_BAD_GZIP_HEADER] = "Bad or incompatible header in compressed file", | 
|  | [ERR_BAD_PART_TABLE] = "Partition table invalid or corrupt", | 
|  | [ERR_BAD_VERSION] = "Mismatched or corrupt version of stage1/stage2", | 
|  | [ERR_BELOW_1MB] = "Loading below 1MB is not supported", | 
|  | [ERR_BOOT_COMMAND] = "Kernel must be loaded before booting", | 
|  | [ERR_BOOT_FAILURE] = "Unknown boot failure", | 
|  | [ERR_BOOT_FEATURES] = "Unsupported Multiboot features requested", | 
|  | [ERR_DEV_FORMAT] = "Unrecognized device string", | 
|  | [ERR_DEV_VALUES] = "Invalid device requested", | 
|  | [ERR_EXEC_FORMAT] = "Invalid or unsupported executable format", | 
|  | [ERR_FILELENGTH] = | 
|  | "Filesystem compatibility error, cannot read whole file", | 
|  | [ERR_FILE_NOT_FOUND] = "File not found", | 
|  | [ERR_FSYS_CORRUPT] = "Inconsistent filesystem structure", | 
|  | [ERR_FSYS_MOUNT] = "Cannot mount selected partition", | 
|  | [ERR_GEOM] = "Selected cylinder exceeds maximum supported by BIOS", | 
|  | [ERR_NEED_LX_KERNEL] = "Linux kernel must be loaded before initrd", | 
|  | [ERR_NEED_MB_KERNEL] = "Multiboot kernel must be loaded before modules", | 
|  | [ERR_NO_DISK] = "Selected disk does not exist", | 
|  | [ERR_NO_PART] = "No such partition", | 
|  | [ERR_NUMBER_PARSING] = "Error while parsing number", | 
|  | [ERR_OUTSIDE_PART] = "Attempt to access block outside partition", | 
|  | [ERR_PRIVILEGED] = "Must be authenticated", | 
|  | [ERR_READ] = "Disk read error", | 
|  | [ERR_SYMLINK_LOOP] = "Too many symbolic links", | 
|  | [ERR_UNALIGNED] = "File is not sector aligned", | 
|  | [ERR_UNRECOGNIZED] = "Unrecognized command", | 
|  | [ERR_WONT_FIT] = "Selected item cannot fit into memory", | 
|  | [ERR_WRITE] = "Disk write error", | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* static for BIOS memory map fakery */ | 
|  | static struct AddrRangeDesc fakemap[3] = | 
|  | { | 
|  | {20, 0, 0, MB_ARD_MEMORY}, | 
|  | {20, 0x100000, 0, MB_ARD_MEMORY}, | 
|  | {20, 0x1000000, 0, MB_ARD_MEMORY} | 
|  | }; | 
|  |  | 
|  | /* A big problem is that the memory areas aren't guaranteed to be: | 
|  | (1) contiguous, (2) sorted in ascending order, or (3) non-overlapping. | 
|  | Thus this kludge.  */ | 
|  | static unsigned long | 
|  | mmap_avail_at (unsigned long bottom) | 
|  | { | 
|  | unsigned long long top; | 
|  | unsigned long addr; | 
|  | int cont; | 
|  |  | 
|  | top = bottom; | 
|  | do | 
|  | { | 
|  | for (cont = 0, addr = mbi.mmap_addr; | 
|  | addr < mbi.mmap_addr + mbi.mmap_length; | 
|  | addr += *((unsigned long *) addr) + 4) | 
|  | { | 
|  | struct AddrRangeDesc *desc = (struct AddrRangeDesc *) addr; | 
|  |  | 
|  | if (desc->Type == MB_ARD_MEMORY | 
|  | && desc->BaseAddr <= top | 
|  | && desc->BaseAddr + desc->Length > top) | 
|  | { | 
|  | top = desc->BaseAddr + desc->Length; | 
|  | cont++; | 
|  | } | 
|  | } | 
|  | } | 
|  | while (cont); | 
|  |  | 
|  | /* For now, GRUB assumes 32bits addresses, so...  */ | 
|  | if (top > 0xFFFFFFFF) | 
|  | top = 0xFFFFFFFF; | 
|  |  | 
|  | return (unsigned long) top - bottom; | 
|  | } | 
|  | #endif /* ! STAGE1_5 */ | 
|  |  | 
|  | #ifdef SUPPORT_DISKLESS | 
|  | /* Set up the diskless environment so that GRUB can get a configuration | 
|  | file from a network.  */ | 
|  | static int | 
|  | setup_diskless_environment (void) | 
|  | { | 
|  | /* For now, there is no difference between BOOTP and DHCP in GRUB.  */ | 
|  | if (! bootp ()) | 
|  | { | 
|  | grub_printf ("BOOTP/DHCP fails.\n"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* This will be erased soon, though...  */ | 
|  | print_network_configuration (); | 
|  | return 1; | 
|  | } | 
|  | #endif /* SUPPORT_DISKLESS */ | 
|  |  | 
|  | /* This queries for BIOS information.  */ | 
|  | void | 
|  | init_bios_info (void) | 
|  | { | 
|  | #ifndef STAGE1_5 | 
|  | unsigned long cont, memtmp, addr; | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | *  Get information from BIOS on installed RAM. | 
|  | */ | 
|  |  | 
|  | mbi.mem_lower = get_memsize (0); | 
|  | mbi.mem_upper = get_memsize (1); | 
|  |  | 
|  | #ifndef STAGE1_5 | 
|  | /* | 
|  | *  We need to call this somewhere before trying to put data | 
|  | *  above 1 MB, since without calling it, address line 20 will be wired | 
|  | *  to 0.  Not too desirable. | 
|  | */ | 
|  |  | 
|  | gateA20 (1); | 
|  |  | 
|  | /* Store the size of extended memory in EXTENDED_MEMORY, in order to | 
|  | tell it to non-Multiboot OSes.  */ | 
|  | extended_memory = mbi.mem_upper; | 
|  |  | 
|  | /* | 
|  | *  The "mbi.mem_upper" variable only recognizes upper memory in the | 
|  | *  first memory region.  If there are multiple memory regions, | 
|  | *  the rest are reported to a Multiboot-compliant OS, but otherwise | 
|  | *  unused by GRUB. | 
|  | */ | 
|  |  | 
|  | addr = get_code_end (); | 
|  | mbi.mmap_addr = addr; | 
|  | mbi.mmap_length = 0; | 
|  | cont = 0; | 
|  |  | 
|  | do | 
|  | { | 
|  | cont = get_mmap_entry ((void *) addr, cont); | 
|  |  | 
|  | /* If the returned buffer's length is zero, quit. */ | 
|  | if (! *((unsigned long *) addr)) | 
|  | break; | 
|  |  | 
|  | mbi.mmap_length += *((unsigned long *) addr) + 4; | 
|  | addr += *((unsigned long *) addr) + 4; | 
|  | } | 
|  | while (cont); | 
|  |  | 
|  | if (mbi.mmap_length) | 
|  | { | 
|  | unsigned long long max_addr; | 
|  |  | 
|  | /* | 
|  | *  This is to get the lower memory, and upper memory (up to the | 
|  | *  first memory hole), into the "mbi.mem_{lower,upper}" | 
|  | *  elements.  This is for OS's that don't care about the memory | 
|  | *  map, but might care about total RAM available. | 
|  | */ | 
|  | mbi.mem_lower = mmap_avail_at (0) >> 10; | 
|  | mbi.mem_upper = mmap_avail_at (0x100000) >> 10; | 
|  |  | 
|  | /* Find the maximum available address. Ignore any memory holes.  */ | 
|  | for (max_addr = 0, addr = mbi.mmap_addr; | 
|  | addr < mbi.mmap_addr + mbi.mmap_length; | 
|  | addr += *((unsigned long *) addr) + 4) | 
|  | { | 
|  | struct AddrRangeDesc *desc = (struct AddrRangeDesc *) addr; | 
|  |  | 
|  | if (desc->Type == MB_ARD_MEMORY | 
|  | && desc->BaseAddr + desc->Length > max_addr) | 
|  | max_addr = desc->BaseAddr + desc->Length; | 
|  | } | 
|  |  | 
|  | extended_memory = (max_addr - 0x100000) >> 10; | 
|  | } | 
|  | else if ((memtmp = get_eisamemsize ()) != -1) | 
|  | { | 
|  | cont = memtmp & ~0xFFFF; | 
|  | memtmp = memtmp & 0xFFFF; | 
|  |  | 
|  | if (cont != 0) | 
|  | extended_memory = (cont >> 10) + 0x3c00; | 
|  | else | 
|  | extended_memory = memtmp; | 
|  |  | 
|  | if (!cont || (memtmp == 0x3c00)) | 
|  | memtmp += (cont >> 10); | 
|  | else | 
|  | { | 
|  | /* XXX should I do this at all ??? */ | 
|  |  | 
|  | mbi.mmap_addr = (unsigned long) fakemap; | 
|  | mbi.mmap_length = sizeof (fakemap); | 
|  | fakemap[0].Length = (mbi.mem_lower << 10); | 
|  | fakemap[1].Length = (memtmp << 10); | 
|  | fakemap[2].Length = cont; | 
|  | } | 
|  |  | 
|  | mbi.mem_upper = memtmp; | 
|  | } | 
|  |  | 
|  | saved_mem_upper = mbi.mem_upper; | 
|  |  | 
|  | /* | 
|  | *  Initialize other Multiboot Info flags. | 
|  | */ | 
|  |  | 
|  | mbi.flags = MB_INFO_MEMORY | MB_INFO_CMDLINE | MB_INFO_BOOTDEV; | 
|  |  | 
|  | #endif /* STAGE1_5 */ | 
|  |  | 
|  | #ifdef SUPPORT_DISKLESS | 
|  | /* If SUPPORT_DISKLESS is defined, initialize the network here.  */ | 
|  | if (! setup_diskless_environment ()) | 
|  | return; | 
|  | #endif | 
|  |  | 
|  | /* Set boot drive and partition.  */ | 
|  | saved_drive = boot_drive; | 
|  | saved_partition = install_partition; | 
|  |  | 
|  | /* Start main routine here.  */ | 
|  | cmain (); | 
|  | } |