Commit 59825c88 authored by Nifou's avatar Nifou

Change arguments management, include environment variables and auxiliary vector

parent 72ba386f
......@@ -15,13 +15,28 @@
* along with this program. If not, see https://www.gnu.org/licenses.
*/
//! A loader which loads ELF static binaries
use alloc::prelude::v1::{Vec, String};
use alloc::{
prelude::v1::String,
collections::btree_map::BTreeMap,
};
use core::ptr::copy_nonoverlapping;
use goblin::elf::Elf;
use goblin::elf::{
Elf,
program_header::{PT_LOAD, PT_PHDR},
};
use x86_64::structures::paging::page_table::PageTableFlags as Flags;
use crate::memory;
use crate::tasking::{process::Process, scheduler};
use crate::tasking::{
process::Process,
scheduler,
info::{
ProcessInfo,
AuxVecItem,
},
};
#[derive(PartialEq, Debug, Clone, Copy)]
/// An ELF check error
......@@ -41,6 +56,9 @@ pub struct ElfLoader<'a> {
/// The content of the binary
data: &'a [u8],
/// The path of the binary
path: String,
}
/// The ELF magic
......@@ -48,8 +66,8 @@ static MAGIC: [u8; 4] = [0x7f, 'E' as u8, 'L' as u8, 'F' as u8];
impl<'a> ElfLoader<'a> {
/// Create a new ElfLoader with the specified binary
pub fn new(binary: Elf<'a>, data: &'a [u8]) -> Self {
Self { binary, data }
pub fn new(binary: Elf<'a>, data: &'a [u8], path: String) -> Self {
Self { binary, data, path}
}
/// Check the binary
......@@ -60,8 +78,8 @@ impl<'a> ElfLoader<'a> {
}
// Verify that all segment are above the limit
for ph in &self.binary.program_headers.clone()[2..] {
if ph.p_type == goblin::elf::program_header::PT_LOAD {
for ph in &self.binary.program_headers {
if ph.p_type == PT_LOAD {
let end = ph.p_vaddr + ph.p_memsz as u64;
if end > memory::KERNEL_START_VIRT {
......@@ -73,12 +91,32 @@ impl<'a> ElfLoader<'a> {
Ok(())
}
fn info(&self) -> ProcessInfo {
let args = vec![self.path.clone()];
let envs = vec!["PATH=/bin".into()];
let mut auxv = BTreeMap::new();
if let Some(phdr) = self.binary.program_headers.iter().find(|ph| ph.p_type == PT_PHDR) {
auxv.insert(AuxVecItem::PHDR as u8, phdr.p_vaddr as usize);
} else if let Some(elf_addr) = self.binary.program_headers.iter().find(|ph| ph.p_type == PT_LOAD && ph.p_offset == 0) {
auxv.insert(AuxVecItem::PHDR as u8, (elf_addr.p_vaddr + self.binary.header.e_phoff) as usize);
} else {
println!("No program header found!");
}
auxv.insert(AuxVecItem::PHENT as u8, self.binary.header.e_phentsize as usize);
auxv.insert(AuxVecItem::PHNUM as u8, self.binary.header.e_phnum as usize);
auxv.insert(AuxVecItem::PAGESZ as u8, 4096); // TODO: Constant?
auxv.insert(AuxVecItem::ENTRY as u8, self.binary.header.e_entry as usize);
ProcessInfo { args, envs, auxv, first_arg_addr: 0 }
}
/// Load the binary in memory
pub fn load(&mut self, name: String) -> Process {
let mut process = Process::new(self.binary.entry, Vec::from([name.as_bytes().to_vec()]));
pub fn load(&mut self) -> Process {
let mut process = Process::new(self.binary.entry, self.info());
for ph in self.binary.program_headers.clone() {
if ph.p_type == goblin::elf::program_header::PT_LOAD {
if ph.p_type == PT_LOAD {
let start = ph.p_vaddr as u64;
println!(
......
......@@ -32,14 +32,14 @@ pub fn load(path: String) {
match Elf::parse(data) {
Ok(binary) => {
let mut loader = ElfLoader::new(binary, data);
let mut loader = ElfLoader::new(binary, data, path);
if let Err(e) = loader.check() {
println!(
"Error during the check of the ELF binary: {:?}. The binary is not loaded.",
e
);
} else {
let process = loader.load(path);
let process = loader.load();
loader.run(process);
}
}
......
......@@ -19,7 +19,7 @@ use linked_list_allocator::LockedHeap;
use super::{KERNEL_START_VIRT, KERNEL_SIZE};
/// The start of the kernel's heap
/// The start of the kernel's heap in virtual memory
pub const HEAP_START: usize = KERNEL_START_VIRT as usize + KERNEL_SIZE as usize + 4096;
/// The size of the kernel's heap
......
......@@ -126,7 +126,7 @@ fn remap_kernel(frame: PhysFrame) -> RecursivePageTable<'static> {
Page::containing_address(VirtAddr::new(KERNEL_START_VIRT)),
Page::containing_address(VirtAddr::new(KERNEL_START_VIRT + KERNEL_SIZE))
).enumerate() {
let frame = PhysFrame::containing_address(PhysAddr::new(i as u64 * 4096));
let frame = PhysFrame::containing_address(PhysAddr::new(KERNEL_START_PHYS + i as u64 * 4096));
unsafe { mapper.map_to(page, frame, Flags::PRESENT | Flags::WRITABLE, &mut *FRAME_ALLOCATOR.r#try().unwrap().lock()).unwrap().ignore(); }
}
......
/*
* Copyright (C) 2018-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.
*/
//! Arguments management and storage
use alloc::prelude::v1::Vec;
/// The address where the arguments will be copied for each process
pub const ARGUMENTS_ADDR: u64 = 0x80000000;
#[derive(PartialEq, Debug, Clone)]
/// A struct which represents arguments passed to a program
pub struct Arguments {
argc: u64,
argv: Vec<Vec<u8>>,
}
impl Arguments {
/// Create a new Arguments
pub fn new(args: Vec<Vec<u8>>) -> Self {
Self {
argc: args.len() as u64,
argv: args,
}
}
/// Get the number of arguments
pub fn argc(&self) -> u64 { self.argc }
/// Get the arguments
pub fn argv(&self) -> Vec<Vec<u8>> { self.argv.clone() }
}
/*
* 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.
*/
//! Management of the informations about a process
use alloc::{
prelude::v1::{String, Vec},
collections::btree_map::BTreeMap,
};
use core::ptr::null;
/// An item in the [auxiliary vector](https://www.gnu.org/software/libc/manual/html_node/Auxiliary-Vector.html)
#[derive(PartialEq, Debug, Clone)]
pub enum AuxVecItem {
/// Pointer to the program headers of the program
PHDR = 3,
/// Size of a program header's entry
PHENT = 4,
/// Number of program headers
PHNUM = 5,
/// Size of a page
PAGESZ = 6,
/// Entry point of the program
ENTRY = 9,
}
/// The informations about a process (arguments, environment, auxiliary vector, ...)
#[derive(PartialEq, Debug, Clone)]
pub struct ProcessInfo {
/// The arguments of the program
pub args: Vec<String>,
/// The environment variables of the program
pub envs: Vec<String>,
/// The auxiliary vector of the program
pub auxv: BTreeMap<u8, usize>,
/// The address of the first argument
pub first_arg_addr: u64,
}
impl ProcessInfo {
/// Write the infos at the address `stack_top`
pub fn push_at(&mut self, stack_top: usize) -> usize {
let mut writer = StackWriter { sp: stack_top };
// Program name
writer.push_str(&self.args[0]);
// Environment variables
let envs: Vec<_> = self
.envs
.iter()
.map(|arg| {
writer.push_str(arg.as_str());
writer.sp
})
.collect();
// Arguments
let argv: Vec<_> = self
.args
.iter()
.map(|arg| {
writer.push_str(arg.as_str());
writer.sp
})
.collect();
// Set the first argument address
self.first_arg_addr = argv[0] as u64;
// Auxiliary vector
writer.push_slice(&[null::<u8>(), null::<u8>()]);
for (&type_, &value) in self.auxv.iter() {
writer.push_slice(&[type_ as usize, value]);
}
// Environment pointers
writer.push_slice(&[null::<u8>()]);
writer.push_slice(envs.as_slice());
// Arguments pointers
writer.push_slice(&[null::<u8>()]);
writer.push_slice(argv.as_slice());
writer.push_slice(&[argv.len()]);
writer.sp
}
}
struct StackWriter {
sp: usize,
}
impl StackWriter {
fn push_slice<T: Copy>(&mut self, vs: &[T]) {
use core::{
mem::{align_of, size_of},
slice,
};
self.sp -= vs.len() * size_of::<T>();
self.sp -= self.sp % align_of::<T>();
unsafe { slice::from_raw_parts_mut(self.sp as *mut T, vs.len()) }.copy_from_slice(vs);
}
fn push_str(&mut self, s: &str) {
self.push_slice(&[b'\0']);
self.push_slice(s.as_bytes());
}
}
......@@ -26,7 +26,6 @@ use x86_64::{
use crate::memory;
use crate::memory::table::ProcessPageTable;
use crate::tasking::process::STACK_SIZE;
#[derive(PartialEq, Debug, Clone)]
/// A structure which represents the memory used by a process.
......@@ -37,15 +36,15 @@ pub struct ProcessMemory {
impl ProcessMemory {
/// Create a new ProcessMemory
pub fn new(stack_addr: u64) -> Self {
let frame = memory::FRAME_ALLOCATOR
pub fn new() -> Self {
let page_table_frame = memory::FRAME_ALLOCATOR
.r#try()
.unwrap()
.lock()
.allocate_frame()
.unwrap();
let table = ProcessPageTable::new(frame);
let table = ProcessPageTable::new(page_table_frame);
table.with(|mapper| {
// Map the kernel
......@@ -53,16 +52,18 @@ impl ProcessMemory {
Page::containing_address(VirtAddr::new(memory::KERNEL_START_VIRT)),
Page::containing_address(VirtAddr::new(memory::KERNEL_START_VIRT + memory::KERNEL_SIZE))
).enumerate() {
let frame = PhysFrame::containing_address(PhysAddr::new(i as u64 * 4096));
let frame = PhysFrame::containing_address(PhysAddr::new(memory::KERNEL_START_PHYS + i as u64 * 4096));
unsafe { mapper.map_to(page, frame, Flags::PRESENT | Flags::WRITABLE, &mut *memory::FRAME_ALLOCATOR.r#try().unwrap().lock()).unwrap().ignore(); }
}
});
// Map the stack
let flags = Flags::PRESENT | Flags::WRITABLE;
unsafe {
table.map(stack_addr, stack_addr + STACK_SIZE as u64, flags, flags);
}
// Map the kernel's page table frame
if let Some(frame) = memory::KERNEL_PAGE_TABLE.r#try() {
unsafe { mapper.identity_map(*frame, Flags::PRESENT | Flags::WRITABLE, &mut *memory::FRAME_ALLOCATOR.r#try().unwrap().lock()).unwrap().ignore(); }
}
// Map the table itself
unsafe { mapper.identity_map(page_table_frame, Flags::PRESENT | Flags::WRITABLE, &mut *memory::FRAME_ALLOCATOR.r#try().unwrap().lock()).unwrap().ignore(); }
});
Self {
table,
......
......@@ -21,9 +21,11 @@
//! * A struct ProcessMemory which handles all mappings of the process
//! * A struct Arguments which copies arguments at the right place and which provides argc
//! (argument count) and argv (arguments)
//! * A struct ProcessInfo which contains the arguments, the environment variables and the
//! auxiliary vector of the process
pub mod context;
pub mod memory;
pub mod process;
pub mod registers;
pub mod scheduler;
pub mod args;
pub mod info;
......@@ -15,18 +15,16 @@
* along with this program. If not, see https://www.gnu.org/licenses.
*/
//! The representation of a process with its memory, its registers, ...
use alloc::prelude::v1::{Box, Vec};
use core::{
mem,
ptr::copy_nonoverlapping,
};
use x86_64::structures::paging::page_table::PageTableFlags as Flags;
use super::context::Context;
use super::memory::ProcessMemory;
use super::args::{Arguments, ARGUMENTS_ADDR};
use super::info::ProcessInfo;
use crate::memory;
/// The address of the user stack
pub const STACK_ADDR: u64 = 0xa0000000;
/// The size of a process' stack
pub const STACK_SIZE: usize = 65536;
......@@ -52,75 +50,32 @@ pub struct Process {
/// The memory used by this process
pub memory: ProcessMemory,
/// Arguments of the process (argc + argv)
pub args: Arguments,
/// Info about the process (arguments, environment, ...)
pub info: ProcessInfo,
}
impl Process {
/// Create a new process
pub fn new(rip: u64, args: Vec<Vec<u8>>) -> Self {
let stack = vec![0; STACK_SIZE];
let rsp = Box::into_raw(Box::new(stack)) as u64;
pub fn new(rip: u64, mut info: ProcessInfo) -> Self {
// 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) as u64;
let mut process = Self {
ctx: Context::new(rip, rsp),
state: ProcessState::Alive,
memory: ProcessMemory::new(rsp),
args: Arguments::new(args),
memory: ProcessMemory::new(),
info,
};
// Initialize arguments
process.copy_args();
process
}
/// Copy the arguments in the stack and at ARGUMENTS_ADDR
pub fn copy_args(&mut self) {
let mut arg_size = 0;
let mut sp = self.ctx.get_rsp();
// Add the mappings in the process' page table
process.memory.mappings(mappings.clone(), Flags::PRESENT | Flags::WRITABLE, Flags::PRESENT | Flags::USER_ACCESSIBLE);
// Push null-terminator
sp -= mem::size_of::<usize>() as u64;
unsafe { *(sp as *mut usize) = 0; }
// Unmap the stack in the kernel's address space
memory::unmap(mappings);
// Push arguments and variables
for arg in self.args.argv().iter().rev() {
// Push content
sp -= mem::size_of::<usize>() as u64;
unsafe { *(sp as *mut usize) = ARGUMENTS_ADDR as usize + arg_size; }
arg_size += arg.len() + 1;
}
// Push arguments length
sp -= mem::size_of::<usize>() as u64;
unsafe { *(sp as *mut usize) = self.args.argv().len(); }
if arg_size > 0 {
let mappings =
memory::map(ARGUMENTS_ADDR, ARGUMENTS_ADDR + arg_size as u64, Flags::PRESENT | Flags::WRITABLE);
let mut arg_offset = 0;
for arg in self.args.argv().iter().rev() {
unsafe {
copy_nonoverlapping(arg.as_ptr(),
(ARGUMENTS_ADDR + arg_offset) as *mut u8,
arg.len());
}
arg_offset += arg.len() as u64;
unsafe {
*((ARGUMENTS_ADDR + arg_offset) as *mut u8) = 0;
}
arg_offset += 1;
}
// Add the mappings in the process' page table
self.memory.mappings(mappings.clone(), Flags::PRESENT | Flags::NO_EXECUTE | Flags::USER_ACCESSIBLE, Flags::PRESENT | Flags::USER_ACCESSIBLE);
memory::unmap(mappings);
}
process
}
/// Kill the process
......@@ -128,7 +83,7 @@ impl Process {
self.state = ProcessState::Killed;
}
/// Jump to this process
/// Jump to this process
pub fn jmp(&self, rip: u64, rsp: u64, arg1: u64, arg2: u64) {
use crate::gdt;
......@@ -147,8 +102,8 @@ impl Process {
"{r11}"(1 << 9), // Flags - Set interrupt enable flag
"{r12}"(gdt::GDT_USER_CODE << 3 | 3), // Code segment
"{r13}"(rip), // IP
"{r14}"(arg2), // Argument 2 (argv)
"{r15}"(arg1) // Argument 1 (argc)
"{r14}"(arg2), // Argument 2
"{r15}"(arg1) // Argument 1
: // No clobbers
: "intel", "volatile");
......@@ -172,7 +127,7 @@ impl Process {
xor r13, r13
xor r14, r14
xor r15, r15
finit
fninit
pop rdi
pop rsi
iretq"
......
......@@ -21,7 +21,6 @@ use lazy_static::lazy_static;
use spin::Mutex;
use super::process::{Process, ProcessState};
use super::args::ARGUMENTS_ADDR;
lazy_static! {
/// A global scheduler
......@@ -83,12 +82,13 @@ impl Scheduler {
// Switch to the process' page table
let rip = self.threads[current_thread].ctx.get_rip();
let rsp = self.threads[current_thread].ctx.get_rsp();
let argc = self.threads[current_thread].args.argc();
let argc = self.threads[current_thread].info.args.len();
let argv = self.threads[current_thread].info.first_arg_addr;
self.threads[current_thread].memory.switch_page_table();
// Jump to the process
self.threads[current_thread].jmp(rip, rsp, argc, ARGUMENTS_ADDR);
self.threads[current_thread].jmp(rip, rsp, argc as u64, argv);
}
/// Add a thread in the queue
......
......@@ -40,7 +40,7 @@ macro_rules! println {
}
#[no_mangle]
pub extern "C" fn main(_argc: i32, argv: *const *const u8) -> i32 {
pub extern "C" fn main(_argc: i32, argv: *const u8) -> i32 {
// This function print the name of the program passed in the arguments.
// We can't use heap for now, so we print each character separatly
print!("Program name: ");
......
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