diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8d9fb2e --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2013, The Regents of the University of California (Regents). +Copyright (c) 2018-2019, The libfemto authors +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the Regents nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING +OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED +HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE +MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..181bf60 --- /dev/null +++ b/Makefile @@ -0,0 +1,118 @@ +CROSS_COMPILE ?= riscv64-unknown-elf- + +AR = $(CROSS_COMPILE)ar + +CFLAGS = -mcmodel=medany -ffunction-sections -fdata-sections +LDFLAGS = -nostartfiles -nostdlib -nostdinc -static -lgcc \ + -Wl,--nmagic -Wl,--gc-sections +INCLUDES = -Ienv/common + +libfemto_dirs = libfemto/std libfemto/drivers libfemto/arch/riscv +libfemto_src = $(sort $(foreach d,$(libfemto_dirs),$(wildcard $(d)/*.c))) +libfemto_asm = $(sort $(foreach d,$(libfemto_dirs),$(wildcard $(d)/*.s))) +libfemto_objs = $(patsubst %.s,%.o,$(libfemto_asm)) \ + $(patsubst %.c,%.o,$(libfemto_src)) + +# +# Compiler configurations and target environment definitions +# + +subdirs = examples + +libs = libfemto + +configs = rv64imac + +CC_rv32imac = $(CROSS_COMPILE)gcc +CFLAGS_rv32imac = -Os -march=rv32imac -mabi=ilp32 -Ienv/common/rv32 +LDFLAGS_rv32imac = + +CC_rv64imac = $(CROSS_COMPILE)gcc +CFLAGS_rv64imac = -Os -march=rv64imac -mabi=lp64 -Ienv/common/rv64 +LDFLAGS_rv64imac = + +targets = rv64imac:qemu-sifive_u + +# +# make rules +# + +all: all_programs + +clean: + rm -fr build + +backup: clean + tar czf ../$(shell basename $(shell pwd)).tar.gz . + +# +# To view commands use: make V=1 +# + +ifdef V +cmd = @mkdir -p $2 ; echo "$3"; $3 +else +cmd = @echo "$1"; mkdir -p $2 ; $3 +endif + +# +# Build system functions to generate pattern rules for all configs +# + +define pattern = +build/obj/$(2)/%.o: %.$(3) + $(call cmd,$(1).$(2) $$@,$$(@D),$(CC_$(2)) $(CFLAGS_$(2)) $(CFLAGS) \ + $$(INCLUDES) -c $$^ -o $$@) +endef + +$(foreach c,$(configs),$(eval $(call pattern,CC,$(c),c))) +$(foreach c,$(configs),$(eval $(call pattern,AS,$(c),s))) + +# +# Build system functions to generate library rules for all configs +# + +define archive = +build/lib/$(2)/$(3).a: $(addprefix build/obj/$(2)/,$($(3)_objs)) + $(call cmd,$(1).$(2) $$@,$$(@D),$(AR) cr $$@ $$^) +LIBS_$(2) += build/lib/$(2)/$(3).a +endef + +define lib = +$(foreach c,$(configs),$(eval $(call archive,AR,$(c),$(1)))) +INCLUDES += -I$(1)/include +endef + +$(foreach l,$(libs),$(eval $(call lib,$(l)))) + +# +# Build system functions to generate build rules for all subdirs +# + +sub_makes := $(foreach dir,$(subdirs),$(wildcard ${dir}/*/rules.mk)) +$(foreach makefile,$(sub_makes),$(eval include $(makefile))) +sub_dirs := $(foreach m,$(sub_makes),$(m:/rules.mk=)) +module_name = $(lastword $(subst /, ,$(1))) +module_objs = $(addprefix build/obj/$(3)/,$(addprefix $(2)/,$($(1)_objs))) +config_arch = $(word 1,$(subst :, ,$(1))) +config_env = $(word 2,$(subst :, ,$(1))) + +define rule = +build/bin/$(3)/$(4)/$(1): \ +build/obj/$(3)/env/$(4)/crt.o $(2) $$(LIBS_$(3)) + $$(call cmd,LD.$(3) $$@,$$(@D),$(CC_$(3)) $(CFLAGS_$(3)) $$(CFLAGS) \ + $$(LDFLAGS_$(3)) $$(LDFLAGS) -T env/$(4)/default.lds $$^ -o $$@) +endef + +define module = +program_names += $(foreach cfg,$(targets),build/bin/$(call \ + config_arch,$(cfg))/$(call config_env,$(cfg))/$(1)) + +$(foreach cfg,$(targets),$(eval $(call rule,$(1),$(call \ + module_objs,$(1),$(2),$(call config_arch,$(cfg))),$(call \ + config_arch,$(cfg)),$(call config_env,$(cfg))))) +endef + +$(foreach d,$(sub_dirs),$(eval $(call module,$(call module_name,$(d)),$(d)))) + +all_programs: $(program_names) diff --git a/README.md b/README.md new file mode 100644 index 0000000..90204bb --- /dev/null +++ b/README.md @@ -0,0 +1,95 @@ +# Bare metal RISC-V hello world in C + +This is a copy of the [riscv-probe](https://github.com) repository stripped +from everything but the 64-bit hello world program for the `sifive_u` QEMU +machine. + +The directory structure is kept. This makes this repository a good starting +point to dive into riscv-probe. In particular we keep the `crt.s` and `start.c` +files although they are almost empty. They are used in the original repository +to initialize libfemto. Here libfemto just provides a `puts` function. + +Having such a stripped down hello world program written in C was necessary for +me to learn how to create a [bare metal RISC-V assembly hello +world](https://github.com) version because I couldn't find a complete example +program in assembly only that was writing to the UART. + + +## Building + +Assuming the default prefix for the GNU toolchain is correct, and the toolchain +is in the `$PATH`, running `make` produce our `hello` program. Refer to the +original repository otherwise. + +``` +$ make +AS.rv64imac build/obj/rv64imac/env/qemu-sifive_u/crt.o +CC.rv64imac build/obj/rv64imac/examples/hello/hello.o +CC.rv64imac build/obj/rv64imac/libfemto/arch/riscv/start.o +CC.rv64imac build/obj/rv64imac/libfemto/std/putchar.o +AR.rv64imac build/lib/rv64imac/libfemto.a +LD.rv64imac build/bin/rv64imac/qemu-sifive_u/hello +``` + +The result is a 64-bit RISC-V binary. + +``` +$ file build/bin/rv64imac/qemu-sifive_u/hello +build/bin/rv64imac/qemu-sifive_u/hello: ELF 64-bit LSB executable, UCB RISC-V, +version 1 (SYSV), statically linked, not stripped +``` + + +## Running + +Run it with: + +``` +$ qemu-system-riscv64 -nographic -machine sifive_u \ + -kernel build/bin/rv64imac/qemu-sifive_u/hello +Hello. +QEMU: Terminated +``` + +Note: the program enters an infinite loop after producing the `Hello.` text. +Type `ctrl-a x` to stop QEMU. + + +## Assembly + +To dissamble the program: + +``` +$ riscv64-unknown-elf-objdump -d build/bin/rv64imac/qemu-sifive_u/hello +build/bin/rv64imac/qemu-sifive_u/hello: file format elf64-littleriscv + +Disassembly of section .text: + +0000000080000000 <_start>: + 80000000: 0040006f j 80000004
+ +0000000080000004
: + 80000004: 1141 addi sp,sp,-16 + 80000006: e022 sd s0,0(sp) + 80000008: e406 sd ra,8(sp) + 8000000a: 00000417 auipc s0,0x0 + 8000000e: 02e40413 addi s0,s0,46 # 80000038 + 80000012: 00044503 lbu a0,0(s0) + 80000016: e111 bnez a0,8000001a + 80000018: a001 j 80000018 + 8000001a: 0405 addi s0,s0,1 + 8000001c: 006000ef jal ra,80000022 + 80000020: bfcd j 80000012 + +0000000080000022 : + 80000022: 100137b7 lui a5,0x10013 + 80000026: 4398 lw a4,0(a5) + 80000028: 02071693 slli a3,a4,0x20 + 8000002c: fe06cde3 bltz a3,80000026 + 80000030: 0ff57513 andi a0,a0,255 + 80000034: c388 sw a0,0(a5) + 80000036: 8082 ret +``` + +I used the above listing to guide modifications to the `hello.S` program +provided in rv8 (I think) to finally create a pure assembly hello world. diff --git a/env/qemu-sifive_u/crt.s b/env/qemu-sifive_u/crt.s new file mode 100644 index 0000000..265e1a2 --- /dev/null +++ b/env/qemu-sifive_u/crt.s @@ -0,0 +1,7 @@ +# See LICENSE for license details. + +.section .text.init,"ax",@progbits +.globl _start + +_start: + j main diff --git a/env/qemu-sifive_u/default.lds b/env/qemu-sifive_u/default.lds new file mode 100644 index 0000000..9c1a7df --- /dev/null +++ b/env/qemu-sifive_u/default.lds @@ -0,0 +1,46 @@ +OUTPUT_ARCH( "riscv" ) + +ENTRY( _start ) + +MEMORY +{ + ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M +} + +PHDRS +{ + text PT_LOAD; + data PT_LOAD; + bss PT_LOAD; +} + +SECTIONS +{ + .text : { + PROVIDE(_text_start = .); + *(.text.init) *(.text .text.*) + PROVIDE(_text_end = .); + } >ram AT>ram :text + + .rodata : { + PROVIDE(_rodata_start = .); + *(.rodata .rodata.*) + PROVIDE(_rodata_end = .); + } >ram AT>ram :text + + .data : { + . = ALIGN(4096); + PROVIDE(_data_start = .); + *(.sdata .sdata.*) *(.data .data.*) + PROVIDE(_data_end = .); + } >ram AT>ram :data + + .bss :{ + PROVIDE(_bss_start = .); + *(.sbss .sbss.*) *(.bss .bss.*) + PROVIDE(_bss_end = .); + } >ram AT>ram :bss + + PROVIDE(_memory_start = ORIGIN(ram)); + PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram)); +} diff --git a/examples/hello/hello.c b/examples/hello/hello.c new file mode 100644 index 0000000..2bb2beb --- /dev/null +++ b/examples/hello/hello.c @@ -0,0 +1,8 @@ +#include + +void main() +{ + const char *s = "Hello.\n"; + while (*s) putchar(*s++); + while(1); +} diff --git a/examples/hello/rules.mk b/examples/hello/rules.mk new file mode 100644 index 0000000..4d43f63 --- /dev/null +++ b/examples/hello/rules.mk @@ -0,0 +1 @@ +hello_objs = hello.o diff --git a/libfemto/arch/riscv/start.c b/libfemto/arch/riscv/start.c new file mode 100644 index 0000000..bbe95dc --- /dev/null +++ b/libfemto/arch/riscv/start.c @@ -0,0 +1,8 @@ +// See LICENSE for license details. + +void main(); + +void libfemto_start_main() +{ + main(); +} diff --git a/libfemto/include/stdio.h b/libfemto/include/stdio.h new file mode 100644 index 0000000..a2e2b4b --- /dev/null +++ b/libfemto/include/stdio.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +int putchar(int); + +#ifdef __cplusplus +} +#endif diff --git a/libfemto/std/putchar.c b/libfemto/std/putchar.c new file mode 100644 index 0000000..8e7731e --- /dev/null +++ b/libfemto/std/putchar.c @@ -0,0 +1,16 @@ +// See LICENSE for license details. + +#include + +enum { + /* UART Registers */ + UART_REG_TXFIFO = 0, +}; + +static volatile int *uart = (int *)(void *)0x10013000; + +int putchar(int ch) +{ + while (uart[UART_REG_TXFIFO] < 0); + return uart[UART_REG_TXFIFO] = ch & 0xff; +}