【浅析操作系统主引导扇区】

操作系统主引导扇区

前言

本文参考了以下相关文章以及书籍

Linux启动流程:从启动到 GRUB

《Linux内核完全注释》

引导启动程序(boot)

你需要具备有以下的知识:

  • 熟悉AT&T和Intel基本语法
  • bochs基本调试指令

总体流程

Linux操作系统启动部分的主要执行流程:一般来说,在接通电源时,主板就已经部分通电了。其处于 Standby 模式,检测是否需要开机。当用户按下电源开关(开机键)后,电源开始进入正常工作模式,它会给主板上电,按照时序供应 5V 和 12V 电源,然后输出 Power_OK 信号,通知主板可以正式工作。最后,主板会向 CPU 的 reset 引脚发送信号,于是CPU将清除所有寄存器中的数据并加载为预设值,根据 Intel SDM vol3 Table 9-1,寄存器值为:

EIP          0000FFF0H
CS Selector  F000H
CS base      FFFF0000H
CS Limit     FFFFH

初始值

段寄存器初始值

此时CPU自动进入实模式,并从地址0xFFFF0处开始自动执行代码,这个地址就是ROM-BIOS中的地址。PC机的BIOS将会执行系统的某些硬件检测和诊断功能,并在物理地址0处开始设置和初始化中断向量。此后,它将第一个扇区读入内存绝对地址0x7c00处,并跳转到这个地方开始引导启动机器运行。

当bootsect.s被执行的时候,它会把自己移动到内存0x90000处并把启动盘里后2kB读入内存0x90200处。至于内核其他部分,则会读入0x10000处

Q:此时 CS 为 0xf000 , IP 为 0xfff0 。根据 real mode 下地址的计算方法,此时 CS base 应该等于 0xf000 << 4 = 0xf0000 。但为何 Intel SDM 中说它是 0xffff0000 呢?

A:根据 Intel SDM vol3 9.1.4 First Instruction Executed :

The address FFFFFFF0H is beyond the 1-MByte addressable range of 
the processor while in real-address mode. The processor is initialized to 
this starting address as follows. The CS register has two parts: the visible 
segment selector part and the hidden base address part. In real-address 
mode, the base address is normally formed by shifting the 16-bit segment 
selector value 4 bits to the left to produce a 20-bit base address. However, 
during a hardware reset, the segment selector in the CS register is loaded 
with F000H and the base address is loaded with FFFF0000H. The starting 
address is thus formed by adding the base address to the value in the EIP
 register (that is, FFFF0000 + FFF0H = FFFFFFF0H).  

即根据上文的意思,寄存器有一个隐藏的描述符高速缓存器(就算不在32位保护模式,CPU也可以使用它),缓存器当中存储了一个32位的段基地址0xFFFF0000,在 reset 的时候被设置成的。且在16位实模式下仅能取出段基地址的前20位,因此我们需要使用的是这个隐藏的是段基地址前20位+地址寄存器IP去访问内存,而不需要计算得到的 0xF0000 再加上寄存器IP的值,这样提高了访问内存(包括ROM和其他外硬件的映射)的效率。

关于更多描述符高速缓存器的知识可以查阅Intel官方文档或者国内大佬李忠的个人网站

鼠侠网

第一条指令

查看这个地址,一般都是跳转指令:

0xfffffff0:  ljmp   $0xf000,$0xe05b

但有趣的是查看 0xffff0(0xf0000 + 0xffff) 也是同样的内容:

111

​ 此时CPU指针指向了,0xFFFF0000,即4G内存地址空间最后一个64k的最后16个字节处。这里是系统ROM BIOS存放的地址,并且BIOS会在这里存放一条跳转指令,跳转到BIOS代码中64kb范围内某一条指令开始执行。由于目前大厂商的BIOS容量不定,且又都在1MB/2MB左右,且存储在闪存(Flash Memory)ROM中,因此为了能够执行或访问BIOS中超过64KB范围而又远远不在0-1M空间中的其他BIOS代码或数据,BIOS会首先使用32位访问模式把数据段寄存器的范围设置为4G(而非原来的64K),这样CPU就可以在0-4G范围内执行和操作数据。

​ 此后,BIOS执行了一系列硬件检测和初始化操作后,就会和原来PC机兼容的64KB BIOS代码和数据复制到内存低端1M末端64K处,然后跳转至这个地方并让CPU真正运行在实模式地址下

222

附上IBM PC compatible PC 规定的内存布局:

	+------------------+  <- 0xFFFFFFFF (4GB)
	|      32-bit      |
	|  memory mapped   |
	|     devices      |
	|                  |
	/\/\/\/\/\/\/\/\/\/\
	
	/\/\/\/\/\/\/\/\/\/\
	|                  |
	|      Unused      |
	|                  |
	+------------------+  <- depends on amount of RAM
	|                  |
	|                  |
	| Extended Memory  |
	|                  |
	|                  |
	+------------------+  <- 0x00100000 (1MB)
	|     BIOS ROM     |
	+------------------+  <- 0x000F0000 (960KB)
	|  16-bit devices, |
	|  expansion ROMs  |
	+------------------+  <- 0x000C0000 (768KB)
	|   VGA Display    |
	+------------------+  <- 0x000A0000 (640KB)
	|                  |
	|    Low Memory    |
	|                  |
	+------------------+  <- 0x00000000

主引导扇区基本结构

  • 代码 446B
  • 分区表 64B (4 * 16)
  • 魔数 0xaa55

主要功能

