System Learning Daily 5

记一个大坑,坑里的问题十分诡异。

ATA PIO Mode 的驱动问题

这是个很简单的问题,稍加注意即可避免。但由于学艺不精,这个问题在九个月就困扰我,一直没有解决。

最近我终于发现了问题所在。

长话短说

这个问题会导致磁盘读写不全。

遭遇的诡异问题

MBR加载OSLoader的时候由于磁盘读写有问题,所以可能出现各种情况。我在此总结一下出现的各种奇怪的现象。

这些现象都很诡异。

  1. 使用ATA PIO模式读硬盘时,传参数为N个Sector,实际上只能读出来N/2个Sector
  2. 在C语言代码中无法调用函数,如果调用函数则可能出现某个从实模式跳转到保护模式的jmp指令出错。出现这个问题的原因是:我把GDT放到了程序的最后面,本来GDT的位置是磁盘恰巧可以读出来的位置,然后,加入了一个函数调用,因为对齐的原因,GDT的位置跑到了硬盘恰巧读不出来的位置。在执行jmp的时候就会出现GFP(Genaral Protection Fault),查看QEMU的log发现error_code = 8,正好是那个代码段的段选择子。
  3. 总之,由于磁盘读写不全,任何情况下,我只要修改了镜像的内存布局,程序的行为就会大变样。

原因

为什么会读写不全呢?

啊,感叹一句,还是怪我自己没有好好阅读 OSDev ATA_PIO_Mode 上面的驱动代码实例,所以浪费了很多的时间。

每次在发送指令到0x1f7端口之后以及读完一个Sector之后,至少要等待400ns的时间,目的是获得准确的Status Reister的状态。等待400ns的最好方法就是直接插入四句如下指令

1
in al, dx

每一次in都需要100ns的时间。具体细节请参考 OSDev ATA_PIO_Mode

而我的读取硬盘的代码中这一部分内容缺失了,没有等待这400ns的时间。这就导致了这样一种奇怪的现象(也有可能不止于此,有可能更诡异):

  1. Reading Sector_1 works
  2. Reading Sector_2 doesn’t work
  3. Reading Sector_3 works
  4. Reading Sector_3 doesn’t work

我是怎么发现原因的

很久以前我就发现了这样的一个问题,当时写了简单的MBR和一个仅仅实现了打log功能的Kernel(23333)。

那个时候我就发现磁盘读取不全,我的办法很奇怪,就是把读取数变大。当然,这样也会出现问题,不过问题没有那么严重了。

最近我再次对这个问题进行Debug,其实在这几天我学到了很多东西,使用工具也比以前更加地熟练。

最终,仔细阅读了 OSDev ATA_PIO_Mode 上面的内容,才发现自己这里少了一些东西。

Relief

从昨天晚上Debug到今天下午。其实没用多少时间。

问题解决了,很高兴~