OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 4:25 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: AHCI Port COMRESET ( & How to handle PortConnectChange INT)
PostPosted: Wed Jul 20, 2022 1:21 pm 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
Hello. I was struggling with my AHCI Driver, I made it work on QEMU and VBOX But on my 2 computers, the Port (COM)Reset is stuck on PORT_CONNECT_STATUS_CHANGE Interrupt and no D2H Is sent.

So how can I handle PORT_CONNECT_STATUS_CHANGE Event And how to do a COMRESET.

The interrupt handling works well, I can ready data from Real Hardware if I do not COMRESET (But I cannot know which of the ports is ATAPI and the ATAPI Port on my laptop doesn't respond (I already know it is ATAPI))

Here is the code of the COMRESET :

Code:
HbaPort->CommandStatus.Start = 0;
    HbaPort->CommandStatus.FisReceiveEnable = 0;
    while(HbaPort->CommandStatus.CurrentCommandSlot || HbaPort->CommandStatus.FisReceiveRunning);

    Port->FirstD2h = 0;

    // Some code allocating Received FIS & Command List ...

Port->Port->InterruptEnable.D2HRegisterFisInterruptEnable = 1;
    Port->Port->InterruptEnable.PioSetupFisInterruptEnable = 1;
    Port->Port->InterruptEnable.DmaSetupFisInterruptEnable = 1;
    Port->Port->InterruptEnable.SetDeviceBitsFisInterruptEnable = 1;
    Port->Port->InterruptEnable.UnknownFisInterruptEnable = 1;
    Port->Port->InterruptEnable.DescriptorProcessedInterruptEnable = 1;
    Port->Port->InterruptEnable.PortChangeInterruptEnable = 1;
    Port->Port->InterruptEnable.DeviceMechanicalPresenceEnable = 1;
    Port->Port->InterruptEnable.PhyRdyChangeInterruptEnable = 1;
    Port->Port->InterruptEnable.IncorrectPortMultiplierEnable = 1;
    Port->Port->InterruptEnable.OverflowEnable = 1;
    Port->Port->InterruptEnable.InterfaceNonFatalErrorEnable = 1;
    Port->Port->InterruptEnable.InterfaceFatalErrorEnable = 1;
    Port->Port->InterruptEnable.HostBusDataErrorEnable = 1;
    Port->Port->InterruptEnable.HostBusFatalErrorEnable = 1;
    Port->Port->InterruptEnable.TaskFileErrorEnable = 1;
    Port->Port->InterruptEnable.ColdPresenceDetectEnable = 1;

    wr32(&Port->Port->SataError, -1); // Static function I made
    Port->Port->CommandStatus.FisReceiveEnable = 1;
    while(!Port->Port->CommandStatus.FisReceiveRunning);

    BOOL DeviceDetected = 0;
    if(Port->Ahci->Hba->HostCapabilities.SupportsStaggeredSpinup) {
        Port->Port->CommandStatus.SpinupDevice = 1;
    }
        Port->Port->SataControl.DeviceDetectionInitialization = 1;
        Sleep(3);
        Port->Port->SataControl.DeviceDetectionInitialization = 0;

    UINT Countdown = 20;
    for(;;) {
        // if(Port->FirstD2h && !HbaPort->SataStatus.DeviceDetection) break;
        if(!Countdown) break;
        Countdown--;
        if(HbaPort->SataStatus.DeviceDetection == 3) {
            if(Port->Ahci->Hba->HostCapabilities.SupportsStaggeredSpinup) {
            }
            DeviceDetected = 1;
            break;
        }
        Sleep(1);
    }




    if(!DeviceDetected) {
        if(Port->Ahci->Hba->HostCapabilities.SupportsStaggeredSpinup) {
            Port->Port->CommandStatus.SpinupDevice = 0;
        }
        Port->DeviceDetected = 0;
        return;
    } else {
        Port->DeviceDetected = 1;
        // SystemDebugPrint(L"ATTACHED_DEVICE_ON_PORT");
    }
     // wait for first D2H FIS (Containing device signature)
    Countdown = 25;
    while(!Port->FirstD2h) {
        Countdown--;
        if(!Countdown) {
            if(Port->Port->PortSignature.SectorCount == 0x101) break; // Port signnature received but no interrupt delivered
            Port->DeviceDetected = 0;
            return;
        }
        Sleep(1);
    }



    *(DWORD*)&Port->Port->SataError = -1; // Clear SATA_ERROR

    if(*(DWORD*)&HbaPort->PortSignature == 0xEB140101) {
        HbaPort->CommandStatus.DeviceIsAtapi = 1;
    }


    // Wait for device initialization (transfer of initial FIS & Device Signature...)
   
    SystemDebugPrint(L"Port Signature : %x", HbaPort->PortSignature);

    Port->Port->CommandStatus.FisReceiveEnable = 1;
    Port->Port->CommandStatus.Start = 1;




Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Wed Jul 20, 2022 4:23 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
devc1 wrote:
Hello. I was struggling with my AHCI Driver, I made it work on QEMU and VBOX But on my 2 computers, the Port (COM)Reset is stuck on PORT_CONNECT_STATUS_CHANGE Interrupt and no D2H Is sent.

Have you performed all of the initialization steps as listed in section 10.1.2 of the AHCI specification? Does your interrupt handler acknowledge the interrupt by clearing PxSERR.DIAG.X?

devc1 wrote:
Code:
HbaPort->CommandStatus.Start = 0;
    HbaPort->CommandStatus.FisReceiveEnable = 0;
    while(HbaPort->CommandStatus.CurrentCommandSlot || HbaPort->CommandStatus.FisReceiveRunning);

There are two problems with this code. The first is that you're reading PxCMD.CCS instead of PxCMD.CR to determine whether the port is running. The second is that you must ensure the port has stopped before you clear PxCMD.FRE instead of clearing PxCMD.FRE at the same time you clear PxCMD.ST. (See section 10.3 of the AHCI specification for details.)

devc1 wrote:
Code:
Port->Port->InterruptEnable.D2HRegisterFisInterruptEnable = 1;
    Port->Port->InterruptEnable.PioSetupFisInterruptEnable = 1;
    Port->Port->InterruptEnable.DmaSetupFisInterruptEnable = 1;
    Port->Port->InterruptEnable.SetDeviceBitsFisInterruptEnable = 1;
    Port->Port->InterruptEnable.UnknownFisInterruptEnable = 1;
    Port->Port->InterruptEnable.DescriptorProcessedInterruptEnable = 1;
    Port->Port->InterruptEnable.PortChangeInterruptEnable = 1;
    Port->Port->InterruptEnable.DeviceMechanicalPresenceEnable = 1;
    Port->Port->InterruptEnable.PhyRdyChangeInterruptEnable = 1;
    Port->Port->InterruptEnable.IncorrectPortMultiplierEnable = 1;
    Port->Port->InterruptEnable.OverflowEnable = 1;
    Port->Port->InterruptEnable.InterfaceNonFatalErrorEnable = 1;
    Port->Port->InterruptEnable.InterfaceFatalErrorEnable = 1;
    Port->Port->InterruptEnable.HostBusDataErrorEnable = 1;
    Port->Port->InterruptEnable.HostBusFatalErrorEnable = 1;
    Port->Port->InterruptEnable.TaskFileErrorEnable = 1;
    Port->Port->InterruptEnable.ColdPresenceDetectEnable = 1;

GCC does not recommend using volatile bitfields for MMIO. If nothing else, it makes your code much bigger and slower by turning each of those 17 assignments into a separate read/modify/write.

devc1 wrote:
Code:
    wr32(&Port->Port->SataError, -1); // Static function I made

Why do you need a separate function for this?

devc1 wrote:
Code:
    *(DWORD*)&Port->Port->SataError = -1; // Clear SATA_ERROR

Type punning by casts is undefined behavior, and this cast discards the volatile qualifier.


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Thu Jul 21, 2022 9:51 pm 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
Well I managed to get this work on real hardware, by setting Interface Communication control, Spinup device and DevicePowerOn (if staggered spinup is supported) and I declare that the fis has been sent after PhyRdy change interrupt and wait for busy, I know this is not the right implementation and Thanks also for the things you have mentionned I think this is why I do not get D2H Interrupt on real hardware on reset.

I reset the HBA And take AHCI Ownership according to specification, then I setup and reset ports and I do a test read from devices, currently I do not know how to IDENTIFY_DEVICE and how PIO works.

Secondly I am using MSVC with optimizations disabled and dword aligned bitfields and I have no problem with other drivers, BUT what u said is true writing registers Bit-Per'Bit really slows done the driver.


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Fri Jul 22, 2022 6:59 am 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
Well it's still does not send a First D2H Interrupt


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Fri Jul 22, 2022 11:55 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
According to the AHCI spec, the HBA will raise the D2H register FIS received interrupt when the FIS is copied into memory. Since you're waiting for the interrupt before you've enabled the DMA engines, the HBA can't copy the FIS into memory, and you won't receive the interrupt.

Looking at the spec, I'm not sure you'll ever receive a D2H register FIS interrupt for the first D2H register FIS - you might need to use a different interrupt to detect when the drive is ready to communicate after a reset.


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Fri Jul 22, 2022 7:18 pm 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
What is this DMA engines, are u talking about CMDxFRE. I used PhyRdy change interrupt and wait for the Port Busy & DRQ as an alternative to D2H interrupt on real hardware and it is really working without problems. Then I check port signature and set CMDxATAPI if it has ATAPI Signature, after that I just enable Start and Fis receive on port.

I use interrupts and command queuing, everything works fine until the last (asynchronous) command which I do not get an interrupt for it (muti-processor-ing), is there anyway to tell the hba that I received only this Command Issue bits, I doesn't wanna implement an IPC server because I can have a great deal in performace with synchronized instructions (and muti-processor-ing). And I do not have a clue what does PORTxSATA_ACTIVE, this also minimises work on CPU cause interrupts with task scheduling will optimize cpu work by putting waiting threads on an IO_WAIT status and not running them.


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Sat Jul 23, 2022 1:16 pm 
Offline
Member
Member

Joined: Mon Dec 07, 2020 8:09 am
Posts: 212
I always poll during the initialization and ID phase. Many things can go wrong and IMO it's not worth it to get stuck waiting for an interrupt that won't come, in order to shave 10 ms off the total boot time.

Tried these just out of curiosity though:

On Qemu the card sets D2H bit in the interrupt state register.

On hardware, seems that the card doesn't set any bit there even after seeing the correct 0x101 signature.

@Octocontrabass have a related question about the reset sequence, AHCI spec 1.3 section 10.1.1 step 7 says:

Quote:
If PxTFD.STS.BSY, PxTFD.STS.DRQ, and PxTFD.STS.ERR are all ‘0’,
prior to the maximum allowed time as specified in the ATA/ATAPI-7 specification, the
device is ready.


but I can't find anything about this in the ATA-ATAPI-7 or 8 draft. Any idea on what the timeout should be here?


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Sat Jul 23, 2022 4:23 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
xeyes wrote:
On Qemu the card sets D2H bit in the interrupt state register.

On hardware, seems that the card doesn't set any bit there even after seeing the correct 0x101 signature.

This might be a QEMU bug. I haven't checked earlier versions of the AHCI specification, but 1.3 says the D2H interrupt is used only to indicate a D2H register FIS has been written to memory, and the state machine in the spec doesn't seem to allow a D2H register FIS to both update the signature register and be written to memory.

xeyes wrote:
Any idea on what the timeout should be here?

All I can find is clause 11.1, which says a single parallel device must clear BSY within 5 seconds, and clause 13.3, which suggests that serial devices must adhere to that timing to maintain compatibility with parallel devices. I would take that to mean the timeout must be at least 5 seconds.


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Sat Jul 23, 2022 7:49 pm 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
Great, so now I'm not making anything wrong but, How to report that I only received some specific bits in Command Issue, do I need a mutex if it is multi processor. And what do PORTxSATA_ACTIVE.

Another question, how PIO works (with AHCI). And how to send an IDENTIFY_DEVICE with PIO and how to get data from the IO Ports.


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Sat Jul 23, 2022 8:39 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
devc1 wrote:
Great, so now I'm not making anything wrong but, How to report that I only received some specific bits in Command Issue, do I need a mutex if it is multi processor.

I don't understand this question.

devc1 wrote:
And what do PORTxSATA_ACTIVE.

Do you mean PxSACT? That's the register that tracks queued command completion. Before you write to PxCI to issue a NCQ command, you write the same value to PxSACT to tell the HBA you're going to issue a queued command. When you receive an IRQ for command completion after issuing NCQ commands, you read PxSACT to see which queued commands have completed instead of reading PxCI.

Keep in mind you may not be able to use all 32 bits of PxSACT, depending on the HBA and drive capabilities.

devc1 wrote:
Another question, how PIO works (with AHCI).

PIO works the same as DMA, but there are some limitations: the HBA may not support more than one DRQ block per command, and DRQ blocks can't be bigger than 8kB.

devc1 wrote:
And how to send an IDENTIFY_DEVICE with PIO and how to get data from the IO Ports.

Sending the IDENTIFY DEVICE command works exactly the same as sending any other non-queued read command. IDENTIFY DEVICE always uses one 512-byte DRQ block, so the AHCI PIO limitations won't get in the way. There are no I/O ports involved.


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Sat Jul 23, 2022 9:56 pm 
Offline
Member
Member

Joined: Mon Dec 07, 2020 8:09 am
Posts: 212
Octocontrabass wrote:
xeyes wrote:
On Qemu the card sets D2H bit in the interrupt state register.

On hardware, seems that the card doesn't set any bit there even after seeing the correct 0x101 signature.

This might be a QEMU bug. I haven't checked earlier versions of the AHCI specification, but 1.3 says the D2H interrupt is used only to indicate a D2H register FIS has been written to memory, and the state machine in the spec doesn't seem to allow a D2H register FIS to both update the signature register and be written to memory.

xeyes wrote:
Any idea on what the timeout should be here?

All I can find is clause 11.1, which says a single parallel device must clear BSY within 5 seconds, and clause 13.3, which suggests that serial devices must adhere to that timing to maintain compatibility with parallel devices. I would take that to mean the timeout must be at least 5 seconds.


Could be, IIRC Q means quick so 100% accurate is probably not a high priority, it can't be anyway due to timing.

Thanks for digging up the timeout value!

The draft I got doesn't even have clause 11, only then did I realize that there are a few different volumes #-o


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Sun Jul 24, 2022 7:05 am 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
Octocontrabass wrote:
devc1 wrote:
Great, so now I'm not making anything wrong but, How to report that I only received some specific bits in Command Issue, do I need a mutex if it is multi processor.

I don't understand this question.



I mean because when you have a multiprocessor system, you may set command issue or sata active(SACT) when u are receiving the interrupt and handling command issue, for e.g. CI=0x81 when u are about to ack the interrupt and you have passed slot 7 it may be cleared, and u have acked the interrupt and u will get no interrupt after until a next command. When I try a SataRead with asynchronous commands it gets stuck on the last command even on uniprocessor mode in QEMU (and Real hardware)


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Sun Jul 24, 2022 1:46 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
When you're accessing the HBA from multiple CPUs concurrently, you need some kind of mutex to ensure you're not setting any bits in PxCI or PxSACT before your interrupt handler has acknowledged those command slots as completed.

I'm not sure why you're not receiving an interrupt when the last command completes, though.


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Sun Jul 24, 2022 9:17 pm 
Offline
Member
Member

Joined: Fri Feb 11, 2022 4:55 am
Posts: 435
Location: behind the keyboard
Okay thanks, I will be here responding after a 1 or 2 days because I'm completly changing my scheduler from simple to priority preemtive (and optimized), My computer supports AVX so I'm also planning to make AVX/AVX512 versions of my scheduler, This is to optimize & give more idle cpu time and faster task switching. Thanks another time. The good thing about my kernel is that it only supports 64 bit and ACPI Hardware (likely 15 years+). So every processor will have atleast SSE3 Support.


Top
 Profile  
 
 Post subject: Re: AHCI Port COMRESET ( & How to handle PortConnectChange I
PostPosted: Mon Jul 25, 2022 10:12 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
devc1 wrote:
The good thing about my kernel is that it only supports 64 bit and ACPI Hardware (likely 15 years+). So every processor will have atleast SSE3 Support.

All 64-bit hardware supports ACPI, but some 64-bit hardware does not support SSE3.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], SemrushBot [Bot] and 61 guests


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 post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group