ided: Improvements to DMA code, fixes some virtualbox issues

This commit is contained in:
Jeremy Soller
2022-09-13 14:38:50 -06:00
parent b8cb9d0e03
commit 2462857e2d
+73 -23
View File
@@ -45,8 +45,8 @@ pub struct Channel {
pub busmaster_command: Pio<u8>,
pub busmaster_status: Pio<u8>,
pub busmaster_prdt: Pio<u32>,
prdt: Dma<[PrdtEntry; 16]>,
buf: Dma<[u8; 16 * 4096]>,
prdt: Dma<[PrdtEntry; 128]>,
buf: Dma<[u8; 128 * 512]>,
}
impl Channel {
@@ -71,29 +71,17 @@ impl Channel {
prdt: unsafe {
Dma::from_physbox_zeroed(
//TODO: PhysBox::new_in_32bit_space(4096)?
PhysBox::new(4096)?
PhysBox::new(4096 /* 128 * 8 page aligned */)?
)?.assume_init()
},
buf: unsafe {
Dma::from_physbox_zeroed(
//TODO: PhysBox::new_in_32bit_space(16 * 4096)?
PhysBox::new(16 * 4096)?
PhysBox::new(128 * 512)?
)?.assume_init()
},
};
for i in 0..chan.prdt.len() {
chan.prdt[i] = PrdtEntry {
phys: (chan.buf.physical() + i * 4096).try_into().unwrap(),
size: 4096,
flags: if i + 1 == chan.prdt.len() {
1 << 15 // End of table
} else {
0
},
};
}
Ok(chan)
}
@@ -189,14 +177,26 @@ impl Disk for AtaDisk {
//TODO: support other LBA modes
assert!(block < 0x1_0000_0000_0000);
let sectors = chunk.len() / 512;
assert!(sectors < 0x1_0000);
let sectors = (chunk.len() + 511) / 512;
assert!(sectors <= 128);
let mut chan = self.chan.lock().unwrap();
if self.dma {
// Stop bus master
chan.busmaster_command.writef(1, false);
// Make PRDT EOT match chunk size
for i in 0..sectors {
chan.prdt[i] = PrdtEntry {
phys: (chan.buf.physical() + i * 512).try_into().unwrap(),
size: 512,
flags: if i + 1 == sectors {
1 << 15 // End of table
} else {
0
},
};
}
// Set PRDT
let prdt = chan.prdt.physical();
chan.busmaster_prdt.write(prdt.try_into().unwrap());
@@ -207,6 +207,7 @@ impl Disk for AtaDisk {
}
// Select drive
//TODO: upper part of LBA 28
chan.device_select.write(0xE0 | (self.dev << 4));
if self.lba_48 {
@@ -248,11 +249,30 @@ impl Disk for AtaDisk {
// Wait for transaction to finish
chan.polling(false)?;
//TODO: use bus master status
// Wait for bus master to finish
let error = loop {
let status = chan.busmaster_status.read();
if status & 1 << 1 != 0 {
// Break with error status
break true;
}
if status & 1 == 0 {
// Break when not busy and no error
break false;
}
};
// Stop bus master
chan.busmaster_command.writef(1, false);
// Clear bus master error and interrupt
chan.busmaster_status.write(0b110);
if error {
log::error!("IDE bus master error");
return Err(Error::new(EIO));
}
// Read buffer
chunk.copy_from_slice(&chan.buf[..chunk.len()]);
} else {
@@ -283,14 +303,26 @@ impl Disk for AtaDisk {
//TODO: support other LBA modes
assert!(block < 0x1_0000_0000_0000);
let sectors = chunk.len() / 512;
assert!(sectors < 0x1_0000);
let sectors = (chunk.len() + 511) / 512;
assert!(sectors <= 128);
let mut chan = self.chan.lock().unwrap();
if self.dma {
// Stop bus master
chan.busmaster_command.writef(1, false);
// Make PRDT EOT match chunk size
for i in 0..sectors {
chan.prdt[i] = PrdtEntry {
phys: (chan.buf.physical() + i * 512).try_into().unwrap(),
size: 512,
flags: if i + 1 == sectors {
1 << 15 // End of table
} else {
0
},
};
}
// Set PRDT
let prdt = chan.prdt.physical();
chan.busmaster_prdt.write(prdt.try_into().unwrap());
@@ -304,6 +336,7 @@ impl Disk for AtaDisk {
}
// Select drive
//TODO: upper part of LBA 28
chan.device_select.write(0xE0 | (self.dev << 4));
if self.lba_48 {
@@ -345,12 +378,29 @@ impl Disk for AtaDisk {
// Wait for transaction to finish
chan.polling(false)?;
//TODO: use bus master status
// Wait for bus master to finish
let error = loop {
let status = chan.busmaster_status.read();
if status & 1 << 1 != 0 {
// Break with error status
break true;
}
if status & 1 == 0 {
// Break when not busy and no error
break false;
}
};
// Stop bus master
chan.busmaster_command.writef(1, false);
chan.check_status()?;
// Clear bus master error and interrupt
chan.busmaster_status.write(0b110);
if error {
log::error!("IDE bus master error");
return Err(Error::new(EIO));
}
} else {
for sector in 0..sectors {
chan.polling(false)?;