From d838adddd50c06fd81c077a3c41558dfed46610f Mon Sep 17 00:00:00 2001 From: sanspie Date: Fri, 6 Feb 2026 15:52:53 +0300 Subject: [PATCH] initiall commit --- riscv32-os/.gitignore | 5 ++ riscv32-os/Makefile | 19 +++++ riscv32-os/common.c | 82 ++++++++++++++++++++ riscv32-os/common.h | 16 ++++ riscv32-os/kernel.c | 171 ++++++++++++++++++++++++++++++++++++++++++ riscv32-os/kernel.h | 24 ++++++ riscv32-os/kernel.ld | 24 ++++++ riscv32-os/run.sh | 29 +++++++ setup.sh | 20 +++++ 9 files changed, 390 insertions(+) create mode 100644 riscv32-os/.gitignore create mode 100644 riscv32-os/Makefile create mode 100644 riscv32-os/common.c create mode 100644 riscv32-os/common.h create mode 100644 riscv32-os/kernel.c create mode 100644 riscv32-os/kernel.h create mode 100644 riscv32-os/kernel.ld create mode 100755 riscv32-os/run.sh create mode 100755 setup.sh diff --git a/riscv32-os/.gitignore b/riscv32-os/.gitignore new file mode 100644 index 0000000..5fdc570 --- /dev/null +++ b/riscv32-os/.gitignore @@ -0,0 +1,5 @@ +*.o +*.elf +*.map +*.bin +qemu.log diff --git a/riscv32-os/Makefile b/riscv32-os/Makefile new file mode 100644 index 0000000..671422e --- /dev/null +++ b/riscv32-os/Makefile @@ -0,0 +1,19 @@ +CC ?= clang +QEMU ?= qemu-system-riscv32 +FW ?= /usr/share/qemu/opensbi-riscv32-generic-fw_dynamic.bin + +CFLAGS = -fuse-ld=lld -std=c11 -O2 -g3 -Wall -Wextra \ + --target=riscv32 -march=rv32imac -mabi=ilp32 \ + -ffreestanding -nostdlib + +all: kernel.elf + +kernel.elf: kernel.c common.c common.h kernel.h kernel.ld + $(CC) $(CFLAGS) -Wl,-T,kernel.ld -Wl,-Map,kernel.map -o $@ kernel.c common.c + +run: kernel.elf + $(QEMU) -machine virt -nographic -serial mon:stdio --no-reboot \ + -bios $(FW) -kernel kernel.elf + +clean: + rm -f *.o *.elf *.map qemu.log diff --git a/riscv32-os/common.c b/riscv32-os/common.c new file mode 100644 index 0000000..8e7cd76 --- /dev/null +++ b/riscv32-os/common.c @@ -0,0 +1,82 @@ +#include "common.h" + +void putchar(char ch); + +void *memset(void *buf, int c, size_t n) { + uint8_t *p = (uint8_t*)buf; + while (n--) *p++ = (uint8_t)c; + return buf; +} + +static void print_uint_hex(uint32_t v) { + for (int i = 7; i >= 0; i--) { + uint32_t nib = (v >> (i * 4)) & 0xF; + putchar("0123456789abcdef"[nib]); + } +} + +static void print_int_dec(int v) { + if (v == 0) { putchar('0'); return; } + if (v < 0) { putchar('-'); v = -v; } + + int div = 1; + while (v / div > 9) div *= 10; + + while (div > 0) { + putchar('0' + (v / div)); + v %= div; + div /= 10; + } +} + +void printf(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + + while (*fmt) { + if (*fmt != '%') { + putchar(*fmt++); + continue; + } + + fmt++; + char spec = *fmt ? *fmt : '\0'; + + if (spec == '\0') { putchar('%'); break; } + + switch (spec) { + case '%': + putchar('%'); + break; + case 'c': { + int ch = va_arg(ap, int); + putchar((char)ch); + break; + } + case 's': { + const char *s = va_arg(ap, const char*); + if (!s) s = "(null)"; + while (*s) putchar(*s++); + break; + } + case 'd': { + int v = va_arg(ap, int); + print_int_dec(v); + break; + } + case 'x': { + uint32_t v = va_arg(ap, uint32_t); + print_uint_hex(v); + break; + } + default: + putchar('%'); + putchar(spec); + break; + } + + fmt++; + } + + va_end(ap); +} \ No newline at end of file diff --git a/riscv32-os/common.h b/riscv32-os/common.h new file mode 100644 index 0000000..6ec1197 --- /dev/null +++ b/riscv32-os/common.h @@ -0,0 +1,16 @@ +#pragma once + +typedef unsigned char uint8_t; +typedef unsigned int uint32_t; +typedef unsigned long size_t; + +#define NULL ((void*)0) + +/* Use compiler builtins instead of libc stdarg */ +#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 *memset(void *buf, int c, size_t n); +void printf(const char *fmt, ...); diff --git a/riscv32-os/kernel.c b/riscv32-os/kernel.c new file mode 100644 index 0000000..614c68d --- /dev/null +++ b/riscv32-os/kernel.c @@ -0,0 +1,171 @@ +#include "kernel.h" + +extern char __bss[], __bss_end[], __stack_top[]; + +static inline struct sbiret sbi_call(long a0, long a1, long a2, long a3, + long a4, long a5, long fid, long eid) { + register long x0 __asm__("a0") = a0; + register long x1 __asm__("a1") = a1; + register long x2 __asm__("a2") = a2; + register long x3 __asm__("a3") = a3; + register long x4 __asm__("a4") = a4; + register long x5 __asm__("a5") = a5; + register long x6 __asm__("a6") = fid; + register long x7 __asm__("a7") = eid; + + __asm__ __volatile__("ecall" + : "+r"(x0), "+r"(x1) + : "r"(x2), "r"(x3), "r"(x4), "r"(x5), "r"(x6), "r"(x7) + : "memory"); + return (struct sbiret){ .error = x0, .value = x1 }; +} + +void putchar(char ch) { + sbi_call((long)ch, 0, 0, 0, 0, 0, 0, 1); +} + +int getchar(void) { + int ch; + do { + struct sbiret ret = sbi_call(0, 0, 0, 0, 0, 0, 0, 2); + ch = (int)ret.error; + } while (ch == -1); + return ch; +} + +static void get_sbi_spec_version(void) { + struct sbiret ret = sbi_call(0, 0, 0, 0, 0, 0, 0, SBI_EXT_BASE); + + if (ret.error != SBI_SUCCESS) { + printf("Error: %d\n", (int)ret.error); + return; + } + + long version = ret.value; + int major = (version >> 24) & 0x7F; + int minor = version & 0xFFFFFF; + + printf("SBI Specification Version: %d.%d\n", major, minor); +} + +static void get_num_counters(void) { + struct sbiret ret = sbi_call(0, 0, 0, 0, 0, 0, 0, SBI_EXT_PMU); + + if (ret.error != SBI_SUCCESS) { + printf("Error: %d\n", (int)ret.error); + return; + } + + printf("Number of counters: %d\n", (int)ret.value); +} + +static int read_number(void) { + int num = 0; + int ch; + + while (1) { + ch = getchar(); + + if (ch == '\r' || ch == '\n') { + putchar('\n'); + break; + } + + if (ch >= '0' && ch <= '9') { + putchar(ch); + num = num * 10 + (ch - '0'); + } + } + + return num; +} + +static void get_counter_info(void) { + printf("Enter counter index: "); + int idx = read_number(); + + struct sbiret ret = sbi_call(idx, 0, 0, 0, 0, 0, 1, SBI_EXT_PMU); + + if (ret.error == SBI_ERR_NOT_SUPPORTED) { + printf("PMU extension not supported\n"); + return; + } + + if (ret.error != SBI_SUCCESS) { + printf("Error: %d (invalid counter index?)\n", (int)ret.error); + return; + } + + uint32_t info = (uint32_t)ret.value; + + uint32_t csr = info & 0xFFF; + uint32_t width_minus_one = (info >> 12) & 0x3F; + uint32_t type = (info >> 31) & 0x1; + + printf("Counter %d details:\n", idx); + printf(" Type: %s\n", type ? "Firmware" : "Hardware"); + + if (type == 0) { + printf(" CSR: 0x%x\n", csr); + printf(" Width: %d bits\n", width_minus_one + 1); + } else { + printf(" (CSR and Width fields ignored for firmware counters)\n"); + } + + printf(" Raw info: 0x%x\n", info); +} + +static void system_shutdown(void) { + printf("Goodbye!\n"); + sbi_call(0, 0, 0, 0, 0, 0, 0, SBI_EXT_SRST); +} + +static void print_menu(void) { + printf("1. Get SBI specification version\n"); + printf("2. Get number of counters\n"); + printf("3. Get details of a counter\n"); + printf("4. System Shutdown\n"); +} + +static void kernel_main(void) { + memset(__bss, 0, (size_t)(__bss_end - __bss)); + + print_menu(); + + while (1) { + printf("Choose option (1-4): "); + int ch = getchar(); + putchar(ch); + putchar('\n'); + + switch (ch) { + case '1': + get_sbi_spec_version(); + break; + case '2': + get_num_counters(); + break; + case '3': + get_counter_info(); + break; + case '4': + system_shutdown(); + return; + default: + printf("Invalid option\n"); + break; + } + } +} + +__attribute__((section(".text.boot"))) +__attribute__((naked)) +void boot(void) { + __asm__ __volatile__( + "mv sp, %[stack_top]\n" + "j %[kmain]\n" + : + : [stack_top] "r" (__stack_top), + [kmain] "i" (kernel_main) + ); +} \ No newline at end of file diff --git a/riscv32-os/kernel.h b/riscv32-os/kernel.h new file mode 100644 index 0000000..d1c554d --- /dev/null +++ b/riscv32-os/kernel.h @@ -0,0 +1,24 @@ +#pragma once + +#include "common.h" + +struct sbiret { + long error; + long value; +}; + +#define SBI_SUCCESS 0 +#define SBI_ERR_FAILED -1 +#define SBI_ERR_NOT_SUPPORTED -2 +#define SBI_ERR_INVALID_PARAM -3 + +#define SBI_EXT_BASE 0x10 +#define SBI_EXT_PMU 0x504D55 +#define SBI_EXT_SRST 0x53525354 + +#define PANIC(fmt, ...) do { \ + printf("PANIC: %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ + for (;;) { __asm__ __volatile__("wfi"); } \ +} while (0) + +int getchar(void); \ No newline at end of file diff --git a/riscv32-os/kernel.ld b/riscv32-os/kernel.ld new file mode 100644 index 0000000..43ebb29 --- /dev/null +++ b/riscv32-os/kernel.ld @@ -0,0 +1,24 @@ +ENTRY(boot) + +SECTIONS { + /* QEMU virt loads the kernel at 0x80200000 */ + . = 0x80200000; + + .text : { + KEEP(*(.text.boot)); + *(.text .text.*); + } + + .rodata : ALIGN(4) { *(.rodata .rodata.*); } + .data : ALIGN(4) { *(.data .data.*); } + + .bss : ALIGN(4) { + __bss = .; + *(.bss .bss.* .sbss .sbss.*); + __bss_end = .; + } + + . = ALIGN(4); + . += 128 * 1024; /* 128KB stack */ + __stack_top = .; +} diff --git a/riscv32-os/run.sh b/riscv32-os/run.sh new file mode 100755 index 0000000..923dba9 --- /dev/null +++ b/riscv32-os/run.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +QEMU="${QEMU:-qemu-system-riscv32}" +CC="${CC:-clang}" + +FW="${FW:-/usr/share/qemu/opensbi-riscv32-generic-fw_dynamic.bin}" +if [[ ! -f "$FW" ]]; then + echo "ERROR: OpenSBI firmware not found at: $FW" + echo "Try: ls -1 /usr/share/qemu/*opensbi*riscv32*fw_dynamic*.bin" + exit 1 +fi + +CFLAGS=( + -fuse-ld=lld + -std=c11 -O2 -g3 -Wall -Wextra + --target=riscv32 + -march=rv32imac -mabi=ilp32 + -ffreestanding -nostdlib +) + +"$CC" "${CFLAGS[@]}" \ + -Wl,-T,kernel.ld -Wl,-Map,kernel.map \ + -o kernel.elf \ + kernel.c common.c + +"$QEMU" -machine virt -nographic -serial mon:stdio --no-reboot \ + -bios "$FW" \ + -kernel kernel.elf diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..3011846 --- /dev/null +++ b/setup.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo +echo "== Checks ==" +clang --version | head -n1 +qemu-system-riscv32 --version | head -n1 + +echo +echo "clang riscv32 target:" +clang -print-targets | grep -E 'riscv32' || { + echo "ERROR: clang does not list riscv32 target." + exit 1 +} + +echo +echo "OpenSBI firmware candidates:" +ls -1 /usr/share/qemu/*opensbi*riscv32*fw_dynamic*.bin 2>/dev/null || true +echo +echo "Done."