System Learning Daily 5
记一个大坑,坑里的问题十分诡异。
ATA PIO Mode 的驱动问题
这是个很简单的问题,稍加注意即可避免。但由于学艺不精,这个问题在九个月就困扰我,一直没有解决。
最近我终于发现了问题所在。
长话短说
这个问题会导致磁盘读写不全。
遭遇的诡异问题
MBR加载OSLoader的时候由于磁盘读写有问题,所以可能出现各种情况。我在此总结一下出现的各种奇怪的现象。
这些现象都很诡异。
- 使用ATA PIO模式读硬盘时,传参数为N个
Sector,实际上只能读出来N/2个Sector。 - 在C语言代码中无法调用函数,如果调用函数则可能出现某个从实模式跳转到保护模式的jmp指令出错。出现这个问题的原因是:我把
GDT放到了程序的最后面,本来GDT的位置是磁盘恰巧可以读出来的位置,然后,加入了一个函数调用,因为对齐的原因,GDT的位置跑到了硬盘恰巧读不出来的位置。在执行jmp的时候就会出现GFP(Genaral Protection Fault),查看QEMU的log发现error_code = 8,正好是那个代码段的段选择子。 - 总之,由于磁盘读写不全,任何情况下,我只要修改了镜像的内存布局,程序的行为就会大变样。
原因
为什么会读写不全呢?
啊,感叹一句,还是怪我自己没有好好阅读 OSDev ATA_PIO_Mode 上面的驱动代码实例,所以浪费了很多的时间。
每次在发送指令到0x1f7端口之后以及读完一个Sector之后,至少要等待400ns的时间,目的是获得准确的Status Reister的状态。等待400ns的最好方法就是直接插入四句如下指令
1 | in al, dx |
每一次in都需要100ns的时间。具体细节请参考 OSDev ATA_PIO_Mode
而我的读取硬盘的代码中这一部分内容缺失了,没有等待这400ns的时间。这就导致了这样一种奇怪的现象(也有可能不止于此,有可能更诡异):
- Reading Sector_1 works
- Reading Sector_2 doesn’t work
- Reading Sector_3 works
- Reading Sector_3 doesn’t work
- …
我是怎么发现原因的
很久以前我就发现了这样的一个问题,当时写了简单的MBR和一个仅仅实现了打log功能的Kernel(23333)。
那个时候我就发现磁盘读取不全,我的办法很奇怪,就是把读取数变大。当然,这样也会出现问题,不过问题没有那么严重了。
最近我再次对这个问题进行Debug,其实在这几天我学到了很多东西,使用工具也比以前更加地熟练。
最终,仔细阅读了 OSDev ATA_PIO_Mode 上面的内容,才发现自己这里少了一些东西。
Relief
从昨天晚上Debug到今天下午。其实没用多少时间。
问题解决了,很高兴~