OS开发笔记(3)——grub-install代码阅读(基于i386-pc平台)
前言
由于安装grub到虚拟磁盘需要挂载,而挂载需要root权限,而且windows下也用不了(虽然我自己都不用Windows安装),所以我打算研究一下grub-install的代码,自己做一个简易的脚本来作为替代。
代码
读完之后再看,发现grub-install只是做了一些识别设备信息并匹配设置的准备工作,真正生成core.img的是grub-mkimage,真正负责安装的是grub-bios-setup(对于i386-pc平台)
以下代码根据我需要的目标平台i386-pc进行了删减,从而方便阅读
// 读取环境变量和命令行参数(略)
if (!grub_install_source_directory) {
if (!target) {
// 如果没有指定平台则使用本机信息
const char *t;
t = get_default_platform();
if (!t)
grub_util_error("%s", _("Unable to determine your platform."
" Use --target."));
target = xstrdup(t);
}
// 没有指定grub库目录则使用默认的
grub_install_source_directory =
grub_util_path_concat(2, grub_util_get_pkglibdir(), target);
}
switch (platform) {
case GRUB_INSTALL_PLATFORM_I386_PC:
// 对于i386-pc平台,默认使用bios引导,添加biosdisk模块
if (!disk_module)
disk_module = xstrdup("biosdisk");
break;
// 省略
}
switch (platform) {
case GRUB_INSTALL_PLATFORM_I386_PC:
case GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275:
// 没有指定安装设备就报错
if (!install_device)
grub_util_error("%s", _("install device isn't specified"));
break;
// 省略
}
// 如果没有指定boot目录,使用安装设备根目录下的boot/
if (!bootdir)
bootdir = grub_util_path_concat(3, "/", rootdir, GRUB_BOOT_DIR_NAME);
// 生成boot/grub目录的规范化路径
{
char *t = grub_util_path_concat(2, bootdir, GRUB_DIR_NAME);
grub_install_mkdir_p(t);
grubdir = grub_canonicalize_file_name(t);
if (!grubdir)
grub_util_error(_("failed to get canonical path of `%s'"), t);
free(t);
}
// 生成device.map文件路径
device_map = grub_util_path_concat(2, grubdir, "device.map");
if (recheck)
// 启用recheck后如果存在device.map就删除
grub_util_unlink(device_map);
// 检查device.map中是否有重复
device_map_check_duplicates(device_map);
// 读取device.map并注册设备
grub_util_biosdisk_init(device_map);
/* Initialize all modules. */
grub_init_all(); // (动态生成)调用所有模块的初始化函数
grub_gcry_init_all(); // 初始化加密模块
grub_hostfs_init(); // 注册本机文件系统接口
grub_host_init(); // 注册本机设备接口
switch (platform) {
// 略
default:
is_efi = 0;
break;
// 略
}
// 对其他平台的处理,略
size_t ndev = 0;
// 尝试获取安装grub的设备
/* Write device to a variable so we don't have to traverse /dev every time. */
grub_devices = grub_guess_root_devices(grubdir);
if (!grub_devices || !grub_devices[0])
grub_util_error(_("cannot find a device for %s (is /dev mounted?)"),
grubdir);
for (curdev = grub_devices; *curdev; curdev++) {
grub_util_pull_device(*curdev); // 对于普通设备,什么也不做
ndev++;
}
// 根据device.map获取所有设备
for (curdev = grub_devices, curdrive = grub_drives; *curdev;
curdev++, curdrive++) {
*curdrive = grub_util_get_grub_dev(*curdev);
if (!*curdrive)
grub_util_error(
_("cannot find a GRUB drive for %s. Check your device.map"),
*curdev);
}
*curdrive = 0;
// 打开设备
grub_dev = grub_device_open(grub_drives[0]);
if (!grub_dev)
grub_util_error("%s", grub_errmsg);
// 探测文件系统
grub_fs = grub_fs_probe(grub_dev);
if (!grub_fs)
grub_util_error("%s", grub_errmsg);
// 添加文件系统需要的模块
grub_install_push_module(grub_fs->name);
// 探测安装grub的磁盘需要的模块
if (grub_dev->disk)
probe_mods(grub_dev->disk);
// 为其他磁盘探测需要的模块
for (curdrive = grub_drives + 1; *curdrive; curdrive++) {
grub_device_t dev = grub_device_open(*curdrive);
if (!dev)
continue;
if (dev->disk)
probe_mods(dev->disk);
grub_device_close(dev);
}
// 补充需要的加密、驱动模块和参数输入的模块,略
// 将boot/grub/从相对路径转换成绝对路径
relative_grubdir = grub_make_system_path_relative_to_its_root(grubdir);
if (relative_grubdir[0] == '\0') {
free(relative_grubdir);
relative_grubdir = xstrdup("/");
}
char *prefix_drive = NULL;
char *install_drive = NULL;
if (install_device) {
if (install_device[0] == '(' &&
install_device[grub_strlen(install_device) - 1] == ')') {
// 如果有指定安装设备则将install_drive设为指定的设备
size_t len = grub_strlen(install_device) - 2;
install_drive = xmalloc(len + 1);
memcpy(install_drive, install_device + 1, len);
install_drive[len] = '\0';
} else {
// 没有指定则自动获取
grub_util_pull_device(install_device);
install_drive = grub_util_get_grub_dev(install_device);
if (!install_drive)
grub_util_error(
_("cannot find a GRUB drive for %s. Check your device.map"),
install_device);
}
}
// 把grub的文件都复制进去
grub_install_copy_files(grub_install_source_directory, grubdir, platform);
// 创建grub运行时的环境变量文件
char *envfile = grub_util_path_concat(2, grubdir, "grubenv");
if (!grub_util_is_regular(envfile))
grub_util_create_envblk_file(envfile);
// 生成平台文件夹路径,例如boot/grub/i386-pc/
char *platname = grub_install_get_platform_name(platform);
char *platdir;
{
char *t = grub_util_path_concat(2, grubdir, platname);
platdir = grub_canonicalize_file_name(t);
if (!platdir)
grub_util_error(_("failed to get canonical path of `%s'"), t);
free(t);
}
// 删除load.cfg
load_cfg = grub_util_path_concat(2, platdir, "load.cfg");
grub_util_unlink(load_cfg);
if (!have_abstractions) {
// 有抽象层,可能意味着有文件系统?
if ((disk_module && grub_strcmp(disk_module, "biosdisk") != 0) // 可通过非BIOS调用管理的磁盘
|| grub_drives[1] // 有多个磁盘
|| (!install_drive && platform != GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275)
|| (install_drive && !is_same_disk(grub_drives[0], install_drive)) // 要安装到的磁盘不是第一个磁盘
|| !have_bootdev(platform) // 对于i386-pc,have_bootdev的返回为1,所以可以忽略
) {
char *uuid = NULL;
/* generic method (used on coreboot and ata mod). */
if (!force_file_id // 如果为指定force_file_id
&& grub_fs->fs_uuid // 有生成uuid的实现
&& grub_fs->fs_uuid(grub_dev, &uuid) // 成功生成uuid
) {
grub_print_error();
grub_errno = 0;
uuid = NULL;
}
// 如果没有load.cfg就创建新的
if (!load_cfg_f)
load_cfg_f = grub_util_fopen(load_cfg, "wb");
have_load_cfg = 1;
if (uuid) {
// 有uuid则配置为根据uuid搜索grub所在的分区
fprintf(load_cfg_f, "search.fs_uuid %s root ", uuid);
grub_install_push_module("search_fs_uuid");
} else {
// 无uuid则生成一个标识文件用于定位
char *rndstr = get_rndstr();
char *fl = grub_util_path_concat(3, grubdir, "uuid", rndstr);
char *fldir = grub_util_path_concat(2, grubdir, "uuid");
char *relfl;
FILE *flf;
grub_install_mkdir_p(fldir);
flf = grub_util_fopen(fl, "w");
if (!flf)
grub_util_error(_("Can't create file: %s"), strerror(errno));
fclose(flf);
relfl = grub_make_system_path_relative_to_its_root(fl);
fprintf(load_cfg_f, "search.file %s root ", relfl);
grub_install_push_module("search_fs_file");
}
// 循环添加所有磁盘设备到load.cfg中,略
fprintf(load_cfg_f, "\n"); // 搜索命令结束
// 设定环境变量命令,指定grub目录位置
char *escaped_relpath = escape(relative_grubdir);
fprintf(load_cfg_f, "set prefix=($root)'%s'\n", escaped_relpath);
} else {
// 对于其他磁盘,硬编码分区号到core映像中
/* We need to hardcode the partition number in the core image's prefix. */
char *p;
for (p = grub_drives[0]; *p;) {
// 跳过转义字符
if (*p == '\\' && p[1]) {
p += 2;
continue;
}
// 遇到分隔符或结束
if (*p == ',' || *p == '\0')
break;
p++;
}
prefix_drive = xasprintf("(%s)", p);
}
} else {
// 启用加密时的处理,略
// 直接复制驱动器号
prefix_drive = xasprintf("(%s)", grub_drives[0]);
}
// 调用grub-mkimage生成core.img,略
switch (platform) {
case GRUB_INSTALL_PLATFORM_I386_PC: {
// 复制boot.img到grub平台目录
char *boot_img_src =
grub_util_path_concat(2, grub_install_source_directory, "boot.img");
char *boot_img = grub_util_path_concat(2, platdir, "boot.img");
grub_install_copy_file(boot_img_src, boot_img, 1);
// 中间略
/* Now perform the installation. */
if (install_bootsector) {
// 调用grub-bios-setup安装编译好的boot.img和刚生成的core.img
grub_util_bios_setup(platdir, "boot.img", "core.img", install_drive,
force, fs_probe, allow_floppy, add_rs_codes,
!grub_install_is_short_mbrgap_supported());
grub_set_install_backup_ponr();
}
break;
}
// 其他平台略
}
// 结束部分
/*
* Either there are no platform specific code, or it didn't raise
* ponr. Raise it here, because usually this is already past point
* of no return. If we leave this flag false, at exit all the modules
* will be removed from the prefix which would be very confusing.
*/
grub_set_install_backup_ponr();
fprintf(stderr, "%s\n", _("Installation finished. No error reported."));
/* Free resources. */
grub_gcry_fini_all();
grub_fini_all();
分类:
OS开发笔记
, OS开发笔记 / 引导程序
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)