I'm trying to write a simple 32 bit x86 operating system and I have an issue with my ATA disk driver code.
I have written the following C++ class whose function read_sects_pio28
should read sect_count
disc sectors, starting at offset sect_offs
, to physical address 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))
;
}
};
I want to use this this function to load the executable ELF segment of my kernel, which is currently only 688 bytes large, to address 0x100000
. On disk, the segment starts at 0x1800
and so read_sects_pio28
is called with pa_dest=0x10000
, sect_offs=12
, sect_count=2
(I have verified that this is actually the case using a debugger).
But this seems to only sort of work. Here is what the segment looks like on disk:
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 |[email protected]|
00001960 08 8b 18 8b 45 08 0f b7 40 04 8d 48 01 8b 55 08 |[email protected].|
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 |[email protected]..|
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 |....'...........|
And here is what is actually loaded into memory:
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 |....'...........|
It seems like a contiguous chunk in the middle is missing. What could be the reason for this?
EDIT: following some suggestions I have added a missing volatile
in in
an switched to 48 bit PIO, but the issue persists.
question from:
https://stackoverflow.com/questions/65601995/ata-disk-driver-skipping-chunk-of-memory