From 850792bbf13614c517f4f3290bfa2f12ef23ddaf Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 7 Sep 2016 21:16:30 -0600 Subject: [PATCH] Load a very simple ELF and launch it in usermode --- arch/x86_64/src/idt.rs | 1 + arch/x86_64/src/interrupt/syscall.rs | 37 ++++--- arch/x86_64/src/lib.rs | 2 +- arch/x86_64/src/paging/mapper.rs | 9 ++ arch/x86_64/src/paging/table.rs | 2 +- arch/x86_64/src/start.rs | 44 ++++----- init/main | Bin 0 -> 512 bytes init/main.asm | 20 ++++ init/main.o | Bin 0 -> 880 bytes kernel/elf.rs | 140 +++++++++++++++++++++++---- kernel/lib.rs | 5 +- 11 files changed, 201 insertions(+), 59 deletions(-) create mode 100755 init/main create mode 100644 init/main.asm create mode 100644 init/main.o diff --git a/arch/x86_64/src/idt.rs b/arch/x86_64/src/idt.rs index 75f54f1..e53e259 100644 --- a/arch/x86_64/src/idt.rs +++ b/arch/x86_64/src/idt.rs @@ -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); } diff --git a/arch/x86_64/src/interrupt/syscall.rs b/arch/x86_64/src/interrupt/syscall.rs index 45eae6a..feac040 100644 --- a/arch/x86_64/src/interrupt/syscall.rs +++ b/arch/x86_64/src/interrupt/syscall.rs @@ -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"); } diff --git a/arch/x86_64/src/lib.rs b/arch/x86_64/src/lib.rs index d54cb5d..bd18ee9 100644 --- a/arch/x86_64/src/lib.rs +++ b/arch/x86_64/src/lib.rs @@ -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] diff --git a/arch/x86_64/src/paging/mapper.rs b/arch/x86_64/src/paging/mapper.rs index 5ca7570..dbbaa60 100644 --- a/arch/x86_64/src/paging/mapper.rs +++ b/arch/x86_64/src/paging/mapper.rs @@ -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())); diff --git a/arch/x86_64/src/paging/table.rs b/arch/x86_64/src/paging/table.rs index db2564f..956787b 100644 --- a/arch/x86_64/src/paging/table.rs +++ b/arch/x86_64/src/paging/table.rs @@ -66,7 +66,7 @@ impl Table 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() diff --git a/arch/x86_64/src/start.rs b/arch/x86_64/src/start.rs index 1b1047d..c99dd64 100644 --- a/arch/x86_64/src/start.rs +++ b/arch/x86_64/src/start.rs @@ -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"); +} diff --git a/init/main b/init/main new file mode 100755 index 0000000000000000000000000000000000000000..872fc339dff51bc94418dfb899402b6aff7c415c GIT binary patch literal 512 zcmb<-^>JfjWMqH=CI&kO5N`v616T+`0+|RUKp6`LCLqbezzh~<1*?P+FdY#3DpVLo zGcYKCML@=}0r3qm4@4(`SumOpDh{Jz`qJ8O?*fT=?1ZV@4Kiv63y?V5umi+C+W;gy zQgd?hbrj0;i*iyFxft|{Gm1-!N)nS8^h#1IN*MG~5=#<+$|wfhKoeko0T~7hw+&E} xRiQMD5`ZzFv>=cM>1T%WVZMTe3&Q^p1t5PS17y9(;?JNQab!Ut8>Ai?008+ZBm@8e literal 0 HcmV?d00001 diff --git a/init/main.asm b/init/main.asm new file mode 100644 index 0000000..514295b --- /dev/null +++ b/init/main.asm @@ -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 diff --git a/init/main.o b/init/main.o new file mode 100644 index 0000000000000000000000000000000000000000..5c5b590756309e75462979c74c12c64a19f70f38 GIT binary patch literal 880 zcmb<-^>JfjWMqH=Mg}_u1P><4z~F#jLfH-s>CU=+Ij>OcvYnJ_gVIsmF4W)DaWNH0t+Ogydq_AXEudF%udV6Ypc zYzIi)*@hh;_Sps?0RtYXIXU?{3g!7lIVp- { 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 { - 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 { + 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 + } } } diff --git a/kernel/lib.rs b/kernel/lib.rs index 19d1a4e..d88d926 100644 --- a/kernel/lib.rs +++ b/kernel/lib.rs @@ -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(); } }