aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkartofen <mladenovnasko0@gmail.com>2025-06-08 23:05:09 +0300
committerkartofen <mladenovnasko0@gmail.com>2025-06-08 23:05:09 +0300
commit5c2f11724affb32aee22f7400aa1f953a6081813 (patch)
tree4ed11119f3256a4140852ba2c1f011f9ba7edbfd
-rw-r--r--Makefile47
-rw-r--r--README.md11
-rw-r--r--src/main.s49
-rw-r--r--src/mylloc.c132
-rw-r--r--src/mylloc.s50
-rw-r--r--src/std.s210
-rwxr-xr-xvm.sh69
7 files changed, 568 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1e83dfc
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,47 @@
+BITS := 32
+
+# TOOLCHAIN := riscv$(BITS)-linux-gnu-
+# TOOLCHAIN := riscv$(BITS)-unknown-elf-
+TOOLCHAIN := riscv$(BITS)-unknown-linux-gnu-
+
+CC := $(TOOLCHAIN)gcc
+
+SRC := src
+OBJ := obj
+BIN := bin
+
+FILES = $(shell find $(SRC) -type f)
+SRCS = $(filter %.s, $(FILES))
+OBJS = $(SRCS:$(SRC)/%.s=$(OBJ)/%.o)
+
+FLAGS := -nostdlib -static -s
+
+# KERNEL := linux-6.1.38.img$(BITS)
+KERNEL := linux-5.4.294.img$(BITS)
+INITFS := initfs.img
+NAME := test
+
+export
+
+all: vm
+clean:
+ rm -rf $(BIN)
+ rm -rf $(OBJ)
+
+$(NAME): $(BIN)/$(NAME)
+
+$(OBJ)/%.o: $(SRC)/%.s
+ mkdir -p $(dir $@)
+ $(CC) $(FLAGS) -c $< -o $@
+
+$(BIN)/$(NAME): $(OBJS)
+ mkdir -p $(dir $@)
+ $(CC) $(FLAGS) $^ -o $@
+
+vm: $(BIN)/$(INITFS) $(BIN)/$(KERNEL)
+ ./vm.sh run
+$(BIN)/$(INITFS): $(NAME)
+ ./vm.sh initramfs $(BIN)/$(NAME)
+$(BIN)/$(KERNEL):
+ ./vm.sh kernel
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..eba4b15
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+### RISC-V Things
+
+Things I am testing with risc-v assembly
+
+### Notes
+
+Compiling the linux kernel for the 32-bit version doesn't work.
+So programs that were not compiled with riscv{32,64}-linux-gnu-...
+won't work even if the kernel is compiled with it, and I don't
+have the 32 bit version on my system so for now i can't do 32 bit
+builds
diff --git a/src/main.s b/src/main.s
new file mode 100644
index 0000000..52c4f03
--- /dev/null
+++ b/src/main.s
@@ -0,0 +1,49 @@
+;; .globl _start
+
+_start:
+ la a0, start_str
+ call puts
+
+ addi sp, sp, -8
+ la t0, helloworld
+ sw t0, 4(sp)
+
+ li t0, 69
+ sw t0, 0(sp)
+
+ la a0, fmtstr2
+ call printf
+
+ addi sp, sp, -4
+ sw a0, 0(sp)
+ la a0, fmtstr2
+ call printf
+
+ la a0, fmtstr3
+ call printf
+
+ la a0, end_str
+ call puts
+loop:
+ j loop
+
+.section .data
+fmtstr3:
+ .ascii "%s\0"
+fmtstr2:
+ .ascii "%d\n"
+ .byte 0x0
+fmtstr:
+ .ascii "hello %d %d%d %s thing test%%\n\n\\\n%%\n"
+ .byte 0x0
+
+helloworld:
+ .ascii "Hello World!"
+ .byte 0xA, 0x0
+.equ helloworld_sz, . - helloworld
+
+start_str:
+ .ascii "\n| START |\n\n\0"
+end_str:
+ .ascii "\n| END |\n\0"
+
diff --git a/src/mylloc.c b/src/mylloc.c
new file mode 100644
index 0000000..fc33612
--- /dev/null
+++ b/src/mylloc.c
@@ -0,0 +1,132 @@
+#include <stdio.h>
+#include <stdint.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+// TODO: do more error checking
+
+#define BLOCK_SZ sizeof(struct_block)
+#define BLOCK_ARR_CAP 1024
+
+static inline void *_brk(void *addr) {
+ return (void *)syscall(SYS_brk, addr);
+}
+
+typedef uint64_t u64;
+
+struct block {
+ void *addr; // pointer to the start address of the block
+ u64 size; // the size of the block
+ struct block *next; // pointer to the previous block
+};
+
+static void *heap_start;
+
+static struct block *block_root;
+static struct block block_arr[BLOCK_ARR_CAP];
+
+void *mylloc(u64 size)
+{
+ if(heap_start == NULL) {
+ heap_start = _brk(NULL);
+ }
+
+ void *addr = heap_start;
+
+ struct block *next = block_root;
+ struct block *prev = NULL;
+
+ // find the first available spot to allocate
+ while(next != NULL) {
+ if(size <= (u64)(next->addr - addr)) {
+ break;
+ }
+
+ addr += next->size;
+ prev = next;
+ next = next->next;
+ }
+
+ // no such spot, so extend the system break;
+ if(next == NULL) {
+ _brk(addr + size);
+ }
+
+ // find an empty block and fill it with information
+ for(int i = 0; i < BLOCK_ARR_CAP; i++) {
+ if(block_arr[i].addr != NULL) {
+ continue;
+ }
+
+ block_arr[i].addr = addr;
+ block_arr[i].size = size;
+ block_arr[i].next = next;
+
+ if(prev == NULL) {
+ block_root = &block_arr[i];
+ } else {
+ prev->next = &block_arr[i];
+ }
+
+ break;
+ }
+
+ return addr;
+}
+
+void myfree(void *addr)
+{
+ struct block *prev = NULL;
+ struct block *block = block_root;
+
+ while(block != NULL)
+ {
+ if(block->addr != addr) {
+ prev = block;
+ block = block->next;
+ continue;
+ }
+
+ block->addr = NULL;
+
+ if(prev == NULL) {
+ block_root = block->next;
+ if(block->next == NULL) {
+ _brk(heap_start);
+ }
+ } else {
+ prev->next = block->next;
+ if(block->next == NULL) {
+ _brk(prev->addr + prev->size);
+ }
+ }
+
+ break;
+ }
+}
+
+int main(void)
+{
+ char *str1 = mylloc(10);
+ memset(str1, '2', 10);
+ str1[9] = '\0';
+
+ char *str2 = mylloc(20);
+ memset(str2, '1', 20);
+ str2[19] = '\0';
+
+ myfree(str1);
+
+ char *str3 = mylloc(11);
+ memset(str3, '3', 11);
+ str3[10] = '\0';
+
+ printf("%s\n%s\n", str2, str3);
+
+ myfree(str3);
+ myfree(str2);
+}
diff --git a/src/mylloc.s b/src/mylloc.s
new file mode 100644
index 0000000..9657b73
--- /dev/null
+++ b/src/mylloc.s
@@ -0,0 +1,50 @@
+;;# struct block {
+;;# void *addr; // pointer to the start address of the block
+;;# u32 size; // the size of the block
+;;# void *next // pointer to the previous block
+;;# };
+#
+#;; .section .data
+#
+#;; ;;;# Constants
+#;; .equ SYS_BRK, 214
+#;; .equ BLOCK_SZ, 12
+#;; .equ BLOCK_ARR_CAP, 1024
+#
+#;; .section .bss
+#
+#;; system_break_start:
+#;; .space 4
+#;; system_break_cur:
+#;; .space 4
+#
+#;; block_root:
+#;; .space BLOCK_SZ
+#;; block_arr:
+#;; .space BLOCK_SZ * BLOCK_ARR_CAP
+#
+#;; .section .text
+#
+#;; .globl malloc
+#;; .globl free
+#
+#;; malloc:
+#;; mv t6, a0 ;# save the arg
+#
+#;; la t1, system_break_start ;# load the break
+#;; la t2, system_break_cur ;#
+#;; lw a0, 0(t1) ;# has the break been initialized
+#;; bnez a0, block_loop ;#
+#
+#;; li a7, SYS_BRK ;# do brk syscall with an invalid arg
+#;; ecall ;# to get the current break
+#
+#;; sw a0 0(t1) ;# save the break
+#;; sw a0 0(t2) ;#
+#
+#;; block_loop: ;# in this loop find the first free address
+#
+#;; ret
+#;; free:
+#;; ret
+#
diff --git a/src/std.s b/src/std.s
new file mode 100644
index 0000000..bfdb547
--- /dev/null
+++ b/src/std.s
@@ -0,0 +1,210 @@
+.section .data
+;;;# Constants
+ .equ SYS_WRITE, 64
+ .equ SYS_BRK, 214
+ .equ STDOUT, 1
+ .equ NULL, 0
+
+.section .text
+
+;;;# size_t strlen(char *buf)
+;;;# returns the lenght of buf
+;;;# (buf must be a NULL-terminated)
+;;;# returns 0 when buf is empty
+.globl strlen
+
+;;;# int puts(char *buf)
+;;;# does a sys_write on buf
+;;;# (buf must be a NULL-terminated)
+;;;# returns the amount of charaters written
+;;;# < 0 on fail
+.globl puts
+
+;;;# int putc(char ch)
+;;;# does a sys_write on ch
+;;;# returns 1 on success
+.globl putc
+
+;;;# int printf(char *buf, ...)
+;;;# does what libc printf does
+;;;# (buf must be NULL-terminated)
+;;;# (the variadic args are on the stack
+;;;# with the first begin at 0(sp))
+;;;# returns the amount of characters written
+;;;# < 0 on fail
+.globl printf
+
+;;;# strlen
+strlen:
+ mv t0, a0 ;# copy buf addr
+ li t3, NULL ;# NULL
+strlen_loop:
+ lb t2, 0(t0) ;# load cur char
+ beq t2, t3, strlen_end ;# end loop when its \0
+
+ addi t0, t0, 1 ;# next element in the array
+ j strlen_loop
+strlen_end:
+ sub a0, t0, a0 ;# get the amount of elements
+ ret
+
+;;;# puts
+puts:
+ mv a1, a0 ;# copy buf
+
+ addi sp, sp, -4 ;# save ra
+ sw ra, 0(sp) ;#
+
+ call strlen
+ mv a2, a0 ;# move the len into a2
+
+ lw ra, 0(sp) ;# load ra
+ addi sp, sp, 4 ;#
+
+ li a7, SYS_WRITE
+ li a0, STDOUT
+ ecall
+ ret
+
+;;;# putc
+putc:
+ addi sp, sp, -1 ;# save the char on the stack
+ sb a0, 0(sp) ;# so it is an address
+
+ li a7, SYS_WRITE ;#
+ li a0, STDOUT ;#
+ mv a1, sp ;# do of write syscall
+ li a2, 1 ;#
+ ecall ;#
+
+ addi sp, sp, 1 ;# restore the stack
+ ret
+
+;;;# printf
+printf:
+ ;;# using t6 and t5 like saved registers since i can't
+ ;;# use the stack normally because of the variadic args
+ mv t6, zero ;# the character counter
+ mv t5, a0 ;# the fmt string
+printf_loop:
+ lb t4, 0(t5) ;# load the next char
+
+ li t3, NULL ;# branch to end on null
+ beq t4, t3, printf_end ;#
+ li t3, '%' ;# branch on %
+ beq t4, t3, printf_on_fmt ;#
+ li t3, '\' ;# branch on escape character
+ beq t4, t3, printf_on_esc ;#
+
+ li a0, STDOUT ;#
+ mv a1, t5 ;# the character must be normal
+ li a2, 1 ;# so we just write it
+ li a7, SYS_WRITE ;#
+ ecall ;#
+
+ addi t6, t6, 1 ;# we wrote one more byte
+printf_loop_again:
+ addi t5, t5, 1 ;# go to the next char
+ j printf_loop
+printf_end:
+ mv a0, t6 ;# return the amount of written bytes
+ ret
+
+;;;# each branch pops the data from the stack,
+;;;# then it prints it and increases the byte counter (t6)
+printf_on_fmt:
+ addi t5, t5, 1 ;# go to the next char
+ lb t4, 0(t5) ;# load the next char
+
+ li t3, NULL
+ beq t4, t3, printf_end ;# branch to end on null
+ li t3, '%'
+ beq t4, t3, printf_perc ;# print '%' on '%'
+ li t3, 's'
+ beq t4, t3, printf_string ;# print string on 's'
+ li t3, 'd'
+ beq t4, t3, printf_dec_num ;# print decimal on 'd'
+
+ j printf_loop_again ;# no such program character
+
+;;;# each branch sets a0 to a char, then the char is written
+printf_on_esc:
+ addi t5, t5, 1 ;# go to the next char
+ lb t4, 0(t5) ;# load the next char
+
+ li t3, NULL
+ beq t4, t3, printf_end ;# branch to end on null
+ li t3, '\'
+ beq t4, t3, printf_esc_slsh ;# putc slash
+ li t3, 'n'
+ beq t4, t3, printf_esc_nl ;# putc \n
+printf_on_esc_ret:
+ addi sp, sp, -4 ;# push ra to the stack
+ sw ra, 0(sp) ;#
+
+ call putc
+
+ lw ra, 0(sp) ;# pop ra from the stack
+ addi sp, sp, 4 ;#
+
+ add t6, t6, a0 ;# add amount of printed chars
+ j printf_loop_again
+
+printf_esc_slsh:
+ li a0, '\'
+ j printf_on_esc_ret
+printf_esc_nl:
+ li a0, 0xA
+ j printf_on_esc_ret
+
+printf_perc:
+ addi sp, sp, -4 ;# push ra to the stack
+ sw ra, 0(sp) ;#
+
+ li a0, '%' ;# putc '%'
+ call putc ;#
+
+ lw ra, 0(sp) ;# pop ra from the stack
+ addi sp, sp, 4 ;#
+
+ add t6, t6, a0 ;# add amount of printed chars
+ j printf_loop_again
+
+printf_string:
+ lw a0, 0(sp) ;# get the string from the stack
+ sw ra, 0(sp) ;# save ra to stack
+
+ call puts
+
+ lw ra, 0(sp) ;# pop ra from the stack
+ addi sp, sp, 4 ;#
+
+ add t6, t6, a0 ;# add amount of printed chars
+ j printf_loop_again
+
+printf_dec_num:
+ addi t0, zero, 10 ;# divisor
+ mv t2, zero ;# counter
+
+ lw t3, 0(sp) ;# pop the number from the stack
+ addi sp, sp, 4 ;#
+printf_dec_num_loop:
+ remu t1, t3, t0 ;# get the last digit
+ addi t2, t2, 1 ;# inc the counter
+
+ addi t1, t1, '0' ;# turns digit into a char
+ addi sp, sp, -1 ;# one byte on stack
+ sb t1, 0(sp) ;# save on stack
+
+ divu t3, t3, t0 ;# remove the last digit
+ bne t3, zero, printf_dec_num_loop ;# loop again when the number is not 0
+
+ li a0, STDOUT ;#
+ mv a1, sp ;# do a write syscall
+ mv a2, t2 ;# on the string in the stack
+ li a7, SYS_WRITE ;#
+ ecall ;#
+
+ add sp, sp, t2 ;# clear the stack
+ add t6, t6, t2 ;# add to the char counter
+ j printf_loop_again
diff --git a/vm.sh b/vm.sh
new file mode 100755
index 0000000..04f7027
--- /dev/null
+++ b/vm.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+set -xe
+
+function run
+{
+ # 32 bit does NOT work, even if the kernel is obviously compiled
+ # for 32 bit, the binary could be broken, but I didn't manage to
+ # build qemu from source,
+ # pretty sure that riscv32-unknown-linux-gnu- works fine
+ qemu-system-riscv64 \
+ -machine virt \
+ -kernel "$BIN/$KERNEL" \
+ -initrd "$BIN/$INITFS" \
+ -append "console=ttyS0" -nographic \
+ -monitor none \
+ -serial stdio
+}
+
+function initramfs
+{
+ IMG="$(pwd)/$BIN/$INITFS"
+ DIR="${IMG%.*}"
+ mkdir -p $DIR
+
+ # copy the program
+ cp $1 "$DIR/init"
+
+ # make image and delete folder
+ (cd $DIR; find . | cpio -H newc -o | gzip > $IMG)
+ rm -rf $DIR
+}
+
+function kernel
+{
+ DIR="${KERNEL%.*}"
+ ARCHIVE="$DIR.tar.xz"
+
+ BACKUP="backup"
+ mkdir -p $BACKUP
+
+ if [[ -n $(find backup/ -type f -name "$KERNEL") ]]; then
+ cp $BACKUP/$KERNEL $BIN/
+ exit 0
+ fi
+
+ if [[ -n $(find backup/ -type f -name "$ARCHIVE") ]]; then
+ cp $BACKUP/$ARCHIVE $BIN/
+ else
+ wget -P $BIN https://cdn.kernel.org/pub/linux/kernel/v5.x/$ARCHIVE
+ cp $BIN/$ARCHIVE $BACKUP/
+ fi
+
+ tar -xf $BIN/$ARCHIVE -C $BIN/
+ rm $BIN/$ARCHIVE
+
+ # TOOLCHAIN in the Makefile
+ cd $BIN/$DIR
+ make ARCH=riscv CROSS_COMPILE=$TOOLCHAIN defconfig
+ make ARCH=riscv CROSS_COMPILE=$TOOLCHAIN -j $(nproc)
+ cd ../../
+
+ cp $BIN/$DIR/arch/riscv/boot/Image $BIN/$KERNEL
+ cp $BIN/$KERNEL $BACKUP
+
+ rm -rf $BIN/$DIR
+}
+
+$1 $2