啊
回忆一下写CPU的过程,CPU里的时序电路基本都有一个 reset 的过程
正是这个 reset 的过程,建立了程序员和硬件之间的桥梁
啊
啊
回忆一下我们买的电脑:
电脑的品牌是 惠普HP, CPU是Intel的,主板是华硕的
当开机的时候,屏幕上会显示 惠普HP 的商标
所以,firmware 应该不是由Intel来开发的,而是由惠普来开发的
Intel 会约定好 CPU Reset 之后的状态(寄存器值,比如 PC) (注意:内存控制器严格来说不是CPU的一部分)
所以,CPU在启动的时候,做的第一件事情就是取出 PC 指向的内存位置,取出一条指令译码执行
所以,主板厂商会和CPU公司约定,只要主板厂商在 PC reset 的地址那里放上代码,那么主板厂商的代码就能够执行 (第一个约定)
再回忆一下,有时候在更新 windows 操作系统时,会看到 “更新固件” 的字样:
实际上,是主板厂商会在主板上放上一个小的存储器,这个存储器可读可写,这个存储器有代码和数据,惠普HP的 logo 就是这个存储器的数据的一部分(这个存储器里的代码和数据应该就叫做firmware了)
(个人猜测:这个小的存储器和DRAM不是一个东西,可以直接通过内存地址访问吗?)
随后,还有第二个约定,那就是主板厂商,或者叫写 firmware 的人和操作系统开发者的约定:
firmware 会读取 存储介质(如硬盘)上的某块数据,把它加载到内存上的某块地方,并且跳转到它的某个地址开始执行。比如,加载存储介质上的第二级loader,或者直接加载操作系统。
啊
接下来看看 x86 处理器的 reset 行为
可以看到 Intel 手册中规定了 上电 和 reset 时的寄存器的值
Firmware 负责加载OS (这就是 bootloader?)
QEMU -kernel 则会跳过 Firmware 直接加载内核
uboot 就是一个开源的加载器(firmware/bootloader)
BIOS 和 UEFI 都是固件,它们除了加载其它程序外,还有一个功能:配置、初始化各个硬件
补充:超线程(hyperthreading),一个物理core可以用来当两个 logic core 使用,在操作系统看来这是两个一样的core
硬件上的某些功能,比如刚刚提到的超线程,被Intel设计成可开关,而开关这些功能并不是由操作系统完成的,而是在加载操作系统之前,由固件配置
还有:配置主板上某些端口的开关、是否使用独立显卡、CDROM启动、U盘启动、软盘启动等等
BIOS 和 UEFI 的不同可以看这里 https://www.freecodecamp.org/news/uefi-vs-bios/
BIOS其实是比较老旧的固件,今天的硬件复杂得多,BIOS已经不再使用,我们使用新的固件:UEFI
CPU上电后,PC跳转到 0x0fff, 开始执行 firmware 代码, firmware 代码会从磁盘上读取 MBR,放到内存地址 0x7c00,接着再跳转到 0x7c00 (到这一步,计算机的控制权就到了操作系统开发者手里了!)
MBR(master boot record) 的 512字节中,其实只有446字节可以使用,其中64字节用来存储分区表(partition table)。然后还有两个字节为 0x55 0xAA,用来标识这是一个可引导的设备(在今天看来可能没有什么意义)
如果没有 JTAG 帮助扫描硬件上的信号,想要进行 OS, 固件 的调试几乎是不可能的
这是 16 位的 x86 汇编代码(MBR 代码),建议自己上网找一下完成的代码,分析一下
qemu-system-x86_64 在刚启动时可以看到 $rip = 0xfff0,其实这里隐藏了点东西
由于 x86 刚启动处于 16位模式,此时的IP实际上由 $cs * 16 + $rip 共同构成
此时的汇编指令都是16位指令,直接使用 64位 的反汇编器会出错,应该把这些代码丢给 16位的反汇编器
(注意:原本gdb可以使用 x/10i 来打印10个指令)
一个.gdbinit 的很好的写法
也可以写一个 init.gdb 文件,然后使用 gdb -x init.gdb 去初始化gdb
使用 file a.out 可以把源代码加载进来(没错!汇编代码也可以被加载哦!),就不用再看反汇编器给的代码了
还支持对行号打断点
再修改一下 .gdbinit 文件,我们可以给 内存地址 0x7c00 加上一个 watchpoint,这样就能知道到底是哪块代码加载了 MBR 到 0x7c00 上
在QEMU中,加载 MBR 的 BIOS 是 CBIOS,这是一个开源的firmware,我们可以把它找来,让 gdb 加载,进行代码调试(gdb对16位汇编的支持比较糟糕,一般还是用源码进行调试)
用上述命令可以识别 ${AM_HOME} 环境变量,把它们全部替换成更短的 $AM_HOME,增强可读性
在 compile_commands.json 里插入编译选项,就可以消除 vscode 上的红线