diff options
Diffstat (limited to 'code/fe310/gloss')
31 files changed, 566 insertions, 0 deletions
diff --git a/code/fe310/gloss/Makefile b/code/fe310/gloss/Makefile new file mode 100644 index 0000000..a937a53 --- /dev/null +++ b/code/fe310/gloss/Makefile @@ -0,0 +1,17 @@ +include ../common.mk +CFLAGS += -I../include + +src = sys_access.c sys_chdir.c sys_chmod.c sys_chown.c sys_close.c sys_execve.c sys_exit.c sys_faccessat.c sys_fork.c sys_fstat.c sys_fstatat.c sys_ftime.c sys_getcwd.c sys_getpid.c \ + sys_isatty.c sys_kill.c sys_link.c sys_lseek.c sys_lstat.c sys_open.c sys_openat.c sys_read.c sys_sbrk.c sys_stat.c sys_sysconf.c sys_unlink.c sys_utime.c sys_wait.c sys_write.c +obj = crt0.o $(src:.c=.o) + +%.o: %.c + $(CC) $(CFLAGS) -c $< + +%.o: %.S + $(CC) $(CFLAGS) -c $< + +all: $(obj) + +clean: + rm -f *.o diff --git a/code/fe310/gloss/crt0.S b/code/fe310/gloss/crt0.S new file mode 100644 index 0000000..920ee4b --- /dev/null +++ b/code/fe310/gloss/crt0.S @@ -0,0 +1,246 @@ +/* Copyright (c) 2017-2018 SiFive Inc. All rights reserved. + + This copyrighted material is made available to anyone wishing to use, + modify, copy, or redistribute it subject to the terms and conditions + of the FreeBSD License. This program is distributed in the hope that + it will be useful, but WITHOUT ANY WARRANTY expressed or implied, + including the implied warranties of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. A copy of this license is available at + http://www.opensource.org/licenses. +*/ + +/* crt0.S: Entry point for RISC-V METAL programs. */ + +.section .text.libgloss.start +.global _start +.type _start, @function + + /* _start is defined by the METAL to have been called with the following + * arguments: + * a0: the hart ID of the currently executing hart. Harts can start at + * any arbitrary point, it's the C library's job to ensure the code is + * safe. + * a1: a pointer to a description of the machine on which this code is + * currently executing. This is probably 0 on an embedded system + * because they tend to not be dynamically portable. As such, newlib + * ignores this argument. + * a2: a pointer to a function that must be run after the envirnoment has + * been initialized, but before user code can be expected to be run. + * If this is 0 then there is no function to be run. */ +_start: +.cfi_startproc +.cfi_undefined ra + + /* This is a bit funky: it's not usually sane for _start to return, but in + * this case we actually want to in order to signal an error to the METAL. */ + mv s0, ra + + /* Before doing anything we must initialize the global pointer, as we cannot + * safely perform any access that may be relaxed without GP being set. This + * is done with relaxation disabled to avoid relaxing the address calculation + * to just "addi gp, gp, 0". */ +.option push +.option norelax + la gp, __global_pointer$ +.option pop + + /* The METAL is designed for a bare-metal environment and therefor is expected + * to define its own stack pointer. We also align the stack pointer here + * because the only RISC-V ABI that's currently defined mandates 16-byte + * stack alignment. */ + la sp, _sp + + /* Increment by hartid number of stack sizes */ + li t0, 0 + la t1, __stack_size +1: + beq t0, a0, 1f + add sp, sp, t1 + addi t0, t0, 1 + j 1b +1: + andi sp, sp, -16 + + /* If we're not hart 0, skip the initialization work */ + la t0, __metal_boot_hart + bne a0, t0, _skip_init + + /* Embedded systems frequently require relocating the data segment before C + * code can be run -- for example, the data segment may exist in flash upon + * boot and then need to get relocated into a non-persistant writable memory + * before C code can execute. If this is the case we do so here. This step + * is optional: if the METAL provides an environment in which this relocation + * is not necessary then it must simply set metal_segment_data_source_start to + * be equal to metal_segment_data_target_start. */ + la t0, metal_segment_data_source_start + la t1, metal_segment_data_target_start + la t2, metal_segment_data_target_end + + beq t0, t1, 2f + bge t1, t2, 2f + +1: +#if __riscv_xlen == 32 + lw a0, 0(t0) + addi t0, t0, 4 + sw a0, 0(t1) + addi t1, t1, 4 + blt t1, t2, 1b +#else + ld a0, 0(t0) + addi t0, t0, 8 + sd a0, 0(t1) + addi t1, t1, 8 + blt t1, t2, 1b +#endif +2: + + /* Copy the ITIM section */ + la t0, metal_segment_itim_source_start + la t1, metal_segment_itim_target_start + la t2, metal_segment_itim_target_end + + beq t0, t1, 2f + bge t1, t2, 2f + +1: +#if __riscv_xlen == 32 + lw a0, 0(t0) + addi t0, t0, 4 + sw a0, 0(t1) + addi t1, t1, 4 + blt t1, t2, 1b +#else + ld a0, 0(t0) + addi t0, t0, 8 + sd a0, 0(t1) + addi t1, t1, 8 + blt t1, t2, 1b +#endif +2: + + /* Fence all subsequent instruction fetches until after the ITIM writes + complete */ + fence.i + + /* Zero the BSS segment. */ + la t1, metal_segment_bss_target_start + la t2, metal_segment_bss_target_end + + bge t1, t2, 2f + +1: +#if __riscv_xlen == 32 + sw x0, 0(t1) + addi t1, t1, 4 + blt t1, t2, 1b +#else + sd x0, 0(t1) + addi t1, t1, 8 + blt t1, t2, 1b +#endif +2: + + /* At this point we're in an environment that can execute C code. The first + * thing to do is to make the callback to the parent environment if it's been + * requested to do so. */ + beqz a2, 1f + jalr a2 +1: + + /* The RISC-V port only uses new-style constructors and destructors. */ + la a0, __libc_fini_array + call atexit + call __libc_init_array + +_skip_init: + + /* Synchronize harts so that secondary harts wait until hart 0 finishes + initializing */ + call __metal_synchronize_harts + + /* Check RISC-V isa and enable FS bits if Floating Point architecture. */ + csrr a5, misa + li a4, 0x10028 + and a5, a5, a4 + beqz a5, 1f + csrr a5, mstatus + lui a4, 0x2 + or a5, a5, a4 + csrw mstatus, a5 + csrwi fcsr, 0 +1: + + /* This is a C runtime, so main() is defined to have some arguments. Since + * there's nothing sane the METAL can pass we don't bother with that but + * instead just setup as close to a NOP as we can. */ + li a0, 1 /* argc=1 */ + la a1, argv /* argv = {"libgloss", NULL} */ + la a2, envp /* envp = {NULL} */ + call secondary_main + + /* Call exit to handle libc's cleanup routines. Under normal contains this + * shouldn't even get called, but I'm still not using a tail call here + * because returning to the METAL is the right thing to do in pathological + * situations. */ + call exit + + /* And here's where we return. Again, it's a bit odd but the METAL defines + * this as a bad idea (ie, as opposed to leaving it undefined) and at this + * point it's really the only thing left to do. */ + mv ra, s0 + ret + +.cfi_endproc + +/* RISC-V systems always use __libc_{init,fini}_array, but for compatibility we + * define _{init,fini} to do nothing. */ +.global _init +.type _init, @function +.global _fini +.type _fini, @function +_init: +_fini: + ret +.size _init, .-_init +.size _fini, .-_fini + +/* By default, secondary_main will cause secondary harts to spin forever. + * Users can redefine secondary_main themselves to run code on secondary harts */ +.weak secondary_main +.global secondary_main +.type secondary_main, @function + +secondary_main: + addi sp, sp, -16 +#if __riscv_xlen == 32 + sw ra, 4(sp) +#else + sd ra, 8(sp) +#endif + csrr t0, mhartid + la t1, __metal_boot_hart + beq t0, t1, 2f +1: + wfi + j 1b +2: + call main +#if __riscv_xlen == 32 + lw ra, 4(sp) +#else + ld ra, 8(sp) +#endif + addi sp, sp, 16 + ret + +/* This shim allows main() to be passed a set of arguments that can satisfy the + * requirements of the C API. */ +.section .rodata.libgloss.start +argv: +.dc.a name +envp: +.dc.a 0 +name: +.asciz "libgloss" + diff --git a/code/fe310/gloss/sys_access.c b/code/fe310/gloss/sys_access.c new file mode 100644 index 0000000..c0bc153 --- /dev/null +++ b/code/fe310/gloss/sys_access.c @@ -0,0 +1,8 @@ +#include <errno.h> + +int +_access(const char *file, int mode) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_chdir.c b/code/fe310/gloss/sys_chdir.c new file mode 100644 index 0000000..f33d26a --- /dev/null +++ b/code/fe310/gloss/sys_chdir.c @@ -0,0 +1,8 @@ +#include <errno.h> + +int +_chdir(const char *path) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_chmod.c b/code/fe310/gloss/sys_chmod.c new file mode 100644 index 0000000..67412bf --- /dev/null +++ b/code/fe310/gloss/sys_chmod.c @@ -0,0 +1,9 @@ +#include <errno.h> +#include <sys/types.h> + +int +_chmod(const char *path, mode_t mode) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_chown.c b/code/fe310/gloss/sys_chown.c new file mode 100644 index 0000000..302952e --- /dev/null +++ b/code/fe310/gloss/sys_chown.c @@ -0,0 +1,9 @@ +#include <sys/types.h> +#include <errno.h> + +int +_chown(const char *path, uid_t owner, gid_t group) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_close.c b/code/fe310/gloss/sys_close.c new file mode 100644 index 0000000..26dd6a5 --- /dev/null +++ b/code/fe310/gloss/sys_close.c @@ -0,0 +1,8 @@ +#include <errno.h> + +int +_close(int file) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_execve.c b/code/fe310/gloss/sys_execve.c new file mode 100644 index 0000000..9ae9f7e --- /dev/null +++ b/code/fe310/gloss/sys_execve.c @@ -0,0 +1,8 @@ +#include <errno.h> + +int +_execve(const char *name, char *const argv[], char *const env[]) +{ + errno = ENOMEM; + return -1; +} diff --git a/code/fe310/gloss/sys_exit.c b/code/fe310/gloss/sys_exit.c new file mode 100644 index 0000000..1502b3e --- /dev/null +++ b/code/fe310/gloss/sys_exit.c @@ -0,0 +1,7 @@ +#include <stdlib.h> + +__attribute__ ((noreturn)) void +_exit(int st) { while (1); } + +int +atexit(void (*f)(void)) { return 0; } diff --git a/code/fe310/gloss/sys_faccessat.c b/code/fe310/gloss/sys_faccessat.c new file mode 100644 index 0000000..873d52c --- /dev/null +++ b/code/fe310/gloss/sys_faccessat.c @@ -0,0 +1,8 @@ +#include <errno.h> + +int +_faccessat(int dirfd, const char *file, int mode, int flags) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_fork.c b/code/fe310/gloss/sys_fork.c new file mode 100644 index 0000000..64e6756 --- /dev/null +++ b/code/fe310/gloss/sys_fork.c @@ -0,0 +1,8 @@ +#include <errno.h> + +int +_fork() +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_fstat.c b/code/fe310/gloss/sys_fstat.c new file mode 100644 index 0000000..fedc289 --- /dev/null +++ b/code/fe310/gloss/sys_fstat.c @@ -0,0 +1,9 @@ +#include <errno.h> +#include <sys/stat.h> + +int +_fstat(int file, struct stat *st) +{ + errno = -ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_fstatat.c b/code/fe310/gloss/sys_fstatat.c new file mode 100644 index 0000000..f2f43bd --- /dev/null +++ b/code/fe310/gloss/sys_fstatat.c @@ -0,0 +1,9 @@ +#include <errno.h> +#include <sys/stat.h> + +int +_fstatat(int dirfd, const char *file, struct stat *st, int flags) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_ftime.c b/code/fe310/gloss/sys_ftime.c new file mode 100644 index 0000000..65c1563 --- /dev/null +++ b/code/fe310/gloss/sys_ftime.c @@ -0,0 +1,9 @@ +#include <errno.h> +#include <sys/timeb.h> + +int +_ftime(struct timeb *tp) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_getcwd.c b/code/fe310/gloss/sys_getcwd.c new file mode 100644 index 0000000..82e8404 --- /dev/null +++ b/code/fe310/gloss/sys_getcwd.c @@ -0,0 +1,8 @@ +#include <errno.h> + +char * +_getcwd(char *buf, size_t size) +{ + errno = -ENOSYS; + return NULL; +} diff --git a/code/fe310/gloss/sys_getpid.c b/code/fe310/gloss/sys_getpid.c new file mode 100644 index 0000000..589ad11 --- /dev/null +++ b/code/fe310/gloss/sys_getpid.c @@ -0,0 +1,7 @@ +#include <errno.h> + +int +_getpid() +{ + return 1; +} diff --git a/code/fe310/gloss/sys_isatty.c b/code/fe310/gloss/sys_isatty.c new file mode 100644 index 0000000..70aec43 --- /dev/null +++ b/code/fe310/gloss/sys_isatty.c @@ -0,0 +1,7 @@ +#include <unistd.h> + +int +_isatty(int fd) +{ + return ((fd == STDIN_FILENO) || (fd == STDOUT_FILENO) || (fd == STDERR_FILENO)); +} diff --git a/code/fe310/gloss/sys_kill.c b/code/fe310/gloss/sys_kill.c new file mode 100644 index 0000000..9003f26 --- /dev/null +++ b/code/fe310/gloss/sys_kill.c @@ -0,0 +1,8 @@ +#include <errno.h> + +int +_kill(int pid, int sig) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_link.c b/code/fe310/gloss/sys_link.c new file mode 100644 index 0000000..40d5912 --- /dev/null +++ b/code/fe310/gloss/sys_link.c @@ -0,0 +1,7 @@ +#include <errno.h> + +int _link(const char *old_name, const char *new_name) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_lseek.c b/code/fe310/gloss/sys_lseek.c new file mode 100644 index 0000000..d28a781 --- /dev/null +++ b/code/fe310/gloss/sys_lseek.c @@ -0,0 +1,9 @@ +#include <sys/types.h> +#include <errno.h> + +off_t +_lseek(int file, off_t ptr, int dir) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_lstat.c b/code/fe310/gloss/sys_lstat.c new file mode 100644 index 0000000..97a4585 --- /dev/null +++ b/code/fe310/gloss/sys_lstat.c @@ -0,0 +1,8 @@ +#include <errno.h> +#include <sys/stat.h> + +int _lstat(const char *file, struct stat *st) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_open.c b/code/fe310/gloss/sys_open.c new file mode 100644 index 0000000..a59f627 --- /dev/null +++ b/code/fe310/gloss/sys_open.c @@ -0,0 +1,8 @@ +#include <errno.h> + +int +_open(const char *name, int flags, int mode) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_openat.c b/code/fe310/gloss/sys_openat.c new file mode 100644 index 0000000..206de3b --- /dev/null +++ b/code/fe310/gloss/sys_openat.c @@ -0,0 +1,8 @@ +#include <errno.h> + +int +_openat(int dirfd, const char *name, int flags, int mode) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_read.c b/code/fe310/gloss/sys_read.c new file mode 100644 index 0000000..1857fc6 --- /dev/null +++ b/code/fe310/gloss/sys_read.c @@ -0,0 +1,23 @@ +#include <sys/types.h> +#include <errno.h> +#include <unistd.h> + +#include "platform.h" + +/* Read from a file. */ +ssize_t +_read(int fd, void *ptr, size_t len) +{ + if (fd != STDIN_FILENO) { + errno = ENOSYS; + return -1; + } + + char *current = ptr; + for (size_t i = 0; i < len; i++) { + volatile uint32_t r; + while ((r = UART0_REG(UART_REG_RXFIFO)) & 0x80000000); + current[i] = r & 0xFF; + } + return len; +} diff --git a/code/fe310/gloss/sys_sbrk.c b/code/fe310/gloss/sys_sbrk.c new file mode 100644 index 0000000..cc01c8f --- /dev/null +++ b/code/fe310/gloss/sys_sbrk.c @@ -0,0 +1,39 @@ +#include <sys/types.h> + +/* brk is handled entirely within the C library. This limits METAL programs that + * use the C library to be disallowed from dynamically allocating memory + * without talking to the C library, but that sounds like a sane way to go + * about it. Note that there is no error checking anywhere in this file, users + * will simply get the relevant error when actually trying to use the memory + * that's been allocated. */ +extern char metal_segment_heap_target_start; +extern char metal_segment_heap_target_end; +static char *brk = &metal_segment_heap_target_start; + +int +_brk(void *addr) +{ + brk = addr; + return 0; +} + +char * +_sbrk(ptrdiff_t incr) +{ + char *old = brk; + + /* If __heap_size == 0, we can't allocate memory on the heap */ + if(&metal_segment_heap_target_start == &metal_segment_heap_target_end) { + return (void *)-1; + } + + /* Don't move the break past the end of the heap */ + if ((brk + incr) < &metal_segment_heap_target_end) { + brk += incr; + } else { + brk = &metal_segment_heap_target_end; + return (void *)-1; + } + + return old; +} diff --git a/code/fe310/gloss/sys_stat.c b/code/fe310/gloss/sys_stat.c new file mode 100644 index 0000000..3c2e419 --- /dev/null +++ b/code/fe310/gloss/sys_stat.c @@ -0,0 +1,9 @@ +#include <errno.h> +#include <sys/stat.h> + +int +_stat(const char *file, struct stat *st) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_sysconf.c b/code/fe310/gloss/sys_sysconf.c new file mode 100644 index 0000000..452a252 --- /dev/null +++ b/code/fe310/gloss/sys_sysconf.c @@ -0,0 +1,16 @@ +#include <unistd.h> +#include <time.h> + +/* Get configurable system variables. */ + +long +_sysconf(int name) +{ + switch (name) + { + case _SC_CLK_TCK: + return CLOCKS_PER_SEC; + } + + return -1; +} diff --git a/code/fe310/gloss/sys_unlink.c b/code/fe310/gloss/sys_unlink.c new file mode 100644 index 0000000..b369d20 --- /dev/null +++ b/code/fe310/gloss/sys_unlink.c @@ -0,0 +1,8 @@ +#include <errno.h> + +int +_unlink(const char *name) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_utime.c b/code/fe310/gloss/sys_utime.c new file mode 100644 index 0000000..33d557a --- /dev/null +++ b/code/fe310/gloss/sys_utime.c @@ -0,0 +1,9 @@ +#include <errno.h> +struct utimbuf; + +int +_utime(const char *path, const struct utimbuf *times) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_wait.c b/code/fe310/gloss/sys_wait.c new file mode 100644 index 0000000..9d459f1 --- /dev/null +++ b/code/fe310/gloss/sys_wait.c @@ -0,0 +1,7 @@ +#include <errno.h> + +int _wait(int *status) +{ + errno = ENOSYS; + return -1; +} diff --git a/code/fe310/gloss/sys_write.c b/code/fe310/gloss/sys_write.c new file mode 100644 index 0000000..2095940 --- /dev/null +++ b/code/fe310/gloss/sys_write.c @@ -0,0 +1,22 @@ +#include <sys/types.h> +#include <errno.h> +#include <unistd.h> + +#include "platform.h" + +/* Write to a file. */ +ssize_t +_write(int fd, const void *ptr, size_t len) +{ + if ((fd != STDOUT_FILENO) && (fd != STDERR_FILENO)) { + errno = ENOSYS; + return -1; + } + + const char *current = ptr; + for (size_t i = 0; i < len; i++) { + while (UART0_REG(UART_REG_TXFIFO) & 0x80000000); + UART0_REG(UART_REG_TXFIFO) = current[i]; + } + return len; +} |