读取内核加载器,并跳转执行

硬盘读写

  • CHS模式:/ Cylinder / Head / Sector
  • LBA模式:/ Logical Block Address

读取硬盘

;---------------------------------
;IO端口       端口用途
;primary通道  secondary通道  读操作时              写操作时 
;0x1f0        0x170          data                 data
;0x1f1        0x171          error                features
;0x1f2        0x172          sector countsector   count
;0x1f3        0x173          LBA low              LBA low
;0x1f4        0x174          LBA mid              LBA mid
;0x1f5        0x175          LBA high             LBA high
;0x1f6        0x176          device               device
;0x1f7        0x177          status               command
;--------------------------------

0x1f6 device端口(8) 
	;0 ~ 3表示起始扇区的24 ~ 27位
    ;第4位:  0表示主盘,1表示从盘 
    ;5~7位固定为1
    ;6位1表示采用LBA寻址
  
0x1f7:
;status寄存器 (读取时)
;0位: 1表示发生错误,错误信息见error寄存器
;3位: 1表示硬盘已经准备好数据,随时可以输出
;6位: 1表示设备就绪,等待命令
;7位: 1表示硬盘正忙,勿扰

command寄存器(写操作时)
- 0xEC: 识别硬盘
- 0x20: 读硬盘
- 0x30: 写硬盘

读取硬盘参考代码

;准备开始读取硬盘
;-------------------------------------------------------------------------------
;功能:读取硬盘n个扇区
;eax = LBA逻辑扇区号
;bx = 将数据写入的内存地址
;cx = 读取扇区数
;-------------------------------------------------------------------------------
;---------------------------------
;IO端口       端口用途
;primary通道  secondary通道  读操作时              写操作时 
;0x1f0        0x170          data                 data
;0x1f1        0x171          error                features
;0x1f2        0x172          sector countsector   count
;0x1f3        0x173          LBA low              LBA low
;0x1f4        0x174          LBA mid              LBA mid
;0x1f5        0x175          LBA high             LBA high
;0x1f6        0x176          device               device
;0x1f7        0x177          status               command
;--------------------------------

    mov eax, LOADER_START_SECTOR    ;loader起始扇区,28位地址,
    mov bx, LOADER_BASE_ADDR        ;loader写入的地址
    mov cx, 1                       ;待读入的扇区数
                                    ;写入从LOADER_START_SECTOR开始的一个扇区
    call rd_disk              ;读扇区函数
    
    jmp $


rd_disk:

;备份eax, cx原值
    mov esi, eax
    mov di, cx

;读写硬盘
;第一步:设置要读取的扇区数
    mov dx, 0x1f2       ;虚拟硬盘ata0 master 通过0x1f2端口访问
    mov al, cl          ;向端口dx输出al寄存器(也就是cl中的内容),表示读取一个扇区
    out dx, al
    mov eax, esi        ;恢复


;第二步:将LBA地址写入端口0x1f3~0x1f4
    ;LBA地址7~0位(刚好一个al寄存器8位)写入端口0x1f3
    mov dx, 0x1f3
    out dx, al

    ;LBA地址15~8位写入端口0x1f4
    mov cl, 8
    shr eax, cl             ;逻辑右移8位
    mov dx, 0x1f4
    out dx, al

    ;LBA地址23~16位写入端口0x1f5
    shr eax, cl             ;再移8位
    mov dx, 0x1f5
    out dx, al

    
    ;LBA地址24~27位写入端口0x1f6(0x1f6 device端口(8位))
	;0 ~ 3表示起始扇区的24 ~ 27位
    ;第4位:  0表示主盘,1表示从盘 
    ;5 7位固定为1
    ;6位1表示采用LBA寻址,0表示CHS模式

    shr eax, cl             ;此时eax低八位为初始时的31-24位
    and al, 0x0f            ;取27~24位
    ;!!!
    or al, 0xe0             ;设置端口0x1f6端口高四位为e 1110
    mov dx, 0x1f6
    out dx, al

;第三步:向0x1f7端口写入读命令 
;0x20 读扇区
;0x30 写扇区
;0xc4 读多个扇区
;0xc4 写多个扇区
    mov dx, 0x1f7
    mov al, 0x20
    out dx, al

;第4步:检测硬盘状态
;status寄存器 
;0位: 1表示发生错误,错误信息见error寄存器
;3位: 1表示硬盘已经准备好数据,随时可以输出
;6位: 1表示设备就绪,等待命令
;7位: 1表示硬盘正忙,勿扰
.not_ready:
    nop
    in al, dx               ;读0x1f7端口
    and al, 0x88            ;0x88 = 1000 1000b   仅对3 7位进行判断
    cmp al, 0x08            ;判断硬盘是否可以传输数据
    jnz .not_ready          ;若al = 0x08 zf = 1 表示可以传输数据, 若zf = 1即jnz轮询等待


;第5步:从0x1f0端口读数据
    mov ax, di              ;ax设置读取扇区数
    mov dx, 256             
    mul dx                  ;ax * dx = 256* 1 = 256字 = 512个字节
                            ;mul 低16位在ax中, cx = 256,每次读取一个字
                            ;结果高位默认在 DX 中存放,低位在 AX 中存放
    mov cx, ax
    mov dx, 0x1f0

.go_on_read:
    in ax, dx
    mov [bx], ax            ;将读入的一个扇区写入到内存0x900处
    add bx, 2
    loop .go_on_read        ;256次,一次一个字
    ret                     ;返回

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值