AHCI driver for RDOS
AHCI driver for RDOS
I've gathered the specification (AHCI is free, the SATA spec costed $25) and will start work on the AHCI using an asm-based device-driver. I've also located the source for Linux AHCI (unusable) and FreeBSD AHCI (a little more usable) in order to see how others have done.
I'll post progress here...
I'll post progress here...
Re: AHCI driver for RDOS
The SATA spec is over 750 pages, most of which is useless for driver-development. About the only usable information is the FIS-fields, and mostly the host-to-device FIS that is part of the AHCI interface.
The AHCI spec is only 123 pages, and more usable. The problem (as usual) is what functions to discard (the majority), and which must be implemented. The major issue is if it is possible to discard the ugly port multiplier functionality or not, and if anybody uses it, and for what in that case.
Each port can support up to 32 slots, but implementations are not required to support 32 slots. I suppose this would be about performance. If there are 32 non-continous outstanding requests for disc-IO, it would probably be best to queue them all for IO, and then wait for the completion of them all (but signalling owners as each request complete so owners can request new IO in parallel). I suspect that AHCI has a potential for much faster disc-IO than IDE, especially since my IDE driver uses PIO-mode.
The driver is here: http://rdos.net/vc/viewvc.cgi/trunk/ker ... iew=markup
The AHCI spec is only 123 pages, and more usable. The problem (as usual) is what functions to discard (the majority), and which must be implemented. The major issue is if it is possible to discard the ugly port multiplier functionality or not, and if anybody uses it, and for what in that case.
Each port can support up to 32 slots, but implementations are not required to support 32 slots. I suppose this would be about performance. If there are 32 non-continous outstanding requests for disc-IO, it would probably be best to queue them all for IO, and then wait for the completion of them all (but signalling owners as each request complete so owners can request new IO in parallel). I suspect that AHCI has a potential for much faster disc-IO than IDE, especially since my IDE driver uses PIO-mode.
The driver is here: http://rdos.net/vc/viewvc.cgi/trunk/ker ... iew=markup
Re: AHCI driver for RDOS
Part of the problem of optimizing is how many slots to use, and how many scatter-gather entries to support. Hardware sets a limit of 32 slots, but as little as one may be implemented. My current design will setup 32 slots with a maximum of 20 scatter.gather entries. Ordinary sectors are 512 bytes, and each scatter-gather entry is best represented as a single sector (because then the sector handle can be put in the reserved space of the scatter-gather entry).
I think there is a need for static command-lists and command-tables, meaning that number of slots and scatter-gather entries cannot change. The current lists would require 5 physical pages (20k) per AHCI port, which should be ok.
The alternative would be to allocate command-tables dynamically, but I doubt this would have a performance benefit, rather it would waste CPU cycles instead.
What I still might consider is to construct an algorithm that can adapt to different number of slots, and increase number of scatter-gather entries as number of slots decrease to always use the memory available. This might be a table with 32-entries in the code-segment.
I'll have a 32-entry table for command list and command table offsets so I can get to a list/table fast regardless of table sizes which change based on scatter-gather entry counts. It is not possible to statically allocate these things in structures (using C wouldn't help much either).
I think there is a need for static command-lists and command-tables, meaning that number of slots and scatter-gather entries cannot change. The current lists would require 5 physical pages (20k) per AHCI port, which should be ok.
The alternative would be to allocate command-tables dynamically, but I doubt this would have a performance benefit, rather it would waste CPU cycles instead.
What I still might consider is to construct an algorithm that can adapt to different number of slots, and increase number of scatter-gather entries as number of slots decrease to always use the memory available. This might be a table with 32-entries in the code-segment.
I'll have a 32-entry table for command list and command table offsets so I can get to a list/table fast regardless of table sizes which change based on scatter-gather entry counts. It is not possible to statically allocate these things in structures (using C wouldn't help much either).
Re: AHCI driver for RDOS
A table of 32 slot-number entries that have:
1. # of slots to use
2. # of scatter-gather entries to use
3. size of PRD-table
4. # of physical pages required
Makes this a snap. I require at least 32 scatter-gather entries at the high-end side, and decrease number of physical pages when more than 128 scatter-gather entries could be fitted down to 2 physical pages.
With this table, 32 slots will use 32 scatter-gather entries, and use 5 physical pages. Between 5 and 31 slots will use 4 physical pages. If only one slot is available, it would use 2 physical pages, and support 504 scatter-gather entries. One additional physical page is used for misc. port data. If FIS based switching is used, this requires another physical page.
Status update: All the tables are initialized. The devices are RESETED, and the power-up logic seems to work up to starting both the FIS receiver and the command engine. Next is to try to read-out the drive configuration.
1. # of slots to use
2. # of scatter-gather entries to use
3. size of PRD-table
4. # of physical pages required
Code: Select all
prd_slot_table STRUC
prd_slots DW ?
prd_entries DW ?
prd_size DW ?
prd_pages DW ?
prd_slot_table ENDS
p1 prd_slot_table <1, 1F8h, 2000h, 2>
p2 prd_slot_table <2, 0F8h, 1000h, 2>
p3 prd_slot_table <3, 0A0h, 0A80h, 2>
p4 prd_slot_table <4, 78h, 800h, 2>
p5 prd_slot_table <5, 90h, 980h, 3>
p6 prd_slot_table <6, 78h, 800h, 3>
p7 prd_slot_table <7, 88h, 900h, 4>
p8 prd_slot_table <8, 78h, 800h, 4>
p9 prd_slot_table <9, 68h, 700h, 4>
p10 prd_slot_table <10, 58h, 600h, 4>
p11 prd_slot_table <11, 50h, 580h, 4>
p12 prd_slot_table <12, 48h, 500h, 4>
p13 prd_slot_table <12, 48h, 500h, 4>
p14 prd_slot_table <14, 40h, 480h, 4>
p15 prd_slot_table <14, 40h, 480h, 4>
p16 prd_slot_table <16, 38h, 400h, 4>
p17 prd_slot_table <16, 38h, 400h, 4>
p18 prd_slot_table <18, 30h, 380h, 4>
p19 prd_slot_table <18, 30h, 380h, 4>
p20 prd_slot_table <18, 30h, 380h, 4>
p21 prd_slot_table <21, 28h, 300h, 4>
p22 prd_slot_table <21, 28h, 300h, 4>
p23 prd_slot_table <21, 28h, 300h, 4>
p24 prd_slot_table <21, 28h, 300h, 4>
p25 prd_slot_table <25, 20h, 280h, 4>
p26 prd_slot_table <25, 20h, 280h, 4>
p27 prd_slot_table <25, 20h, 280h, 4>
p28 prd_slot_table <25, 20h, 280h, 4>
p29 prd_slot_table <25, 20h, 280h, 4>
p30 prd_slot_table <25, 20h, 280h, 4>
p31 prd_slot_table <25, 20h, 280h, 4>
p32 prd_slot_table <32, 20h, 280h, 5>
With this table, 32 slots will use 32 scatter-gather entries, and use 5 physical pages. Between 5 and 31 slots will use 4 physical pages. If only one slot is available, it would use 2 physical pages, and support 504 scatter-gather entries. One additional physical page is used for misc. port data. If FIS based switching is used, this requires another physical page.
Status update: All the tables are initialized. The devices are RESETED, and the power-up logic seems to work up to starting both the FIS receiver and the command engine. Next is to try to read-out the drive configuration.
Re: AHCI driver for RDOS
I figured out I need a few operational procedures now:
1. Allocate a slot (this is done by finding a clear bit in port x command issue)
2. Find the command table address
3. Fill in the FIS with the sector & command code for ATA
4. Add the data buffers to the PRDT (and notice the maximum number of entries as described in the above table).
5. Find and update the command list entry
6. Set the allocated slot-bit so the AHCI controller issues the command
7. Wait for command completion
The first command I need to issue is the IDENTIFY DEVICE (0xEC), as this would return vital drive-information.
1. Allocate a slot (this is done by finding a clear bit in port x command issue)
2. Find the command table address
3. Fill in the FIS with the sector & command code for ATA
4. Add the data buffers to the PRDT (and notice the maximum number of entries as described in the above table).
5. Find and update the command list entry
6. Set the allocated slot-bit so the AHCI controller issues the command
7. Wait for command completion
The first command I need to issue is the IDENTIFY DEVICE (0xEC), as this would return vital drive-information.
Re: AHCI driver for RDOS
There seems to be some type of fatal problem with the in-memory tables, because the controller encounters non-recoverable errors on one disc, and return "command not supported" on the other + strange LBA values.
I suppose I need to create shadow-tables from the physical addresses in order to see what the command table actually looks like to hardware.
Update: Turns out that it does work if single-stepped. There is some kind of race-condition in the initialization code. Anyway, now I got the identification sector from the ATA drive with the correct number of (48-bit LBA) sectors, and an error from the CD/DVD drive.
I suppose I need to create shadow-tables from the physical addresses in order to see what the command table actually looks like to hardware.
Update: Turns out that it does work if single-stepped. There is some kind of race-condition in the initialization code. Anyway, now I got the identification sector from the ATA drive with the correct number of (48-bit LBA) sectors, and an error from the CD/DVD drive.
Re: AHCI driver for RDOS
More problems. I cannot make the IRQ for AHCI fire, and I suspect the chipset is using MSI signalling. The PCI specification is required in order to understand MSI, but the document is only available to PCI-sig members, and membership costs $3000 (out of the question). However, search engines was my friend, and the specification is available for download.
What I still don't understand is what address MSI would write to, and what data. Perhaps the x86-processor documents would describe this?
What I still don't understand is what address MSI would write to, and what data. Perhaps the x86-processor documents would describe this?
Re: AHCI driver for RDOS
Hi,
For 80x86, which address MSI writes to and what data it should write is detailed in Intel's manuals (e.g. "10.11 MESSAGE SIGNALLED INTERRUPTS" within the "APIC" chapter of the System Programming Guide). Alternatively, you can find information on MSI in the datasheets for various Intel chipsets that support it.
Cheers,
Brendan
Which address MSI writes to and what data it should write is determined by the architecture (e.g. 80x86 may be completely different to ARM, and both may be completely different to PowerPC, etc). PCISIG only control "how" it's sent from devices to the chipset and not "where and what" the chipset expects, and so it won't be part of PCI standards.rdos wrote:What I still don't understand is what address MSI would write to, and what data. Perhaps the x86-processor documents would describe this?
For 80x86, which address MSI writes to and what data it should write is detailed in Intel's manuals (e.g. "10.11 MESSAGE SIGNALLED INTERRUPTS" within the "APIC" chapter of the System Programming Guide). Alternatively, you can find information on MSI in the datasheets for various Intel chipsets that support it.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: AHCI driver for RDOS
OK, found it. IOW, MSI seems to be done in a similar way as when the IO-APIC signals interrupts, only it goes directly to the core instead of via IO-APIC. It seems like AMD does not document MSI, but everything points in the direction that it works the same way, with the same address and data encodings. How it works on ARM or PowerPC is of no interest to me.Brendan wrote:Which address MSI writes to and what data it should write is determined by the architecture (e.g. 80x86 may be completely different to ARM, and both may be completely different to PowerPC, etc). PCISIG only control "how" it's sent from devices to the chipset and not "where and what" the chipset expects, and so it won't be part of PCI standards.
For 80x86, which address MSI writes to and what data it should write is detailed in Intel's manuals (e.g. "10.11 MESSAGE SIGNALLED INTERRUPTS" within the "APIC" chapter of the System Programming Guide). Alternatively, you can find information on MSI in the datasheets for various Intel chipsets that support it.
I've added 64 vectors for MSI (0A0h - 0DFh) which can be allocated in sizes of a power of two (1,2,4,8,16 or 32 vectors). I have an allocation function for vectors, and then a function that sets up the int-handler per vector. The stubs for MSI interrupts is basically identical to the stubs for ordinary APIC-ints (the EOI is done to the APIC).
I also need to add the capability-interface to PCI in order to find the MSI capability function number(s).
In the AHCI-driver, there would be two primary modes of operation. Either the device asks for one vector, and then that vector would handle all ports, or the device asks for multiple vectors, and if those can be allocated, there will be one vector per port. I won't bother with the half-baked solution of sharing the last vector. Of course, one vector per port is the most effective, especially for multi-core computers since the IRQs can then run in parallell (I'll use lowest priority delivery-mode) and I don't need to decode port-ISR bits.
Re: AHCI driver for RDOS
Still no luck with IRQs from AHCI. I've found out that the AHCI HBA has a MSI-capability, but it is not enabled. Additionally, it does not have a MSI-X capability. This should mean that it would use PCI IRQs, and not MSI. I'm pretty sure that I've used the correct IRQ, as I put a fixed value of IRQ 19 that Windows reports rather than relying on my own IRQ-identication which is not yet updated to use ACPI.
Some other observations:
1. MC.MSIE is not enabled (PCI IRQs is used)
2. GHC.IE is set (global interrupt flag is set)
3. CMD.ID is clear (interrupts are not disabled in PCI)
4. There are no pending interrupts in the PCI device-status
5. The IS bits for both implemented ports are set in the AHCI device (both ports request interrupts)
6. In the ATA port, the PRD completed bit is set in PxIS, and this interrupt is also enabled in PxIE
7. In the ATAPI port, no bit is set in PxIS
8. In the IOAPIC, interrupts are set as edge-triggered, and the MSI function also set edge-triggered mode.
The PCI IRQ is never called, and when forcing MSI-function, no interrupt is generated from the device either. I really wonder what could be wrong.
Some other observations:
1. MC.MSIE is not enabled (PCI IRQs is used)
2. GHC.IE is set (global interrupt flag is set)
3. CMD.ID is clear (interrupts are not disabled in PCI)
4. There are no pending interrupts in the PCI device-status
5. The IS bits for both implemented ports are set in the AHCI device (both ports request interrupts)
6. In the ATA port, the PRD completed bit is set in PxIS, and this interrupt is also enabled in PxIE
7. In the ATAPI port, no bit is set in PxIS
8. In the IOAPIC, interrupts are set as edge-triggered, and the MSI function also set edge-triggered mode.
The PCI IRQ is never called, and when forcing MSI-function, no interrupt is generated from the device either. I really wonder what could be wrong.
Re: AHCI driver for RDOS
It still doesn't work, but now I can at least confirm that PCI has asserted the IRQ when running in PCI IRQ-mode. I missed this before since I read the wrong PCI-registers. When running in MSI-mode, the bit is not set.
PCI configuration space registers:
Global HBA registers:
Port 1 HBA registers (ATA)
Port 2 HBA registers (ATAPI):
PCI configuration space registers:
Code: Select all
CMD: 0007
BME
MSE
ISOE
STS: 02B8
STS: 0000 0010 1011 1000
FBC
C66
CL
IS
INTR: 0205
Pin 2
Line 5
Code: Select all
CAP: E720FFC3
CAP: 1110 0111 0010 0000 1111 1111 1100 0011
S64A
SNCQ
SSNTF
SALP
SAL
SCLO
Gen 2
PMD
SSC
PSC
NCS=32
CCCS
EMS
Ports=3
GHC: 80000002
GHC: 1000 0000 0000 0000 0000 0000 0000 0010
AE
IE
IS: 00000001
PI: 00000003
VS: 00010200
C_CTL: 00010130
C_CTL: 0000 0000 0000 0001 0000 0001 0011 0000
Timeout = 1ms
CC = 1
Int=6
C_PORT:00000000
E_LOC: 01000002
EM_CTL:07010000
CAP2: 00000000
BOHC: 00000000
Code: Select all
PxCLB: 00000000003DA800
PxFB: 00000000003DA700
PxIS: 00000022
PxIS: 0000 0000 0000 0000 0000 0000 0010 0010
DPS
PSS
PxIE: 79400060
PxCMD: 0000C017
PxCMD: 0000 0000 0000 0000 1100 0000 0001 0111
CR
FR
FRE
POD
SUD
ST
PxTFD: 00000050
PxSIG: 00000101
LBA: 1
Count: 1
PxSSTS:00000113
PxSSTS: 0000 0000 0000 0000 0000 0001 0001 0011
Active
Gen 1
Present
PxSCTL:00000000
PxSERR:00000000
PxSACT:00000000
PxCI: 00000000
PxSNTF:00000000
PxFBS: 00000000
Code: Select all
PxCLB: 00000000003E0800
PxFB: 00000000003E0700
PxIS: 00000000
PxIE: 79400060
PxCMD: 0000C017
PxTFD: 00000100
PxTFD: 0000 0000 0000 0000 0000 0001 0000 0000
ERR
PxSIG: EB140101
LBA: EB1401
Count: 1
PxSSTS:00000113
PxSSTS: 0000 0000 0000 0000 0000 0001 0001 0011
Active
Gen 1
Present
PxSCTL:00000000
PxSERR:00000000
PxSACT:00000000
PxCI: 00000000
PxSNTF:00000000
PxFBS: 00000000
Re: AHCI driver for RDOS
I've now tested all free vectors in IO-APIC in conjunction with PCI IRQs, and none of them seem to get called. I suspect that the PCI-line is not routed to the APIC.
That leaves MSI as the only option.
Here are the MSI settings:
That leaves MSI as the only option.
Here are the MSI settings:
Code: Select all
80:
Cap: 05
81:
Next: 70
82:
Msi control: 0049
0000 0000 0100 1001
16 vectors enable
16 vectors cap
MSIE
84:
Address: FEEFF00C
FEE: bit 20-31 according to Intel
Dest: FF
RH: 1 (lowest prio)
DM: 1 (logical)
88:
Data: 01A0
0000 0001 1010 0000
Edge mode
Lowest prio
Vector: A0
Re: AHCI driver for RDOS
By setting RH=DM=0 it works.
Now I have a functioning IRQ and I can continue where I left off with driver.
Edit: With this code the IDENTIFY command is race-free and can be run at full speed with no problems. Next would be to read the partition table.
Now I have a functioning IRQ and I can continue where I left off with driver.
Edit: With this code the IDENTIFY command is race-free and can be run at full speed with no problems. Next would be to read the partition table.
Re: AHCI driver for RDOS
Now the first step in the file-system initialization process works (the disc assign callback). The next two steps (drive assign 1&2) would be next.
Re: AHCI driver for RDOS
Now read sector also works, and both drive-assign callbacks work. Now only the actual sector read/write server-thread needs to be implemented.