Heapless memory region initialization

This commit is contained in:
Zhang Junyang
2024-12-29 21:53:00 +08:00
committed by Tate, Hongliang Tian
parent 0c8200dc7b
commit 51349a3da1
7 changed files with 236 additions and 129 deletions

View File

@ -38,7 +38,7 @@ pub(crate) fn init() {
let mut writer = {
let framebuffer = boot::framebuffer_arg();
let mut size = 0;
for region in memory_regions() {
for region in memory_regions().iter() {
if region.typ() == MemoryRegionType::Framebuffer {
size = region.len();
}

View File

@ -4,7 +4,7 @@
pub mod smp;
use alloc::{string::String, vec::Vec};
use alloc::string::String;
use core::arch::global_asm;
use fdt::Fdt;
@ -13,7 +13,10 @@ use spin::Once;
use crate::{
boot::{
kcmdline::KCmdlineArg,
memory_region::{non_overlapping_regions_from, MemoryRegion, MemoryRegionType},
memory_region::{
non_overlapping_regions_from, MemoryRegion, MemoryRegionArray, MemoryRegionType,
MAX_REGIONS,
},
BootloaderAcpiArg, BootloaderFramebufferArg,
},
early_println,
@ -50,8 +53,8 @@ fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
fn init_framebuffer_info(_framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {}
fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
let mut regions = Vec::<MemoryRegion>::new();
fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
let mut regions = MemoryRegionArray::new();
for region in DEVICE_TREE.get().unwrap().memory().regions() {
if region.size.unwrap_or(0) > 0 {
@ -89,7 +92,7 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
));
}
memory_regions.call_once(|| non_overlapping_regions_from(regions.as_ref()));
memory_regions.call_once(|| regions.into_non_overlapping());
}
fn parse_initramfs_range() -> Option<(usize, usize)> {

View File

@ -3,7 +3,7 @@
//! The Linux 64-bit Boot Protocol supporting module.
//!
use alloc::{borrow::ToOwned, format, string::String, vec::Vec};
use alloc::{borrow::ToOwned, format, string::String};
use core::ffi::CStr;
use linux_boot_params::{BootParams, E820Type, LINUX_BOOT_HEADER_MAGIC};
@ -12,7 +12,7 @@ use spin::Once;
use crate::{
boot::{
kcmdline::KCmdlineArg,
memory_region::{non_overlapping_regions_from, MemoryRegion, MemoryRegionType},
memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg,
},
mm::kspace::{paddr_to_vaddr, LINEAR_MAPPING_BASE_VADDR},
@ -118,39 +118,45 @@ impl From<E820Type> for MemoryRegionType {
}
}
fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
let mut regions = Vec::<MemoryRegion>::new();
fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
let mut regions = MemoryRegionArray::new();
let boot_params = BOOT_PARAMS.get().unwrap();
// Add regions from E820.
let num_entries = boot_params.e820_entries as usize;
for e820_entry in &boot_params.e820_table[0..num_entries] {
regions.push(MemoryRegion::new(
e820_entry.addr as usize,
e820_entry.size as usize,
e820_entry.typ.into(),
));
regions
.push(MemoryRegion::new(
e820_entry.addr as usize,
e820_entry.size as usize,
e820_entry.typ.into(),
))
.unwrap();
}
// Add the kernel region.
regions.push(MemoryRegion::kernel());
regions.push(MemoryRegion::kernel()).unwrap();
// Add the initramfs region.
regions.push(MemoryRegion::new(
boot_params.hdr.ramdisk_image as usize,
boot_params.hdr.ramdisk_size as usize,
MemoryRegionType::Module,
));
regions
.push(MemoryRegion::new(
boot_params.hdr.ramdisk_image as usize,
boot_params.hdr.ramdisk_size as usize,
MemoryRegionType::Module,
))
.unwrap();
// Add the AP boot code region that will be copied into by the BSP.
regions.push(MemoryRegion::new(
super::smp::AP_BOOT_START_PA,
super::smp::ap_boot_code_size(),
MemoryRegionType::Reclaimable,
));
regions
.push(MemoryRegion::new(
super::smp::AP_BOOT_START_PA,
super::smp::ap_boot_code_size(),
MemoryRegionType::Reclaimable,
))
.unwrap();
memory_regions.call_once(|| non_overlapping_regions_from(regions.as_ref()));
memory_regions.call_once(|| regions.into_non_overlapping());
}
/// The entry point of the Rust code portion of Asterinas.

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{string::String, vec::Vec};
use alloc::string::String;
use core::arch::global_asm;
use spin::Once;
@ -8,7 +8,7 @@ use spin::Once;
use crate::{
boot::{
kcmdline::KCmdlineArg,
memory_region::{non_overlapping_regions_from, MemoryRegion, MemoryRegionType},
memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg,
},
mm::{
@ -103,8 +103,8 @@ fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg
});
}
fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
let mut regions = Vec::<MemoryRegion>::new();
fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
let mut regions = MemoryRegionArray::new();
let info = MB1_INFO.get().unwrap();
@ -116,7 +116,7 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
entry.length().try_into().unwrap(),
entry.memory_type(),
);
regions.push(region);
regions.push(region).unwrap();
}
// Add the framebuffer region.
@ -126,14 +126,16 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
height: info.framebuffer_table.height as usize,
bpp: info.framebuffer_table.bpp as usize,
};
regions.push(MemoryRegion::new(
fb.address,
(fb.width * fb.height * fb.bpp + 7) / 8, // round up when divide with 8 (bits/Byte)
MemoryRegionType::Framebuffer,
));
regions
.push(MemoryRegion::new(
fb.address,
(fb.width * fb.height * fb.bpp + 7) / 8, // round up when divide with 8 (bits/Byte)
MemoryRegionType::Framebuffer,
))
.unwrap();
// Add the kernel region.
regions.push(MemoryRegion::kernel());
regions.push(MemoryRegion::kernel()).unwrap();
// Add the initramfs area.
if info.mods_count != 0 {
@ -145,22 +147,26 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
(*(paddr_to_vaddr(modules_addr + 4) as *const u32)) as usize,
)
};
regions.push(MemoryRegion::new(
start,
end - start,
MemoryRegionType::Module,
));
regions
.push(MemoryRegion::new(
start,
end - start,
MemoryRegionType::Module,
))
.unwrap();
}
// Add the AP boot code region that will be copied into by the BSP.
regions.push(MemoryRegion::new(
super::smp::AP_BOOT_START_PA,
super::smp::ap_boot_code_size(),
MemoryRegionType::Reclaimable,
));
regions
.push(MemoryRegion::new(
super::smp::AP_BOOT_START_PA,
super::smp::ap_boot_code_size(),
MemoryRegionType::Reclaimable,
))
.unwrap();
// Initialize with non-overlapping regions.
memory_regions.call_once(move || non_overlapping_regions_from(regions.as_ref()));
memory_regions.call_once(move || regions.into_non_overlapping());
}
/// Representation of Multiboot Information according to specification.

