ATA/ATAPI - reading disc problem and GPF on write

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
kubawal
Member
Member
Posts: 26
Joined: Thu Mar 06, 2014 9:14 am

ATA/ATAPI - reading disc problem and GPF on write

Post by kubawal »

I'm writing ATA/ATAPI hard drive driver with this tutorial: http://wiki.osdev.org/PCI_IDE_Controller
Detecting of discs works perfectly but I have a lot of problems while acessing to it.

Code: Select all

BYTE ataAcess(ataDevice& dev, int direction, DWORD lba, BYTE sects, WORD sel, DWORD offset)
{
    unsigned char lba_mode /* 0: CHS, 1:LBA28, 2: LBA48 */, dma /* 0: No DMA, 1: DMA */, cmd;
    unsigned char lba_io[6];
    ataChannel& channel = contr[dev.controllerId].chann[dev.channelId]; // ataChannel is structure with port base, bmide base...
    unsigned int  words      = 256;
    unsigned short cyl, i;
    unsigned char head, sect, err;
    
    // turn off IRQ
    writeReg(channel, ataREG_CONTROL, channel.nIEN = (ide_irq_invoked = 0x0) + 0x02);
    
    // choose adressing and params
    if (lba >= 0x10000000) 
    { 
        // LBA48:
        lba_mode  = 2;
        lba_io[0] = (lba & 0x000000FF) >> 0;
        lba_io[1] = (lba & 0x0000FF00) >> 8;
        lba_io[2] = (lba & 0x00FF0000) >> 16;
        lba_io[3] = (lba & 0xFF000000) >> 24;
        lba_io[4] = 0; 
        lba_io[5] = 0;
        head      = 0;
    } 
    else if (dev.capabilities & 0x200)
    {
        // LBA28:
        lba_mode  = 1;
        lba_io[0] = (lba & 0x00000FF) >> 0;
        lba_io[1] = (lba & 0x000FF00) >> 8;
        lba_io[2] = (lba & 0x0FF0000) >> 16;
        lba_io[3] = 0; // 
        lba_io[4] = 0; // 
        lba_io[5] = 0; // 
        head      = (lba & 0xF000000) >> 24;
    } 
    else 
    {
        // CHS:
        lba_mode  = 0;
        sect      = (lba % 63) + 1;
        cyl       = (lba + 1  - sect) / (16 * 63);
        lba_io[0] = sect;
        lba_io[1] = (cyl >> 0) & 0xFF;
        lba_io[2] = (cyl >> 8) & 0xFF;
        lba_io[3] = 0;
        lba_io[4] = 0;
        lba_io[5] = 0;
        head      = (lba + 1  - sect) % (16 * 63) / (63);
    }
    
    // NO DMA
    dma = 0;
    
    // wait until disc is ready
    while (readReg(channel, ataREG_STATUS) & ataSR_BSY)
        ;
    
    // choose disc
    if (lba_mode == 0)
        writeReg(channel, ataREG_HDDEVSEL, 0xA0 | (dev.id << 4) | head); // Drive & CHS.
    else
        writeReg(channel, ataREG_HDDEVSEL, 0xE0 | (dev.id << 4) | head); // Drive & LBA
    
    // set params
    if (lba_mode == 2) 
    {
        writeReg(channel, ataREG_SECCOUNT1,   0);
        writeReg(channel, ataREG_LBA3,   lba_io[3]);
        writeReg(channel, ataREG_LBA4,   lba_io[4]);
        writeReg(channel, ataREG_LBA5,   lba_io[5]);
    }
    writeReg(channel, ataREG_SECCOUNT0,   sects);
    writeReg(channel, ataREG_LBA0,   lba_io[0]);
    writeReg(channel, ataREG_LBA1,   lba_io[1]);
    writeReg(channel, ataREG_LBA2,   lba_io[2]);
    
    // choose right command
    if (lba_mode == 0 && dma == 0 && direction == 0) cmd = ataCMD_READ_PIO;
    if (lba_mode == 1 && dma == 0 && direction == 0) cmd = ataCMD_READ_PIO;
    if (lba_mode == 2 && dma == 0 && direction == 0) cmd = ataCMD_READ_PIO_EXT;
    if (lba_mode == 0 && dma == 1 && direction == 0) cmd = ataCMD_READ_DMA;
    if (lba_mode == 1 && dma == 1 && direction == 0) cmd = ataCMD_READ_DMA;
    if (lba_mode == 2 && dma == 1 && direction == 0) cmd = ataCMD_READ_DMA_EXT;
    if (lba_mode == 0 && dma == 0 && direction == 1) cmd = ataCMD_WRITE_PIO;
    if (lba_mode == 1 && dma == 0 && direction == 1) cmd = ataCMD_WRITE_PIO;
    if (lba_mode == 2 && dma == 0 && direction == 1) cmd = ataCMD_WRITE_PIO_EXT;
    if (lba_mode == 0 && dma == 1 && direction == 1) cmd = ataCMD_WRITE_DMA;
    if (lba_mode == 1 && dma == 1 && direction == 1) cmd = ataCMD_WRITE_DMA;
    if (lba_mode == 2 && dma == 1 && direction == 1) cmd = ataCMD_WRITE_DMA_EXT;
    writeReg(channel, ataREG_COMMAND, cmd); // ... and send it
       
    if (dma)
        // NO DMA
        if (direction == 0);
            // DMA Read.
        else;
            // DMA Write.
    else
        if (direction == 0)
            // reading
            for (i = 0; i < sects; i++)
            {
                if (err = readPolling(channel, 1))
                    return err; // error
                
                // receive data
                asm("pushw %ds");
                asm("mov %%ax, %%ds" : : "a"(sel));
                asm("rep insw" : : "c"(words), "d"(channel.base), "D"(offset));
                asm("popw %ds");
                offset += (words*2);
            } 
        else 
        {
            // write
            for (i = 0; i < sects; i++)
            {
                readPolling(channel, 0);
                
                // send data
                asm("pushw %ds");
                asm("mov %%ax, %%ds" : : "a"(sel));
		asm("rep outsw" : : "c"(words), "d"(channel.base), "S"(offset));
                asm("popw %ds");
                offset += (words*2);
            }
            
            // cache flush
            writeReg(channel, ataREG_COMMAND, (BYTE []) { ataCMD_CACHE_FLUSH,
                                                           ataCMD_CACHE_FLUSH,
                                                           ataCMD_CACHE_FLUSH_EXT}[lba_mode]);
            readPolling(channel, 0); // Polling.
        } 
    return 0;
}
And here is my problem: reading works with no error but it read only 0's.
And writing... it causes Global Protection Fault. I readed in ATA PIO article on wiki, that I shoudn't use rep outsw, maybe this is reason?

