commit 3f963fa4acc8546d4a3f0904c73923d428d43084
parent ceb0118d76285bbeb873d25871ca641cfa8d4803
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Sun, 7 Aug 2022 08:16:05 -0400
drv/pc/pci: make PCI fields writable
Diffstat:
6 files changed, 126 insertions(+), 61 deletions(-)
diff --git a/REFS.md b/REFS.md
@@ -24,7 +24,9 @@ helpful:
* [FreeVGA][freevga]
* [Ralf Brown's x86 interrupt list][ralfint]
+* [Intel's ICH7 datasheet][ich7]
[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
diff --git a/fs/drv/pc/pci.fs b/fs/drv/pc/pci.fs
@@ -1,5 +1,20 @@
\ PCI driver
?f<< /lib/str.fs
+?f<< /lib/bit.fs
+?f<< /lib/wordtbl.fs
+
+\ PCI registers
+\ Data from PCI registers are always fetched in 4 bytes wide chunks, but the
+\ registers themselves can be 4, 2 or 1 byte long. They never overlap two
+\ chunks.
+\ In this unit, we create a data structure called "pcifield" which interacts
+\ with PCI registers through a $100 bytes wide buffer, "_buf". We only ever talk
+\ to one PCI "address" (a bus/slot/function combination) at once and whenever
+\ we select a new address, the buffer is flushed. When a data is read, we check
+\ if the corresponding 4b value has been fetched in the buffer. If not, we fetch
+\ it. Then, we can return the value. For writing, we read it first, then write
+\ the new data in the buffer, then send the appropriate chunk back to the
+\ device.
$cf8 const CFGADDR
$cfc const CFGDATA
@@ -10,33 +25,75 @@ $100 const META_FIELDDESC \ data: a string
0 value pcislot
0 value pcifunc
0 value _readcnt \ how many registers are read in current buffer
-create _buf $50 allot
+create _buf $100 allot
+\ bit mask indicating which 4b chunk have been read. we need 64 bits.
+create _rdmask 8 allot
+
+: _pciaddr ( off -- n )
+ $fc and pcifunc 8 lshift or pcislot 11 lshift or pcibus 16 lshift or
+ $80000000 or ;
+
+: _pci@ ( pciaddr -- n ) CFGADDR p! CFGDATA p@ ;
+: _pci! ( n pciaddr -- n ) CFGADDR p! CFGDATA p! ;
+
+: _reginbuf? ( off -- f )
+ $80 /mod ( bit*4 maskoff ) _rdmask + @ ( bit*4 bitmask ) swap 4 / bit? ;
+
+: _reginbuf! ( off -- )
+ $80 /mod ( bit*4 maskoff ) _rdmask + dup @ ( bit*4 a bitmask )
+ rot 4 / ( a bitmask bit ) bit1! ( a bitmask ) swap ! ;
+
+: _pcireg@ ( off -- ) \ place reg value in buffer
+ $fc and dup _reginbuf? not if ( off )
+ dup _pciaddr _pci@ ( off n ) over _reginbuf! ( off n )
+ swap _buf + ! else drop then ;
+
+: _pcireg! ( off -- ) \ push value from buffer to device
+ $fc and dup _reginbuf? not if abort" Doing PCI write without a buffer!" then
+ dup _buf + @ swap _pciaddr _pci! ;
+
+: pcisel ( bus slot func -- f )
+ to pcifunc to pcislot to pcibus
+ _rdmask 8 0 fill 0 _pcireg@ _buf @ -1 = not ;
+
+3 wordtbl _ 'w c@ 'w w@ 'w @
+: _n@ ( a width -- n ) _ swap wexec ;
+3 wordtbl _ 'w c! 'w w! 'w !
+: _n! ( n a width -- ) _ swap wexec ;
+
0 value _currentlist
-: _
+
+\ Partially obeys "to" semantic. That is, only a regular "to" will work. Other
+\ "to" words will trigger a regular "to" write.
+: pcifield ( width offset "desc" -- )
+ doer c, c,
current _currentlist ! 4 to+ _currentlist
'"' expectchar current 'emeta lladd drop
- META_FIELDDESC , [compile] S" drop ;
-: pcifield ( off -- ) _buf + &@ _ ;
-: pcifieldw ( off -- ) _buf + &w@ _ ;
-: pcifieldc ( off -- ) _buf + &c@ _ ;
+ META_FIELDDESC , [compile] S" drop does> ( n? 'field )
+ c@+ dup >r dup _pcireg@ _buf + swap c@ ( n? a width R:off ) to? if
+ _n! r> _pcireg! else rdrop _n@ then ;
+
+: pcifield4 2 swap pcifield ;
+: pcifield2 1 swap pcifield ;
+: pcifield1 0 swap pcifield ;
\ Common header
12 const COMMONFIELDCNT
create commonfieldlist COMMONFIELDCNT 4 * allot
commonfieldlist to _currentlist
-$00 pcifieldw pci.vendor "Vendor ID"
-$02 pcifieldw pci.device "Device ID"
-$04 pcifieldw pci.command "Command"
-$06 pcifieldw pci.status "Status"
-$08 pcifieldc pci.revision "Revision ID"
-$09 pcifieldc pci.progIF "progIF"
-$0a pcifieldc pci.subclass "Subclass"
-$0b pcifieldc pci.class "Class"
-$0c pcifieldc pci.cachelinesz "Cache Line Size"
-$0d pcifieldc pci.lattimer "Latency Timer"
-$0e pcifieldc pci.hdrtype "Type"
-$0f pcifieldc pci.bist "BIST"
+$00 pcifield2 pci.vendor "Vendor ID"
+$02 pcifield2 pci.device "Device ID"
+$04 pcifield2 pci.command "Command"
+$06 pcifield2 pci.status "Status"
+$08 pcifield1 pci.revision "Revision ID"
+$09 pcifield1 pci.progIF "progIF"
+$0a pcifield1 pci.subclass "Subclass"
+$0b pcifield1 pci.class "Class"
+$0c pcifield1 pci.cachelinesz "Cache Line Size"
+$0d pcifield1 pci.lattimer "Latency Timer"
+$0e pcifield1 pci.hdrtype "Type"
+$0f pcifield1 pci.bist "BIST"
create summaryfields
' pci.vendor , ' pci.device , ' pci.class , ' pci.subclass , ' pci.hdrtype ,
@@ -46,50 +103,32 @@ create summaryfields
create type0fieldlist TYPE0FIELDCNT 4 * allot
type0fieldlist to _currentlist
-$10 pcifield pci0.bar0 "BAR0"
-$14 pcifield pci0.bar1 "BAR1"
-$18 pcifield pci0.bar2 "BAR2"
-$1c pcifield pci0.bar3 "BAR3"
-$20 pcifield pci0.bar4 "BAR4"
-$24 pcifield pci0.bar5 "BAR5"
-$28 pcifield pci0.cisptr "Cardbus CIS ptr"
-$2c pcifieldw pci0.subsystemvendor "Subsystem Vendor"
-$2e pcifieldw pci0.subsystem "Subsystem ID"
-$30 pcifield pci0.expansionaddr "Expansion Addr"
-$34 pcifieldc pci0.capabiliesptr "Capabilities ptr"
-$3c pcifieldc pci0.interruptline "Interrupt Line"
-$3d pcifieldc pci0.interruptpin "Interrupt PIN"
-$3e pcifieldc pci0.mingrant "Min grant"
-$3f pcifieldc pci0.maxlatency "Max latency"
-
-: pciaddr ( off -- n )
- $fc and pcifunc 8 lshift or pcislot 11 lshift or pcibus 16 lshift or
- $80000000 or ;
-
-: _pci@ ( pciaddr -- n ) CFGADDR p! CFGDATA p@ ;
-
-\\ Read the first register of pci address and return whether it's a valid device
-: pci@ ( bus slot func -- f )
- to pcifunc to pcislot to pcibus
- 0 to _readcnt 0 pciaddr _pci@ dup -1 = not if ( n )
- _buf ! 1 to _readcnt 1 else 1+ ( 0 ) then ;
-
-\ Ensure that at least "n" registers are read in _buf
-: _ensureread ( n )
- begin _readcnt over < while
- _readcnt 4 * dup pciaddr _pci@ ( off n )
- swap _buf + ! 1 to+ _readcnt repeat ( n ) drop ;
+$10 pcifield4 pci0.bar0 "BAR0"
+$14 pcifield4 pci0.bar1 "BAR1"
+$18 pcifield4 pci0.bar2 "BAR2"
+$1c pcifield4 pci0.bar3 "BAR3"
+$20 pcifield4 pci0.bar4 "BAR4"
+$24 pcifield4 pci0.bar5 "BAR5"
+$28 pcifield4 pci0.cisptr "Cardbus CIS ptr"
+$2c pcifield2 pci0.subsystemvendor "Subsystem Vendor"
+$2e pcifield2 pci0.subsystem "Subsystem ID"
+$30 pcifield4 pci0.expansionaddr "Expansion Addr"
+$34 pcifield1 pci0.capabiliesptr "Capabilities ptr"
+$3c pcifield1 pci0.interruptline "Interrupt Line"
+$3d pcifield1 pci0.interruptpin "Interrupt PIN"
+$3e pcifield1 pci0.mingrant "Min grant"
+$3f pcifield1 pci0.maxlatency "Max latency"
: _nextfunc ( -- f )
begin
1 to+ pcifunc pcifunc 8 < while
- pcibus pcislot pcifunc pci@ not while repeat
+ pcibus pcislot pcifunc pcisel not while repeat
1 else 0 then ;
: _nextslot ( -- f )
begin
1 to+ pcislot pcislot $20 < while
- pcibus pcislot 0 pci@ not while repeat
+ pcibus pcislot 0 pcisel not while repeat
1 else 0 then ;
: _getdesc ( 'field -- str )
@@ -99,16 +138,16 @@ $3f pcifieldc pci0.maxlatency "Max latency"
\\ Print current PCI device in a one line summary
: .pciln ( -- )
- .pciaddr 5 dup _ensureread >r summaryfields begin ( a )
+ .pciaddr 5 >r summaryfields begin ( a )
@+ dup _getdesc stype spc> execute .x? spc> next drop nl> ;
: .pcislot ( bus slot -- )
- 0 pci@ if
+ 0 pcisel if
.pciln pci.hdrtype $80 and if \ multi-function
begin _nextfunc while .pciln repeat then then ;
: .pcibus ( bus -- )
- 0 0 pci@ if pcibus pcislot .pcislot then
+ 0 0 pcisel if pcibus pcislot .pcislot then
begin _nextslot while pcibus pcislot .pcislot repeat ;
: nspcs ( n -- ) ?dup if >r begin spc> next then ;
@@ -116,11 +155,12 @@ $3f pcifieldc pci0.maxlatency "Max latency"
: .pcifield ( 'field -- )
dup _getdesc dup stype c@ ( 'field len ) 17 -^ nspcs execute .x? nl> ;
-: _ ( fieldlist cnt -- )
- nl> $10 _ensureread >r begin @+ .pcifield next drop ;
+: .pcifields ( fieldlist cnt -- )
+ nl> >r begin @+ .pcifield next drop ;
: .pci ( bus slot func -- )
- pci@ .pciaddr not if ." No device" nl> exit then
- commonfieldlist COMMONFIELDCNT _ ;
+ pcisel .pciaddr not if ." No device" nl> exit then
+ commonfieldlist COMMONFIELDCNT .pcifields ;
+
+: .pci0 ( bus slot func -- ) type0fieldlist TYPE0FIELDCNT .pcifields ;
-: .pci0 ( bus slot func -- ) type0fieldlist TYPE0FIELDCNT _ ;
diff --git a/fs/lib/bit.fs b/fs/lib/bit.fs
@@ -0,0 +1,9 @@
+\ Playing with bits
+
+: bitmask ( bit -- mask ) \ 2 --> $04, 31 --> $80000000
+ 1 swap lshift ;
+
+: bit? ( n bit -- f ) bitmask and bool ;
+
+: bit1! ( n bit -- n ) bitmask or ;
+: bit0! ( n bit -- n ) bitmask ^ and ;
diff --git a/fs/tests/lib/all.fs b/fs/tests/lib/all.fs
@@ -1,5 +1,6 @@
\ Run all test suites
f<< /tests/lib/core.fs
+f<< /tests/lib/bit.fs
f<< /tests/lib/str.fs
f<< /tests/lib/xdict.fs
f<< /tests/lib/struct.fs
diff --git a/fs/tests/lib/bit.fs b/fs/tests/lib/bit.fs
@@ -0,0 +1,11 @@
+?f<< /tests/harness.fs
+?f<< /lib/bit.fs
+testbegin
+2 bitmask $04 #eq
+31 bitmask $80000000 #eq
+5 bitmask ^ $ffffffdf #eq
+$42 0 bit? not #
+$42 1 bit? #
+$42 2 bit1! $46 #eq
+$42 1 bit0! $40 #eq
+testend
diff --git a/fs/xcomp/bootlo.fs b/fs/xcomp/bootlo.fs
@@ -42,6 +42,8 @@
: ?swap ( n n -- l h ) 2dup > if swap then ;
: min ?swap drop ; : max ?swap nip ;
: =><= ( n l h -- f ) over - rot> ( h n l ) - >= ;
+: neg 0 -^ ;
+: ^ -1 xor ;
\ Memory
: c@+ ( a -- a+1 c ) dup 1+ swap c@ ;