View File

@ -1,9 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{
string::{String, ToString},
vec::Vec,
};
use alloc::string::{String, ToString};
use core::arch::global_asm;
use multiboot2::{BootInformation, BootInformationHeader, MemoryAreaType};
@ -12,7 +9,7 @@ use spin::Once;
use crate::{
boot::{
kcmdline::KCmdlineArg,
memory_region::{non_overlapping_regions_from, MemoryRegion, MemoryRegionType},
memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg,
},
mm::kspace::paddr_to_vaddr,
@ -99,8 +96,8 @@ impl From<MemoryAreaType> for MemoryRegionType {
}
}
fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
let mut regions = Vec::<MemoryRegion>::new();
fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
let mut regions = MemoryRegionArray::new();
let mb2_info = MB2_INFO.get().unwrap();
@ -117,7 +114,7 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
(end - start).try_into().unwrap(),
area_typ,
);
regions.push(region);
regions.push(region).unwrap();
}
if let Some(Ok(fb_tag)) = mb2_info.framebuffer_tag() {
@ -128,35 +125,41 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
height: fb_tag.height() as usize,
bpp: fb_tag.bpp() as usize,
};
regions.push(MemoryRegion::new(
fb.address,
(fb.width * fb.height * fb.bpp + 7) / 8, // round up when divide with 8 (bits/Byte)
MemoryRegionType::Framebuffer,
));
regions
.push(MemoryRegion::new(
fb.address,
(fb.width * fb.height * fb.bpp + 7) / 8, // round up when divide with 8 (bits/Byte)
MemoryRegionType::Framebuffer,
))
.unwrap();
}
// Add the kernel region since Grub does not specify it.
regions.push(MemoryRegion::kernel());
regions.push(MemoryRegion::kernel()).unwrap();
// Add the boot module region since Grub does not specify it.
let mb2_module_tag = mb2_info.module_tags();
for module in mb2_module_tag {
regions.push(MemoryRegion::new(
module.start_address() as usize,
module.module_size() as usize,
MemoryRegionType::Module,
));
regions
.push(MemoryRegion::new(
module.start_address() as usize,
module.module_size() as usize,
MemoryRegionType::Module,
))
.unwrap();
}
// Add the AP boot code region that will be copied into by the BSP.
regions.push(MemoryRegion::new(
super::smp::AP_BOOT_START_PA,
super::smp::ap_boot_code_size(),
MemoryRegionType::Reclaimable,
));
regions
.push(MemoryRegion::new(
super::smp::AP_BOOT_START_PA,
super::smp::ap_boot_code_size(),
MemoryRegionType::Reclaimable,
))
.unwrap();
// Initialize with non-overlapping regions.
memory_regions.call_once(move || non_overlapping_regions_from(regions.as_ref()));
memory_regions.call_once(move || regions.into_non_overlapping());
}
/// The entry point of Rust code called by inline asm.

