ATA 磁盘驱动程序“跳过”内存块
Posted
技术标签:
【中文标题】ATA 磁盘驱动程序“跳过”内存块【英文标题】:ATA disk driver "skipping" chunk of memory 【发布时间】:2021-04-12 13:36:22 【问题描述】:我正在尝试编写一个简单的 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
开始,因此read_sects_pio28
以pa_dest=0x10000
、sect_offs=12
、sect_count=2
调用(我已经使用调试器验证了这实际上是这种情况)。
但这似乎只有某种有效。以下是该段在磁盘上的样子:
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,但问题仍然存在。
【问题讨论】:
看起来您有 24 位 lba 驱动程序。你需要的地方 48 @АлексейНеудачин 我虽然总是可以使用 24 位 lba,但我如何才能确定是否是这种情况?目前我只在使用 qemu,而不是在实际硬件上。 也许那时是可能的。然后你需要 90 年代后期的硬盘驱动器。我认为 quemu 应该提供一些模型 如果 quemu 将使用通用 ata 控制器,那么它将是 48 @АлексейНеудачин 这是有道理的,我认为 48 位驱动程序会“向后兼容”,但我想这没有多大意义。 【参考方案1】:我想通了,我犯了两个非常微不足道的错误:首先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 个双字),而不仅仅是四分之一。
【讨论】:
将指针作为uint32_t dest
传递没有什么意义。使用uintptr_t
或void*
,甚至T*
。
@PeterCordes:在 OS 开发 afaik 中使用固定宽度整数和指针来表示地址的可互换性非常普遍。
在这里硬编码指针大小似乎毫无意义,只是让移植到 x86-64 变得更加困难。如果您使用了void*
和size_t
,则无需针对64 位模式更改此代码。所以我的部分反对意见是,它是一个标准名称,如uint32_t
,您不能将其重新定义为 64 位类型,甚至比整数类型更重要。 (虽然由于这个函数只是取消引用它,而不是将它拆分成 page : offset 部分或任何东西,可以说调用者应该是那个将它转换为 T*
的人,如果它还不是一个指针。)对于指针作为整数,使用 @ 987654337@ 或自定义类型
例如在ins(PORT, (uint32_t)&struct.buf, size)
这样的情况下,这对调用者来说只是纯粹的不便
@PeterCordes:x86_64 是个好点,我应该改一下。以上是关于ATA 磁盘驱动程序“跳过”内存块的主要内容,如果未能解决你的问题,请参考以下文章