grub-bios-setup代码分析(基于i386-pc平台)

news/2025/2/3 19:38:29/文章来源:https://www.cnblogs.com/Ryan1202/p/18697654

前言

读完grub-install的代码发现grub安装的核心部分还是在于grub-bios-setupgrub-mkimage仅仅是用于生成core.img所以可以直接使用,而grub-bios-setup则是只能操作设备所以需要被“取缔”


grub启动的流程

grub的启动有三个部分:

  1. 第一部分的boot.S(编译成boot.img)保存在引导扇区,由BIOS加载
  2. 第二部分根据启动设备的不同,有cdboot.Sdiskboot.Slnxboot.Spxeboot.S等几种不同的程序,通常只有一个扇区大,将扇区号和大小写入boot.img后由boot.S加载
  3. 第三部分就是grub的核心grub-core了,grub-core由第二部分的程序(从磁盘启动就是diskboot.S)加载,如果从磁盘启动会把blocklists写入到diskboot的末尾

Blocklists

grub中,grub-core在磁盘中的位置通过blocklists来描述。在bios-setup中,blocklists的结构如下:

struct blocklists
{struct grub_boot_blocklist *first_block, *block;grub_uint16_t current_segment;grub_uint16_t last_length;grub_disk_addr_t first_sector;
};

在i386-pc平台上,grub_boot_blocklist就是grub_pc_bios_boot_blocklist

struct grub_pc_bios_boot_blocklist
{grub_uint64_t start;grub_uint16_t len;grub_uint16_t segment;
} GRUB_PACKED;

如果grub-core保存在一块连续的空间中(比如放在第一个分区和引导扇区之间的空间中),那么只需要一个grub_boot_blocklist就能表示;如果grub-core被分割开存储(如存在文件系统中),就可以通过多个grub_boot_blocklist来表示。

代码

了解完这些知识后,就可以开始读grub-bios-setup的核心代码——grub_util_bios_setup()

能够嵌入的情况

