1+ use core:: ops:: Range ;
2+
13use pci_types:: { ConfigRegionAccess , PciAddress , PciHeader } ;
24use x86_64:: instructions:: port:: Port ;
35
46use crate :: drivers:: pci:: { PCI_DEVICES , PciDevice } ;
57
6- const PCI_MAX_BUS_NUMBER : u8 = 32 ;
7- const PCI_MAX_DEVICE_NUMBER : u8 = 32 ;
8-
98const PCI_CONFIG_ADDRESS_ENABLE : u32 = 1 << 31 ;
109
1110const CONFIG_ADDRESS : Port < u32 > = Port :: new ( 0xcf8 ) ;
1211const CONFIG_DATA : Port < u32 > = Port :: new ( 0xcfc ) ;
1312
1413#[ derive( Debug , Copy , Clone ) ]
15- pub ( crate ) struct PciConfigRegion ;
14+ pub enum PciConfigRegion {
15+ Pci ( LegacyPciConfigRegion ) ,
16+ #[ cfg( feature = "acpi" ) ]
17+ PciE ( pcie:: McfgEntry ) ,
18+ }
19+
20+ impl ConfigRegionAccess for PciConfigRegion {
21+ unsafe fn read ( & self , address : PciAddress , offset : u16 ) -> u32 {
22+ match self {
23+ PciConfigRegion :: Pci ( entry) => unsafe { entry. read ( address, offset) } ,
24+ #[ cfg( feature = "acpi" ) ]
25+ PciConfigRegion :: PciE ( entry) => unsafe { entry. read ( address, offset) } ,
26+ }
27+ }
28+
29+ unsafe fn write ( & self , address : PciAddress , offset : u16 , value : u32 ) {
30+ match self {
31+ PciConfigRegion :: Pci ( entry) => unsafe {
32+ entry. write ( address, offset, value) ;
33+ } ,
34+ #[ cfg( feature = "acpi" ) ]
35+ PciConfigRegion :: PciE ( entry) => unsafe {
36+ entry. write ( address, offset, value) ;
37+ } ,
38+ }
39+ }
40+ }
41+
42+ #[ derive( Debug , Copy , Clone ) ]
43+ pub ( crate ) struct LegacyPciConfigRegion ;
1644
17- impl PciConfigRegion {
45+ impl LegacyPciConfigRegion {
1846 pub const fn new ( ) -> Self {
1947 Self { }
2048 }
2149}
2250
23- impl ConfigRegionAccess for PciConfigRegion {
51+ impl ConfigRegionAccess for LegacyPciConfigRegion {
2452 #[ inline]
2553 unsafe fn read ( & self , pci_addr : PciAddress , register : u16 ) -> u32 {
2654 let mut config_address = CONFIG_ADDRESS ;
@@ -57,13 +85,29 @@ impl ConfigRegionAccess for PciConfigRegion {
5785}
5886
5987pub ( crate ) fn init ( ) {
60- debug ! ( "Scanning PCI Busses 0 to {}" , PCI_MAX_BUS_NUMBER - 1 ) ;
88+ #[ cfg( feature = "acpi" ) ]
89+ if pcie:: init_pcie ( ) {
90+ info ! ( "Initialized PCIe" ) ;
91+ return ;
92+ }
93+
94+ // For Hermit, we currently limit scanning to the first 32 buses.
95+ const PCI_MAX_BUS_NUMBER : u8 = 32 ;
96+ scan_bus (
97+ 0 ..PCI_MAX_BUS_NUMBER ,
98+ PciConfigRegion :: Pci ( LegacyPciConfigRegion :: new ( ) ) ,
99+ ) ;
100+ info ! ( "Initialized PCI" ) ;
101+ }
102+
103+ fn scan_bus ( bus_range : Range < u8 > , pci_config : PciConfigRegion ) {
104+ debug ! ( "Scanning PCI buses {bus_range:?}" ) ;
61105
62106 // Hermit only uses PCI for network devices.
63107 // Therefore, multifunction devices as well as additional bridges are not scanned.
64- // We also limit scanning to the first 32 buses.
65- let pci_config = PciConfigRegion :: new ( ) ;
66- for bus in 0 .. PCI_MAX_BUS_NUMBER {
108+ for bus in bus_range {
109+ // For Hermit, we currently limit scanning to the first 32 devices.
110+ const PCI_MAX_DEVICE_NUMBER : u8 = 32 ;
67111 for device in 0 ..PCI_MAX_DEVICE_NUMBER {
68112 let pci_address = PciAddress :: new ( 0 , bus, device, 0 ) ;
69113 let header = PciHeader :: new ( pci_address) ;
@@ -76,3 +120,118 @@ pub(crate) fn init() {
76120 }
77121 }
78122}
123+
124+ #[ cfg( feature = "acpi" ) ]
125+ mod pcie {
126+ use core:: { ptr, slice} ;
127+
128+ use memory_addresses:: { PhysAddr , VirtAddr } ;
129+ use pci_types:: { ConfigRegionAccess , PciAddress } ;
130+
131+ use super :: PciConfigRegion ;
132+ use crate :: arch:: mm:: paging:: {
133+ self , LargePageSize , PageTableEntryFlags , PageTableEntryFlagsExt ,
134+ } ;
135+ use crate :: env:: kernel:: acpi;
136+ use crate :: mm:: device_alloc:: DeviceAlloc ;
137+
138+ pub fn init_pcie ( ) -> bool {
139+ let Some ( table) = acpi:: get_mcfg_table ( ) else {
140+ return false ;
141+ } ;
142+
143+ let start = ptr:: with_exposed_provenance :: < McfgEntry > ( table. table_start_address ( ) + 8 ) ;
144+ let end = ptr:: with_exposed_provenance :: < McfgEntry > ( table. table_end_address ( ) ) ;
145+ let entries = unsafe { slice:: from_ptr_range ( start..end) } ;
146+
147+ if entries. is_empty ( ) {
148+ return false ;
149+ }
150+
151+ for entry in entries {
152+ init_pcie_bus ( entry) ;
153+ }
154+
155+ true
156+ }
157+
158+ #[ derive( Clone , Copy , Debug ) ]
159+ #[ repr( C , packed) ]
160+ pub struct McfgEntry {
161+ pub base_address : u64 ,
162+ pub pci_segment_group : u16 ,
163+ pub bus_number_start : u8 ,
164+ pub bus_number_end : u8 ,
165+ _reserved : u32 ,
166+ }
167+
168+ impl McfgEntry {
169+ pub fn pci_config_space_address (
170+ & self ,
171+ bus_number : u8 ,
172+ device : u8 ,
173+ function : u8 ,
174+ ) -> PhysAddr {
175+ PhysAddr :: new (
176+ self . base_address
177+ + ( ( u64:: from ( bus_number) << 20 )
178+ | ( ( u64:: from ( device) & 0x1f ) << 15 )
179+ | ( ( u64:: from ( function) & 0x7 ) << 12 ) ) ,
180+ )
181+ }
182+ }
183+
184+ impl ConfigRegionAccess for McfgEntry {
185+ unsafe fn read ( & self , address : PciAddress , offset : u16 ) -> u32 {
186+ assert ! ( address. segment( ) == self . pci_segment_group) ;
187+ assert ! ( address. bus( ) >= self . bus_number_start) ;
188+ assert ! ( address. bus( ) <= self . bus_number_end) ;
189+
190+ let phys_addr =
191+ self . pci_config_space_address ( address. bus ( ) , address. device ( ) , address. function ( ) )
192+ + u64:: from ( offset) ;
193+ let ptr = DeviceAlloc . ptr_from :: < u32 > ( phys_addr) ;
194+
195+ unsafe { ptr. read_volatile ( ) }
196+ }
197+
198+ unsafe fn write ( & self , address : PciAddress , offset : u16 , value : u32 ) {
199+ assert ! ( address. segment( ) == self . pci_segment_group) ;
200+ assert ! ( address. bus( ) >= self . bus_number_start) ;
201+ assert ! ( address. bus( ) <= self . bus_number_end) ;
202+
203+ let phys_addr =
204+ self . pci_config_space_address ( address. bus ( ) , address. device ( ) , address. function ( ) )
205+ + u64:: from ( offset) ;
206+ let ptr = DeviceAlloc . ptr_from :: < u32 > ( phys_addr) ;
207+
208+ unsafe {
209+ ptr. write_volatile ( value) ;
210+ }
211+ }
212+ }
213+
214+ fn init_pcie_bus ( bus_entry : & McfgEntry ) {
215+ let phys_addr = PhysAddr :: new ( bus_entry. base_address ) ;
216+ let virt_addr = VirtAddr :: from_ptr ( DeviceAlloc . ptr_from :: < ( ) > ( phys_addr) ) ;
217+ if paging:: virtual_to_physical ( virt_addr) != Some ( phys_addr) {
218+ debug ! ( "Mapping PCIe memory" ) ;
219+ let flags = {
220+ let mut flags = PageTableEntryFlags :: empty ( ) ;
221+ flags. normal ( ) . writable ( ) . execute_disable ( ) ;
222+ flags
223+ } ;
224+ paging:: map :: < LargePageSize > (
225+ virt_addr,
226+ phys_addr,
227+ bus_entry. bus_number_end . into ( ) ,
228+ flags,
229+ ) ;
230+ }
231+
232+ super :: scan_bus (
233+ bus_entry. bus_number_start ..bus_entry. bus_number_end ,
234+ PciConfigRegion :: PciE ( * bus_entry) ,
235+ ) ;
236+ }
237+ }
0 commit comments