ATA DMA Implementation on QEMU Version 2.5.0 w/ Rust 1.22.0
Page 1 of 1

Author:  arysef [ Mon Dec 04, 2017 12:33 am ]
Post subject:  ATA DMA Implementation on QEMU Version 2.5.0 w/ Rust 1.22.0

I'm trying out using PCI Busmastering ATA DMA to read from a hard disk.
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 largest issue has been figuring out how to debug the issue, because I'm unaware of many intermediary steps where progress can be checked to narrow down the issue.

Thanks for the help and time!

pub fn read_from_disk_v2(drive: u8, lba: u32, sector_count: u32) -> Result<PhysicalAddress, ()> {

    //Resetting bus master command register,
    DMA_PRIM_COMMAND_BYTE.try().expect("DMA_PRIM_COMMAND_BYTE not initialized in read_from_diskv2").lock().write(0);
    DMA_PRIM_STATUS_BYTE.try().expect("DMA_PRIM_STATUS_BYTE not initialized in read_from_diskv2").lock().write(4);

    ///TODO: Check setting bit 2 in pci command register
    pci_write(0, 1, 1, GET_COMMAND, 2);
    //Frame allocation occurs here
     if let Some(frame) = allocate_frame() {
        let addr = frame.start_address();
        if addr >= (u32::max_value() as usize) {
            error!("read_from_disk: alloc'd frame start addr={:#x} is too high (above 32-bit range)!", addr);
            return Err(());
        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 = {
            let tasklist = task::get_tasklist().read();
            let curr_task = tasklist.get_current().unwrap().write();
            let curr_mmi = curr_task.mmi.as_ref().unwrap();
            let mut curr_mmi_locked = curr_mmi.lock();
            curr_mmi_locked.translate(prdt_ref as usize)

        if prdt_paddr.unwrap() < (u32::max_value() as usize) {
                DMA_PRIM_PRDT_ADDR.try().expect("DMA_PRDT_ADD_LOW not configured").lock().write(prdt_paddr.unwrap() as u32 );
                DMA_PRIM_STATUS_BYTE.try().expect("DMA_PRIM_STATUS_BYTE not configured").lock().write(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
        unsafe{DMA_PRIM_COMMAND_BYTE.try().expect("DMA_PRIM_COMMAND_BYTE not initialized in read_from_disk_v2").lock().write(0x08);}
        let original_status = DMA_PRIM_STATUS_BYTE.try().expect("DMA_PRIM_STATUS_BYTE not configured").lock().read();
        //0xFA is value to clear bits 0 and 2 (0b11111010 in binary)
        unsafe{DMA_PRIM_STATUS_BYTE.try().expect("DMA_PRIM_STATUS_BYTE not configured").lock().write(original_status & 0xFA);}
        //selects the drive and sends LBA and sector count(1 in this case) to appropriate registers
        //setting bit 1 of the command byte starts the transfer
        unsafe{DMA_PRIM_COMMAND_BYTE.try().expect("DMA_PRIM_COMMAND_BYTE not initialized in read_from_disk_v2").lock().write(0x08 | 0x01);}
        return Ok(start_addr as PhysicalAddress);

This is the dma_read function referred to above:

pub fn dma_read(drive:u8, lba:u32)->Result<u16,u16>{
   let mut chosen_drive = &AtaIdentifyData{..Default::default()};

   if drive == 0xE0 {
      chosen_drive = &ATA_DEVICES.try().expect("ATA_DEVICES used before initialization").primary_master;

   if drive == 0xF0{
      chosen_drive = &ATA_DEVICES.try().expect("ATA_DEVICES used before initialization").primary_slave;
   trace!("{} number of sectors", chosen_drive.sector_count_28);
   if drive != 0xE0 && drive != 0xF0 {
      error!("input drive value {:#x} is unacceptable", drive);
      return Err(0);
   if lba+1> chosen_drive.sector_count_28{
      error!("lba {} out of range of sectors, sector count: {}", lba, chosen_drive.sector_count_28);
      return Err(0);
    //selects master drive(using 0xE0 value) in primary bus (by writing to primary_bus_select-port 0x1F6)
    let master_select: u8 = drive | (0 << 4) | ((lba >> 24) & 0x0F) as u8;

      //number of consecutive sectors to read from, set at 1
      //lba is written to disk ports
      LBALO.lock().write((lba)as u8);
      LBAMID.lock().write((lba>>8)as u8);
      LBAHI.lock().write((lba>>16)as u8);


   return Ok(1); // TODO: fix return value

   // old code below

   if COMMAND_IO.lock().read()%2 == 1{
      trace!("error bit set");
      return Err(0);

   //pausing 100 pit ticks to ensure data has time to be transferred (temporary measure)
   let start = pit_clock::PIT_TICKS.load(Ordering::SeqCst);
   while start+100 > pit_clock::PIT_TICKS.load(Ordering::SeqCst){}

   //data is ready to read from memory


Page 1 of 1 All times are UTC - 6 hours
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group