use crate::{
MemblockError, MemblockFlags,
iter::{FreeRegionIter, FreeRegionRevIter},
region::{MemblockRegion, MemblockType},
};
pub struct Memblock {
memory: MemblockType,
reserved: MemblockType,
bottom_up: bool,
current_limit: usize,
}
impl Default for Memblock {
#[moa_sec_macros::init]
fn default() -> Self {
Self::new()
}
}
impl Memblock {
pub const fn new() -> Self {
Self {
memory: MemblockType::new("memory"),
reserved: MemblockType::new("reserved"),
bottom_up: false,
current_limit: usize::MAX,
}
}
#[moa_sec_macros::init]
pub fn add(&mut self, base: usize, size: usize) -> Result<(), MemblockError> {
self.memory.add_range(base, size, MemblockFlags::empty())
}
#[moa_sec_macros::init]
pub fn add_flags(
&mut self,
base: usize,
size: usize,
flags: MemblockFlags,
) -> Result<(), MemblockError> {
self.memory.add_range(base, size, flags)
}
#[moa_sec_macros::init]
pub fn remove(&mut self, base: usize, size: usize) -> Result<(), MemblockError> {
self.memory.remove_range(base, size)
}
#[moa_sec_macros::init]
pub fn reserve(&mut self, base: usize, size: usize) -> Result<(), MemblockError> {
self.reserved.add_range(base, size, MemblockFlags::empty())
}
#[moa_sec_macros::init]
pub fn free(&mut self, base: usize, size: usize) -> Result<(), MemblockError> {
self.reserved.remove_range(base, size)
}
#[moa_sec_macros::init]
pub fn mark_nomap(&mut self, base: usize, size: usize) -> Result<(), MemblockError> {
self.memory.set_flags(base, size, MemblockFlags::NOMAP)
}
#[moa_sec_macros::init]
pub fn clear_nomap(&mut self, base: usize, size: usize) -> Result<(), MemblockError> {
self.memory.clear_flags(base, size, MemblockFlags::NOMAP)
}
#[moa_sec_macros::init]
pub fn alloc(&mut self, size: usize, align: usize) -> Option<usize> {
self.alloc_range(size, align, 0, self.current_limit)
}
#[moa_sec_macros::init]
pub fn alloc_range(
&mut self,
size: usize,
align: usize,
min_addr: usize,
max_addr: usize,
) -> Option<usize> {
if size == 0 {
return None;
}
let align = if align == 0 { 1 } else { align };
let max_addr = max_addr.min(self.current_limit);
let found = if self.bottom_up {
self.find_bottom_up(size, align, min_addr, max_addr)
.or_else(|| self.find_top_down(size, align, min_addr, max_addr))
} else {
self.find_top_down(size, align, min_addr, max_addr)
};
if let Some(addr) = found {
self.reserve(addr, size).ok()?;
}
found
}
#[moa_sec_macros::init]
fn find_bottom_up(
&self,
size: usize,
align: usize,
min_addr: usize,
max_addr: usize,
) -> Option<usize> {
let skip = MemblockFlags::NOMAP;
for (base, region_size) in FreeRegionIter::new(&self.memory, &self.reserved, skip) {
let start = base.max(min_addr);
let aligned = align_up(start, align);
let end = base + region_size;
if aligned >= end {
continue;
}
if end.min(max_addr) - aligned >= size {
return Some(aligned);
}
}
None
}
#[moa_sec_macros::init]
fn find_top_down(
&self,
size: usize,
align: usize,
min_addr: usize,
max_addr: usize,
) -> Option<usize> {
let skip = MemblockFlags::NOMAP;
for (base, region_size) in FreeRegionRevIter::new(&self.memory, &self.reserved, skip) {
let end = (base + region_size).min(max_addr);
if end < size {
continue;
}
let aligned = align_down(end - size, align);
if aligned < base || aligned < min_addr {
continue;
}
return Some(aligned);
}
None
}
#[inline(always)]
pub fn is_memory(&self, addr: usize) -> bool {
self.memory.find(addr).is_some()
}
#[inline(always)]
pub fn is_reserved(&self, addr: usize) -> bool {
self.reserved.find(addr).is_some()
}
#[moa_sec_macros::init]
pub fn free_regions(&self) -> FreeRegionIter<'_> {
FreeRegionIter::new(&self.memory, &self.reserved, MemblockFlags::NOMAP)
}
#[moa_sec_macros::init]
pub fn free_regions_rev(&self) -> FreeRegionRevIter<'_> {
FreeRegionRevIter::new(&self.memory, &self.reserved, MemblockFlags::NOMAP)
}
#[inline(always)]
pub fn phys_mem_start(&self) -> Option<usize> {
self.memory.regions().first().map(|r| r.base)
}
#[inline(always)]
pub fn phys_mem_end(&self) -> Option<usize> {
self.memory.regions().last().map(MemblockRegion::end)
}
#[inline(always)]
pub fn phys_mem_size(&self) -> usize {
self.memory.total_size()
}
#[moa_sec_macros::init]
pub fn set_bottom_up(&mut self, bottom_up: bool) {
self.bottom_up = bottom_up;
}
#[moa_sec_macros::init]
pub fn is_bottom_up(&self) -> bool {
self.bottom_up
}
#[moa_sec_macros::init]
pub fn set_current_limit(&mut self, limit: usize) {
self.current_limit = limit;
}
#[moa_sec_macros::init]
pub fn current_limit(&self) -> usize {
self.current_limit
}
#[inline(always)]
pub fn memory(&self) -> &MemblockType {
&self.memory
}
#[inline(always)]
pub fn reserved(&self) -> &MemblockType {
&self.reserved
}
}
#[inline(always)]
fn align_up(addr: usize, align: usize) -> usize {
debug_assert!(align.is_power_of_two());
(addr + align - 1) & !(align - 1)
}
#[inline(always)]
fn align_down(addr: usize, align: usize) -> usize {
debug_assert!(align.is_power_of_two());
addr & !(align - 1)
}