flat assembler
Message board for the users of flat assembler.

Index > OS Construction > ATA IRQ Delays

Author
Thread Post new topic Reply to topic
Hapkidoyoka



Joined: 03 Mar 2008
Posts: 13
Hapkidoyoka 16 Jun 2011, 19:37
I have a problem with interrupts not firing.
Installed ISR OK...
Selected the drive OK...
Cleared the nIEN bit OK...
Sent IDENTIFY (0xEC) to the drive OK...

But I get no interrupt unless I read the status (not alternate) register twice. As soon as those reads are done the IRQ fires.
This boils down to polling for completion just to fire the IRQ, which is pretty dumb.

Any ideas?

[edit]

I get the same behaviour with read sectors; if I poll for DRQ (alternate status) then read the status register twice - the IRQ fires.
Post 16 Jun 2011, 19:37
View user's profile Send private message Reply with quote
Dex4u



Joined: 08 Feb 2005
Posts: 1601
Location: web
Dex4u 16 Jun 2011, 21:01
Are you acknowledging the IRQ ?.
Eg:
Code:
  mov     al,0x20                                
     out     0xa0,al                                  
   out     0x20,al                                  
    
Post 16 Jun 2011, 21:01
View user's profile Send private message Reply with quote
Hapkidoyoka



Joined: 03 Mar 2008
Posts: 13
Hapkidoyoka 16 Jun 2011, 22:52
Thanks Dex.

Yes, I'm clearing the APIC EOI (not using PIC), but the ISR isn't firing at all; unless I read the status register twice after sending any commands.

If it helps, I'm in long mode and the problem ATA controller is operating in native mode (single IRQ 15, 2 channels). All other ISRs are running fine the IOAPIC sends all interrupts to the BSP only.

I have exhausted all the ATA specs/docs/forums I can find. Now I'm hoping someone who may have came across this before can help... please????
Post 16 Jun 2011, 22:52
View user's profile Send private message Reply with quote
Dex4u



Joined: 08 Feb 2005
Posts: 1601
Location: web
Dex4u 16 Jun 2011, 23:21
Post 16 Jun 2011, 23:21
View user's profile Send private message Reply with quote
Hapkidoyoka



Joined: 03 Mar 2008
Posts: 13
Hapkidoyoka 17 Jun 2011, 15:49
Yes Dex, but I did read it again carefully several times again after you suggested it.

I have stripped my init code down to something that should be testable and/or readable:
Code:
COMMAND_PORT equ $EC00
CONTROL_PORT equ $E880

TESTME:
     ;select drive
       mov     dx,COMMAND_PORT+6       ;drive/head
 shl     al,4
        mov     al,$A0          ;! or $E0?
  out     dx,al
;      mov     dx,CONTROL_PORT+2       ;alt status
;        times 4 in al,dx ;400ns delay
       ;clear nIEN
 mov     al,8
        out     dx,al
;      times 4 in al,dx ;400ns delay

   ;id drive
   xor     eax,eax
     mov     dx,COMMAND_PORT+5       ;cyl high
   out     dx,al
       dec     edx     ;cly low
    out     dx,al
       dec     edx     ;sector mumber
      out     dx,al
       dec     edx     ;sector count
       out     dx,al
       add     edx,5   ;back to COMMAND_PORT+7 command/status
      mov     al,$EC  ;IdentifyDrive
;debugstr "sending $EC%n"
       out     dx,al
       mov     dx,CONTROL_PORT+2       ;alt status
 times 5 in al,dx        ;400ns delay + read
;debugstr 5 "waiting%n"
    ;check return from id
       xor     ebx,ebx ;default HDD (atapiflag)
    inc     al
  cmp     al,1    ;bad if al=0 or al=1
;debugstr be "bad id return%n"
    jbe     .error
      ;wait for id to complete
@@:     in      al,dx
       test    al,$80 ;BSY
 jnz     @b
  test    al,$9 ;DRQ or ERR
   jz      @b
  test    al,$1 ;ERR
;debugstr z "ATA drive%n"
   jz      @f ;if no error, ok to proceed
      ;could be SATA/ATAPI
        mov     dx,COMMAND_PORT+7       ;cmd/status
 in      al,dx ;clear interrupt that didn't fire?
   sub     edx,2   ;cyl high
   in      al,dx
       shl     eax,8
       dec     edx     ;cyl low
    in      al,dx
       cmp     ax,$EB14        ;is it ATAPI?
       cmove   ebx,eax ;set something into ebx atapi flag
;debugstr e "ATAPI drive%n"
 je      @f
  cmp     ax,$C33C        ;is it SATA?
;debugstr e "SATA drive%n"
        jne     .error
      ;read data
