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:
parent
cbff6afeca
commit
1627cc7733
@ -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"));
|
||||
|
||||
|
@ -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!();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
6
risc_v/src/userspace/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
helloworld
|
||||
helloworld.elf
|
||||
sleepy
|
||||
sleepy.elf
|
||||
shell
|
||||
shell.elf
|
23
risc_v/src/userspace/Makefile
Normal file
23
risc_v/src/userspace/Makefile
Normal 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)
|
8
risc_v/src/userspace/helloworld.cpp
Normal file
8
risc_v/src/userspace/helloworld.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
#include <printf.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Hello World\n");
|
||||
return 0;
|
||||
}
|
10
risc_v/src/userspace/shell.cpp
Normal file
10
risc_v/src/userspace/shell.cpp
Normal 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;
|
||||
}
|
11
risc_v/src/userspace/sleepy.cpp
Normal file
11
risc_v/src/userspace/sleepy.cpp
Normal 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;
|
||||
}
|
2
risc_v/src/userspace/startlib/.gitignore
vendored
Normal file
2
risc_v/src/userspace/startlib/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.a
|
||||
*.o
|
25
risc_v/src/userspace/startlib/Makefile
Normal file
25
risc_v/src/userspace/startlib/Makefile
Normal 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)
|
49
risc_v/src/userspace/startlib/linker.lds
Normal file
49
risc_v/src/userspace/startlib/linker.lds
Normal 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);
|
||||
}
|
1042
risc_v/src/userspace/startlib/printf.cpp
Normal file
1042
risc_v/src/userspace/startlib/printf.cpp
Normal file
File diff suppressed because it is too large
Load Diff
113
risc_v/src/userspace/startlib/printf.h
Normal file
113
risc_v/src/userspace/startlib/printf.h
Normal 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_
|
10
risc_v/src/userspace/startlib/start.S
Normal file
10
risc_v/src/userspace/startlib/start.S
Normal 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
|
14
risc_v/src/userspace/startlib/syscall.S
Normal file
14
risc_v/src/userspace/startlib/syscall.S
Normal 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
|
14
risc_v/src/userspace/startlib/syscall.h
Normal file
14
risc_v/src/userspace/startlib/syscall.h
Normal 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)
|
Loading…
Reference in New Issue
Block a user