grub legacy root命令执行过程详解
root命令执行过程:
grub所有支持的命令都在struct builtin *builtin_table[]中
struct builtin
{
/* The command name. */
char *name;
/* The callback function. */
int (*func) (char *, int);
/* The combination of the flags defined above. */
int flags;
/* The short version of the documentation. */
char *short_doc;
/* The long version of the documentation. */
char *long_doc;
};
grub先调用struct builtin *find_command (char *command)解析root命令为
static struct builtin builtin_root =
{
"root",
root_func,
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
"root [DEVICE [HDBIAS]]",
"Set the current \"root device\" to the device DEVICE, then"
" attempt to mount it to get the partition size (for passing the"
" partition descriptor in `ES:ESI', used by some chain-loaded"
" bootloaders), the BSD drive-type (for booting BSD kernels using"
" their native boot format), and correctly determine "
" the PC partition where a BSD sub-partition is located. The"
" optional HDBIAS parameter is a number to tell a BSD kernel"
" how many BIOS drive numbers are on controllers before the current"
" one. For example, if there is an IDE disk and a SCSI disk, and your"
" FreeBSD root partition is on the SCSI disk, then use a `1' for HDBIAS."
};
然后调用static int root_func (char *arg, int flags),该函数实际返回 real_root_func (arg, 1)
1. 不带参数,real_root_func(arg, 1)显示当前root分区
grub> root
(hd0,2): Filesystem type is ext2fs, partition type 0x83
当static int real_root_func (char *arg, int attempt_mount)
的arg为空的时候,该函数调用static void print_root_device (void)
然后返回。
这块代码很简单,如下:
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 ();
}
saved_drive
是作为root的drive number,初始为boot_drive=0
saved_partition
是作为root的partition number, 初始为install_partition = 0x20000
这两个值都在static int real_root_func (char *arg, int attempt_mount)
中当被修改
void
print_fsys_type (void)
{
if (! do_completion)
{
printf (" Filesystem type ");
if (fsys_type != NUM_FSYS)
printf ("is %s, ", fsys_table[fsys_type].name);
else
printf ("unknown, ");
if (current_partition == 0xFFFFFF)
printf ("using whole disk\n");
else
printf ("partition type 0x%x\n", current_slice & 0xFF);
}
}
do_completion
是一个全局变量,用来标识current_partition是否已经mount,其值通过int print_completions (int is_filename, int is_completion)
来修改,print_completions
将is_completion的值赋给do_completion。
2. 带参数,real_root_func(arg, 1)尝试mount指定分区
mount成功:
grub> root (hd0,0)
Filesystem type is ext2fs, partition type 0x83
mount失败:
grub> root (hd0,1)
Filesystem type is unknown, partition type 0x82
real_root_func(arg, 1)
先调用set_device (arg)
将arg中的drive和partition信息写入current_drive和current_partition,然后调用open_device()
来mount current_drive和current_partition然后调用int set_bootdev (int hdbias)
来设置BSD partition和chain-loading(这里没看懂。。。)
最后print_fsys_type ()
打印分区由fsys_type指明的current_partition的信息
int
open_device (void)
{
if (open_partition ())
attempt_mount ();
if (errnum != ERR_NONE)
return 0;
return 1;
}
这里是一串的调用。。。
open_device() -> open_partition() -> real_open_partition(0)
open_partition(0)返回1的情况有以下几种:
current_drive == NETWORK_DRIVE
current_partition == 0xFFFFFF,即当前分区是整块磁盘
找到了current_drive中的current_partition
判断partition是否相同的方法
dest_partition == current_partition
:对非BSD的partition有效
对于BSD partition,使用下面的方法判断:
bsd_part = (current_partition >> 8) & 0xFF;
((dest_partition >> 16) == 0xFF && ((dest_partition >> 8) & 0xFF) == bsd_part)))
bsd partition使用不同的机制,所以需要特别处理
/* these ones are special, as they use their own partitioning scheme
to subdivide the PC partitions from there. */
#define PC_SLICE_TYPE_FREEBSD 0xa5
#define PC_SLICE_TYPE_OPENBSD 0xa6
#define PC_SLICE_TYPE_NETBSD 0xa9
实际完成mount的是attempt_mount()
static void
attempt_mount (void)
{
#ifndef STAGE1_5
for (fsys_type = 0; fsys_type < fsys_type ="="" errnum ="="" errnum =" ERR_FSYS_MOUNT;" fsys_type =" 0;" fsys_type =" NUM_FSYS;" errnum =" ERR_FSYS_MOUNT;" retval =" 1;">s_magic != EXT2_SUPER_MAGIC)
retval = 0;
return retval;
}
全局变量current_slice用来描述partition type,part_length用来描述partition的长度(单位,sector)
在real_open_partition (0)
中调用next_partition (current_drive, dest_partition, ¤t_partition, ¤t_slice, &part_start, &part_length, &part_offset, &entry, &ext_offset, buf)
将current_slice赋值为current_partition的partition type,将part_length赋值为current_partition的length
devread (SBLOCK, 0, sizeof (struct ext2_super_block), (char *) SUPERBLOCK)
将第二个sector,偏移0开始的sizeof(ext2_super_block)长度的数据读入到SUPERBLOCK指向的内存中,实现grub中所谓的mount
S_ISREG(m) is it a regular file?
S_ISDIR(m) directory?
S_ISCHR(m) character device?
S_ISBLK(m) block device?
S_ISFIFO(m) fifo?
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
/* linux/stat.h */
#define S_IFMT 00170000
#define S_IFLNK 0120000
#define S_IFREG 0100000
#define S_IFDIR 0040000
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)