Compare commits
13 Commits
10a38cf572
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 090e97e3e1 | |||
| b9b8d1147c | |||
| ff788bb819 | |||
| 172da6cf07 | |||
| 94636f8c09 | |||
| 889a16d168 | |||
| 5cd7aa3cab | |||
| 968b26fd93 | |||
| 2abfbec7e6 | |||
| 5a490b03f9 | |||
| c4596dcd16 | |||
| 3496194425 | |||
| 0aa03bf92a |
@@ -1,3 +1,5 @@
|
||||
# rivos
|
||||
|
||||
This toy os is made following the [OS in 1,000 lines](https://1000os.seiya.me/en/) project.
|
||||
A small educational operating system targeting the RISC-V architecture.
|
||||
|
||||
Focused on simplicity, low-level exploration, and learning by building.
|
||||
|
||||
5
format.sh
Executable file
5
format.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -xue
|
||||
|
||||
find . -type f -name '*.c' -exec clang-format -i {} \;
|
||||
find . -type f -name '*.h' -exec clang-format -i {} \;
|
||||
13
include/io.h
Normal file
13
include/io.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef RIVOS_IO_H
|
||||
#define RIVOS_IO_H
|
||||
|
||||
#include "../include/types.h"
|
||||
|
||||
#define va_list __builtin_va_list
|
||||
#define va_start __builtin_va_start
|
||||
#define va_end __builtin_va_end
|
||||
#define va_arg __builtin_va_arg
|
||||
|
||||
void kprintf(const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
11
include/kernel.h
Normal file
11
include/kernel.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef RIVOS_KERNEL_H
|
||||
#define RIVOS_KERNEL_H
|
||||
|
||||
typedef struct sbiret {
|
||||
long error;
|
||||
long value;
|
||||
} sbiret_s;
|
||||
|
||||
void kputchar(char ch);
|
||||
|
||||
#endif
|
||||
10
include/memory.h
Normal file
10
include/memory.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef RIVOS_MEMORY_H
|
||||
#define RIVOS_MEMORY_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void *kmemset(void *buf, char c, size_t n);
|
||||
|
||||
void *kmemcpy(void *dst, const void *src, size_t n);
|
||||
|
||||
#endif
|
||||
15
include/string.h
Normal file
15
include/string.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef RIVOS_STRING_H
|
||||
#define RIVOS_STRING_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
* This is not a safe function, check dst size
|
||||
*/
|
||||
char *kstrcpy(char *dst, const char *src);
|
||||
|
||||
size_t kstrlen(const char *str);
|
||||
|
||||
int kstrcmp(const char *s1, const char *s2);
|
||||
|
||||
#endif
|
||||
17
include/types.h
Normal file
17
include/types.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef RIVOS_TYPES_H
|
||||
#define RIVOS_TYPES_H
|
||||
|
||||
/**
|
||||
* Define our standard types
|
||||
*/
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef u32 size_t;
|
||||
|
||||
#define true 1
|
||||
#define false 0
|
||||
#define NULL ((void *)0)
|
||||
|
||||
#endif
|
||||
28
kernel.ld
28
kernel.ld
@@ -1,29 +1,35 @@
|
||||
/*
|
||||
/**
|
||||
* This is the entry point
|
||||
* `boot` should be a defined label
|
||||
*/
|
||||
ENTRY(boot)
|
||||
|
||||
/* SECTIONS tells to linker how to arrange sections inside the memory */
|
||||
/**
|
||||
* SECTIONS tells to linker how to arrange sections inside the memory
|
||||
*/
|
||||
SECTIONS {
|
||||
/*
|
||||
/**
|
||||
* This is the base address (start address of the kernel)
|
||||
* OpenSBI takes little ram
|
||||
*/
|
||||
. = 0x80200000;
|
||||
|
||||
/* This section contains the code of the program */
|
||||
/**
|
||||
* This section contains the code of the program
|
||||
*/
|
||||
.text :{
|
||||
/*
|
||||
/**
|
||||
* Take the .text.boot before everything
|
||||
* KEEP() prevents the linker to mark it as "unused"
|
||||
*/
|
||||
KEEP(*(.text.boot));
|
||||
/* Include everything else inside the .text section */
|
||||
/**
|
||||
* Include everything else inside the .text section
|
||||
*/
|
||||
*(.text .text.*);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Contains constant read-only data
|
||||
* Align the start of the section at 4 byte
|
||||
*/
|
||||
@@ -31,12 +37,16 @@ SECTIONS {
|
||||
*(.rodata .rodata.*);
|
||||
}
|
||||
|
||||
/* Contains read/write data */
|
||||
/**
|
||||
* Contains read/write data
|
||||
*/
|
||||
.data : ALIGN(4) {
|
||||
*(.data .data.*);
|
||||
}
|
||||
|
||||
/* Contains read/data with an initial value of zero */
|
||||
/**
|
||||
* Contains read/data with an initial value of zero
|
||||
*/
|
||||
.bss : ALIGN(4) {
|
||||
__bss = .;
|
||||
*(.bss .bss.* .sbss .sbss.*);
|
||||
|
||||
5
run.sh
5
run.sh
@@ -17,7 +17,10 @@ CC=clang
|
||||
CFLAGS="-std=c11 -O2 -g3 -Wall -Wextra --target=riscv32-unknown-elf -fuse-ld=lld -fno-stack-protector -ffreestanding -nostdlib"
|
||||
|
||||
$CC $CFLAGS -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf \
|
||||
src/kernel.c
|
||||
src/kernel.c \
|
||||
src/io.c \
|
||||
src/memory.c \
|
||||
src/string.c
|
||||
|
||||
# Run QEMU
|
||||
# -machine virt: generic riscv platform
|
||||
|
||||
87
src/io.c
Normal file
87
src/io.c
Normal file
@@ -0,0 +1,87 @@
|
||||
#include "../include/io.h"
|
||||
#include "../include/string.h"
|
||||
|
||||
void kputchar(char ch);
|
||||
|
||||
void kprintf(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
while (*fmt) {
|
||||
if (*fmt != '%') {
|
||||
kputchar(*fmt);
|
||||
fmt++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip '%' */
|
||||
fmt++;
|
||||
switch (*fmt) {
|
||||
case '\0': {
|
||||
/* Simply print the char */
|
||||
kputchar('%');
|
||||
|
||||
goto end;
|
||||
}
|
||||
case '%': {
|
||||
/* Print '%' */
|
||||
kputchar('%');
|
||||
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
/* Print a NULL-terminated string */
|
||||
const char *s = va_arg(args, const char *);
|
||||
while (*s) {
|
||||
kputchar(*s);
|
||||
s++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
/* Print an integer in decimal */
|
||||
int value = va_arg(args, int);
|
||||
unsigned magnitude = value;
|
||||
if (value < 0) {
|
||||
kputchar('-');
|
||||
magnitude = -magnitude;
|
||||
}
|
||||
|
||||
unsigned divisor = 1;
|
||||
while (magnitude / divisor > 9) {
|
||||
divisor *= 10;
|
||||
}
|
||||
|
||||
while (divisor > 0) {
|
||||
kputchar('0' + magnitude / divisor);
|
||||
magnitude %= divisor;
|
||||
divisor /= 10;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
/* Print an integer in hexadecimal */
|
||||
unsigned value = va_arg(args, unsigned);
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
unsigned nibble = (value >> (i * 4)) & 0xF;
|
||||
/* From a char array we get the nibble position */
|
||||
kputchar("0123456789ABCDEF"[nibble]);
|
||||
}
|
||||
}
|
||||
default: {
|
||||
const char *str = "NOT IMPLEMENTED";
|
||||
size_t len = kstrlen(str);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
kputchar(str[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Next char */
|
||||
fmt++;
|
||||
}
|
||||
end:
|
||||
va_end(args);
|
||||
}
|
||||
63
src/kernel.c
63
src/kernel.c
@@ -1,26 +1,63 @@
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned int u32;
|
||||
typedef u32 size_t;
|
||||
#include "../include/kernel.h"
|
||||
#include "../include/io.h"
|
||||
#include "../include/memory.h"
|
||||
|
||||
extern char __bss[], __bss_end[], __stack_top[];
|
||||
|
||||
void *memset(void *buf, char c, size_t n) {
|
||||
u8 *p = (u8 *)buf;
|
||||
while (n--) {
|
||||
*p++ = c;
|
||||
}
|
||||
/**
|
||||
* This function will call OpenSBI
|
||||
* ecall is used as the control transfer instruction,
|
||||
* it switches from kernel mode (s-mode) to opensbi mode (m-mode)
|
||||
* a7 encodes the SBI extension ID (EID)
|
||||
* a6 encodes the SBI function ID (FID)
|
||||
*
|
||||
* a0 returns and error code
|
||||
*/
|
||||
sbiret_s sbi_call(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long fid, long eid) {
|
||||
register long a0 __asm__("a0") = arg0;
|
||||
register long a1 __asm__("a1") = arg1;
|
||||
register long a2 __asm__("a2") = arg2;
|
||||
register long a3 __asm__("a3") = arg3;
|
||||
register long a4 __asm__("a4") = arg4;
|
||||
register long a5 __asm__("a5") = arg5;
|
||||
register long a6 __asm__("a6") = fid;
|
||||
register long a7 __asm__("a7") = eid;
|
||||
|
||||
return buf;
|
||||
__asm__ __volatile__("ecall"
|
||||
: "=r"(a0), "=r"(a1)
|
||||
: "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(a6), "r"(a7)
|
||||
: "memory");
|
||||
|
||||
return (sbiret_s){.error = a0, .value = a1};
|
||||
}
|
||||
|
||||
void kputchar(char ch) {
|
||||
sbi_call(ch, 0, 0, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
/* Kernel main */
|
||||
void kernel_main(void) {
|
||||
memset(__bss, 0, __bss_end - __bss);
|
||||
/* Usually it is not required */
|
||||
kmemset(__bss, 0, __bss_end - __bss);
|
||||
|
||||
for (;;)
|
||||
;
|
||||
/* Start here */
|
||||
kprintf("%s, %s\n", "Hello", "world!");
|
||||
kprintf("3 + 4 = %d\n", 3 + 4);
|
||||
|
||||
for (;;) {
|
||||
__asm__ __volatile__("wfi");
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((section(".text.boot"))) __attribute__((naked)) void boot(void) {
|
||||
/* This is the entry section (boot) */
|
||||
__attribute__((section(".text.boot")))
|
||||
/* Do not add other stuff like return */
|
||||
__attribute__((naked)) void
|
||||
boot(void) {
|
||||
/**
|
||||
* Put stack_top inside sp (stack pointer)
|
||||
* and jump to kernel_main
|
||||
*/
|
||||
__asm__ __volatile__("mv sp, %[stack_top]\n"
|
||||
"j kernel_main\n"
|
||||
:
|
||||
|
||||
21
src/memory.c
Normal file
21
src/memory.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "../include/memory.h"
|
||||
|
||||
void *kmemset(void *buf, char c, size_t n) {
|
||||
u8 *p = (u8 *)buf;
|
||||
while (n--) {
|
||||
*p++ = c;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void *kmemcpy(void *dst, const void *src, size_t n) {
|
||||
u8 *dest = (u8 *)dst;
|
||||
const u8 *source = (u8 *)src;
|
||||
|
||||
while (n--) {
|
||||
*dest++ = *source++;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
29
src/string.c
Normal file
29
src/string.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "../include/string.h"
|
||||
|
||||
char *kstrcpy(char *dst, const char *src) {
|
||||
while (*src) {
|
||||
*dst = *src;
|
||||
src++;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
size_t kstrlen(const char *str) {
|
||||
size_t len = 0;
|
||||
|
||||
while (*str) {
|
||||
len++;
|
||||
str++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int kstrcmp(const char *s1, const char *s2) {
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user