Load a very simple ELF and launch it in usermode

This commit is contained in:
Jeremy Soller 2016-09-07 21:16:30 -06:00
parent 398838dc1b
commit 850792bbf1
11 changed files with 201 additions and 59 deletions

View file

@ -60,6 +60,7 @@ pub unsafe fn init() {
// Set syscall function
IDT[0x80].set_func(syscall::syscall);
IDT[0x80].set_flags(IDT_PRESENT | IDT_RING_3 | IDT_INTERRUPT);
dtables::lidt(&IDTR);
}

View file

@ -1,22 +1,29 @@
#[naked]
pub unsafe extern fn syscall() {
extern {
fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> usize;
#[inline(never)]
unsafe fn inner() {
extern {
fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> usize;
}
let mut a;
{
let b;
let c;
let d;
let e;
let f;
asm!("xchg bx, bx" : "={rax}"(a), "={rbx}"(b), "={rcx}"(c), "={rdx}"(d), "={rsi}"(e), "={rdi}"(f)
: : : "intel", "volatile");
a = syscall(a, b, c, d, e, f);
}
asm!("xchg bx, bx" : : "{rax}"(a) : : "intel", "volatile");
}
let a;
let b;
let c;
let d;
let e;
let f;
asm!("" : "={rax}"(a), "={rbx}"(b), "={rcx}"(c), "={rdx}"(d), "={rsi}"(e), "={rdi}"(f)
: : : "intel", "volatile");
inner();
let a = syscall(a, b, c, d, e, f);
asm!("" : : "{rax}"(a) : : "intel", "volatile");
// Pop scratch registers, error code, and return
// Interrupt return
asm!("iretq" : : : : "intel", "volatile");
}

View file

@ -17,7 +17,7 @@ extern crate hole_list_allocator as allocator;
extern crate bitflags;
extern crate ransid;
extern crate spin;
extern crate x86;
pub extern crate x86;
/// Print to console
#[macro_export]

View file

@ -46,6 +46,15 @@ impl Mapper {
self.map_to(page, frame, flags)
}
/// Update flags for a page
pub fn remap(&mut self, page: Page, flags: EntryFlags) {
let mut p3 = self.p4_mut().next_table_mut(page.p4_index()).expect("failed to remap: no p3");
let mut p2 = p3.next_table_mut(page.p3_index()).expect("failed to remap: no p2");
let mut p1 = p2.next_table_mut(page.p2_index()).expect("failed to remap: no p1");
let frame = p1[page.p1_index()].pointed_frame().expect("failed to remap: not mapped");
p1[page.p1_index()].set(frame, flags | entry::PRESENT);
}
/// Identity map a frame
pub fn identity_map(&mut self, frame: Frame, flags: EntryFlags) {
let page = Page::containing_address(VirtualAddress::new(frame.start_address().get()));

View file

@ -66,7 +66,7 @@ impl<L> Table<L> where L: HierarchicalLevel {
assert!(!self[index].flags().contains(HUGE_PAGE),
"mapping code does not support huge pages");
let frame = allocate_frame().expect("no frames available");
self[index].set(frame, PRESENT | WRITABLE);
self[index].set(frame, PRESENT | WRITABLE | USER_ACCESSIBLE /* Allow users to go down the page table, implement permissions at the page level */);
self.next_table_mut(index).unwrap().zero();
}
self.next_table_mut(index).unwrap()

View file

@ -137,28 +137,6 @@ pub unsafe extern fn kstart() -> ! {
kmain();
}
unsafe fn usermode(ip: usize, sp: usize) {
// Test usermode
asm!("xchg bx, bx
mov rax, 0x2B
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push rax
push rbx
pushfq
mov rax, 0x23
push rax
push rcx
iretq"
:
: "{rbx}"(sp), "{rcx}"(ip)
: "rax", "rbx", "rcx", "sp"
: "intel", "volatile");
}
/// Entry to rust for an AP
pub unsafe extern fn kstart_ap(stack_start: usize, stack_end: usize) -> ! {
{
@ -223,3 +201,25 @@ pub unsafe extern fn kstart_ap(stack_start: usize, stack_end: usize) -> ! {
kmain_ap(ap_number);
}
pub unsafe fn usermode(ip: usize, sp: usize) {
// Test usermode
asm!("xchg bx, bx
mov rax, 0x2B
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push rax
push rbx
pushfq
mov rax, 0x23
push rax
push rcx
iretq"
:
: "{rbx}"(sp), "{rcx}"(ip)
: "rax", "rbx", "rcx", "sp"
: "intel", "volatile");
}

BIN
init/main Executable file

Binary file not shown.

20
init/main.asm Normal file
View file

@ -0,0 +1,20 @@
[bits 64]
section .text
global _start ;must be declared for linker (ld)
_start: ;tell linker entry point
xchg bx, bx
mov rdx,len ;message length
mov rcx,msg ;message to write
mov rbx,1 ;file descriptor (stdout)
mov rax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov rax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg db 'Hello, world!',0xa ;our dear string
len equ $ - msg ;length of our dear string

BIN
init/main.o Normal file

Binary file not shown.

View file

@ -1,8 +1,8 @@
//! ELF executables
use collections::{String, Vec};
use collections::String;
use core::{ptr, str};
use core::str;
#[cfg(target_arch = "x86")]
use goblin::elf32::{header, program_header};
@ -10,9 +10,15 @@ use goblin::elf32::{header, program_header};
#[cfg(target_arch = "x86_64")]
use goblin::elf64::{header, program_header};
use arch::externs::{memcpy, memset};
use arch::paging::{entry, ActivePageTable, Page, VirtualAddress};
use arch::start::usermode;
use arch::x86::tlb;
/// An ELF executable
pub struct Elf<'a> {
pub data: &'a [u8],
header: &'a header::Header
}
impl<'a> Elf<'a> {
@ -25,29 +31,125 @@ impl<'a> Elf<'a> {
} else if data.get(header::EI_CLASS) != Some(&header::ELFCLASS) {
Err(format!("Elf: Invalid architecture: {:?} != {:?}", data.get(header::EI_CLASS), header::ELFCLASS))
} else {
Ok(Elf { data: data })
Ok(Elf {
data: data,
header: unsafe { &*(data.as_ptr() as usize as *const header::Header) }
})
}
}
pub unsafe fn load_segments(&self) -> Vec<program_header::ProgramHeader> {
let mut segments = Vec::new();
let header = &*(self.data.as_ptr() as usize as *const header::Header);
for i in 0..header.e_phnum {
let segment = ptr::read((self.data.as_ptr() as usize + header.e_phoff as usize + i as usize * header.e_phentsize as usize) as *const program_header::ProgramHeader);
if segment.p_type == program_header::PT_LOAD || segment.p_type == program_header::PT_TLS {
segments.push(segment);
}
pub fn segments(&'a self) -> ElfSegments<'a> {
ElfSegments {
data: self.data,
header: self.header,
i: 0
}
segments
}
/// Get the entry field of the header
pub unsafe fn entry(&self) -> usize {
let header = &*(self.data.as_ptr() as usize as *const header::Header);
header.e_entry as usize
pub fn entry(&self) -> usize {
self.header.e_entry as usize
}
/// Test function to run. Remove and replace with proper syscall
pub fn run(self) {
let mut active_table = unsafe { ActivePageTable::new() };
for segment in self.segments() {
println!("Segment {:X} flags {:X} off {:X} virt {:X} phys {:X} file {} mem {} align {}",
segment.p_type, segment.p_flags, segment.p_offset,
segment.p_vaddr, segment.p_paddr, segment.p_filesz,
segment.p_memsz, segment.p_align);
if segment.p_type == program_header::PT_LOAD {
let start_page = Page::containing_address(VirtualAddress::new(segment.p_vaddr as usize));
let end_page = Page::containing_address(VirtualAddress::new((segment.p_vaddr + segment.p_memsz) as usize));
for page in Page::range_inclusive(start_page, end_page) {
active_table.map(page, entry::NO_EXECUTE | entry::WRITABLE);
}
unsafe {
// Update the page table
tlb::flush_all();
// Copy file data
memcpy(segment.p_vaddr as *mut u8,
(self.data.as_ptr() as usize + segment.p_offset as usize) as *const u8,
segment.p_filesz as usize);
// Set BSS
memset((segment.p_vaddr + segment.p_filesz) as *mut u8,
0,
(segment.p_memsz - segment.p_filesz) as usize);
}
let mut flags = entry::NO_EXECUTE | entry::USER_ACCESSIBLE;
if segment.p_flags & program_header::PF_R == program_header::PF_R {
flags.insert(entry::PRESENT);
}
// W ^ X. If it is executable, do not allow it to be writable, even if requested
if segment.p_flags & program_header::PF_X == program_header::PF_X {
flags.remove(entry::NO_EXECUTE);
} else if segment.p_flags & program_header::PF_W == program_header::PF_W {
flags.insert(entry::WRITABLE);
}
for page in Page::range_inclusive(start_page, end_page) {
println!("{:X}: {:?}", page.start_address().get(), flags);
active_table.remap(page, flags);
}
unsafe {
// Update the page table
tlb::flush_all();
}
}
}
unsafe {
// Map stack
let start_page = Page::containing_address(VirtualAddress::new(0x80000000));
let end_page = Page::containing_address(VirtualAddress::new(0x80000000 + 64*1024 - 1));
for page in Page::range_inclusive(start_page, end_page) {
active_table.map(page, entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE);
}
// Update the page table
tlb::flush_all();
// Clear stack
memset(0x80000000 as *mut u8, 0, 64 * 1024);
// Go to usermode
usermode(self.entry(), 0x80000000 + 64*1024 - 256);
}
}
}
pub struct ElfSegments<'a> {
data: &'a [u8],
header: &'a header::Header,
i: usize
}
impl<'a> Iterator for ElfSegments<'a> {
type Item = &'a program_header::ProgramHeader;
fn next(&mut self) -> Option<Self::Item> {
if self.i < self.header.e_phnum as usize {
let item = unsafe {
&* ((
self.data.as_ptr() as usize
+ self.header.e_phoff as usize
+ self.i * self.header.e_phentsize as usize
) as *const program_header::ProgramHeader)
};
self.i += 1;
Some(item)
} else {
None
}
}
}

View file

@ -133,6 +133,9 @@ pub extern fn kmain() {
let pid = syscall::getpid();
println!("BSP: {:?}", pid);
let elf = elf::Elf::from(include_bytes!("../init/main")).expect("could not load elf");
elf.run();
/*
if let Ok(_context_lock) = context::contexts_mut().spawn(context_test) {
print!("Spawned context\n");
@ -143,7 +146,7 @@ pub extern fn kmain() {
print!("Main halt\n");
*/
loop {
unsafe { interrupt::enable_and_halt(); }
}