/* builtins.c - the GRUB builtin commands */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,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 stdio.h before shared.h, because we can't define WITHOUT_LIBC_STUBS here. */ #ifdef GRUB_UTIL # include #endif #include #include #include #ifdef SUPPORT_NETBOOT # define GRUB 1 # include #endif #ifdef SUPPORT_SERIAL # include # include #endif #ifdef GRUB_UTIL # include #else /* ! GRUB_UTIL */ # include # include #endif /* ! GRUB_UTIL */ #ifdef USE_MD5_PASSWORDS # include #endif /* The type of kernel loaded. */ kernel_t kernel_type; /* The boot device. */ static int bootdev; /* True when the debug mode is turned on, and false when it is turned off. */ int debug = 0; /* The default entry. */ int default_entry = 0; /* The fallback entry. */ int fallback_entryno; int fallback_entries[MAX_FALLBACK_ENTRIES]; /* The number of current entry. */ int current_entryno; /* The address for Multiboot command-line buffer. */ static char *mb_cmdline; /* The password. */ char *password; /* The password type. */ password_t password_type; /* The flag for indicating that the user is authoritative. */ int auth = 0; /* The timeout. */ int grub_timeout = -1; /* Whether to show the menu or not. */ int show_menu = 1; /* The BIOS drive map. */ static unsigned short bios_drive_map[DRIVE_MAP_SIZE + 1]; /* Prototypes for allowing straightfoward calling of builtins functions inside other functions. */ static int configfile_func (char *arg, int flags); /* Initialize the data for builtins. */ void init_builtins (void) { kernel_type = KERNEL_TYPE_NONE; /* BSD and chainloading evil hacks! */ bootdev = set_bootdev (0); mb_cmdline = (char *) MB_CMDLINE_BUF; } /* Initialize the data for the configuration file. */ void init_config (void) { default_entry = 0; password = 0; fallback_entryno = -1; fallback_entries[0] = -1; grub_timeout = -1; } /* Check a password for correctness. Returns 0 if password was correct, and a value != 0 for error, similarly to strcmp. */ int check_password (char *entered, char* expected, password_t type) { switch (type) { case PASSWORD_PLAIN: return strcmp (entered, expected); #ifdef USE_MD5_PASSWORDS case PASSWORD_MD5: return check_md5_password (entered, expected); #endif default: /* unsupported password type: be secure */ return 1; } } /* Print which sector is read when loading a file. */ static void disk_read_print_func (int sector, int offset, int length) { grub_printf ("[%d,%d,%d]", sector, offset, length); } /* blocklist */ static int blocklist_func (char *arg, int flags) { char *dummy = (char *) RAW_ADDR (0x100000); int start_sector; int num_sectors = 0; int num_entries = 0; int last_length = 0; auto void disk_read_blocklist_func (int sector, int offset, int length); /* Collect contiguous blocks into one entry as many as possible, and print the blocklist notation on the screen. */ auto void disk_read_blocklist_func (int sector, int offset, int length) { if (num_sectors > 0) { if (start_sector + num_sectors == sector && offset == 0 && last_length == SECTOR_SIZE) { num_sectors++; last_length = length; return; } else { if (last_length == SECTOR_SIZE) grub_printf ("%s%d+%d", num_entries ? "," : "", start_sector - part_start, num_sectors); else if (num_sectors > 1) grub_printf ("%s%d+%d,%d[0-%d]", num_entries ? "," : "", start_sector - part_start, num_sectors-1, start_sector + num_sectors-1 - part_start, last_length); else grub_printf ("%s%d[0-%d]", num_entries ? "," : "", start_sector - part_start, last_length); num_entries++; num_sectors = 0; } } if (offset > 0) { grub_printf("%s%d[%d-%d]", num_entries ? "," : "", sector-part_start, offset, offset+length); num_entries++; } else { start_sector = sector; num_sectors = 1; last_length = length; } } /* Open the file. */ if (! grub_open (arg)) return 1; /* Print the device name. */ grub_printf ("(%cd%d", (current_drive & 0x80) ? 'h' : 'f', current_drive & ~0x80); if ((current_partition & 0xFF0000) != 0xFF0000) grub_printf (",%d", (current_partition >> 16) & 0xFF); if ((current_partition & 0x00FF00) != 0x00FF00) grub_printf (",%c", 'a' + ((current_partition >> 8) & 0xFF)); grub_printf (")"); /* Read in the whole file to DUMMY. */ disk_read_hook = disk_read_blocklist_func; if (! grub_read (dummy, -1)) goto fail; /* The last entry may not be printed yet. Don't check if it is a * full sector, since it doesn't matter if we read too much. */ if (num_sectors > 0) grub_printf ("%s%d+%d", num_entries ? "," : "", start_sector - part_start, num_sectors); grub_printf ("\n"); fail: disk_read_hook = 0; grub_close (); return errnum; } static struct builtin builtin_blocklist = { "blocklist", blocklist_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "blocklist FILE", "Print the blocklist notation of the file FILE." }; /* boot */ static int boot_func (char *arg, int flags) { /* Clear the int15 handler if we can boot the kernel successfully. This assumes that the boot code never fails only if KERNEL_TYPE is not KERNEL_TYPE_NONE. Is this assumption is bad? */ if (kernel_type != KERNEL_TYPE_NONE) unset_int15_handler (); #ifdef SUPPORT_NETBOOT /* Shut down the networking. */ cleanup_net (); #endif switch (kernel_type) { case KERNEL_TYPE_FREEBSD: case KERNEL_TYPE_NETBSD: /* *BSD */ bsd_boot (kernel_type, bootdev, (char *) mbi.cmdline); break; case KERNEL_TYPE_LINUX: /* Linux */ linux_boot (); break; case KERNEL_TYPE_BIG_LINUX: /* Big Linux */ big_linux_boot (); break; case KERNEL_TYPE_CHAINLOADER: /* Chainloader */ /* Check if we should set the int13 handler. */ if (bios_drive_map[0] != 0) { int i; /* Search for SAVED_DRIVE. */ for (i = 0; i < DRIVE_MAP_SIZE; i++) { if (! bios_drive_map[i]) break; else if ((bios_drive_map[i] & 0xFF) == saved_drive) { /* Exchage SAVED_DRIVE with the mapped drive. */ saved_drive = (bios_drive_map[i] >> 8) & 0xFF; break; } } /* Set the handler. This is somewhat dangerous. */ set_int13_handler (bios_drive_map); } gateA20 (0); boot_drive = saved_drive; chain_stage1 (0, BOOTSEC_LOCATION, boot_part_addr); break; case KERNEL_TYPE_MULTIBOOT: /* Multiboot */ multi_boot ((int) entry_addr, (int) &mbi); break; default: errnum = ERR_BOOT_COMMAND; return 1; } return 0; } static struct builtin builtin_boot = { "boot", boot_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "boot", "Boot the OS/chain-loader which has been loaded." }; #ifdef SUPPORT_NETBOOT /* bootp */ static int bootp_func (char *arg, int flags) { int with_configfile = 0; if (grub_memcmp (arg, "--with-configfile", sizeof ("--with-configfile") - 1) == 0) { with_configfile = 1; arg = skip_to (0, arg); } if (! bootp ()) { if (errnum == ERR_NONE) errnum = ERR_DEV_VALUES; return 1; } /* Notify the configuration. */ print_network_configuration (); /* XXX: this can cause an endless loop, but there is no easy way to detect such a loop unfortunately. */ if (with_configfile) configfile_func (config_file, flags); return 0; } static struct builtin builtin_bootp = { "bootp", bootp_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "bootp [--with-configfile]", "Initialize a network device via BOOTP. If the option `--with-configfile'" " is given, try to load a configuration file specified by the 150 vendor" " tag." }; #endif /* SUPPORT_NETBOOT */ /* cat */ static int cat_func (char *arg, int flags) { char c; if (! grub_open (arg)) return 1; while (grub_read (&c, 1)) { /* Because running "cat" with a binary file can confuse the terminal, print only some characters as they are. */ if (grub_isspace (c) || (c >= ' ' && c <= '~')) grub_putchar (c); else grub_putchar ('?'); } grub_close (); return 0; } static struct builtin builtin_cat = { "cat", cat_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "cat FILE", "Print the contents of the file FILE." }; /* chainloader */ static int chainloader_func (char *arg, int flags) { int force = 0; char *file = arg; /* If the option `--force' is specified? */ if (substring ("--force", arg) <= 0) { force = 1; file = skip_to (0, arg); } /* Open the file. */ if (! grub_open (file)) { kernel_type = KERNEL_TYPE_NONE; return 1; } /* Read the first block. */ if (grub_read ((char *) BOOTSEC_LOCATION, SECTOR_SIZE) != SECTOR_SIZE) { grub_close (); kernel_type = KERNEL_TYPE_NONE; /* This below happens, if a file whose size is less than 512 bytes is loaded. */ if (errnum == ERR_NONE) errnum = ERR_EXEC_FORMAT; return 1; } /* If not loading it forcibly, check for the signature. */ if (! force && (*((unsigned short *) (BOOTSEC_LOCATION + BOOTSEC_SIG_OFFSET)) != BOOTSEC_SIGNATURE)) { grub_close (); errnum = ERR_EXEC_FORMAT; kernel_type = KERNEL_TYPE_NONE; return 1; } grub_close (); kernel_type = KERNEL_TYPE_CHAINLOADER; /* XXX: Windows evil hack. For now, only the first five letters are checked. */ if (IS_PC_SLICE_TYPE_FAT (current_slice) && ! grub_memcmp ((char *) BOOTSEC_LOCATION + BOOTSEC_BPB_SYSTEM_ID, "MSWIN", 5)) *((unsigned long *) (BOOTSEC_LOCATION + BOOTSEC_BPB_HIDDEN_SECTORS)) = part_start; errnum = ERR_NONE; return 0; } static struct builtin builtin_chainloader = { "chainloader", chainloader_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "chainloader [--force] FILE", "Load the chain-loader FILE. If --force is specified, then load it" " forcibly, whether the boot loader signature is present or not." }; /* This function could be used to debug new filesystem code. Put a file in the new filesystem and the same file in a well-tested filesystem. Then, run "cmp" with the files. If no output is obtained, probably the code is good, otherwise investigate what's wrong... */ /* cmp FILE1 FILE2 */ static int cmp_func (char *arg, int flags) { /* The filenames. */ char *file1, *file2; /* The addresses. */ char *addr1, *addr2; int i; /* The size of the file. */ int size; /* Get the filenames from ARG. */ file1 = arg; file2 = skip_to (0, arg); if (! *file1 || ! *file2) { errnum = ERR_BAD_ARGUMENT; return 1; } /* Terminate the filenames for convenience. */ nul_terminate (file1); nul_terminate (file2); /* Read the whole data from FILE1. */ addr1 = (char *) RAW_ADDR (0x100000); if (! grub_open (file1)) return 1; /* Get the size. */ size = filemax; if (grub_read (addr1, -1) != size) { grub_close (); return 1; } grub_close (); /* Read the whole data from FILE2. */ addr2 = addr1 + size; if (! grub_open (file2)) return 1; /* Check if the size of FILE2 is equal to the one of FILE2. */ if (size != filemax) { grub_printf ("Differ in size: 0x%x [%s], 0x%x [%s]\n", size, file1, filemax, file2); grub_close (); return 0; } if (! grub_read (addr2, -1)) { grub_close (); return 1; } grub_close (); /* Now compare ADDR1 with ADDR2. */ for (i = 0; i < size; i++) { if (addr1[i] != addr2[i]) grub_printf ("Differ at the offset %d: 0x%x [%s], 0x%x [%s]\n", i, (unsigned) addr1[i], file1, (unsigned) addr2[i], file2); } return 0; } static struct builtin builtin_cmp = { "cmp", cmp_func, BUILTIN_CMDLINE, "cmp FILE1 FILE2", "Compare the file FILE1 with the FILE2 and inform the different values" " if any." }; /* color */ /* Set new colors used for the menu interface. Support two methods to specify a color name: a direct integer representation and a symbolic color name. An example of the latter is "blink-light-gray/blue". */ static int color_func (char *arg, int flags) { char *normal; char *highlight; int new_normal_color; int new_highlight_color; static char *color_list[16] = { "black", "blue", "green", "cyan", "red", "magenta", "brown", "light-gray", "dark-gray", "light-blue", "light-green", "light-cyan", "light-red", "light-magenta", "yellow", "white" }; auto int color_number (char *str); /* Convert the color name STR into the magical number. */ auto int color_number (char *str) { char *ptr; int i; int color = 0; /* Find the separator. */ for (ptr = str; *ptr && *ptr != '/'; ptr++) ; /* If not found, return -1. */ if (! *ptr) return -1; /* Terminate the string STR. */ *ptr++ = 0; /* If STR contains the prefix "blink-", then set the `blink' bit in COLOR. */ if (substring ("blink-", str) <= 0) { color = 0x80; str += 6; } /* Search for the color name. */ for (i = 0; i < 16; i++) if (grub_strcmp (color_list[i], str) == 0) { color |= i; break; } if (i == 16) return -1; str = ptr; nul_terminate (str); /* Search for the color name. */ for (i = 0; i < 8; i++) if (grub_strcmp (color_list[i], str) == 0) { color |= i << 4; break; } if (i == 8) return -1; return color; } normal = arg; highlight = skip_to (0, arg); new_normal_color = color_number (normal); if (new_normal_color < 0 && ! safe_parse_maxint (&normal, &new_normal_color)) return 1; /* The second argument is optional, so set highlight_color to inverted NORMAL_COLOR. */ if (! *highlight) new_highlight_color = ((new_normal_color >> 4) | ((new_normal_color & 0xf) << 4)); else { new_highlight_color = color_number (highlight); if (new_highlight_color < 0 && ! safe_parse_maxint (&highlight, &new_highlight_color)) return 1; } if (current_term->setcolor) current_term->setcolor (new_normal_color, new_highlight_color); return 0; } static struct builtin builtin_color = { "color", color_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "color NORMAL [HIGHLIGHT]", "Change the menu colors. The color NORMAL is used for most" " lines in the menu, and the color HIGHLIGHT is used to highlight the" " line where the cursor points. If you omit HIGHLIGHT, then the" " inverted color of NORMAL is used for the highlighted line." " The format of a color is \"FG/BG\". FG and BG are symbolic color names." " A symbolic color name must be one of these: black, blue, green," " cyan, red, magenta, brown, light-gray, dark-gray, light-blue," " light-green, light-cyan, light-red, light-magenta, yellow and white." " But only the first eight names can be used for BG. You can prefix" " \"blink-\" to FG if you want a blinking foreground color." }; /* configfile */ static int configfile_func (char *arg, int flags) { char *new_config = config_file; /* Check if the file ARG is present. */ if (! grub_open (arg)) return 1; grub_close (); /* Copy ARG to CONFIG_FILE. */ while ((*new_config++ = *arg++) != 0) ; #ifdef GRUB_UTIL /* Force to load the configuration file. */ use_config_file = 1; #endif /* Make sure that the user will not be authoritative. */ auth = 0; /* Restart cmain. */ grub_longjmp (restart_env, 0); /* Never reach here. */ return 0; } static struct builtin builtin_configfile = { "configfile", configfile_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "configfile FILE", "Load FILE as the configuration file." }; /* debug */ static int debug_func (char *arg, int flags) { if (debug) { debug = 0; grub_printf (" Debug mode is turned off\n"); } else { debug = 1; grub_printf (" Debug mode is turned on\n"); } return 0; } static struct builtin builtin_debug = { "debug", debug_func, BUILTIN_CMDLINE, "debug", "Turn on/off the debug mode." }; /* default */ static int default_func (char *arg, int flags) { #ifndef SUPPORT_DISKLESS if (grub_strcmp (arg, "saved") == 0) { default_entry = saved_entryno; return 0; } #endif /* SUPPORT_DISKLESS */ if (! safe_parse_maxint (&arg, &default_entry)) return 1; return 0; } static struct builtin builtin_default = { "default", default_func, BUILTIN_MENU, #if 0 "default [NUM | `saved']", "Set the default entry to entry number NUM (if not specified, it is" " 0, the first entry) or the entry number saved by savedefault." #endif }; #ifdef GRUB_UTIL /* device */ static int device_func (char *arg, int flags) { char *drive = arg; char *device; /* Get the drive number from DRIVE. */ if (! set_device (drive)) return 1; /* Get the device argument. */ device = skip_to (0, drive); /* Terminate DEVICE. */ nul_terminate (device); if (! *device || ! check_device (device)) { errnum = ERR_FILE_NOT_FOUND; return 1; } assign_device_name (current_drive, device); return 0; } static struct builtin builtin_device = { "device", device_func, BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "device DRIVE DEVICE", "Specify DEVICE as the actual drive for a BIOS drive DRIVE. This command" " can be used only in the grub shell." }; #endif /* GRUB_UTIL */ #ifdef SUPPORT_NETBOOT /* dhcp */ static int dhcp_func (char *arg, int flags) { /* For now, this is an alias for bootp. */ return bootp_func (arg, flags); } static struct builtin builtin_dhcp = { "dhcp", dhcp_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "dhcp", "Initialize a network device via DHCP." }; #endif /* SUPPORT_NETBOOT */ /* displayapm */ static int displayapm_func (char *arg, int flags) { if (mbi.flags & MB_INFO_APM_TABLE) { grub_printf ("APM BIOS information:\n" " Version: 0x%x\n" " 32-bit CS: 0x%x\n" " Offset: 0x%x\n" " 16-bit CS: 0x%x\n" " 16-bit DS: 0x%x\n" " 32-bit CS length: 0x%x\n" " 16-bit CS length: 0x%x\n" " 16-bit DS length: 0x%x\n", (unsigned) apm_bios_info.version, (unsigned) apm_bios_info.cseg, apm_bios_info.offset, (unsigned) apm_bios_info.cseg_16, (unsigned) apm_bios_info.dseg_16, (unsigned) apm_bios_info.cseg_len, (unsigned) apm_bios_info.cseg_16_len, (unsigned) apm_bios_info.dseg_16_len); } else { grub_printf ("No APM BIOS found or probe failed\n"); } return 0; } static struct builtin builtin_displayapm = { "displayapm", displayapm_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "displayapm", "Display APM BIOS information." }; /* displaymem */ static int displaymem_func (char *arg, int flags) { if (get_eisamemsize () != -1) grub_printf (" EISA Memory BIOS Interface is present\n"); if (get_mmap_entry ((void *) SCRATCHADDR, 0) != 0 || *((int *) SCRATCHADDR) != 0) grub_printf (" Address Map BIOS Interface is present\n"); grub_printf (" Lower memory: %uK, " "Upper memory (to first chipset hole): %uK\n", mbi.mem_lower, mbi.mem_upper); if (mbi.flags & MB_INFO_MEM_MAP) { struct AddrRangeDesc *map = (struct AddrRangeDesc *) mbi.mmap_addr; int end_addr = mbi.mmap_addr + mbi.mmap_length; grub_printf (" [Address Range Descriptor entries " "immediately follow (values are 64-bit)]\n"); while (end_addr > (int) map) { char *str; if (map->Type == MB_ARD_MEMORY) str = "Usable RAM"; else str = "Reserved"; grub_printf (" %s: Base Address: 0x%x X 4GB + 0x%x,\n" " Length: 0x%x X 4GB + 0x%x bytes\n", str, (unsigned long) (map->BaseAddr >> 32), (unsigned long) (map->BaseAddr & 0xFFFFFFFF), (unsigned long) (map->Length >> 32), (unsigned long) (map->Length & 0xFFFFFFFF)); map = ((struct AddrRangeDesc *) (((int) map) + 4 + map->size)); } } return 0; } static struct builtin builtin_displaymem = { "displaymem", displaymem_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "displaymem", "Display what GRUB thinks the system address space map of the" " machine is, including all regions of physical RAM installed." }; /* dump FROM TO */ #ifdef GRUB_UTIL static int dump_func (char *arg, int flags) { char *from, *to; FILE *fp; char c; from = arg; to = skip_to (0, arg); if (! *from || ! *to) { errnum = ERR_BAD_ARGUMENT; return 1; } nul_terminate (from); nul_terminate (to); if (! grub_open (from)) return 1; fp = fopen (to, "w"); if (! fp) { errnum = ERR_WRITE; return 1; } while (grub_read (&c, 1)) if (fputc (c, fp) == EOF) { errnum = ERR_WRITE; fclose (fp); return 1; } if (fclose (fp) == EOF) { errnum = ERR_WRITE; return 1; } grub_close (); return 0; } static struct builtin builtin_dump = { "dump", dump_func, BUILTIN_CMDLINE, "dump FROM TO", "Dump the contents of the file FROM to the file TO. FROM must be" " a GRUB file and TO must be an OS file." }; #endif /* GRUB_UTIL */ static char embed_info[32]; /* embed */ /* Embed a Stage 1.5 in the first cylinder after MBR or in the bootloader block in a FFS. */ static int embed_func (char *arg, int flags) { char *stage1_5; char *device; char *stage1_5_buffer = (char *) RAW_ADDR (0x100000); int len, size; int sector; stage1_5 = arg; device = skip_to (0, stage1_5); /* Open a Stage 1.5. */ if (! grub_open (stage1_5)) return 1; /* Read the whole of the Stage 1.5. */ len = grub_read (stage1_5_buffer, -1); grub_close (); if (errnum) return 1; size = (len + SECTOR_SIZE - 1) / SECTOR_SIZE; /* Get the device where the Stage 1.5 will be embedded. */ set_device (device); if (errnum) return 1; if (current_partition == 0xFFFFFF) { /* Embed it after the MBR. */ char mbr[SECTOR_SIZE]; char ezbios_check[2*SECTOR_SIZE]; int i; /* Open the partition. */ if (! open_partition ()) return 1; /* No floppy has MBR. */ if (! (current_drive & 0x80)) { errnum = ERR_DEV_VALUES; return 1; } /* Read the MBR of CURRENT_DRIVE. */ if (! rawread (current_drive, PC_MBR_SECTOR, 0, SECTOR_SIZE, mbr)) return 1; /* Sanity check. */ if (! PC_MBR_CHECK_SIG (mbr)) { errnum = ERR_BAD_PART_TABLE; return 1; } /* Check if the disk can store the Stage 1.5. */ for (i = 0; i < 4; i++) if (PC_SLICE_TYPE (mbr, i) && PC_SLICE_START (mbr, i) - 1 < size) { errnum = ERR_NO_DISK_SPACE; return 1; } /* Check for EZ-BIOS signature. It should be in the third * sector, but due to remapping it can appear in the second, so * load and check both. */ if (! rawread (current_drive, 1, 0, 2 * SECTOR_SIZE, ezbios_check)) return 1; if (! memcmp (ezbios_check + 3, "AERMH", 5) || ! memcmp (ezbios_check + 512 + 3, "AERMH", 5)) { /* The space after the MBR is used by EZ-BIOS which we must * not overwrite. */ errnum = ERR_NO_DISK_SPACE; return 1; } sector = 1; } else { /* Embed it in the bootloader block in the filesystem. */ int start_sector; /* Open the partition. */ if (! open_device ()) return 1; /* Check if the current slice supports embedding. */ if (fsys_table[fsys_type].embed_func == 0 || ! fsys_table[fsys_type].embed_func (&start_sector, size)) { errnum = ERR_DEV_VALUES; return 1; } sector = part_start + start_sector; } /* Clear the cache. */ buf_track = -1; /* Now perform the embedding. */ if (! devwrite (sector - part_start, size, stage1_5_buffer)) return 1; grub_printf (" %d sectors are embedded.\n", size); grub_sprintf (embed_info, "%d+%d", sector - part_start, size); return 0; } static struct builtin builtin_embed = { "embed", embed_func, BUILTIN_CMDLINE, "embed STAGE1_5 DEVICE", "Embed the Stage 1.5 STAGE1_5 in the sectors after MBR if DEVICE" " is a drive, or in the \"bootloader\" area if DEVICE is a FFS partition." " Print the number of sectors which STAGE1_5 occupies if successful." }; /* begin first part of sburtchin eptedit patch (new function - add lines) */ /* epteditalpha PART SLOT TYPE BCYL BHEAD BSEC ECYL EHEAD ESEC RSEC TSEC */ static int epteditalpha_func (char *arg, int flags) { int ept_slot, new_type, rel_sec, tot_sec; int beg_cyl, beg_head, beg_sec, end_cyl, end_head, end_sec; int start_cl, start_ch, start_dh; int end_cl, end_ch, end_dh; unsigned long part = 0xFFFFFF; unsigned long start, len, offset, ext_offset; int entry, type; char mbr[512]; /* Convert CHS address to the INT 13 format. */ auto void chs_to_int13 (int cylinder, int head, int sector, int *cl, int *ch, int *dh); void chs_to_int13 (int cylinder, int head, int sector, int *cl, int *ch, int *dh) { int bufgeomcylinders; bufgeomcylinders = buf_geom.cylinders; bufgeomcylinders += 2; if (cylinder >= bufgeomcylinders) cylinder = bufgeomcylinders - 1; *cl = sector | ((cylinder & 0x300) >> 2); *ch = cylinder & 0xFF; *dh = head; } /* Get the drive and the partition. */ if (! set_device (arg)) return 1; /* The drive must be a hard disk. */ if (! (current_drive & 0x80)) { errnum = ERR_BAD_ARGUMENT; return 1; } /* The partition must be a PC slice. */ if ((current_partition >> 16) == 0xFF || (current_partition & 0xFFFF) != 0xFFFF) { errnum = ERR_BAD_ARGUMENT; return 1; } /* Get the EPT slot. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &ept_slot)) return 1; /* The EPT slot must be "current" or "next". */ if ( !(ept_slot == 'c' || ept_slot == 'n') ) { errnum = ERR_BAD_ARGUMENT; return 1; } /* Get the new partition type. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &new_type)) return 1; /* The partition type is unsigned char. */ if (new_type > 0xFF) { errnum = ERR_BAD_ARGUMENT; return 1; } /* Get the new begin cylinder. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &beg_cyl)) return 1; /* Check for start past end of disk. */ if (beg_cyl >= buf_geom.total_sectors / (buf_geom.heads * buf_geom.sectors) ) { errnum = ERR_GEOM; return 1; } /* Get the new begin head. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &beg_head)) return 1; /* Check for invalid head value. */ if (beg_head >= buf_geom.heads) { errnum = ERR_GEOM; return 1; } /* Get the new begin sector. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &beg_sec)) return 1; /* Check for invalid sector value. */ if (beg_sec > buf_geom.sectors) { errnum = ERR_GEOM; return 1; } /* Get the new end cylinder. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &end_cyl)) return 1; /* Check for end past end of disk. */ if (end_cyl >= buf_geom.total_sectors / (buf_geom.heads * buf_geom.sectors) ) { errnum = ERR_GEOM; return 1; } /* Get the new end head. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &end_head)) return 1; /* Check for invalid head value. */ if (end_head >= buf_geom.heads) { errnum = ERR_GEOM; return 1; } /* Get the new end sector. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &end_sec)) return 1; /* Check for invalid sector value. */ if (end_sec > buf_geom.sectors) { errnum = ERR_GEOM; return 1; } /* Get the new relative sectors. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &rel_sec)) return 1; /* Get the new total sectors. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &tot_sec)) return 1; /* Check for unreasonable relative sectors + total sectors. */ if (rel_sec + tot_sec > buf_geom.total_sectors) { errnum = ERR_GEOM; return 1; } /* Store the partition information in the MBR. */ chs_to_int13 (beg_cyl, beg_head, beg_sec, &start_cl, &start_ch, &start_dh); chs_to_int13 (end_cyl, end_head, end_sec, &end_cl, &end_ch, &end_dh); /* Look for the partition. */ while (next_partition (current_drive, 0xFFFFFF, &part, &type, &start, &len, &offset, &entry, &ext_offset, mbr)) { if (part == current_partition) { /* Found. */ /* Adjust for "current" or "next" slot. */ if (ept_slot == 'n') entry++; /* Set the new values for SLOT in EPT. */ PC_SLICE_FLAG (mbr, entry) = 0; PC_SLICE_HEAD (mbr, entry) = start_dh; PC_SLICE_SEC (mbr, entry) = start_cl; PC_SLICE_CYL (mbr, entry) = start_ch; PC_SLICE_TYPE (mbr, entry) = new_type; PC_SLICE_EHEAD (mbr, entry) = end_dh; PC_SLICE_ESEC (mbr, entry) = end_cl; PC_SLICE_ECYL (mbr, entry) = end_ch; PC_SLICE_START (mbr, entry) = rel_sec; PC_SLICE_LENGTH (mbr, entry) = tot_sec; /* Write back the MBR to the disk. */ buf_track = -1; if (! rawwrite (current_drive, offset, mbr)) return 1; /* Succeed. */ return 0; } } /* The partition was not found. ERRNUM was set by next_partition. */ return 1; } static struct builtin builtin_epteditalpha = { "epteditalpha", epteditalpha_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "epteditalpha PART SLOT TYPE BCYL BHEAD BSEC ECYL EHEAD ESEC RSEC TSEC", "Change data in SLOT (""c"" [current] or ""n"" [next]) in extended partition table" "of logical partition PART to type TYPE, begin cylinder BCYL, begin head" "BHEAD, begin sector BSEC, end cylinder ECYL, end head EHEAD, end" "sector ESEC, relative sectors RSEC, and total sectors TSEC" }; /* end first part of sburtchin eptedit patch (new function) */ /* fallback */ static int fallback_func (char *arg, int flags) { int i = 0; while (*arg) { int entry; int j; if (! safe_parse_maxint (&arg, &entry)) return 1; /* Remove duplications to prevent infinite looping. */ for (j = 0; j < i; j++) if (entry == fallback_entries[j]) break; if (j != i) continue; fallback_entries[i++] = entry; if (i == MAX_FALLBACK_ENTRIES) break; arg = skip_to (0, arg); } if (i < MAX_FALLBACK_ENTRIES) fallback_entries[i] = -1; fallback_entryno = (i == 0) ? -1 : 0; return 0; } static struct builtin builtin_fallback = { "fallback", fallback_func, BUILTIN_MENU, #if 0 "fallback NUM...", "Go into unattended boot mode: if the default boot entry has any" " errors, instead of waiting for the user to do anything, it" " immediately starts over using the NUM entry (same numbering as the" " `default' command). This obviously won't help if the machine" " was rebooted by a kernel that GRUB loaded." #endif }; /* find */ /* Search for the filename ARG in all of partitions. */ static int find_func (char *arg, int flags) { char *filename = arg; unsigned long drive; unsigned long tmp_drive = saved_drive; unsigned long tmp_partition = saved_partition; int got_file = 0; /* Floppies. */ for (drive = 0; drive < 8; drive++) { current_drive = drive; current_partition = 0xFFFFFF; if (open_device ()) { saved_drive = current_drive; saved_partition = current_partition; if (grub_open (filename)) { grub_close (); grub_printf (" (fd%d)\n", drive); got_file = 1; } } errnum = ERR_NONE; } /* Hard disks. */ for (drive = 0x80; drive < 0x88; drive++) { unsigned long part = 0xFFFFFF; unsigned long start, len, offset, ext_offset; int type, entry; char buf[SECTOR_SIZE]; current_drive = drive; while (next_partition (drive, 0xFFFFFF, &part, &type, &start, &len, &offset, &entry, &ext_offset, buf)) { if (type != PC_SLICE_TYPE_NONE && ! IS_PC_SLICE_TYPE_BSD (type) && ! IS_PC_SLICE_TYPE_EXTENDED (type)) { current_partition = part; if (open_device ()) { saved_drive = current_drive; saved_partition = current_partition; if (grub_open (filename)) { int bsd_part = (part >> 8) & 0xFF; int pc_slice = part >> 16; grub_close (); if (bsd_part == 0xFF) grub_printf (" (hd%d,%d)\n", drive - 0x80, pc_slice); else grub_printf (" (hd%d,%d,%c)\n", drive - 0x80, pc_slice, bsd_part + 'a'); got_file = 1; } } } /* We want to ignore any error here. */ errnum = ERR_NONE; } /* next_partition always sets ERRNUM in the last call, so clear it. */ errnum = ERR_NONE; } saved_drive = tmp_drive; saved_partition = tmp_partition; if (got_file) { errnum = ERR_NONE; return 0; } errnum = ERR_FILE_NOT_FOUND; return 1; } static struct builtin builtin_find = { "find", find_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "find FILENAME", "Search for the filename FILENAME in all of partitions and print the list of" " the devices which contain the file." }; /* fstest */ static int fstest_func (char *arg, int flags) { if (disk_read_hook) { disk_read_hook = NULL; printf (" Filesystem tracing is now off\n"); } else { disk_read_hook = disk_read_print_func; printf (" Filesystem tracing is now on\n"); } return 0; } static struct builtin builtin_fstest = { "fstest", fstest_func, BUILTIN_CMDLINE, "fstest", "Toggle filesystem test mode." }; /* geometry */ static int geometry_func (char *arg, int flags) { struct geometry geom; char *msg; char *device = arg; #ifdef GRUB_UTIL char *ptr; #endif /* Get the device number. */ set_device (device); if (errnum) return 1; /* Check for the geometry. */ if (get_diskinfo (current_drive, &geom)) { errnum = ERR_NO_DISK; return 1; } /* Attempt to read the first sector, because some BIOSes turns out not to support LBA even though they set the bit 0 in the support bitmap, only after reading something actually. */ if (biosdisk (BIOSDISK_READ, current_drive, &geom, 0, 1, SCRATCHSEG)) { errnum = ERR_READ; return 1; } #ifdef GRUB_UTIL ptr = skip_to (0, device); if (*ptr) { char *cylinder, *head, *sector, *total_sector; int num_cylinder, num_head, num_sector, num_total_sector; cylinder = ptr; head = skip_to (0, cylinder); sector = skip_to (0, head); total_sector = skip_to (0, sector); if (! safe_parse_maxint (&cylinder, &num_cylinder) || ! safe_parse_maxint (&head, &num_head) || ! safe_parse_maxint (§or, &num_sector)) return 1; disks[current_drive].cylinders = num_cylinder; disks[current_drive].heads = num_head; disks[current_drive].sectors = num_sector; if (safe_parse_maxint (&total_sector, &num_total_sector)) disks[current_drive].total_sectors = num_total_sector; else disks[current_drive].total_sectors = num_cylinder * num_head * num_sector; errnum = 0; geom = disks[current_drive]; buf_drive = -1; } #endif /* GRUB_UTIL */ #ifdef GRUB_UTIL msg = device_map[current_drive]; #else if (geom.flags & BIOSDISK_FLAG_LBA_EXTENSION) msg = "LBA"; else msg = "CHS"; #endif grub_printf ("drive 0x%x: C/H/S = %d/%d/%d, " "The number of sectors = %d, %s\n", current_drive, geom.cylinders, geom.heads, geom.sectors, geom.total_sectors, msg); real_open_partition (1); return 0; } static struct builtin builtin_geometry = { "geometry", geometry_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "geometry DRIVE [CYLINDER HEAD SECTOR [TOTAL_SECTOR]]", "Print the information for a drive DRIVE. In the grub shell, you can" " set the geometry of the drive arbitrarily. The number of the cylinders," " the one of the heads, the one of the sectors and the one of the total" " sectors are set to CYLINDER, HEAD, SECTOR and TOTAL_SECTOR," " respectively. If you omit TOTAL_SECTOR, then it will be calculated based" " on the C/H/S values automatically." }; /* halt */ static int halt_func (char *arg, int flags) { int no_apm; no_apm = (grub_memcmp (arg, "--no-apm", 8) == 0); grub_halt (no_apm); /* Never reach here. */ return 1; } static struct builtin builtin_halt = { "halt", halt_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "halt [--no-apm]", "Halt your system. If APM is avaiable on it, turn off the power using" " the APM BIOS, unless you specify the option `--no-apm'." }; /* help */ #define MAX_SHORT_DOC_LEN 39 #define MAX_LONG_DOC_LEN 66 static int help_func (char *arg, int flags) { int all = 0; if (grub_memcmp (arg, "--all", sizeof ("--all") - 1) == 0) { all = 1; arg = skip_to (0, arg); } if (! *arg) { /* Invoked with no argument. Print the list of the short docs. */ struct builtin **builtin; int left = 1; for (builtin = builtin_table; *builtin != 0; builtin++) { int len; int i; /* If this cannot be used in the command-line interface, skip this. */ if (! ((*builtin)->flags & BUILTIN_CMDLINE)) continue; /* If this doesn't need to be listed automatically and "--all" is not specified, skip this. */ if (! all && ! ((*builtin)->flags & BUILTIN_HELP_LIST)) continue; len = grub_strlen ((*builtin)->short_doc); /* If the length of SHORT_DOC is too long, truncate it. */ if (len > MAX_SHORT_DOC_LEN - 1) len = MAX_SHORT_DOC_LEN - 1; for (i = 0; i < len; i++) grub_putchar ((*builtin)->short_doc[i]); for (; i < MAX_SHORT_DOC_LEN; i++) grub_putchar (' '); if (! left) grub_putchar ('\n'); left = ! left; } /* If the last entry was at the left column, no newline was printed at the end. */ if (! left) grub_putchar ('\n'); } else { /* Invoked with one or more patterns. */ do { struct builtin **builtin; char *next_arg; /* Get the next argument. */ next_arg = skip_to (0, arg); /* Terminate ARG. */ nul_terminate (arg); for (builtin = builtin_table; *builtin; builtin++) { /* Skip this if this is only for the configuration file. */ if (! ((*builtin)->flags & BUILTIN_CMDLINE)) continue; if (substring (arg, (*builtin)->name) < 1) { char *doc = (*builtin)->long_doc; /* At first, print the name and the short doc. */ grub_printf ("%s: %s\n", (*builtin)->name, (*builtin)->short_doc); /* Print the long doc. */ while (*doc) { int len = grub_strlen (doc); int i; /* If LEN is too long, fold DOC. */ if (len > MAX_LONG_DOC_LEN) { /* Fold this line at the position of a space. */ for (len = MAX_LONG_DOC_LEN; len > 0; len--) if (doc[len - 1] == ' ') break; } grub_printf (" "); for (i = 0; i < len; i++) grub_putchar (*doc++); grub_putchar ('\n'); } } } arg = next_arg; } while (*arg); } return 0; } static struct builtin builtin_help = { "help", help_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "help [--all] [PATTERN ...]", "Display helpful information about builtin commands. Not all commands" " aren't shown without the option `--all'." }; /* hiddenmenu */ static int hiddenmenu_func (char *arg, int flags) { show_menu = 0; return 0; } static struct builtin builtin_hiddenmenu = { "hiddenmenu", hiddenmenu_func, BUILTIN_MENU, #if 0 "hiddenmenu", "Hide the menu." #endif }; /* hide */ static int hide_func (char *arg, int flags) { if (! set_device (arg)) return 1; if (! set_partition_hidden_flag (1)) return 1; return 0; } static struct builtin builtin_hide = { "hide", hide_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "hide PARTITION", "Hide PARTITION by setting the \"hidden\" bit in" " its partition type code." }; #ifdef SUPPORT_NETBOOT /* ifconfig */ static int ifconfig_func (char *arg, int flags) { char *svr = 0, *ip = 0, *gw = 0, *sm = 0; if (! eth_probe ()) { grub_printf ("No ethernet card found.\n"); errnum = ERR_DEV_VALUES; return 1; } while (*arg) { if (! grub_memcmp ("--server=", arg, sizeof ("--server=") - 1)) svr = arg + sizeof("--server=") - 1; else if (! grub_memcmp ("--address=", arg, sizeof ("--address=") - 1)) ip = arg + sizeof ("--address=") - 1; else if (! grub_memcmp ("--gateway=", arg, sizeof ("--gateway=") - 1)) gw = arg + sizeof ("--gateway=") - 1; else if (! grub_memcmp ("--mask=", arg, sizeof("--mask=") - 1)) sm = arg + sizeof ("--mask=") - 1; else { errnum = ERR_BAD_ARGUMENT; return 1; } arg = skip_to (0, arg); } if (! ifconfig (ip, sm, gw, svr)) { errnum = ERR_BAD_ARGUMENT; return 1; } print_network_configuration (); return 0; } static struct builtin builtin_ifconfig = { "ifconfig", ifconfig_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "ifconfig [--address=IP] [--gateway=IP] [--mask=MASK] [--server=IP]", "Configure the IP address, the netmask, the gateway and the server" " address or print current network configuration." }; #endif /* SUPPORT_NETBOOT */ /* impsprobe */ static int impsprobe_func (char *arg, int flags) { #ifdef GRUB_UTIL /* In the grub shell, we cannot probe IMPS. */ errnum = ERR_UNRECOGNIZED; return 1; #else /* ! GRUB_UTIL */ if (!imps_probe ()) printf (" No MPS information found or probe failed\n"); return 0; #endif /* ! GRUB_UTIL */ } static struct builtin builtin_impsprobe = { "impsprobe", impsprobe_func, BUILTIN_CMDLINE, "impsprobe", "Probe the Intel Multiprocessor Specification 1.1 or 1.4" " configuration table and boot the various CPUs which are found into" " a tight loop." }; /* initrd */ static int initrd_func (char *arg, int flags) { switch (kernel_type) { case KERNEL_TYPE_LINUX: case KERNEL_TYPE_BIG_LINUX: if (! load_initrd (arg)) return 1; break; default: errnum = ERR_NEED_LX_KERNEL; return 1; } return 0; } static struct builtin builtin_initrd = { "initrd", initrd_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "initrd FILE [ARG ...]", "Load an initial ramdisk FILE for a Linux format boot image and set the" " appropriate parameters in the Linux setup area in memory." }; /* install */ static int install_func (char *arg, int flags) { char *stage1_file, *dest_dev, *file, *addr; char *stage1_buffer = (char *) RAW_ADDR (0x100000); char *stage2_buffer = stage1_buffer + SECTOR_SIZE; char *old_sect = stage2_buffer + SECTOR_SIZE; char *stage2_first_buffer = old_sect + SECTOR_SIZE; char *stage2_second_buffer = stage2_first_buffer + SECTOR_SIZE; /* XXX: Probably SECTOR_SIZE is reasonable. */ char *config_filename = stage2_second_buffer + SECTOR_SIZE; char *dummy = config_filename + SECTOR_SIZE; int new_drive = GRUB_INVALID_DRIVE; int dest_drive, dest_partition, dest_sector; int src_drive, src_partition, src_part_start; int i; struct geometry dest_geom, src_geom; int saved_sector; int stage2_first_sector, stage2_second_sector; char *ptr; int installaddr, installlist; /* Point to the location of the name of a configuration file in Stage 2. */ char *config_file_location; /* If FILE is a Stage 1.5? */ int is_stage1_5 = 0; /* Must call grub_close? */ int is_open = 0; /* If LBA is forced? */ int is_force_lba = 0; /* Was the last sector full? */ int last_length = SECTOR_SIZE; #ifdef GRUB_UTIL /* If the Stage 2 is in a partition mounted by an OS, this will store the filename under the OS. */ char *stage2_os_file = 0; #endif /* GRUB_UTIL */ auto void disk_read_savesect_func (int sector, int offset, int length); auto void disk_read_blocklist_func (int sector, int offset, int length); /* Save the first sector of Stage2 in STAGE2_SECT. */ auto void disk_read_savesect_func (int sector, int offset, int length) { if (debug) printf ("[%d]", sector); /* ReiserFS has files which sometimes contain data not aligned on sector boundaries. Returning an error is better than silently failing. */ if (offset != 0 || length != SECTOR_SIZE) errnum = ERR_UNALIGNED; saved_sector = sector; } /* Write SECTOR to INSTALLLIST, and update INSTALLADDR and INSTALLSECT. */ auto void disk_read_blocklist_func (int sector, int offset, int length) { if (debug) printf("[%d]", sector); if (offset != 0 || last_length != SECTOR_SIZE) { /* We found a non-sector-aligned data block. */ errnum = ERR_UNALIGNED; return; } last_length = length; if (*((unsigned long *) (installlist - 4)) + *((unsigned short *) installlist) != sector || installlist == (int) stage2_first_buffer + SECTOR_SIZE + 4) { installlist -= 8; if (*((unsigned long *) (installlist - 8))) errnum = ERR_WONT_FIT; else { *((unsigned short *) (installlist + 2)) = (installaddr >> 4); *((unsigned long *) (installlist - 4)) = sector; } } *((unsigned short *) installlist) += 1; installaddr += 512; } /* First, check the GNU-style long option. */ while (1) { if (grub_memcmp ("--force-lba", arg, sizeof ("--force-lba") - 1) == 0) { is_force_lba = 1; arg = skip_to (0, arg); } #ifdef GRUB_UTIL else if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0) { stage2_os_file = arg + sizeof ("--stage2=") - 1; arg = skip_to (0, arg); nul_terminate (stage2_os_file); } #endif /* GRUB_UTIL */ else break; } stage1_file = arg; dest_dev = skip_to (0, stage1_file); if (*dest_dev == 'd') { new_drive = 0; dest_dev = skip_to (0, dest_dev); } file = skip_to (0, dest_dev); addr = skip_to (0, file); /* Get the installation address. */ if (! safe_parse_maxint (&addr, &installaddr)) { /* ADDR is not specified. */ installaddr = 0; ptr = addr; errnum = 0; } else ptr = skip_to (0, addr); #ifndef NO_DECOMPRESSION /* Do not decompress Stage 1 or Stage 2. */ no_decompression = 1; #endif /* Read Stage 1. */ is_open = grub_open (stage1_file); if (! is_open || ! grub_read (stage1_buffer, SECTOR_SIZE) == SECTOR_SIZE) goto fail; /* Read the old sector from DEST_DEV. */ if (! set_device (dest_dev) || ! open_partition () || ! devread (0, 0, SECTOR_SIZE, old_sect)) goto fail; /* Store the information for the destination device. */ dest_drive = current_drive; dest_partition = current_partition; dest_geom = buf_geom; dest_sector = part_start; /* Copy the possible DOS BPB, 59 bytes at byte offset 3. */ grub_memmove (stage1_buffer + BOOTSEC_BPB_OFFSET, old_sect + BOOTSEC_BPB_OFFSET, BOOTSEC_BPB_LENGTH); /* If for a hard disk, copy the possible MBR/extended part table. */ if (dest_drive & 0x80) grub_memmove (stage1_buffer + STAGE1_WINDOWS_NT_MAGIC, old_sect + STAGE1_WINDOWS_NT_MAGIC, STAGE1_PARTEND - STAGE1_WINDOWS_NT_MAGIC); /* Check for the version and the signature of Stage 1. */ if (*((short *)(stage1_buffer + STAGE1_VER_MAJ_OFFS)) != COMPAT_VERSION || (*((unsigned short *) (stage1_buffer + BOOTSEC_SIG_OFFSET)) != BOOTSEC_SIGNATURE)) { errnum = ERR_BAD_VERSION; goto fail; } /* This below is not true any longer. But should we leave this alone? */ /* If DEST_DRIVE is a floppy, Stage 2 must have the iteration probe routine. */ if (! (dest_drive & 0x80) && (*((unsigned char *) (stage1_buffer + BOOTSEC_PART_OFFSET)) == 0x80 || stage1_buffer[BOOTSEC_PART_OFFSET] == 0)) { errnum = ERR_BAD_VERSION; goto fail; } grub_close (); /* Open Stage 2. */ is_open = grub_open (file); if (! is_open) goto fail; src_drive = current_drive; src_partition = current_partition; src_part_start = part_start; src_geom = buf_geom; if (! new_drive) new_drive = src_drive; else if (src_drive != dest_drive) grub_printf ("Warning: the option `d' was not used, but the Stage 1 will" " be installed on a\ndifferent drive than the drive where" " the Stage 2 resides.\n"); /* Set the boot drive. */ *((unsigned char *) (stage1_buffer + STAGE1_BOOT_DRIVE)) = new_drive; /* Set the "force LBA" flag. */ *((unsigned char *) (stage1_buffer + STAGE1_FORCE_LBA)) = is_force_lba; /* If DEST_DRIVE is a hard disk, enable the workaround, which is for buggy BIOSes which don't pass boot drive correctly. Instead, they pass 0x00 or 0x01 even when booted from 0x80. */ if (dest_drive & BIOS_FLAG_FIXED_DISK) /* Replace the jmp (2 bytes) with double nop's. */ *((unsigned short *) (stage1_buffer + STAGE1_BOOT_DRIVE_CHECK)) = 0x9090; /* Read the first sector of Stage 2. */ disk_read_hook = disk_read_savesect_func; if (grub_read (stage2_first_buffer, SECTOR_SIZE) != SECTOR_SIZE) goto fail; stage2_first_sector = saved_sector; /* Read the second sector of Stage 2. */ if (grub_read (stage2_second_buffer, SECTOR_SIZE) != SECTOR_SIZE) goto fail; stage2_second_sector = saved_sector; /* Check for the version of Stage 2. */ if (*((short *) (stage2_second_buffer + STAGE2_VER_MAJ_OFFS)) != COMPAT_VERSION) { errnum = ERR_BAD_VERSION; goto fail; } /* Check for the Stage 2 id. */ if (stage2_second_buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2) is_stage1_5 = 1; /* If INSTALLADDR is not specified explicitly in the command-line, determine it by the Stage 2 id. */ if (! installaddr) { if (! is_stage1_5) /* Stage 2. */ installaddr = 0x8000; else /* Stage 1.5. */ installaddr = 0x2000; } *((unsigned long *) (stage1_buffer + STAGE1_STAGE2_SECTOR)) = stage2_first_sector; *((unsigned short *) (stage1_buffer + STAGE1_STAGE2_ADDRESS)) = installaddr; *((unsigned short *) (stage1_buffer + STAGE1_STAGE2_SEGMENT)) = installaddr >> 4; i = (int) stage2_first_buffer + SECTOR_SIZE - 4; while (*((unsigned long *) i)) { if (i < (int) stage2_first_buffer || (*((int *) (i - 4)) & 0x80000000) || *((unsigned short *) i) >= 0xA00 || *((short *) (i + 2)) == 0) { errnum = ERR_BAD_VERSION; goto fail; } *((int *) i) = 0; *((int *) (i - 4)) = 0; i -= 8; } installlist = (int) stage2_first_buffer + SECTOR_SIZE + 4; installaddr += SECTOR_SIZE; /* Read the whole of Stage2 except for the first sector. */ grub_seek (SECTOR_SIZE); disk_read_hook = disk_read_blocklist_func; if (! grub_read (dummy, -1)) goto fail; disk_read_hook = 0; /* Find a string for the configuration filename. */ config_file_location = stage2_second_buffer + STAGE2_VER_STR_OFFS; while (*(config_file_location++)) ; /* Set the "force LBA" flag for Stage2. */ *((unsigned char *) (stage2_second_buffer + STAGE2_FORCE_LBA)) = is_force_lba; if (*ptr == 'p') { *((long *) (stage2_second_buffer + STAGE2_INSTALLPART)) = src_partition; if (is_stage1_5) { /* Reset the device information in FILE if it is a Stage 1.5. */ unsigned long device = 0xFFFFFFFF; grub_memmove (config_file_location, (char *) &device, sizeof (device)); } ptr = skip_to (0, ptr); } if (*ptr) { grub_strcpy (config_filename, ptr); nul_terminate (config_filename); if (! is_stage1_5) /* If it is a Stage 2, just copy PTR to CONFIG_FILE_LOCATION. */ grub_strcpy (config_file_location, ptr); else { char *real_config; unsigned long device; /* Translate the external device syntax to the internal device syntax. */ if (! (real_config = set_device (ptr))) { /* The Stage 2 PTR does not contain the device name, so use the root device instead. */ errnum = ERR_NONE; current_drive = saved_drive; current_partition = saved_partition; real_config = ptr; } if (current_drive == src_drive) { /* If the drive where the Stage 2 resides is the same as the one where the Stage 1.5 resides, do not embed the drive number. */ current_drive = GRUB_INVALID_DRIVE; } device = (current_drive << 24) | current_partition; grub_memmove (config_file_location, (char *) &device, sizeof (device)); grub_strcpy (config_file_location + sizeof (device), real_config); } /* If a Stage 1.5 is used, then we need to modify the Stage2. */ if (is_stage1_5) { char *real_config_filename = skip_to (0, ptr); is_open = grub_open (config_filename); if (! is_open) goto fail; /* Skip the first sector. */ grub_seek (SECTOR_SIZE); disk_read_hook = disk_read_savesect_func; if (grub_read (stage2_buffer, SECTOR_SIZE) != SECTOR_SIZE) goto fail; disk_read_hook = 0; grub_close (); is_open = 0; /* Sanity check. */ if (*(stage2_buffer + STAGE2_STAGE2_ID) != STAGE2_ID_STAGE2) { errnum = ERR_BAD_VERSION; goto fail; } /* Set the "force LBA" flag for Stage2. */ *(stage2_buffer + STAGE2_FORCE_LBA) = is_force_lba; /* If REAL_CONFIG_FILENAME is specified, copy it to the Stage2. */ if (*real_config_filename) { /* Specified */ char *location; /* Find a string for the configuration filename. */ location = stage2_buffer + STAGE2_VER_STR_OFFS; while (*(location++)) ; /* Copy the name. */ grub_strcpy (location, real_config_filename); } /* Write it to the disk. */ buf_track = -1; #ifdef GRUB_UTIL /* In the grub shell, access the Stage 2 via the OS filesystem service, if possible. */ if (stage2_os_file) { FILE *fp; fp = fopen (stage2_os_file, "r+"); if (! fp) { errnum = ERR_FILE_NOT_FOUND; goto fail; } if (fseek (fp, SECTOR_SIZE, SEEK_SET) != 0) { fclose (fp); errnum = ERR_BAD_VERSION; goto fail; } if (fwrite (stage2_buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE) { fclose (fp); errnum = ERR_WRITE; goto fail; } fclose (fp); } else #endif /* GRUB_UTIL */ { if (! devwrite (saved_sector - part_start, 1, stage2_buffer)) goto fail; } } } /* Clear the cache. */ buf_track = -1; /* Write the modified sectors of Stage2 to the disk. */ #ifdef GRUB_UTIL if (! is_stage1_5 && stage2_os_file) { FILE *fp; fp = fopen (stage2_os_file, "r+"); if (! fp) { errnum = ERR_FILE_NOT_FOUND; goto fail; } if (fwrite (stage2_first_buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE) { fclose (fp); errnum = ERR_WRITE; goto fail; } if (fwrite (stage2_second_buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE) { fclose (fp); errnum = ERR_WRITE; goto fail; } fclose (fp); } else #endif /* GRUB_UTIL */ { /* The first. */ current_drive = src_drive; current_partition = src_partition; if (! open_partition ()) goto fail; if (! devwrite (stage2_first_sector - src_part_start, 1, stage2_first_buffer)) goto fail; if (! devwrite (stage2_second_sector - src_part_start, 1, stage2_second_buffer)) goto fail; } /* Write the modified sector of Stage 1 to the disk. */ current_drive = dest_drive; current_partition = dest_partition; if (! open_partition ()) goto fail; devwrite (0, 1, stage1_buffer); fail: if (is_open) grub_close (); disk_read_hook = 0; #ifndef NO_DECOMPRESSION no_decompression = 0; #endif return errnum; } static struct builtin builtin_install = { "install", install_func, BUILTIN_CMDLINE, "install [--stage2=STAGE2_FILE] [--force-lba] STAGE1 [d] DEVICE STAGE2 [ADDR] [p] [CONFIG_FILE] [REAL_CONFIG_FILE]", "Install STAGE1 on DEVICE, and install a blocklist for loading STAGE2" " as a Stage 2. If the option `d' is present, the Stage 1 will always" " look for the disk where STAGE2 was installed, rather than using" " the booting drive. The Stage 2 will be loaded at address ADDR, which" " will be determined automatically if you don't specify it. If" " the option `p' or CONFIG_FILE is present, then the first block" " of Stage 2 is patched with new values of the partition and name" " of the configuration file used by the true Stage 2 (for a Stage 1.5," " this is the name of the true Stage 2) at boot time. If STAGE2 is a Stage" " 1.5 and REAL_CONFIG_FILE is present, then the Stage 2 CONFIG_FILE is" " patched with the configuration filename REAL_CONFIG_FILE." " If the option `--force-lba' is specified, disable some sanity checks" " for LBA mode. If the option `--stage2' is specified, rewrite the Stage" " 2 via your OS's filesystem instead of the raw device." }; /* ioprobe */ static int ioprobe_func (char *arg, int flags) { #ifdef GRUB_UTIL errnum = ERR_UNRECOGNIZED; return 1; #else /* ! GRUB_UTIL */ unsigned short *port; /* Get the drive number. */ set_device (arg); if (errnum) return 1; /* Clean out IO_MAP. */ grub_memset ((char *) io_map, 0, IO_MAP_SIZE * sizeof (unsigned short)); /* Track the int13 handler. */ track_int13 (current_drive); /* Print out the result. */ for (port = io_map; *port != 0; port++) grub_printf (" 0x%x", (unsigned int) *port); return 0; #endif /* ! GRUB_UTIL */ } static struct builtin builtin_ioprobe = { "ioprobe", ioprobe_func, BUILTIN_CMDLINE, "ioprobe DRIVE", "Probe I/O ports used for the drive DRIVE." }; /* kernel */ static int kernel_func (char *arg, int flags) { int len; kernel_t suggested_type = KERNEL_TYPE_NONE; unsigned long load_flags = 0; #ifndef AUTO_LINUX_MEM_OPT load_flags |= KERNEL_LOAD_NO_MEM_OPTION; #endif /* Deal with GNU-style long options. */ while (1) { /* If the option `--type=TYPE' is specified, convert the string to a kernel type. */ if (grub_memcmp (arg, "--type=", 7) == 0) { arg += 7; if (grub_memcmp (arg, "netbsd", 6) == 0) suggested_type = KERNEL_TYPE_NETBSD; else if (grub_memcmp (arg, "freebsd", 7) == 0) suggested_type = KERNEL_TYPE_FREEBSD; else if (grub_memcmp (arg, "openbsd", 7) == 0) /* XXX: For now, OpenBSD is identical to NetBSD, from GRUB's point of view. */ suggested_type = KERNEL_TYPE_NETBSD; else if (grub_memcmp (arg, "linux", 5) == 0) suggested_type = KERNEL_TYPE_LINUX; else if (grub_memcmp (arg, "biglinux", 8) == 0) suggested_type = KERNEL_TYPE_BIG_LINUX; else if (grub_memcmp (arg, "multiboot", 9) == 0) suggested_type = KERNEL_TYPE_MULTIBOOT; else { errnum = ERR_BAD_ARGUMENT; return 1; } } /* If the `--no-mem-option' is specified, don't pass a Linux's mem option automatically. If the kernel is another type, this flag has no effect. */ else if (grub_memcmp (arg, "--no-mem-option", 15) == 0) load_flags |= KERNEL_LOAD_NO_MEM_OPTION; else break; /* Try the next. */ arg = skip_to (0, arg); } len = grub_strlen (arg); /* Reset MB_CMDLINE. */ mb_cmdline = (char *) MB_CMDLINE_BUF; if (len + 1 > MB_CMDLINE_BUFLEN) { errnum = ERR_WONT_FIT; return 1; } /* Copy the command-line to MB_CMDLINE. */ grub_memmove (mb_cmdline, arg, len + 1); kernel_type = load_image (arg, mb_cmdline, suggested_type, load_flags); if (kernel_type == KERNEL_TYPE_NONE) return 1; mb_cmdline += len + 1; return 0; } static struct builtin builtin_kernel = { "kernel", kernel_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "kernel [--no-mem-option] [--type=TYPE] FILE [ARG ...]", "Attempt to load the primary boot image from FILE. The rest of the" " line is passed verbatim as the \"kernel command line\". Any modules" " must be reloaded after using this command. The option --type is used" " to suggest what type of kernel to be loaded. TYPE must be either of" " \"netbsd\", \"freebsd\", \"openbsd\", \"linux\", \"biglinux\" and" " \"multiboot\". The option --no-mem-option tells GRUB not to pass a" " Linux's mem option automatically." }; /* lock */ static int lock_func (char *arg, int flags) { if (! auth && password) { errnum = ERR_PRIVILEGED; return 1; } return 0; } static struct builtin builtin_lock = { "lock", lock_func, BUILTIN_CMDLINE, "lock", "Break a command execution unless the user is authenticated." }; /* makeactive */ static int makeactive_func (char *arg, int flags) { if (! make_saved_active ()) return 1; return 0; } static struct builtin builtin_makeactive = { "makeactive", makeactive_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "makeactive", "Set the active partition on the root disk to GRUB's root device." " This command is limited to _primary_ PC partitions on a hard disk." }; /* map */ /* Map FROM_DRIVE to TO_DRIVE. */ static int map_func (char *arg, int flags) { char *to_drive; char *from_drive; unsigned long to, from; int i; to_drive = arg; from_drive = skip_to (0, arg); /* Get the drive number for TO_DRIVE. */ set_device (to_drive); if (errnum) return 1; to = current_drive; /* Get the drive number for FROM_DRIVE. */ set_device (from_drive); if (errnum) return 1; from = current_drive; /* Search for an empty slot in BIOS_DRIVE_MAP. */ for (i = 0; i < DRIVE_MAP_SIZE; i++) { /* Perhaps the user wants to override the map. */ if ((bios_drive_map[i] & 0xff) == from) break; if (! bios_drive_map[i]) break; } if (i == DRIVE_MAP_SIZE) { errnum = ERR_WONT_FIT; return 1; } if (to == from) /* If TO is equal to FROM, delete the entry. */ grub_memmove ((char *) &bios_drive_map[i], (char *) &bios_drive_map[i + 1], sizeof (unsigned short) * (DRIVE_MAP_SIZE - i)); else bios_drive_map[i] = from | (to << 8); return 0; } static struct builtin builtin_map = { "map", map_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "map TO_DRIVE FROM_DRIVE", "Map the drive FROM_DRIVE to the drive TO_DRIVE. This is necessary" " when you chain-load some operating systems, such as DOS, if such an" " OS resides at a non-first drive." }; #ifdef USE_MD5_PASSWORDS /* md5crypt */ static int md5crypt_func (char *arg, int flags) { char crypted[36]; char key[32]; unsigned int seed; int i; const char *const seedchars = "./0123456789ABCDEFGHIJKLMNOPQRST" "UVWXYZabcdefghijklmnopqrstuvwxyz"; /* First create a salt. */ /* The magical prefix. */ grub_memset (crypted, 0, sizeof (crypted)); grub_memmove (crypted, "$1$", 3); /* Create the length of a salt. */ seed = currticks (); /* Generate a salt. */ for (i = 0; i < 8 && seed; i++) { /* FIXME: This should be more random. */ crypted[3 + i] = seedchars[seed & 0x3f]; seed >>= 6; } /* A salt must be terminated with `$', if it is less than 8 chars. */ crypted[3 + i] = '$'; #ifdef DEBUG_MD5CRYPT grub_printf ("salt = %s\n", crypted); #endif /* Get a password. */ grub_memset (key, 0, sizeof (key)); get_cmdline ("Password: ", key, sizeof (key) - 1, '*', 0); /* Crypt the key. */ make_md5_password (key, crypted); grub_printf ("Encrypted: %s\n", crypted); return 0; } static struct builtin builtin_md5crypt = { "md5crypt", md5crypt_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "md5crypt", "Generate a password in MD5 format." }; #endif /* USE_MD5_PASSWORDS */ /* module */ static int module_func (char *arg, int flags) { int len = grub_strlen (arg); switch (kernel_type) { case KERNEL_TYPE_MULTIBOOT: if (mb_cmdline + len + 1 > (char *) MB_CMDLINE_BUF + MB_CMDLINE_BUFLEN) { errnum = ERR_WONT_FIT; return 1; } grub_memmove (mb_cmdline, arg, len + 1); if (! load_module (arg, mb_cmdline)) return 1; mb_cmdline += len + 1; break; case KERNEL_TYPE_LINUX: case KERNEL_TYPE_BIG_LINUX: if (! load_initrd (arg)) return 1; break; default: errnum = ERR_NEED_MB_KERNEL; return 1; } return 0; } static struct builtin builtin_module = { "module", module_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "module FILE [ARG ...]", "Load a boot module FILE for a Multiboot format boot image (no" " interpretation of the file contents is made, so users of this" " command must know what the kernel in question expects). The" " rest of the line is passed as the \"module command line\", like" " the `kernel' command." }; /* modulenounzip */ static int modulenounzip_func (char *arg, int flags) { int ret; #ifndef NO_DECOMPRESSION no_decompression = 1; #endif ret = module_func (arg, flags); #ifndef NO_DECOMPRESSION no_decompression = 0; #endif return ret; } static struct builtin builtin_modulenounzip = { "modulenounzip", modulenounzip_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "modulenounzip FILE [ARG ...]", "The same as `module', except that automatic decompression is" " disabled." }; /* pager [on|off] */ static int pager_func (char *arg, int flags) { /* If ARG is empty, toggle the flag. */ if (! *arg) use_pager = ! use_pager; else if (grub_memcmp (arg, "on", 2) == 0) use_pager = 1; else if (grub_memcmp (arg, "off", 3) == 0) use_pager = 0; else { errnum = ERR_BAD_ARGUMENT; return 1; } grub_printf (" Internal pager is now %s\n", use_pager ? "on" : "off"); return 0; } static struct builtin builtin_pager = { "pager", pager_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "pager [FLAG]", "Toggle pager mode with no argument. If FLAG is given and its value" " is `on', turn on the mode. If FLAG is `off', turn off the mode." }; /* partnewbeta PART TYPE START LEN */ static int partnewbeta_func (char *arg, int flags) { int new_type, new_start, new_len; int start_cl, start_ch, start_dh; int end_cl, end_ch, end_dh; int entry; char mbr[512]; /* Convert a LBA address to a CHS address in the INT 13 format. */ auto void lba_to_chs (int lba, int *cl, int *ch, int *dh); void lba_to_chs (int lba, int *cl, int *ch, int *dh) { /* beg old code int cylinder, head, sector; end old code */ /* begin first part of sburtchin partnew patch (replace lines) */ int cylinder, head, sector, bufgeomcylinders; /* end first part of sburtchin partnew patch */ /* begin second part of sburtchin partnew patch (add lines) */ if (lba <= 0) { *cl = 0; *ch = 0; *dh = 0; } else { /* end second part of sburtchin partnew patch */ sector = lba % buf_geom.sectors + 1; head = (lba / buf_geom.sectors) % buf_geom.heads; cylinder = lba / (buf_geom.sectors * buf_geom.heads); /* beg old code if (cylinder >= buf_geom.cylinders) cylinder = buf_geom.cylinders - 1; end old code */ /* begin third part of sburtchin partnew patch (replace lines) */ bufgeomcylinders = buf_geom.cylinders; bufgeomcylinders += 2; if (cylinder >= bufgeomcylinders) cylinder = bufgeomcylinders - 1; /* end third part of sburtchin partnew patch */ *cl = sector | ((cylinder & 0x300) >> 2); *ch = cylinder & 0xFF; *dh = head; /* begin fourth part of sburtchin partnew patch (add lines) */ } /* end fourth part of sburtchin partnew patch */ } /* Get the drive and the partition. */ if (! set_device (arg)) return 1; /* The drive must be a hard disk. */ if (! (current_drive & 0x80)) { errnum = ERR_BAD_ARGUMENT; return 1; } /* The partition must a primary partition. */ if ((current_partition >> 16) > 3 || (current_partition & 0xFFFF) != 0xFFFF) { errnum = ERR_BAD_ARGUMENT; return 1; } entry = current_partition >> 16; /* Get the new partition type. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &new_type)) return 1; /* The partition type is unsigned char. */ if (new_type > 0xFF) { errnum = ERR_BAD_ARGUMENT; return 1; } /* Get the new partition start. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &new_start)) return 1; /* Get the new partition length. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &new_len)) return 1; /* Read the MBR. */ if (! rawread (current_drive, 0, 0, SECTOR_SIZE, mbr)) return 1; /* Check if the new partition will fit in the disk. */ if (new_start + new_len > buf_geom.total_sectors) { errnum = ERR_GEOM; return 1; } /* Store the partition information in the MBR. */ lba_to_chs (new_start, &start_cl, &start_ch, &start_dh); lba_to_chs (new_start + new_len - 1, &end_cl, &end_ch, &end_dh); PC_SLICE_FLAG (mbr, entry) = 0; PC_SLICE_HEAD (mbr, entry) = start_dh; PC_SLICE_SEC (mbr, entry) = start_cl; PC_SLICE_CYL (mbr, entry) = start_ch; PC_SLICE_TYPE (mbr, entry) = new_type; PC_SLICE_EHEAD (mbr, entry) = end_dh; PC_SLICE_ESEC (mbr, entry) = end_cl; PC_SLICE_ECYL (mbr, entry) = end_ch; PC_SLICE_START (mbr, entry) = new_start; PC_SLICE_LENGTH (mbr, entry) = new_len; /* Make sure that the MBR has a valid signature. */ PC_MBR_SIG (mbr) = PC_MBR_SIGNATURE; /* Write back the MBR to the disk. */ buf_track = -1; if (! rawwrite (current_drive, 0, mbr)) return 1; return 0; } static struct builtin builtin_partnewbeta = { "partnewbeta", partnewbeta_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "partnewbeta PART TYPE START LEN", "Create a primary partition at the starting address START with the" " length LEN, with the type TYPE. START and LEN are in sector units." }; /* parttype PART TYPE */ static int parttype_func (char *arg, int flags) { int new_type; unsigned long part = 0xFFFFFF; unsigned long start, len, offset, ext_offset; int entry, type; char mbr[512]; /* Get the drive and the partition. */ if (! set_device (arg)) return 1; /* The drive must be a hard disk. */ if (! (current_drive & 0x80)) { errnum = ERR_BAD_ARGUMENT; return 1; } /* The partition must be a PC slice. */ if ((current_partition >> 16) == 0xFF || (current_partition & 0xFFFF) != 0xFFFF) { errnum = ERR_BAD_ARGUMENT; return 1; } /* Get the new partition type. */ arg = skip_to (0, arg); if (! safe_parse_maxint (&arg, &new_type)) return 1; /* The partition type is unsigned char. */ if (new_type > 0xFF) { errnum = ERR_BAD_ARGUMENT; return 1; } /* Look for the partition. */ while (next_partition (current_drive, 0xFFFFFF, &part, &type, &start, &len, &offset, &entry, &ext_offset, mbr)) { if (part == current_partition) { /* Found. */ /* Set the type to NEW_TYPE. */ PC_SLICE_TYPE (mbr, entry) = new_type; /* Write back the MBR to the disk. */ buf_track = -1; if (! rawwrite (current_drive, offset, mbr)) return 1; /* Succeed. */ return 0; } } /* The partition was not found. ERRNUM was set by next_partition. */ return 1; } static struct builtin builtin_parttype = { "parttype", parttype_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "parttype PART TYPE", "Change the type of the partition PART to TYPE." }; /* password */ static int password_func (char *arg, int flags) { int len; password_t type = PASSWORD_PLAIN; #ifdef USE_MD5_PASSWORDS if (grub_memcmp (arg, "--md5", 5) == 0) { type = PASSWORD_MD5; arg = skip_to (0, arg); } #endif if (grub_memcmp (arg, "--", 2) == 0) { type = PASSWORD_UNSUPPORTED; arg = skip_to (0, arg); } if ((flags & (BUILTIN_CMDLINE | BUILTIN_SCRIPT)) != 0) { /* Do password check! */ char entered[32]; /* Wipe out any previously entered password */ entered[0] = 0; get_cmdline ("Password: ", entered, 31, '*', 0); nul_terminate (arg); if (check_password (entered, arg, type) != 0) { errnum = ERR_PRIVILEGED; return 1; } } else { len = grub_strlen (arg); /* PASSWORD NUL NUL ... */ if (len + 2 > PASSWORD_BUFLEN) { errnum = ERR_WONT_FIT; return 1; } /* Copy the password and clear the rest of the buffer. */ password = (char *) PASSWORD_BUF; grub_memmove (password, arg, len); grub_memset (password + len, 0, PASSWORD_BUFLEN - len); password_type = type; } return 0; } static struct builtin builtin_password = { "password", password_func, BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_NO_ECHO, "password [--md5] PASSWD [FILE]", "If used in the first section of a menu file, disable all" " interactive editing control (menu entry editor and" " command line). If the password PASSWD is entered, it loads the" " FILE as a new config file and restarts the GRUB Stage 2. If you" " omit the argument FILE, then GRUB just unlocks privileged" " instructions. You can also use it in the script section, in" " which case it will ask for the password, before continueing." " The option --md5 tells GRUB that PASSWD is encrypted with" " md5crypt." }; /* pause */ static int pause_func (char *arg, int flags) { printf("%s\n", arg); /* If ESC is returned, then abort this entry. */ if (ASCII_CHAR (getkey ()) == 27) return 1; return 0; } static struct builtin builtin_pause = { "pause", pause_func, BUILTIN_CMDLINE | BUILTIN_NO_ECHO, "pause [MESSAGE ...]", "Print MESSAGE, then wait until a key is pressed." }; #ifdef GRUB_UTIL /* quit */ static int quit_func (char *arg, int flags) { stop (); /* Never reach here. */ return 0; } static struct builtin builtin_quit = { "quit", quit_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "quit", "Exit from the GRUB shell." }; #endif /* GRUB_UTIL */ #ifdef SUPPORT_NETBOOT /* rarp */ static int rarp_func (char *arg, int flags) { if (! rarp ()) { if (errnum == ERR_NONE) errnum = ERR_DEV_VALUES; return 1; } /* Notify the configuration. */ print_network_configuration (); return 0; } static struct builtin builtin_rarp = { "rarp", rarp_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "rarp", "Initialize a network device via RARP." }; #endif /* SUPPORT_NETBOOT */ static int read_func (char *arg, int flags) { int addr; if (! safe_parse_maxint (&arg, &addr)) return 1; grub_printf ("Address 0x%x: Value 0x%x\n", addr, *((unsigned *) RAW_ADDR (addr))); return 0; } static struct builtin builtin_read = { "read", read_func, BUILTIN_CMDLINE, "read ADDR", "Read a 32-bit value from memory at address ADDR and" " display it in hex format." }; /* reboot */ static int reboot_func (char *arg, int flags) { grub_reboot (); /* Never reach here. */ return 1; } static struct builtin builtin_reboot = { "reboot", reboot_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "reboot", "Reboot your system." }; /* Print the root device information. */ static void print_root_device (void) { if (saved_drive == NETWORK_DRIVE) { /* Network drive. */ grub_printf (" (nd):"); } else if (saved_drive & 0x80) { /* Hard disk drive. */ grub_printf (" (hd%d", saved_drive - 0x80); if ((saved_partition & 0xFF0000) != 0xFF0000) grub_printf (",%d", saved_partition >> 16); if ((saved_partition & 0x00FF00) != 0x00FF00) grub_printf (",%c", ((saved_partition >> 8) & 0xFF) + 'a'); grub_printf ("):"); } else { /* Floppy disk drive. */ grub_printf (" (fd%d):", saved_drive); } /* Print the filesystem information. */ current_partition = saved_partition; current_drive = saved_drive; print_fsys_type (); } static int real_root_func (char *arg, int attempt_mount) { int hdbias = 0; char *biasptr; char *next; /* If ARG is empty, just print the current root device. */ if (! *arg) { print_root_device (); return 0; } /* Call set_device to get the drive and the partition in ARG. */ next = set_device (arg); if (! next) return 1; /* Ignore ERR_FSYS_MOUNT. */ if (attempt_mount) { if (! open_device () && errnum != ERR_FSYS_MOUNT) return 1; } else { /* This is necessary, because the location of a partition table must be set appropriately. */ if (open_partition ()) { set_bootdev (0); if (errnum) return 1; } } /* Clear ERRNUM. */ errnum = 0; saved_partition = current_partition; saved_drive = current_drive; if (attempt_mount) { /* BSD and chainloading evil hacks !! */ biasptr = skip_to (0, next); safe_parse_maxint (&biasptr, &hdbias); errnum = 0; bootdev = set_bootdev (hdbias); if (errnum) return 1; /* Print the type of the filesystem. */ print_fsys_type (); } return 0; } static int root_func (char *arg, int flags) { return real_root_func (arg, 1); } static struct builtin builtin_root = { "root", root_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "root [DEVICE [HDBIAS]]", "Set the current \"root device\" to the device DEVICE, then" " attempt to mount it to get the partition size (for passing the" " partition descriptor in `ES:ESI', used by some chain-loaded" " bootloaders), the BSD drive-type (for booting BSD kernels using" " their native boot format), and correctly determine " " the PC partition where a BSD sub-partition is located. The" " optional HDBIAS parameter is a number to tell a BSD kernel" " how many BIOS drive numbers are on controllers before the current" " one. For example, if there is an IDE disk and a SCSI disk, and your" " FreeBSD root partition is on the SCSI disk, then use a `1' for HDBIAS." }; /* rootnoverify */ static int rootnoverify_func (char *arg, int flags) { return real_root_func (arg, 0); } static struct builtin builtin_rootnoverify = { "rootnoverify", rootnoverify_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "rootnoverify [DEVICE [HDBIAS]]", "Similar to `root', but don't attempt to mount the partition. This" " is useful for when an OS is outside of the area of the disk that" " GRUB can read, but setting the correct root device is still" " desired. Note that the items mentioned in `root' which" " derived from attempting the mount will NOT work correctly." }; /* savedefault */ static int savedefault_func (char *arg, int flags) { #if !defined(SUPPORT_DISKLESS) && !defined(GRUB_UTIL) unsigned long tmp_drive = saved_drive; unsigned long tmp_partition = saved_partition; char *default_file = (char *) DEFAULT_FILE_BUF; char buf[10]; char sect[SECTOR_SIZE]; int entryno; int sector_count = 0; int saved_sectors[2]; int saved_offsets[2]; int saved_lengths[2]; /* Save sector information about at most two sectors. */ auto void disk_read_savesect_func (int sector, int offset, int length); void disk_read_savesect_func (int sector, int offset, int length) { if (sector_count < 2) { saved_sectors[sector_count] = sector; saved_offsets[sector_count] = offset; saved_lengths[sector_count] = length; } sector_count++; } /* This command is only useful when you boot an entry from the menu interface. */ if (! (flags & BUILTIN_SCRIPT)) { errnum = ERR_UNRECOGNIZED; return 1; } /* Determine a saved entry number. */ if (*arg) { if (grub_memcmp (arg, "fallback", sizeof ("fallback") - 1) == 0) { int i; int index = 0; for (i = 0; i < MAX_FALLBACK_ENTRIES; i++) { if (fallback_entries[i] < 0) break; if (fallback_entries[i] == current_entryno) { index = i + 1; break; } } if (index >= MAX_FALLBACK_ENTRIES || fallback_entries[index] < 0) { /* This is the last. */ errnum = ERR_BAD_ARGUMENT; return 1; } entryno = fallback_entries[index]; } else if (! safe_parse_maxint (&arg, &entryno)) return 1; } else entryno = current_entryno; /* Open the default file. */ saved_drive = boot_drive; saved_partition = install_partition; if (grub_open (default_file)) { int len; disk_read_hook = disk_read_savesect_func; len = grub_read (buf, sizeof (buf)); disk_read_hook = 0; grub_close (); if (len != sizeof (buf)) { /* This is too small. Do not modify the file manually, please! */ errnum = ERR_READ; goto fail; } if (sector_count > 2) { /* Is this possible?! Too fragmented! */ errnum = ERR_FSYS_CORRUPT; goto fail; } /* Set up a string to be written. */ grub_memset (buf, '\n', sizeof (buf)); grub_sprintf (buf, "%d", entryno); if (saved_lengths[0] < sizeof (buf)) { /* The file is anchored to another file and the first few bytes are spanned in two sectors. Uggh... */ if (! rawread (current_drive, saved_sectors[0], 0, SECTOR_SIZE, sect)) goto fail; grub_memmove (sect + saved_offsets[0], buf, saved_lengths[0]); if (! rawwrite (current_drive, saved_sectors[0], sect)) goto fail; if (! rawread (current_drive, saved_sectors[1], 0, SECTOR_SIZE, sect)) goto fail; grub_memmove (sect + saved_offsets[1], buf + saved_lengths[0], sizeof (buf) - saved_lengths[0]); if (! rawwrite (current_drive, saved_sectors[1], sect)) goto fail; } else { /* This is a simple case. It fits into a single sector. */ if (! rawread (current_drive, saved_sectors[0], 0, SECTOR_SIZE, sect)) goto fail; grub_memmove (sect + saved_offsets[0], buf, sizeof (buf)); if (! rawwrite (current_drive, saved_sectors[0], sect)) goto fail; } /* Clear the cache. */ buf_track = -1; } fail: saved_drive = tmp_drive; saved_partition = tmp_partition; return errnum; #else /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */ errnum = ERR_UNRECOGNIZED; return 1; #endif /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */ } static struct builtin builtin_savedefault = { "savedefault", savedefault_func, BUILTIN_CMDLINE, "savedefault [NUM | `fallback']", "Save the current entry as the default boot entry if no argument is" " specified. If a number is specified, this number is saved. If" " `fallback' is used, next fallback entry is saved." }; #ifdef SUPPORT_SERIAL /* serial */ static int serial_func (char *arg, int flags) { unsigned short port = serial_hw_get_port (0); unsigned int speed = 9600; int word_len = UART_8BITS_WORD; int parity = UART_NO_PARITY; int stop_bit_len = UART_1_STOP_BIT; /* Process GNU-style long options. FIXME: We should implement a getopt-like function, to avoid duplications. */ while (1) { if (grub_memcmp (arg, "--unit=", sizeof ("--unit=") - 1) == 0) { char *p = arg + sizeof ("--unit=") - 1; int unit; if (! safe_parse_maxint (&p, &unit)) return 1; if (unit < 0 || unit > 3) { errnum = ERR_DEV_VALUES; return 1; } port = serial_hw_get_port (unit); } else if (grub_memcmp (arg, "--speed=", sizeof ("--speed=") - 1) == 0) { char *p = arg + sizeof ("--speed=") - 1; int num; if (! safe_parse_maxint (&p, &num)) return 1; speed = (unsigned int) num; } else if (grub_memcmp (arg, "--port=", sizeof ("--port=") - 1) == 0) { char *p = arg + sizeof ("--port=") - 1; int num; if (! safe_parse_maxint (&p, &num)) return 1; port = (unsigned short) num; } else if (grub_memcmp (arg, "--word=", sizeof ("--word=") - 1) == 0) { char *p = arg + sizeof ("--word=") - 1; int len; if (! safe_parse_maxint (&p, &len)) return 1; switch (len) { case 5: word_len = UART_5BITS_WORD; break; case 6: word_len = UART_6BITS_WORD; break; case 7: word_len = UART_7BITS_WORD; break; case 8: word_len = UART_8BITS_WORD; break; default: errnum = ERR_BAD_ARGUMENT; return 1; } } else if (grub_memcmp (arg, "--stop=", sizeof ("--stop=") - 1) == 0) { char *p = arg + sizeof ("--stop=") - 1; int len; if (! safe_parse_maxint (&p, &len)) return 1; switch (len) { case 1: stop_bit_len = UART_1_STOP_BIT; break; case 2: stop_bit_len = UART_2_STOP_BITS; break; default: errnum = ERR_BAD_ARGUMENT; return 1; } } else if (grub_memcmp (arg, "--parity=", sizeof ("--parity=") - 1) == 0) { char *p = arg + sizeof ("--parity=") - 1; if (grub_memcmp (p, "no", sizeof ("no") - 1) == 0) parity = UART_NO_PARITY; else if (grub_memcmp (p, "odd", sizeof ("odd") - 1) == 0) parity = UART_ODD_PARITY; else if (grub_memcmp (p, "even", sizeof ("even") - 1) == 0) parity = UART_EVEN_PARITY; else { errnum = ERR_BAD_ARGUMENT; return 1; } } # ifdef GRUB_UTIL /* In the grub shell, don't use any port number but open a tty device instead. */ else if (grub_memcmp (arg, "--device=", sizeof ("--device=") - 1) == 0) { char *p = arg + sizeof ("--device=") - 1; char dev[256]; /* XXX */ char *q = dev; while (*p && ! grub_isspace (*p)) *q++ = *p++; *q = 0; serial_set_device (dev); } # endif /* GRUB_UTIL */ else break; arg = skip_to (0, arg); } /* Initialize the serial unit. */ if (! serial_hw_init (port, speed, word_len, parity, stop_bit_len)) { errnum = ERR_BAD_ARGUMENT; return 1; } return 0; } static struct builtin builtin_serial = { "serial", serial_func, BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "serial [--unit=UNIT] [--port=PORT] [--speed=SPEED] [--word=WORD] [--parity=PARITY] [--stop=STOP] [--device=DEV]", "Initialize a serial device. UNIT is a digit that specifies which serial" " device is used (e.g. 0 == COM1). If you need to specify the port number," " set it by --port. SPEED is the DTE-DTE speed. WORD is the word length," " PARITY is the type of parity, which is one of `no', `odd' and `even'." " STOP is the length of stop bit(s). The option --device can be used only" " in the grub shell, which specifies the file name of a tty device. The" " default values are COM1, 9600, 8N1." }; #endif /* SUPPORT_SERIAL */ /* setkey */ struct keysym { char *unshifted_name; /* the name in unshifted state */ char *shifted_name; /* the name in shifted state */ unsigned char unshifted_ascii; /* the ascii code in unshifted state */ unsigned char shifted_ascii; /* the ascii code in shifted state */ unsigned char keycode; /* keyboard scancode */ }; /* The table for key symbols. If the "shifted" member of an entry is NULL, the entry does not have shifted state. */ static struct keysym keysym_table[] = { {"escape", 0, 0x1b, 0, 0x01}, {"1", "exclam", '1', '!', 0x02}, {"2", "at", '2', '@', 0x03}, {"3", "numbersign", '3', '#', 0x04}, {"4", "dollar", '4', '$', 0x05}, {"5", "percent", '5', '%', 0x06}, {"6", "caret", '6', '^', 0x07}, {"7", "ampersand", '7', '&', 0x08}, {"8", "asterisk", '8', '*', 0x09}, {"9", "parenleft", '9', '(', 0x0a}, {"0", "parenright", '0', ')', 0x0b}, {"minus", "underscore", '-', '_', 0x0c}, {"equal", "plus", '=', '+', 0x0d}, {"backspace", 0, '\b', 0, 0x0e}, {"tab", 0, '\t', 0, 0x0f}, {"q", "Q", 'q', 'Q', 0x10}, {"w", "W", 'w', 'W', 0x11}, {"e", "E", 'e', 'E', 0x12}, {"r", "R", 'r', 'R', 0x13}, {"t", "T", 't', 'T', 0x14}, {"y", "Y", 'y', 'Y', 0x15}, {"u", "U", 'u', 'U', 0x16}, {"i", "I", 'i', 'I', 0x17}, {"o", "O", 'o', 'O', 0x18}, {"p", "P", 'p', 'P', 0x19}, {"bracketleft", "braceleft", '[', '{', 0x1a}, {"bracketright", "braceright", ']', '}', 0x1b}, {"enter", 0, '\n', 0, 0x1c}, {"control", 0, 0, 0, 0x1d}, {"a", "A", 'a', 'A', 0x1e}, {"s", "S", 's', 'S', 0x1f}, {"d", "D", 'd', 'D', 0x20}, {"f", "F", 'f', 'F', 0x21}, {"g", "G", 'g', 'G', 0x22}, {"h", "H", 'h', 'H', 0x23}, {"j", "J", 'j', 'J', 0x24}, {"k", "K", 'k', 'K', 0x25}, {"l", "L", 'l', 'L', 0x26}, {"semicolon", "colon", ';', ':', 0x27}, {"quote", "doublequote", '\'', '"', 0x28}, {"backquote", "tilde", '`', '~', 0x29}, {"shift", 0, 0, 0, 0x2a}, {"backslash", "bar", '\\', '|', 0x2b}, {"z", "Z", 'z', 'Z', 0x2c}, {"x", "X", 'x', 'X', 0x2d}, {"c", "C", 'c', 'C', 0x2e}, {"v", "V", 'v', 'V', 0x2f}, {"b", "B", 'b', 'B', 0x30}, {"n", "N", 'n', 'N', 0x31}, {"m", "M", 'm', 'M', 0x32}, {"comma", "less", ',', '<', 0x33}, {"period", "greater", '.', '>', 0x34}, {"slash", "question", '/', '?', 0x35}, {"alt", 0, 0, 0, 0x38}, {"space", 0, ' ', 0, 0x39}, {"capslock", 0, 0, 0, 0x3a}, {"F1", 0, 0, 0, 0x3b}, {"F2", 0, 0, 0, 0x3c}, {"F3", 0, 0, 0, 0x3d}, {"F4", 0, 0, 0, 0x3e}, {"F5", 0, 0, 0, 0x3f}, {"F6", 0, 0, 0, 0x40}, {"F7", 0, 0, 0, 0x41}, {"F8", 0, 0, 0, 0x42}, {"F9", 0, 0, 0, 0x43}, {"F10", 0, 0, 0, 0x44}, /* Caution: do not add NumLock here! we cannot deal with it properly. */ {"delete", 0, 0x7f, 0, 0x53} }; static int setkey_func (char *arg, int flags) { char *to_key, *from_key; int to_code, from_code; int map_in_interrupt = 0; auto int find_key_code (char *key); auto int find_ascii_code (char *key); auto int find_key_code (char *key) { int i; for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++) { if (keysym_table[i].unshifted_name && grub_strcmp (key, keysym_table[i].unshifted_name) == 0) return keysym_table[i].keycode; else if (keysym_table[i].shifted_name && grub_strcmp (key, keysym_table[i].shifted_name) == 0) return keysym_table[i].keycode; } return 0; } auto int find_ascii_code (char *key) { int i; for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++) { if (keysym_table[i].unshifted_name && grub_strcmp (key, keysym_table[i].unshifted_name) == 0) return keysym_table[i].unshifted_ascii; else if (keysym_table[i].shifted_name && grub_strcmp (key, keysym_table[i].shifted_name) == 0) return keysym_table[i].shifted_ascii; } return 0; } to_key = arg; from_key = skip_to (0, to_key); if (! *to_key) { /* If the user specifies no argument, reset the key mappings. */ grub_memset (bios_key_map, 0, KEY_MAP_SIZE * sizeof (unsigned short)); grub_memset (ascii_key_map, 0, KEY_MAP_SIZE * sizeof (unsigned short)); return 0; } else if (! *from_key) { /* The user must specify two arguments or zero argument. */ errnum = ERR_BAD_ARGUMENT; return 1; } nul_terminate (to_key); nul_terminate (from_key); to_code = find_ascii_code (to_key); from_code = find_ascii_code (from_key); if (! to_code || ! from_code) { map_in_interrupt = 1; to_code = find_key_code (to_key); from_code = find_key_code (from_key); if (! to_code || ! from_code) { errnum = ERR_BAD_ARGUMENT; return 1; } } if (map_in_interrupt) { int i; /* Find an empty slot. */ for (i = 0; i < KEY_MAP_SIZE; i++) { if ((bios_key_map[i] & 0xff) == from_code) /* Perhaps the user wants to overwrite the map. */ break; if (! bios_key_map[i]) break; } if (i == KEY_MAP_SIZE) { errnum = ERR_WONT_FIT; return 1; } if (to_code == from_code) /* If TO is equal to FROM, delete the entry. */ grub_memmove ((char *) &bios_key_map[i], (char *) &bios_key_map[i + 1], sizeof (unsigned short) * (KEY_MAP_SIZE - i)); else bios_key_map[i] = (to_code << 8) | from_code; /* Ugly but should work. */ unset_int15_handler (); set_int15_handler (); } else { int i; /* Find an empty slot. */ for (i = 0; i < KEY_MAP_SIZE; i++) { if ((ascii_key_map[i] & 0xff) == from_code) /* Perhaps the user wants to overwrite the map. */ break; if (! ascii_key_map[i]) break; } if (i == KEY_MAP_SIZE) { errnum = ERR_WONT_FIT; return 1; } if (to_code == from_code) /* If TO is equal to FROM, delete the entry. */ grub_memmove ((char *) &ascii_key_map[i], (char *) &ascii_key_map[i + 1], sizeof (unsigned short) * (KEY_MAP_SIZE - i)); else ascii_key_map[i] = (to_code << 8) | from_code; } return 0; } static struct builtin builtin_setkey = { "setkey", setkey_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "setkey [TO_KEY FROM_KEY]", "Change the keyboard map. The key FROM_KEY is mapped to the key TO_KEY." " A key must be an alphabet, a digit, or one of these: escape, exclam," " at, numbersign, dollar, percent, caret, ampersand, asterisk, parenleft," " parenright, minus, underscore, equal, plus, backspace, tab, bracketleft," " braceleft, bracketright, braceright, enter, control, semicolon, colon," " quote, doublequote, backquote, tilde, shift, backslash, bar, comma," " less, period, greater, slash, question, alt, space, capslock, FX (X" " is a digit), and delete. If no argument is specified, reset key" " mappings." }; /* setup */ static int setup_func (char *arg, int flags) { /* Point to the string of the installed drive/partition. */ char *install_ptr; /* Point to the string of the drive/parition where the GRUB images reside. */ char *image_ptr; unsigned long installed_drive, installed_partition; unsigned long image_drive, image_partition; unsigned long tmp_drive, tmp_partition; char stage1[64]; char stage2[64]; char config_filename[64]; char real_config_filename[64]; char cmd_arg[256]; char device[16]; char *buffer = (char *) RAW_ADDR (0x100000); int is_force_lba = 0; char *stage2_arg = 0; char *prefix = 0; auto int check_file (char *file); auto void sprint_device (int drive, int partition); auto int embed_stage1_5 (char * stage1_5, int drive, int partition); /* Check if the file FILE exists like Autoconf. */ int check_file (char *file) { int ret; grub_printf (" Checking if \"%s\" exists... ", file); ret = grub_open (file); if (ret) { grub_close (); grub_printf ("yes\n"); } else grub_printf ("no\n"); return ret; } /* Construct a device name in DEVICE. */ void sprint_device (int drive, int partition) { grub_sprintf (device, "(%cd%d", (drive & 0x80) ? 'h' : 'f', drive & ~0x80); if ((partition & 0xFF0000) != 0xFF0000) { char tmp[16]; grub_sprintf (tmp, ",%d", (partition >> 16) & 0xFF); grub_strncat (device, tmp, 256); } if ((partition & 0x00FF00) != 0x00FF00) { char tmp[16]; grub_sprintf (tmp, ",%c", 'a' + ((partition >> 8) & 0xFF)); grub_strncat (device, tmp, 256); } grub_strncat (device, ")", 256); } int embed_stage1_5 (char *stage1_5, int drive, int partition) { /* We install GRUB into the MBR, so try to embed the Stage 1.5 in the sectors right after the MBR. */ sprint_device (drive, partition); grub_sprintf (cmd_arg, "%s %s", stage1_5, device); /* Notify what will be run. */ grub_printf (" Running \"embed %s\"... ", cmd_arg); embed_func (cmd_arg, flags); if (! errnum) { /* Construct the blocklist representation. */ grub_sprintf (buffer, "%s%s", device, embed_info); grub_printf ("succeeded\n"); return 1; } else { grub_printf ("failed (this is not fatal)\n"); return 0; } } struct stage1_5_map { char *fsys; char *name; }; struct stage1_5_map stage1_5_map[] = { {"ext2fs", "/e2fs_stage1_5"}, {"fat", "/fat_stage1_5"}, {"ufs2", "/ufs2_stage1_5"}, {"ffs", "/ffs_stage1_5"}, {"iso9660", "/iso9660_stage1_5"}, {"jfs", "/jfs_stage1_5"}, {"minix", "/minix_stage1_5"}, {"reiserfs", "/reiserfs_stage1_5"}, {"vstafs", "/vstafs_stage1_5"}, {"xfs", "/xfs_stage1_5"} }; tmp_drive = saved_drive; tmp_partition = saved_partition; /* Check if the user specifies --force-lba. */ while (1) { if (grub_memcmp ("--force-lba", arg, sizeof ("--force-lba") - 1) == 0) { is_force_lba = 1; arg = skip_to (0, arg); } else if (grub_memcmp ("--prefix=", arg, sizeof ("--prefix=") - 1) == 0) { prefix = arg + sizeof ("--prefix=") - 1; arg = skip_to (0, arg); nul_terminate (prefix); } #ifdef GRUB_UTIL else if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0) { stage2_arg = arg; arg = skip_to (0, arg); nul_terminate (stage2_arg); } #endif /* GRUB_UTIL */ else break; } install_ptr = arg; image_ptr = skip_to (0, install_ptr); /* Make sure that INSTALL_PTR is valid. */ set_device (install_ptr); if (errnum) return 1; installed_drive = current_drive; installed_partition = current_partition; /* Mount the drive pointed by IMAGE_PTR. */ if (*image_ptr) { /* If the drive/partition where the images reside is specified, get the drive and the partition. */ set_device (image_ptr); if (errnum) return 1; } else { /* If omitted, use SAVED_PARTITION and SAVED_DRIVE. */ current_drive = saved_drive; current_partition = saved_partition; } image_drive = saved_drive = current_drive; image_partition = saved_partition = current_partition; /* Open it. */ if (! open_device ()) goto fail; /* Check if stage1 exists. If the user doesn't specify the option `--prefix', attempt /boot/grub and /grub. */ /* NOTE: It is dangerous to run this command without `--prefix' in the grub shell, since that affects `--stage2'. */ if (! prefix) { prefix = "/boot/grub"; grub_sprintf (stage1, "%s%s", prefix, "/stage1"); if (! check_file (stage1)) { errnum = ERR_NONE; prefix = "/grub"; grub_sprintf (stage1, "%s%s", prefix, "/stage1"); if (! check_file (stage1)) goto fail; } } else { grub_sprintf (stage1, "%s%s", prefix, "/stage1"); if (! check_file (stage1)) goto fail; } /* The prefix was determined. */ grub_sprintf (stage2, "%s%s", prefix, "/stage2"); grub_sprintf (config_filename, "%s%s", prefix, "/menu.lst"); *real_config_filename = 0; /* Check if stage2 exists. */ if (! check_file (stage2)) goto fail; { char *fsys = fsys_table[fsys_type].name; int i; int size = sizeof (stage1_5_map) / sizeof (stage1_5_map[0]); /* Iterate finding the same filesystem name as FSYS. */ for (i = 0; i < size; i++) if (grub_strcmp (fsys, stage1_5_map[i].fsys) == 0) { /* OK, check if the Stage 1.5 exists. */ char stage1_5[64]; grub_sprintf (stage1_5, "%s%s", prefix, stage1_5_map[i].name); if (check_file (stage1_5)) { if (embed_stage1_5 (stage1_5, installed_drive, installed_partition) || embed_stage1_5 (stage1_5, image_drive, image_partition)) { grub_strcpy (real_config_filename, config_filename); sprint_device (image_drive, image_partition); grub_sprintf (config_filename, "%s%s", device, stage2); grub_strcpy (stage2, buffer); } } errnum = 0; break; } } /* Construct a string that is used by the command "install" as its arguments. */ sprint_device (installed_drive, installed_partition); #if 1 /* Don't embed a drive number unnecessarily. */ grub_sprintf (cmd_arg, "%s%s%s%s %s%s %s p %s %s", is_force_lba? "--force-lba " : "", stage2_arg? stage2_arg : "", stage2_arg? " " : "", stage1, (installed_drive != image_drive) ? "d " : "", device, stage2, config_filename, real_config_filename); #else /* NOT USED */ /* This code was used, because we belived some BIOSes had a problem that they didn't pass a booting drive correctly. It turned out, however, stage1 could trash a booting drive when checking LBA support, because some BIOSes modified the register %dx in INT 13H, AH=48H. So it becamed unclear whether GRUB should use a pre-defined booting drive or not. If the problem still exists, it would be necessary to switch back to this code. */ grub_sprintf (cmd_arg, "%s%s%s%s d %s %s p %s %s", is_force_lba? "--force-lba " : "", stage2_arg? stage2_arg : "", stage2_arg? " " : "", stage1, device, stage2, config_filename, real_config_filename); #endif /* NOT USED */ /* Notify what will be run. */ grub_printf (" Running \"install %s\"... ", cmd_arg); /* Make sure that SAVED_DRIVE and SAVED_PARTITION are identical with IMAGE_DRIVE and IMAGE_PARTITION, respectively. */ saved_drive = image_drive; saved_partition = image_partition; /* Run the command. */ if (! install_func (cmd_arg, flags)) grub_printf ("succeeded\nDone.\n"); else grub_printf ("failed\n"); fail: saved_drive = tmp_drive; saved_partition = tmp_partition; return errnum; } static struct builtin builtin_setup = { "setup", setup_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "setup [--prefix=DIR] [--stage2=STAGE2_FILE] [--force-lba] INSTALL_DEVICE [IMAGE_DEVICE]", "Set up the installation of GRUB automatically. This command uses" " the more flexible command \"install\" in the backend and installs" " GRUB into the device INSTALL_DEVICE. If IMAGE_DEVICE is specified," " then find the GRUB images in the device IMAGE_DEVICE, otherwise" " use the current \"root device\", which can be set by the command" " \"root\". If you know that your BIOS should support LBA but GRUB" " doesn't work in LBA mode, specify the option `--force-lba'." " If you install GRUB under the grub shell and you cannot unmount the" " partition where GRUB images reside, specify the option `--stage2'" " to tell GRUB the file name under your OS." }; #if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) /* terminal */ static int terminal_func (char *arg, int flags) { /* The index of the default terminal in TERM_TABLE. */ int default_term = -1; struct term_entry *prev_term = current_term; int to = -1; int lines = 0; int no_message = 0; unsigned long term_flags = 0; /* XXX: Assume less than 32 terminals. */ unsigned long term_bitmap = 0; /* Get GNU-style long options. */ while (1) { if (grub_memcmp (arg, "--dumb", sizeof ("--dumb") - 1) == 0) term_flags |= TERM_DUMB; else if (grub_memcmp (arg, "--no-echo", sizeof ("--no-echo") - 1) == 0) /* ``--no-echo'' implies ``--no-edit''. */ term_flags |= (TERM_NO_ECHO | TERM_NO_EDIT); else if (grub_memcmp (arg, "--no-edit", sizeof ("--no-edit") - 1) == 0) term_flags |= TERM_NO_EDIT; else if (grub_memcmp (arg, "--timeout=", sizeof ("--timeout=") - 1) == 0) { char *val = arg + sizeof ("--timeout=") - 1; if (! safe_parse_maxint (&val, &to)) return 1; } else if (grub_memcmp (arg, "--lines=", sizeof ("--lines=") - 1) == 0) { char *val = arg + sizeof ("--lines=") - 1; if (! safe_parse_maxint (&val, &lines)) return 1; /* Probably less than four is meaningless.... */ if (lines < 4) { errnum = ERR_BAD_ARGUMENT; return 1; } } else if (grub_memcmp (arg, "--silent", sizeof ("--silent") - 1) == 0) no_message = 1; else break; arg = skip_to (0, arg); } /* If no argument is specified, show current setting. */ if (! *arg) { grub_printf ("%s%s%s%s\n", current_term->name, current_term->flags & TERM_DUMB ? " (dumb)" : "", current_term->flags & TERM_NO_EDIT ? " (no edit)" : "", current_term->flags & TERM_NO_ECHO ? " (no echo)" : ""); return 0; } while (*arg) { int i; char *next = skip_to (0, arg); nul_terminate (arg); for (i = 0; term_table[i].name; i++) { if (grub_strcmp (arg, term_table[i].name) == 0) { if (term_table[i].flags & TERM_NEED_INIT) { errnum = ERR_DEV_NEED_INIT; return 1; } if (default_term < 0) default_term = i; term_bitmap |= (1 << i); break; } } if (! term_table[i].name) { errnum = ERR_BAD_ARGUMENT; return 1; } arg = next; } /* If multiple terminals are specified, wait until the user pushes any key on one of the terminals. */ if (term_bitmap & ~(1 << default_term)) { int time1, time2 = -1; /* XXX: Disable the pager. */ count_lines = -1; /* Get current time. */ while ((time1 = getrtsecs ()) == 0xFF) ; /* Wait for a key input. */ while (to) { int i; for (i = 0; term_table[i].name; i++) { if (term_bitmap & (1 << i)) { if (term_table[i].checkkey () >= 0) { (void) term_table[i].getkey (); default_term = i; goto end; } } } /* Prompt the user, once per sec. */ if ((time1 = getrtsecs ()) != time2 && time1 != 0xFF) { if (! no_message) { /* Need to set CURRENT_TERM to each of selected terminals. */ for (i = 0; term_table[i].name; i++) if (term_bitmap & (1 << i)) { current_term = term_table + i; grub_printf ("\rPress any key to continue.\n"); } /* Restore CURRENT_TERM. */ current_term = prev_term; } time2 = time1; if (to > 0) to--; } } } end: current_term = term_table + default_term; current_term->flags = term_flags; if (lines) max_lines = lines; else /* 24 would be a good default value. */ max_lines = 24; /* If the interface is currently the command-line, restart it to repaint the screen. */ if (current_term != prev_term && (flags & BUILTIN_CMDLINE)) grub_longjmp (restart_cmdline_env, 0); return 0; } static struct builtin builtin_terminal = { "terminal", terminal_func, BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "terminal [--dumb] [--no-echo] [--no-edit] [--timeout=SECS] [--lines=LINES] [--silent] [console] [serial] [hercules]", "Select a terminal. When multiple terminals are specified, wait until" " you push any key to continue. If both console and serial are specified," " the terminal to which you input a key first will be selected. If no" " argument is specified, print current setting. The option --dumb" " specifies that your terminal is dumb, otherwise, vt100-compatibility" " is assumed. If you specify --no-echo, input characters won't be echoed." " If you specify --no-edit, the BASH-like editing feature will be disabled." " If --timeout is present, this command will wait at most for SECS" " seconds. The option --lines specifies the maximum number of lines." " The option --silent is used to suppress messages." }; #endif /* SUPPORT_SERIAL || SUPPORT_HERCULES */ #ifdef SUPPORT_SERIAL static int terminfo_func (char *arg, int flags) { struct terminfo term; if (*arg) { struct { const char *name; char *var; } options[] = { {"--name=", term.name}, {"--cursor-address=", term.cursor_address}, {"--clear-screen=", term.clear_screen}, {"--enter-standout-mode=", term.enter_standout_mode}, {"--exit-standout-mode=", term.exit_standout_mode} }; grub_memset (&term, 0, sizeof (term)); while (*arg) { int i; char *next = skip_to (0, arg); nul_terminate (arg); for (i = 0; i < sizeof (options) / sizeof (options[0]); i++) { const char *name = options[i].name; int len = grub_strlen (name); if (! grub_memcmp (arg, name, len)) { grub_strcpy (options[i].var, ti_unescape_string (arg + len)); break; } } if (i == sizeof (options) / sizeof (options[0])) { errnum = ERR_BAD_ARGUMENT; return errnum; } arg = next; } if (term.name[0] == 0 || term.cursor_address[0] == 0) { errnum = ERR_BAD_ARGUMENT; return errnum; } ti_set_term (&term); } else { /* No option specifies printing out current settings. */ ti_get_term (&term); grub_printf ("name=%s\n", ti_escape_string (term.name)); grub_printf ("cursor_address=%s\n", ti_escape_string (term.cursor_address)); grub_printf ("clear_screen=%s\n", ti_escape_string (term.clear_screen)); grub_printf ("enter_standout_mode=%s\n", ti_escape_string (term.enter_standout_mode)); grub_printf ("exit_standout_mode=%s\n", ti_escape_string (term.exit_standout_mode)); } return 0; } static struct builtin builtin_terminfo = { "terminfo", terminfo_func, BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "terminfo [--name=NAME --cursor-address=SEQ [--clear-screen=SEQ]" " [--enter-standout-mode=SEQ] [--exit-standout-mode=SEQ]]", "Define the capabilities of your terminal. Use this command to" " define escape sequences, if it is not vt100-compatible." " You may use \\e for ESC and ^X for a control character." " If no option is specified, the current settings are printed." }; #endif /* SUPPORT_SERIAL */ /* testload */ static int testload_func (char *arg, int flags) { int i; kernel_type = KERNEL_TYPE_NONE; if (! grub_open (arg)) return 1; disk_read_hook = disk_read_print_func; /* Perform filesystem test on the specified file. */ /* Read whole file first. */ grub_printf ("Whole file: "); grub_read ((char *) RAW_ADDR (0x100000), -1); /* Now compare two sections of the file read differently. */ for (i = 0; i < 0x10ac0; i++) { *((unsigned char *) RAW_ADDR (0x200000 + i)) = 0; *((unsigned char *) RAW_ADDR (0x300000 + i)) = 1; } /* First partial read. */ grub_printf ("\nPartial read 1: "); grub_seek (0); grub_read ((char *) RAW_ADDR (0x200000), 0x7); grub_read ((char *) RAW_ADDR (0x200007), 0x100); grub_read ((char *) RAW_ADDR (0x200107), 0x10); grub_read ((char *) RAW_ADDR (0x200117), 0x999); grub_read ((char *) RAW_ADDR (0x200ab0), 0x10); grub_read ((char *) RAW_ADDR (0x200ac0), 0x10000); /* Second partial read. */ grub_printf ("\nPartial read 2: "); grub_seek (0); grub_read ((char *) RAW_ADDR (0x300000), 0x10000); grub_read ((char *) RAW_ADDR (0x310000), 0x10); grub_read ((char *) RAW_ADDR (0x310010), 0x7); grub_read ((char *) RAW_ADDR (0x310017), 0x10); grub_read ((char *) RAW_ADDR (0x310027), 0x999); grub_read ((char *) RAW_ADDR (0x3109c0), 0x100); grub_printf ("\nHeader1 = 0x%x, next = 0x%x, next = 0x%x, next = 0x%x\n", *((int *) RAW_ADDR (0x200000)), *((int *) RAW_ADDR (0x200004)), *((int *) RAW_ADDR (0x200008)), *((int *) RAW_ADDR (0x20000c))); grub_printf ("Header2 = 0x%x, next = 0x%x, next = 0x%x, next = 0x%x\n", *((int *) RAW_ADDR (0x300000)), *((int *) RAW_ADDR (0x300004)), *((int *) RAW_ADDR (0x300008)), *((int *) RAW_ADDR (0x30000c))); for (i = 0; i < 0x10ac0; i++) if (*((unsigned char *) RAW_ADDR (0x200000 + i)) != *((unsigned char *) RAW_ADDR (0x300000 + i))) break; grub_printf ("Max is 0x10ac0: i=0x%x, filepos=0x%x\n", i, filepos); disk_read_hook = 0; grub_close (); return 0; } static struct builtin builtin_testload = { "testload", testload_func, BUILTIN_CMDLINE, "testload FILE", "Read the entire contents of FILE in several different ways and" " compares them, to test the filesystem code. The output is somewhat" " cryptic, but if no errors are reported and the final `i=X," " filepos=Y' reading has X and Y equal, then it is definitely" " consistent, and very likely works correctly subject to a" " consistent offset error. If this test succeeds, then a good next" " step is to try loading a kernel." }; /* testvbe MODE */ static int testvbe_func (char *arg, int flags) { int mode_number; struct vbe_controller controller; struct vbe_mode mode; if (! *arg) { errnum = ERR_BAD_ARGUMENT; return 1; } if (! safe_parse_maxint (&arg, &mode_number)) return 1; /* Preset `VBE2'. */ grub_memmove (controller.signature, "VBE2", 4); /* Detect VBE BIOS. */ if (get_vbe_controller_info (&controller) != 0x004F) { grub_printf (" VBE BIOS is not present.\n"); return 0; } if (controller.version < 0x0200) { grub_printf (" VBE version %d.%d is not supported.\n", (int) (controller.version >> 8), (int) (controller.version & 0xFF)); return 0; } if (get_vbe_mode_info (mode_number, &mode) != 0x004F || (mode.mode_attributes & 0x0091) != 0x0091) { grub_printf (" Mode 0x%x is not supported.\n", mode_number); return 0; } /* Now trip to the graphics mode. */ if (set_vbe_mode (mode_number | (1 << 14)) != 0x004F) { grub_printf (" Switching to Mode 0x%x failed.\n", mode_number); return 0; } /* Draw something on the screen... */ { unsigned char *base_buf = (unsigned char *) mode.phys_base; int scanline = controller.version >= 0x0300 ? mode.linear_bytes_per_scanline : mode.bytes_per_scanline; /* FIXME: this assumes that any depth is a modulo of 8. */ int bpp = mode.bits_per_pixel / 8; int width = mode.x_resolution; int height = mode.y_resolution; int x, y; unsigned color = 0; /* Iterate drawing on the screen, until the user hits any key. */ while (checkkey () == -1) { for (y = 0; y < height; y++) { unsigned char *line_buf = base_buf + scanline * y; for (x = 0; x < width; x++) { unsigned char *buf = line_buf + bpp * x; int i; for (i = 0; i < bpp; i++, buf++) *buf = (color >> (i * 8)) & 0xff; } color++; } } /* Discard the input. */ getkey (); } /* Back to the default text mode. */ if (set_vbe_mode (0x03) != 0x004F) { /* Why?! */ grub_reboot (); } return 0; } static struct builtin builtin_testvbe = { "testvbe", testvbe_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "testvbe MODE", "Test the VBE mode MODE. Hit any key to return." }; #ifdef SUPPORT_NETBOOT /* tftpserver */ static int tftpserver_func (char *arg, int flags) { if (! *arg || ! ifconfig (0, 0, 0, arg)) { errnum = ERR_BAD_ARGUMENT; return 1; } print_network_configuration (); return 0; } static struct builtin builtin_tftpserver = { "tftpserver", tftpserver_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "tftpserver IPADDR", "Override the TFTP server address." }; #endif /* SUPPORT_NETBOOT */ /* timeout */ static int timeout_func (char *arg, int flags) { if (! safe_parse_maxint (&arg, &grub_timeout)) return 1; return 0; } static struct builtin builtin_timeout = { "timeout", timeout_func, BUILTIN_MENU, #if 0 "timeout SEC", "Set a timeout, in SEC seconds, before automatically booting the" " default entry (normally the first entry defined)." #endif }; /* title */ static int title_func (char *arg, int flags) { /* This function is not actually used at least currently. */ return 0; } static struct builtin builtin_title = { "title", title_func, BUILTIN_TITLE, #if 0 "title [NAME ...]", "Start a new boot entry, and set its name to the contents of the" " rest of the line, starting with the first non-space character." #endif }; /* unhide */ static int unhide_func (char *arg, int flags) { if (! set_device (arg)) return 1; if (! set_partition_hidden_flag (0)) return 1; return 0; } static struct builtin builtin_unhide = { "unhide", unhide_func, BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST, "unhide PARTITION", "Unhide PARTITION by clearing the \"hidden\" bit in its" " partition type code." }; /* uppermem */ static int uppermem_func (char *arg, int flags) { if (! safe_parse_maxint (&arg, (int *) &mbi.mem_upper)) return 1; mbi.flags &= ~MB_INFO_MEM_MAP; return 0; } static struct builtin builtin_uppermem = { "uppermem", uppermem_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "uppermem KBYTES", "Force GRUB to assume that only KBYTES kilobytes of upper memory are" " installed. Any system address range maps are discarded." }; /* vbeprobe */ static int vbeprobe_func (char *arg, int flags) { struct vbe_controller controller; unsigned short *mode_list; int mode_number = -1; auto unsigned long vbe_far_ptr_to_linear (unsigned long); unsigned long vbe_far_ptr_to_linear (unsigned long ptr) { unsigned short seg = (ptr >> 16); unsigned short off = (ptr & 0xFFFF); return (seg << 4) + off; } if (*arg) { if (! safe_parse_maxint (&arg, &mode_number)) return 1; } /* Set the signature to `VBE2', to obtain VBE 3.0 information. */ grub_memmove (controller.signature, "VBE2", 4); if (get_vbe_controller_info (&controller) != 0x004F) { grub_printf (" VBE BIOS is not present.\n"); return 0; } /* Check the version. */ if (controller.version < 0x0200) { grub_printf (" VBE version %d.%d is not supported.\n", (int) (controller.version >> 8), (int) (controller.version & 0xFF)); return 0; } /* Print some information. */ grub_printf (" VBE version %d.%d\n", (int) (controller.version >> 8), (int) (controller.version & 0xFF)); /* Iterate probing modes. */ for (mode_list = (unsigned short *) vbe_far_ptr_to_linear (controller.video_mode); *mode_list != 0xFFFF; mode_list++) { struct vbe_mode mode; if (get_vbe_mode_info (*mode_list, &mode) != 0x004F) continue; /* Skip this, if this is not supported or linear frame buffer mode is not support. */ if ((mode.mode_attributes & 0x0081) != 0x0081) continue; if (mode_number == -1 || mode_number == *mode_list) { char *model; switch (mode.memory_model) { case 0x00: model = "Text"; break; case 0x01: model = "CGA graphics"; break; case 0x02: model = "Hercules graphics"; break; case 0x03: model = "Planar"; break; case 0x04: model = "Packed pixel"; break; case 0x05: model = "Non-chain 4, 256 color"; break; case 0x06: model = "Direct Color"; break; case 0x07: model = "YUV"; break; default: model = "Unknown"; break; } grub_printf (" 0x%x: %s, %ux%ux%u\n", (unsigned) *mode_list, model, (unsigned) mode.x_resolution, (unsigned) mode.y_resolution, (unsigned) mode.bits_per_pixel); if (mode_number != -1) break; } } if (mode_number != -1 && mode_number != *mode_list) grub_printf (" Mode 0x%x is not found or supported.\n", mode_number); return 0; } static struct builtin builtin_vbeprobe = { "vbeprobe", vbeprobe_func, BUILTIN_CMDLINE | BUILTIN_HELP_LIST, "vbeprobe [MODE]", "Probe VBE information. If the mode number MODE is specified, show only" " the information about only the mode." }; /* The table of builtin commands. Sorted in dictionary order. */ struct builtin *builtin_table[] = { &builtin_blocklist, &builtin_boot, #ifdef SUPPORT_NETBOOT &builtin_bootp, #endif /* SUPPORT_NETBOOT */ &builtin_cat, &builtin_chainloader, &builtin_cmp, &builtin_color, &builtin_configfile, &builtin_debug, &builtin_default, #ifdef GRUB_UTIL &builtin_device, #endif /* GRUB_UTIL */ #ifdef SUPPORT_NETBOOT &builtin_dhcp, #endif /* SUPPORT_NETBOOT */ &builtin_displayapm, &builtin_displaymem, #ifdef GRUB_UTIL &builtin_dump, #endif /* GRUB_UTIL */ &builtin_embed, /* begin second part of sburtchin eptedit patch (new function - add lines) */ &builtin_epteditalpha, /* end second part of sburtchin eptedit patch (new function) */ &builtin_fallback, &builtin_find, &builtin_fstest, &builtin_geometry, &builtin_halt, &builtin_help, &builtin_hiddenmenu, &builtin_hide, #ifdef SUPPORT_NETBOOT &builtin_ifconfig, #endif /* SUPPORT_NETBOOT */ &builtin_impsprobe, &builtin_initrd, &builtin_install, &builtin_ioprobe, &builtin_kernel, &builtin_lock, &builtin_makeactive, &builtin_map, #ifdef USE_MD5_PASSWORDS &builtin_md5crypt, #endif /* USE_MD5_PASSWORDS */ &builtin_module, &builtin_modulenounzip, &builtin_pager, /* beg old code - - - - - - - - - - - - - &builtin_partnew, end old code - - - - - - - - - - - - - - - */ /* begin fift part of sburtchin partnew patch (replace lines) */ &builtin_partnewbeta, /* end fift part of sburtchin partnew patch */ &builtin_parttype, &builtin_password, &builtin_pause, #ifdef GRUB_UTIL &builtin_quit, #endif /* GRUB_UTIL */ #ifdef SUPPORT_NETBOOT &builtin_rarp, #endif /* SUPPORT_NETBOOT */ &builtin_read, &builtin_reboot, &builtin_root, &builtin_rootnoverify, &builtin_savedefault, #ifdef SUPPORT_SERIAL &builtin_serial, #endif /* SUPPORT_SERIAL */ &builtin_setkey, &builtin_setup, #if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) &builtin_terminal, #endif /* SUPPORT_SERIAL || SUPPORT_HERCULES */ #ifdef SUPPORT_SERIAL &builtin_terminfo, #endif /* SUPPORT_SERIAL */ &builtin_testload, &builtin_testvbe, #ifdef SUPPORT_NETBOOT &builtin_tftpserver, #endif /* SUPPORT_NETBOOT */ &builtin_timeout, &builtin_title, &builtin_unhide, &builtin_uppermem, &builtin_vbeprobe, 0 };