From 4cc12e6e26b11be8de3c8ba7746f3ac2a188f704 Mon Sep 17 00:00:00 2001 From: Francesco Date: Mon, 26 Jan 2026 01:40:11 +0100 Subject: [PATCH] feat(io): implement printf and strlen --- include/io.h | 14 +++++++ include/kernel.h | 9 +++++ run.sh | 3 +- src/io.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++ src/kernel.c | 62 +++++++++++++++++++++++++---- 5 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 include/io.h create mode 100644 include/kernel.h create mode 100644 src/io.c diff --git a/include/io.h b/include/io.h new file mode 100644 index 0000000..bc2c631 --- /dev/null +++ b/include/io.h @@ -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 diff --git a/include/kernel.h b/include/kernel.h new file mode 100644 index 0000000..700797e --- /dev/null +++ b/include/kernel.h @@ -0,0 +1,9 @@ +#ifndef RIVOS_KERNEL_H +#define RIVOS_KERNEL_H + +typedef struct sbiret_s { + long error; + long value; +} sbiret_s; + +#endif diff --git a/run.sh b/run.sh index 2da46fa..bc52bf0 100755 --- a/run.sh +++ b/run.sh @@ -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" $CC $CFLAGS -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf \ - src/kernel.c + src/kernel.c \ + src/io.c # Run QEMU # -machine virt: generic riscv platform diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..3a9bcb7 --- /dev/null +++ b/src/io.c @@ -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; +} diff --git a/src/kernel.c b/src/kernel.c index e501bb3..ef03ff3 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -1,10 +1,41 @@ -typedef unsigned char u8; -typedef unsigned int u32; -typedef u32 size_t; +#include "../include/kernel.h" +#include "../include/io.h" +#include "../include/types.h" 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; while (n--) { *p++ = c; @@ -13,14 +44,29 @@ void *memset(void *buf, char c, size_t n) { return buf; } +/* Kernel main */ 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" "j kernel_main\n" :