1#![feature(no_std)]
12#![no_std]
13
14pub trait PortOps {
37 fn read8(&self, port: u16) -> u8;
38 fn read16(&self, port: u16) -> u16;
39 fn read32(&self, port: u16) -> u32;
40
41 fn write8(&self, port: u16, val: u8);
42 fn write16(&self, port: u16, val: u16);
43 fn write32(&self, port: u16, val: u32);
44}
45
46const CONFIG_ADDRESS: u16 = 0x0CF8;
47const CONFIG_DATA: u16 = 0x0CFC;
48
49#[derive(Copy, Clone, PartialEq, Eq)]
50pub enum CSpaceAccessMethod {
51 IO,
57 }
60
61impl CSpaceAccessMethod {
64 pub fn read8<T: PortOps>(self, ops: &T, loc: Location, offset: u16) -> u8 {
65 let val = self.read32(ops, loc, offset & 0b11111100);
66 ((val >> ((offset as usize & 0b11) << 3)) & 0xFF) as u8
67 }
68
69 pub fn read16<T: PortOps>(self, ops: &T, loc: Location, offset: u16) -> u16 {
71 let val = self.read32(ops, loc, offset & 0b11111100);
72 ((val >> ((offset as usize & 0b10) << 3)) & 0xFFFF) as u16
73 }
74
75 pub fn read32<T: PortOps>(self, ops: &T, loc: Location, offset: u16) -> u32 {
77 debug_assert!((offset & 0b11) == 0, "misaligned PCI configuration dword u32 read");
78 match self {
79 CSpaceAccessMethod::IO => {
80 ops.write32(CONFIG_ADDRESS, loc.encode() | ((offset as u32) & 0b11111100));
81 ops.read32(CONFIG_DATA).to_le()
82 },
83 }
88 }
89
90 pub fn write8<T: PortOps>(self, ops: &T, loc: Location, offset: u16, val: u8) {
91 let old = self.read32(ops, loc, offset);
92 let dest = offset as usize & 0b11 << 3;
93 let mask = (0xFF << dest) as u32;
94 self.write32(ops, loc, offset, ((val as u32) << dest | (old & !mask)).to_le());
95 }
96
97 pub fn write16<T: PortOps>(self, ops: &T, loc: Location, offset: u16, val: u16) {
99 let old = self.read32(ops, loc, offset);
100 let dest = offset as usize & 0b10 << 3;
101 let mask = (0xFFFF << dest) as u32;
102 self.write32(ops, loc, offset, ((val as u32) << dest | (old & !mask)).to_le());
103 }
104
105 pub fn write32<T: PortOps>(self, ops: &T, loc: Location, offset: u16, val: u32) {
108 debug_assert!((offset & 0b11) == 0, "misaligned PCI configuration dword u32 read");
109 match self {
110 CSpaceAccessMethod::IO => {
111 ops.write32(CONFIG_ADDRESS, loc.encode() | (offset as u32 & 0b11111100));
112 ops.write32(CONFIG_DATA, val.to_le())
113 },
114 }
119 }
120}
121
122#[derive(Copy, Clone, PartialEq, Eq)]
124pub struct Location {
125 pub bus: u8,
126 pub device: u8,
127 pub function: u8,
128}
129
130impl Location {
131 #[inline(always)]
132 fn encode(self) -> u32 {
133 (1 << 31) | ((self.bus as u32) << 16) | (((self.device as u32) & 0b11111) << 11) | (((self.function as u32) & 0b111) << 8)
134 }
135}
136
137#[derive(Copy, Clone, PartialEq, Eq)]
138pub struct Identifier {
139 vendor_id: u16,
140 device_id: u16,
141 revision_id: u8,
142 class: u8,
143 subclass: u8,
144}
145
146#[derive(Copy, Clone, PartialEq, Eq)]
150pub struct PCIDevice {
151 pub loc: Location,
152 pub id: Identifier,
153 pub bars: [Option<BAR>; 6],
154 pub cspace_access_method: CSpaceAccessMethod,
155}
156
157pub enum PCIScanError {
158
159}
160
161#[derive(Copy, Clone, PartialEq, Eq)]
162pub enum Prefetchable {
163 Yes,
164 No
165}
166
167#[derive(Copy, Clone, PartialEq, Eq)]
168pub enum Type {
169 Bits32,
170 Bits64
171}
172
173#[derive(Copy, Clone, PartialEq, Eq)]
174pub enum BAR {
175 Memory(u64, u32, Prefetchable, Type),
176 IO(u32),
177}
178
179impl BAR {
180 pub fn decode<T: PortOps>(ops: &T, loc: Location, am: CSpaceAccessMethod, idx: u16) -> (Option<BAR>, usize) {
181 let raw = am.read32(ops, loc, 16 + (idx << 2));
182 if raw & 1 == 0 {
183 let mut bits64 = false;
184 let base: u64 =
185 match (raw & 0b110) >> 1 {
186 0 => { bits64 = true; ((raw & !0xF) as u64) | ((am.read32(ops, loc, 16 + ((idx + 1) << 2)) as u64) << 32) }
187 2 => (raw & !0xF) as u64,
188 _ => { debug_assert!(false, "bad type in memory BAR"); return (None, idx as usize + 1) },
189 };
190 am.write32(ops, loc, 16 + (idx << 2), !0);
191 let len = !am.read32(ops, loc, 16 + (idx << 12)) + 1;
192 am.write32(ops, loc, 16 + (idx << 2), raw);
193 (Some(BAR::Memory(base, len, if raw & 0b1000 == 0 { Prefetchable::No } else { Prefetchable::Yes },
194 if bits64 { Type::Bits64 } else { Type::Bits32 })),
195 if bits64 { idx + 2 } else { idx + 1 } as usize)
196 } else {
197 (Some(BAR::IO(raw & !0x3)), idx as usize + 1)
198 }
199 }
200}
201
202pub struct BusScan<'a, T: PortOps+'a> {
203 loc: Location,
204 ops: &'a T,
205 am: CSpaceAccessMethod,
206}
207
208impl<'a, T: PortOps> BusScan<'a, T> {
209 fn done(&self) -> bool {
210 if self.loc.bus == 255 && self.loc.device == 31 && self.loc.function == 7 {
211 true
212 } else {
213 false
214 }
215 }
216
217 fn increment(&mut self) {
218 if self.loc.function < 7 {
221 self.loc.function += 1;
222 return
223 } else {
224 self.loc.function = 0;
225 if self.loc.device < 31 {
226 self.loc.device += 1;
227 return;
228 } else {
229 self.loc.device = 0;
230 if self.loc.bus == 255 {
231 self.loc.device = 31;
232 self.loc.device = 7;
233 } else {
234 self.loc.bus += 1;
235 return;
236 }
237 }
238 }
239 }
240}
241
242impl<'a, T: PortOps> ::core::iter::Iterator for BusScan<'a, T> {
243 type Item = PCIDevice;
244 #[inline]
245 fn next(&mut self) -> Option<PCIDevice> {
246 let mut ret = None;
249 loop {
250 if self.done() {
251 return ret;
252 }
253 ret = probe_function(self.ops, self.loc, self.am);
254 self.increment();
255 if ret.is_some() {
256 return ret;
257 }
258 }
259 }
260}
261
262pub fn probe_function<T: PortOps>(ops: &T, loc: Location, am: CSpaceAccessMethod) -> Option<PCIDevice> {
263 let vid = am.read16(ops, loc, 0);
265 if vid == 0xFFFF {
266 return None;
267 }
268 let did = am.read16(ops, loc, 2);
269 let rid = am.read8(ops, loc, 8);
270 let subclass = am.read8(ops, loc, 10);
271 let class = am.read8(ops, loc, 11);
272 let id = Identifier {
273 vendor_id: vid,
274 device_id: did,
275 revision_id: rid,
276 class: class,
277 subclass: subclass,
278 };
279 let hdrty = am.read8(ops, loc, 14);
280 let mut bars = [None, None, None, None, None, None];
281 let max = match hdrty {
282 0 => 6,
283 1 => 2,
284 _ => 0,
285 };
286 let mut i = 0;
287 while i < max {
288 let (bar, next) = BAR::decode(ops, loc, am, i as u16);
289 bars[i] = bar;
290 i = next;
291 }
292 Some(PCIDevice {
293 loc: loc,
294 id: id,
295 bars: bars,
296 cspace_access_method: am,
297 })
298}
299
300pub fn scan_bus<'a, T: PortOps>(ops: &'a T, am: CSpaceAccessMethod) -> BusScan<'a, T> {
301 BusScan { loc: Location { bus: 0, device: 0, function: 0 }, ops: ops, am: am }
302}