commit 2d45f46cd48a9523b98b8abe12e1159e142fb743
parent a33b782973b52f099b08f8f28aa587631d55b8fe
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Wed, 19 Oct 2022 20:16:20 -0400
drv/pc/ahci: getting up to the IDENTIFY phase! (in QEMU)
To try, change the "pcrun" make target to add "-machine q35" and then do this
at the Dusk prompt:
ahci$
.ahci
\ Locate the port number of an appropriate device
( portno ) AHCIDevice :new value dev
dev AHCIDevice :enable
dev AHCIDevice :identify .x
\ Should print something like 00000058, the same status than .ataall under the
\ "pc" QEMU machine.
Diffstat:
5 files changed, 170 insertions(+), 18 deletions(-)
diff --git a/REFS.md b/REFS.md
@@ -25,8 +25,12 @@ helpful:
* [FreeVGA][freevga]
* [Ralf Brown's x86 interrupt list][ralfint]
* [Intel's ICH7 datasheet][ich7]
+* [AHCI 1.3.1 specifications][ahci]
+* [Serial ATA 1.0 specifications][sata]
[osdev]: https://wiki.osdev.org/
[freevga]: http://www.osdever.net/FreeVGA/home.htm
[ralfint]: http://www.cs.cmu.edu/~ralf/files.html
[ich7]: https://www.intel.com/content/dam/doc/datasheet/i-o-controller-hub-7-datasheet.pdf
+[ahci]: https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1-3-1.pdf
+[sata]: https://www.seagate.com/support/disc/manuals/sata/sata_im.pdf
diff --git a/fs/drv/pc/ahci.fs b/fs/drv/pc/ahci.fs
@@ -1,5 +1,9 @@
\ AHCI driver
?f<< /drv/pc/pci.fs
+?f<< /drv/pc/sata.fs
+
+\ Align here to n bytes, n being a power of 2.
+: alignn ( n -- ) dup 1- here and dup if - allot else 2drop then ;
struct[ HBAPort
sfield clb
@@ -21,6 +25,66 @@ struct[ HBAPort
sfield fbs
11 CELLSZ * sallot \ reserved
4 CELLSZ * sfield' vendor
+
+ : :waitcmd begin dup cmd $8000 ( CR ) and not until drop ;
+ : :startcmd
+ dup :waitcmd
+ \ ST can only be set *after* FRE
+ dup cmd $10 ( FRE ) or over to cmd
+ dup cmd 1 ( ST ) or swap to cmd ;
+ : :stopcmd dup cmd $ffffffee ( ~FRE+~ST ) and swap to cmd ;
+ : :ci! 1 over to ci begin dup ci not until drop ;
+ : :sigstr ( self -- str ) sig case
+ $101 of = S" ATA" endof
+ $eb140101 of = S" ATAPI" endof
+ $c33c0101 of = S" bridge" endof
+ $96690101 of = S" multiplier" endof
+ S" unknown" endcase ;
+]struct
+
+struct[ RsvdFIS \ buffer for receiving FIS from AHCI controller
+ $20 sfield' dma
+ $20 sfield' pio
+ $18 sfield' d2h
+ $02 sfield' sdb \ set device bits
+ $40 sfield' unknown
+ $60 sallot \ reserved
+
+ : :new ( -- self ) $100 alignn here SZ allot0 ;
+]struct
+
+struct[ PRDTable
+ sfield addr \ address of data, 2-bit aligned
+ sfield addrhi
+ 4 sallot \ reserved
+ sfieldw bytecount \ 0-indexed! 0=1 byte 1=2 bytes etc. must be odd.
+ sfieldb bytecounthi \ bytecount's maximum is 4 MB
+ sfieldb flags \ b7=Interrupt on Completion
+]struct
+
+struct[ CmdHeader
+ sfieldw flags
+ sfieldw prdtl \ Physical Region Descriptor Table Length
+ sfield prdbc \ PRD byte count
+ sfield cmdtba \ address of a CmdTable, 128-bit aligned
+ sfield cmdtbau
+ $10 sallot \ reserved
+
+ : :cfl! ( n self -- ) tuck flags $fff0 and or swap to flags ;
+ : :new ( -- cmdlist ) $400 alignn here SZ $20 * allot0 ;
+]struct
+
+struct[ CmdTable
+ $40 sfield' fis
+ $10 sfield' atapicmd
+ $30 sallot \ reserved
+ 0 sfield' prdt \ variable length, list of PRDTable
+
+ : :new ( cmdhdr prdt-count -- cmdt )
+ $80 alignn here swap PRDTable SZ * SZ + allot0 ( cmdhdr cmdt )
+ FISH2D TYPE over fis c! ( cmdhdr cmdt )
+ 2dup swap to CmdHeader cmdtba ( cmdhdr cmdt )
+ FISH2D SZ >> >> rot CmdHeader :cfl! ( cmdt ) ;
]struct
struct[ HBAMem
@@ -37,10 +101,37 @@ struct[ HBAMem
sfield bohc
$a0 SZ - sallot \ reserved
$100 SZ - sfield' vendor
- HBAPort SZ 32 * sfield' ports
+ HBAPort SZ $20 * sfield' ports
+
+ : :port ( n self -- 'port ) ports swap HBAPort SZ * + ;
]struct
-0 structbind HBAMem hba
+0 structbind HBAMem ahci
+
+struct[ AHCIDevice
+ sconst portno
+ sconst port \ HBAPort
+ sconst cmds \ CmdHeader * 32
+ sconst rsvdfis \ RsvdFIS
+
+ : :new ( portno -- self )
+ RsvdFIS :new CmdHeader :new here >r ( portno rsvdfis cmds )
+ rot dup ( portno ) , ahci :port , ( cmds ) , ( rsvdfis ) ,
+ r@ port HBAPort :stopcmd
+ r@ cmds to r@ port HBAPort clb
+ r@ rsvdfis to r@ port HBAPort fb
+ \ We only need a single command table, which itself only needs a single PRDT
+ r@ cmds 1 CmdTable :new
+ r> ;
+ : :enable port HBAPort :startcmd ;
+ : :cmd0 cmds CmdHeader cmdtba ;
+ : :fis0 :cmd0 CmdTable fis ;
+ : :identify ( self -- status )
+ dup :fis0 $ec ( IDENTIFY ) over to FISH2D command ( fis )
+ $80 ( enable C ) swap to FISH2D flags ( self )
+ dup port HBAPort :ci!
+ dup rsvdfis RsvdFIS pio FISPIOSetup status ;
+]struct
\ Search PCI bus for a AHCI controller and if found, bind "hba" to its BAR5
\ value.
@@ -49,27 +140,20 @@ struct[ HBAMem
0 >r 0 >r begin \ V1=slot V2=func
V1 $20 < while
0 V1 V2 pcisel if
- pci.class 1 = pci.subclass 6 = and if pci0.bar5 ['] hba rebind then then
+ pci.class 1 = pci.subclass 6 = and if pci0.bar5 ['] ahci rebind then then
1 to+ V2 V2 8 = if 0 to V2 1 to+ V1 then repeat rfree ;
\ Do we have a AHCI controller?
-: ahci? hba :self bool ;
-
-: sigstr ( sig -- str ) case
- $101 of = S" ATA" endof
- $eb140101 of = S" ATAPI" endof
- $c33c0101 of = S" bridge" endof
- $96690101 of = S" multiplier" endof
- S" unknown" endcase ;
+: ahci? ahci :self bool ;
: .ahci
ahci? not if abort" No AHCI controller" then
- ." HBA addr: " hba :self .x nl>
- ." Ports implemented: " hba pi .x nl>
- $20 >r hba pi 0 begin ( pi id )
+ ." HBA addr: " ahci :self .x nl>
+ ." Ports implemented: " ahci pi .x nl>
+ $20 >r ahci pi 0 begin ( pi id )
2dup bit? if
- hba ports over HBAPort SZ * + dup HBAPort ssts $0f0f and $0103 = if
+ dup ahci :port dup HBAPort ssts $0f0f and $0103 = if
( pi id port ) \ detected and active
- ." Device " over . ." type: " HBAPort sig sigstr stype nl>
+ ." Device " over . ." type: " HBAPort :sigstr stype nl>
else drop then then
( pi id ) 1+ next ;
diff --git a/fs/drv/pc/sata.fs b/fs/drv/pc/sata.fs
@@ -0,0 +1,63 @@
+\ SATA Driver
+
+struct[ FIS
+ sfieldb type
+]struct
+
+extends FIS struct[ FISH2D
+ sfieldb flags \ b7=C
+ sfieldb command
+ sfieldb features
+ sfieldb sector
+ sfieldw cylinder
+ sfieldb device
+ sfieldb sectorhi
+ sfieldw cylinderhi
+ sfieldb featureshi
+ sfieldw count
+ 1 sallot \ reserved
+ sfieldb control
+ 4 sallot \ reserved
+
+ $27 const TYPE
+]struct
+
+extends FIS struct[ FISD2H
+ sfieldb flags \ b6=I
+ sfieldb status
+ sfieldb error
+ sfieldb sector
+ sfieldw cylinder
+ sfieldb device
+ sfieldb sectorhi
+ sfieldw cylinderhi
+ 1 sallot \ reserved
+ sfieldw count
+ 6 sallot \ reserved
+
+ $34 const TYPE
+]struct
+
+extends FIS struct[ FISPIOSetup
+ sfieldb flags \ b6=I b5=D
+ sfieldb status
+ sfieldb error
+ sfieldb sector
+ sfieldw cylinder
+ sfieldb device
+ sfieldb sectorhi
+ sfieldw cylinderhi
+ 1 sallot \ reserved
+ sfieldb e_status
+ 4 sallot \ reserved
+ sfieldw transfercount
+ 2 sallot \ reserved
+
+ $5f const TYPE
+]struct
+
+extends FIS struct[ FISData
+ 3 sallot \ reserved
+ 0 sfield' data \ variable length
+ $46 const TYPE
+]struct
diff --git a/fs/tests/kernel.fs b/fs/tests/kernel.fs
@@ -125,7 +125,7 @@ data1 Foo :bleh 46 #eq
myword 46 #eq
extends Foo struct[ Bazooka
- sfield bling
+ sconst bling
]struct
create data3 7 , 9 c, ' mybleh , 999 ,
data3 Bazooka bling 999 #eq
diff --git a/fs/xcomp/bootlo.fs b/fs/xcomp/bootlo.fs
@@ -258,7 +258,8 @@ does> ( 'struct )
: sfield CELLSZ _svalue ;
: sfieldw 2 _svalue ;
: sfieldb 1 _svalue ;
-: sfield' ( sz -- ) _cur e>w structsz &+ _sfield ;
+: sfield' ( sz -- ) doer _sfield does> CELLSZ + @ ( a off ) + ;
+: sconst doer CELLSZ _sfield does> CELLSZ + @ ( a off ) + @ ;
: smethod doer _cur e>w structsz , CELLSZ sallot does> @ over + @ execute ;
struct[ Struct