Commit 0b8ad72e authored by Nifou's avatar Nifou

Make the boot informations global, remove the areas and start to rewrite the frame allocator

parent 6bbd187f
/*
* Copyright (C) 2020 Nicolas Fouquet
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see https://www.gnu.org/licenses.
*/
//! Boot informations management
use multiboot2::BootInformation;
use spin::Once;
/// A structure which contains the boot informations
///
/// This structure is just a Sync + Send helper to make BootInformation safe between threads
pub struct BootInfo(pub BootInformation);
impl BootInfo {
/// Get the inner BootInformation struct
pub fn get(&'static self) -> &'static BootInformation { &self.0 }
/// Reload the inner structure with a new address
pub unsafe fn set(&mut self, addr: usize) { self.0 = multiboot2::load(addr); }
}
unsafe impl Sync for BootInfo {}
unsafe impl Send for BootInfo {}
/// A global BootInformations
///
/// This structure contains all informations passed from the bootloader to the kernel, like:
/// * The memory areas (used by the frame allocator)
/// * The sections of the kernel
/// * The VBE tags and structures
/// * The command line used to boot the kernel
pub static BOOT_INFO: Once<BootInfo> = Once::new();
......@@ -43,17 +43,15 @@ pub mod sse;
pub mod tasking;
pub mod syscall;
pub mod common;
pub mod boot_info;
use alloc::{alloc::Layout, prelude::v1::String};
use core::panic::PanicInfo;
use multiboot2::MemoryAreaIter;
use memory::area::{Area, Areas};
use memory::{KERNEL_START_PHYS, KERNEL_START_VIRT};
/// An initialization function which runs initializations of all components
pub fn init(mut areas: MemoryAreaIter,
multiboot_start: u64,
multiboot_end: u64) {
pub fn init() {
// Init GDT
print!("Init GDT");
unsafe {
......@@ -83,32 +81,28 @@ pub fn init(mut areas: MemoryAreaIter,
unsafe { irq::idt::PICS.lock().initialize() };
println!(" [ OK ]");
print!("Initialize virtual memory mapper");
let area1 = areas.next().unwrap();
let area1 = Area::new(area1.start_address(), area1.end_address());
let area2 = areas.next().unwrap();
let area2 = Area::new(area2.start_address(), area2.end_address());
print!("Init heap");
memory::heap::init();
println!(" [ OK ]");
print!("Initialize virtual memory mapper");
enable_nxe_bit();
enable_write_protect_bit();
memory::init(Areas::new(
(multiboot_start, multiboot_end),
(area1, area2),
));
memory::init();
println!(" [ OK ]");
print!("Init heap");
memory::heap::init();
println!(" [ OK ]");
println!("Areas detected:");
let areas = boot_info::BOOT_INFO.r#try().unwrap().get().memory_map_tag()
.expect("Memory map tag required")
.memory_areas().clone();
let mut total_size = 0;
println!(
"Memory {} MiB, lower: {} KiB, upper: {} MiB",
(area1.size() + area2.size()) / (1024 * 1024),
area1.size() / 1024,
area2.size() / (1024 * 1024)
);
for area in areas {
println!(" - From {:#x} to {:#x}. Type: {:?}", area.start_address(), area.end_address(), area.typ());
total_size += area.size();
}
println!("Total size: | {} MiB |", total_size / (1024 * 1024));
}
......@@ -116,18 +110,12 @@ pub fn init(mut areas: MemoryAreaIter,
#[cfg(not(feature = "test"))]
/// The entry point of the kernel
pub extern "C" fn kmain(info_addr: usize) {
let boot_info = unsafe { multiboot2::load(info_addr) };
let areas = boot_info
.memory_map_tag()
.expect("Memory map tag required")
.memory_areas();
let boot_info = unsafe { multiboot2::load((info_addr as u64 + (KERNEL_START_VIRT - KERNEL_START_PHYS)) as usize) };
let multiboot_start = boot_info.start_address() as u64;
let multiboot_end = boot_info.end_address() as u64;
boot_info::BOOT_INFO.call_once(|| boot_info::BootInfo(boot_info));
println!("Initialize components...");
init(areas, multiboot_start, multiboot_end);
init();
elf::load(String::from("/bin/hello"));
elf::load(String::from("/bin/hello2"));
......@@ -146,18 +134,12 @@ pub extern "C" fn kmain(info_addr: usize) {
use memory::map;
let boot_info = unsafe { multiboot2::load(info_addr) };
let areas = boot_info
.memory_map_tag()
.expect("Memory map tag required")
.memory_areas();
let boot_info = unsafe { multiboot2::load((info_addr as u64 + (KERNEL_START_VIRT - KERNEL_START_PHYS)) as usize) };
let multiboot_start = boot_info.start_address() as u64;
let multiboot_end = boot_info.end_address() as u64;
BOOT_INFO.call_once(|| BootInfo(boot_info));
println!("Initialize components...");
init(areas, multiboot_start, multiboot_end);
init();
println!("Run tests");
serial::test_serial();
......
/*
* Copyright (C) 2020 Nicolas Fouquet
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see https://www.gnu.org/licenses.
*/
//! Memory areas management
use x86_64::{structures::paging::frame::PhysFrame, PhysAddr};
#[derive(PartialEq, Debug, Clone, Copy)]
/// The areas of the memory used by the FrameAllocator. They contains:
/// - The kernel area which must not be written
/// - The multiboot area which must not be written too
/// - The upper and lower memory areas
pub struct Areas {
/// The multiboot area's start
pub multiboot_start: PhysFrame,
/// The multiboot area's end
pub multiboot_end: PhysFrame,
/// The lower memory area's
area1: Area,
/// The upper memory area's
area2: Area,
}
impl Areas {
/// Create a new Area's container
pub fn new(multiboot: (u64, u64), areas: (Area, Area)) -> Self {
Self {
multiboot_start: PhysFrame::containing_address(PhysAddr::new(multiboot.0)),
multiboot_end: PhysFrame::containing_address(PhysAddr::new(multiboot.1)),
area1: areas.0,
area2: areas.1,
}
}
/// Get all memory's areas
pub fn get_areas(&self) -> [Area; 2] {
[self.area1.clone(), self.area2.clone()]
}
/// Get the multiboot areas
pub fn get_multiboot(&self) -> (PhysFrame, PhysFrame) {
(self.multiboot_start.clone(), self.multiboot_end.clone())
}
}
#[derive(PartialEq, Debug, Clone, Copy)]
/// A struct which represents an area of memory
pub struct Area {
start: u64,
end: u64,
size: u64,
}
impl Area {
/// Create a new area
pub fn new(start: u64, end: u64) -> Self {
Self {
start,
end,
size: end - start,
}
}
/// Get the start of this area
pub fn start(&self) -> u64 {
self.start
}
/// Get the en of the area
pub fn end(&self) -> u64 {
self.end
}
/// Get the size of the area
pub fn size(&self) -> u64 {
self.size
}
}
......@@ -15,22 +15,28 @@
* along with this program. If not, see https://www.gnu.org/licenses.
*/
//! Physical memory's frames allocator
use alloc::prelude::v1::Vec;
use x86_64::{
structures::paging::{FrameAllocator, PhysFrame, Size4KiB},
PhysAddr,
};
use multiboot2::{MemoryAreaIter, MemoryArea, MemoryAreaType};
use super::area::Areas;
use crate::boot_info::BOOT_INFO;
use super::heap::{HEAP_START_PHYS, HEAP_SIZE};
/// An allocator which gives frames of physical memory
#[derive(Debug, Clone)]
pub struct NextFrameAllocator {
/// The areas of the memory
pub areas: Areas,
/// The areas of physical memory
///
/// See [OSDEV](https://wiki.osdev.org/Memory_Map_(x86))
areas: MemoryAreaIter<'static>,
/// The current area
current_area: usize,
/// The current area for new frames
current_area: Option<&'static MemoryArea>,
/// An area of deallocated frames
free: Vec<PhysFrame>,
/// The next frame of physical memory
next: PhysFrame,
......@@ -38,63 +44,71 @@ pub struct NextFrameAllocator {
impl NextFrameAllocator {
/// Create a new FrameAllocator with the given areas
pub fn new(areas: Areas) -> Self {
Self {
areas: areas,
current_area: 0,
pub fn new() -> Self {
let mut result = Self {
areas: BOOT_INFO.r#try().unwrap().get().memory_map_tag()
.expect("Memory map tag required")
.memory_areas(),
current_area: None,
free: vec![],
next: PhysFrame::containing_address(PhysAddr::new(HEAP_START_PHYS + HEAP_SIZE as u64)),
}
};
result.choose_next_area();
result
}
/// Get the areas used by the FrameAllocator
pub fn areas(&self) -> Areas {
self.areas.clone()
/// Try to get the current area
///
/// This function returns the current area or panics
fn try_current(&self) -> &MemoryArea {
self.current_area.as_ref().expect("Allocator not initialized or it is an Out Of Memory!")
}
/// Choose the next area
fn choose_next_area(&mut self) {
self.current_area += 1;
if self.current_area >= self.areas.get_areas().len() {
// Switch to the next area
if let Some(area) = self.areas.next() {
self.current_area = Some(area);
} else {
panic!("KERNEL PANIC: Out of memory!");
}
let current_area = self.areas.get_areas()[self.current_area];
let start_frame = PhysFrame::containing_address(PhysAddr::new(current_area.start()));
if self.next < start_frame {
self.next = start_frame;
match self.current_area.as_ref().unwrap().typ() {
MemoryAreaType::Available => {
let start_frame = addr_to_frame(self.try_current().start_address());
if self.next < start_frame {
self.next = start_frame;
}
},
_ => {
// This memory area is not available, try another
self.choose_next_area();
},
}
}
/// Get a new frame of physical memory
fn get_frame(&mut self) -> PhysFrame {
let current_area = self.areas.get_areas()[self.current_area];
let current_area = self.try_current();
let current_area_last_frame = {
let address = current_area.start() + current_area.size() - 1;
let address = current_area.end_address();
PhysFrame::containing_address(PhysAddr::new(address))
};
if self.next > current_area_last_frame {
// all frames of current area are used, switch to next area
if !self.free.is_empty() {
// The free frames are priority
self.free.pop().unwrap()
} else if self.next > current_area_last_frame {
// It remains zero frames in this area, switch to another
self.choose_next_area();
} else if self.next >= self.areas.get_multiboot().0
&& self.next <= self.areas.get_multiboot().1
{
// `frame` is used by the multiboot information structure
self.next = PhysFrame::from_start_address(PhysAddr::new(
self.areas.get_multiboot().1.start_address().as_u64() + 4096,
))
.unwrap();
self.get_frame()
} else {
// frame is unused, increment `next_free_frame` and return it
self.next = PhysFrame::from_start_address(PhysAddr::new(
self.next.start_address().as_u64() + 4096,
))
.unwrap();
return self.next;
// This frame is unused, use it
self.next = self.next + 1;
self.next
}
// `frame` was not valid, try it again with the updated `next_free_frame`
self.get_frame()
}
}
......@@ -103,3 +117,8 @@ unsafe impl FrameAllocator<Size4KiB> for NextFrameAllocator {
Some(self.get_frame())
}
}
/// Translate an address to a frame
pub fn addr_to_frame(addr: u64) -> PhysFrame {
PhysFrame::from_start_address(PhysAddr::new(addr)).expect("Not start address")
}
......@@ -19,8 +19,6 @@
//! * Virtual memory management, such as mappings
//! * A frame allocator which allocate frames of physical memory memory which will be mapped
//! later to a page of virtual memory
//! * A memory controller which contains all of them in only one structure
pub mod area;
pub mod frame_allocator;
pub mod heap;
pub mod table;
......@@ -41,7 +39,6 @@ use x86_64::{
VirtAddr, PhysAddr,
};
use area::Areas;
use frame_allocator::NextFrameAllocator;
use self::table::ProcessPageTable;
use self::heap::{HEAP_START_VIRT, HEAP_START_PHYS, HEAP_SIZE};
......@@ -86,8 +83,8 @@ lazy_static! {
pub type Mappings = Vec<(PhysFrame, Page)>;
/// Remap the kernel, initialize the kernel memory mapper and the allocator
pub fn init(areas: Areas) {
init_allocator(areas);
pub fn init() {
init_allocator();
init_mapper();
let frame = PhysFrame::from_start_address(PhysAddr::new(MULTIBOOT_START_PHYS)).unwrap();
......@@ -112,8 +109,8 @@ fn init_mapper() {
}
/// Initialize the frame allocator
fn init_allocator(areas: Areas) {
FRAME_ALLOCATOR.call_once(|| Mutex::new(NextFrameAllocator::new(areas)));
fn init_allocator() {
FRAME_ALLOCATOR.call_once(|| Mutex::new(NextFrameAllocator::new()));
}
/// Remap the kernel and switch to a new page table
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment