duskos

dusk os fork
git clone git://git.alexwennerberg.com/duskos
Log | Files | Refs | README | LICENSE

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:
MREFS.md | 2++
Mfs/drv/pc/pci.fs | 162+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Afs/lib/bit.fs | 9+++++++++
Mfs/tests/lib/all.fs | 1+
Afs/tests/lib/bit.fs | 11+++++++++++
Mfs/xcomp/bootlo.fs | 2++
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@ ;