// An extremely minimalist syscalls.c for newlib
// Based on riscv newlib libgloss/riscv/sys_*.c
//
// Copyright 2019 Clifford Wolf
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

#define UNIMPL_FUNC(_f) ".globl " #_f "\n.type " #_f ", @function\n" #_f ":\n"
#define STDOUT_ADDR 0x1a10f000
#define EXIT_CODE_ADDR 0x1a104000

/* clang-format off */
asm (
	".text\n"
	".align 2\n"
	UNIMPL_FUNC(_open)
	UNIMPL_FUNC(_openat)
	UNIMPL_FUNC(_lseek)
	UNIMPL_FUNC(_stat)
	UNIMPL_FUNC(_lstat)
	UNIMPL_FUNC(_fstatat)
	UNIMPL_FUNC(_isatty)
	UNIMPL_FUNC(_access)
	UNIMPL_FUNC(_faccessat)
	UNIMPL_FUNC(_link)
	UNIMPL_FUNC(_unlink)
	UNIMPL_FUNC(_execve)
	UNIMPL_FUNC(_getpid)
	UNIMPL_FUNC(_fork)
	UNIMPL_FUNC(_kill)
	UNIMPL_FUNC(_wait)
	UNIMPL_FUNC(_times)
	UNIMPL_FUNC(_gettimeofday)
	UNIMPL_FUNC(_ftime)
	UNIMPL_FUNC(_utime)
	UNIMPL_FUNC(_chown)
	UNIMPL_FUNC(_chmod)
	UNIMPL_FUNC(_chdir)
	UNIMPL_FUNC(_getcwd)
	UNIMPL_FUNC(_sysconf)
	"j unimplemented_syscall\n"
);
/* clang-format on */

void unimplemented_syscall()
{
    const char *p = "Unimplemented system call called!\n";
    while (*p)
        *(volatile int *)STDOUT_ADDR = *(p++);
    asm volatile("ebreak");
    __builtin_unreachable();
}

ssize_t _read(int file, void *ptr, size_t len)
{
    // always EOF
    return 0;
}

ssize_t _write(int file, const void *ptr, size_t len)
{
    const void *eptr = ptr + len;
    while (ptr != eptr)
        *(volatile int *)STDOUT_ADDR = *(char *)(ptr++);
    return len;
}

int _close(int file)
{
    // close is called before _exit()
    return 0;
}

int _fstat(int file, struct stat *st)
{
    // fstat is called during libc startup
    errno = ENOENT;
    return -1;
}

void *_sbrk(ptrdiff_t incr)
{
    extern unsigned char _end[]; // Defined by linker
    static unsigned long heap_end;

    if (heap_end == 0)
        heap_end = (long)_end;

    heap_end += incr;
    return (void *)(heap_end - incr);
}

void _exit(int exit_status)
{
    *(volatile int *)EXIT_CODE_ADDR = exit_status == 0 ? 0xf00d : exit_status;
}
