Thursday, May 8, 2008

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, &current_partition, &current_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)