1use memory_range::MemoryRange;
7use thiserror::Error;
8
9const PAGE_SIZE: u64 = 4096;
10const FOUR_GB: u64 = 0x1_0000_0000;
11
12#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
14#[cfg_attr(feature = "mesh", derive(mesh_protobuf::Protobuf))]
15#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
16pub struct MemoryRangeWithNode {
17 pub range: MemoryRange,
19 pub vnode: u32,
21}
22
23impl core::fmt::Display for MemoryRangeWithNode {
24 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
25 write!(f, "{}({})", self.range, self.vnode)
26 }
27}
28
29#[derive(Debug, Clone)]
31#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
32pub struct MemoryLayout {
33 #[cfg_attr(feature = "inspect", inspect(with = "inspect_ranges_with_metadata"))]
34 ram: Vec<MemoryRangeWithNode>,
35 #[cfg_attr(feature = "inspect", inspect(with = "inspect_ranges"))]
36 mmio: Vec<MemoryRange>,
37 #[cfg_attr(feature = "inspect", inspect(with = "inspect_ranges"))]
38 pci_ecam: Vec<MemoryRange>,
39 #[cfg_attr(feature = "inspect", inspect(with = "inspect_ranges"))]
40 pci_mmio: Vec<MemoryRange>,
41 vtl2_range: Option<MemoryRange>,
44}
45
46#[cfg(feature = "inspect")]
47fn inspect_ranges(ranges: &[MemoryRange]) -> impl '_ + inspect::Inspect {
48 inspect::iter_by_key(ranges.iter().map(|range| {
49 (
50 range.to_string(),
51 inspect::adhoc(|i| {
52 i.respond().hex("length", range.len());
53 }),
54 )
55 }))
56}
57
58#[cfg(feature = "inspect")]
59fn inspect_ranges_with_metadata(ranges: &[MemoryRangeWithNode]) -> impl '_ + inspect::Inspect {
60 inspect::iter_by_key(ranges.iter().map(|range| {
61 (
62 range.range.to_string(),
63 inspect::adhoc(|i| {
64 i.respond()
65 .hex("length", range.range.len())
66 .hex("vnode", range.vnode);
67 }),
68 )
69 }))
70}
71
72#[derive(Debug, Error)]
74pub enum Error {
75 #[error("invalid memory size")]
77 BadSize,
78 #[error("invalid MMIO gap configuration")]
80 BadMmioGaps,
81 #[error("invalid memory or MMIO ranges")]
83 BadMemoryRanges,
84 #[error("vtl2 range is below end of ram")]
86 Vtl2RangeBeforeEndOfRam,
87}
88
89fn validate_ranges(ranges: &[MemoryRange]) -> Result<(), Error> {
90 validate_ranges_core(ranges, |x| x)
91}
92
93fn validate_ranges_with_metadata(ranges: &[MemoryRangeWithNode]) -> Result<(), Error> {
94 validate_ranges_core(ranges, |x| &x.range)
95}
96
97fn validate_ranges_core<T>(ranges: &[T], getter: impl Fn(&T) -> &MemoryRange) -> Result<(), Error> {
100 if ranges.iter().any(|x| getter(x).is_empty())
101 || !ranges.iter().zip(ranges.iter().skip(1)).all(|(x, y)| {
102 let x = getter(x);
103 let y = getter(y);
104 x <= y && !x.overlaps(y)
105 })
106 {
107 return Err(Error::BadMemoryRanges);
108 }
109
110 Ok(())
111}
112
113#[derive(Debug, Copy, Clone, PartialEq, Eq)]
115pub enum AddressType {
116 Ram,
118 Mmio,
120 PciEcam,
122 PciMmio,
124}
125
126impl MemoryLayout {
127 pub fn new(
139 ram_size: u64,
140 mmio_gaps: &[MemoryRange],
141 pci_ecam_gaps: &[MemoryRange],
142 pci_mmio_gaps: &[MemoryRange],
143 vtl2_range: Option<MemoryRange>,
144 ) -> Result<Self, Error> {
145 if ram_size == 0 || ram_size & (PAGE_SIZE - 1) != 0 {
146 return Err(Error::BadSize);
147 }
148
149 validate_ranges(mmio_gaps)?;
150 validate_ranges(pci_ecam_gaps)?;
151 validate_ranges(pci_mmio_gaps)?;
152
153 let mut combined_gaps = mmio_gaps
154 .iter()
155 .chain(pci_ecam_gaps)
156 .chain(pci_mmio_gaps)
157 .copied()
158 .collect::<Vec<_>>();
159 combined_gaps.sort();
160 validate_ranges(&combined_gaps)?;
161
162 let mut ram = Vec::new();
163 let mut remaining = ram_size;
164 let mut remaining_gaps = combined_gaps.iter().cloned();
165 let mut last_end = 0;
166
167 while remaining > 0 {
168 let (this, next_end) = if let Some(gap) = remaining_gaps.next() {
169 (remaining.min(gap.start() - last_end), gap.end())
170 } else {
171 (remaining, 0)
172 };
173
174 if this > 0 {
175 ram.push(MemoryRangeWithNode {
176 range: MemoryRange::new(last_end..last_end + this),
177 vnode: 0,
178 });
179 }
180 remaining -= this;
181 last_end = next_end;
182 }
183
184 Self::build(
185 ram,
186 mmio_gaps.to_vec(),
187 pci_ecam_gaps.to_vec(),
188 pci_mmio_gaps.to_vec(),
189 vtl2_range,
190 )
191 }
192
193 pub fn new_from_ranges(
199 memory: &[MemoryRangeWithNode],
200 gaps: &[MemoryRange],
201 ) -> Result<Self, Error> {
202 validate_ranges_with_metadata(memory)?;
203 validate_ranges(gaps)?;
204 Self::build(memory.to_vec(), gaps.to_vec(), vec![], vec![], None)
205 }
206
207 fn build(
211 ram: Vec<MemoryRangeWithNode>,
212 mmio: Vec<MemoryRange>,
213 pci_ecam: Vec<MemoryRange>,
214 pci_mmio: Vec<MemoryRange>,
215 vtl2_range: Option<MemoryRange>,
216 ) -> Result<Self, Error> {
217 let mut all_ranges = ram
218 .iter()
219 .map(|x| &x.range)
220 .chain(&mmio)
221 .chain(&vtl2_range)
222 .chain(&pci_ecam)
223 .chain(&pci_mmio)
224 .copied()
225 .collect::<Vec<_>>();
226
227 all_ranges.sort();
228 validate_ranges(&all_ranges)?;
229
230 if all_ranges
231 .iter()
232 .zip(all_ranges.iter().skip(1))
233 .any(|(x, y)| x.overlaps(y))
234 {
235 return Err(Error::BadMemoryRanges);
236 }
237
238 let last_ram_entry = ram.last().ok_or(Error::BadMemoryRanges)?;
239 let end_of_ram = last_ram_entry.range.end();
240
241 if let Some(range) = vtl2_range {
242 if range.start() < end_of_ram {
243 return Err(Error::Vtl2RangeBeforeEndOfRam);
244 }
245 }
246
247 Ok(Self {
248 ram,
249 mmio,
250 pci_ecam,
251 pci_mmio,
252 vtl2_range,
253 })
254 }
255
256 pub fn mmio(&self) -> &[MemoryRange] {
258 &self.mmio
259 }
260
261 pub fn ram(&self) -> &[MemoryRangeWithNode] {
263 &self.ram
264 }
265
266 pub fn vtl2_range(&self) -> Option<MemoryRange> {
270 self.vtl2_range
271 }
272
273 pub fn ram_size(&self) -> u64 {
275 self.ram.iter().map(|r| r.range.len()).sum()
276 }
277
278 pub fn end_of_ram(&self) -> u64 {
280 self.ram.last().expect("mmio set").range.end()
282 }
283
284 pub fn ram_below_4gb(&self) -> u64 {
286 self.ram
287 .iter()
288 .filter(|r| r.range.end() < FOUR_GB)
289 .map(|r| r.range.len())
290 .sum()
291 }
292
293 pub fn ram_above_4gb(&self) -> u64 {
295 self.ram
296 .iter()
297 .filter(|r| r.range.end() >= FOUR_GB)
298 .map(|r| r.range.len())
299 .sum()
300 }
301
302 pub fn ram_above_high_mmio(&self) -> Option<u64> {
306 if self.mmio.len() != 2 {
307 return None;
308 }
309
310 Some(
311 self.ram
312 .iter()
313 .filter(|r| r.range.start() >= self.mmio[1].end())
314 .map(|r| r.range.len())
315 .sum(),
316 )
317 }
318
319 pub fn max_ram_below_4gb(&self) -> Option<u64> {
323 Some(
324 self.ram
325 .iter()
326 .rev()
327 .find(|r| r.range.end() < FOUR_GB)?
328 .range
329 .end(),
330 )
331 }
332
333 pub fn end_of_layout(&self) -> u64 {
335 [
336 self.mmio.last().expect("mmio set").end(),
337 self.end_of_ram(),
338 self.pci_ecam.last().map(|r| r.end()).unwrap_or(0),
339 self.pci_mmio.last().map(|r| r.end()).unwrap_or(0),
340 ]
341 .into_iter()
342 .max()
343 .unwrap()
344 }
345
346 pub fn probe_address(&self, address: u64) -> Option<AddressType> {
352 let ranges = self
353 .ram
354 .iter()
355 .map(|r| (&r.range, AddressType::Ram))
356 .chain(self.mmio.iter().map(|r| (r, AddressType::Mmio)))
357 .chain(self.pci_ecam.iter().map(|r| (r, AddressType::PciEcam)))
358 .chain(self.pci_mmio.iter().map(|r| (r, AddressType::PciMmio)));
359
360 for (range, address_type) in ranges {
361 if range.contains_addr(address) {
362 return Some(address_type);
363 }
364 }
365
366 None
367 }
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 const KB: u64 = 1024;
375 const MB: u64 = 1024 * KB;
376 const GB: u64 = 1024 * MB;
377 const TB: u64 = 1024 * GB;
378
379 #[test]
380 fn layout() {
381 let mmio = &[
382 MemoryRange::new(GB..2 * GB),
383 MemoryRange::new(3 * GB..4 * GB),
384 ];
385 let ram = &[
386 MemoryRangeWithNode {
387 range: MemoryRange::new(0..GB),
388 vnode: 0,
389 },
390 MemoryRangeWithNode {
391 range: MemoryRange::new(2 * GB..3 * GB),
392 vnode: 0,
393 },
394 MemoryRangeWithNode {
395 range: MemoryRange::new(4 * GB..TB + 2 * GB),
396 vnode: 0,
397 },
398 ];
399
400 let layout = MemoryLayout::new(TB, mmio, &[], &[], None).unwrap();
401 assert_eq!(
402 layout.ram(),
403 &[
404 MemoryRangeWithNode {
405 range: MemoryRange::new(0..GB),
406 vnode: 0
407 },
408 MemoryRangeWithNode {
409 range: MemoryRange::new(2 * GB..3 * GB),
410 vnode: 0
411 },
412 MemoryRangeWithNode {
413 range: MemoryRange::new(4 * GB..TB + 2 * GB),
414 vnode: 0
415 },
416 ]
417 );
418 assert_eq!(layout.mmio(), mmio);
419 assert_eq!(layout.ram_size(), TB);
420 assert_eq!(layout.end_of_ram(), TB + 2 * GB);
421 assert_eq!(layout.end_of_layout(), TB + 2 * GB);
422
423 let layout = MemoryLayout::new_from_ranges(ram, mmio).unwrap();
424 assert_eq!(
425 layout.ram(),
426 &[
427 MemoryRangeWithNode {
428 range: MemoryRange::new(0..GB),
429 vnode: 0
430 },
431 MemoryRangeWithNode {
432 range: MemoryRange::new(2 * GB..3 * GB),
433 vnode: 0
434 },
435 MemoryRangeWithNode {
436 range: MemoryRange::new(4 * GB..TB + 2 * GB),
437 vnode: 0
438 },
439 ]
440 );
441 assert_eq!(layout.mmio(), mmio);
442 assert_eq!(layout.ram_size(), TB);
443 assert_eq!(layout.end_of_ram(), TB + 2 * GB);
444 assert_eq!(layout.end_of_layout(), TB + 2 * GB);
445 }
446
447 #[test]
448 fn bad_layout() {
449 MemoryLayout::new(TB + 1, &[], &[], &[], None).unwrap_err();
450 let mmio = &[
451 MemoryRange::new(3 * GB..4 * GB),
452 MemoryRange::new(GB..2 * GB),
453 ];
454 MemoryLayout::new(TB, mmio, &[], &[], None).unwrap_err();
455
456 MemoryLayout::new_from_ranges(&[], mmio).unwrap_err();
457
458 let ram = &[MemoryRangeWithNode {
459 range: MemoryRange::new(0..GB),
460 vnode: 0,
461 }];
462 MemoryLayout::new_from_ranges(ram, mmio).unwrap_err();
463
464 let ram = &[MemoryRangeWithNode {
465 range: MemoryRange::new(0..GB + MB),
466 vnode: 0,
467 }];
468 let mmio = &[
469 MemoryRange::new(GB..2 * GB),
470 MemoryRange::new(3 * GB..4 * GB),
471 ];
472 MemoryLayout::new_from_ranges(ram, mmio).unwrap_err();
473
474 let mmio = &[
475 MemoryRange::new(GB..2 * GB),
476 MemoryRange::new(3 * GB..4 * GB),
477 ];
478 let pci_ecam = &[MemoryRange::new(GB..GB + MB)];
479 MemoryLayout::new(TB, mmio, pci_ecam, &[], None).unwrap_err();
480
481 let mmio = &[
482 MemoryRange::new(GB..2 * GB),
483 MemoryRange::new(3 * GB..4 * GB),
484 ];
485 let pci_mmio = &[MemoryRange::new(GB..GB + MB)];
486 MemoryLayout::new(TB, mmio, &[], pci_mmio, None).unwrap_err();
487
488 let pci_ecam = &[MemoryRange::new(GB..GB + MB)];
489 let pci_mmio = &[MemoryRange::new(GB..GB + MB)];
490 MemoryLayout::new(TB, &[], pci_ecam, pci_mmio, None).unwrap_err();
491 }
492
493 #[test]
494 fn pci_ranges() {
495 let mmio = &[MemoryRange::new(3 * GB..4 * GB)];
496 let pci_ecam = &[MemoryRange::new(2 * TB - GB..2 * TB)];
497 let pci_mmio = &[
498 MemoryRange::new(2 * GB..3 * GB),
499 MemoryRange::new(5 * GB..6 * GB),
500 ];
501
502 let layout = MemoryLayout::new(TB, mmio, pci_ecam, pci_mmio, None).unwrap();
503 assert_eq!(
504 layout.ram(),
505 &[
506 MemoryRangeWithNode {
507 range: MemoryRange::new(0..2 * GB),
508 vnode: 0,
509 },
510 MemoryRangeWithNode {
511 range: MemoryRange::new(4 * GB..5 * GB),
512 vnode: 0,
513 },
514 MemoryRangeWithNode {
515 range: MemoryRange::new(6 * GB..TB + 3 * GB),
516 vnode: 0,
517 },
518 ]
519 );
520 assert_eq!(layout.end_of_layout(), 2 * TB);
521
522 assert_eq!(layout.probe_address(2 * GB), Some(AddressType::PciMmio));
523 assert_eq!(
524 layout.probe_address(2 * GB + MB),
525 Some(AddressType::PciMmio)
526 );
527 assert_eq!(layout.probe_address(5 * GB), Some(AddressType::PciMmio));
528 assert_eq!(
529 layout.probe_address(5 * GB + MB),
530 Some(AddressType::PciMmio)
531 );
532 assert_eq!(
533 layout.probe_address(2 * TB - GB),
534 Some(AddressType::PciEcam)
535 );
536 }
537
538 #[test]
539 fn probe_address() {
540 let mmio = &[
541 MemoryRange::new(GB..2 * GB),
542 MemoryRange::new(3 * GB..4 * GB),
543 ];
544 let ram = &[
545 MemoryRangeWithNode {
546 range: MemoryRange::new(0..GB),
547 vnode: 0,
548 },
549 MemoryRangeWithNode {
550 range: MemoryRange::new(2 * GB..3 * GB),
551 vnode: 0,
552 },
553 MemoryRangeWithNode {
554 range: MemoryRange::new(4 * GB..TB + 2 * GB),
555 vnode: 0,
556 },
557 ];
558
559 let layout = MemoryLayout::new_from_ranges(ram, mmio).unwrap();
560
561 assert_eq!(layout.probe_address(0), Some(AddressType::Ram));
562 assert_eq!(layout.probe_address(256), Some(AddressType::Ram));
563 assert_eq!(layout.probe_address(2 * GB), Some(AddressType::Ram));
564 assert_eq!(layout.probe_address(4 * GB), Some(AddressType::Ram));
565 assert_eq!(layout.probe_address(TB), Some(AddressType::Ram));
566 assert_eq!(layout.probe_address(TB + 1), Some(AddressType::Ram));
567
568 assert_eq!(layout.probe_address(GB), Some(AddressType::Mmio));
569 assert_eq!(layout.probe_address(GB + 123), Some(AddressType::Mmio));
570 assert_eq!(layout.probe_address(3 * GB), Some(AddressType::Mmio));
571
572 assert_eq!(layout.probe_address(TB + 2 * GB), None);
573 assert_eq!(layout.probe_address(TB + 3 * GB), None);
574 assert_eq!(layout.probe_address(4 * TB), None);
575 }
576}