1
0
mirror of https://github.com/sgmarz/osblog.git synced 2024-11-23 18:06:20 +04:00

Read and start a process...Added userspace library...will switch this to newlib later.

This commit is contained in:
Stephen Marz 2020-04-25 21:23:00 -04:00
parent cbff6afeca
commit 1627cc7733
18 changed files with 1453 additions and 31 deletions

View File

@ -8,4 +8,5 @@
// import a full assembly file, which is what I want here.
global_asm!(include_str!("asm/boot.S"));
global_asm!(include_str!("asm/mem.S"));
global_asm!(include_str!("asm/trap.S"));
global_asm!(include_str!("asm/trap.S"));

View File

@ -242,19 +242,21 @@ pub fn get_mtime() -> usize {
/// Copy one data from one memory location to another.
pub unsafe fn memcpy(dest: *mut u8, src: *const u8, bytes: usize) {
let bytes_as_8 = bytes / 8;
let bytes_as_1 = bytes % 8;
let dest_as_8 = dest as *mut u64;
let src_as_8 = src as *const u64;
for i in 0..bytes_as_8 {
*(dest_as_8.add(i)) = *(src_as_8.add(i));
}
let bytes_remaining = bytes_as_8 * 8;
for i in bytes_remaining..bytes_remaining + bytes_as_1 {
let bytes_completed = bytes_as_8 * 8;
let bytes_remaining = bytes - bytes_completed;
for i in bytes_completed..bytes_remaining {
*(dest.add(i)) = *(src.add(i));
}
}
/// Dumps the registers of a given trap frame. This is NOT the
/// current CPU registers!
pub fn dump_registers(frame: *const TrapFrame) {
print!(" ");
for i in 1..32 {
@ -262,7 +264,7 @@ pub fn dump_registers(frame: *const TrapFrame) {
println!();
print!(" ");
}
print!("{:2}:{:08x} ", i, unsafe { (*frame).regs[i] });
print!("x{:2}:{:08x} ", i, unsafe { (*frame).regs[i] });
}
println!();
}

View File

@ -23,13 +23,13 @@ use core::ptr::null_mut;
// How many pages are we going to give a process for their
// stack?
const STACK_PAGES: usize = 5;
pub const STACK_PAGES: usize = 5;
// We want to adjust the stack to be at the bottom of the memory allocation
// regardless of where it is on the kernel heap.
const STACK_ADDR: usize = 0x1_0000_0000;
pub const STACK_ADDR: usize = 0x1_0000_0000;
// All processes will have a defined starting point in virtual memory.
// We will use this later when we load processes from disk.
// const PROCESS_STARTING_ADDR: usize = 0x2000_0000;
pub const PROCESS_STARTING_ADDR: usize = 0x2000_0000;
// Here, we store a process list. It uses the global allocator
// that we made before and its job is to store all processes.
@ -43,7 +43,7 @@ const STACK_ADDR: usize = 0x1_0000_0000;
pub static mut PROCESS_LIST: Option<VecDeque<Process>> = None;
// We can search through the process list to get a new PID, but
// it's probably easier and faster just to increase the pid:
static mut NEXT_PID: u16 = 1;
pub static mut NEXT_PID: u16 = 1;
// The following set_* and get_by_pid functions are C-style functions
// They probably need to be re-written in a more Rusty style, but for
@ -170,12 +170,13 @@ pub unsafe fn get_by_pid(pid: u16) -> *mut Process {
fn init_process() {
// We can't do much here until we have system calls because
// we're running in User space.
println!("Init process started...");
loop {
// Alright, I forgot. We cannot put init to sleep since the
// scheduler will loop until it finds a process to run. Since
// the scheduler is called in an interrupt context, nothing else
// can happen until a process becomes available.
println!("Init is still here :), alright, back to sleep.");
// println!("Init is still here :), alright, back to sleep.");
// 500 wfi's should take 500 context switches before we print
// Init is still here. Depending on our context switch time,
// this might be around 3 seconds.
@ -246,7 +247,9 @@ pub fn add_kernel_process(func: fn()) -> u16 {
root: zalloc(1) as *mut Table,
state: ProcessState::Running,
data: ProcessData::zero(),
sleep_until: 0, };
sleep_until: 0,
program: null_mut()
};
unsafe {
NEXT_PID += 1;
}
@ -325,7 +328,9 @@ pub fn add_kernel_process_args(func: fn(args_ptr: usize), args: usize) -> u16 {
root: zalloc(1) as *mut Table,
state: ProcessState::Running,
data: ProcessData::zero(),
sleep_until: 0, };
sleep_until: 0,
program: null_mut(),
};
unsafe {
NEXT_PID += 1;
}
@ -406,13 +411,14 @@ pub enum ProcessState {
}
pub struct Process {
frame: *mut TrapFrame,
stack: *mut u8,
pid: u16,
root: *mut Table,
state: ProcessState,
data: ProcessData,
sleep_until: usize,
pub frame: *mut TrapFrame,
pub stack: *mut u8,
pub pid: u16,
pub root: *mut Table,
pub state: ProcessState,
pub data: ProcessData,
pub sleep_until: usize,
pub program: *mut u8,
}
// Most of this operating system runs more of a C-style, where
@ -423,6 +429,7 @@ pub struct Process {
// the first parameter (the *this parameter in C++) is a reference
// to ourself. We can write static functions as a member of this
// structure by omitting a self.
// 25-Apr-2020: (SM) Alright, I made everything public......
impl Process {
pub fn get_frame_address(&self) -> usize {
self.frame as usize
@ -440,6 +447,10 @@ impl Process {
unsafe { (*self.frame).pc }
}
pub fn get_program_address_mut(&mut self) -> *mut u8 {
self.program
}
pub fn get_table_address(&self) -> usize {
self.root as usize
}
@ -478,7 +489,9 @@ impl Process {
root: zalloc(1) as *mut Table,
state: ProcessState::Running,
data: ProcessData::zero(),
sleep_until: 0, };
sleep_until: 0,
program: null_mut()
};
unsafe {
satp_fence_asid(NEXT_PID as usize);
NEXT_PID += 1;
@ -557,6 +570,9 @@ impl Drop for Process {
}
dealloc(self.root as *mut u8);
dealloc(self.frame as *mut u8);
if !self.program.is_null() {
dealloc(self.program);
}
}
}

View File

@ -27,11 +27,16 @@ pub unsafe fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize {
delete_process((*frame).pid as u16);
0
},
1 => {
2 => {
// Easy putchar
print!("{}", (*frame).regs[10] as u8 as char);
mepc + 4
},
8 => {
dump_registers(frame);
mepc + 4
},
2 => {
10 => {
// Sleep
set_sleeping((*frame).pid as u16, (*frame).regs[10]);
0

View File

@ -1,22 +1,93 @@
// test.rs
use crate::{kmem::{kfree, kmalloc},
syscall::syscall_fs_read};
process::{Process,
NEXT_PID,
PROCESS_LIST,
PROCESS_STARTING_ADDR,
STACK_ADDR,
STACK_PAGES, ProcessState, ProcessData},
syscall::syscall_fs_read};
use crate::page::{zalloc, Table, map, EntryBits};
use crate::cpu::{memcpy, TrapFrame, CpuMode, satp_fence_asid, SatpMode, build_satp};
pub fn test_block() {
// Let's test the block driver!
let bytes_to_read = 1024 * 50;
let buffer = kmalloc(bytes_to_read);
unsafe {
let bytes_read =
syscall_fs_read(8, 5, buffer, bytes_to_read as u32, 0);
println!("FS Read returned {} bytes", bytes_read);
for i in 0..16 * 4 {
print!("{:02x} ", buffer.add(i).read());
if (i + 1) % 16 == 0 {
println!();
let bytes_read = syscall_fs_read(8, 8, buffer, bytes_to_read as u32, 0);
if bytes_read != 12288 {
println!(
"Unable to load program at inode 8, which should be \
12,288 bytes, got {}",
bytes_read
);
}
else {
// Let's get this program running!
let program_pages = (bytes_read / 4096) + 1;
let my_pid = unsafe { NEXT_PID + 1 };
unsafe {
NEXT_PID += 1;
}
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;
unsafe {
memcpy(ptr, buffer, bytes_read);
}
let table = unsafe { my_proc.root.as_mut().unwrap() };
for i in 0..program_pages {
let vaddr = PROCESS_STARTING_ADDR + (i << 12);
let paddr = ptr as usize + (i << 12);
map(table, vaddr, paddr, EntryBits::UserReadWriteExecute.val(), 0);
}
// Map the stack
let ptr = my_proc.stack as *mut u8;
for i in 0..STACK_PAGES {
let vaddr = STACK_ADDR + (i << 12);
let paddr = ptr as usize + (i << 12);
map(table, vaddr, paddr, EntryBits::UserReadWrite.val(), 0);
}
// Set everything up in the trap frame
unsafe {
(*my_proc.frame).pc = PROCESS_STARTING_ADDR;
// Stack pointer
(*my_proc.frame).regs[2] =
STACK_ADDR as usize + STACK_PAGES * 4096;
(*my_proc.frame).mode = CpuMode::User as usize;
(*my_proc.frame).pid = my_proc.pid as usize;
}
unsafe {
(*my_proc.frame).satp =
build_satp(
SatpMode::Sv39,
my_proc.pid as usize,
my_proc.root as usize,
);
}
if let Some(mut pl) = unsafe { PROCESS_LIST.take() } {
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!();
kfree(buffer);

6
risc_v/src/userspace/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
helloworld
helloworld.elf
sleepy
sleepy.elf
shell
shell.elf

View File

@ -0,0 +1,23 @@
CROSS=riscv64-unknown-elf-
CXX=g++
OBJCOPY=objcopy
CXXFLAGS=-Wall -O0 -ffreestanding -nostartfiles -nostdlib -static -march=rv64g -mabi=lp64d
LINKER_SCRIPT=-T./startlib/linker.lds
INCLUDES=-I./startlib
LIBS=-L./startlib
LIB=-lstart
SOURCES=$(wildcard *.cpp)
OUT=$(patsubst %.cpp,%.elf,$(SOURCES))
PROGS=$(patsubst %.cpp,%,$(SOURCES))
all: $(OUT)
%.elf:%.cpp Makefile
$(CROSS)$(CXX) $(CXXFLAGS) $(LINKER_SCRIPT) $(INCLUDES) $(LIBS) -o $@ $< $(LIB)
$(CROSS)$(OBJCOPY) -O binary $@ $@.bin
mv $@.bin $(basename $@)
clean:
rm -f $(OUT)
rm -f *.bin *.elf
rm -f $(PROGS)

View File

@ -0,0 +1,8 @@
#include <printf.h>
#include <syscall.h>
int main()
{
printf("Hello World\n");
return 0;
}

View File

@ -0,0 +1,10 @@
#include <printf.h>
#include <syscall.h>
int main()
{
unsigned long a;
asm volatile("mv %0, sp\n" : "=r"(a));
printf("Stack is at %p\n", a);
return 0;
}

View File

@ -0,0 +1,11 @@
#include <printf.h>
#include <syscall.h>
int main()
{
printf("I'm going to bed.\nYou can watch me sleep for 100 switches using 'top'\n");
for (int i = 0;i < 100;i++) {
syscall_sleep(1000000);
}
return 0;
}

View File

@ -0,0 +1,2 @@
*.a
*.o

View File

@ -0,0 +1,25 @@
CROSS=riscv64-unknown-elf-
CXX=g++
OBJCOPY=objcopy
AR=ar
CXXFLAGS=-Wall -O0 -ffreestanding -nostartfiles -nostdlib -I. -march=rv64g -mabi=lp64d
OUT=libstart.a
SOURCES_S=$(wildcard *.S)
SOURCES_CPP=$(wildcard *.cpp)
OBJS=$(patsubst %.S,%.o,$(SOURCES_S)) $(patsubst %.cpp,%.o,$(SOURCES_CPP))
all: $(OUT)
$(OUT): $(OBJS) Makefile
rm -f $(OUT)
$(AR) rcv $(OUT) $(OBJS)
%.o: %.S
$(CROSS)$(CXX) $(CXXFLAGS) -c $< -o $@
%.o: %.cpp
$(CROSS)$(CXX) $(CXXFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -f $(OUT) $(OBJS)

View File

@ -0,0 +1,49 @@
OUTPUT_ARCH( "riscv" )
ENTRY( _start )
MEMORY
{
ram (wxa!ri) : ORIGIN = 0x20000000, LENGTH = 128M
}
PHDRS
{
text PT_LOAD;
data PT_LOAD;
bss PT_LOAD;
}
SECTIONS
{
.text : {
PROVIDE(_text_start = .);
*(.text.init) *(.text .text.*)
PROVIDE(_text_end = .);
} >ram AT>ram :text
PROVIDE(_global_pointer = .);
.rodata : {
PROVIDE(_rodata_start = .);
*(.rodata .rodata.*)
PROVIDE(_rodata_end = .);
} >ram AT>ram :text
.data : {
. = ALIGN(4096);
PROVIDE(_data_start = .);
*(.sdata .sdata.*) *(.data .data.*)
PROVIDE(_data_end = .);
} >ram AT>ram :data
.bss :{
PROVIDE(_bss_start = .);
*(.sbss .sbss.*) *(.bss .bss.*)
PROVIDE(_bss_end = .);
} >ram AT>ram :bss
PROVIDE(_memory_start = ORIGIN(ram));
PROVIDE(_stack = _bss_end + 0x80000);
PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
PROVIDE(_heap_start = _stack);
PROVIDE(_heap_size = _memory_end - _stack);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,113 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
// embedded systems with a very limited resources.
// Use this instead of bloated standard/newlib printf.
// These routines are thread safe and reentrant.
//
///////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef _PRINTF_H_
#define _PRINTF_H_
#include <stdarg.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Output a character to a custom device like UART, used by the printf() function
* This function is declared here only. You have to write your custom implementation somewhere
* \param character Character to output
*/
void _putchar(char character);
/**
* Tiny printf implementation
* You have to implement _putchar if you use printf()
* To avoid conflicts with the regular printf() API it is overridden by macro defines
* and internal underscore-appended functions like printf_() are used
* \param format A string that specifies the format of the output
* \return The number of characters that are written into the array, not counting the terminating null character
*/
//int cosc361_printf(const char* format, ...);
int printf(const char *format, ...);
/**
* Tiny sprintf implementation
* Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
* \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
* \param format A string that specifies the format of the output
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
*/
int sprintf(char* buffer, const char* format, ...);
/**
* Tiny snprintf/vsnprintf implementation
* \param buffer A pointer to the buffer where to store the formatted string
* \param count The maximum number of characters to store in the buffer, including a terminating null character
* \param format A string that specifies the format of the output
* \param va A value identifying a variable arguments list
* \return The number of characters that COULD have been written into the buffer, not counting the terminating
* null character. A value equal or larger than count indicates truncation. Only when the returned value
* is non-negative and less than count, the string has been completely written.
*/
int snprintf(char* buffer, size_t count, const char* format, ...);
int vsnprintf(char* buffer, size_t count, const char* format, va_list va);
/**
* Tiny vprintf implementation
* \param format A string that specifies the format of the output
* \param va A value identifying a variable arguments list
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
*/
#define vprintf vprintf_
int vprintf_(const char* format, va_list va);
/**
* printf with output function
* You may use this as dynamic alternative to printf() with its fixed _putchar() output
* \param out An output function which takes one character and an argument pointer
* \param arg An argument pointer for user data passed to output function
* \param format A string that specifies the format of the output
* \return The number of characters that are sent to the output function, not counting the terminating null character
*/
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
#ifdef __cplusplus
}
#endif
#endif // _PRINTF_H_

View File

@ -0,0 +1,10 @@
.section .text.init
.global _start
_start:
call main
# Exit system call after main
li a0, 93
j make_syscall
.type _start, function
.size _start, .-_start

View File

@ -0,0 +1,14 @@
.section .text
.global make_syscall
make_syscall:
mv a7, a0
mv a0, a1
mv a1, a2
mv a2, a3
mv a3, a4
mv a4, a5
mv a5, a6
ecall
ret
.type make_syscall, function
.size make_syscall, .-make_syscall

View File

@ -0,0 +1,14 @@
#pragma once
extern "C"
{
unsigned long make_syscall(unsigned long sysno,
unsigned long a1=0,
unsigned long a2=0,
unsigned long a3=0);
}
#define syscall_exit() make_syscall(93)
#define syscall_get_char() make_syscall(1)
#define syscall_put_char(x) make_syscall(2, (unsigned long)x)
#define syscall_yield() make_syscall(9)
#define syscall_sleep(x) make_syscall(10, (unsigned long)x)