Bard's Gallery

grub legacy root命令执行过程详解

Bergwolf TECH

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)
Bergwolf
Everyday citizen, A gear