@@ -12,14 +12,37 @@ const CONFIG_ADDRESS: Port<u32> = Port::new(0xcf8);
12
12
const CONFIG_DATA : Port < u32 > = Port :: new ( 0xcfc ) ;
13
13
14
14
#[ derive( Debug , Copy , Clone ) ]
15
- pub ( crate ) struct PciConfigRegion ;
15
+ struct PciConfigRegion ;
16
16
17
17
impl PciConfigRegion {
18
18
pub const fn new ( ) -> Self {
19
19
Self { }
20
20
}
21
21
}
22
22
23
+ #[ derive( Debug , Copy , Clone ) ]
24
+ pub enum PciConfigAccess {
25
+ PciConfigRegion ( PciConfigRegion ) ,
26
+ #[ cfg( feature = "acpi" ) ]
27
+ PcieConfigRegion ( pcie:: McfgTableEntry ) ,
28
+ }
29
+
30
+ impl ConfigRegionAccess for PciConfigAccess {
31
+ unsafe fn read ( & self , address : PciAddress , offset : u16 ) -> u32 {
32
+ match self {
33
+ PciConfigAccess :: PciConfigRegion ( entry) => entry. read ( address, offset) ,
34
+ PciConfigAccess :: PcieConfigRegion ( entry) => entry. read ( address, offset) ,
35
+ }
36
+ }
37
+
38
+ unsafe fn write ( & self , address : PciAddress , offset : u16 , value : u32 ) {
39
+ match self {
40
+ PciConfigAccess :: PciConfigRegion ( entry) => entry. write ( address, offset, value) ,
41
+ PciConfigAccess :: PcieConfigRegion ( entry) => entry. write ( address, offset, value) ,
42
+ }
43
+ }
44
+ }
45
+
23
46
impl ConfigRegionAccess for PciConfigRegion {
24
47
#[ inline]
25
48
unsafe fn read ( & self , pci_addr : PciAddress , register : u16 ) -> u32 {
@@ -59,20 +82,135 @@ impl ConfigRegionAccess for PciConfigRegion {
59
82
pub ( crate ) fn init ( ) {
60
83
debug ! ( "Scanning PCI Busses 0 to {}" , PCI_MAX_BUS_NUMBER - 1 ) ;
61
84
85
+ #[ cfg( feature = "acpi" ) ]
86
+ if pcie:: init_pcie ( ) { return ; }
87
+
88
+ enumerate_devices ( 0 , PCI_MAX_BUS_NUMBER , PciConfigAccess :: PciConfigRegion ( PciConfigRegion :: new ( ) ) )
89
+ }
90
+
91
+ fn enumerate_devices ( bus_start : u8 , bus_end : u8 , access : PciConfigAccess ) {
62
92
// Hermit only uses PCI for network devices.
63
93
// Therefore, multifunction devices as well as additional bridges are not scanned.
64
94
// We also limit scanning to the first 32 buses.
65
- let pci_config = PciConfigRegion :: new ( ) ;
66
- for bus in 0 ..PCI_MAX_BUS_NUMBER {
95
+ for bus in bus_start..bus_end {
67
96
for device in 0 ..PCI_MAX_DEVICE_NUMBER {
68
97
let pci_address = PciAddress :: new ( 0 , bus, device, 0 ) ;
69
98
let header = PciHeader :: new ( pci_address) ;
70
99
71
- let ( device_id, vendor_id) = header. id ( pci_config ) ;
100
+ let ( device_id, vendor_id) = header. id ( access ) ;
72
101
if device_id != u16:: MAX && vendor_id != u16:: MAX {
73
- let device = PciDevice :: new ( pci_address, pci_config ) ;
102
+ let device = PciDevice :: new ( pci_address, access ) ;
74
103
PCI_DEVICES . with ( |pci_devices| pci_devices. unwrap ( ) . push ( device) ) ;
75
104
}
76
105
}
77
106
}
78
107
}
108
+
109
+ #[ cfg( feature = "acpi" ) ]
110
+ mod pcie {
111
+ use core:: ptr;
112
+ use pci_types:: { ConfigRegionAccess , PciAddress } ;
113
+ use memory_addresses:: { PhysAddr , VirtAddr } ;
114
+ use super :: { PciConfigAccess , PCI_MAX_BUS_NUMBER } ;
115
+ use crate :: env;
116
+ use crate :: env:: kernel:: acpi;
117
+
118
+ pub fn init_pcie ( ) -> bool {
119
+ let Some ( table) = acpi:: get_mcfg_table ( ) else { return false ; } ;
120
+
121
+ let mut start_addr: * const McfgTableEntry = core:: ptr:: with_exposed_provenance ( table. table_start_address ( ) + 8 ) ;
122
+ let end_addr: * const McfgTableEntry = core:: ptr:: with_exposed_provenance ( table. table_end_address ( ) + 8 ) ;
123
+
124
+ if start_addr == end_addr {
125
+ return false ;
126
+ }
127
+
128
+ while start_addr < end_addr {
129
+ unsafe {
130
+ let read = ptr:: read_unaligned ( start_addr) ;
131
+ init_pcie_bus ( read) ;
132
+ start_addr = start_addr. add ( 1 ) ;
133
+ }
134
+ }
135
+
136
+ true
137
+ }
138
+
139
+ #[ derive( Debug ) ]
140
+ #[ repr( C ) ]
141
+ struct PcieDeviceConfig {
142
+ vendor_id : u16 ,
143
+ device_id : u16 ,
144
+ _reserved : [ u8 ; 4096 - 8 ]
145
+ }
146
+
147
+ #[ derive( Debug , Copy , Clone ) ]
148
+ #[ repr( C ) ]
149
+ pub ( crate ) struct McfgTableEntry {
150
+ pub base_address : u64 ,
151
+ pub pci_segment_number : u16 ,
152
+ pub start_pci_bus : u8 ,
153
+ pub end_pci_bus : u8 ,
154
+ _reserved : u32
155
+ }
156
+
157
+ impl McfgTableEntry {
158
+ pub fn pci_config_space_address ( & self , bus_number : u8 , device : u8 , function : u8 ) -> PhysAddr {
159
+ PhysAddr :: new (
160
+ self . base_address +
161
+ ( ( bus_number as u64 ) << 20 ) |
162
+ ( ( ( device as u64 ) & 0x1f ) << 15 ) |
163
+ ( ( ( function as u64 ) & 0x7 ) << 12 )
164
+ )
165
+ }
166
+ }
167
+
168
+ #[ derive( Debug ) ]
169
+ #[ cfg( feature = "pci" ) ]
170
+ struct McfgTable ( alloc:: vec:: Vec < McfgTableEntry > ) ;
171
+
172
+ impl PcieDeviceConfig {
173
+ fn get < ' a > ( physical_address : PhysAddr ) -> & ' a Self {
174
+ assert ! ( env:: is_uefi( ) ) ;
175
+
176
+ // For UEFI Systems, the tables are already mapped so we only need to return a proper reference to the table
177
+ let allocated_virtual_address = VirtAddr :: new ( physical_address. as_u64 ( ) ) ;
178
+ let ptr: * const PcieDeviceConfig = allocated_virtual_address. as_ptr ( ) ;
179
+
180
+ unsafe { ptr. as_ref ( ) . unwrap ( ) }
181
+ }
182
+ }
183
+
184
+ impl ConfigRegionAccess for McfgTableEntry {
185
+ unsafe fn read ( & self , address : PciAddress , offset : u16 ) -> u32 {
186
+ assert_eq ! ( address. segment( ) , self . pci_segment_number) ;
187
+ assert ! ( address. bus( ) >= self . start_pci_bus) ;
188
+ assert ! ( address. bus( ) <= self . end_pci_bus) ;
189
+
190
+ let ptr = self . pci_config_space_address ( address. bus ( ) , address. device ( ) , address. function ( ) ) + offset as u64 ;
191
+ let ptr = ptr. as_usize ( ) as * const u32 ;
192
+
193
+ unsafe { * ptr }
194
+ }
195
+
196
+ unsafe fn write ( & self , address : PciAddress , offset : u16 , value : u32 ) {
197
+ assert_eq ! ( address. segment( ) , self . pci_segment_number) ;
198
+ assert ! ( address. bus( ) >= self . start_pci_bus) ;
199
+ assert ! ( address. bus( ) <= self . end_pci_bus) ;
200
+
201
+ let ptr = self . pci_config_space_address ( address. bus ( ) , address. device ( ) , address. function ( ) ) + offset as u64 ;
202
+ let ptr = ptr. as_usize ( ) as * mut u32 ;
203
+
204
+ unsafe { * ptr = value; }
205
+ }
206
+ }
207
+
208
+ fn init_pcie_bus ( bus_entry : McfgTableEntry ) {
209
+ if bus_entry. start_pci_bus > PCI_MAX_BUS_NUMBER {
210
+ return ;
211
+ }
212
+
213
+ let end = if bus_entry. end_pci_bus > PCI_MAX_BUS_NUMBER { PCI_MAX_BUS_NUMBER } else { bus_entry. end_pci_bus } ;
214
+ super :: enumerate_devices ( bus_entry. start_pci_bus , end, PciConfigAccess :: PcieConfigRegion ( bus_entry) ) ;
215
+ }
216
+ }
0 commit comments