BIOS

想快速看懂“上电到操作系统启动”这段黑屏时间到底发生了什么?

📚 基本概念速读

名称 定义 省流
BIOS Basic Input/Output System,主板固化固件,负责最初的硬件初始化与引导。 启动第一棒
UEFI 现代固件接口,取代传统 16bit BIOS;但业内仍习惯统称"BIOS"。 BIOS 2.0
POST Power-On Self-Test,BIOS 执行的自检流程,点亮蜂鸣器/Debug LED。 自体检
Boot Device BIOS 搜索可启动介质(SSD/U 盘/网卡)的目标。 “从哪儿启动”
CMOS Complementary Metal-Oxide-Semiconductor,存储 BIOS 设置的小容量存储器,断电时靠纽扣电池供电。 设置仓库

UEFI vs 传统 BIOS 简要对比:

特性 传统 BIOS UEFI
启动方式 从固定地址 0xFFFFFFF0 启动,16位实模式 从 EFI 系统分区(ESP)启动,支持 32/64 位
分区表 主要使用 MBR(主引导记录) 主要使用 GPT(GUID 分区表)
磁盘容量限制 MBR 限制 2TB GPT 支持 9.4 ZB(zettabytes)
启动速度 较慢(需要 POST 自检) 较快(并行初始化,跳过部分自检)
图形界面 文本界面 支持图形界面和鼠标操作
安全启动 不支持 支持 Secure Boot(安全启动)
网络启动 需要 Option ROM 原生支持 PXE 网络启动
编程语言 汇编语言为主 主要使用 C 语言
兼容性 仅支持 x86/x64 支持 x86、x64、ARM 等多种架构

注意:本文主要介绍传统 BIOS的工作原理,这是理解现代 UEFI 的基础。UEFI 在底层仍然兼容传统 BIOS 的某些机制(如 Legacy Boot 模式)。

⚡ 按下电源键的逐帧解说

  1. 硬件上电阶段(Power-On)
    • 主板供电,所有核心电压被拉起。
    • CPU 复位后被强制从物理地址 0xFFFFFFF0 取第一条指令(实模式下无虚拟地址转换)。
    • 该地址由芯片组映射到 BIOS ROM,因此固件立即接管执行。
  2. POST 自检阶段(Power-On Self Test)
    • 检测 CPU 是否正常。
    • 检测内存大小和完整性。
    • 检测显卡、键盘等基本设备。
    • 若一切正常,蜂鸣器发出“嘀”的提示音。
  3. BIOS 初始化阶段
    • 初始化中断向量表(IVT)。
    • 初始化显卡、磁盘控制器等硬件。
    • 提供 BIOS 中断服务(INT 0x10、0x13 等)。
    • 读取 CMOS,获取启动设备顺序。
  4. 寻找引导设备
    • 按顺序尝试设备(硬盘 → 光驱 → USB 等)。
    • 读取每个设备的第一个扇区(512 字节)。
    • 检查扇区末尾是否为 0x55AA 引导标志,确认后继续加载。
  5. 加载 MBR 到内存
    • 将引导扇区加载到物理地址 0x7C00
    • 设置 CS:IP = 0x0000:0x7C00
    • 跳转执行(CPU 控制权交给引导程序)。
  6. 引导程序执行(Stage 1 / Stage 2)
    • Stage 1:运行 MBR 引导扇区(512 字节)。
    • Stage 2:加载扩展引导加载器(负责加载内核等)。
    • 切换到保护模式(16 位 → 32 位)。
  7. 内核接管系统
    • 完成内核初始化。
    • 设置中断描述符表(IDT)。
    • 初始化内存管理。
    • 启动第一个进程。

🧠 BIOS 在哪里?

  • 固化在主板上的 EEPROM / SPI Flash 芯片里。
  • 地址通常映射到物理内存高端(如 0xF0000-0xFFFFF 区域)。
  • 不依赖操作系统,通电后即可独立工作。

物理地址空间分布(32位系统):

