Bochs DMA Errors and invlaid DMA Bus Master port # PCI BAR4

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Bochs DMA Errors and invlaid DMA Bus Master port # PCI BAR4

Post by kemosparc »

HI,

I have a problem with DMA in bochs and qemu.

I have my OS run correctly in virtual box. when I tried to run it on bochs the DMA IDE interrupts stopped to fire upon reading sectors.

I have been struggling with this for a couple of days with no clue of what is going on.

I have enabled DMA debugging in side bochs and I have noticed during the BIOS initialization that the DMA debug messages indicate that the DMA Bus Master I/O port is 0x80 or 0x000d ?!!

Code: Select all

00000000000d[DMA   ] write: address=000d value=00
00000000000d[DMA   ] DMA-1: master clear
00000000000d[DMA   ] write: address=00da value=00
00000000000d[DMA   ] DMA-2: master clear
00000000005d[DMA   ] write: address=00d6 value=c0
00000000005d[DMA   ] DMA-2: mode register[0] = c0
00000000005d[DMA   ] write: address=00d4 value=00
00000000005d[DMA   ] DMA-2: set_mask_bit=0, channel=0, mask now=00h
00000005160d[DMA   ] write: address=0080 value=00
00000005160d[DMA   ] write: extra page register 0x0080 (unused)
00000005200d[DMA   ] write: address=0080 value=00
00000005200d[DMA   ] write: extra page register 0x0080 (unused)
00000005235d[DMA   ] write: address=0080 value=00
00000005235d[DMA   ] write: extra page register 0x0080 (unused)
00000005275d[DMA   ] write: address=0080 value=00
00000005275d[DMA   ] write: extra page register 0x0080 (unused)
00000005310d[DMA   ] write: address=0080 value=00

......
The important thing is that it is different from the value I read from BAR4 in the PCI header of the ATA disk I have which is 0xC001, and having the status bit set indicates that it is an I/O port and not mem/io so the port address i 0xc000.

When I try to write to this address, bochs does not generate any debug output which made me suspect that there is something wrong with this address.

I hardwired the dma port address to 0x80 and only then bochs starts spitting out debug info upon writing to that address.

The problem is that I still cannot get interrupts to fire.

An error occurs when I try to write to the address offset to set the DMA table (offset 4) and the debug output is as follows:

Code: Select all

00704828900d[DMA   ] write: address=0080 value=00
00704828900d[DMA   ] write: extra page register 0x0080 (unused)
00704828910d[DMA   ] write: address=0082 value=06
00704828910d[DMA   ] DMA-1: page register 3 = 06
00704828920e[DEV   ] write to port 0x0084 with len 4 ignored
00704838220d[DMA   ] read addr=0082
00704843265d[DMA   ] write: address=0080 value=09
00704843265d[DMA   ] write: extra page register 0x0080 (unused)
Notice the write to 0x0084, that is the attempt to write to the address offset which says that it is ignored.

Any help will be much appreciated.
Thanks a lot,
Karim.
Octocontrabass
Member
Member
Posts: 5586
Joined: Mon Mar 25, 2013 7:01 pm

Re: Bochs DMA Errors and invlaid DMA Bus Master port # PCI B

Post by Octocontrabass »

kemosparc wrote:I have enabled DMA debugging in side bochs and I have noticed during the BIOS initialization that the DMA debug messages indicate that the DMA Bus Master I/O port is 0x80 or 0x000d ?!!
Those are the ISA DMA controller ports. They're not used for PCI.

Enable debugging for the pci_ide device instead of the DMA device.
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Bochs DMA Errors and invlaid DMA Bus Master port # PCI B

Post by kemosparc »

Hi,

Thanks a lot for the reply.

I have enabled the pci_ide debug option.

I did some experiments that might help helping me:)

Now for virtualbox my OS run with no problems and with the expected functionalities.

I have downloaded bochs 2.6.9 (the latest) and I have tried running my OS on the three environments Bochs 2.6.8, 2.6.9, and Qemu.

First of all here is the code portion that has the problem:

Code: Select all