Thanks for help!
Octocontrabass
Member
Member
Posts: 5590
Joined: Mon Mar 25, 2013 7:01 pm

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by Octocontrabass »

kubawal wrote:I'm writing ATA/ATAPI hard drive driver with this tutorial: http://wiki.osdev.org/PCI_IDE_Controller
the tutorial page wrote:The factual accuracy of this article or section is disputed.
You are having issues because the examples on that page are wrong. (Inline assembly should never look like that!)
kubawal
Member
Member
Posts: 26
Joined: Thu Mar 06, 2014 9:14 am

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by kubawal »

What's wrong in it? In ATA PIO Mode article asm reading code looks like my inline assambly (rep insw too).
So what instruction I should use? Can you explain me why that inline asm is wrong?
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by Combuster »

ATA PIO mode doesn't have inline assembly.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
kubawal
Member
Member
Posts: 26
Joined: Thu Mar 06, 2014 9:14 am

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by kubawal »

Aaa.. GAS has other syntax :D
So it should look like this?

Code: Select all

asm("pushw %es");
                asm("movb %%es, %%ax" : : "a"(sel));
                asm("rep insw" : : "c"(words), "d"(channel.base), "D"(offset));
                asm("popw %es");
and

Code: Select all

asm("pushw %ds");
                asm("movb %%ds, %%ax" : : "a"(sel));
				asm("rep outsw" : : "c"(words), "d"(channel.base), "S"(offset));
                asm("popw %ds");
/edit:
Yeah, I tried it and it works without errors. But it still didn't read and write properly (I modified virtual disc file with some random symbols and it still read only 0's)

Maybe bug is in calling code?

Code: Select all

// gdtKERNEL_DATA_SEG = 0x10
ataAcess(dev, 0, 0, 1, gdtKERNEL_DATA_SEG, (DWORD) (&buf)); // read bootsector to buf

txtWriteString("[Driv/ata][info]1 sector:\n");

//debug
for(WORD i = 0; i < ataSECTORSIZE; i++)
{
	if(buf[i] != 0x00)
//write if != 0
	{
	    itoa(buf[i], numbuf);
	    txtWriteString(numbuf);
	}
}
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by Combuster »

it works without errors. But it still didn't read and write properly
There's at least one lie in there :wink:

Regardless, it is the compiler's task is to optimise that inline assembly of yours and turn it into the following as the best alternative:

Code: Select all

 
Before you wonder, that is an empty code block. Which serves as a reminder not to steal code that is marked as wrong if you don't know what you're doing.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
kubawal
Member
Member
Posts: 26
Joined: Thu Mar 06, 2014 9:14 am

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by kubawal »

Should I use asm volatile ? I don't know what are you going for. :?
User avatar
sortie
Member
Member
Posts: 931
Joined: Wed Mar 21, 2012 3:01 pm
Libera.chat IRC: sortie

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by sortie »

I notice your above code doesn't have balanced {} in one location and the if else blocks gets parsed wrong.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by gerryg400 »

