ATAPI interrupt problem

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.
Andrej
Posts: 19
Joined: Wed Jun 22, 2016 2:52 pm

ATAPI interrupt problem

Post by Andrej »

Hello,
I'm trying to load my kernel from an optical drive, but my reading function is not working. I'm trying to read a sector in PIO mode based on http://wiki.osdev.org/ATAPI. After the command byte string is sent to the drive as 6 words it doesn't sends any interrupts at all. Interrupt lines 14 and 15 are enabled in the PIC (i'm able to read keyboard interrupts).
First step is detecting the device by reading its registers 0x1F2-0x1F5. I get 0x53, 0xAA, 0x00, 0x08 as a result instead of 0x01, 0x01, 0x14, 0xEB.
My code what does the detection:

Code: Select all

.global detecting_packet_device
.type detecting_packet_device, @function
detecting_packet_device:
    push    %ebp
    mov     %esp,%ebp
    #call    reset_atapi
    inbyte  $ATA_SECTOR_COUNT
    and     $0xff,%eax
    push    %eax
    call    printreg
    push    $10
    call    printchar
    inbyte  $ATA_ADDRESS1
    and     $0xff,%eax
    push    %eax
    call    printreg
    push    $10
    call    printchar
    inbyte  $ATA_ADDRESS2
    and     $0xff,%eax
    push    %eax
    call    printreg
    push    $10
    call    printchar
    inbyte  $ATA_ADDRESS3
    and     $0xff,%eax
    push    %eax
    call    printreg
    push    $10
    call    printchar
    mov     %ebp,%esp
    pop     %ebp
    ret
I do not reset the device before calling this function, I just leave it the way BIOS left it there.
My reading function (The part where the actual read would happen is not implemented now, when it was there it was just reading full 0s):
The function should read sector 0x10 - the firs non 0 sector.

Code: Select all

.equ ATA_DATA, 0x1F0
.equ ATA_FEATURES, 0x1F1
.equ ATA_SECTOR_COUNT, 0x1F2
.equ ATA_ADDRESS1, 0x1F3
.equ ATA_ADDRESS2, 0x1F4
.equ ATA_ADDRESS3, 0x1F5
.equ ATA_DRIVE_SELECT, 0x1F6
.equ ATA_COMMAND, 0x1F7
.equ ATA_DCR, 0x3F6

.global read_atapi
.type read_atapi, @function
read_atapi:
    push    %ebp
    push    %ebx
    mov     %esp,%ebp
    #ATA_DRIVE_SLAVE & (1<<4) // 0xB0&(1<<4)
    outbyte $0x10, $ATA_DRIVE_SELECT
    wait_400_ns
    outbyte $0x0, $ATA_FEATURES
    #atapi sector size (2048) & 0xff
    outbyte $0x0, $ATA_ADDRESS2
    #atapi sector size (2048) >> 8
    outbyte $0x8, $ATA_ADDRESS3
    #ATA packet command
    outbyte $0xA0, $ATA_COMMAND
    wait_while_ide_is_busy:
        inbyte $ATA_COMMAND
        andb    $0x80, %al
        jnz     wait_while_ide_is_busy
    xor     %eax, %eax
    #while (!((status = inb (ATA_COMMAND (bus))) & 0x8) && !(status & 0x1))
    wait_ide_to_be_ready:
        inbyte $ATA_COMMAND
        mov     %eax, %ebx
        andb    $0x08, %bl
        notb    %bl
        andb    $0x01, %al
        notb    %al
        cmpb    $0x0, %bl
        jz      wait_ide_to_be_ready
        cmpb    $0x0, %al
        jz      wait_ide_to_be_ready
    outword $0xa800, $ATA_DATA
    outword $0x0000, $ATA_DATA
    outword $0x0010, $ATA_DATA
    outword $0x0000, $ATA_DATA
    outword $0x0001, $ATA_DATA
    outword $0x0000, $ATA_DATA
    #jmp     .
    #inbyte  $ATA_ADDRESS2
    #inbyte  $ATA_ADDRESS3
    mov     %ebp,%esp
    pop     %ebx
    pop     %ebp
    ret
The interrupt handler looks like:

Code: Select all