bl.first_sector = (grub_disk_addr_t) -1;// 设定启动时加载的目标地址
bl.current_segment =GRUB_BOOT_I386_PC_KERNEL_SEG + (GRUB_DISK_SECTOR_SIZE >> 4);
bl.last_length = 0;// 打开boot.img和core.img文件,略// core.img第一个扇区末尾就是diskboot的末尾,存着blocklist结构
/* Have FIRST_BLOCK to point to the first blocklist.  */
bl.first_block = (struct grub_boot_blocklist *) (core_img+ GRUB_DISK_SECTOR_SIZE- sizeof (*bl.block));// 打开目标设备
grub_util_info ("Opening dest `%s'", dest);
dest_dev = grub_device_open (dest);
if (! dest_dev)grub_util_error ("%s", grub_errmsg);core_dev = dest_dev;// 遍历寻找合适的设备作为根设备
{char **root_devices = grub_guess_root_devices(dir);char **cur;int found = 0;if (!root_devices)grub_util_error(_("cannot find a device for %s (is /dev mounted?)"), dir);for (cur = root_devices; *cur; cur++) {char *drive;grub_device_t try_dev;drive = grub_util_get_grub_dev(*cur);if (!drive)continue;try_dev = grub_device_open(drive);if (!try_dev) {free(drive);continue;}if (!found && try_dev->disk->id == dest_dev->disk->id &&try_dev->disk->dev->id == dest_dev->disk->dev->id) {if (root_dev)grub_device_close(root_dev);free(root);root_dev = try_dev;root = drive;found = 1;continue;}if (!root_dev) {root_dev = try_dev;root = drive;continue;}grub_device_close(try_dev);free(drive);}if (!root_dev) {grub_util_error("guessing the root device failed, because of `%s'",grub_errmsg);}grub_util_info("guessed root_dev `%s' from ""dir `%s'",root_dev->disk->name, dir);for (cur = root_devices; *cur; cur++)free(*cur);free(root_devices);
}
// 设置grub运行时的root环境变量
grub_util_info("setting the root device to `%s'", root);
if (grub_env_set("root", root) != GRUB_ERR_NONE)grub_util_error("%s", grub_errmsg);{// 写入boot.imgchar *tmp_img;grub_uint8_t *boot_drive_check;/* Read the original sector from the disk.  */tmp_img = xmalloc(GRUB_DISK_SECTOR_SIZE);if (grub_disk_read(dest_dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, tmp_img))grub_util_error("%s", grub_errmsg);boot_drive_check =(grub_uint8_t *)(boot_img + GRUB_BOOT_MACHINE_DRIVE_CHECK);/* Copy the possible DOS BPB.  */memcpy(boot_img + GRUB_BOOT_MACHINE_BPB_START,tmp_img + GRUB_BOOT_MACHINE_BPB_START,GRUB_BOOT_MACHINE_BPB_END - GRUB_BOOT_MACHINE_BPB_START);/* If DEST_DRIVE is a hard disk, enable the workaround, which isfor buggy BIOSes which don't pass boot drive correctly. Instead,they pass 0x00 or 0x01 even when booted from 0x80.  */// 如果不需要软盘支持,则替换原指令为nop指令if (!allow_floppy && !grub_util_biosdisk_is_floppy(dest_dev->disk)) {/* Replace the jmp (2 bytes) with double nop's.  */boot_drive_check[0] = 0x90;boot_drive_check[1] = 0x90;}// 写入core.imgstruct identify_partmap_ctx ctx = {.dest_partmap = NULL,.container = dest_dev->disk->partition,.multiple_partmaps = 0};int is_ldm;grub_err_t err;grub_disk_addr_t *sectors;int i;grub_fs_t fs;unsigned int nsec, maxsec;// 遍历所有分区grub_partition_iterate(dest_dev->disk, identify_partmap, &ctx);/* Copy the partition table.  */if (ctx.dest_partmap ||(!allow_floppy && !grub_util_biosdisk_is_floppy(dest_dev->disk)))memcpy(boot_img + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC,tmp_img + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC,GRUB_BOOT_MACHINE_PART_END - GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC);free(tmp_img);if (ctx.container &&grub_strcmp(ctx.container->partmap->name, "msdos") == 0 &&ctx.dest_partmap &&(ctx.container->msdostype == GRUB_PC_PARTITION_TYPE_NETBSD ||ctx.container->msdostype == GRUB_PC_PARTITION_TYPE_OPENBSD)) {grub_util_warn("%s",_("Attempting to install GRUB to a disk with multiple ""partition labels or both partition label and ""filesystem.  This is not supported yet."));goto unable_to_embed;}// 找到一个可用的文件系统fs = grub_fs_probe(dest_dev);if (!fs)grub_errno = GRUB_ERR_NONE;is_ldm = grub_util_is_ldm(dest_dev->disk);// 中间错误处理,略nsec = core_sectors;if (add_rs_codes)maxsec = 2 * core_sectors;elsemaxsec = core_sectors;if (maxsec >((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) >> GRUB_DISK_SECTOR_BITS))maxsec =((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) >> GRUB_DISK_SECTOR_BITS);if (is_ldm)// 忽略else if (ctx.dest_partmap)// 如果有分区表则用该分区表的嵌入方法err = ctx.dest_partmap->embed(dest_dev->disk, &nsec, maxsec,GRUB_EMBED_PCBIOS, &sectors, warn_small);else// 没有则尝试嵌入到文件系统中err = fs->fs_embed(dest_dev, &nsec, maxsec, GRUB_EMBED_PCBIOS, &sectors);if (!err && nsec < core_sectors) {err = grub_error(GRUB_ERR_OUT_OF_RANGE,N_("Your embedding area is unusually small.  ""core.img won't fit in it."));}if (err) {grub_util_warn("%s", grub_errmsg);grub_errno = GRUB_ERR_NONE;goto unable_to_embed;}// 清除原来的blocklists/* Clean out the blocklists.  */bl.block = bl.first_block;while (bl.block->len) {grub_memset(bl.block, 0, sizeof(*bl.block));bl.block--;if ((char *)bl.block <= core_img)grub_util_error("%s", _("no terminator in the core image"));}bl.block = bl.first_block;for (i = 0; i < nsec; i++)save_blocklists(sectors[i] + grub_partition_get_start(ctx.container), 0,GRUB_DISK_SECTOR_SIZE, &bl);// 确保在最后一个blocklist作为终止/* Make sure that the last blocklist is a terminator.  */if (bl.block == bl.first_block)bl.block--;bl.block->start = 0;bl.block->len = 0;bl.block->segment = 0;// 将xxboot如diskboot的位置信息写入boot.imgwrite_rootdev(root_dev, boot_img, bl.first_sector);// 重新分配并对齐core.img的大小到扇区大小的倍数/* Round up to the nearest sector boundary, and zero the extra memory */core_img = xrealloc(core_img, nsec * GRUB_DISK_SECTOR_SIZE);assert(core_img && (nsec * GRUB_DISK_SECTOR_SIZE >= core_size));memset(core_img + core_size, 0, nsec * GRUB_DISK_SECTOR_SIZE - core_size);bl.first_block =(struct grub_boot_blocklist *)(core_img + GRUB_DISK_SECTOR_SIZE -sizeof(*bl.block));// 未使用reed solomon编码的大小grub_size_t no_rs_length;no_rs_length = grub_get_unaligned16(core_img + GRUB_DISK_SECTOR_SIZE +GRUB_KERNEL_I386_PC_NO_REED_SOLOMON_LENGTH);if (no_rs_length == 0xffff)grub_util_error("%s", _("core.img version mismatch"));if (add_rs_codes) {// 使用reed solomon进行编码,略}// 将修改完的core.img写到磁盘中/* Write the core image onto the disk.  */for (i = 0; i < nsec; i++)grub_disk_write(dest_dev->disk, sectors[i], 0, GRUB_DISK_SECTOR_SIZE,core_img + i * GRUB_DISK_SECTOR_SIZE);grub_free(sectors);goto finish;
}

