use crate::{
cache::SMP_CACHE_BYTES,
vm::{addr::paddr::Paddr, pgtable::page::PAGE_SIZE},
};
use super::{common::memblock_remove_range, Memblock, MemblockFlags, MEMBLOCK_ALLOC_ACCESSIBLE};
fn memblock_find_range_bottom_up(
start: Paddr,
end: Paddr,
size: usize,
align: usize,
flags: MemblockFlags,
memblock: &Memblock,
) -> Paddr {
for (mut this_start, mut this_end) in memblock.iter_free(flags) {
this_start = this_start.clamp(start, end);
this_end = this_end.clamp(start, end);
let cand = this_start.to_value().next_multiple_of(align);
if cand < this_end.to_value() && this_end.to_value() - cand >= size {
return Paddr::from(cand);
}
}
Paddr::from(0)
}
fn memblock_find_range_top_down(
start: Paddr,
end: Paddr,
size: usize,
align: usize,
flags: MemblockFlags,
memblock: &Memblock,
) -> Paddr {
for (mut this_start, mut this_end) in memblock.iter_free_rev(flags) {
this_start = this_start.clamp(start, end);
this_end = this_end.clamp(start, end);
if this_end.to_value() < size {
continue;
}
let cand = (this_end - size).to_value().next_multiple_of(align) - align;
if cand >= this_start.to_value() {
return Paddr::from(cand);
}
}
Paddr::from(0)
}
fn __memblock_find_in_range(
size: usize,
align: usize,
mut start: Paddr,
mut end: Paddr,
flags: MemblockFlags,
memblock: &Memblock,
) -> Paddr {
if end.to_value() == MEMBLOCK_ALLOC_ACCESSIBLE {
end = memblock.current_limit;
}
start = start.max(Paddr::from(PAGE_SIZE));
end = end.max(start);
if memblock.is_bottom_up() && end > memblock.bottom_limit() {
let bottom_up_start = start.max(memblock.bottom_limit());
let ret = memblock_find_range_bottom_up(bottom_up_start, end, size, align, flags, memblock);
if ret.to_value() != 0 {
return ret;
}
}
memblock_find_range_top_down(start, end, size, align, flags, memblock)
}
fn memblock_alloc_internal(
size: usize,
mut align: usize,
mut min_addr: Paddr,
mut max_addr: Paddr,
memblock: &mut Memblock,
) -> Paddr {
let flags = MemblockFlags::MEMBLOCK_NONE;
if align == 0 {
align = SMP_CACHE_BYTES;
}
if max_addr > memblock.current_limit() {
max_addr = memblock.current_limit();
}
loop {
let alloc = __memblock_find_in_range(size, align, min_addr, max_addr, flags, memblock);
if alloc.to_value() != 0 && memblock.reserve(alloc, size) == Ok(()) {
return alloc;
}
if min_addr.to_value() != 0 {
min_addr = Paddr::from(0);
continue;
}
return Paddr::from(0);
}
}
impl Memblock {
pub fn alloc_try(
&mut self,
size: usize,
align: usize,
min_addr: Paddr,
max_addr: Paddr,
) -> Result<Paddr, ()> {
let ret = memblock_alloc_internal(size, align, min_addr, max_addr, self);
if ret.to_value() == 0 {
return Err(());
}
Ok(ret)
}
pub fn alloc(&mut self, size: usize, align: usize) -> Result<Paddr, ()> {
self.alloc_try(
size,
align,
Paddr::from(0),
Paddr::from(MEMBLOCK_ALLOC_ACCESSIBLE),
)
}
pub fn alloc_from(&mut self, size: usize, align: usize, min_addr: Paddr) -> Result<Paddr, ()> {
self.alloc_try(
size,
align,
min_addr,
Paddr::from(MEMBLOCK_ALLOC_ACCESSIBLE),
)
}
pub fn alloc_range(
&mut self,
size: usize,
mut align: usize,
start: Paddr,
end: Paddr,
flags: MemblockFlags,
) -> Result<Paddr, ()> {
if align == 0 {
align = SMP_CACHE_BYTES;
}
let found = __memblock_find_in_range(size, align, start, end, flags, self);
if found.to_value() != 0 && self.reserve(found, size) == Ok(()) {
return Ok(found);
}
Err(())
}
pub fn free(&mut self, base: Paddr, size: usize) -> Result<(), ()> {
memblock_remove_range(&mut self.reserved, base, size)
}
}