Commit 46916ce3 authored by Nifou's avatar Nifou

Use a better user memory scheme. The user memory is now divided in three...

Use a better user memory scheme. The user memory is now divided in three parts: the executable image, the data segment and the stack
parent c7206893
......@@ -29,7 +29,7 @@ use crate::memory::{self, PAGE_SIZE};
use crate::tasking::{
info::{AuxVecItem, ProcessInfo},
memory::MemoryArea,
process::ProcessBuilder,
process::{ProcessBuilder, STACK_SIZE, DATA_SEGMENT_SIZE},
};
#[derive(PartialEq, Debug, Clone, Copy)]
......@@ -142,12 +142,6 @@ impl<'a> ElfLoader<'a> {
if ph.p_type == PT_LOAD {
let start = ph.p_vaddr as u64;
println!(
"Copy from {:#p} to {:#p}",
(self.data.as_ptr() as u64 + ph.p_offset) as *const u8,
start as *mut u8
);
// Map the binary in the kernel's address space
let mappings =
memory::map(start, start + ph.p_memsz, Flags::PRESENT | Flags::WRITABLE);
......@@ -200,6 +194,32 @@ impl<'a> ElfLoader<'a> {
memory::unmap(mappings);
}
}
// Collect all addresses
let start_image = self.process.as_ref().unwrap().process.memory.areas.first().unwrap().start;
let end_image = self.process.as_ref().unwrap().process.memory.areas.last().unwrap().end;
let start_data_segment = end_image;
let end_data_segment = start_data_segment + DATA_SEGMENT_SIZE as u64 + PAGE_SIZE as u64;
let start_stack = end_data_segment;
let end_stack = start_stack + STACK_SIZE as u64;
// Create the stack and the data segment after the creation of the executable image
self.process.as_mut().unwrap().create_data_segment(start_data_segment);
self.process.as_mut().unwrap().create_stack(start_stack);
// Set the addresses in the struct ProcessMemory
let mut memory = self.process.as_mut().unwrap().memory();
memory.image = Some((start_image, end_image));
memory.data_segment = Some((start_data_segment, end_data_segment));
memory.stack = Some((start_stack, end_stack));
println!("Binary {}:\n - | Executable image | Start: {:#x}, End: {:#x}\n - | Data segment | Start: {:#x}, End: {:#x}\n - | Stack | Start: {:#x}, End: {:#x}",
self.path,
start_image, end_image,
start_data_segment, end_data_segment,
start_stack, end_stack);
}
/// Get the created process
......@@ -212,5 +232,7 @@ impl<'a> ElfLoader<'a> {
}
/// Add the created process to the list of processes
pub fn add(&self) { self.process.as_ref().unwrap().build(); }
pub fn add(&mut self) {
self.process.as_ref().unwrap().build();
}
}
......@@ -249,116 +249,125 @@ pub extern "x86-interrupt" fn page_fault_handler(
) {
use x86_64::registers::control::Cr2;
// Use the kernel's page table
crate::memory::switch_to_kernel_page_table();
let mut handled = false;
let addr = Cr2::read();
let page = Page::<Size4KiB>::containing_address(addr);
let mut processes = PROCESSES.write();
let current_thread = SCHEDULER.get().current().unwrap().clone();
if let Some(area) = processes
.get(&current_thread.process)
.expect("Could not find the current process")
.memory
.get_area(addr.as_u64())
{
let mut flags = area.flags;
if flags.contains(Flags::BIT_9) && error_code.contains(PageFaultErrorCode::CAUSED_BY_WRITE)
if let Some(current_thread) = SCHEDULER.get().current() {
// Use the kernel's page table
crate::memory::switch_to_kernel_page_table();
let page = Page::<Size4KiB>::containing_address(addr);
let mut processes = PROCESSES.write();
if let Some(area) = processes
.get(&current_thread.process)
.expect("Could not find the current process")
.memory
.get_area(addr.as_u64())
{
// --- Copy-on-write handler ---
// Allocate a new frame which will be the clone of the frame
let frame = crate::memory::FRAME_ALLOCATOR
.r#try()
.unwrap()
.lock()
.allocate_frame()
.unwrap();
// Create a temporary page to copy data between the two frames
let mut tmp =
TemporaryPage::new(Page::containing_address(VirtAddr::new(TMP_PAGE_ADDR)));
// TODO: Add the kernel heap in the process' memory to avoid this ugly and slow line
// (because we would be in the process' memory space and not in the kernel's memory space)
let mut dest_frame = None;
processes
.get_mut(&current_thread.process)
.expect("Could not find the current process")
.memory
.table
.with(|mapper| {
dest_frame = mapper.translate_page(page).ok();
});
unsafe {
let mut flags = area.flags;
if flags.contains(Flags::BIT_9) && error_code.contains(PageFaultErrorCode::CAUSED_BY_WRITE)
{
// --- Copy-on-write handler ---
// Allocate a new frame which will be the clone of the frame
let frame = crate::memory::FRAME_ALLOCATOR
.r#try()
.unwrap()
.lock()
.allocate_frame()
.unwrap();
// Create a temporary page to copy data between the two frames
let mut tmp =
TemporaryPage::new(Page::containing_address(VirtAddr::new(TMP_PAGE_ADDR)));
// TODO: Add the kernel heap in the process' memory to avoid this ugly and slow line
// (because we would be in the process' memory space and not in the kernel's memory space)
let mut dest_frame = None;
processes
.get_mut(&current_thread.process)
.expect("Could not find the current process")
.memory
.table
.with(|mapper| {
dest_frame = mapper.translate_page(page).ok();
});
unsafe {
crate::memory::KERNEL_MAPPER
.r#try()
.unwrap()
.lock()
.map_to(
page,
dest_frame.unwrap(),
Flags::PRESENT | Flags::NO_EXECUTE,
&mut *crate::memory::FRAME_ALLOCATOR.r#try().unwrap().lock(),
)
.unwrap()
.flush();
}
// ---
{
let src = (addr.as_u64() - (addr.as_u64() % PAGE_SIZE as u64)) as *const u8;
let dest = tmp.map(
frame.clone(),
Flags::PRESENT | Flags::WRITABLE | Flags::NO_EXECUTE,
) as *mut u8;
unsafe { copy_nonoverlapping(src, dest, PAGE_SIZE) };
}
tmp.unmap();
// TODO: Add the kernel heap in the process' memory to avoid this ugly and slow line
// (because we would be in the process' memory space and not in the kernel's memory space)
crate::memory::KERNEL_MAPPER
.r#try()
.unwrap()
.lock()
.map_to(
page,
dest_frame.unwrap(),
Flags::PRESENT | Flags::NO_EXECUTE,
&mut *crate::memory::FRAME_ALLOCATOR.r#try().unwrap().lock(),
)
.unmap(page)
.unwrap()
.1
.flush();
// ---
// Change the page's frame
processes
.get_mut(&current_thread.process)
.expect("Could not find the current process")
.memory
.table
.unmap(page);
// Add the WRITABLE flag because the page is now cloned
flags.insert(Flags::WRITABLE);
flags.remove(Flags::BIT_9);
// FIXME: Map with ProcessPageTable::map and change the frame in the areas
let mut area = MemoryArea::new(
page.start_address().as_u64(),
page.start_address().as_u64() + PAGE_SIZE as u64,
flags,
);
area.mappings(vec![(frame, page)]);
processes
.get_mut(&current_thread.process)
.expect("Could not find the current process")
.memory
.map(area);
handled = true;
processes
.get(&current_thread.process)
.expect("Could not find the current process")
.memory
.switch_page_table();
drop(processes);
}
// ---
{
let src = (addr.as_u64() - (addr.as_u64() % PAGE_SIZE as u64)) as *const u8;
let dest = tmp.map(
frame.clone(),
Flags::PRESENT | Flags::WRITABLE | Flags::NO_EXECUTE,
) as *mut u8;
unsafe { copy_nonoverlapping(src, dest, PAGE_SIZE) };
}
tmp.unmap();
// TODO: Add the kernel heap in the process' memory to avoid this ugly and slow line
// (because we would be in the process' memory space and not in the kernel's memory space)
crate::memory::KERNEL_MAPPER
.r#try()
.unwrap()
.lock()
.unmap(page)
.unwrap()
.1
.flush();
// ---
// Change the page's frame
processes
.get_mut(&current_thread.process)
.expect("Could not find the current process")
.memory
.table
.unmap(page);
// Add the WRITABLE flag because the page is now cloned
flags.insert(Flags::WRITABLE);
flags.remove(Flags::BIT_9);
// FIXME: Map with ProcessPageTable::map and change the frame in the areas
let mut area = MemoryArea::new(
page.start_address().as_u64(),
page.start_address().as_u64() + PAGE_SIZE as u64,
flags,
);
area.mappings(vec![(frame, page)]);
processes
.get_mut(&current_thread.process)
.expect("Could not find the current process")
.memory
.map(area);
handled = true;
}
}
......@@ -371,13 +380,6 @@ pub extern "x86-interrupt" fn page_fault_handler(
);
stop();
}
processes
.get(&current_thread.process)
.expect("Could not find the current process")
.memory
.switch_page_table();
drop(processes);
}
/// A double fault exception handler
......
......@@ -139,7 +139,7 @@ impl ProcessPageTable {
self.with(|mapper| {
let page_range = {
let start_addr = VirtAddr::new(start);
let end_addr = VirtAddr::new(end);
let end_addr = VirtAddr::new(end - 1);
let start_page = Page::containing_address(start_addr);
let end_page = Page::containing_address(end_addr);
Page::range_inclusive(start_page, end_page)
......
......@@ -21,6 +21,9 @@ pub enum Error {
/// No such file or directory
ENOENT = 2,
/// Out of memory
ENOMEM = 12,
/// Invalid value
EINVAL = 22,
......
......@@ -132,29 +132,20 @@ impl Syscall {
print!("brk({:#x}) = ", addr);
if addr == 0 {
println!("0x10000000");
let mut processes = PROCESSES.write();
// Use the kernel's page table
crate::memory::switch_to_kernel_page_table();
let processes = PROCESSES.read();
// Get the current thread's process
let current_process = SCHEDULER.get().current().unwrap().process;
let area = MemoryArea::new(0x10000000, 0x10004000, Flags::PRESENT | Flags::WRITABLE);
processes
.get_mut(&current_process)
.unwrap()
.memory
.map(area);
processes
.get_mut(&current_process)
.unwrap()
.memory
.switch_page_table();
Ok(0x10000000)
let current = SCHEDULER.get().current().unwrap().process;
if let Some(data_segment) = processes.get(&current).unwrap().memory.data_segment {
println!("{:#x}", data_segment.0);
Ok(data_segment.0 as usize)
} else {
println!("ENOMEM");
Err(Error::ENOMEM)
}
} else {
// TODO: Change program break
println!("{:#x}", addr);
Ok(addr)
}
......
......@@ -70,7 +70,6 @@ pub const SIGTERM: usize = 15;
impl Syscall {
/// Exit from the current thread by switching to the next thread
pub fn exit(&self, code: usize) -> Result {
// TODO: Only exit the process, use exit_group to remove all threads
match code {
0 => println!("Exit success"),
_ => println!("Error"),
......
......@@ -40,6 +40,15 @@ pub struct ProcessMemory {
/// The areas used by this process
pub areas: Vec<MemoryArea>,
/// The start and end addresses of the executable image
pub image: Option<(u64, u64)>,
/// The start and end addresses of the data segment
pub data_segment: Option<(u64, u64)>,
/// The start and end addresses of the stack
pub stack: Option<(u64, u64)>,
}
impl ProcessMemory {
......@@ -109,6 +118,9 @@ impl ProcessMemory {
Self {
table,
areas: vec![],
image: None,
data_segment: None,
stack: None,
}
}
......@@ -143,7 +155,7 @@ impl ProcessMemory {
// Test for space between existing areas
if let Some(area) = self.areas.iter().enumerate().find(|(i, area)| {
if self.areas[i + 1].start - area.end >= len as u64 {
if self.areas.len() > i + 1 && self.areas[i + 1].start - area.end >= len as u64 {
true
} else {
false
......
......@@ -27,12 +27,12 @@ use super::{
};
use crate::memory::{self, PAGE_SIZE};
/// The address of the user stack
pub const STACK_ADDR: u64 = 0xa0000000;
/// The size of a process' stack
pub const STACK_SIZE: usize = 65536;
/// The size of a process' data segment
pub const DATA_SEGMENT_SIZE: usize = 65536;
/// The next Process ID which will be used
pub static NEXT_PID: AtomicUsize = AtomicUsize::new(0);
......@@ -80,17 +80,8 @@ pub struct Process {
impl Process {
/// Create a new process
pub fn new(mut info: ProcessInfo) -> (Self, usize, u64) {
// We can use a static address for the stack because the content of it will be switched
// with the switch of page tables
let mappings = memory::map(
STACK_ADDR - STACK_SIZE as u64,
STACK_ADDR,
Flags::PRESENT | Flags::WRITABLE,
);
let rsp = info.push_at(STACK_ADDR as usize);
let mut process = Self {
pub fn new(info: ProcessInfo) -> Self {
Self {
pid: Pid(NEXT_PID.fetch_add(1, Ordering::Relaxed)),
state: ProcessState::Alive,
memory: ProcessMemory::new(),
......@@ -99,7 +90,20 @@ impl Process {
threads: vec![],
children: vec![],
parent: None,
};
}
}
/// Create the user stack
///
/// This function is called after the creation of the binary in memory, so the stack address
/// can be flexible with the end of the binary
pub fn create_stack(&mut self, addr: u64) -> (usize, u64) {
let mappings = memory::map(
addr,
addr + STACK_SIZE as u64,
Flags::PRESENT | Flags::WRITABLE,
);
let rsp = self.info.push_at(addr as usize + STACK_SIZE);
// Add the mappings in the process' page table
let mut area = MemoryArea::new(
......@@ -108,12 +112,26 @@ impl Process {
Flags::PRESENT | Flags::WRITABLE,
);
area.mappings(mappings.clone());
process.memory.map(area);
self.memory.map(area);
// Unmap the stack in the kernel's address space
memory::unmap(mappings);
(process, rsp, info.first_arg_addr)
(rsp, self.info.first_arg_addr)
}
/// Create the data segment
///
/// This function is called after the creation of the binary in memory, so the data segment address
/// can be flexible with the end of the binary
pub fn create_data_segment(&mut self, addr: u64) {
// Add the mappings in the process' page table
let area = MemoryArea::new(
addr,
addr + DATA_SEGMENT_SIZE as u64,
Flags::PRESENT | Flags::WRITABLE,
);
self.memory.map(area);
}
/// Exit handler called when the process exits
......@@ -156,14 +174,32 @@ pub struct ProcessBuilder {
impl ProcessBuilder {
/// Create a new ProcessBuilder
pub fn new(rip: usize, info: ProcessInfo) -> Self {
let (mut process, rsp, first_arg_addr) = Process::new(info.clone());
let mut process = Process::new(info.clone());
let cr3 = process.memory.table.frame().start_address().as_u64() as usize;
let thread = Thread::new(process.pid, rip, rsp, cr3, info.args.len(), first_arg_addr);
let thread = Thread::new(process.pid, rip, cr3);
process.threads.push(thread.tid);
Self { thread, process }
}
/// Create the user stack in the underlying process
pub fn create_stack(&mut self, addr: u64) {
let (rsp, first_arg_addr) = self.process.create_stack(addr);
let mut regs = self.thread.get_kstack();
// Set the stack pointer
regs.rsp = rsp;
// Set the arguments
regs.rdi = self.process.info.args.len();
regs.rsi = first_arg_addr as usize;
}
/// Create the data segment
pub fn create_data_segment(&mut self, addr: u64) {
self.process.create_data_segment(addr);
}
/// Get the memory of the process
pub fn memory(&mut self) -> &mut ProcessMemory { &mut self.process.memory }
......
......@@ -67,20 +67,15 @@ pub struct Thread {
impl Thread {
/// Create a new Thread
pub fn new(process: Pid, rip: usize, rsp: usize, cr3: usize, argc: usize, argv: u64) -> Self {
pub fn new(process: Pid, rip: usize, cr3: usize) -> Self {
let kstack_pointer =
Box::into_raw(unsafe { Box::from_raw(HEAP_ALLOCATOR.alloc(Layout::from_size_align_unchecked(KSTACK_SIZE, 4096)) as *mut [u8; KSTACK_SIZE]) }) as u64 + KSTACK_SIZE as u64;
// Create a new set of registers
let mut regs = Registers::default();
// Set the arguments
regs.rdi = argc;
regs.rsi = argv as usize;
// Create a new stack frame
// Create a new stack frame (without rsp)
regs.rip = rip;
regs.rsp = rsp;
regs.rflags = 1 << 9;
regs.cs = gdt::GDT_USER_CODE << 3 | 3;
regs.ds = gdt::GDT_USER_DATA << 3 | 3;
......@@ -103,6 +98,11 @@ impl Thread {
}
}
/// Translate the kernel stack into a struct Registers
pub fn get_kstack(&mut self) -> &mut Registers {
unsafe { &mut *(self.kstack as *mut Registers) }
}
/// Create a new Thread used during the `fork` syscall
pub fn fork(process: Pid, mut regs: Registers) -> (Self, Mappings) {
let kstack_pointer =
......
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