kubawal, disassemble your code with objdump. Find the area of code where the inline asm is and you will see the problem.
If a trainstation is where trains stop, what is a workstation ?
kubawal
Member
Member
Posts: 26
Joined: Thu Mar 06, 2014 9:14 am

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by kubawal »

I read Inline assembly article and I changed my code:

Code: Select all

#define ASM __asm__ __volatile__
#define MEMORY :"memory"
ASM("pushw %%es" : : MEMORY);
                ASM("mov %%ax, %%es" : : "a"(sel) MEMORY);
                ASM("rep insw" : : "c"(words), "d"(channel.base), "D"(offset) MEMORY);
                ASM("popw %%es" : : MEMORY);

Code: Select all

ASM("pushw %%ds":: MEMORY);
                ASM("mov %%ax, %%ds" : : "a"(sel) MEMORY);
				ASM("rep outsw" :: "c"(words), "d"(channel.base), "S"(offset) MEMORY);
                ASM("popw %%ds":: MEMORY);
objdump disassembly for this lines:

Code: Select all

    b96:	66 1e                	pushw  %ds
     b98:	0f b7 44 24 1a       	movzwl 0x1a(%esp),%eax
     b9d:	8e d8                	mov    %eax,%ds
     b9f:	b9 00 01 00 00       	mov    $0x100,%ecx
     ba4:	0f b7 55 02          	movzwl 0x2(%ebp),%edx
     ba8:	66 f3 6f             	rep outsw %ds:(%esi),(%dx)
     bab:	66 1f                	popw   %ds
and

Code: Select all

     a0c:	66 06                	pushw  %es
     a0e:	89 f0                	mov    %esi,%eax
     a10:	8e d8                	mov    %eax,%ds
     a12:	b9 00 01 00 00       	mov    $0x100,%ecx
     a17:	0f b7 55 02          	movzwl 0x2(%ebp),%edx
     a1b:	66 f3 6d             	rep insw (%dx),%es:(%edi)
     a1e:	66 07                	popw   %es
And now, it still causes GPF. What I'm doing wrong?

/edit: And added {}'s. Nothing changed.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by gerryg400 »

Code: Select all

    b96:   66 1e                   pushw  %ds
     b98:   0f b7 44 24 1a          movzwl 0x1a(%esp),%eax
Tihs may be one of your problems. You have pushed onto the stack. The very next instruction the compiler tries to get a variable from the stack. It doesn't know that your have changed the stack pointer.
If a trainstation is where trains stop, what is a workstation ?
User avatar
iansjack
Member
Member
Posts: 4707
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by iansjack »

It would be much safer to separate the assembler routines into external functions rather than using inline assembler. Playing fast and loose with the stack and the segment registers within a C function would seem to be a very efficient way of screwing things up.
kubawal
Member
Member
Posts: 26
Joined: Thu Mar 06, 2014 9:14 am

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by kubawal »

I wrote these routines in ASM. But it STILL causes GPF.
Bochs gives me this error msg:

Code: Select all

fetch_raw_descriptor: GDT: index (6107) c20 > limit (17)
But, as selector i use normally 0x10.

Code: Select all

SECTION .text

GLOBAL ataRepOutsw
ataRepOutsw:
	push ebp
	mov ebp, esp
	push ecx
	push edx
	push esi
	push ds
	
	mov ecx, [ebp + 8] ; words
	mov edx, [ebp + 10] ; base
	mov esi, [ebp + 12] ; offset
	mov ds, [ebp + 16] ; selector
	
	rep outsw
	
	pop ds
	pop esi
	pop edx
	pop ecx
	pop ebp
	ret
	
GLOBAL ataRepInsw
ataRepInsw:
	push ebp
	mov ebp, esp
	push ecx
	push edx
	push edi
	push es	
	
	mov ecx, [ebp + 8] ; words
	mov edx, [ebp + 10] ; base
	mov edi, [ebp + 12] ; offset
	mov es, [ebp + 16] ; selector
	
	rep insw
	
	pop es
	pop edi
	pop edx
	pop ecx
	pop ebp
	ret
And in c++:

Code: Select all

extern "C" void __attribute__((__cdecl__)) ataRepInsw(WORD words, WORD port, DWORD offset, WORD selector);
extern "C" void __attribute__((__cdecl__)) ataRepOutsw(WORD words, WORD port, DWORD offset, WORD selector);

//...

ataRepInsw(words, channel.base, offset, sel);

//...

ataRepOutsw(words, channel.base, offset, sel);
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: ATA/ATAPI - reading disc problem and GPF on write

Post by Combuster »

Let me tell you one thing: I can keep giving hints, but at the current rate pointing out individual errors only makes it an exercise of solving your problem for you.

For one, you obviously have not run this through a debugger. You can check where the 6107 comes from and where the correct value is. That'd
1) teach you how the ABI really works.
2) teach you what register widths are
3) saves you from wasting time waiting for an answer.

The cargo cult treatment of ES makes it obvious you still have no idea what that code has to be doing.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Post Reply