Octocontrabass wrote:
Well, I didn't manage to get a post in before you started, but the AHCI specification is where to start for an AHCI driver. You'll also need to refer to the ATA ACS specification for information about the commands you can send to the drive, and maybe the SATA specification for things that aren't explained in the AHCI or ATA specs.
Ethin wrote:
2. It locates the first BAR that is memory-based and not I/O based and selects that as the default BAR.
According to the AHCI specification, you should always use BAR5 instead of searching.
I'm not familiar enough with AHCI to say if there are any other problems.
Thanks for the specification references and help, I'll acquire those (I already have the SATA IO 3.4 specification). I'm now getting these from my kernel and a bit worried:
Quote:
ahci: Mis-aligned write to addr 0x0000000000000005
ahci: Mis-aligned write to addr 0x0000000000000006
ahci: Mis-aligned write to addr 0x0000000000000007
ahci: Mis-aligned write to addr 0x0000000000000009
ahci: Mis-aligned write to addr 0x000000000000000a
ahci: Mis-aligned write to addr 0x000000000000000b
This is odd, the only reads/writes are:
* ABAR + GHC (set bit 31)
* Reading from ABAR + PI and ABAR + CAP.NP
* Reading from PORTADDR + PXCMD, and then to set bits 28 to 31 to 1 to bring the port power state to active (I think these are the right bits, my PDF reader got a bit messed up with that), now leaving the rest of the PXCMD bits alone. Then writing the new command bits to PORTADDR + PXCMD.
* And, finally, repeated reads of PORTADR + PXCMD, bits 28 to 31, to wait until the port becomes ready.
Those are the only reads/writes I'm doing right now. I'm using two functions that I wrote -- read_memory and write_memory -- to do this because I didn't want duplicated code everywhere. They are as follows:
Code:
// ...
use core::ptr::*;
//...
pub fn read_memory(address: u64) -> u64 {
let addr: *const u64 = address as *const u64;
unsafe { read_volatile(addr) }
}
pub fn write_memory(address: u64, value: u64) {
let addr: *mut u64 = address as *mut u64;
unsafe {
write_volatile(addr, value);
}
}
I'm not really sure why this is miss-aligned...
Edit: would someone mind posting the bits for PXCMD? Clearly I'm either not setting the right bits or something else is wrong. Here's how my PDF reader displays the table -- it certainly is not elegant.
Quote:
3.3.7 Offset 18h: PxCMD – Port x Command and Status
Bit
Type
Reset
Description
Interface Communication Control (ICC):
This field is used to control power
management states of the interface. If the Link layer is currently in the L_IDLE state, or
L_NoCommPower state, writes to this field shall cause the HBA to initiate a transition to
the interface power management state requested. If the Link layer is not currently in
the L_IDLE state or L_NoCommPower state, writes to this field shall have no effect.
Value
Definition
Fh - 9h
8h
Reserved
DevSleep: This shall cause the HBA to assert the DEVSLP signal
associated with the port; the HBA shall ignore the Device Sleep Idle
Timeout value specified by PxDEVSLP.DITO. Software shall only
request DevSleep when the interface is in an idle state (i.e. PxCI is
cleared to 0h and PxSACT are cleared to 0h); if CAP2.SDS is cleared
to ‘0’ or if the interface is not idle at the time the register is written,
then the HBA shall not assert the DEVSLP signal and the interface
remains in its current state. If CAPS.SDS is set to ‘1’, CAP2.DESO is
set to ‘1’, and PxSSTS.IPM is not set to ‘6h’, then the HBA shall not
assert the DEVSLP signal and the interface shall remain in its current
state. Additionally, the HBA shall not assert the DEVSLP signal until
PHYRDY has been achieved (after a previous de-assertion).
7h
Reserved
Fh - 7h
Reserved
6h
Slumber: This shall cause the HBA to request a transition of the
interface to the Slumber state. The SATA device may reject the
request and the interface shall remain in its current state.
5h - 3h
Reserved
2h
Partial:
This shall cause the HBA to request a transition of the
interface to the Partial state.
The SATA device may reject the
request and the interface shall remain in its current state.
1h
Active:
This shall cause the HBA to request a transition of the
interface into the active state.
0h
No-Op / Idle: When software reads this value, it indicates the HBA is
ready to accept a new interface control command, although the
transition to the previously selected state may not yet have occurred.
31:28 RW 0h
RW/
RO
RW/
RO
When system software writes a non-reserved value other than No-Op (0h), the HBA
shall perform the actions described above (for the value written) and update this field
back to Idle (0h).
If software writes to this field to change the state to a state the link is already in (i.e.
interface is in the active state and a request is made to go to the active state), the HBA
shall take no action and return this field to Idle. For all but DevSleep, if the interface is
in a low power state and software wants to transition to a different low power state,
software must first bring the link to active and then initiate the transition to the desired
low power state. If CAPS2.DESO is cleared to ‘0’, transition to DevSleep may occur
from any other interface state. If CAP2.DESO is set to ‘1’, then DevSleep may only be
transitioned to if the interface is in Slumber.
It pretty much goes on like that. The bits for the HBA registers were fine, those displayed properly -- or at least well enough I could figure them out -- but now its messing up. I'll try another reader while I'm at it and see how that goes.
Edit 2: removed my rant on PDFs...
Edit 3: the bit stuff is no longer necessary -- I got it sorted out.