老PC从U盘启动裸机内核,到底行不行?

介绍 在上一篇文章里,我们从引导扇区开始写了一个会打招呼的裸机“内核”,在QEMU里跑得很开心,最后把“拿真机开刀”的几个坑留了下来。这一篇就来填坑,回答一个具体的问题:老PC到底能不能从U盘启动我们自己写的裸机内核? 先把答案放这:能。大约2003年以后、BIOS里有USB启动选项的机器基本都可以。但上一篇那个镜像直接dd进U盘,在不少真机上会翻车——不是BIOS压根不认这个U盘,就是读盘读出一堆垃圾。这篇文章给引导程序做两个小升级,把这两个坑都填平。 U盘启动的原理:BIOS的“模拟大法” 先想一个问题:BIOS又不懂文件系统,也没有什么“USB启动协议”,它是怎么从U盘启动的?答案是模拟(emulation):BIOS用自己内置的USB驱动把U盘伪装成一块普通硬盘(USB-HDD模式)或软盘(USB-FDD模式),然后照常提供INT 13h读盘服务、照常执行“搬运第一个扇区到0x7C00”的老流程。对我们的引导程序来说,U盘“看起来”就是一块硬盘。 听上去很美,但两个坑正是藏在这套模拟里: 步骤 升级一:改用INT 13h扩展读(LBA) 治CHS的办法很直接:不用CHS。1990年代中期之后的BIOS都提供“INT 13h扩展”,可以用LBA(逻辑块地址)直接说“从第几个扇区开始读几个”,完全绕开几何参数。顺便理清计数方式:内核在磁盘的“第2个扇区”,用CHS的说法是柱面0、磁头0、扇区2(CHS的扇区从1数),换成LBA就是LBA 1(LBA从0数,LBA 0是引导扇区自己)。 用法分两步:先用功能41h探测扩展是否可用,可用的话就用功能42h读。42h的参数不再塞寄存器,而是填一个16字节的“参数包”(Disk Address Packet): 注意我们保留了上一篇的CHS读法作为兜底(use_chs分支):万一碰上真·上古BIOS不支持扩展(多见于软盘模拟模式),就退回老办法,两头都照顾到。 升级二:给引导扇区塞一张分区表 MBR的512字节其实是有规定格局的:前446字节归引导代码,接下来64字节是4个分区表项(每个16字节),最后2字节是魔数0xAA55。我们的引导代码离446字节还远,正好把分区表补上,专治坑二: 这个分区基本上是“演给BIOS看”的:它从LBA 2048开始,而我们的内核放在LBA 1到16——藏在分区开始之前的空隙里(现代分区工具管这段叫MBR gap,GRUB也把自己的一部分塞在这里,我们算是蹭了个正规位置)。分区里并没有真的FAT文件系统,BIOS也不会去检查;讲究的话,你可以之后真的把它格式化成FAT放点文件,两不耽误。 完整引导程序…