不能嵌入的情况

上面是能够成功嵌入的情况,下面是对不能嵌入时的处理:

unable_to_embed:
// 不能嵌入的情况
if (dest_dev->disk->dev->id != root_dev->disk->dev->id)grub_util_error("%s",_("embedding is not possible, but this is required for ""RAID and LVM install"));// 判断grub所在分区是否可以使用blocklist方式安装grub-core
{grub_fs_t fs;fs = grub_fs_probe(root_dev);if (!fs)grub_util_error(_("can't determine filesystem on %s"), root);if (!fs->blocklist_install)grub_util_error(_("filesystem `%s' doesn't support blocklists"),fs->name);
}// 输出调试与错误信息,略grub_util_biosdisk_flush(root_dev->disk);// 清除blocklists
/* Clean out the blocklists.  */
bl.block = bl.first_block;
while (bl.block->len) {bl.block->start = 0;bl.block->len = 0;bl.block->segment = 0;bl.block--;if ((char *)bl.block <= core_img)grub_util_error("%s", _("no terminator in the core image"));
}bl.block = bl.first_block;// 生成blocklists
grub_install_get_blocklist(root_dev, core_path, core_img, core_size,save_blocklists, &bl);if (bl.first_sector == (grub_disk_addr_t)-1)grub_util_error("%s", _("can't retrieve blocklists"));write_rootdev(root_dev, boot_img, bl.first_sector);/* Write the first two sectors of the core image onto the disk.  */
grub_util_info("opening the core image `%s'", core_path);
fp = grub_util_fd_open(core_path, GRUB_UTIL_FD_O_WRONLY);// 输出错误信息,略// 刷新缓存,应用之前的更改
grub_util_biosdisk_flush(root_dev->disk);// 禁用缓存
grub_disk_cache_invalidate_all();// 检查blocklists是否有效
{char *buf, *ptr = core_img;size_t len = core_size;grub_uint64_t blk, offset = 0;grub_partition_t container = core_dev->disk->partition;grub_err_t err;core_dev->disk->partition = 0;buf = xmalloc(core_size);blk = bl.first_sector;err = grub_disk_read(core_dev->disk, blk + offset, 0, GRUB_DISK_SECTOR_SIZE,buf);if (err)grub_util_error(_("cannot read `%s': %s"), core_dev->disk->name,grub_errmsg);if (grub_memcmp(buf, ptr, GRUB_DISK_SECTOR_SIZE) != 0)grub_util_error("%s", _("blocklists are invalid"));ptr += GRUB_DISK_SECTOR_SIZE;len -= GRUB_DISK_SECTOR_SIZE;bl.block = bl.first_block;while (bl.block->len) {size_t cur = grub_target_to_host16(bl.block->len)<< GRUB_DISK_SECTOR_BITS;blk = grub_target_to_host64(bl.block->start);if (cur > len)cur = len;err = grub_disk_read(core_dev->disk, blk + offset, 0, cur, buf);if (err)grub_util_error(_("cannot read `%s': %s"), core_dev->disk->name,grub_errmsg);if (grub_memcmp(buf, ptr, cur) != 0)grub_util_error("%s", _("blocklists are invalid"));ptr += cur;len -= cur;bl.block--;if ((char *)bl.block <= core_img)grub_util_error("%s", _("no terminator in the core image"));}if (len)grub_util_error("%s", _("blocklists are incomplete"));core_dev->disk->partition = container;free(buf);
}

对这两种情况处理完了之后,就是收尾工作了

finish:
// 写入boot.img
if (grub_disk_write(dest_dev->disk, BOOT_SECTOR, 0, GRUB_DISK_SECTOR_SIZE,boot_img))grub_util_error("%s", grub_errmsg);// 刷新缓存
grub_util_biosdisk_flush(root_dev->disk);
grub_util_biosdisk_flush(dest_dev->disk);// 释放资源
free(core_path);
free(core_img);
free(boot_img);
grub_device_close(dest_dev);
grub_device_close(root_dev);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/878355.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

こんばんは

