ostd/boot/memory_region.rs
1// SPDX-License-Identifier: MPL-2.0
2//! Information of memory regions in the boot phase.
3use vstd::prelude::*;
4use vstd_extra::prelude::*;
5
6use crate::specs::arch::*;
7
8use crate::mm::{Paddr, Vaddr};
9use align_ext::AlignExt;
10use core::ops::Deref;
11
12//use crate::mm::{kspace::kernel_loaded_offset, Paddr, Vaddr, PAGE_SIZE};
13
14/// The type of initial memory regions that are needed for the kernel.
15#[verus_verify]
16#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
17pub enum MemoryRegionType {
18 /// Maybe points to an unplugged DIMM module. It's bad anyway.
19 BadMemory = 0,
20 /// Some holes not specified by the bootloader/firmware. It may be used for
21 /// I/O memory but we don't know for sure.
22 Unknown = 1,
23 /// In ACPI spec, this area needs to be preserved when sleeping.
24 NonVolatileSleep = 2,
25 /// Reserved by BIOS or bootloader, do not use.
26 Reserved = 3,
27 /// The place where kernel sections are loaded.
28 Kernel = 4,
29 /// The place where kernel modules (e.g. initrd) are loaded, could be reused.
30 Module = 5,
31 /// The memory region provided as the framebuffer.
32 Framebuffer = 6,
33 /// Once used in the boot phase. Kernel can reclaim it after initialization.
34 Reclaimable = 7,
35 /// Directly usable by the frame allocator.
36 Usable = 8,
37}
38
39/// The information of initial memory regions that are needed by the kernel.
40/// The sections are **not** guaranteed to not overlap. The region must be page aligned.
41#[verus_verify]
42#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
43pub struct MemoryRegion {
44 base: usize,
45 len: usize,
46 typ: MemoryRegionType,
47}
48
49verus! {
50
51impl MemoryRegion {
52 #[verifier::type_invariant]
53 closed spec fn type_inv(self) -> bool {
54 self.base + self.len <= MAX_PADDR
55 }
56
57 /// Whether `self` is contained in `old_region`, with the same type.
58 pub closed spec fn is_sub_region(self, old_region: Self) -> bool {
59 self.typ == old_region.typ && old_region.base <= self.base && self.base + self.len
60 <= old_region.base + old_region.len
61 }
62
63 /// Whether the region shares no address with `region`.
64 pub closed spec fn is_separate(self, region: Self) -> bool {
65 self.base + self.len <= region.base || region.base + region.len <= self.base
66 }
67
68 /// Whether both boundaries of the region are multiples of `align`.
69 pub closed spec fn aligned(self, align: int) -> bool {
70 self.base as int % align == 0 && (self.base + self.len) % align == 0
71 }
72}
73
74} // verus!
75#[verus_verify]
76impl MemoryRegion {
77 /// Constructs a valid memory region.
78 #[verus_verify(dual_spec)]
79 #[verus_spec(
80 requires
81 base + len <= MAX_PADDR,
82 returns
83 Self::new(base, len, typ),
84 )]
85 pub const fn new(base: Paddr, len: usize, typ: MemoryRegionType) -> Self {
86 MemoryRegion { base, len, typ }
87 }
88
89 /// Constructs a bad memory region.
90 #[verus_verify(dual_spec)]
91 #[verus_spec(returns Self::bad())]
92 pub const fn bad() -> Self {
93 MemoryRegion {
94 base: 0,
95 len: 0,
96 typ: MemoryRegionType::BadMemory,
97 }
98 }
99
100 /*
101 /// Constructs a memory region where kernel sections are loaded.
102 ///
103 /// Most boot protocols do not mark the place where the kernel loads as unusable. In this case,
104 /// we need to explicitly construct and append this memory region.
105 pub fn kernel() -> Self {
106 // These are physical addresses provided by the linker script.
107 extern "C" {
108 fn __kernel_start();
109 fn __kernel_end();
110 }
111 MemoryRegion {
112 base: __kernel_start as usize - kernel_loaded_offset(),
113 len: __kernel_end as usize - __kernel_start as usize,
114 typ: MemoryRegionType::Kernel,
115 }
116 }
117
118 /// Constructs a framebuffer memory region.
119 pub fn framebuffer(fb: &crate::boot::BootloaderFramebufferArg) -> Self {
120 Self {
121 base: fb.address,
122 len: (fb.width * fb.height * fb.bpp).div_ceil(8), // round up when divide with 8 (bits/Byte)
123 typ: MemoryRegionType::Framebuffer,
124 }
125 }
126
127 /// Constructs a module memory region from a byte slice that lives in the linear mapping.
128 ///
129 /// # Panics
130 ///
131 /// This method will panic if the byte slice does not live in the linear mapping.
132 pub fn module(bytes: &[u8]) -> Self {
133 let vaddr = bytes.as_ptr() as Vaddr;
134 assert!(crate::mm::kspace::LINEAR_MAPPING_VADDR_RANGE.contains(&vaddr));
135
136 Self {
137 base: vaddr - crate::mm::kspace::LINEAR_MAPPING_BASE_VADDR,
138 len: bytes.len(),
139 typ: MemoryRegionType::Reclaimable,
140 }
141 } */
142
143 /// The physical address of the base of the region.
144 #[verus_verify(dual_spec)]
145 #[verus_spec(returns self.base())]
146 pub fn base(&self) -> Paddr {
147 self.base
148 }
149
150 /// The length in bytes of the region.
151 #[verus_verify(dual_spec)]
152 #[verus_spec(returns self.len())]
153 pub fn len(&self) -> usize {
154 self.len
155 }
156
157 /// The physical address of the end of the region.
158 #[verus_verify(dual_spec)]
159 #[verus_spec(returns self.end())]
160 pub fn end(&self) -> Paddr {
161 proof! {
162 use_type_invariant(self);
163 }
164 self.base + self.len
165 }
166
167 /// Checks whether the region is empty
168 #[verus_verify(dual_spec)]
169 #[verus_spec(returns self.is_empty())]
170 pub fn is_empty(&self) -> bool {
171 self.len == 0
172 }
173
174 /// The type of the region.
175 #[verus_verify(dual_spec)]
176 #[verus_spec(returns self.typ())]
177 pub fn typ(&self) -> MemoryRegionType {
178 self.typ
179 }
180
181 /// Returns the region with boundaries rounded to `PAGE_SIZE` boundaries:
182 /// inward for `Usable` regions, outward for the others.
183 #[verus_spec(ret =>
184 requires
185 self.typ == MemoryRegionType::Usable
186 ==> nat_align_up(self.base as nat, PAGE_SIZE as nat)
187 <= nat_align_down((self.base + self.len) as nat, PAGE_SIZE as nat),
188 ensures
189 ret.typ == self.typ,
190 ret.aligned(PAGE_SIZE as int),
191 self.typ == MemoryRegionType::Usable
192 ==> ret.base == nat_align_up(self.base as nat, PAGE_SIZE as nat),
193 self.typ == MemoryRegionType::Usable
194 ==> ret.base + ret.len
195 == nat_align_down((self.base + self.len) as nat, PAGE_SIZE as nat),
196 self.typ != MemoryRegionType::Usable
197 ==> ret.base == nat_align_down(self.base as nat, PAGE_SIZE as nat),
198 self.typ != MemoryRegionType::Usable
199 ==> ret.base + ret.len
200 == nat_align_up((self.base + self.len) as nat, PAGE_SIZE as nat),
201 self.typ == MemoryRegionType::Usable
202 ==> ret.is_sub_region(*self),
203 self.typ != MemoryRegionType::Usable
204 ==> self.is_sub_region(ret),
205 )]
206 fn as_aligned(&self) -> Self {
207 proof! {
208 use_type_invariant(self);
209 lemma_pow2_is_pow2_to64();
210 }
211 let (base, end) = match self.typ() {
212 MemoryRegionType::Usable => (
213 self.base().align_up(PAGE_SIZE),
214 self.end().align_down(PAGE_SIZE),
215 ),
216 _ => (
217 self.base().align_down(PAGE_SIZE),
218 self.end().align_up(PAGE_SIZE),
219 ),
220 };
221 MemoryRegion {
222 base,
223 len: end - base,
224 typ: self.typ,
225 }
226 }
227}
228
229/// The maximum number of regions that can be handled.
230///
231/// The choice of 512 is probably fine since old Linux boot protocol only
232/// allows 128 regions.
233//
234// TODO: confirm the number or make it configurable.
235#[verus_verify]
236pub const MAX_REGIONS: usize = 512;
237
238/// A heapless set of memory regions.
239///
240/// The set cannot contain more than `LEN` regions.
241#[verus_verify]
242pub struct MemoryRegionArray<const LEN: usize = MAX_REGIONS> {
243 regions: [MemoryRegion; LEN],
244 count: usize,
245}
246
247verus! {
248
249impl<const LEN: usize> MemoryRegionArray<LEN> {
250 #[verifier::type_invariant]
251 closed spec fn type_inv(self) -> bool {
252 self.count <= LEN
253 }
254}
255
256impl<const LEN: usize> View for MemoryRegionArray<LEN> {
257 type V = Seq<MemoryRegion>;
258
259 closed spec fn view(&self) -> Seq<MemoryRegion> {
260 Seq::new(self.count as nat, |i: int| self.regions[i])
261 }
262}
263
264} // verus!
265#[verus_verify]
266impl<const LEN: usize> Default for MemoryRegionArray<LEN> {
267 #[verus_spec(ret =>
268 ensures
269 ret@ == Seq::<MemoryRegion>::empty()
270 )]
271 fn default() -> Self {
272 Self::new()
273 }
274}
275#[verus_verify]
276impl<const LEN: usize> Deref for MemoryRegionArray<LEN> {
277 type Target = [MemoryRegion];
278
279 #[verus_spec(ret =>
280 ensures
281 ret@ == self@,
282 )]
283 fn deref(&self) -> &Self::Target {
284 proof! {
285 use_type_invariant(self);
286 }
287 &self.regions[..self.count]
288 }
289}
290#[verus_verify]
291impl<const LEN: usize> MemoryRegionArray<LEN> {
292 /// Constructs an empty set.
293 #[verus_spec(ret =>
294 ensures
295 ret@ == Seq::<MemoryRegion>::empty(),
296 )]
297 pub const fn new() -> Self {
298 let ret = Self {
299 regions: [MemoryRegion::bad(); LEN],
300 count: 0,
301 };
302
303 proof! {
304 assert(ret@ == Seq::<MemoryRegion>::empty());
305 };
306
307 ret
308 }
309
310 /// Appends a region to the set.
311 ///
312 /// If the set is full, an error is returned.
313 #[verus_spec(ret =>
314 ensures
315 old(self)@.len() < LEN ==> {
316 &&& final(self)@ == old(self)@.push(region)
317 &&& ret.is_ok()
318 },
319 old(self)@.len() == LEN ==> {
320 &&& final(self)@ == old(self)@
321 &&& ret.is_err()
322 },
323 )]
324 pub fn push(&mut self, region: MemoryRegion) -> Result<(), &'static str> {
325 proof! {
326 use_type_invariant(&*self);
327 }
328 if self.count < self.regions.len() {
329 self.regions[self.count] = region;
330 self.count = self.count + 1;
331 proof! {
332 assert(self@ == old(self)@.push(region)) by {
333 assert (forall |i: int| 0 <= i < self@.len() ==> #[trigger] self@[i] == old(self)@.push(region)[i]);
334 };
335 };
336 Ok(())
337 } else {
338 Err("MemoryRegionArray is full")
339 }
340 }
341 /*
342 /// Sorts the regions and returns a full set of non-overlapping regions.
343 ///
344 /// If an address is in multiple regions, the region with the lowest
345 /// usability will be its type.
346 ///
347 /// All the addresses between 0 and the end of the last region will be in
348 /// the resulting set. If an address is not in any region, it will be marked
349 /// as [`MemoryRegionType::Unknown`].
350 ///
351 /// If any of the region boundaries are not page-aligned, they will be aligned
352 /// according to the type of the region.
353 ///
354 /// # Panics
355 ///
356 /// This method will panic if the number of output regions is greater than `LEN`.
357 pub fn into_non_overlapping(mut self) -> Self {
358 let max_addr = self
359 .iter()
360 .map(|r| r.end())
361 .max()
362 .unwrap_or(0)
363 .align_down(PAGE_SIZE);
364 self.regions.iter_mut().for_each(|r| *r = r.as_aligned());
365
366 let mut result = MemoryRegionArray::<LEN>::new();
367
368 let mut cur_right = 0;
369
370 while cur_right < max_addr {
371 // Find the most restrictive type.
372 let typ = self
373 .iter()
374 .filter(|region| (region.base()..region.end()).contains(&cur_right))
375 .map(|region| region.typ())
376 .min()
377 .unwrap_or(MemoryRegionType::Unknown);
378
379 // Find the right boundary.
380 let right = self
381 .iter()
382 .filter_map(|region| {
383 if region.base() > cur_right {
384 Some(region.base())
385 } else if region.end() > cur_right {
386 Some(region.end())
387 } else {
388 None
389 }
390 })
391 .min()
392 .unwrap();
393
394 result
395 .push(MemoryRegion::new(cur_right, right - cur_right, typ))
396 .unwrap();
397
398 cur_right = right;
399 }
400
401 // Merge the adjacent regions with the same type.
402 let mut merged_count = 1;
403 for i in 1..result.count {
404 if result[i].typ() == result.regions[merged_count - 1].typ() {
405 result.regions[merged_count - 1] = MemoryRegion::new(
406 result.regions[merged_count - 1].base(),
407 result.regions[merged_count - 1].len() + result[i].len(),
408 result.regions[merged_count - 1].typ(),
409 );
410 } else {
411 result.regions[merged_count] = result[i];
412 merged_count += 1;
413 }
414 }
415 result.count = merged_count;
416
417 result
418 }*/
419}
420
421#[cfg(ktest)]
422mod test {
423 use super::*;
424 use crate::prelude::ktest;
425
426 #[ktest]
427 fn test_sort_full_non_overlapping() {
428 let mut regions = MemoryRegionArray::<64>::new();
429 // Regions that can be combined.
430 regions
431 .push(MemoryRegion::new(
432 0,
433 PAGE_SIZE + 1,
434 MemoryRegionType::Usable,
435 ))
436 .unwrap();
437 regions
438 .push(MemoryRegion::new(
439 PAGE_SIZE - 1,
440 PAGE_SIZE + 2,
441 MemoryRegionType::Usable,
442 ))
443 .unwrap();
444 regions
445 .push(MemoryRegion::new(
446 PAGE_SIZE * 2,
447 PAGE_SIZE * 5,
448 MemoryRegionType::Usable,
449 ))
450 .unwrap();
451 // A punctured region.
452 regions
453 .push(MemoryRegion::new(
454 PAGE_SIZE * 3 + 1,
455 PAGE_SIZE - 2,
456 MemoryRegionType::BadMemory,
457 ))
458 .unwrap();
459 // A far region that left a hole in the middle.
460 regions
461 .push(MemoryRegion::new(
462 PAGE_SIZE * 9,
463 PAGE_SIZE * 2,
464 MemoryRegionType::Usable,
465 ))
466 .unwrap();
467
468 let regions = regions.into_non_overlapping();
469
470 assert_eq!(regions.count, 5);
471 assert_eq!(regions[0].base(), 0);
472 assert_eq!(regions[0].len(), PAGE_SIZE * 3);
473 assert_eq!(regions[0].typ(), MemoryRegionType::Usable);
474
475 assert_eq!(regions[1].base(), PAGE_SIZE * 3);
476 assert_eq!(regions[1].len(), PAGE_SIZE);
477 assert_eq!(regions[1].typ(), MemoryRegionType::BadMemory);
478
479 assert_eq!(regions[2].base(), PAGE_SIZE * 4);
480 assert_eq!(regions[2].len(), PAGE_SIZE * 3);
481 assert_eq!(regions[2].typ(), MemoryRegionType::Usable);
482
483 assert_eq!(regions[3].base(), PAGE_SIZE * 7);
484 assert_eq!(regions[3].len(), PAGE_SIZE * 2);
485 assert_eq!(regions[3].typ(), MemoryRegionType::Unknown);
486
487 assert_eq!(regions[4].base(), PAGE_SIZE * 9);
488 assert_eq!(regions[4].len(), PAGE_SIZE * 2);
489 assert_eq!(regions[4].typ(), MemoryRegionType::Usable);
490 }
491}