Heap Allocation #1014
Replies: 39 comments 105 replies
-
Thank you I was waiting this part ! |
Beta Was this translation helpful? Give feedback.
-
Hey Philipp, thank you for the great post ( as always ✌️ ) I wanted to note that I think now it's possible to move the allocator to its own module and out of Can't wait for your next tutorial! |
Beta Was this translation helpful? Give feedback.
-
@phil-opp As @shakram02 noted, rust-lang/rust#62735 now makes it possible to define the global allocator in a submodule. |
Beta Was this translation helpful? Give feedback.
-
@shakram02 @toothbrush7777777 Thank you for reporting this and sorry for the late reply. I created #714 to move the Edit: Blog update in #715. |
Beta Was this translation helpful? Give feedback.
-
The comment in the first code block in https://os.phil-opp.com/heap-allocation/#the-global-allocator-attribute should be |
Beta Was this translation helpful? Give feedback.
-
@Menschenkindlein We recently moved the definition to the |
Beta Was this translation helpful? Give feedback.
-
I guess it doesn't need |
Beta Was this translation helpful? Give feedback.
-
Ah, good catch! Fixed in #728. |
Beta Was this translation helpful? Give feedback.
-
How do you handle memory allocation failure gracefully in here ? (If my users starts forkbombing I’d like to avoid crashing the kernel and instead tell the user nope, your fork is a spoon) |
Beta Was this translation helpful? Give feedback.
-
Currently the only way is to use the Alternatively, you could create your own |
Beta Was this translation helpful? Give feedback.
-
Going down the rabbit hole in stumbled onto https://github.com/vcombey/fallible_collections not sure if other such crate exist, but this looks like a reasonable thing to use, until hopefully the original alloc crate decides on how to play nice with people who need to handle memory allocation errors. (The ability to have the allocator specify an Error type with the default being ! would imho be very nice) |
Beta Was this translation helpful? Give feedback.
-
Looks useful!
I'm not aware of any proposals to change the global allocator, but there is the allocators working group that works on making collections parametrizable over the separate |
Beta Was this translation helpful? Give feedback.
-
Hello. First, thank you for this amazing tutorial. I really enjoy it. I do have a build error when using the linked-list-allocator:
It seems that |
Beta Was this translation helpful? Give feedback.
-
@n-osborne Thanks! The |
Beta Was this translation helpful? Give feedback.
-
@phil-opp Thanks, it works like a charm. |
Beta Was this translation helpful? Give feedback.
-
You should read https://os.phil-opp.com/async-await/ for an example. |
Beta Was this translation helpful? Give feedback.
-
Hey @phil-opp thanks for the great tutorial. This is perhaps more of a Rust question, but I'm wondering why we still need to use I see your statement here:
I thought with |
Beta Was this translation helpful? Give feedback.
-
Why can't we use |
Beta Was this translation helpful? Give feedback.
-
Why is the partial sum function in |
Beta Was this translation helpful? Give feedback.
-
Is there a way, by chance, to avoid deadlocking the Logger when crates like //snip
use acpi::{
AcpiError, AcpiHandler, AcpiTables, HpetInfo, PciConfigRegions, PhysicalMapping,
PlatformInfo, RsdpError,
};
use printk::LockedPrintk; // https://github.com/kennystrawnmusic/printk
use conquer_once::spin::OnceCell;
use spin::Mutex;
use crate::mem::frames::Falloc; // my custom name for the frame allocator in this tutorial
use x86_64::{
structures::paging::{Mapper, Page, PageTableFlags, OffsetPageTable, PhysFrame, Size4KiB},
PhysAddr, VirtAddr,
};
use bootloader::{BootInfo,entry_point};
//snip
pub static PRINTK: OnceCell<LockedPrintk> = OnceCell::uninit();
pub static MAPPER: OnceCell<Mutex<OffsetPageTable>> = OnceCell::uninit();
pub static FRAME_ALLOCATOR: OnceCell<Mutex<Falloc>> = OnceCell::uninit();
#[derive(Clone)]
pub struct KernelAcpi;
impl AcpiHandler for KernelAcpi {
unsafe fn map_physical_region<T>(
&self,
physical_address: usize,
size: usize,
) -> PhysicalMapping<Self, T> {
let physical_mapping_frame = PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(physical_address as u64));
let physical_mapping_page = Page::<Size4KiB>::containing_address(VirtAddr::new(physical_address as u64));
let res = MAPPER.get().unwrap().lock().map_to(
physical_mapping_page,
physical_mapping_frame,
PageTableFlags::PRESENT | PageTableFlags::WRITABLE,
&mut *FRAME_ALLOCATOR.get().unwrap().lock(),
);
let mapped = match res {
Ok(page) => page,
Err(e) => panic!("Error mapping pages: {:#?}", e)
};
mapped.flush();
PhysicalMapping::new(
physical_address,
NonNull::new((physical_address as usize) as *mut _).unwrap(), //page must exist
size,
size,
Self,
)
}
fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}
}
// snip
pub fn printk_init(buffer: &'static mut [u8], info: FrameBufferInfo) {
let p = PRINTK.get_or_init(move || LockedPrintk::new(buffer, info));
set_logger(p).expect("Logger has already been set!");
set_max_level(LevelFilter::Trace);
info!("Hello, Kernel!");
}
//snip
entry_point! maink;
fn maink(info: &'static mut BootInfo) -> ! {
let buffer_optional = &mut _info.framebuffer;
let buffer_option = buffer_optional.as_mut();
let buffer = buffer_option.unwrap();
let bi = buffer.info().clone();
let raw_buffer = buffer.buffer_mut();
printk_init(raw_buffer, bi);
//snip
let offset = VirtAddr::new(_info.physical_memory_offset.clone().into_option().unwrap());
let map = unsafe { map_memory(offset) };
let falloc = unsafe { Falloc::new(&_info.memory_regions) };
MAPPER.get_or_init(move || Mutex::new(map));
FRAME_ALLOCATOR.get_or_init(move || Mutex::new(falloc));
crate::mem::heap_init(
&mut *MAPPER.get().unwrap().lock(),
&mut *FRAME_ALLOCATOR.get().unwrap().lock(),
)
.unwrap_or_else(|e| error!("Failed to map{:#?}", e));
let rsdp = info.rsdp_addr.clone().into_option().unwrap();
let tables = unsafe { AcpiTables::from_rsdp(KernelAcpi, rsdp as usize).unwrap() };
info!(
"Interrupt model: {:#?}",
tables.platform_info().unwrap().interrupt_model
);
//snip
} The last |
Beta Was this translation helpful? Give feedback.
-
So, for those who are also looking to use this tutorial to get the ACPI tables parsed, here's the code that worked on my end: // snip
use acpi::{
AcpiError, AcpiHandler, AcpiTables, HpetInfo, InterruptModel, PciConfigRegions,
PhysicalMapping, PlatformInfo, RsdpError,
};
use conquer_once::spin::OnceCell;
// snip
pub static PHYS_OFFSET: OnceCell<u64> = OnceCell::uninit();
// snip
#[derive(Clone)]
pub struct KernelAcpi;
impl AcpiHandler for KernelAcpi {
unsafe fn map_physical_region<T>(
&self,
physical_address: usize,
size: usize,
) -> PhysicalMapping<Self, T> {
let physical_mapping_page = Page::<Size4KiB>::containing_address(VirtAddr::new(
(physical_address + PHYS_OFFSET.get().unwrap().clone() as usize) as u64,
));
let page_size = physical_mapping_page.size() as usize;
//round to nearest page/frame size
let page_padded = ((size - 1) / page_size + 1) * page_size;
PhysicalMapping::new(
physical_address,
NonNull::new(
(physical_address + PHYS_OFFSET.get().unwrap().clone() as usize) as *mut _,
)
.unwrap(), //page must exist
size,
page_padded,
Self,
)
}
fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}
}
// snip
entry_point!(maink);
fn maink(system_info: &'static mut BootInfo) -> ! {
// snip
let cloned_offset = system_info
.physical_memory_offset
.clone()
.into_option()
.unwrap();
PHYS_OFFSET.get_or_init(move || cloned_offset);
// snip
} Takes a lot of working around stumbling blocks, that's for sure. |
Beta Was this translation helpful? Give feedback.
-
Does anyone know what to plug into physical_address(0x0, 0x1, 0x6, 0x0) // 8086:29c0
physical_address(0x0, 0x1, 0x6, 0x1) // 8086:29c0
physical_address(0x0, 0x1, 0x6, 0x2) // 8086:29c0
physical_address(0x1, 0x6, 0x0, 0x0) // None
physical_address(0x1, 0x6, 0x1, 0x0) // None
physical_address(0x1, 0x6, 0x2, 0x0) // None In all of those attempts the only pieces of hardware I was able to find were memory controllers and host bridges, despite the official documentation clearly stating that a device class of 0x1 and subclass of 0x6 is the standard for AHCI controllers. So what other possibilities are there? Are the devices and classes meant to be plugged into that function and if not, what is? Pinging @IsaacWoods since he's that crate's largest contributor. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
In the first figure of section The Interrupt Stack Frame also calls this out
|
Beta Was this translation helpful? Give feedback.
-
When trying to run the tests at the end of this post, I get the following error:
I get that the fix is to implement the trait, but it might be useful to do so in the blog, too. Especially since we probably need to implement the other two sizes as well. |
Beta Was this translation helpful? Give feedback.
-
i was trying to implement the heap and ended up with this error. does anyone know how to solve it? |
Beta Was this translation helpful? Give feedback.
-
linked_list_allocator vulnerable to out-of-bound writes on |
Beta Was this translation helpful? Give feedback.
-
For those who may run into same thing after me. I found I was getting an Fixed by changing the GlobalAlloc import from: |
Beta Was this translation helpful? Give feedback.
-
Is there a way of knowing which virtual address ranges are already being used? So instead of doing pub const HEAP_START: usize = 0x_4444_4444_0000;
pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB the available virtual addresses usable for dynamic allocation could be figured out at runtime. |
Beta Was this translation helpful? Give feedback.
-
This is a general purpose comment thread for the Heap Allocation post.
Beta Was this translation helpful? Give feedback.
All reactions