QEMU PIIX3 DMA IDE LBA calculation strangeness
Posted: Wed Oct 30, 2024 6:45 am
For the first time in a long time, hello OSDev forums! I've not been here since I was maybe 15 (I don't have access to that account any more, long dead email address), getting on for literally half my life ago. I was probably a bit of an annoying know-it-all so-and-so back then, as most teens are, and for that I can only apologise.
I'm getting rather a lot further this time, and am almost at the point where I can read in a file from its path, the longer way around (as in, enumerate ACPI, emumerate PCI bus, find and initialise IDE controllers, and so forth), giving me a solid foundation to build upon.
I'm experiencing some strangeness around LBA addresses. For addresses <255, I have to add 1 to it for some reason - attempting to read LBA 0 gives me an ABORT, but reading LBA 1 gives me the first sector.
For addresses >255, things seem to get all kinds of muddled. Attempting to read sector 465 (+1 when programming = 466) instead yields sector 1217, verified by outputting the hex, and comparing the disk image manually in a hex editor to find that particular string.
Neither of these things seem like they should be true, yet here we are, and for the life of me I can't work out what I've done wrong.
The device reports on the PCI bus as a PIIX3 IDE controller. Using debug printing I can confirm the sector I'm reading is indeed sector 466, taken 1 indexed, when programming the LBA registers.
The command line I'm using is:
The relevant code is below (Rust):
The one thing I'm aware I should do is convert IdeController to a Mutex, not a RWLock, but that shouldn't affect this, as it's single threaded polling code (although I'm doing DMA, I'm not awaiting interrupts, I haven't implemented that yet, and I also haven't started any APs, this is BSP only).
Help would be greatly appreciated, as I'm at a genuine loss as to why it's behaving the way that it is.
Kind regards,
Venos :3
I'm getting rather a lot further this time, and am almost at the point where I can read in a file from its path, the longer way around (as in, enumerate ACPI, emumerate PCI bus, find and initialise IDE controllers, and so forth), giving me a solid foundation to build upon.
I'm experiencing some strangeness around LBA addresses. For addresses <255, I have to add 1 to it for some reason - attempting to read LBA 0 gives me an ABORT, but reading LBA 1 gives me the first sector.
For addresses >255, things seem to get all kinds of muddled. Attempting to read sector 465 (+1 when programming = 466) instead yields sector 1217, verified by outputting the hex, and comparing the disk image manually in a hex editor to find that particular string.
Neither of these things seem like they should be true, yet here we are, and for the life of me I can't work out what I've done wrong.
The device reports on the PCI bus as a PIIX3 IDE controller. Using debug printing I can confirm the sector I'm reading is indeed sector 466, taken 1 indexed, when programming the LBA registers.
The command line I'm using is:
Code: Select all
qemu-system-x86_64 -bios $OVMF_PURE_EFI -drive format=raw,file=uefi.img -accel kvm
Code: Select all
const IDE_CTL_HOB: u8 = 1 << 7;
const IDE_CTL_REG: u16 = 0;
const IDE_REG_LBA0: u16 = 3;
const IDE_REG_LBA1: u16 = 4;
const IDE_REG_LBA2: u16 = 5;
const IDE_REG_SECCOUNT: u16 = 2;
impl IdeDrive {
fn select_drive_and_set_xfer_params(&self, ctl: &RwLockWriteGuard<'_, IdeController>, offset: u64, size: u64) {
self.select(&ctl);
// debug to confirm I'm trying to read the sector I think I should be
log::info!("Reading {}", offset);
if self.ident.is_lba48() {
unsafe {
let mut lba3_reg = Port::<u8>::new(ctl.io_base + IDE_REG_LBA0);
let mut lba4_reg = Port::<u8>::new(ctl.io_base + IDE_REG_LBA1);
let mut lba5_reg = Port::<u8>::new(ctl.io_base + IDE_REG_LBA2);
let mut seccount1_reg = Port::<u8>::new(ctl.io_base + IDE_REG_SECCOUNT);
let mut ctl_reg = Port::<u8>::new(ctl.control_base + IDE_CTL_REG);
let control_word_high_order = ctl_reg.read() | IDE_CTL_HOB;
ctl_reg.write(control_word_high_order);
lba3_reg.write((offset >> 24) as u8);
lba4_reg.write((offset >> 32) as u8);
lba5_reg.write((offset >> 40) as u8);
seccount1_reg.write((size >> 8) as u8);
let control_word_low_order = ctl_reg.read() & !IDE_CTL_HOB;
ctl_reg.write(control_word_low_order);
}
}
unsafe {
let mut lba0_reg = Port::<u8>::new(ctl.io_base + IDE_REG_LBA0);
let mut lba1_reg = Port::<u8>::new(ctl.io_base + IDE_REG_LBA1);
let mut lba2_reg = Port::<u8>::new(ctl.io_base + IDE_REG_LBA2);
let mut seccount0_reg = Port::<u8>::new(ctl.io_base + IDE_REG_SECCOUNT);
lba0_reg.write(offset as u8);
lba1_reg.write((offset >> 8) as u8);
lba2_reg.write((offset >> 16) as u8);
seccount0_reg.write(size as u8);
}
}
}
Help would be greatly appreciated, as I'm at a genuine loss as to why it's behaving the way that it is.
Kind regards,
Venos :3