Compare commits

...

4 Commits

Author SHA1 Message Date
4cc12e6e26 feat(io): implement printf and strlen 2026-01-26 01:40:11 +01:00
ef9899bac1 feat(format.sh): format script 2026-01-26 01:39:31 +01:00
b3db8e7e59 feat(types): move types to header file 2026-01-26 01:39:16 +01:00
6db84bf318 refactor(kernel.ld): adjust comments 2026-01-26 01:38:41 +01:00
8 changed files with 214 additions and 18 deletions

5
format.sh Executable file
View 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 {} \;

14
include/io.h Normal file
View File

@@ -0,0 +1,14 @@
#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 r_printf(const char *fmt, ...);
size_t r_strlen(const char *str);
#endif

9
include/kernel.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef RIVOS_KERNEL_H
#define RIVOS_KERNEL_H
typedef struct sbiret_s {
long error;
long value;
} sbiret_s;
#endif

11
include/types.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef RIVOS_TYPES_H
#define RIVOS_TYPES_H
/**
* Define our standard types
*/
typedef unsigned char u8;
typedef unsigned int u32;
typedef u32 size_t;
#endif

View File

@@ -1,29 +1,35 @@
/* /**
* This is the entry point * This is the entry point
* `boot` should be a defined label * `boot` should be a defined label
*/ */
ENTRY(boot) 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 { SECTIONS {
/* /**
* This is the base address (start address of the kernel) * This is the base address (start address of the kernel)
* OpenSBI takes little ram * OpenSBI takes little ram
*/ */
. = 0x80200000; . = 0x80200000;
/* This section contains the code of the program */ /**
* This section contains the code of the program
*/
.text :{ .text :{
/* /**
* Take the .text.boot before everything * Take the .text.boot before everything
* KEEP() prevents the linker to mark it as "unused" * KEEP() prevents the linker to mark it as "unused"
*/ */
KEEP(*(.text.boot)); KEEP(*(.text.boot));
/* Include everything else inside the .text section */ /**
* Include everything else inside the .text section
*/
*(.text .text.*); *(.text .text.*);
} }
/* /**
* Contains constant read-only data * Contains constant read-only data
* Align the start of the section at 4 byte * Align the start of the section at 4 byte
*/ */
@@ -31,12 +37,16 @@ SECTIONS {
*(.rodata .rodata.*); *(.rodata .rodata.*);
} }
/* Contains read/write data */ /**
* Contains read/write data
*/
.data : ALIGN(4) { .data : ALIGN(4) {
*(.data .data.*); *(.data .data.*);
} }
/* Contains read/data with an initial value of zero */ /**
* Contains read/data with an initial value of zero
*/
.bss : ALIGN(4) { .bss : ALIGN(4) {
__bss = .; __bss = .;
*(.bss .bss.* .sbss .sbss.*); *(.bss .bss.* .sbss .sbss.*);

3
run.sh
View File

@@ -17,7 +17,8 @@ CC=clang
CFLAGS="-std=c11 -O2 -g3 -Wall -Wextra --target=riscv32-unknown-elf -fuse-ld=lld -fno-stack-protector -ffreestanding -nostdlib" 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 \ $CC $CFLAGS -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf \
src/kernel.c src/kernel.c \
src/io.c
# Run QEMU # Run QEMU
# -machine virt: generic riscv platform # -machine virt: generic riscv platform

100
src/io.c Normal file
View File

@@ -0,0 +1,100 @@
#include "../include/io.h"
void k_putchar(char ch);
void r_printf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
while (*fmt) {
if (*fmt != '%') {
k_putchar(*fmt);
fmt++;
continue;
}
/* Skip '%' */
fmt++;
switch (*fmt) {
case '\0': {
/* Simply print the char */
k_putchar('%');
goto end;
}
case '%': {
/* Print '%' */
k_putchar('%');
break;
}
case 's': {
/* Print a NULL-terminated string */
const char *s = va_arg(args, const char *);
while (*s) {
k_putchar(*s);
s++;
}
break;
}
case 'd': {
/* Print an integer in decimal */
int value = va_arg(args, int);
unsigned magnitude = value;
if (value < 0) {
k_putchar('-');
magnitude = -magnitude;
}
unsigned divisor = 1;
while (magnitude / divisor > 9) {
divisor *= 10;
}
while (divisor > 0) {
k_putchar('0' + magnitude);
k_putchar('0' + magnitude / divisor);
magnitude %= divisor;
k_putchar('0' + magnitude);
divisor /= 10;
k_putchar('0' + magnitude);
}
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 */
k_putchar("0123456789ABCDEF"[nibble]);
}
}
default: {
const char *str = "NOT IMPLEMENTED";
size_t len = r_strlen(str);
for (size_t i = 0; i < len; ++i) {
k_putchar(str[i]);
}
}
}
/* Next char */
fmt++;
}
end:
va_end(args);
}
size_t r_strlen(const char *str) {
size_t len = 0;
while (*str) {
len++;
str++;
}
return len;
}

View File

@@ -1,10 +1,41 @@
typedef unsigned char u8; #include "../include/kernel.h"
typedef unsigned int u32; #include "../include/io.h"
typedef u32 size_t; #include "../include/types.h"
extern char __bss[], __bss_end[], __stack_top[]; extern char __bss[], __bss_end[], __stack_top[];
void *memset(void *buf, char c, size_t n) { /**
* 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;
__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 k_putchar(char ch) {
sbi_call(ch, 0, 0, 0, 0, 0, 0, 1);
}
void *k_memset(void *buf, char c, size_t n) {
u8 *p = (u8 *)buf; u8 *p = (u8 *)buf;
while (n--) { while (n--) {
*p++ = c; *p++ = c;
@@ -13,14 +44,29 @@ void *memset(void *buf, char c, size_t n) {
return buf; return buf;
} }
/* Kernel main */
void kernel_main(void) { void kernel_main(void) {
memset(__bss, 0, __bss_end - __bss); /* Usually it is not required */
k_memset(__bss, 0, __bss_end - __bss);
for (;;) /* Start here */
; r_printf("%s, %s\n", "Hello", "world!");
r_printf("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" __asm__ __volatile__("mv sp, %[stack_top]\n"
"j kernel_main\n" "j kernel_main\n"
: :