diff --git a/ableos/src/devices/pci/ide.rs b/ableos/src/devices/pci/ide.rs index 81576729..60a617e4 100644 --- a/ableos/src/devices/pci/ide.rs +++ b/ableos/src/devices/pci/ide.rs @@ -76,9 +76,15 @@ const CMD_IDENTIFY: u8 = 0xEC; /// ATA read using LBA28 DMA command const CMD_READ_DMA: u8 = 0xC8; +/// ATA write using LBA28 DMA command +const CMD_WRITE_DMA: u8 = 0xCA; + /// ATA read using LBA48 DMA command const CMD_READ_DMA_EXT: u8 = 0x25; +/// ATA write using LBA48 DMA command +const CMD_WRITE_DMA_EXT: u8 = 0x35; + pub struct PciIde { device_info: PciDeviceInfo, ide_devices: Vec, @@ -311,14 +317,14 @@ impl PciIde { loop { let status = unsafe { self.bmi_status(channel) }; + // FIXME: error handling + // Bit 2 (INT) set? if (status >> 2) & 1 == 1 { break; } } - // FIXME: error handling - unsafe { // Stop DMA self.stop(channel); @@ -335,6 +341,91 @@ impl PciIde { Ok(()) } + pub fn write( + &mut self, + channel: Channel, + drive: Drive, + lba: u64, + data: &[u8], + ) -> Result<(), TryFromIntError> { + // FIXME: make this an error + assert!(data.len() % SECTOR_SIZE as usize == 0); + + let lba48_support = self + .ide_devices + .iter() + .find(|d| d.channel == channel && d.drive == drive) + .map(|d| d.lba48_support) + .unwrap(); // FIXME: make this an error + let lba48 = lba > 0xFFFFFFF && lba48_support; + + // FIXME: make this an error + assert!((lba48 && lba > 0xFFFFFFF) || (!lba48 && lba <= 0xFFFFFFF)); + + let byte_count = data.len() as u16; + let sector_count = byte_count / SECTOR_SIZE; + + // prepare PRD table + let prd = PRDT_START as *mut PhysRegionDescriptor; + unsafe { + (*prd).data_buffer = self.buffer_frames.as_ref().unwrap()[0] + .start_address() + .as_u64() + .try_into()?; + (*prd).byte_count = byte_count; + // this is the end of table + (*prd).eot = 1 << 7; + // this byte is reserved, we should probably set it to 0 + (*prd)._0 = 0; + } + + // copy the data over to the DMA buffer + for i in 0..byte_count { + let addr = (BUFFER_START + i as u64) as *mut u8; + unsafe { + *addr = *data.get(i as usize).unwrap_or(&0); + } + } + + unsafe { + self.load_prdt(channel); + self.stop(channel); + self.set_write(channel); + self.clear_bmi_status(channel); + select_drive(drive, channel); + set_lba(channel, lba, sector_count, lba48); + + if lba48 { + ata_send_command(CMD_WRITE_DMA_EXT, channel); + } else { + ata_send_command(CMD_WRITE_DMA, channel); + } + + self.start(channel); + } + + loop { + let status = unsafe { self.bmi_status(channel) }; + + // FIXME: error handling + + // Bit 2 (INT) set? + if (status >> 2) & 1 == 1 { + break; + } + } + + unsafe { + // Stop DMA + self.stop(channel); + + // Clear the interrupt bit + self.clear_bmi_status(channel); + } + + Ok(()) + } + pub fn device_info(&self) -> PciDeviceInfo { self.device_info }