@@:
;debugstr "reading data%n"
       mov     rdi,buff
    mov     ecx,256
     mov     dx,COMMAND_PORT ;data
       cld
 rep insw
    mov     dx,CONTROL_PORT+2       ;alt status
 times 4 in al,dx ;400ns
;debugstr "data read%n"

;debugstr "select drive as lba28%n"
   ;select drive as lba28
      mov     dx,COMMAND_PORT+6 ;drive/head
       shl     al,4
        mov     al,$E0  ;drive0 lba28 (block bits 24-27)
    out     dx,al
       ;fill in lba info to block 0
        mov     dx,COMMAND_PORT+1       ;write precomensation
       xor     eax,eax
     out     dx,al
       inc     eax
 inc     edx     ;sector count
       out     dx,al
       xor     eax,eax
     inc     edx     ;sector number (block bits 0-7)
     out     dx,al
       inc     edx     ;cyl low (block bits 8-15)
  out     dx,al
       inc     edx     ;cyl high (block bits 16-23)
        out     dx,al

;debugstr "wait for drive to RDY%n"

      mov     dx,CONTROL_PORT+2 ;alt status
@@:        in      al,dx
       test    al,$80 ;BSY
 jnz     @b
  test    al,$40 ;RDY
 jz      @b
  mov     eax,$20 ;ReadSectorsRetry
   mov     dx,COMMAND_PORT+7       ;cmd/status
;debugstr "sending read%n"
 out     dx,al
       mov     dx,CONTROL_PORT+2 ;alt status
       times 4 in al,dx ;400ns

;the ISR fires here, but reports status as D0 - which can't be a good thing Sad

;debugstr 5 "waiting%n"
     ;wait for completion
        mov     dx,CONTROL_PORT+2 ;alt status
.stillbusy:        in      al,dx
       test    al,$80 ;BSY
;debugstr nz "BSY%n"
       jnz     .stillbusy
  in      al,dx
       test    al,$8 ;DRQ
;debugstr z "not RDY%n"
     jz      .stillbusy

;debugstr "reading data%n"
      mov     rdi,buff2
   mov     ecx,256
     mov     dx,COMMAND_PORT ;data
       cld
 rep insw
    mov     dx,CONTROL_PORT+2       ;alt status
 ;times 4 in al,dx ;400ns
;debugstr 5 "data read%n"

 mov     dx,CONTROL_PORT+2
   in      al,dx
;debugstr "about to read status (asr=%b)",rax
    mov     dx,COMMAND_PORT+7
   in      al,dx

;the ISR fires here for some reason, status 0x50

;debugstr 2 " (sr=%b)%n",rax
 mov     dx,CONTROL_PORT+2
   in      al,dx
;debugstr "about to read status again (asr=%b)",rax
      mov     dx,COMMAND_PORT+7
   in      al,dx
;debugstr 2 " (sr=%b)%n",rax
;debugstr 2 "status reads done%n"

  mov     rsi,buff ;or esi if in 32 bit
       mov     rdi,buff2 ;or edi if in 32 bit
      movzx   eax, word [rdi+$1FE] ;$AA55
;debugstr 2 "test complete rsi=id rdi=mbr sig %w%n",rax
    ud2     ;use exception handler browse memory

.error:
     mov     dx,COMMAND_PORT+7 ;cmd/status
       in      al,dx ;clear interrupt
;debugstr "no drive?!%n"
        ud2

HunkAlignWORD
buff: rb 512
buff2: rb 512    
The ISR just reads from the status and replies to the APIC (I also have it printing out the value of the status reg).

When I send the ReadSectors the ISR fires and reports a value of 0xD0 in the status register.

It seems that I'm an interrupt behind, and sending the ReadSectors is firing the
interrupt that should have occured for the Identify; and the extra read status at the end is firing the interrupt for the ReadSectors?
Post 17 Jun 2011, 15:49
View user's profile Send private message Reply with quote
BAiC



Joined: 22 Mar 2011
Posts: 272
Location: California
BAiC 17 Jun 2011, 18:33
this might seem off topic, but you really should get the base address from a PCI Enumerator. I test my code on multiple emulators/hardware and they all have different base addresses for their respective devices.
Post 17 Jun 2011, 18:33
View user's profile Send private message Visit poster's website Reply with quote
Hapkidoyoka



Joined: 03 Mar 2008
Posts: 13
Hapkidoyoka 18 Jun 2011, 21:25
Agreed BAiC,

The numbers are from my systems PCI configuration space, and are correct.
I merely put them in here as equs for simplicity of posting.

The code posted is scratch written test code based on my current source; but it still exhibits the same behaviour.