地址范围 映射内容 说明
0x00000000 - 0x0009FFFF 常规 RAM 640KB 传统内存(0x00000-0x003FF 为 IVT,占用前 1KB
0x000A0000 - 0x000BFFFF 视频 RAM (VRAM) 显卡显存映射
0x000C0000 - 0x000DFFFF 扩展 ROM 网卡/RAID 卡 Option ROM
0x000E0000 - 0x000EFFFF 保留区域 未使用
0x000F0000 - 0x000FFFFF BIOS ROM (低端) 传统 BIOS 代码区
0x00100000 - 0x3FFFFFFF 扩展 RAM 1MB 以上常规内存
0xFFF00000 - 0xFFFFFFFF BIOS ROM (高端) 0xFFFFFFF0 位于此区域

64位系统的地址映射:

  • 64位系统(x86-64)在启动时仍然使用兼容模式,地址映射与32位系统基本相同。
  • CPU 上电后从 0xFFFFFFF0 启动(仍然是32位地址空间的高端)。
  • 在进入64位长模式(Long Mode)之前,系统仍然使用32位地址空间布局。
  • 主要区别
    • 64位模式下,物理地址空间可以扩展到 2⁴⁸ 或 2⁵² 字节(取决于 CPU 支持)。
    • 但 BIOS ROM 的映射位置(0xF0000-0xFFFFF0xFFF00000-0xFFFFFFFF)保持不变,以确保向后兼容。
    • 现代 UEFI 系统可能使用不同的映射方式,但传统 BIOS 模式仍然遵循上述布局。

为什么 BIOS 分低端和高端?

这是 x86 架构的历史兼容性与现代启动机制共同作用的结果:

  1. 历史原因(低端映射 0xF0000-0xFFFFF
    • 在早期的 IBM PC(1981年)中,BIOS ROM 被映射到 0xF0000-0xFFFFF(位于 1MB 地址空间内)。
    • 实模式下,BIOS 中断服务(如 INT 0x10、INT 0x13)的代码位于该区域。
    • 为了保持向后兼容,现代系统仍保留该映射。
  2. 现代启动机制(高端映射 0xFFF00000-0xFFFFFFFF
    • 在 32 位系统中,CPU 上电后从 0xFFFFFFF0 取第一条指令。
    • 该地址位于 32 位地址空间的高端(接近 4GB 边界)。
    • 芯片组将 0xFFFFFFF0 映射到同一块 BIOS ROM 芯片,确保上电后能立即执行 BIOS 代码。
  3. 为什么需要两个映射?
    • 低端映射:用于兼容实模式下的 BIOS 中断调用和旧代码。
    • 高端映射:满足 CPU 上电后从 0xFFFFFFF0 启动的要求。
  4. 实际物理位置
    • 两个地址映射到同一块主板上的 BIOS ROM 芯片(SPI Flash/EEPROM)。
    • 芯片组通过内存映射 I/O(MMIO)将这两个地址都指向同一物理芯片,实现"一个 ROM,两个地址窗口"。

0xFFFFFFF0 访问的是什么?

  • 这是内存映射 I/O(MMIO)地址,由芯片组(北桥/南桥)将 CPU 的物理地址请求映射到主板上的 BIOS ROM 芯片
  • CPU 访问 0xFFFFFFF0 时,芯片组会拦截这个地址,转而从 SPI Flash/EEPROM 芯片中读取数据。
  • 这种映射在硬件层面完成,无需软件参与,确保 CPU 上电后能立即执行 BIOS 代码。

ROM 是内存吗?与硬盘存储有什么区别?

特性 ROM(只读存储器) RAM(随机存取存储器) 硬盘存储(HDD/SSD)
是否属于内存 ✅ 是(非易失性内存) ✅ 是(易失性内存) ❌ 不是(外存/存储设备)
断电后数据 保留 丢失 保留
访问速度 较慢(纳秒级) 很快(纳秒级) 很慢(微秒/毫秒级)
可写性 只读(或需特殊方式写入) 可读写 可读写
容量 小(几MB到几十MB) 中等(几GB到几十GB) 大(几百GB到几TB)
用途 BIOS、固件、引导代码 运行程序、临时数据 操作系统、文件、数据
CPU 直接访问 ✅ 可以(通过内存映射) ✅ 可以 ❌ 不可以(需通过驱动)
物理位置 主板芯片 内存条插槽 SATA/NVMe 接口

🧱 BIOS 的四大职责

🔍 POST

加电自检(POST):验证 CPU、内存、显卡、键盘等核心器件是否健康。

步骤 要做什么 输出结果
检测 CPU 验证寄存器、跑基本指令序列 CPU OK / 错误蜂鸣码
检测内存 统计容量、快速读写测试、记录可用区域 内存容量 + 可用信息
检测显卡 初始化显示适配器、点亮 BIOS 画面 有画面或报显卡故障
检测其他设备 键盘控制器、磁盘控制器、其他 PCI 设备 可用外设列表

🛠️ 硬件初始化

硬件初始化:配置芯片组、显卡、磁盘控制器,为系统做好底层环境。

模块 关键动作 输出结果
芯片组/总线 设置北桥/南桥、PCIe Root Complex、Vcore/Vtt、电源状态 控制器上线,CPU/内存/外设路径打通
存储控制器 初始化 SATA/NVMe/AHCI、分配 I/O Port/MMIO 磁盘/NVMe 可被扫描、读写
显示/外设 启动 GOP/VESA、USB 控制器、风扇曲线、安全策略 BIOS 画面正常显示,输入设备可用
内存映射 构建 E820、ACPI、SMBIOS 等表的基址与长度 OS 能正确识别硬件拓扑与资源

建立中断向量表

BIOS 在内存最低地址 (0x00000 - 0x003FF) 建立中断向量表(RAM中,因此可修改):

实模式中断向量表 (IVT) 结构:

地址范围 中断向量 功能
0x0000-0x0003 INT 0x00 (除零) 除法错误
0x0004-0x0007 INT 0x01 (单步) 调试单步
0x0008-0x000B INT 0x02 (NMI) 不可屏蔽中断
... ... ...
0x0040-0x0043 INT 0x10 视频服务 ⭐
0x004C-0x004F INT 0x13 磁盘服务 ⭐
0x0058-0x005B INT 0x16 键盘服务 ⭐
... ... ...

每个中断向量占 4 字节:

1
[2字节偏移地址] [2字节段地址]

为什么偏移地址在前?存储格式详解:

这是 x86 架构的小端序(Little-Endian) 存储格式。以 INT 0x10 为例(IVT 地址 0x0040):

地址 内容 说明
0x0040 IP_low 偏移地址低字节(如 0x00
0x0041 IP_high 偏移地址高字节(如 0x50
0x0042 CS_low 段地址低字节(如 0xC0
0x0043 CS_high 段地址高字节(如 0xF0

小端序规则:多字节数据中,低字节存储在低地址,高字节存储在高地址

  • 偏移地址 IP = 0x5000 → 存储为 [0x00, 0x50](低字节在前)
  • 段地址 CS = 0xF000 → 存储为 [0x00, 0xF0](低字节在前)

为什么 IP 在 CS 之前?

  • 这是 x86 实模式的约定:先存储偏移地址(IP),再存储段地址(CS)
  • CPU 读取时按顺序:先读 IP(2字节),再读 CS(2字节),然后执行 jmp CS:IP
  • 这种顺序与实模式下的段:偏移寻址方式一致:物理地址 = CS × 16 + IP

🚀 引导加载

引导加载流程:

  1. 读取 CMOS 获取启动设备顺序
  2. 按顺序尝试每个设备:
    • 软盘驱动器(很少用了)
    • 硬盘(HDD/SSD)
    • 光驱(CD/DVD)
    • USB 设备
    • 网络启动(PXE)
  3. 读取设备的第 0 扇区(512字节)
  4. 检查最后两字节是否为 0x55AA
    • 是 → 这是有效的引导扇区
    • 否 → 尝试下一个设备

    为什么是 0x55AA?

    • 引导扇区签名0x55AA 是 MBR(Master Boot Record)和引导扇区的魔数(Magic Number),位于扇区的最后两个字节(偏移 510-511)。
    • 字节顺序:由于 x86 是小端序(Little-Endian),0x55AA 在内存中存储为 [0xAA, 0x55](低字节在前)。
    • 为什么选择这个值?
      • 0x55 = 01010101(二进制),0xAA = 10101010(二进制)
      • 这两个字节的奇偶校验位都是奇数(1 的个数为奇数),可以用于简单的错误检测。
      • 这个组合在正常数据中出现的概率很低,适合作为"签名"。
    • 位置:放在扇区末尾(510-511 字节),确保整个扇区被正确读取后才能验证。
    • 历史:这是 IBM PC 兼容机规定的标准,所有引导扇区都必须以 0x55AA 结尾,否则 BIOS 不会将其识别为可引导设备。
  5. 将引导扇区加载到内存 0x0000:0x7c00

    0x0000:0x7c00 是什么意思?
    • 这是实模式下的段:偏移地址表示法,格式为 段地址:偏移地址
    • 物理地址计算公式物理地址 = 段地址 × 16 + 偏移地址
    • 0x0000:0x7c00 = 0x0000 × 16 + 0x7c00 = 0x00000 + 0x7c00 = 0x7c00(物理地址)
    • 最终指向物理地址 0x7c00
    • 实模式下用段:偏移表示,是因为 16 位寄存器只能表示 64KB,通过段地址可以访问更大的地址空间。
  6. 设置寄存器:
    • CS = 0x0000(代码段寄存器)
    • IP = 0x7c00(指令指针寄存器)
    • DL = 启动设备号 (0x00=软盘, 0x80=硬盘)
  7. 跳转执行: JMP 0x0000:0x7c00

为什么是 0x7C00?

这是 IBM PC 5150 (1981年) 规定的地址,选择这个值有以下几个原因:

  1. 内存布局考虑
    • 引导扇区大小为 512 字节(0x200 字节)。
    • 0x7C000x7DFF(512字节)用于存放引导代码。
    • 0x7E00 开始可以用于引导程序的栈空间和数据区。
  2. 与 BIOS 数据区的距离
    • BIOS 数据区(BDA)位于 0x00400-0x004FF(256字节)。
    • IVT 位于 0x00000-0x003FF(1KB)。
    • 选择 0x7C00 确保引导代码与这些关键区域有足够距离,避免冲突。
  3. 栈空间预留
    • 实模式下,栈通常向下增长(从高地址向低地址)。
    • 0x7C00 以下有足够的空间(约 32KB)可以作为栈使用。
    • 例如:设置 SS:SP = 0x0000:0x7C00,栈可以向下扩展到 0x0000
  4. 历史约定
    • IBM PC 5150 的 BIOS 设计者选择了这个值,后续所有兼容机都遵循这个约定。
    • 即使现代系统已经不需要这些限制,为了兼容性仍然使用 0x7C00
  5. 计算验证
    • 0x7C00 = 31744 字节 = 31KB
    • 0x000000x7C00 有 31KB 空间,足够存放 IVT、BDA、视频缓冲区等。
    • 引导扇区加载后,还有约 32KB 空间可用于栈和临时数据。

MBR vs GPT 分区表:

传统 BIOS 主要使用 MBR(Master Boot Record) 分区表,而现代 UEFI 系统使用 GPT(GUID Partition Table)

特性 MBR GPT
全称 Master Boot Record(主引导记录) GUID Partition Table(GUID 分区表)
位置 磁盘第一个扇区(512字节) 磁盘开头多个扇区,有主备份
分区数量 最多 4 个主分区(或 3 个主分区 + 1 个扩展分区) 理论上无限制(通常支持 128 个分区)
磁盘容量限制 最大 2TB(由于使用 32 位 LBA) 最大 9.4 ZB(zettabytes,使用 64 位 LBA)
分区表备份 无备份,损坏后难以恢复 有主备份和次备份,更安全
兼容性 所有操作系统都支持 需要 UEFI 或 GPT 兼容的 BIOS
引导代码 MBR 包含引导代码(446字节) GPT 不包含引导代码,由 EFI 系统分区(ESP)处理
CRC 校验 有,可以检测分区表损坏
使用场景 传统 BIOS + 小容量磁盘 UEFI + 大容量磁盘(>2TB)

MBR 结构(512字节):

  • 0x000-0x1BD(446字节):引导代码
  • 0x1BE-0x1FD(64字节):4 个分区表项(每个 16 字节)
  • 0x1FE-0x1FF(2字节):引导签名 0x55AA

GPT 结构:

  • 保护性 MBR:第一个扇区,用于兼容不支持 GPT 的系统
  • GPT 头:包含分区表信息
  • 分区表项:每个分区 128 字节,包含 GUID、名称、起始/结束 LBA 等
  • 备份分区表:磁盘末尾有完整备份

🔌 BIOS 中断服务详解

中断是什么?

中断(Interrupt)是 CPU 暂停当前执行流程,转而去处理特定事件或请求的机制。中断分为三类:

  • 硬件中断:由硬件设备触发(如键盘按下、时钟滴答、网卡收到数据包),通过中断控制器(如 8259 PIC)通知 CPU。
  • 软件中断:由程序主动触发(如 int 0x10 调用 BIOS 视频服务),用于调用系统服务。
  • 异常中断:由 CPU 内部事件触发(如除零错误、页错误、非法指令),用于错误处理。

什么是“中断向量”?

  • 中断向量(Interrupt Vector) 是一个“入口索引”,用于指示某个中断的处理函数地址。
  • 在 BIOS 实模式中,中断向量由 IVT(Interrupt Vector Table) 管理:表项编号 0x00~0xFF,对应中断号。
  • IVT 中存储的是中断处理函数的地址(实模式的段:偏移)。因此可以理解为:“中断向量”=“中断号对应的处理函数地址”。
  • CPU 收到中断后,会用中断号(向量)作为索引,在 IVT 中查到处理函数的段地址和偏移地址,然后跳转执行。

中断的地址是物理地址还是虚拟地址?

实模式下(BIOS 运行的环境):

  • 中断向量表中的地址是物理地址(更准确说是实模式地址)。
  • IVT 位于物理地址 0x00000-0x003FF,每个向量存储的是处理程序的段地址:偏移地址(实模式寻址方式)。
  • CPU 通过 段地址 × 16 + 偏移地址 计算出物理地址,直接访问内存,无需虚拟地址转换。

保护模式下(操作系统运行的环境):

  • 使用 IDT(Interrupt Descriptor Table) 替代 IVT。
  • IDT 中的地址可以是虚拟地址(如果启用了分页),CPU 会通过页表转换为物理地址。

BIOS 提供了一系列中断服务,让引导程序可以使用硬件:

💡 深入理解:中断向量表 (IVT)

在实模式下,BIOS 中断是通过中断向量表(Interrupt Vector Table, IVT)来实现的。

IVT 的结构:

  • 地址范围0x00000 - 0x003FF(1024 字节 = 1KB)
  • 每个中断向量:占 4 字节(2 字节偏移地址 + 2 字节段地址)
  • 总向量数:256 个中断向量(0x000xFF

内存布局:

地址 大小 内容
0x0000 2 字节 INT 0x00 偏移地址
0x0002 2 字节 INT 0x00 段地址
0x0004 2 字节 INT 0x01 偏移地址
0x0006 2 字节 INT 0x01 段地址
... ... ...
0x0040 2 字节 INT 0x10 偏移地址 ⭐
0x0042 2 字节 INT 0x10 段地址 ⭐
... ... ...
0x03FC 2 字节 INT 0xFF 偏移地址
0x03FE 2 字节 INT 0xFF 段地址

🔄 中断调用过程

当执行 int 0x10 时,CPU 自动执行以下步骤:

  1. 将 FLAGS 寄存器压栈(保存状态)

    1
    push FLAGS

  2. CPU 自动清除 IF 标志(禁止中断嵌套)
    • CPU 自动将 IF 标志设为 0,屏蔽可屏蔽硬件中断。
    • 这相当于自动执行了 cli(Clear Interrupt Flag)指令的效果。
    • 注意:这是 CPU 的自动行为,不需要在代码中手动执行 cli
  3. CPU 自动清除 TF 标志(禁止单步调试)
    • CPU 自动将 TF(Trap Flag)标志设为 0,禁用单步调试模式。
    • 这也是 CPU 的自动行为,确保中断处理程序不会在调试模式下执行。
  4. 将返回地址压栈(CS 和 IP)

    1
    2
    push CS
    push IP

  5. 从 IVT 读取中断处理程序地址
    • 计算:中断向量地址 = 0x10 × 4 = 0x40
    • 读取 IP:读取 [0x40] → IP(偏移)
    • 读取 CS:读取 [0x42] → CS(段地址)
  6. 跳转到中断处理程序

    1
    jmp CS:IP

  7. 中断处理程序执行完毕后,执行 iret 返回

    1
    iret: pop IP, pop CS, pop FLAGS

中断处理程序的栈管理:

栈的使用:

  • 当 CPU 执行 int 指令时,会自动将 FLAGS、CS、IP 依次压栈(按此顺序)。
  • 栈指针(SP)会自动减少(栈向下增长),为这些数据预留空间。
  • 中断处理程序可以使用栈来保存其他寄存器(如 AX、BX 等),但必须在使用前保存,使用后恢复

栈布局(进入中断处理程序时):

1
2
3
4
5
高地址
[原始 FLAGS] ← SP(栈顶,最低地址)
[原始 CS]
[原始 IP] ← 栈底(最高地址)
低地址

必须用 iret 返回的原因:

  • iret 指令会按相反顺序弹出 IP、CS、FLAGS,恢复 CPU 状态。
  • 如果用普通的 ret 返回,只会弹出 IP 和 CS,不会恢复 FLAGS,导致中断标志(IF)等状态错误。
  • iret 还会自动恢复 IF 标志(如果之前被清除),重新允许硬件中断。

寄存器保存约定:

  • 必须保存:如果中断处理程序会修改的寄存器(如 AX、BX、CX、DX、SI、DI、BP、DS、ES 等)。
  • 不需要保存:FLAGS、CS、IP(CPU 自动保存)。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    my_handler:
    push ax ; 保存寄存器
    push bx
    ; ... 处理逻辑 ...
    pop bx ; 恢复寄存器(注意顺序相反)
    pop ax
    iret ; 必须用 iret 返回

图示说明:

调用:int 0x10

  1. 保存现场(栈)

    1
    2
    3
    [FLAGS]  ← SP (栈顶)
    [CS]
    [IP]

  2. 计算中断向量地址

    1
    0x10 × 4 = 0x40

  3. 读取 IVT

    1
    2
    3
    4
    0x40: IP_low  (偏移地址低字节)
    0x41: IP_high (偏移地址高字节)
    0x42: CS_low (段地址低字节)
    0x43: CS_high (段地址高字节)

  4. 跳转到 BIOS 代码

    1
    jmp [读取的CS]:[读取的IP]

  5. BIOS 处理并返回

    1
    iret (恢复现场)

📋 常见中断向量

中断号 IVT地址 用途 类型
0x00 0x0000 除零错误 CPU异常
0x01 0x0004 单步调试 CPU调试
0x03 0x000C 断点 CPU调试
0x08 0x0020 时钟中断 硬件IRQ0
0x09 0x0024 键盘中断 硬件IRQ1
0x0E 0x0038 软盘中断 硬件IRQ6
0x10 0x0040 视频服务 BIOS服务 ⭐
0x13 0x004C 磁盘服务 BIOS服务 ⭐
0x15 0x0054 系统服务 BIOS服务 ⭐
0x16 0x0058 键盘服务 BIOS服务 ⭐
0x1A 0x0068 时钟服务 BIOS服务
0x21 0x0084 DOS服务 操作系统(后期)

💻 自定义中断向量示例

安装自定义的 INT 0x80 处理程序(类似 Linux 系统调用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
my_int80_handler:
; 处理逻辑
cmp ah, 0x01
je print_string ; 功能1:打印字符串
; ...
iret ; 必须用iret返回!

install_handler:
cli ; 关闭中断
; 计算IVT地址: 0x80 × 4 = 0x200
xor ax, ax
mov es, ax ; ES = 0
mov bx, 0x0200 ; 偏移 = 0x200

; 写入偏移地址
mov word [es:bx], my_int80_handler
; 写入段地址
mov word [es:bx+2], cs

sti ; 恢复中断
ret

⚠️ 重要注意事项

  1. 保护模式下 IVT 不可用
    • 进入保护模式后,IVT 被 IDT(Interrupt Descriptor Table)取代
    • BIOS 中断只能在实模式下使用。
    • 这就是为什么必须在切换到保护模式前完成所有硬件检测和数据读取。
  2. 中断优先级
    • CPU 异常(0x00-0x1F):优先级最高,不可屏蔽。
    • 硬件中断(0x08-0x0F, 0x70-0x77):可屏蔽,由 cli/sti 控制。
    • 软件中断(0x20-0xFF):通过 int 指令主动触发。
  3. 中断重入问题
    • int 指令会自动清除 IF 标志,防止中断嵌套。
    • 中断处理程序可选择性使用 sti 重新启用中断。

IF 标志是什么?

  • IF(Interrupt Flag)位于 CPU 的 EFLAGS/FLAGS 寄存器中,用于控制可屏蔽硬件中断是否被响应。
  • IF=1 表示允许 PIC 发送的硬件中断,IF=0 则屏蔽(也就是 cli 指令的效果)。
  • 当执行 int 指令或进入中断处理程序时,CPU 会自动清除 IF=0,避免中断嵌套;处理完毕后通常用 iret 恢复原有 IF 状态,或通过 sti 手动重新开启。

📺 常用 BIOS 中断服务详解

INT 0x10 - 视频服务

这是我们在引导程序中最常用的中断之一。

AH值 功能 参数 用途示例
0x00 设置视频模式 AL=模式号 切换文本/图形模式
0x01 设置光标形状 CX=形状 修改光标外观
0x02 设置光标位置 DH=行, DL=列 移动光标
0x06 滚动窗口 AL=行数 清屏
0x0E 显示字符 AL=字符 输出文本 ⭐
0x13 显示字符串 ES:BP=地址 批量输出

什么是 AH? 在 x86 架构中,CPU 提供了多组通用寄存器(如 AX、BX、CX、DX),每个寄存器 16 位。在实际应用中,这些寄存器还可以细分低 8 位(如 AL、BL)与高 8 位(如 AH、BH):

  • AX:16 位寄存器,可以看作由高 8 位 AH 和低 8 位 AL 组成。
  • AH(“A High”):AX 的高 8 位,常用于传递功能号或状态(例如 BIOS 中断的功能号一般通过 AH 传递)。
  • AL(“A Low”):AX 的低 8 位,常用于传递附加参数或数据(如字符等)。

示例:输出 "Hello" 到屏幕

1
2
3
4
mov ah, 0x0E    ; 功能号:Teletype输出
mov al, 'H' ; 字符
int 0x10 ; 调用BIOS
;重复输出e, l, l, o

INT 0x13 - 磁盘服务

用于读写磁盘扇区:

AH值 功能 参数 返回值
0x00 复位磁盘 DL=驱动器号 CF=错误标志
0x02 读扇区 ⭐ AL=扇区数, CH=柱面, CL=扇区, DH=磁头, DL=驱动器, ES:BX=缓冲区 AL=实际读取扇区数
0x03 写扇区 同上 AL=实际写入扇区数
0x08 获取驱动器参数 DL=驱动器 返回几何参数
0x41 检测扩展功能 - 是否支持LBA
0x42 扩展读 (LBA) DS:SI=参数包 支持大于8GB磁盘

CHS 寻址模式

  • C (Cylinder):柱面号: 0-1023
  • H (Head):磁头号: 0-255
  • S (Sector):扇区号: 1-63(注意:从1开始!)

计算公式:

1
物理扇区号 = (C × 磁头数 + H) × 每磁道扇区数 + (S - 1)

INT 0x16 - 键盘服务

AH值 功能 返回值
0x00 读取按键 AL=ASCII, AH=扫描码
0x01 检查按键 ZF=1无按键, AL=字符
0x02 读取状态 AL=Shift/Ctrl/Alt状态

INT 0x15 - 系统服务

AH值 功能 用途
0xE8, 0x01 获取内存大小 检测系统内存
0xE8, 0x20 获取内存映射 获取详细内存布局 ⭐
0x24, 0x01 启用A20 访问1MB以上内存

什么是 A20 地址线?为什么需要启用它?

A20 的历史背景:

  • 在早期的 8086/8088 CPU 中,只有 20 根地址线(A0-A19),可以访问 1MB 的地址空间(0x00000-0xFFFFF)。
  • 8086 使用段:偏移寻址方式,理论上可以表示 0xFFFF:0xFFFF = 0x10FFEF(约 1MB + 64KB),但由于只有 20 根地址线,超出 1MB 的地址会回绕到低地址(例如 0x100000 会回绕到 0x00000)。
  • 这种回绕特性被一些程序利用,作为"内存保护"机制。

80286 及以后的问题:

  • 80286 引入了 24 根地址线(可访问 16MB),80386 引入了 32 根地址线(可访问 4GB)。
  • 为了保持向后兼容,IBM 在 AT 机型的键盘控制器中增加了一个开关,默认关闭 A20 地址线,使系统行为与 8086 一致。
  • 这个开关控制的就是第 21 根地址线(A20),因此被称为 "A20 Gate"。

为什么需要启用 A20?

  • 要访问 1MB 以上的内存(扩展内存),必须启用 A20 地址线。
  • 现代操作系统和引导程序都需要访问超过 1MB 的内存,因此必须在进入保护模式前启用 A20。

启用 A20 的方法:

  • 通过键盘控制器(8042)的端口 0x64/0x60
  • 通过 BIOS 中断 INT 0x15, AH=0x24, AL=0x01
  • 通过 Fast A20(直接操作端口 0x92,更快但可能不兼容所有硬件)