.global StartOfIDT
StartOfIDT:
    .rep 0x31
    .word   my_general_interrupt_handler
    .word   8
    .byte   0
    .byte   0b10001111
    .word   0
    .endr

    .word   keyboard_interrupt
    .word   8
    .byte   0
    .byte   0b10001110
    .word   0

    .rep 12
    .word   my_general_interrupt_handler
    .word   8
    .byte   0
    .byte   0b10001111
    .word   0
    .endr

    .word   ide_interrupt_1
    .word   8
    .byte   0
    .byte   0b10001110
    .word   0

    .word   ide_interrupt_1
    .word   8
    .byte   0
    .byte   0b10001110
    .word   0
IDTend:

.global IDTdescriptor
IDTdescriptor:
    .word 2047
    .int    StartOfIDT

.equ MASTER_PIC_COMMAND, 0x20
.equ MASTER_PIC_DATA, 0x21
.equ SLAVE_PIC_COMMAND, 0xa0
.equ SLAVE_PIC_DATA, 0xa1

.global Init_interrupts
.type Init_interrupts, @function
Init_interrupts:
    pusha
    lidt    (IDTdescriptor)
    xor     %ecx,%ecx
    #starting the initialization
    mov     $MASTER_PIC_COMMAND,%dx
    mov     $0x11,%al
    out     %al,%dx
    call	iowait
    mov     $SLAVE_PIC_COMMAND,%dx
    mov     $0x11,%al
    out     %al,%dx
    call	iowait
    #providing the offset for the irqs
    mov     $0x30,%al
    mov     $MASTER_PIC_DATA,%dx
    out     %al,%dx
    call	iowait
    mov     $SLAVE_PIC_DATA,%dx
    mov     $0x38,%al
    out     %al,%dx
    call	iowait
    #tell Master PIC that there is a slave PIC at IRQ2 (0000 0100)
    mov     $4,%al
    mov     $MASTER_PIC_DATA,%dx
    out     %al,%dx
    call	iowait
    #tell Slave PIC its cascade identity (0000 0010)
    mov     $2,%al
    mov     $SLAVE_PIC_DATA,%dx
    out     %al,%dx
    call	iowait
    #set up 8086/88 (MCS-80/85) mode for master and slave also
    mov     $MASTER_PIC_DATA,%dx
    mov     $0x01,%al
    out     %al,%dx
    call	iowait
    mov     $SLAVE_PIC_DATA,%dx
    mov     $0x01,%al
    out     %al,%dx
    #masking the interrupts
    #set requiered bit 0 to enable an interrupt
    call	iowait
    mov     $0xf9,%al
    mov     $MASTER_PIC_DATA,%dx
    out     %al,%dx
    mov     $0x3f,%al
    mov     $SLAVE_PIC_DATA,%dx
    out     %al,%dx
    popa
    sti
    ret
My hypervisor is VirtualBox 4.3.34_Ubuntu r104062. The compiler is gcc 4.8.4 for Ubuntu.

Thank You in advance,
Andrej
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: ATAPI interrupt problem

Post by Combuster »

Did you enable IRQ 2 as well? Otherwise the second daisychained PIC is ignored and IRQs 8-15 won't arrive regardless of their individual state.
"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 ]
Andrej
Posts: 19
Joined: Wed Jun 22, 2016 2:52 pm

Re: ATAPI interrupt problem

Post by Andrej »

Yes, IRQ2 is enabled.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: ATAPI interrupt problem

Post by BenLunt »

Without looking through your code, (sorry, the AS style just looks backward to me), I have a few suggestions.

First, we recently discussed this subject in the following thread:
http://forum.osdev.org/viewtopic.php?f=1&t=30444

Next:
How modern is your test computer? It may require you to enable the PCI device before you read and write to ports 0x1Fx. Most of the time, the BIOS should have done this for you in case, on a rare occasion now-a-days, you have a non-PCI aware OS. However, you can no longer assume this. You may need to enumerate the PCI, find each ATA controller, initialize, then start your ATA detection code. On that note, it may be an SATA controller working in ATA mode.

Another thing is that maybe there is not a controller at 0x1Fx. Maybe the hardware no longer assumes non-PCI aware platforms and requires you to enumerate the PCI to retrieve the ATA devices.

The PCI ATA specs also state that you must test the Programing Interface byte to see if the controller is in Compatibility mode or Native Mode.

Just checking the registers for certain values is not considered enough to detect ATAPI devices. The thread I mentioned above, has details on how you should detect for ATAPI.

Read through the thread above and see if that helps. My book listed below describes in detail all of this process, detecting the PCI and all ATA(PI)/SATA devices on the PCI Bus, device detection, and reading and writing devices, as well as using AHCI SATA devices.