It's doing my noodle in!
Post 18 Jun 2011, 21:25
View user's profile Send private message Reply with quote
Cptpingu



Joined: 21 Jun 2011
Posts: 1
Cptpingu 21 Jun 2011, 14:12
Not looking good!

Until an ATA expert comes along will have to stick with polling????!!!

I don't get how the controller/drive can be keeping hold of the interrupt until a status read (well two actually) occurs. That pretty much rules out (doesnt it) any possibility that the APIC or IOAPIC is in some weird state and holding back the IRQ.

Anyone have any idea if perhaps the drive was in LBA48 mode (shouldn't be we only use LBA28) I've seen docs that say about writing to ports twice to set arguments... Just wonder if the "twice" bit could be related?
Post 21 Jun 2011, 14:12
View user's profile Send private message Reply with quote
BAiC



Joined: 22 Mar 2011
Posts: 272
Location: California
BAiC 01 Jul 2011, 04:38
the Enumerator tells you what the interrupt vector is. I have multiple IDE controllers and my enumerator reports most of them having different IRQs.
Post 01 Jul 2011, 04:38
View user's profile Send private message Visit poster's website Reply with quote
BAiC



Joined: 22 Mar 2011
Posts: 272
Location: California
BAiC 01 Jul 2011, 05:33
it's possible you have to add 1 to the IRQ number you find in the Enumerator. I'm pretty sure I read it in one of the IDE specs.
Post 01 Jul 2011, 05:33
View user's profile Send private message Visit poster's website Reply with quote
Hapkidoyoka



Joined: 03 Mar 2008
Posts: 13
Hapkidoyoka 14 Jul 2011, 22:00
Sorry for the delay - been busy Sad

@BAiC: Thanks for the ideas, it always helps to reexamine the problem.

@Cptpingu: Not looking good indeed, if we can't solve this, we'll have to implement full polling for now and shelf it for later.


Firstly, just a point to confuse the issue...
The device is on IRQ 18, I had patched the APIC to forward it as IRQ 15 on the cpu (I did this gain parity with vmware for testing, but forgot about it).

Secondly, to hopefully clarify the issue...
I am getting an interrupt from the device - just not anytime useful (like, after reading the status); so I don't think the IRQ number is an issue.

So at the moment I have to poll until a command is complete, then read the status, and then the interrupt fires. Far from ideal, I think you'll agree.

Is there any reason why this should be so?
Post 14 Jul 2011, 22:00
View user's profile Send private message Reply with quote
BAiC



Joined: 22 Mar 2011
Posts: 272
Location: California
BAiC 22 Jul 2011, 12:07
I got the Interrupt Pin confused with the Interrupt Line documentation. the Pin is not valid when it's zero: INTA=1, INTB=2, etc. so nevermind that.

Code:
shl     al,4
mov     al,$A0          ;! or $E0?     

I didn't spot it earlier but perhaps the code is just wrong: the first instruction in this sequence can be removed without changing the result. perhaps you meant OR al, 0xA0 rather than mov? if AL is supposed to be part of the LBA then the shift left doesn't occur at all. LBA28 bits 24..27 are the lowest order bits of the device register not the highest.

I am currently entrenched in this area of development with Mathis and the driver I have for ATA does work. I'll upload my current version a little later.
Post 22 Jul 2011, 12:07
View user's profile Send private message Visit poster's website Reply with quote
cod3b453



Joined: 25 Aug 2004
Posts: 618
cod3b453 22 Jul 2011, 19:02
I only skimmed the code and don't particularly follow.

What device are you trying to access?
(Note that ATAPI requires a packet to be programmed to the device)

Assuming your command_port is actually the data port, I have port+2 down as the sector count register and port+0x206 as the alternate status; though this is for IDE controllers using ports 0x0170/0x01F0.
Post 22 Jul 2011, 19:02
View user's profile Send private message Reply with quote
BAiC



Joined: 22 Mar 2011
Posts: 272
Location: California
BAiC 22 Jul 2011, 23:36
so how do you know where an interrupt fires in the sequence of the code?
Post 22 Jul 2011, 23:36
View user's profile Send private message Visit poster's website Reply with quote
edfed



Joined: 20 Feb 2006
Posts: 4354
Location: Now
edfed 23 Jul 2011, 12:06
irqs can be launched by many different signals.

active low, active high, active edge, etc...
on apic, i don't have any idea about the possible variations, like maybe double impulse to fire one interrupt.

did you try to use the PIC instead of use the APIC?

it is simpler to use the pic first, and when you will have a working code around pic, you will have something ok to work with apic.
Post 23 Jul 2011, 12:06
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.