I've been having trouble getting it to do reads. A single interrupt occurs and the status byte for DMA reads 0x04, but no data is transferred to the space in memory designated by the PRD. For testing, I'm currently using a PRDT with a single PRD in it.
The order of steps I'm taking:
- Clear the command byte
Set bit 3 in the status byte
Send the appropriate values to the to PCI config register
Set bit 2 in the PCI command register
Allocate a frame and retrieve its start address
Create a PRDT (physical region descriptor table) with one PRD, and retrieve its start address
Send the PRDT address to the DMA_PRDT_ADDR register
Reset IRQ status by setting bit 2 in the DMA status byte
Set bit 3 in the DMA command byte to set data transfer direction to read
Clear bits 0 and 2 in the DMA status byte
Set the PRIMARY_BUS_SELECT register for the drive with the drive value, the sector count, and the most significant 8 bits of the LBA
Set the SECTOR_COUNT register and the other 3 LBA registers
Set the COMMAND_IO register to 0xC8 in the drive to set it to DMA read
Set bit 1 of the command byte to start the transfer
Thanks for the help and time!
Code: Select all
pub fn read_from_disk_v2(drive: u8, lba: u32, sector_count: u32) -> Result<PhysicalAddress, ()> {
//Resetting bus master command register,
unsafe{
DMA_PRIM_COMMAND_BYTE.outb(0);
DMA_PRIM_STATUS_BYTE.outb(4);
}
//selects the PCI command register: the device is currently hardcoded to bus 0, slot 1, function 1, and the value to access the command register is an offset of 4
//following steps for this as outlined in http://wiki.osdev.org/PCI#Configuration_Space_Access_Mechanism_.231
PCI_CONFIG_ADDRESS_PORT.outb((1 << 11) | (1 << 8) | (4 & 0xFC) | 0x8000_0000);
//writes the value in the PCI data register to have it transferred to the selected PCI register, using this to set bit 2 in the command register
PCI_DATA_ADDRESS_PORT.outb(4);
//Frame allocation occurs here
if let Some(frame) = allocate_frame() {
let addr = frame.start_address();
let start_addr: u32 = addr as u32;
//prdt table defined: currently one PRD in PRDT for testing
//sector count is multiplied by 512 because 512 bytes in a sector
let prdt: [u64;1] = [start_addr as u64 | (sector_count as u64) *512 << 32 | 1 << 63];
let prdt_ref = &prdt[0] as *const u64;
//gets the physical address of the prdt and sends that to the DMA prdt register
let prdt_paddr = assign_prdt_paddr();
DMA_PRIM_PRDT_ADDR.outl(prdt_paddr);
DMA_PRIM_STATUS_BYTE.outb(0x04); // to reset the interrupt 0x14 status
//set bit 3 to set direction of controller for "read"
//http://wiki.osdev.org/ATA/ATAPI_using_DMA#The_Command_Byte states bit 3 value = 8, need to check if that's a typo
DMA_PRIM_COMMAND_BYTE.outb(0x08);
//get original value in the DMA status register and clear bits 0 and 2
let original_status = DMA_PRIM_STATUS_BYTE.inb();
//0xFA is value to clear bits 0 and 2 (0b11111010 in binary)
unsafe{DMA_PRIM_STATUS_BYTE.outb(original_status & 0xFA);}
//selects the drive (0xE0 for primary in this case) and sends top 8 bits of LBA (0 for testing) and sector count(1 in this case) to appropriate registers
let master_select: u8 = 0xE0 | (0 << 4) | ((lba >> 24) & 0x0F) as u8;
PRIMARY_BUS_SELECT.outl(master_select);
//enters the sector count and the other 3 bytes of the LBA to the appropriate registers
SECTORCOUNT.outb(1);
LBALO.outb(lba);
LBAMID.outb(lba>>8);
LBAHI.outb(lba>>16);
//sets the value in the command_io register to 0xC8, the code for DMA read
COMMAND_IO.outb(0xC8);
//setting bit 1 of the command byte starts the transfer
DMA_PRIM_COMMAND_BYTE.outb(0x08 | 0x01);
return Ok(start_addr);
}
return Err(());
}