aboutsummaryrefslogtreecommitdiff
path: root/arch/riscv
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2024-08-28 22:25:56 +0200
committerMiquel Sabaté Solà <mikisabate@gmail.com>2024-12-05 16:02:49 +0100
commitab03d3be21f229be8c28dba292d5da237a929a73 (patch)
tree8bc53e3031f644532fec255bb392a90cd4ad59fb /arch/riscv
parent1a36e6211674f1a39c7cc0ddff705357a4add65a (diff)
downloadfarga-ab03d3be21f229be8c28dba292d5da237a929a73.tar.gz
farga-ab03d3be21f229be8c28dba292d5da237a929a73.zip
Restructure the whole thing
It will be something more than just messing around with RISC-V stuff. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
Diffstat (limited to 'arch/riscv')
-rw-r--r--arch/riscv/bios/Makefile25
-rw-r--r--arch/riscv/bios/config.ld9
-rw-r--r--arch/riscv/bios/hello.s23
-rw-r--r--arch/riscv/init/Makefile21
-rw-r--r--arch/riscv/init/file_list.txt2
-rw-r--r--arch/riscv/init/hello.c8
-rw-r--r--arch/riscv/init/initramfs.cpiobin0 -> 4959232 bytes
-rw-r--r--arch/riscv/init/little_shell.go19
-rw-r--r--arch/riscv/init/shell.c32
-rw-r--r--arch/riscv/kernel/Makefile20
-rw-r--r--arch/riscv/kernel/hello.ld9
-rw-r--r--arch/riscv/kernel/hello.s15
-rw-r--r--arch/riscv/kernel/infinite.ld9
-rw-r--r--arch/riscv/kernel/infinite.s4
-rw-r--r--arch/riscv/usr/Makefile20
-rw-r--r--arch/riscv/usr/basics.c89
-rw-r--r--arch/riscv/usr/factorial.S28
-rw-r--r--arch/riscv/usr/float.S40
-rw-r--r--arch/riscv/usr/string.S93
19 files changed, 466 insertions, 0 deletions
diff --git a/arch/riscv/bios/Makefile b/arch/riscv/bios/Makefile
new file mode 100644
index 0000000..60e3cc4
--- /dev/null
+++ b/arch/riscv/bios/Makefile
@@ -0,0 +1,25 @@
+NAME = hello
+
+CROSS_AS ?= riscv64-suse-linux-as
+CROSS_LD ?= riscv64-suse-linux-ld
+CROSS_DUMP ?= riscv64-suse-linux-objdump
+ASFLAGS = -march=rv64g -mabi=lp64
+
+EMU ?= qemu-system-riscv64
+
+##
+# Targets
+
+.PHONY: run
+run: clean build
+ $(CROSS_DUMP) -D $(NAME)
+ $(EMU) -machine virt -bios hello
+
+.PHONY: build
+build:
+ $(CROSS_AS) $(ASFLAGS) -o $(NAME).o -c $(NAME).s
+ $(CROSS_LD) -T config.ld $(NAME).o -o $(NAME)
+
+.PHONY: clean
+clean:
+ @rm -f $(NAME).o $(NAME)
diff --git a/arch/riscv/bios/config.ld b/arch/riscv/bios/config.ld
new file mode 100644
index 0000000..085caa3
--- /dev/null
+++ b/arch/riscv/bios/config.ld
@@ -0,0 +1,9 @@
+MEMORY {
+ dram_space (rwx) : ORIGIN = 0x80000000, LENGTH = 128
+}
+
+SECTIONS {
+ .text : {
+ hello.o(.text.bios)
+ } > dram_space
+}
diff --git a/arch/riscv/bios/hello.s b/arch/riscv/bios/hello.s
new file mode 100644
index 0000000..f3aa20a
--- /dev/null
+++ b/arch/riscv/bios/hello.s
@@ -0,0 +1,23 @@
+# From: https://github.com/qemu/qemu/blob/23fa74974d8c96bc95cbecc0d4e2d90f984939f6/hw/riscv/virt.c#L82.
+.set QEMU_UART0_ADDRESS, 0x10000000
+
+.global _start
+.section .text.bios
+
+_start: li a0, 0x68
+ li a1, QEMU_UART0_ADDRESS
+ sb a0, (a1) # 'h'
+
+ li a0, 0x65
+ sb a0, (a1) # 'e'
+
+ li a0, 0x6C
+ sb a0, (a1) # 'l'
+
+ li a0, 0x6C
+ sb a0, (a1) # 'l'
+
+ li a0, 0x6F
+ sb a0, (a1) # 'o'
+
+loop: j loop
diff --git a/arch/riscv/init/Makefile b/arch/riscv/init/Makefile
new file mode 100644
index 0000000..71405c3
--- /dev/null
+++ b/arch/riscv/init/Makefile
@@ -0,0 +1,21 @@
+CROSS_CC ?= riscv64-suse-linux-gcc
+CROSS_LD ?= riscv64-suse-linux-ld
+CCFLAGS = -static
+
+##
+# Targets
+
+.PHONY: hello
+hello: clean
+ $(CROSS_CC) $(CCFLAGS) -o init hello.c
+ cpio -o -H newc < file_list.txt > initramfs.cpio
+
+.PHONY: shell
+shell: clean
+ $(CROSS_CC) $(CCFLAGS) -o init shell.c
+ GOOS=linux GOARCH=riscv64 go build little_shell.go
+ cpio -o -H newc < file_list.txt > initramfs.cpio
+
+.PHONY: clean
+clean:
+ @rm -f *.o init initramfs.cpio
diff --git a/arch/riscv/init/file_list.txt b/arch/riscv/init/file_list.txt
new file mode 100644
index 0000000..9dd8872
--- /dev/null
+++ b/arch/riscv/init/file_list.txt
@@ -0,0 +1,2 @@
+init
+little_shell
diff --git a/arch/riscv/init/hello.c b/arch/riscv/init/hello.c
new file mode 100644
index 0000000..d4e5892
--- /dev/null
+++ b/arch/riscv/init/hello.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+ printf("Hello, world!\n");
+ exit(0);
+}
diff --git a/arch/riscv/init/initramfs.cpio b/arch/riscv/init/initramfs.cpio
new file mode 100644
index 0000000..802848e
--- /dev/null
+++ b/arch/riscv/init/initramfs.cpio
Binary files differ
diff --git a/arch/riscv/init/little_shell.go b/arch/riscv/init/little_shell.go
new file mode 100644
index 0000000..7324323
--- /dev/null
+++ b/arch/riscv/init/little_shell.go
@@ -0,0 +1,19 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+)
+
+func main() {
+ fmt.Println("Hello world from Go!")
+
+ reader := bufio.NewReader(os.Stdin)
+
+ for {
+ fmt.Print("Enter your command: ")
+ line, _ := reader.ReadString('\n')
+ fmt.Printf("Your command is: %s", line)
+ }
+}
diff --git a/arch/riscv/init/shell.c b/arch/riscv/init/shell.c
new file mode 100644
index 0000000..d44b1c7
--- /dev/null
+++ b/arch/riscv/init/shell.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <unistd.h>
+
+int main(int argc, char *argv[])
+{
+ pid_t pid = fork();
+
+ if (pid == -1) {
+ printf("Unable to fork!");
+ return -1;
+ }
+
+ if (pid == 0) {
+ // This is a child process.
+ int status = execl("/little_shell", "irrelevant", NULL);
+
+ if (status == -1) {
+ printf("Forked process cannot start the little_shell");
+ return -2;
+ }
+ }
+
+ int count = 1;
+
+ while (1) {
+ printf("Hello from the original init! %d\n", count);
+ count++;
+ sleep(10);
+ }
+
+ return 0;
+}
diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
new file mode 100644
index 0000000..e943353
--- /dev/null
+++ b/arch/riscv/kernel/Makefile
@@ -0,0 +1,20 @@
+CROSS_AS ?= riscv64-suse-linux-as
+CROSS_LD ?= riscv64-suse-linux-ld
+ASFLAGS = -march=rv64g -mabi=lp64
+
+##
+# Targets
+
+.PHONY: infinite
+infinite: clean
+ $(CROSS_AS) $(ASFLAGS) -o infinite.o -c infinite.s
+ $(CROSS_LD) -T infinite.ld infinite.o -o infinite
+
+.PHONY: hello
+hello: clean
+ $(CROSS_AS) $(ASFLAGS) -o hello.o -c hello.s
+ $(CROSS_LD) -T hello.ld hello.o -o hello
+
+.PHONY: clean
+clean:
+ @rm -f *.o infinite hello
diff --git a/arch/riscv/kernel/hello.ld b/arch/riscv/kernel/hello.ld
new file mode 100644
index 0000000..e4af459
--- /dev/null
+++ b/arch/riscv/kernel/hello.ld
@@ -0,0 +1,9 @@
+MEMORY {
+ kernel_space (rwx) : ORIGIN = 0x80200000, LENGTH = 128
+}
+
+SECTIONS {
+ .text : {
+ hello.o(.text.kernel)
+ } > kernel_space
+}
diff --git a/arch/riscv/kernel/hello.s b/arch/riscv/kernel/hello.s
new file mode 100644
index 0000000..eacd797
--- /dev/null
+++ b/arch/riscv/kernel/hello.s
@@ -0,0 +1,15 @@
+.global _start
+.section .text.kernel
+
+_start: li a7, 0x4442434E
+ li a6, 0x00
+ li a0, 12
+ lla a1, debug_string
+ li a2, 0
+ ecall
+
+loop: j loop
+
+ .section .rodata
+debug_string:
+ .string "Hello world\n"
diff --git a/arch/riscv/kernel/infinite.ld b/arch/riscv/kernel/infinite.ld
new file mode 100644
index 0000000..2eee384
--- /dev/null
+++ b/arch/riscv/kernel/infinite.ld
@@ -0,0 +1,9 @@
+MEMORY {
+ kernel_space (rwx) : ORIGIN = 0x80200000, LENGTH = 128
+}
+
+SECTIONS {
+ .text : {
+ infinite.o(.text.kernel)
+ } > kernel_space
+}
diff --git a/arch/riscv/kernel/infinite.s b/arch/riscv/kernel/infinite.s
new file mode 100644
index 0000000..855f58e
--- /dev/null
+++ b/arch/riscv/kernel/infinite.s
@@ -0,0 +1,4 @@
+ .global _start
+ .section .text.kernel
+
+_start: j _start
diff --git a/arch/riscv/usr/Makefile b/arch/riscv/usr/Makefile
new file mode 100644
index 0000000..6e36dd6
--- /dev/null
+++ b/arch/riscv/usr/Makefile
@@ -0,0 +1,20 @@
+CC = $(CROSS_COMPILE)gcc
+LD = $(CROSS_COMPILE)ld
+CCFLAGS =
+EXE = basics
+
+DEBUG =
+ifneq ($(strip $(DEBUG)),)
+ CCFLAGS += -g
+endif
+
+.PHONY: all
+all: clean $(EXE)
+
+.PHONY: $(EXE)
+$(EXE):
+ gcc $(CCFLAGS) -o $(EXE) main.c factorial.S string.S float.S
+
+.PHONY: clean
+clean:
+ @rm -f $(EXE)
diff --git a/arch/riscv/usr/basics.c b/arch/riscv/usr/basics.c
new file mode 100644
index 0000000..4b5f289
--- /dev/null
+++ b/arch/riscv/usr/basics.c
@@ -0,0 +1,89 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+/*
+ * Returns the factorial for the given unsigned 64-bit integer.
+ *
+ * Implemented in factorial.S
+ */
+int factorial(uint64_t n);
+
+void test_factorial(void)
+{
+ assert(factorial(0) == 0);
+ assert(factorial(1) == 1);
+ assert(factorial(2) == 2);
+ assert(factorial(3) == 6);
+ assert(factorial(4) == 24);
+
+ printf("factorial:\t\tOK\n");
+}
+
+/*
+ * Returns a pointer with the same given string but reversed. Note that the
+ * string cannot be in read-only space since the reversal is done in-place.
+ *
+ * Implemented in string.S
+ */
+char * reverse_string(char *str);
+
+void test_reverse_string(void)
+{
+ assert(reverse_string(NULL) == NULL);
+ assert(reverse_string("") == "");
+
+ char s1[] = "This is a string.";
+ assert(strcmp(reverse_string(s1), ".gnirts a si sihT") == 0);
+
+ char s2[] = ".";
+ assert(strcmp(reverse_string(s2), ".") == 0);
+
+ printf("reverse_string:\t\tOK\n");
+}
+
+/*
+ * Returns true if the given string is a palindrome, false otherwise.
+ *
+ * Implemented in string.S
+ */
+bool is_palindrome(char *str);
+
+void test_is_palindrome(void)
+{
+ assert(is_palindrome(NULL) == 0);
+ assert(is_palindrome("") == 0);
+ assert(is_palindrome("aba") == 1);
+ assert(is_palindrome("aaa") == 1);
+ assert(is_palindrome("This is a a si sihT") == 1);
+
+ printf("is_palindrome:\t\tOK\n");
+}
+
+/*
+ * This function computes the following: ((a + b) * b) + b. Don't try to make
+ * sense of it, that's the computation I ended up while messing with RISC-V
+ * floating point instructions. Anyways, with this in mind, it will return if
+ * the computed value is greater than 10.
+ *
+ * Implemented in float.S
+ */
+bool greater_than_ten(double a, uint64_t b);
+
+void test_greater_than_ten(void)
+{
+ assert(!greater_than_ten(2.3, 1)); // 4.3
+ assert(greater_than_ten(2.3, 2)); // 10.6
+
+ printf("greater_than_ten:\tOK\n");
+}
+
+int main()
+{
+ test_factorial();
+ test_reverse_string();
+ test_is_palindrome();
+ test_greater_than_ten();
+}
diff --git a/arch/riscv/usr/factorial.S b/arch/riscv/usr/factorial.S
new file mode 100644
index 0000000..7e3df12
--- /dev/null
+++ b/arch/riscv/usr/factorial.S
@@ -0,0 +1,28 @@
+.text
+.globl factorial
+.type factorial, @function
+
+factorial:
+ // If input <= 1, just return the given thing.
+ addi t0, zero, 1
+ bgt a0, t0, else
+ jr ra
+else:
+ // Preserve the argument and the return address before the call.
+ addi sp, sp, -16
+ sd a0, 0(sp)
+ sd ra, 8(sp)
+
+ // Call the same function but with a value of argument-1.
+ addi a0, a0, -1
+ call factorial
+
+ // Restore back the original argument and the return address. Then multiply
+ // the original argument with the returned one from the previous call.
+ ld t1, 0(sp)
+ ld ra, 8(sp)
+ addi sp, sp, 16
+ mul a0, t1, a0
+
+ // And return to the caller.
+ jr ra
diff --git a/arch/riscv/usr/float.S b/arch/riscv/usr/float.S
new file mode 100644
index 0000000..f481f3e
--- /dev/null
+++ b/arch/riscv/usr/float.S
@@ -0,0 +1,40 @@
+.text
+.globl greater_than_ten
+.type greater_than_ten, @function
+
+// bool greater_than_ten(double a, uint64_t b);
+greater_than_ten:
+ // Upon entry a -> fa0; and b -> a0. That is, the ABI increments the
+ //(f)a<n> register per argument type.
+
+ // Change the rounding to "Rounds Towards Zero" (bits: 0b001).
+ li t0, 0b001
+ fsrm t0
+
+ // Convert the second parameter to double.
+ fcvt.d.lu ft0, a0
+
+ // ret = a + b
+ fadd.d fa0, fa0, ft0
+
+ // ret = (ret * b) + b
+ fmv.d ft1, ft0
+ fmadd.d fa0, fa0, ft0, ft1
+
+ // Now we need to compare the number with 10.0. There are multiple ways to
+ // load a floating point immediate:
+ // 1. Loading an integer immediate and then convert it into a double. This
+ // is discouraged on the RISC-V assembly manual.
+ // 2. Load a double with `fld` from a memory location on the data section.
+ // 3. Load a floating point immediate with `fli` (requires the `Zfa`
+ // extension).
+ // I don't have the `Zfa` extension on my board, so I'm opting for the
+ // second way.
+ fld ft1, .TEN, t0
+ fle.d a0, ft1, fa0
+
+ ret
+
+.data
+.TEN:
+ .double 10.0
diff --git a/arch/riscv/usr/string.S b/arch/riscv/usr/string.S
new file mode 100644
index 0000000..0f3bcc1
--- /dev/null
+++ b/arch/riscv/usr/string.S
@@ -0,0 +1,93 @@
+.text
+
+.globl reverse_string
+.type reverse_string, @function
+
+// char * reverse_string(char *str);
+reverse_string:
+ // Return early on null pointer.
+ beq a0, zero, end
+
+ // Preserve the original pointer.
+ addi sp, sp, -8
+ sd a0, 0(sp)
+
+ // Set `t0` to point to the end of the string.
+ add t0, a0, zero
+set_end_ptr:
+ lbu t2, 0(t0)
+ beq t2, zero, end_ptr_done
+ addi t0, t0, 1
+ j set_end_ptr
+
+ // We are done iterating, if `a0` and `t0` are equal, then there's nothing
+ // to be done and we can return early. Otherwise decrement `t0` so it points
+ // to the byte right before the null termination.
+end_ptr_done:
+ beq a0, t0, reverse_done
+ addi t0, t0, -1
+
+reverse_loop:
+ // Swap values between the two pointers.
+ lb t1, 0(a0)
+ lb t2, 0(t0)
+ sb t1, 0(t0)
+ sb t2, 0(a0)
+
+ // Move pointers and check whether the pointers have already crossed. If
+ // they have not crossed yet there is still looping to be done. Otherwise
+ // we are done.
+ addi a0, a0, 1
+ addi t0, t0, -1
+ bltu a0, t0, reverse_loop
+
+reverse_done:
+ // Restore things back and return to the caller.
+ ld a0, 0(sp)
+ addi sp, sp, 8
+end:
+ jr ra
+
+.globl is_palindrome
+.type is_palindrome, @function
+
+// bool is_palindrome(char *str);
+is_palindrome:
+ // Return early on null pointer.
+ beq a0, zero, palindrome_no
+
+ // Set `t0` to point to the end of the string.
+ add t0, a0, zero
+pal_set_end_ptr:
+ lbu t2, 0(t0)
+ beq t2, zero, pal_end_ptr_done
+ addi t0, t0, 1
+ j pal_set_end_ptr
+
+ // We are done iterating, if `a0` and `t0` are equal, then there's nothing
+ // to be done and we can return early. Otherwise decrement `t0` so it points
+ // to the byte right before the null termination.
+pal_end_ptr_done:
+ beq a0, t0, palindrome_no
+ addi t0, t0, -1
+
+pal_loop:
+ // Swap values between the two pointers.
+ lb t1, 0(a0)
+ lb t2, 0(t0)
+ bne t1, t2, palindrome_no
+
+ // Move pointers and check whether the pointers have already crossed. If
+ // they have not crossed yet there is still looping to be done. Otherwise
+ // we are done.
+ addi a0, a0, 1
+ addi t0, t0, -1
+ bleu a0, t0, pal_loop
+
+palindrome_yes:
+ li a0, 1
+ jr ra
+
+palindrome_no:
+ li a0, 0
+ jr ra