632 lines
18 KiB
C
632 lines
18 KiB
C
/* Copyright 2018 Canaan Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/* Enable kernel-mode log API */
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/unistd.h>
|
|
#include <machine/syscall.h>
|
|
#include <stdbool.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "syscalls.h"
|
|
#include "atomic.h"
|
|
#include "clint.h"
|
|
#include "fpioa.h"
|
|
#include "interrupt.h"
|
|
#include "sysctl.h"
|
|
#include "uarths.h"
|
|
#include "util.h"
|
|
#include "syslog.h"
|
|
#include "dump.h"
|
|
|
|
/**
|
|
* @note System call list
|
|
*
|
|
* See also riscv-newlib/libgloss/riscv/syscalls.c
|
|
*
|
|
* | System call | Number |
|
|
* |------------------|--------|
|
|
* | SYS_exit | 93 |
|
|
* | SYS_exit_group | 94 |
|
|
* | SYS_getpid | 172 |
|
|
* | SYS_kill | 129 |
|
|
* | SYS_read | 63 |
|
|
* | SYS_write | 64 |
|
|
* | SYS_open | 1024 |
|
|
* | SYS_openat | 56 |
|
|
* | SYS_close | 57 |
|
|
* | SYS_lseek | 62 |
|
|
* | SYS_brk | 214 |
|
|
* | SYS_link | 1025 |
|
|
* | SYS_unlink | 1026 |
|
|
* | SYS_mkdir | 1030 |
|
|
* | SYS_chdir | 49 |
|
|
* | SYS_getcwd | 17 |
|
|
* | SYS_stat | 1038 |
|
|
* | SYS_fstat | 80 |
|
|
* | SYS_lstat | 1039 |
|
|
* | SYS_fstatat | 79 |
|
|
* | SYS_access | 1033 |
|
|
* | SYS_faccessat | 48 |
|
|
* | SYS_pread | 67 |
|
|
* | SYS_pwrite | 68 |
|
|
* | SYS_uname | 160 |
|
|
* | SYS_getuid | 174 |
|
|
* | SYS_geteuid | 175 |
|
|
* | SYS_getgid | 176 |
|
|
* | SYS_getegid | 177 |
|
|
* | SYS_mmap | 222 |
|
|
* | SYS_munmap | 215 |
|
|
* | SYS_mremap | 216 |
|
|
* | SYS_time | 1062 |
|
|
* | SYS_getmainvars | 2011 |
|
|
* | SYS_rt_sigaction | 134 |
|
|
* | SYS_writev | 66 |
|
|
* | SYS_gettimeofday | 169 |
|
|
* | SYS_times | 153 |
|
|
* | SYS_fcntl | 25 |
|
|
* | SYS_getdents | 61 |
|
|
* | SYS_dup | 23 |
|
|
*
|
|
*/
|
|
|
|
#ifndef UNUSED
|
|
#define UNUSED(x) (void)(x)
|
|
#endif
|
|
|
|
static const char *TAG = "SYSCALL";
|
|
|
|
extern char _heap_start[];
|
|
extern char _heap_end[];
|
|
char *_heap_cur = &_heap_start[0];
|
|
|
|
|
|
void __attribute__((noreturn)) sys_exit(int code)
|
|
{
|
|
/* Read core id */
|
|
unsigned long core_id = current_coreid();
|
|
/* First print some diagnostic information. */
|
|
LOGW(TAG, "sys_exit called by core %ld with 0x%lx\r\n", core_id, (uint64_t)code);
|
|
while (1)
|
|
continue;
|
|
}
|
|
|
|
static int sys_nosys(long a0, long a1, long a2, long a3, long a4, long a5, unsigned long n)
|
|
{
|
|
UNUSED(a3);
|
|
UNUSED(a4);
|
|
UNUSED(a5);
|
|
|
|
LOGE(TAG, "Unsupported syscall %ld: a0=%lx, a1=%lx, a2=%lx!\r\n", n, a0, a1, a2);
|
|
while (1)
|
|
continue;
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int sys_success(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static size_t sys_brk(size_t pos)
|
|
{
|
|
uintptr_t res = 0;
|
|
/**
|
|
* brk() sets the end of the data segment to the value
|
|
* specified by addr, when that value is reasonable, the system
|
|
* has enough memory, and the process does not exceed its
|
|
* maximum data size.
|
|
*
|
|
* sbrk() increments the program's data space by increment
|
|
* bytes. Calling sbrk() with an increment of 0 can be used to
|
|
* find the current location of the program break.
|
|
*
|
|
* uintptr_t brk(uintptr_t ptr);
|
|
*
|
|
* IN : regs[10] = ptr
|
|
* OUT: regs[10] = ptr
|
|
*/
|
|
|
|
/**
|
|
* First call: Initialization brk pointer. newlib will pass
|
|
* ptr = 0 when it is first called. In this case the address
|
|
* _heap_start will be return.
|
|
*
|
|
* Call again: Adjust brk pointer. The ptr never equal with
|
|
* 0. If ptr is below _heap_end, then allocate memory.
|
|
* Otherwise throw out of memory error, return -1.
|
|
*/
|
|
|
|
if (pos)
|
|
{
|
|
/* Call again */
|
|
if ((uintptr_t)pos > (uintptr_t)&_heap_end[0])
|
|
{
|
|
/* Memory out, return -ENOMEM */
|
|
LOGE(TAG, "Out of memory\r\n");
|
|
res = -ENOMEM;
|
|
}
|
|
else
|
|
{
|
|
/* Adjust brk pointer. */
|
|
_heap_cur = (char *)(uintptr_t)pos;
|
|
/* Return current address. */
|
|
res = (uintptr_t)_heap_cur;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* First call, return initial address */
|
|
res = (uintptr_t)&_heap_start[0];
|
|
}
|
|
return (size_t)res;
|
|
}
|
|
|
|
static ssize_t sys_write(int file, const void *ptr, size_t len)
|
|
{
|
|
ssize_t res = -EBADF;
|
|
|
|
/**
|
|
* Write to a file.
|
|
*
|
|
* ssize_t write(int file, const void *ptr, size_t len)
|
|
*
|
|
* IN : regs[10] = file, regs[11] = ptr, regs[12] = len
|
|
* OUT: regs[10] = len
|
|
*/
|
|
|
|
/* Get size to write */
|
|
register size_t length = len;
|
|
/* Get data pointer */
|
|
register char *data = (char *)ptr;
|
|
|
|
if (STDOUT_FILENO == file || STDERR_FILENO == file)
|
|
{
|
|
/* Write data */
|
|
while (length-- > 0 && *data != 0)
|
|
uarths_putchar(*(data++));
|
|
|
|
/* Return the actual size written */
|
|
res = len;
|
|
}
|
|
else
|
|
{
|
|
/* Not support yet */
|
|
res = -ENOSYS;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int sys_fstat(int file, struct stat *st)
|
|
{
|
|
int res = -EBADF;
|
|
|
|
/**
|
|
* Status of an open file. The sys/stat.h header file required
|
|
* is
|
|
* distributed in the include subdirectory for this C library.
|
|
*
|
|
* int fstat(int file, struct stat* st)
|
|
*
|
|
* IN : regs[10] = file, regs[11] = st
|
|
* OUT: regs[10] = Upon successful completion, 0 shall be
|
|
* returned.
|
|
* Otherwise, -1 shall be returned and errno set to indicate
|
|
* the error.
|
|
*/
|
|
|
|
UNUSED(file);
|
|
|
|
if (st != NULL)
|
|
memset(st, 0, sizeof(struct stat));
|
|
/* Return the result */
|
|
res = -ENOSYS;
|
|
/**
|
|
* Note: This value will return to syscall wrapper, syscall
|
|
* wrapper will set errno to ENOSYS and return -1
|
|
*/
|
|
|
|
return res;
|
|
}
|
|
|
|
static int sys_close(int file)
|
|
{
|
|
int res = -EBADF;
|
|
|
|
/**
|
|
* Close a file.
|
|
*
|
|
* int close(int file)
|
|
*
|
|
* IN : regs[10] = file
|
|
* OUT: regs[10] = Upon successful completion, 0 shall be
|
|
* returned.
|
|
* Otherwise, -1 shall be returned and errno set to indicate
|
|
* the error.
|
|
*/
|
|
|
|
UNUSED(file);
|
|
/* Return the result */
|
|
res = 0;
|
|
return res;
|
|
}
|
|
|
|
static int sys_gettimeofday(struct timeval *tp, void *tzp)
|
|
{
|
|
/**
|
|
* Get the current time. Only relatively correct.
|
|
*
|
|
* int gettimeofday(struct timeval *tp, void *tzp)
|
|
*
|
|
* IN : regs[10] = tp
|
|
* OUT: regs[10] = Upon successful completion, 0 shall be
|
|
* returned.
|
|
* Otherwise, -1 shall be returned and errno set to indicate
|
|
* the error.
|
|
*/
|
|
UNUSED(tzp);
|
|
|
|
if (tp != NULL)
|
|
{
|
|
uint64_t clint_usec = clint->mtime / (sysctl_clock_get_freq(SYSCTL_CLOCK_CPU) / CLINT_CLOCK_DIV / 1000000UL);
|
|
|
|
tp->tv_sec = clint_usec / 1000000UL;
|
|
tp->tv_usec = clint_usec % 1000000UL;
|
|
}
|
|
/* Return the result */
|
|
return 0;
|
|
}
|
|
|
|
uintptr_t __attribute__((weak))
|
|
handle_ecall(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
|
|
{
|
|
UNUSED(cause);
|
|
UNUSED(fregs);
|
|
enum syscall_id_e
|
|
{
|
|
SYS_ID_NOSYS,
|
|
SYS_ID_SUCCESS,
|
|
SYS_ID_EXIT,
|
|
SYS_ID_BRK,
|
|
SYS_ID_WRITE,
|
|
SYS_ID_FSTAT,
|
|
SYS_ID_CLOSE,
|
|
SYS_ID_GETTIMEOFDAY,
|
|
SYS_ID_MAX
|
|
};
|
|
|
|
static uintptr_t (* const syscall_table[])(long a0, long a1, long a2, long a3, long a4, long a5, unsigned long n) =
|
|
{
|
|
[SYS_ID_NOSYS] = (void *)sys_nosys,
|
|
[SYS_ID_SUCCESS] = (void *)sys_success,
|
|
[SYS_ID_EXIT] = (void *)sys_exit,
|
|
[SYS_ID_BRK] = (void *)sys_brk,
|
|
[SYS_ID_WRITE] = (void *)sys_write,
|
|
[SYS_ID_FSTAT] = (void *)sys_fstat,
|
|
[SYS_ID_CLOSE] = (void *)sys_close,
|
|
[SYS_ID_GETTIMEOFDAY] = (void *)sys_gettimeofday,
|
|
};
|
|
|
|
#if defined(__GNUC__)
|
|
#pragma GCC diagnostic ignored "-Woverride-init"
|
|
#endif
|
|
static const uint8_t syscall_id_table[0x100] =
|
|
{
|
|
[0x00 ... 0xFF] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_exit] = SYS_ID_EXIT,
|
|
[0xFF & SYS_exit_group] = SYS_ID_EXIT,
|
|
[0xFF & SYS_getpid] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_kill] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_read] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_write] = SYS_ID_WRITE,
|
|
[0xFF & SYS_open] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_openat] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_close] = SYS_ID_CLOSE,
|
|
[0xFF & SYS_lseek] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_brk] = SYS_ID_BRK,
|
|
[0xFF & SYS_link] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_unlink] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_mkdir] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_chdir] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_getcwd] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_stat] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_fstat] = SYS_ID_FSTAT,
|
|
[0xFF & SYS_lstat] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_fstatat] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_access] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_faccessat] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_pread] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_pwrite] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_uname] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_getuid] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_geteuid] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_getgid] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_getegid] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_mmap] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_munmap] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_mremap] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_time] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_getmainvars] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_rt_sigaction] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_writev] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_gettimeofday] = SYS_ID_GETTIMEOFDAY,
|
|
[0xFF & SYS_times] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_fcntl] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_getdents] = SYS_ID_NOSYS,
|
|
[0xFF & SYS_dup] = SYS_ID_NOSYS,
|
|
};
|
|
#if defined(__GNUC__)
|
|
#pragma GCC diagnostic warning "-Woverride-init"
|
|
#endif
|
|
|
|
regs[10] = syscall_table[syscall_id_table[0xFF & regs[17]]]
|
|
(
|
|
regs[10], /* a0 */
|
|
regs[11], /* a1 */
|
|
regs[12], /* a2 */
|
|
regs[13], /* a3 */
|
|
regs[14], /* a4 */
|
|
regs[15], /* a5 */
|
|
regs[17] /* n */
|
|
);
|
|
|
|
return epc + 4;
|
|
}
|
|
|
|
uintptr_t __attribute__((weak, alias("handle_ecall")))
|
|
handle_ecall_u(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32]);
|
|
|
|
uintptr_t __attribute__((weak, alias("handle_ecall")))
|
|
handle_ecall_h(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32]);
|
|
|
|
uintptr_t __attribute__((weak, alias("handle_ecall")))
|
|
handle_ecall_s(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32]);
|
|
|
|
uintptr_t __attribute__((weak, alias("handle_ecall")))
|
|
handle_ecall_m(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32]);
|
|
|
|
uintptr_t __attribute__((weak))
|
|
handle_misaligned_fetch(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
|
|
{
|
|
dump_core("misaligned fetch", cause, epc, regs, fregs);
|
|
sys_exit(1337);
|
|
return epc;
|
|
}
|
|
|
|
uintptr_t __attribute__((weak))
|
|
handle_fault_fetch(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
|
|
{
|
|
dump_core("fault fetch", cause, epc, regs, fregs);
|
|
sys_exit(1337);
|
|
return epc;
|
|
}
|
|
|
|
uintptr_t __attribute__((weak))
|
|
handle_illegal_instruction(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
|
|
{
|
|
dump_core("illegal instruction", cause, epc, regs, fregs);
|
|
sys_exit(1337);
|
|
return epc;
|
|
}
|
|
|
|
uintptr_t __attribute__((weak))
|
|
handle_breakpoint(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
|
|
{
|
|
dump_core("breakpoint", cause, epc, regs, fregs);
|
|
sys_exit(1337);
|
|
return epc;
|
|
}
|
|
|
|
uintptr_t __attribute__((weak))
|
|
handle_misaligned_load(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
|
|
{
|
|
/* notice this function only support 16bit or 32bit instruction */
|
|
|
|
bool compressed = (*(unsigned short *)epc & 3) != 3;
|
|
bool fpu = 0; /* load to fpu ? */
|
|
uintptr_t addr = 0; /* src addr */
|
|
uint8_t src = 0; /* src register */
|
|
uint8_t dst = 0; /* dst register */
|
|
uint8_t len = 0; /* data length */
|
|
int offset = 0; /* addr offset to addr in reg */
|
|
bool unsigned_ = 0; /* unsigned */
|
|
uint64_t data_load = 0;/* real data load */
|
|
|
|
if (compressed)
|
|
{
|
|
/* compressed instruction should not get this fault. */
|
|
goto on_error;
|
|
}
|
|
else
|
|
{
|
|
uint32_t instruct = *(uint32_t *)epc;
|
|
uint8_t opcode = instruct&0x7F;
|
|
|
|
dst = (instruct >> 7)&0x1F;
|
|
len = (instruct >> 12)&3;
|
|
unsigned_ = (instruct >> 14)&1;
|
|
src = (instruct >> 15)&0x1F;
|
|
offset = (instruct >> 20);
|
|
len = 1 << len;
|
|
switch (opcode)
|
|
{
|
|
case 3:/* load */
|
|
break;
|
|
case 7:/* fpu load */
|
|
fpu = 1;
|
|
break;
|
|
default:
|
|
goto on_error;
|
|
}
|
|
}
|
|
|
|
if (offset >> 11)
|
|
offset = -((offset & 0x3FF) + 1);
|
|
|
|
addr = (uint64_t)((uint64_t)regs[src] + offset);
|
|
|
|
for (int i = 0; i < len; ++i)
|
|
data_load |= ((uint64_t)*((uint8_t *)addr + i)) << (8 * i);
|
|
|
|
|
|
if (!unsigned_ & !fpu)
|
|
{
|
|
/* adjust sign */
|
|
switch (len)
|
|
{
|
|
case 1:
|
|
data_load = (uint64_t)(int64_t)((int8_t)data_load);
|
|
break;
|
|
case 2:
|
|
data_load = (uint64_t)(int64_t)((int16_t)data_load);
|
|
break;
|
|
case 4:
|
|
data_load = (uint64_t)(int64_t)((int32_t)data_load);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fpu)
|
|
fregs[dst] = data_load;
|
|
else
|
|
regs[dst] = data_load;
|
|
|
|
LOGV(TAG, "misaligned load recovered at %08lx. len:%02d,addr:%08lx,reg:%02d,data:%016lx,signed:%1d,float:%1d", (uint64_t)epc, len, (uint64_t)addr, dst, data_load, !unsigned_, fpu);
|
|
|
|
return epc + (compressed ? 2 : 4);
|
|
on_error:
|
|
dump_core("misaligned load", cause, epc, regs, fregs);
|
|
sys_exit(1337);
|
|
return epc;
|
|
}
|
|
|
|
uintptr_t __attribute__((weak))
|
|
handle_fault_load(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
|
|
{
|
|
dump_core("fault load", cause, epc, regs, fregs);
|
|
sys_exit(1337);
|
|
return epc;
|
|
}
|
|
|
|
uintptr_t __attribute__((weak))
|
|
handle_misaligned_store(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
|
|
{
|
|
/* notice this function only support 16bit or 32bit instruction */
|
|
|
|
bool compressed = (*(unsigned short *)epc & 3) != 3;
|
|
bool fpu = 0; /* store to fpu*/
|
|
uintptr_t addr = 0; /* src addr*/
|
|
uint8_t src = 0; /* src register*/
|
|
uint8_t dst = 0; /* dst register*/
|
|
uint8_t len = 0; /* data length*/
|
|
int offset = 0; /* addr offset to addr in reg*/
|
|
uint64_t data_store = 0;/* real data store*/
|
|
|
|
if (compressed)
|
|
{
|
|
/* compressed instruction should not get this fault. */
|
|
goto on_error;
|
|
}
|
|
else
|
|
{
|
|
uint32_t instruct = *(uint32_t *)epc;
|
|
uint8_t opcode = instruct&0x7F;
|
|
|
|
len = (instruct >> 12)&7;
|
|
dst = (instruct >> 15)&0x1F;
|
|
src = (instruct >> 20)&0x1F;
|
|
offset = ((instruct >> 7)&0x1F) | ((instruct >> 20)&0xFE0);
|
|
len = 1 << len;
|
|
switch (opcode)
|
|
{
|
|
case 0x23:/* store */
|
|
break;
|
|
case 0x27:/* fpu store */
|
|
fpu = 1;
|
|
break;
|
|
default:
|
|
goto on_error;
|
|
}
|
|
}
|
|
|
|
if (offset >> 11)
|
|
offset = -((offset & 0x3FF) + 1);
|
|
|
|
addr = (uint64_t)((uint64_t)regs[dst] + offset);
|
|
|
|
|
|
if (fpu)
|
|
data_store = fregs[src];
|
|
else
|
|
data_store = regs[src];
|
|
|
|
for (int i = 0; i < len; ++i)
|
|
*((uint8_t *)addr + i) = (data_store >> (i*8)) & 0xFF;
|
|
|
|
LOGV(TAG, "misaligned store recovered at %08lx. len:%02d,addr:%08lx,reg:%02d,data:%016lx,float:%1d", (uint64_t)epc, len, (uint64_t)addr, src, data_store, fpu);
|
|
|
|
return epc + (compressed ? 2 : 4);
|
|
on_error:
|
|
dump_core("misaligned store", cause, epc, regs, fregs);
|
|
sys_exit(1337);
|
|
return epc;
|
|
}
|
|
|
|
uintptr_t __attribute__((weak))
|
|
handle_fault_store(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
|
|
{
|
|
dump_core("fault store", cause, epc, regs, fregs);
|
|
sys_exit(1337);
|
|
return epc;
|
|
}
|
|
|
|
uintptr_t handle_syscall(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
|
|
{
|
|
|
|
static uintptr_t (* const cause_table[])(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32]) =
|
|
{
|
|
[CAUSE_MISALIGNED_FETCH] = handle_misaligned_fetch,
|
|
[CAUSE_FAULT_FETCH] = handle_fault_fetch,
|
|
[CAUSE_ILLEGAL_INSTRUCTION] = handle_illegal_instruction,
|
|
[CAUSE_BREAKPOINT] = handle_breakpoint,
|
|
[CAUSE_MISALIGNED_LOAD] = handle_misaligned_load,
|
|
[CAUSE_FAULT_LOAD] = handle_fault_load,
|
|
[CAUSE_MISALIGNED_STORE] = handle_misaligned_store,
|
|
[CAUSE_FAULT_STORE] = handle_fault_store,
|
|
[CAUSE_USER_ECALL] = handle_ecall_u,
|
|
[CAUSE_SUPERVISOR_ECALL] = handle_ecall_h,
|
|
[CAUSE_HYPERVISOR_ECALL] = handle_ecall_s,
|
|
[CAUSE_MACHINE_ECALL] = handle_ecall_m,
|
|
};
|
|
|
|
return cause_table[cause](cause, epc, regs, fregs);
|
|
}
|
|
|
|
size_t get_free_heap_size(void)
|
|
{
|
|
return (size_t)(&_heap_end[0] - _heap_cur);
|
|
}
|
|
|