1
0
mirror of https://github.com/sgmarz/osblog.git synced 2024-11-24 02:16:19 +04:00

Now we read ELF files instead of raw binaries

This commit is contained in:
Stephen Marz 2020-04-26 08:33:49 -04:00
parent de97ebf12d
commit 074dc9547a
3 changed files with 163 additions and 5 deletions

View File

@ -17,6 +17,8 @@ use crate::{cpu::{build_satp,
STACK_ADDR,
STACK_PAGES},
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!();
}

View File

@ -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 $@)

View File

@ -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 = .);