XV6

早期的硬盘 IDE(Integrated Device Electronics)的读取方式比较复杂,需要分别指定 CHS (cylinder / head / sector,即柱面 / 磁头 / 扇区);后来的磁盘都支持 LBA(Logical Block Addressing,逻辑块寻址)模式,所有扇区都统一编号,只需指出扇区号即可完成访问。

下面介绍在 LBA 的模式下的 PIO(program IO)来实现对磁盘的读取操作。主板有两个 IDE 通道,每个通道可挂载两个硬盘。访问第一个通道的第一个硬盘的扇区使用 IO 地址寄存器(0x1f0~0x1f7);访问第二个硬盘使用的是(0x1f8~0x1ff)。访问第二个通道的硬盘分别使用(0x170-0x177)和(0x178-0x17f)。我们以第一个通道的第一个硬盘为例,说明 IO 端口寄存器的作用(注意选取本通道的主/从硬盘由第六个寄存器决定):

  1. 0x1f0:读数据,当 0x1f7 不为忙状态时,可以读。
  2. 0x1f2:每次读扇区的数目(最小是 1)。
  3. 0x1f3:如果是 LBA 模式就是 LBA 参数的 0-7 位(相当于扇区)。
  4. 0x1f4:如果是 LBA 模式就是 LBA 参数的 8-15 位(相当于磁头)。
  5. 0x1f5:如果是 LBA 模式就是了 LBA 参数的 16-23 位(相当于柱面)
  6. 0x1f6:第七位必须 1,第六位 1 为 LBA 模式 0 为 chs 模式,第五位必须 1,第四位 是 0 为主盘、1 为从盘,3-0 位是 LBA 的参数 27-24 位。
  7. 0x1f7:读该寄存器将获得状态信息,写出寄存器则可以发出命令(例如发出读命令,然后在不是忙状态的下可以从 0x1f0 读取数据)。命令举例如下:20H 读扇区,出错时允许重试(重读次数由制造商确定),2lH 禁止重试;30H 写扇区(允许重试), 31H 禁止重试。

对照上述定义,我们查看系统启动时载入内核的代码,从 readsect() 函数中可以看出 0x1f2 标识读入一个扇区,0x1f3~0x1f5 给出的是 LBA 的第 0~23 位,0x1f6 为 0xE0(0x1110-0000)标识 LBA 模式访问第一个 IDE 控制器上的第一块(编号为 0)的硬盘, 且 LBA 的第 24~27 位为 0x0000。然后才通过 outb(0x1F7, 0x20) 发出 0x20 的读命令,并通过 waitdisk() 等待磁盘空闲,最后通过 insl(0x1F0, dst, SECTSIZE/4) 读入整个扇区的数据,由于一次读入四字节,因此使用 SECTSIZE/4 次 IO 操作。

注意,IDE 驱动程序中并没有使用 DMA 方式,有兴趣的读者可以将代码修改为支持 DMA 方式以提升系统效率,IDE 相关实现代码在 ide.c