View File

@ -3,8 +3,7 @@
//! Information of memory regions in the boot phase.
//!
use alloc::{vec, vec::Vec};
use core::mem::swap;
use core::ops::Deref;
use crate::mm::kspace::kernel_loaded_offset;
@ -40,10 +39,19 @@ pub struct MemoryRegion {
impl MemoryRegion {
/// Constructs a valid memory region.
pub fn new(base: usize, len: usize, typ: MemoryRegionType) -> Self {
pub const fn new(base: usize, len: usize, typ: MemoryRegionType) -> Self {
MemoryRegion { base, len, typ }
}
/// Constructs a bad memory region.
pub const fn bad() -> Self {
MemoryRegion {
base: 0,
len: 0,
typ: MemoryRegionType::BadMemory,
}
}
/// Constructs a memory region where kernel sections are loaded.
///
/// Most boot protocols do not mark the place where the kernel loads as unusable. In this case,
@ -83,11 +91,11 @@ impl MemoryRegion {
/// Removes range `t` from self, resulting in 0, 1 or 2 truncated ranges.
/// We need to have this method since memory regions can overlap.
pub fn truncate(&self, t: &MemoryRegion) -> Vec<MemoryRegion> {
pub fn truncate(&self, t: &MemoryRegion) -> MemoryRegionArray<2> {
if self.base < t.base {
if self.base + self.len > t.base {
if self.base + self.len > t.base + t.len {
vec![
MemoryRegionArray::from(&[
MemoryRegion {
base: self.base,
len: t.base - self.base,
@ -98,72 +106,153 @@ impl MemoryRegion {
len: self.base + self.len - (t.base + t.len),
typ: self.typ,
},
]
])
} else {
vec![MemoryRegion {
MemoryRegionArray::from(&[MemoryRegion {
base: self.base,
len: t.base - self.base,
typ: self.typ,
}]
}])
}
} else {
vec![*self]
MemoryRegionArray::from(&[*self])
}
} else if self.base < t.base + t.len {
if self.base + self.len > t.base + t.len {
vec![MemoryRegion {
MemoryRegionArray::from(&[MemoryRegion {
base: t.base + t.len,
len: self.base + self.len - (t.base + t.len),
typ: self.typ,
}]
}])
} else {
vec![]
MemoryRegionArray::new()
}
} else {
vec![*self]
MemoryRegionArray::from(&[*self])
}
}
}
/// Truncates regions, resulting in a set of regions that does not overlap.
/// The maximum number of regions that can be handled.
///
/// The truncation will be done according to the type of the regions, that
/// usable and reclaimable regions will be truncated by the unusable regions.
pub fn non_overlapping_regions_from(regions: &[MemoryRegion]) -> Vec<MemoryRegion> {
// We should later use regions in `regions_unusable` to truncate all
// regions in `regions_usable`.
// The difference is that regions in `regions_usable` could be used by
// the frame allocator.
let mut regions_usable = Vec::<MemoryRegion>::new();
let mut regions_unusable = Vec::<MemoryRegion>::new();
/// The choice of 512 is probably fine since old Linux boot protocol only
/// allows 128 regions.
//
// TODO: confirm the number or make it configurable.
pub const MAX_REGIONS: usize = 512;
for r in regions {
match r.typ {
MemoryRegionType::Usable | MemoryRegionType::Reclaimable => {
regions_usable.push(*r);
}
_ => {
regions_unusable.push(*r);
}
}
}
// `regions_*` are 2 rolling vectors since we are going to truncate
// the regions in a iterative manner.
let mut regions = Vec::<MemoryRegion>::new();
let regions_src = &mut regions_usable;
let regions_dst = &mut regions;
// Truncate the usable regions.
for &r_unusable in &regions_unusable {
regions_dst.clear();
for r_usable in &*regions_src {
regions_dst.append(&mut r_usable.truncate(&r_unusable));
}
swap(regions_src, regions_dst);
}
// Combine all the regions processed.
let mut all_regions = regions_unusable;
all_regions.append(&mut regions_usable);
all_regions
/// A heapless set of memory regions.
///
/// The set cannot contain more than `LEN` regions.
pub struct MemoryRegionArray<const LEN: usize = MAX_REGIONS> {
regions: [MemoryRegion; LEN],
count: usize,
}
impl<const LEN: usize> Default for MemoryRegionArray<LEN> {
fn default() -> Self {
Self::new()
}
}
impl<const LEN: usize> Deref for MemoryRegionArray<LEN> {
type Target = [MemoryRegion];
fn deref(&self) -> &Self::Target {
&self.regions[..self.count]
}
}
impl<const LEN: usize> MemoryRegionArray<LEN> {
/// Constructs an empty set.
pub const fn new() -> Self {
Self {
regions: [MemoryRegion::bad(); LEN],
count: 0,
}
}
/// Constructs from an array of regions.
pub fn from(array: &[MemoryRegion]) -> Self {
Self {
regions: core::array::from_fn(|i| {
if i < array.len() {
array[i]
} else {
MemoryRegion::bad()
}
}),
count: array.len(),
}
}
/// Appends a region to the set.
///
/// If the set is full, an error is returned.
pub fn push(&mut self, region: MemoryRegion) -> Result<(), &'static str> {
if self.count < self.regions.len() {
self.regions[self.count] = region;
self.count += 1;
Ok(())
} else {
Err("MemoryRegionArray is full")
}
}
/// Clears the set.
pub fn clear(&mut self) {
self.count = 0;
}
/// Truncates regions, resulting in a set of regions that does not overlap.
///
/// The truncation will be done according to the type of the regions, that
/// usable and reclaimable regions will be truncated by the unusable regions.
///
/// If the output regions are more than `LEN`, the extra regions will be ignored.
pub fn into_non_overlapping(self) -> Self {
// We should later use regions in `regions_unusable` to truncate all
// regions in `regions_usable`.
// The difference is that regions in `regions_usable` could be used by
// the frame allocator.
let mut regions_usable = MemoryRegionArray::<LEN>::new();
let mut regions_unusable = MemoryRegionArray::<LEN>::new();
for r in self.iter() {
match r.typ {
MemoryRegionType::Usable | MemoryRegionType::Reclaimable => {
// If usable memory regions exceeded it's fine to ignore the rest.
let _ = regions_usable.push(*r);
}
_ => {
regions_unusable
.push(*r)
.expect("Too many unusable memory regions");
}
}
}
// `regions_*` are 2 rolling vectors since we are going to truncate
// the regions in a iterative manner.
let mut regions = MemoryRegionArray::<LEN>::new();
let regions_src = &mut regions_usable;
let regions_dst = &mut regions;
// Truncate the usable regions.
for r_unusable in regions_unusable.iter() {
regions_dst.clear();
for r_usable in regions_src.iter() {
for truncated in r_usable.truncate(r_unusable).iter() {
let _ = regions_dst.push(*truncated);
}
}
core::mem::swap(regions_src, regions_dst);
}
// Combine all the regions processed.
let mut all_regions = regions_unusable;
for r in regions_usable.iter() {
let _ = all_regions.push(*r);
}
all_regions
}
}

View File

@ -12,12 +12,12 @@ pub mod kcmdline;
pub mod memory_region;
pub mod smp;
use alloc::{string::String, vec::Vec};
use alloc::string::String;
use kcmdline::KCmdlineArg;
use spin::Once;
use self::memory_region::MemoryRegion;
use self::memory_region::MemoryRegionArray;
/// ACPI information from the bootloader.
///
@ -99,7 +99,7 @@ define_global_static_boot_arguments!(
initramfs, INITRAMFS, &'static [u8];
acpi_arg, ACPI_ARG, BootloaderAcpiArg;
framebuffer_arg, FRAMEBUFFER_ARG, BootloaderFramebufferArg;
memory_regions, MEMORY_REGIONS, Vec<MemoryRegion>;
memory_regions, MEMORY_REGIONS, MemoryRegionArray;
);
/// The initialization method of the boot module.