diff options
author | kartofen <mladenovnasko0@gmail.com> | 2025-06-08 23:05:09 +0300 |
---|---|---|
committer | kartofen <mladenovnasko0@gmail.com> | 2025-06-08 23:05:09 +0300 |
commit | 5c2f11724affb32aee22f7400aa1f953a6081813 (patch) | |
tree | 4ed11119f3256a4140852ba2c1f011f9ba7edbfd |
-rw-r--r-- | Makefile | 47 | ||||
-rw-r--r-- | README.md | 11 | ||||
-rw-r--r-- | src/main.s | 49 | ||||
-rw-r--r-- | src/mylloc.c | 132 | ||||
-rw-r--r-- | src/mylloc.s | 50 | ||||
-rw-r--r-- | src/std.s | 210 | ||||
-rwxr-xr-x | vm.sh | 69 |
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 @@ -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 |