blob: d6a102d16020f262142c94ffbec2b132d7fe6bec [file] [log] [blame] [raw]
/* builtins.c - the GRUB builtin commands */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1996 Erich Boleyn <erich@uruk.org>
* Copyright (C) 1999, 2000, 2001 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 <stdio.h>
#endif
#include <shared.h>
#include <filesys.h>
#ifdef SUPPORT_NETBOOT
# include <etherboot.h>
#endif
#ifdef SUPPORT_SERIAL
# include <serial.h>
#endif
#ifdef GRUB_UTIL
# include <device.h>
#else /* ! GRUB_UTIL */
# include <apic.h>
# include <smp-imps.h>
#endif /* ! GRUB_UTIL */
#ifdef USE_MD5_PASSWORDS
# include <md5.h>
#endif
/* Terminal types. */
int terminal = TERMINAL_CONSOLE;
/* 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_entry = -1;
/* 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;
/* Color settings. */
int normal_color;
int highlight_color;
/* 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];
/* 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;
normal_color = A_NORMAL;
highlight_color = A_REVERSE;
password = 0;
fallback_entry = -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;
/* Collect contiguous blocks into one entry as many as possible,
and print the blocklist notation on the screen. */
static 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,
"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 ();
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;
/* Copy the boot partition information to 0x7be-0x7fd, if
BOOT_DRIVE is a hard disk drive. */
if (boot_drive & 0x80)
{
char *dst, *src;
int i;
/* Read the MBR here, because it might be modified
after opening the partition. */
if (! rawread (boot_drive, boot_part_offset,
0, SECTOR_SIZE, (char *) SCRATCHADDR))
{
/* This should never happen. */
errnum = ERR_READ;
return 0;
}
/* Need only the partition table.
XXX: We cannot use grub_memmove because BOOT_PART_TABLE
(0x07be) is less than 0x1000. */
dst = (char *) BOOT_PART_TABLE;
src = (char *) SCRATCHADDR + BOOTSEC_PART_OFFSET;
while (dst < (char *) BOOT_PART_TABLE + BOOTSEC_PART_LENGTH)
*dst++ = *src++;
/* Set the active flag of the booted partition. */
for (i = 0; i < 4; i++)
PC_SLICE_FLAG (BOOT_PART_TABLE, i) = 0;
*((unsigned char *) boot_part_addr) = PC_SLICE_FLAG_BOOTABLE;
}
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,
"boot",
"Boot the OS/chain-loader which has been loaded."
};
#ifdef SUPPORT_NETBOOT
/* bootp */
static int
bootp_func (char *arg, int flags)
{
if (! bootp ())
{
if (errnum == ERR_NONE)
errnum = ERR_DEV_VALUES;
return 1;
}
/* Notify the configuration. */
print_network_configuration ();
return 0;
}
static struct builtin builtin_bootp =
{
"bootp",
bootp_func,
BUILTIN_CMDLINE | BUILTIN_MENU,
"bootp",
"Initialize a network device via BOOTP."
};
#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))
grub_putchar (c);
grub_close ();
return 0;
}
static struct builtin builtin_cat =
{
"cat",
cat_func,
BUILTIN_CMDLINE,
"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. I don't know why, but Windows seems not to
set the start address of an extended partition in the BPB correctly.
So this is necessary to make Windows bootable even with an extended
partition. Maybe this should be made only for Windows, but how can
we determine if it is Windows or not precisely?! */
if (open_partition () && fat_mount ())
*((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,
"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"
};
/* Convert the color name STR into the magical number. */
static 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;
}
normal_color = new_normal_color;
highlight_color = new_highlight_color;
return 0;
}
static struct builtin builtin_color =
{
"color",
color_func,
BUILTIN_CMDLINE | BUILTIN_MENU,
"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,
"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,
"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,
"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:
Version: 0x%x
32-bit CS: 0x%x
Offset: 0x%x
16-bit CS: 0x%x
16-bit DS: 0x%x
32-bit CS length: 0x%x
16-bit CS length: 0x%x
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,
"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: %u X 4GB + %u 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,
"displaymem",
"Display what GRUB thinks the system address space map of the"
" machine is, including all regions of physical RAM installed."
};
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];
/* 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. */
if (PC_SLICE_START (mbr, 0) - 1 < size)
{
errnum = ERR_DEV_VALUES;
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_DEV_VALUES;
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."
};
/* fallback */
static int
fallback_func (char *arg, int flags)
{
if (! safe_parse_maxint (&arg, &fallback_entry))
return 1;
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,
"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 (&sector, &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,
"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,
"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)
{
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 run in the command-line interface,
skip this. */
if (! ((*builtin)->flags & BUILTIN_CMDLINE))
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;
}
}
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,
"help [PATTERN ...]",
"Display helpful information about builtin commands."
};
/* 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,
"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,
"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,
"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 = 0xFF;
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 */
/* Save the first sector of Stage2 in STAGE2_SECT. */
static 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. */
static 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;
/* 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 = 0xFF;
}
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;
/* 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,
"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,
"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,
"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,
"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,
"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,
"modulenounzip FILE [ARG ...]",
"The same as `module', except that automatic decompression is"
" disabled."
};
/* partnew PART TYPE START LEN */
static int
partnew_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)
{
int cylinder, head, sector;
sector = lba % buf_geom.sectors + 1;
head = (lba / buf_geom.sectors) % buf_geom.heads;
cylinder = lba / (buf_geom.sectors * buf_geom.heads);
if (cylinder >= buf_geom.cylinders)
cylinder = buf_geom.cylinders - 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 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_partnew =
{
"partnew",
partnew_func,
BUILTIN_CMDLINE | BUILTIN_MENU,
"partnew 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,
"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_HIDDEN,
"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_HIDDEN,
"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,
"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,
"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,
"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
root_func (char *arg, int flags)
{
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 (! open_device () && errnum != ERR_FSYS_MOUNT)
return 1;
/* Clear ERRNUM. */
errnum = 0;
saved_partition = current_partition;
saved_drive = current_drive;
/* BSD and chainloading evil hacks !! */
biasptr = skip_to (0, next);
safe_parse_maxint (&biasptr, &hdbias);
errnum = 0;
bootdev = set_bootdev (hdbias);
/* Print the type of the filesystem. */
print_fsys_type ();
return 0;
}
static struct builtin builtin_root =
{
"root",
root_func,
BUILTIN_CMDLINE,
"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)
{
/* If ARG is empty, just print the current root device. */
if (! *arg)
{
print_root_device ();
return 0;
}
if (! set_device (arg))
return 1;
saved_partition = current_partition;
saved_drive = current_drive;
current_drive = -1;
return 0;
}
static struct builtin builtin_rootnoverify =
{
"rootnoverify",
rootnoverify_func,
BUILTIN_CMDLINE,
"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)
char buffer[512];
int *entryno_ptr;
/* This command is only useful when you boot an entry from the menu
interface. */
if (! (flags & BUILTIN_SCRIPT))
{
errnum = ERR_UNRECOGNIZED;
return 1;
}
/* Get the geometry of the boot drive (i.e. the disk which contains
this stage2). */
if (get_diskinfo (boot_drive, &buf_geom))
{
errnum = ERR_NO_DISK;
return 1;
}
/* Load the second sector of this stage2. */
if (! rawread (boot_drive, install_second_sector, 0, SECTOR_SIZE, buffer))
{
return 1;
}
entryno_ptr = (int *) ((char *) buffer + STAGE2_SAVED_ENTRYNO);
/* Check if the saved entry number differs from current entry number. */
if (*entryno_ptr != current_entryno)
{
/* Overwrite the saved entry number. */
*entryno_ptr = current_entryno;
/* Save the image in the disk. */
if (! rawwrite (boot_drive, install_second_sector, buffer))
return 1;
/* Clear the cache. */
buf_track = -1;
}
return 0;
#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",
"Save the current entry as the default boot entry."
};
#ifdef SUPPORT_SERIAL
/* serial */
static int
serial_func (char *arg, int flags)
{
unsigned short port = serial_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_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;
set_serial_device (dev);
}
# endif /* GRUB_UTIL */
else
break;
arg = skip_to (0, arg);
}
/* Initialize the serial unit. */
if (! serial_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,
"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;
static 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;
}
static 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,
"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"},
{"ffs", "/ffs_stage1_5"},
{"fat", "/fat_stage1_5"},
{"minix", "/minix_stage1_5"},
{"reiserfs", "/reiserfs_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);
#ifdef NO_BUGGY_BIOS_IN_THE_WORLD
/* I prefer this, but... */
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 /* ! NO_BUGGY_BIOS_IN_THE_WORLD */
/* Actually, there are several buggy BIOSes in the world, so we
may not expect that your BIOS will pass a booting drive to stage1
correctly. Thus, always specify the option `d', whether
INSTALLED_DRIVE is identical with IMAGE_DRIVE or not. *sigh* */
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 /* ! NO_BUGGY_BIOS_IN_THE_WORLD */
/* 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,
"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."
};
#ifdef SUPPORT_SERIAL
/* terminal */
static int
terminal_func (char *arg, int flags)
{
int default_terminal = 0;
int to = -1;
int dumb = 0;
int saved_terminal = terminal;
/* Get GNU-style long options. */
while (1)
{
if (grub_memcmp (arg, "--dumb", sizeof ("--dumb") - 1) == 0)
dumb = 1;
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
break;
arg = skip_to (0, arg);
}
/* If no argument is specified, show current setting. */
if (! *arg)
{
if (terminal & TERMINAL_CONSOLE)
grub_printf ("console%s\n",
terminal & TERMINAL_DUMB ? " (dumb)" : "");
else if (terminal & TERMINAL_SERIAL)
grub_printf ("serial%s\n",
terminal & TERMINAL_DUMB ? " (dumb)" : " (vt100)");
return 0;
}
/* Clear current setting. */
terminal = dumb ? TERMINAL_DUMB : 0;
while (*arg)
{
if (grub_memcmp (arg, "console", sizeof ("console") - 1) == 0)
{
terminal |= TERMINAL_CONSOLE;
if (! default_terminal)
default_terminal = TERMINAL_CONSOLE;
}
else if (grub_memcmp (arg, "serial", sizeof ("serial") - 1) == 0)
{
terminal |= TERMINAL_SERIAL;
if (! default_terminal)
default_terminal = TERMINAL_SERIAL;
}
else
{
terminal = saved_terminal;
errnum = ERR_BAD_ARGUMENT;
return 1;
}
arg = skip_to (0, arg);
}
/* If a seial console is turned on, wait until the user pushes any key. */
if (terminal & TERMINAL_SERIAL)
{
int time1, time2 = -1;
/* Get current time. */
while ((time1 = getrtsecs ()) == 0xFF)
;
/* Wait for a key input. */
while (to)
{
if ((terminal & TERMINAL_CONSOLE) && console_checkkey () != -1)
{
terminal &= (TERMINAL_CONSOLE | TERMINAL_DUMB);
(void) getkey ();
return 0;
}
else if ((terminal & TERMINAL_SERIAL) && serial_checkkey () != -1)
{
terminal &= (TERMINAL_SERIAL | TERMINAL_DUMB);
(void) getkey ();
/* If the interface is currently the command-line, restart
it to repaint the screen. */
if (flags & BUILTIN_CMDLINE)
grub_longjmp (restart_cmdline_env, 0);
return 0;
}
/* Prompt the user, once per sec. */
if ((time1 = getrtsecs ()) != time2 && time1 != 0xFF)
{
grub_printf ("Press any key to continue.\n");
time2 = time1;
if (to > 0)
to--;
}
}
/* Expired. */
terminal &= (default_terminal | TERMINAL_DUMB);
}
return 0;
}
static struct builtin builtin_terminal =
{
"terminal",
terminal_func,
BUILTIN_MENU | BUILTIN_CMDLINE,
"terminal [--dumb] [--timeout=SECS] [console] [serial]",
"Select a terminal. When serial is 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 speicifies that"
" your terminal is dumb, otherwise, vt100-compatibility is assumed."
" If --timeout is present, this command will wait at most for SECS"
" seconds."
};
#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,
"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,
"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,
"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,
"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;
int count = 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;
count++;
/* XXX: arbitrary. */
if (count == 22)
{
grub_printf ("\nHit any key to continue.\n");
count = 0;
getkey ();
}
}
}
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,
"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,
&builtin_embed,
&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_partnew,
&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,
#ifdef SUPPORT_SERIAL
&builtin_terminal,
#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
};