uint8_t readDMADiskSectors (ATADisk * p_ataDisk,uint64_t addr,uint32_t p_sector_count)
{
    uint16_t io_port = p_ataDisk->io_port;
    uint32_t dma_port_address = p_ataDisk->dma_port_address;
    uint8_t * dma_phy_address = p_ataDisk->dma_phy_address;
    uint32_t * ptr = (uint32_t *)p_ataDisk->prdt;
    memset(dma_phy_address,0,0x2000000);
    
    uint32_t sectors = p_sector_count;
    uint16_t i = 0;
    for ( ;sectors > 0; i+= 2 )
    {
        if ( sectors > 128 )
        {
            ptr[i] = (uint64_t) dma_phy_address+ ((i/2) * 0x10000);
            ptr[i+1] = 0x00000000 | (128 * 0x200); 
            sectors -= 128;
        }
        else if (sectors  <= 128)
        {
            ptr[i] = (uint64_t) dma_phy_address+ ((i/2) * 0x10000);
            ptr[i+1] = 0x80000000 | (sectors * 0x200); 
            sectors = 0;
        }
    }    
    outportb(dma_port_address+BM0_COMMAND+p_ataDisk->bm_offset,0);
    outportb(dma_port_address+BM0_STATUS+p_ataDisk->bm_offset,(1<<2)|(1 << 1));
    outportl(dma_port_address+BM0_ADDRESS+p_ataDisk->bm_offset,(uint32_t)ptr);

    uint8_t select = (0xE0 | (p_ataDisk->slavebit << 4));
    if (kernel.dmaBuffer.current_select != select )
    {
        if (  p_ataDisk->lba48)
        {
            printk ("reading lba48\n");
            outportb(io_port + ATA_REG_HDDEVSEL,select);
        }
        else outportb(io_port + ATA_REG_HDDEVSEL, (select | ((addr >> 24) & 0x0F)));
        ide_400ns_delay(p_ataDisk);
        kernel.dmaBuffer.current_select = select;
    }
    printk ("io_port: %x\n",io_port);

    outportb(io_port + ATA_REG_FEATURES, 0x00);
    outportb(io_port + ATA_REG_SECCOUNT1, (uint8_t)(p_sector_count >> 8));    
    outportb(io_port + ATA_REG_LBA3, (uint8_t)(addr >> 24));
    outportb(io_port + ATA_REG_LBA4, (uint8_t)(addr >> 32));
    outportb(io_port + ATA_REG_LBA5, (uint8_t)(addr >> 40));
    outportb(io_port + ATA_REG_SECCOUNT0,(uint8_t) p_sector_count);
    outportb(io_port + ATA_REG_LBA0, (uint8_t)addr);
    outportb(io_port + ATA_REG_LBA1, (uint8_t)(addr >> 8));
    outportb(io_port + ATA_REG_LBA2, (uint8_t)(addr >> 16));

    if ( p_ataDisk->lba48) outportb(io_port + ATA_REG_COMMAND, ATA_CMD_READ_DMA_EXT);
    else outportb(io_port + ATA_REG_COMMAND, ATA_CMD_READ_DMA); // 0xC8 for reading
    if (inportb(io_port + ATA_REG_STATUS))
    {
        for (uint64_t i = 0 ;;i++)
        {
            uint8_t status = inportb(io_port+ATA_REG_STATUS);
            if ( (status & ATA_SR_BSY))
            {
                printk ("DMA status is BSY: %d\n",i);
                continue;
            }
            else if(status & ATA_SR_ERR) 
            {
                printk ("<<<<<< Error in readDMADiskSector: %x\n",status);
                if (p_ataDisk->bm_offset == 0 ) outportb(ATA_PRIMARY_DCR_AS,0b00000000);
                else outportb(ATA_SECONDARY_DCR_AS,0b00000000);
                if (  p_ataDisk->slavebit )
                    outportb(io_port + ATA_REG_HDDEVSEL, ATA_SLAVE_DRV_SELECTOR);
                else outportb(io_port + ATA_REG_HDDEVSEL, ATA_MASTER_DRV_SELECTOR);
                ide_400ns_delay(p_ataDisk);
                status = inportb(io_port+ATA_REG_STATUS);
                kernel.dmaBuffer.current_select = 0;
                return READ_DMA_FAIL;
            }
            else if ( status & ATA_SR_DRQ )break;
            else printk ("%d:Something else: %d\n",i,status);
        }
    }
    outportb(dma_port_address+BM0_COMMAND+p_ataDisk->bm_offset,(1 << 3)|(1 << 0));   
    return READ_DMA_SUCCESS;
}

