initiall commit

This commit is contained in:
Alexander Karpov 2026-02-06 15:52:53 +03:00
commit d838adddd5
9 changed files with 390 additions and 0 deletions

5
riscv32-os/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.o
*.elf
*.map
*.bin
qemu.log

19
riscv32-os/Makefile Normal file
View File

@ -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

82
riscv32-os/common.c Normal file
View File

@ -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);
}

16
riscv32-os/common.h Normal file
View File

@ -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, ...);

171
riscv32-os/kernel.c Normal file
View File

@ -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)
);
}

24
riscv32-os/kernel.h Normal file
View File

@ -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);

24
riscv32-os/kernel.ld Normal file
View File

@ -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 = .;
}

29
riscv32-os/run.sh Executable file
View File

@ -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

20
setup.sh Executable file
View File

@ -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."