碎碎念——2024.11.18こんばんは~ 没有饭碗可真是麻烦,但遇到命运中的饭碗可真不容易呢?我在这个秋天决定焕然一新,开始替换餐具,但我坚信必须要有那种让我“一见钟情”的碗。在日用品店逛了很久,却始终找不到合适的饭碗。 我曾买过一只很漂亮的抹茶碗,但它太适合茶道了,…

2025多校冲刺省选模拟赛8

2025多校冲刺省选模拟赛8\(T1\) A. 波斯菊 \(10pts/10pts\)每个连通块内以关键点为起点的最长路径 \(d_{i}\) 相互独立,有 \(2n-2k-\sum\limits_{i=1}^{k}d_{k}\) 即为所求。观察到可以直接钦定每个关键点距离其最远的点的是哪个点,这样的话就只需要任意两个路径不相交就能保…

为什么选择持续绩效管理(CPM)

组织变革和发展的关键始于你的员工基础。要想在组织中建立一个不断发展和创新的环境,你必须确保你的员工是以目标、绩效和反馈为导向的。 #持续绩效管理#鼓励上级和下级之间建立直接的双向沟通。一个持续的绩效评估可以确保平稳有效的联系,随着时间的推移,可以看到更好的结果…

unity直接安装插件

一共三步 1.窗口->包管理器2. 在弹出的界面里点左上角的加号 依次解读选项为disk 文件夹(zip压缩文件请提前解压)tarball 压缩包(只支持tar压缩包)git URL 网址下载(国内应该得设置代理)by name 指定软件和版本下载 3.以disk为例,进入解压的地方选中package.json接着…

北航计网课程笔记-四、网络层

第四章 网络层 本章重点 1、IP数据报的格式和分片 2、IP地址的表示和分类分配、子网掩码、地址有效性、广播地址 3、子网划分 4、CIDR路由聚合 5、ARP协议和ICMP协议 6、路由表、路由器的工作原理 7、RIP协议和OSPF协议 8、网络层的综合设计和应用 网络层的功能 网络层的主要任…

北航计网课程笔记-六、应用层

第六章 应用层 应用层概述 应用层对应用程序的通信提供服务。应用层的功能:文件传输、访问和管理 电子邮件 虚拟终端 查询服务和远程作业登录应用层的重要协议FTP SMTP、POP3 HTTP DNS网络应用模型 客户/服务器模型C/S客户是服务请求方,服务器是服务提供方 Web、FTP、远程登录…

北航计网课程笔记-七、一些总结

复习到最后想开摆了,也没总结啥,只是一些格式……一些总结MAC帧格式IP数据报格式 IP首部20B~60B IP总长最多65535BTCP报文格式 TCP首部20B~60B 数据部分最多65535-20-20 = 65495BUDP报文格式UDP&TCP伪首部格式HTTP报文格式

北航计网课程笔记-一、概述

写在前面:本笔记根据王道408完成,北航软院不考的内容不在其中。 总之复习期末把王道刷一遍就行了。 个人学习用,如有错误敬请指正。第一章 概述 计算机网络概念 计算机网络的定义:一些互联的、自治的计算机的集合,主体是多台计算机,媒介为通信媒介, 目的是通信与资源共享…

北航计网课程笔记-二、物理层

第二章 物理层 物理层的任务:将原始的比特流从一台机器上传输到另一台机器上。 确定与传输媒体的接口特性:机械、电气、功能、规程通信基础 一些基本概念数据,信号和码元数据是传送信息的实体;信号是数据的电气或电磁表现;码元是数字通信中数字信号的计量单位,1码元可以携…

Winform和MFC的一个区别

前段时间我嫌桌面快捷键太多,我使用C语言用MFC制作了桌面自定义系统(上图左),可是这个系统存在一些问题,比如(下图) 默认情况下它的右上角没有最大化和最小化的按钮,而Winform是有的(下图)。 还有一个问题:在任务栏,点击时MFC不能最小化,而Winform可以;还有一个很…

应用中的 PostgreSQL项目案例

title: 应用中的 PostgreSQL项目案例 date: 2025/2/3 updated: 2025/2/3 author: cmdragon excerpt: 随着大数据和云计算的兴起,企业在数据管理和数据分析方面面临着越来越复杂的挑战。PostgreSQL 作为一个开源关系型数据库,凭借其卓越的扩展性和强大的功能,逐渐成为众多企业…

来自aakennes的新年祝福

来自aakennes的新年祝福组题人: @aakennes \(A\) P888. 字符串会上树 \(AC\)基础字符串。点击查看代码 string s,t="",w=""; map<string,string>f; int main() { // #define Isaac #ifdef Isaacfreopen("in.in","r",stdin);fr…