Ben
FYSOS: Media Storage Devices
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: ATAPI interrupt problem

Post by BenLunt »

I'm just curious to see if you solved this problem or not. How are you doing with it?
Andrej
Posts: 19
Joined: Wed Jun 22, 2016 2:52 pm

Re: ATAPI interrupt problem

Post by Andrej »

Hi,
The first interrupt is arriving now. The actual read is not done yet (I had no time to deal with it...). Based on your comments from the topic you linked earlier I managed to detect the packet device, the ports 0x1F2-0x1F5 are filled with the 0x01, 0x01, 0x14, 0xEB values. The interrupt was probably not raised because my boot sector was bigger than a sector.
Thank You for your help.
Andrej
Posts: 19
Joined: Wed Jun 22, 2016 2:52 pm

Re: ATAPI interrupt problem

Post by Andrej »

Hi,
It looks like the actual data reading is not working. After the command packet is sent the first interrupt is raised, then the device gives back, that I should get 2048 byte of data, but when I'm reading the ATA_DATA (0x1F0) port I only get 0xFFFF. Bit 3 is set in the 2nd byte of the command packet. The device is using 12 byte command packets. The CD-DVD drive is attached to an IDE controller in VirutalBox.

Code: Select all

    outword $0xa804, $ATA_DATA
    outword $0x0000, $ATA_DATA
    outword $0x0010, $ATA_DATA
    outword $0x0000, $ATA_DATA
    outword $0x0001, $ATA_DATA
    outword $0x0000, $ATA_DATA
    call    schedule
    inbyte  $ATA_ADDRESS3
    inbyte  $ATA_ADDRESS2
    /*gives back 0x800; one sector is read*/
    movl    $256, %ecx
    mov     buffer(%ebp),%ebx
    read_the_sector:
        inword      $ATA_DATA
        push        %eax
        call        printreg
        loop        read_the_sector
The inword macro looks like:

Code: Select all

.macro inword from
    movw    \from, %dx
    inw     %dx, %ax
.endm
Thank You in advance,
Andrej
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: ATAPI interrupt problem

Post by BenLunt »

Andrej wrote:

Code: Select all

    outword $0xa804, $ATA_DATA
    outword $0x0000, $ATA_DATA
    outword $0x0010, $ATA_DATA
    outword $0x0000, $ATA_DATA
    outword $0x0001, $ATA_DATA
    outword $0x0000, $ATA_DATA
The problem is that you are not sending the packet correctly. (I am assuming) outword will send 0x04 then 0xA8 instead of 0xA8 then 0x04 because you are working on a little-endian machine, yes?

Either swap the word to 0x04A8 (and the other 5 words) or send as 12 bytes instead of 6 words.

Ben
Andrej
Posts: 19
Joined: Wed Jun 22, 2016 2:52 pm

Re: ATAPI interrupt problem

Post by Andrej »

Hi,
Swapping the bytes in the words actually helped a bit. I can read the data, but I have some concerns. By http://wiki.osdev.org/ATAPI the byte 9 in the command packet indicates how many sectors needs to be read. Regardless what value I provide there I get back 0x800 from the ATA_ADDRESS2(0x1F4) and ATA_ADDRESS3(0x1F5). My other concern is that in the sample code found under the link mentioned above says that when the drive is done with all data transfers should raise an interrupt. I do not get the interrupt.
In my interrupt handler I'm just setting a global variable (maybe not the most elegant way) to be able to wait for the irq arriving and notifying the PIC about the end of interrupt.
The function which tells the PIC that the interrupt is ended looks like:

Code: Select all

end_interrupt:
    mov     $0xa0,%dx
    mov     $0x20,%al
    out     %al,%dx
    mov     $0x20,%al
    mov     $0x20,%Dx
    out     %al,%dx
    ret
The outword (mentioned in earlier posts) looks like:

Code: Select all

.macro outword what, where
    movw    \where, %dx
    movw    \what, %ax
    outw    %ax, %dx
.endm
Br,
Andrej
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: ATAPI interrupt problem

Post by BenLunt »

Andrej wrote:Swapping the bytes in the words actually helped a bit. I can read the data, but I have some concerns. By http://wiki.osdev.org/ATAPI the byte 9 in the command packet indicates how many sectors needs to be read. Regardless what value I provide there I get back 0x800 from the ATA_ADDRESS2(0x1F4) and ATA_ADDRESS3(0x1F5).
You should receive an interrupt after every sector read, no matter how many you intend to read. Once you receive the interrupt, you need to acknowledge the interrupt both at the PIC and the ATA controller by reading the ATA_STATUS register.

