我正试图编写一个简单的32位x86操作系统,但我的ATA磁盘驱动程序代码有问题。
我已经编写了以下C++类,其函数read_sects_pio28
应该读取sect_count
磁盘扇区,从偏移量sect_offs
开始,到物理地址pa_dest
:
template<typename T>
void out(uint16_t port, T data)
{
asm ("out %0,%1"
: : "a" (data), "d" (port));
}
template<typename T>
T in(uint16_t port)
{
T data;
asm volatile("in %1,%0"
: "=a" (data)
: "d" (port));
return data;
}
template<typename T>
void ins(uint16_t port, uint32_t dest, uint32_t count)
{
static_assert(detail::is_uint_le32_t<T>());
asm volatile("rep ins%z2"
: "+D" (dest), "+c" (count), "=m" (dest)
: "d" (port)
: "memory");
}
class Disk_driver_ata
{
enum : uint32_t
{
ATA_PORT_DATA = 0x1F0,
ATA_PORT_SECT_COUNT = 0x1F2,
ATA_PORT_LBA_LO = 0x1F3,
ATA_PORT_LBA_MID = 0x1F4,
ATA_PORT_LBA_HI = 0x1F5,
ATA_PORT_DRIVE_HEAD = 0x1F6,
ATA_PORT_STATUS = 0x1F7,
ATA_PORT_CMD = 0x1F7
};
enum : uint8_t
{
ATA_PIO48_MASTER = 0x40,
ATA_PIO48_CMD_READ = 0x24,
ATA_STATUS_DRQ = 0x08
};
public:
void read_sects(uint32_t pa_dest,
uint64_t sect_offs,
uint16_t sect_count) const override
{ read_sects_pio48(pa_dest, sect_offs, sect_count); }
private:
static void read_sects_pio48(uint32_t pa_dest,
uint64_t sect_offs,
uint16_t sect_count)
{
x86::outb(ATA_PORT_DRIVE_HEAD, ATA_PIO48_MASTER);
x86::outb(ATA_PORT_SECT_COUNT, (sect_count >> 8) & 0xFF);
x86::outb(ATA_PORT_LBA_LO, (sect_offs >> 24) & 0xFF);
x86::outb(ATA_PORT_LBA_MID, (sect_offs >> 32) & 0xFF);
x86::outb(ATA_PORT_LBA_HI, (sect_offs >> 40) & 0xFF);
x86::outb(ATA_PORT_SECT_COUNT, sect_count & 0xFF);
x86::outb(ATA_PORT_LBA_LO, sect_offs & 0xFF);
x86::outb(ATA_PORT_LBA_MID, (sect_offs >> 8) & 0xFF);
x86::outb(ATA_PORT_LBA_HI, (sect_offs >> 16) & 0xFF);
x86::outb(ATA_PORT_CMD, ATA_PIO48_CMD_READ);
for (uint8_t sec = 0u; sec < sect_count; ++sec) {
poll_status(ATA_STATUS_DRQ);
x86::ins<uint32_t>(ATA_PORT_DATA, pa_dest, DISK_SECT_SIZE / 4);
pa_dest += DISK_SECT_SIZE / 4;
}
}
static void poll_status(uint8_t status)
{
while (!(x86::in<uint8_t>(ATA_PORT_STATUS) & status))
;
}
};
我想使用这个函数来加载我的内核的可执行ELF段,它目前只有688字节大,以寻址0x100000
。在磁盘上,段从0x1800
开始,因此使用pa_dest=0x10000
、sect_offs=12
、sect_count=2
调用read_sects_pio28
(我已经使用调试器验证了实际情况(。
但这似乎只是类型的工作。以下是磁盘上的片段:
00001800 e9 00 00 00 00 55 89 e5 83 ec 08 83 ec 0c 6a 68 |.....U........jh|
00001810 e8 31 02 00 00 83 c4 10 83 ec 0c 6a 65 e8 24 02 |.1.........je.$.|
00001820 00 00 83 c4 10 83 ec 0c 6a 6c e8 17 02 00 00 83 |........jl......|
00001830 c4 10 83 ec 0c 6a 6c e8 0a 02 00 00 83 c4 10 83 |.....jl.........|
00001840 ec 0c 6a 6f e8 fd 01 00 00 83 c4 10 83 ec 0c 6a |..jo...........j|
00001850 0a e8 f0 01 00 00 83 c4 10 90 c9 c3 55 89 e5 83 |............U...|
00001860 ec 18 8b 45 08 66 89 45 f4 0f b7 45 f4 83 ec 0c |...E.f.E...E....|
00001870 50 e8 fa 01 00 00 83 c4 10 c9 c3 55 89 e5 83 ec |P..........U....|
00001880 18 8b 45 08 8b 55 0c 66 89 45 f4 89 d0 88 45 f0 |..E..U.f.E....E.|
00001890 0f b6 55 f0 0f b7 45 f4 83 ec 08 52 50 e8 eb 01 |..U...E....RP...|
000018a0 00 00 83 c4 10 90 c9 c3 55 89 e5 83 ec 18 c7 45 |........U......E|
000018b0 f4 00 00 00 00 83 ec 08 6a 0e 68 d4 03 00 00 e8 |........j.h.....|
000018c0 b7 ff ff ff 83 c4 10 83 ec 0c 68 d5 03 00 00 e8 |..........h.....|
000018d0 88 ff ff ff 83 c4 10 0f b6 c0 c1 e0 08 89 45 f4 |..............E.|
000018e0 83 ec 08 6a 0f 68 d4 03 00 00 e8 8c ff ff ff 83 |...j.h..........|
000018f0 c4 10 83 ec 0c 68 d5 03 00 00 e8 5d ff ff ff 83 |.....h.....]....|
00001900 c4 10 0f b6 c0 09 45 f4 8b 45 08 c7 00 00 80 0b |......E..E......|
00001910 00 8b 45 f4 89 c2 8b 45 08 66 89 50 04 90 c9 c3 |..E....E.f.P....|
00001920 55 89 e5 53 83 ec 14 8b 45 0c 88 45 f7 c6 45 f6 |U..S....E..E..E.|
00001930 f0 0f b6 45 f6 c1 e0 08 89 c2 0f b6 45 f7 09 d0 |...E........E...|
00001940 66 89 45 f4 0f b6 45 f7 83 f8 0a 75 11 8b 45 08 |f.E...E....u..E.|
00001950 0f b7 40 04 8d 50 50 8b 45 08 66 89 50 04 8b 45 |..@..PP.E.f.P..E|
00001960 08 8b 18 8b 45 08 0f b7 40 04 8d 48 01 8b 55 08 |....E...@..H..U.|
00001970 66 89 4a 04 0f b7 c0 01 c0 8d 14 03 0f b7 45 f4 |f.J...........E.|
00001980 66 89 02 90 8b 45 08 0f b7 50 04 0f b7 c2 69 c0 |f....E...P....i.|
00001990 cd cc 00 00 c1 e8 10 66 c1 e8 06 66 89 45 f2 0f |.......f...f.E..|
000019a0 b7 4d f2 89 c8 c1 e0 02 01 c8 c1 e0 04 29 c2 66 |.M...........).f|
000019b0 89 55 f2 8b 45 08 0f b7 40 04 0f b7 c0 69 c0 cd |.U..E...@....i..|
000019c0 cc 00 00 c1 e8 10 66 c1 e8 06 66 89 45 f0 0f b7 |......f...f.E...|
000019d0 55 f0 89 d0 c1 e0 02 01 d0 c1 e0 04 89 c2 0f b7 |U...............|
000019e0 45 f2 01 d0 66 89 45 ee 83 ec 08 6a 0f 68 d4 03 |E...f.E....j.h..|
000019f0 00 00 e8 84 fe ff ff 83 c4 10 0f b7 45 ee 0f b6 |............E...|
00001a00 c0 83 ec 08 50 68 d4 03 00 00 e8 6c fe ff ff 83 |....Ph.....l....|
00001a10 c4 10 83 ec 08 6a 0e 68 d4 03 00 00 e8 5a fe ff |.....j.h.....Z..|
00001a20 ff 83 c4 10 0f b7 45 ee 66 c1 e8 08 0f b6 c0 83 |......E.f.......|
00001a30 ec 08 50 68 d4 03 00 00 e8 3e fe ff ff 83 c4 10 |..Ph.....>......|
00001a40 90 8b 5d fc c9 c3 55 89 e5 83 ec 18 83 ec 0c 8d |..]...U.........|
00001a50 45 f0 50 e8 50 fe ff ff 83 c4 10 83 ec 08 ff 75 |E.P.P..........u|
00001a60 08 8d 45 f0 50 e8 b6 fe ff ff 83 c4 10 90 c9 c3 |..E.P...........|
00001a70 55 89 e5 83 ec 14 8b 45 08 66 89 45 ec 0f b7 45 |U......E.f.E...E|
00001a80 ec 89 c2 ec 88 45 ff 0f b6 45 ff c9 c3 55 89 e5 |.....E...E...U..|
00001a90 83 ec 08 8b 45 08 8b 55 0c 66 89 45 fc 89 d0 88 |....E..U.f.E....|
00001aa0 45 f8 0f b6 45 f8 0f b7 55 fc ee 90 c9 c3 3a 00 |E...E...U.....:.|
00001ab0 00 00 03 00 27 00 00 00 01 01 fb 0e 0d 00 01 01 |....'...........|
以下是实际加载到内存中的内容:
00000000 e9 00 00 00 00 55 89 e5 83 ec 08 83 ec 0c 6a 68 |.....U........jh|
00000010 e8 31 02 00 00 83 c4 10 83 ec 0c 6a 65 e8 24 02 |.1.........je.$.|
00000020 00 00 83 c4 10 83 ec 0c 6a 6c e8 17 02 00 00 83 |........jl......|
00000030 c4 10 83 ec 0c 6a 6c e8 0a 02 00 00 83 c4 10 83 |.....jl.........|
00000040 ec 0c 6a 6f e8 fd 01 00 00 83 c4 10 83 ec 0c 6a |..jo...........j|
00000050 0a e8 f0 01 00 00 83 c4 10 90 c9 c3 55 89 e5 83 |............U...|
00000060 ec 18 8b 45 08 66 89 45 f4 0f b7 45 f4 83 ec 0c |...E.f.E...E....|
00000070 50 e8 fa 01 00 00 83 c4 10 c9 c3 55 89 e5 83 ec |P..........U....|
00000080 c0 83 ec 08 50 68 d4 03 00 00 e8 6c fe ff ff 83 |....Ph.....l....|
00000090 c4 10 83 ec 08 6a 0e 68 d4 03 00 00 e8 5a fe ff |.....j.h.....Z..|
000000a0 ff 83 c4 10 0f b7 45 ee 66 c1 e8 08 0f b6 c0 83 |......E.f.......|
000000b0 ec 08 50 68 d4 03 00 00 e8 3e fe ff ff 83 c4 10 |..Ph.....>......|
000000c0 90 8b 5d fc c9 c3 55 89 e5 83 ec 18 83 ec 0c 8d |..]...U.........|
000000d0 45 f0 50 e8 50 fe ff ff 83 c4 10 83 ec 08 ff 75 |E.P.P..........u|
000000e0 08 8d 45 f0 50 e8 b6 fe ff ff 83 c4 10 90 c9 c3 |..E.P...........|
000000f0 55 89 e5 83 ec 14 8b 45 08 66 89 45 ec 0f b7 45 |U......E.f.E...E|
00000100 ec 89 c2 ec 88 45 ff 0f b6 45 ff c9 c3 55 89 e5 |.....E...E...U..|
00000110 83 ec 08 8b 45 08 8b 55 0c 66 89 45 fc 89 d0 88 |....E..U.f.E....|
00000120 45 f8 0f b6 45 f8 0f b7 55 fc ee 90 c9 c3 3a 00 |E...E...U.....:.|
00000130 00 00 03 00 27 00 00 00 01 01 fb 0e 0d 00 01 01 |....'...........|
似乎中间的一个连续的区块丢失了。这可能是什么原因?
编辑:根据一些建议,我在in
中添加了一个缺失的volatile
,并切换到48位PIO,但问题仍然存在。
我发现了,我犯了两个非常微不足道的错误:首先,ins
的代码不正确,正如Michael Petch指出的那样,它缺少一个volatile
。此外,dest
应具有类型T*
:
template<typename T>
void ins(uint16_t port, uint32_t dest, uint32_t count)
{
static_assert(detail::is_uint_le32_t<T>());
T *dest_ptr = reinterpret_cast<T *>(dest);
asm volatile("rep ins%z2"
: "+D" (dest_ptr), "+c" (count), "=m" (*dest_ptr)
: "d" (port)
: "memory");
}
那么,关键的是,线路pa_dest += DISK_SECT_SIZE / 4
是错误的。这应该是pa_dest += DISK_SECT_SIZE
,因为ins
读取整个扇区(128个双字(,而不仅仅是其中的四分之一