Now, for bochs 2.6.9 everything works very well as virtualbox, and the pci_ide debug info shows the disk firing interrupts as expected after committing to the DMA command register.

On my already installed bochs 2.6.8 (the old one), still the ATA commands run successfully but the DMA does not fire the interrupts and the debug info stops at the debug line indicating that I have written successfully to the DMA command register to start the transfer.

Finally on qemu, It does not even go beyond the ATA extended read. After the read I get caught in the loop of checking the ATA status and never get out of it as the keeps on being busy and printing the busy message after each ata status read.

The disk I am trying to read from is the primary/slave ata.

I start my Qemu using the following command:

Code: Select all

qemu-system-x86_64 -cpu host -m 4096 -smp 4 -drive file=./boot.qcow2,if=ide,index=0,media=disk -drive file=./data.qcow2,if=ide,index=1,media=disk  --enable-kvm
And I use the following bochsrc to start bochs in both versions.

Code: Select all

config_interface: textconfig
display_library: x
romimage: file=/home/kmsobh/crossenv/bochs-2.6.8/bios/BIOS-bochs-latest
vgaromimage: file=/usr/share/vgabios/vgabios.bin
debug: action=ignore, pci_ide=report
error: action=report
info: action=ignore #,ioapic=report #pci=report, pic=report, pci_ide=report
#debug: action=ignore , cpu1=report #, e1000=report, ioapic=report #memory=report #pci_ide=report
memory: guest=8192, host=2048
boot: disk
ata0-master: type=disk, path="./images/boot.raw"
ata0-slave: type=disk, path="./images/data.raw"
cpu: count=4, ips=10000000
cpuid: x86_64=1,apic=x2apic
plugin_ctrl: unmapped=0
One remark that I do not know if it is important or not is that bochs 2.6.8 starts and get the bochs emulator window much much faster than 2.6.9, and as soon as the emulation starts both progress at the same speed.

Please let me know if you can figure out the problem.

I appreciate any help,

Thanks a lot,

Karim.
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Bochs DMA Errors and invlaid DMA Bus Master port # PCI B

Post by kemosparc »

Hi,

Just a quick update. I was trying to compare bochs two versions and I have inserted some printfs in the bochs code to identify what is the difference between the execution of my OS in both 2.6.8 and 2.6.9 and I found that in the method void bx_pci_ide_c::timer() as below the printf which prints "2 >>>>>> ..." is invoked in 2.8.6 which disallow reaching the firing of the interrupt and it keeps on printing it on the console infinitely just right after writing to the BM0 Command:

Code: Select all

void bx_pci_ide_c::timer()
{
  int count;
  Bit32u size, sector_size;
  struct {
    Bit32u addr;
    Bit32u size;
  } prd;
  Bit8u channel = bx_pc_system.triggeredTimerParam();
  if (((BX_PIDE_THIS s.bmdma[channel].status & 0x01) == 0) ||
      (BX_PIDE_THIS s.bmdma[channel].prd_current == 0)) {
printf ("1 >>>>>>>> bx_pci_ide_c::timer()\n");
    return;
  }
  if (BX_PIDE_THIS s.bmdma[channel].cmd_rwcon &&
      !BX_PIDE_THIS s.bmdma[channel].data_ready) {
    printf ("2 >>>>>>>> bx_pci_ide_c::timer(): %d, %d\n",BX_PIDE_THIS s.bmdma[channel].cmd_rwcon,BX_PIDE_THIS s.bmdma[channel].data_ready);
    bx_pc_system.activate_timer(BX_PIDE_THIS s.bmdma[channel].timer_index, 1000, 0);
    return;
  }
  DEV_MEM_READ_PHYSICAL(BX_PIDE_THIS s.bmdma[channel].prd_current, 4, (Bit8u *)&prd.addr);
  DEV_MEM_READ_PHYSICAL(BX_PIDE_THIS s.bmdma[channel].prd_current+4, 4, (Bit8u *)&prd.size);
  size = prd.size & 0xfffe;
  if (size == 0) {
    size = 0x10000;
  }

I am still investigating what causes that, but with no luck till now. I f any one can figure it out please let me know.

Thanks,
Karim.
Post Reply