As I mentioned in the other thread, you need to get the Identify block from the device. It will then tell you if you have a 12-byte or 16-byte packet and the type of packet to send. Most likely you have a MMC-5 type packet device (http://www.13thmonkey.org/documentation ... c5r02c.pdf, section 6.16, page (pdf:436) (doc:380))

The CMD uses bytes 6,7,8, and 9 as the length for command 0xA8.

I don't mean to get you down, but this is the biggest mistake most people make when it comes time to read from an ATAPI device (CDROM). You have to start out reading the Identify block, detecting if ATA or ATAPI, then if ATAPI, getting the ATAPI Identify Block. Then you can see what size command block to send *and* what type of command block to use. Then and only then can you start to read from the device. Most people want to start out reading, but it doesn't work out that way. Sorry.

Read the other thread a little more closely and get the IDENTIFY block. IIRC, I also show some code there too. Or was that a different thread?

Hope this helps,
Ben
Andrej
Posts: 19
Joined: Wed Jun 22, 2016 2:52 pm

Re: ATAPI interrupt problem

Post by Andrej »

Hello,
I'm actually reading the identify block and checking if its really an atapi drive and the length of the command packet (nothing else so far). Reading the status byte helped, now the interrupts are arriving after the sector has been read.
But I have still one problem. Doesn't matters what numbers I put to the bytes 6 - 9 of the A8 commands packet I get back 0x800 as the amount of data that will be transferred. When 0 is written to those bytes the device doesn't gives back any data as it's suspected. But the result is still 0x800. After sending the command packet I should get back from the ports 0x1F4 and 0x1F5 how many bytes the device should transfer me. Or am I wrong? Am I missing something?

Best Regards,
Andrej
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: ATAPI interrupt problem

Post by BenLunt »

This is by design. You can only transfer full sectors. You can read or write a partial sector.

You may stop reading in words from the data register, but the drive will still expect you to read them all, until a reset.

As for writing, you really need to send the whole sector. Who knows what will happen if you only send half of the data. For some reason, the drive might decide that it needs to write to a different physical sector (though as far as you are concerned its the same sector), and if you only write half the data, the other half may already be something else.

You must read and write full sectors...

Ben
Andrej
Posts: 19
Joined: Wed Jun 22, 2016 2:52 pm

Re: ATAPI interrupt problem

Post by Andrej »

Hello,
What happens before my reading function is just BIOS boot from this drive, and I read the 256 word long identify block. I tried to reset the drive with the following code, but no use.

Code: Select all

.equ ATA_DCR, 0x3F6
    outbyte $0x3, $ATA_DCR
    wait_400_ns
    outbyte $0x0, $ATA_DCR
    wait_400_ns
    inbyte  $ATA_COMMAND
wait_400_ns looks like:

Code: Select all

.equ ATA_DRIVE_SELECT, 0x1F6
.macro wait_400_ns
    push    %edx
    push    %eax
    movw    $ATA_DRIVE_SELECT,%dx
    inb     %dx,%al
    inb     %dx,%al
    inb     %dx,%al
    inb     %dx,%al
    pop     %eax
    pop     %edx
.endm
Thank you in advance,
Andrej
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: ATAPI interrupt problem

Post by BenLunt »

Andrej wrote:

Code: Select all

.equ ATA_DCR, 0x3F6
    outbyte $0x3, $ATA_DCR
    wait_400_ns
    outbyte $0x0, $ATA_DCR
    wait_400_ns
    inbyte  $ATA_COMMAND
The reset bit is bit 2, the third bit in. You are writing 00000011b, setting bit 0 and 1, not bit 2.

Also, you should delay at least 2,400nS (2uS + 400nS) whereas the specs say to way 5,000nS before clearing bit 2, then after you should wait 2mS more. (That's 2 milliseconds, 2,000 uS, or 2,000,000nS)
Ben
Andrej
Posts: 19
Joined: Wed Jun 22, 2016 2:52 pm

Re: ATAPI interrupt problem

Post by Andrej »

Hello,
Now I'm setting bit 2 in ATA_DCR and waiting at least 10 microsec (I'm not using timer interrupt) then clearing bit 2, then waiting about 5 milliseconds. The problem is before the reset the ATA status register contains 0x50, but after reset it contains 0x00.

Best Regards,
Andrej
Post Reply