| /* common.c - miscellaneous shared variables and routines */ |
| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 1999,2000,2001,2002,2004 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> |
| #include <iso9660.h> |
| #include "pxe.h" |
| |
| #ifdef SUPPORT_NETBOOT |
| #include <grub.h> |
| #include <bootp.h> |
| #endif |
| |
| /* |
| * Shared BIOS/boot data. |
| */ |
| |
| struct multiboot_info mbi; |
| #ifdef GRUB_UTIL |
| unsigned long saved_drive; |
| unsigned long saved_partition; |
| #endif |
| char saved_dir[256]; |
| //unsigned long cdrom_drives[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; |
| unsigned long cdrom_drive = GRUB_INVALID_DRIVE; |
| unsigned long force_cdrom_as_boot_device = 1; |
| unsigned long ram_drive; |
| unsigned long rd_base = 0; /* Note the rd_base value of -1 invalidates the ram drive. */ |
| unsigned long rd_size = 0; /* The rd_size 0 stands for 4GB, not for length of 0. */ |
| #ifdef GRUB_UTIL |
| unsigned long saved_mem_upper; |
| #endif |
| unsigned long saved_mem_lower; |
| unsigned long saved_mmap_addr; |
| unsigned long saved_mmap_length; |
| |
| #ifndef STAGE1_5 |
| #ifdef SOLARIS_NETBOOT |
| unsigned long dhcpack_length; |
| unsigned long dhcpack_buf; |
| #endif /* SOLARIS_NETBOOT */ |
| /* This saves the maximum size of extended memory (in KB). */ |
| unsigned long extended_memory; |
| unsigned long init_free_mem_start; |
| #endif |
| int is64bit = 0; |
| int errorcheck = 1; |
| |
| /* |
| * 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, or you omitted the required DEVICE part which should lead the filename.", |
| [ERR_DEV_NEED_INIT] = "Device not initialized yet", |
| [ERR_DEV_VALUES] = "Invalid device requested", |
| [ERR_EXEC_FORMAT] = "Invalid or unsupported executable format", |
| [ERR_FILELENGTH] = "Filesystem compatibility error, cannot read whole file", |
| [ERR_FILENAME_FORMAT] = "The leading DEVICE of the filename to find must be stripped off,\n\tand DIR for set-root must begin in a slash(/).", |
| [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_HD_VOL_START_0] = "The BPB hidden_sectors should not be zero for a hard-disk partition boot sector", |
| [ERR_IN_SITU_FLOPPY] = "Only hard drives could be mapped in situ.", |
| [ERR_IN_SITU_MEM] = "Should not use --mem together with --in-situ.", |
| [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_DISK_SPACE] = "No spare sectors on the disk", |
| [ERR_NO_PART] = "No such partition", |
| [ERR_NO_HEADS] = "The number of heads must be specified. The `--heads=0' option tells map to choose a value(but maybe unsuitable) for you", |
| [ERR_NO_SECTORS] = "The number of sectors per track must be specified. The `--sectors-per-track=0' option tells map to choose a value(but maybe unsuitable) for you", |
| [ERR_NON_CONTIGUOUS] = "File for drive emulation must be in one contiguous disk area", |
| [ERR_NUMBER_OVERFLOW] = "Overflow while parsing number", |
| [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", |
| [ERR_INT13_ON_HOOK] = "The int13 handler already on hook", |
| [ERR_INT13_OFF_HOOK] = "The int13 handler not yet on hook", |
| [ERR_NO_DRIVE_MAPPED] = "Refuse to hook int13 because of empty drive map table", |
| [ERR_INVALID_HEADS] = "Invalid heads. Should be between 0 and 256(0 means auto)", |
| [ERR_INVALID_SECTORS] = "Invalid sectors. Should be between 0 and 63(0 means auto)", |
| [ERR_SPECIFY_GEOM] = "Should not specify geometry when mapping a whole drive or when emulating a hard disk with a logical partition", |
| [ERR_EXTENDED_PARTITION] = "Extended partition table is invalid, or its CHS values conflict with the BPB in a logical partition", |
| [ERR_DEL_MEM_DRIVE] = "You should delete other mem drive first, or use `--mem' option to force the deletion", |
| [ERR_SPECIFY_MEM] = "Should not specify `--mem' when mapping a whole drive", |
| [ERR_SPECIFY_RESTRICTION] = "Options --read-only, --fake-write and --unsafe-boot are mutually exclusive. Should not specify them repeatedly.", |
| [ERR_INVALID_FLOPPIES] = "Invalid floppies. Should be between 0 and 2", |
| [ERR_INVALID_HARDDRIVES] = "Invalid harddrives. Should be between 0 and 127", |
| [ERR_INVALID_LOAD_SEGMENT] = "Invalid load segment. Should be between 0 and 0x9FFF", |
| [ERR_INVALID_LOAD_OFFSET] = "Invalid load offset. Should be between 0 and 0xFFFF", |
| [ERR_INVALID_LOAD_LENGTH] = "Invalid load length. Should be between 512 and 0xA0000", |
| [ERR_INVALID_SKIP_LENGTH] = "Invalid skip length. Should be non-negative and less than the file size", |
| [ERR_INVALID_BOOT_CS] = "Invalid boot CS. Should be between 0 and 0xFFFF", |
| [ERR_INVALID_BOOT_IP] = "Invalid boot IP. Should be between 0 and 0xFFFF", |
| [ERR_INVALID_RAM_DRIVE] = "Invalid ram_drive. Should be between 0 and 254", |
| // [ERR_INVALID_RD_BASE] = "Invalid rd_base. Should not be 0xffffffff", |
| // [ERR_INVALID_RD_SIZE] = "Invalid rd_size. Should not be 0", |
| [ERR_MD_BASE] = "When mapping whole mem device at a fixed location, you must specify --mem to a value > 0.", |
| [ERR_RD_BASE] = "RD_BASE must be sector-aligned and non-zero for mapping at a fixed location", |
| [ERR_DOS_BACKUP] = "GRUB was not booted from DOS, or the backup copy of DOS at physical\naddress 0x200000 is corrupt", |
| [ERR_ENABLE_A20] = "Failed to turn on Gate A20!", |
| [ERR_DISABLE_A20] = "Failed to turn off Gate A20!", |
| [ERR_DEFAULT_FILE] = "Invalid DEFAULT file format. Please copy a valid DEFAULT file from the grub4dos release and try again. Also note that the DEFAULT file must be uncompressed.", |
| [ERR_PARTITION_TABLE_FULL] = "Cannot use --in-situ because the partition table is full(i.e., all the 4 entries are in use).", |
| [ERR_MD5_FORMAT] = "Unrecognized md5 string. You must create it using the MD5CRYPT command.", |
| [ERR_WRITE_GZIP_FILE] = "Attempt to write a gzip file", |
| [ERR_FUNC_CALL] = "Invalid function call", |
| // [ERR_WRITE_TO_NON_MEM_DRIVE] = "Only RAM drives can be written when running in a script", |
| |
| [ERR_BAD_GZIP_CRC] = "Incorrect gunzip CRC checksum", |
| [ERR_FILESYSTEM_NOT_FOUND] = "File System not found", |
| /* this zfs file system is not found in the pool of the device */ |
| [ERR_NO_BOOTPATH] = "No valid boot path found in the zfs label. This may be caused by attempting to boot from an off-lined device.", |
| [ERR_NEWER_VERSION] = "Newer on-disk pool version", |
| [ERR_NOTXPM] = "Image not in XPM graphics format", |
| [ERR_TOOMANYCOLORS] = "Image cannot use more than 14 colors", |
| [ERR_CORRUPTXPM] = "File contains corrupt XPM image data" |
| }; |
| |
| |
| /* 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 = saved_mmap_addr; |
| addr < saved_mmap_addr + saved_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 */ |
| |
| /* This queries for BIOS information. */ |
| void |
| init_bios_info (void) |
| { |
| #ifndef STAGE1_5 |
| unsigned long cont, memtmp, addr; |
| int drive; |
| #endif |
| #ifndef GRUB_UTIL |
| #ifndef STAGE1_5 |
| unsigned long force_pxe_as_boot_device; |
| #endif /* ! STAGE1_5 */ |
| #endif /* ! GRUB_UTIL */ |
| |
| /* |
| * Get information from BIOS on installed RAM. |
| */ |
| |
| #ifndef STAGE1_5 |
| DEBUG_SLEEP |
| printf("\rGet lower memory... "); |
| #endif |
| saved_mem_lower = get_memsize (0); /* int12 --------safe enough */ |
| #ifndef STAGE1_5 |
| printf("\rGet upper memory... "); |
| #endif |
| saved_mem_upper = get_memsize (1); /* int15/88 -----safe enough */ |
| |
| #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. |
| */ |
| |
| #ifndef GRUB_UTIL |
| debug = debug_boot + 1; |
| DEBUG_SLEEP |
| printf("\rTurning on gate A20... "); |
| #if 1 |
| { |
| unsigned long j; |
| int wait; |
| int time1; |
| int time2; |
| |
| if (gateA20 (1)) /* int15/24 -----safe enough */ |
| { |
| /* wipe out the messages on success */ |
| printf("\r \r"); |
| wait = 0; /* sleep 0 second after A20 control */ |
| } else { |
| printf("Failure! Report bug, please!\n"); |
| wait = 5; /* sleep 5 second on failure */ |
| } |
| |
| /* Get current time. */ |
| while ((time2 = getrtsecs ()) == 0xFF); |
| |
| for (j = 0; j < 0x00800000; j++) |
| { |
| if ((time1 = getrtsecs ()) != time2 && time1 != 0xFF) |
| { |
| if (wait == 0) |
| break; |
| |
| time2 = time1; |
| wait--; |
| } |
| } |
| } |
| #else |
| extern void grub2_gate_a20 (int on); |
| grub2_gate_a20 (1); |
| printf("\r \r"); /* wipe out the messages */ |
| #endif |
| DEBUG_SLEEP |
| #endif |
| |
| /* Store the size of extended memory in EXTENDED_MEMORY, in order to |
| tell it to non-Multiboot OSes. */ |
| extended_memory = saved_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 (); |
| saved_mmap_addr = addr; |
| saved_mmap_length = 0; |
| cont = 0; |
| |
| printf("\rGet E820 memory... "); |
| do |
| { |
| cont = get_mmap_entry ((void *) addr, cont); /* int15/e820 ------ will write memory! */ |
| |
| /* If the returned buffer's length is zero, quit. */ |
| if (! *((unsigned long *) addr)) |
| break; |
| |
| saved_mmap_length += *((unsigned long *) addr) + 4; |
| addr += *((unsigned long *) addr) + 4; |
| } |
| while (cont); |
| |
| if (! (saved_mmap_length)) |
| printf("\rGet E801 memory... "); |
| |
| if (saved_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. |
| */ |
| saved_mem_lower = mmap_avail_at (0) >> 10; |
| saved_mem_upper = mmap_avail_at (0x100000) >> 10; |
| |
| /* Find the maximum available address. Ignore any memory holes. */ |
| for (max_addr = 0, addr = saved_mmap_addr; |
| addr < saved_mmap_addr + saved_mmap_length; |
| addr += *((unsigned long *) addr) + 4) |
| { |
| struct AddrRangeDesc *desc = (struct AddrRangeDesc *) addr; |
| |
| if (desc->Type == MB_ARD_MEMORY && desc->Length > 0 |
| && desc->BaseAddr + desc->Length > max_addr) |
| max_addr = desc->BaseAddr + desc->Length; |
| } |
| |
| extended_memory = (max_addr - 0x100000) >> 10; |
| } |
| else if ((memtmp = get_eisamemsize ()) != -1) /* int15/e801 ------safe enough */ |
| { |
| cont = memtmp & ~0xFFFF; |
| memtmp = memtmp & 0xFFFF; |
| |
| if (cont != 0) |
| extended_memory = (cont >> 10) + 0x3c00; |
| else |
| extended_memory = memtmp; |
| |
| saved_mem_upper = memtmp; |
| |
| if (!cont || (memtmp == 0x3c00)) |
| { |
| saved_mem_upper += (cont >> 10); |
| |
| // /* XXX should I do this at all ??? */ |
| // |
| // saved_mmap_addr = (unsigned long) fakemap; |
| // saved_mmap_length = sizeof (struct AddrRangeDesc) * 2; |
| // fakemap[0].Length = (saved_mem_lower << 10); |
| // fakemap[1].Length = (saved_mem_upper << 10); |
| // fakemap[2].Length = 0; |
| } |
| //else |
| { |
| /* XXX should I do this at all ??? */ |
| |
| saved_mmap_addr = (unsigned long) fakemap; |
| saved_mmap_length = sizeof (fakemap); |
| fakemap[0].Length = (saved_mem_lower << 10); |
| fakemap[1].Length = (memtmp << 10); |
| fakemap[2].Length = cont; |
| } |
| } |
| |
| printf("\r \r"); /* wipe out the messages */ |
| |
| mbi.mem_upper = saved_mem_upper; |
| mbi.mem_lower = saved_mem_lower; |
| mbi.mmap_addr = saved_mmap_addr; |
| mbi.mmap_length = saved_mmap_length; |
| |
| #ifdef SUPPORT_NETBOOT |
| #ifdef SOLARIS_NETBOOT |
| /* leave room for dhcpack_buf */ |
| dhcpack_buf = addr; |
| addr += sizeof (struct dhcp_t); |
| #endif |
| #endif |
| |
| #ifndef GRUB_UTIL |
| is64bit = check_64bit (); |
| #endif |
| |
| #if 1 |
| /* Get the drive info. */ |
| /* FIXME: This should be postponed until a Multiboot kernel actually |
| requires it, because this could slow down the start-up |
| unreasonably. */ |
| mbi.drives_length = 0; |
| mbi.drives_addr = addr; |
| |
| /* For now, GRUB doesn't probe floppies, since it is trivial to map |
| floppy drives to BIOS drives. */ |
| #ifdef GRUB_UTIL |
| #define FIND_DRIVES 8 |
| #else |
| #define FIND_DRIVES (*((char *)0x475)) |
| #endif |
| if (debug > 1) |
| grub_printf ("hard drives: %d, int13: %X, int15: %X\n", FIND_DRIVES, *(unsigned long *)0x4C, *(unsigned long *)0x54); |
| DEBUG_SLEEP |
| |
| for (drive = 0x80; drive < 0x80 + FIND_DRIVES; drive++) |
| { |
| struct drive_info *info = (struct drive_info *) addr; |
| // unsigned short *port; |
| |
| /* Get the geometry. This ensures that the drive is present. */ |
| |
| if (debug > 1) |
| grub_printf ("get_diskinfo(%X), ", drive); |
| |
| if (get_diskinfo (drive, &tmp_geom)) |
| continue;//break; |
| |
| if (debug > 1) |
| grub_printf (" %sC/H/S=%d/%d/%d, Sector Count/Size=%d/%d\n", |
| (tmp_geom.flags & BIOSDISK_FLAG_LBA_EXTENSION) ? "LBA, " : "", |
| tmp_geom.cylinders, tmp_geom.heads, tmp_geom.sectors, |
| tmp_geom.total_sectors, tmp_geom.sector_size); |
| |
| /* Set the information. */ |
| info->drive_number = drive; |
| info->drive_mode = ((tmp_geom.flags & BIOSDISK_FLAG_LBA_EXTENSION) |
| ? MB_DI_LBA_MODE : MB_DI_CHS_MODE); |
| info->drive_cylinders = tmp_geom.cylinders; |
| info->drive_heads = tmp_geom.heads; |
| info->drive_sectors = tmp_geom.sectors; |
| |
| addr += sizeof (struct drive_info); |
| |
| info->size = addr - (unsigned long) info; |
| mbi.drives_length += info->size; |
| } |
| |
| init_free_mem_start = addr; |
| |
| DEBUG_SLEEP |
| #endif |
| |
| /* |
| * Initialize other Multiboot Info flags. |
| */ |
| |
| mbi.flags = (MB_INFO_MEMORY | MB_INFO_CMDLINE | MB_INFO_BOOTDEV | MB_INFO_DRIVE_INFO | MB_INFO_CONFIG_TABLE | MB_INFO_BOOT_LOADER_NAME); |
| if (saved_mmap_length) |
| mbi.flags |= MB_INFO_MEM_MAP; |
| |
| #endif /* STAGE1_5 */ |
| |
| #ifndef GRUB_UTIL |
| #ifndef STAGE1_5 |
| #ifdef FSYS_PXE |
| force_pxe_as_boot_device = 0; |
| if (! ((*(char *)0x8205) & 0x01)) /* if it is not disable pxe */ |
| { |
| //pxe_detect(); |
| if (! pxe_entry) |
| { |
| PXENV_GET_CACHED_INFO_t get_cached_info; |
| |
| printf("\rbegin pxe scan... "); |
| pxe_scan (); |
| DEBUG_SLEEP |
| |
| if (pxe_entry) |
| { |
| pxe_basemem = *((unsigned short*)0x413); |
| |
| get_cached_info.PacketType = PXENV_PACKET_TYPE_DHCP_ACK; |
| get_cached_info.Buffer = get_cached_info.BufferSize = 0; |
| |
| printf("\rbegin pxe call(type=DHCP_ACK)... "); |
| pxe_call (PXENV_GET_CACHED_INFO, &get_cached_info); |
| DEBUG_SLEEP |
| |
| if (get_cached_info.Status) |
| { |
| grub_printf ("\nFatal: DHCP_ACK failure!\n"); |
| goto pxe_init_fail; |
| } |
| |
| discover_reply = LINEAR(get_cached_info.Buffer); |
| |
| pxe_yip = discover_reply->yip; |
| pxe_sip = discover_reply->sip; |
| pxe_gip = discover_reply->gip; |
| |
| pxe_mac_type = discover_reply->Hardware; |
| pxe_mac_len = discover_reply->Hardlen; |
| grub_memmove (&pxe_mac, &discover_reply->CAddr, pxe_mac_len); |
| |
| get_cached_info.PacketType = PXENV_PACKET_TYPE_CACHED_REPLY; |
| get_cached_info.Buffer = get_cached_info.BufferSize = 0; |
| |
| printf("\rbegin pxe call(type=CACHED_REPLY)... "); |
| pxe_call (PXENV_GET_CACHED_INFO, &get_cached_info); |
| DEBUG_SLEEP |
| |
| if (get_cached_info.Status) |
| { |
| grub_printf ("\nFatal: CACHED_REPLY failure!\n"); |
| pxe_init_fail: |
| pxe_keep = 0; |
| pxe_unload (); |
| DEBUG_SLEEP |
| pxe_entry = 0; |
| discover_reply = 0; |
| goto pxe_init_done; |
| } |
| |
| discover_reply = LINEAR(get_cached_info.Buffer); |
| |
| /* on pxe boot, we only use preset_menu */ |
| if (preset_menu != (char*)0x800) |
| preset_menu = (char*)0x800; |
| if (*config_file) |
| *config_file = 0; |
| force_pxe_as_boot_device = 1; |
| if (bios_id != 1) /* if it is not Bochs ... */ |
| goto set_root;; /* ... skip cdrom check. */ |
| } |
| } |
| } |
| pxe_init_done: |
| #endif /* FSYS_PXE */ |
| #endif /* ! STAGE1_5 */ |
| #endif /* ! GRUB_UTIL */ |
| |
| #if !defined(STAGE1_5) && !defined(GRUB_UTIL) |
| /* Set cdrom drive. */ |
| |
| /* Get the geometry. */ |
| if (debug > 1) |
| printf("\rboot drive=%X, ", boot_drive); |
| cdrom_drive = get_cdinfo (boot_drive, &tmp_geom); |
| if (! cdrom_drive || cdrom_drive != boot_drive) |
| cdrom_drive = GRUB_INVALID_DRIVE; |
| if (cdrom_drive == GRUB_INVALID_DRIVE) |
| { |
| /* read the first sector of the drive */ |
| if (((unsigned char)boot_drive) >= 0x80 + FIND_DRIVES) |
| { |
| struct disk_address_packet |
| { |
| unsigned char length; |
| unsigned char reserved; |
| unsigned short blocks; |
| unsigned long buffer; |
| unsigned long long block; |
| |
| unsigned char dummy[16]; |
| } __attribute__ ((packed)) *dap; |
| |
| dap = (struct disk_address_packet *)0x580; |
| |
| dap->length = 0x10; |
| dap->reserved = 0; |
| dap->blocks = 1; |
| dap->buffer = 0x5F80/*SCRATCHSEG*/ << 16; |
| dap->block = 0; |
| |
| /* set a known value */ |
| grub_memset ((char *)0x5F800, 0xEC, 0x800); |
| biosdisk_int13_extensions (0x4200, (unsigned char)boot_drive, dap); |
| /* see if it is a big sector */ |
| { |
| char *p; |
| for (p = (char *)0x5FA00; p < (char *)0x60000; p++) |
| { |
| if ((*p) != (char)0xEC) |
| { |
| cdrom_drive = boot_drive; |
| break; |
| } |
| } |
| } |
| |
| } /* if (geometry->flags & BIOSDISK_FLAG_LBA_EXTENSION) */ |
| } |
| |
| if (debug > 1) |
| printf("%s\n", cdrom_drive == GRUB_INVALID_DRIVE ? "Not CD":"Is CD"); |
| DEBUG_SLEEP |
| #endif |
| |
| #if !defined(STAGE1_5) && !defined(GRUB_UTIL) |
| |
| if (cdrom_drive == GRUB_INVALID_DRIVE) |
| { |
| int err; |
| int version; |
| struct drive_parameters *drp = (struct drive_parameters *)0x600; |
| |
| #undef FIND_DRIVES |
| #ifdef GRUB_UTIL |
| #define FIND_DRIVES 8 |
| #else |
| #define FIND_DRIVES (*((char *)0x475)) |
| #endif |
| for (drive = 0xFF; drive >= 0x7F; drive--) |
| { |
| if (drive >= 0x80 && drive < 0x80 + FIND_DRIVES) |
| continue; |
| |
| /* Get the geometry. */ |
| if (debug > 1) |
| grub_printf ("\rget_cdinfo(%X),", drive); |
| cdrom_drive = get_cdinfo (drive, &tmp_geom); |
| if (cdrom_drive) |
| { |
| if (drive != 0x7F) |
| break; |
| drive = cdrom_drive; |
| cdrom_drive = get_cdinfo (drive, &tmp_geom); |
| if (cdrom_drive == drive) |
| break; |
| drive = 0x7F; |
| } |
| |
| cdrom_drive = GRUB_INVALID_DRIVE; |
| |
| /* Some buggy BIOSes will hang at EBIOS `Get Drive Parameters' call |
| * (INT 13h function 48h). So we only do further checks for Bochs. |
| */ |
| |
| if (bios_id != 1) /* if it is not Bochs ... */ |
| continue; /* ... skip and try next drive. */ |
| |
| /* When qemu has a cdrom attached but not booted from cdrom, its |
| * `get bootable cdrom status call', the int13/ax=4B01, returns CF=0 |
| * but with a wrong `Bootable CD-ROM Specification Packet' as follows: |
| * |
| * The first byte(packet size) is 0x13, all the rest bytes are 0's. |
| * |
| * So we need to call get_diskinfo() here as a workaround. |
| * |
| * (The bug was reported by Jacopo Lazzari. Thanks!) |
| */ |
| |
| if (drive >= 0x80) |
| { |
| version = check_int13_extensions (drive); |
| |
| if (! (version & 1)) /* not support functions 42h-44h, 47h-48h */ |
| continue; /* failure, try next drive. */ |
| |
| /* It is safe to clear out DRP. */ |
| grub_memset (drp, 0, sizeof (struct drive_parameters)); |
| |
| drp->size = sizeof (struct drive_parameters) - 16; |
| |
| err = biosdisk_int13_extensions (0x4800, drive, drp); |
| if (! err && drp->bytes_per_sector == ISO_SECTOR_SIZE) |
| { |
| /* Assume it is CDROM. */ |
| cdrom_drive = drive; |
| break; |
| } |
| } /* if (drive >= 0x80) */ |
| |
| } /* for (drive = 0x7F; drive < 0xff; drive++) */ |
| } /* if (cdrom_drive == GRUB_INVALID_DRIVE) */ |
| #undef FIND_DRIVES |
| |
| // if (cdrom_drive != GRUB_INVALID_DRIVE) |
| // { |
| // /* skip pxe init if booting from cdrom */ |
| // *(char *)0x8205 |= 0x01; |
| // } |
| |
| if (debug > 1) |
| grub_printf(" cdrom_drive == %X.\n", cdrom_drive); |
| DEBUG_SLEEP |
| #endif /* ! STAGE1_5 && ! GRUB_UTIL */ |
| |
| /* check if the no-emulation-mode bootable cdrom exists. */ |
| |
| /* if cdrom_drive is active, we assume it is the boot device */ |
| if (saved_entryno == 0 && force_cdrom_as_boot_device) |
| force_cdrom_as_boot_device = 0; |
| if (cdrom_drive != GRUB_INVALID_DRIVE && force_cdrom_as_boot_device) |
| { |
| boot_drive = cdrom_drive; /* force it to be the boot drive */ |
| } |
| |
| if (boot_drive == cdrom_drive) |
| /* force it to be "whole drive" without partition table */ |
| install_partition = 0xFFFFFF; |
| |
| #ifndef GRUB_UTIL |
| #ifndef STAGE1_5 |
| set_root: |
| |
| if (force_pxe_as_boot_device) |
| { |
| boot_drive = PXE_DRIVE; |
| install_partition = 0xFFFFFF; |
| } |
| #endif /* ! STAGE1_5 */ |
| #endif /* ! GRUB_UTIL */ |
| |
| /* Set root drive and partition. */ |
| saved_drive = boot_drive; |
| saved_partition = install_partition; |
| |
| #ifndef GRUB_UTIL |
| #ifndef STAGE1_5 |
| debug = 1; |
| if (! atapi_dev_count) |
| min_cdrom_id = (cdrom_drive < 0xE0 && cdrom_drive >= 0xC0) ? 0xE0 : 0xC0; |
| |
| /* if grub.exe is booted as a Linux kernel, check the initrd disk. */ |
| |
| /* the real mode zero page(only the beginning 2 sectors, the boot params) is loaded at 0xA00 */ |
| |
| /* check the header signature "HdrS" (0x53726448) */ |
| |
| ram_drive = 0x7f; /* the default ram_drive is a floppy. */ |
| if (*(unsigned long*)(int*)(0xA00 + 0x202) == 0x53726448) |
| { |
| unsigned long initrd_addr; |
| unsigned long initrd_size; |
| |
| initrd_addr = *(unsigned long*)(int*)(0xA00 + 0x218); |
| initrd_size = *(unsigned long*)(int*)(0xA00 + 0x21c); |
| if (initrd_addr && initrd_size) |
| { |
| rd_base = initrd_addr; |
| rd_size = initrd_size; |
| |
| /* check if there is a partition table */ |
| if (*(unsigned short *)(initrd_addr + 0x40) == 0xAA55) |
| { |
| if (! probe_mbr ((struct master_and_dos_boot_sector *)initrd_addr, 0, initrd_size, 0)) |
| ram_drive = 0xfe; /* partition table is valid, so let it be a harddrive */ |
| else |
| grub_printf ("\nUnrecognized partition table for RAM DRIVE; assuming floppy. Please rebuild\nit using a Microsoft-compatible FDISK tool, if the INITRD is a hard-disk image.\n"); |
| } |
| } |
| } |
| #endif /* ! STAGE1_5 */ |
| #endif /* ! GRUB_UTIL */ |
| |
| /* Start main routine here. */ |
| |
| grub_printf("Starting cmain() ... "); |
| |
| #if !defined(STAGE1_5) && !defined(GRUB_UTIL) |
| DEBUG_SLEEP |
| #endif /* ! STAGE1_5 && ! GRUB_UTIL */ |
| // cmain (); /* moved into asm.S and asmstub.c */ |
| } |