From 074dc9547a9d9fe30bf1abeef5bd998fd54140de Mon Sep 17 00:00:00 2001 From: Stephen Marz Date: Sun, 26 Apr 2020 08:33:49 -0400 Subject: [PATCH] Now we read ELF files instead of raw binaries --- risc_v/src/test.rs | 160 ++++++++++++++++++++++- risc_v/src/userspace/Makefile | 4 +- risc_v/src/userspace/startlib/linker.lds | 4 +- 3 files changed, 163 insertions(+), 5 deletions(-) diff --git a/risc_v/src/test.rs b/risc_v/src/test.rs index 684aea6..db2d9fe 100644 --- a/risc_v/src/test.rs +++ b/risc_v/src/test.rs @@ -16,7 +16,9 @@ use crate::{cpu::{build_satp, PROCESS_STARTING_ADDR, STACK_ADDR, STACK_PAGES}, - syscall::syscall_fs_read}; + syscall::syscall_fs_read}; +use crate::elf; +use crate::fs::BlockBuffer; pub fn test_block() { // The bytes to read would usually come from the inode, but we are in an @@ -145,3 +147,159 @@ pub fn test_block() { println!(); kfree(buffer); } + +/// Test block will load raw binaries into memory to execute them. This function +/// will load ELF files and try to execute them. +pub fn test_elf() { + // The bytes to read would usually come from the inode, but we are in an + // interrupt context right now, so we cannot pause. Usually, this would be done + // by an exec system call. + let files_inode = 25u32; + let files_size = 14304; + let bytes_to_read = 1024 * 50; + let mut buffer = BlockBuffer::new(bytes_to_read); + // Read the file from the disk. I got the inode by mounting + // the harddrive as a loop on Linux and stat'ing the inode. + let bytes_read = syscall_fs_read(8, files_inode, buffer.get_mut(), bytes_to_read as u32, 0); + // After compiling our program, I manually looked and saw it was 18,360 + // bytes. So, to make sure we got the right one, I do a manual check + // here. + if bytes_read != files_size { + println!( + "Unable to load program at inode {}, which should be \ + {} bytes, got {}", + files_inode, + files_size, + bytes_read + ); + return; + } + // Let's get this program running! + // Everything is "page" based since we're going to map pages to + // user space. So, we need to know how many program pages we + // need. Each page is 4096 bytes. + let program_pages = (bytes_read / PAGE_SIZE) + 1; + let my_pid = unsafe { NEXT_PID + 1 }; + let elf_hdr; + unsafe { + NEXT_PID += 1; + // Load the ELF + elf_hdr = (buffer.get() as *const elf::Header).as_ref().unwrap(); + } + if elf_hdr.magic != elf::MAGIC { + println!("ELF magic didn't match."); + return; + } + if elf_hdr.machine != elf::MACHINE_RISCV { + println!("ELF loaded is not RISC-V."); + return; + } + if elf_hdr.obj_type != elf::TYPE_EXEC { + println!("ELF is not an executable."); + return; + } + let mut my_proc = + Process { frame: zalloc(1) as *mut TrapFrame, + stack: zalloc(STACK_PAGES), + pid: my_pid, + root: zalloc(1) as *mut Table, + state: ProcessState::Running, + data: ProcessData::zero(), + sleep_until: 0, + program: zalloc(program_pages), }; + // Map the program in the MMU. + let ptr = my_proc.program; + let table = unsafe { my_proc.root.as_mut().unwrap() }; + unsafe { + let ph_tab = buffer.get().add(elf_hdr.phoff) as *const elf::ProgramHeader; + for i in 0..elf_hdr.phnum as usize { + let ph = ph_tab.add(i).as_ref().unwrap(); + if ph.seg_type != elf::PH_SEG_TYPE_LOAD { + continue; + } + if ph.memsz == 0 { + continue; + } + memcpy(ptr.add(ph.off), buffer.get().add(ph.off), ph.memsz); + let pages = ph.memsz / PAGE_SIZE + 1; + for i in 0..pages { + let vaddr = ph.vaddr + i * PAGE_SIZE; + let paddr = ptr as usize + i * PAGE_SIZE; + map( + table, + vaddr, + paddr, + EntryBits::UserReadExecute.val(), + 0, + ); + } + } + } + // This will map all of the program pages. Notice that in linker.lds in userspace + // we set the entry point address to 0x2000_0000. This is the same address as + // PROCESS_STARTING_ADDR, and they must match. + // Map the stack + let ptr = my_proc.stack as *mut u8; + for i in 0..STACK_PAGES { + let vaddr = STACK_ADDR + i * PAGE_SIZE; + let paddr = ptr as usize + i * PAGE_SIZE; + // We create the stack. We don't load a stack from the disk. This is why I don't + // need to make the stack executable. + map( + table, + vaddr, + paddr, + EntryBits::UserReadWrite.val(), + 0, + ); + } + // Set everything up in the trap frame + unsafe { + // The program counter is a virtual memory address and is loaded into mepc + // when we execute mret. + (*my_proc.frame).pc = elf_hdr.entry_addr; + // Stack pointer. The stack starts at the bottom and works its way up, so we have to + // set the stack pointer to the bottom. + (*my_proc.frame).regs[2] = + STACK_ADDR as usize + STACK_PAGES * PAGE_SIZE; + // USER MODE! This is how we set what'll go into mstatus when we run the process. + (*my_proc.frame).mode = CpuMode::User as usize; + (*my_proc.frame).pid = my_proc.pid as usize; + // The SATP register is used for the MMU, so we need to + // map our table into that register. The switch_to_user + // function will load .satp into the actual register + // when the time comes. + (*my_proc.frame).satp = + build_satp( + SatpMode::Sv39, + my_proc.pid as usize, + my_proc.root as usize, + ); + } + // The ASID field of the SATP register is only 16-bits, and we reserved + // 0 for the kernel, even though we run the kernel in machine mode for now. + // Since we don't reuse PIDs, this means that we can only spawn 65534 processes. + satp_fence_asid(my_pid as usize); + // I took a different tact here than in process.rs. In there I created the process + // while holding onto the process list. It doesn't really matter since this is synchronous, + // but it might get dicey + if let Some(mut pl) = unsafe { PROCESS_LIST.take() } { + // As soon as we push this process on the list, it'll be schedule-able. + println!( + "Added user process to the scheduler...get \ + ready for take-off!" + ); + pl.push_back(my_proc); + unsafe { + PROCESS_LIST.replace(pl); + } + } + else { + println!("Unable to spawn process."); + // Since my_proc couldn't enter the process list, it + // will be dropped and all of the associated allocations + // will be deallocated. + } + println!(); +} + diff --git a/risc_v/src/userspace/Makefile b/risc_v/src/userspace/Makefile index abcaf71..a3338a0 100644 --- a/risc_v/src/userspace/Makefile +++ b/risc_v/src/userspace/Makefile @@ -12,8 +12,8 @@ PROGS=$(patsubst %.cpp,%,$(SOURCES)) all: $(OUT) -%.elf:%.cpp Makefile - $(CROSS)$(CXX) $(CXXFLAGS) $(LINKER_SCRIPT) $(INCLUDES) $(LIBS) -o $@ $< $(LIB) +%.elf:%.cpp Makefile startlib/linker.lds startlib/*.h + $(CROSS)$(CXX) $(CXXFLAGS) $(INCLUDES) $(LIBS) -o $@ $< $(LIB) $(CROSS)$(OBJCOPY) -O binary $@ $@.bin mv $@.bin $(basename $@) diff --git a/risc_v/src/userspace/startlib/linker.lds b/risc_v/src/userspace/startlib/linker.lds index 85333ca..7040bcd 100644 --- a/risc_v/src/userspace/startlib/linker.lds +++ b/risc_v/src/userspace/startlib/linker.lds @@ -10,6 +10,7 @@ MEMORY PHDRS { text PT_LOAD; + rodata PT_LOAD; data PT_LOAD; bss PT_LOAD; } @@ -26,10 +27,9 @@ SECTIONS PROVIDE(_rodata_start = .); *(.rodata .rodata.*) PROVIDE(_rodata_end = .); - } >ram AT>ram :text + } >ram AT>ram :rodata .data : { - . = ALIGN(4096); PROVIDE(_data_start = .); *(.sdata .sdata.*) *(.data .data.*) PROVIDE(_data_end = .);