commit 16107e2ec3ad181f8d1905b71edf72b65487ac34 from: Benjamin Stürz date: Sun Jun 16 06:59:09 2024 UTC initial support for signals commit - c21d1989ef14647a6262e7ac4c5a2438df77abe4 commit + 16107e2ec3ad181f8d1905b71edf72b65487ac34 blob - a0baec7dd2d222cb9851c15e968d68c23e0ddede blob + 910b207a5c86bc89611cebafb24cf0fa056f2e3d --- .gitignore +++ .gitignore @@ -1,5 +1,6 @@ examples/*.elf src/syscalls.h +src/signal-bootstrap-code.h src/*.o tools/bin tools/build blob - 038e3eabdf2b72e8a0eb09c150efc3ad81ad0ebf blob + e1164a3ca8c929fc4bcd7264ad638b734d6dc10e --- Makefile +++ Makefile @@ -12,13 +12,14 @@ COPT = -g -O2 CFLAGS = ${CFLAGS_OS} ${COPT} -std=c2x -Wall -Wextra LDFLAGS = ${LDFLAGS_OS} -lpthread -OBJ = src/linurv.o src/ecall.o src/cpu.o src/exec.o -T = test +OBJ = src/linurv.o src/ecall.o src/cpu.o src/exec.o src/signal.o +T = signal PROGS = examples/test.elf \ examples/echo.elf \ examples/cat.elf \ examples/hello.elf \ - examples/asm.elf + examples/asm.elf \ + examples/signal.elf all: linurv ${PROGS} @@ -30,13 +31,13 @@ run: linurv ${PROGS} cp -f linurv rootfs/bin cp -f ${PROGS} rootfs/bin cp -f test.txt rootfs/ - ${CHROOT} rootfs /bin/linurv /bin/$T.elf + ${CHROOT} rootfs /bin/linurv -v /bin/$T.elf distclean: clean (cd tools; ${MAKE} distclean) clean: - rm -f linurv src/*.o examples/*.elf *.core src/syscalls.h + rm -f linurv src/*.o examples/*.elf *.core src/syscalls.h src/signal-bootstrap-code.h src/sbc.* rm -rf rootfs install: linurv @@ -49,12 +50,19 @@ linurv: ${OBJ} src/ecall.o: src/syscalls.h +src/cpu.o: src/signal-bootstrap-code.h + src/syscalls.h: src/syscalls.inc src/gensyscalls.sh sh src/gensyscalls.sh < src/syscalls.inc > $@ .c.o: ${CC} -c -o $@ $< ${CFLAGS} +src/signal-bootstrap-code.h: src/signal-bootstrap-code.S + ${CROSS}-as -o src/sbc.o src/signal-bootstrap-code.S + ${CROSS}-objcopy -O binary src/sbc.o src/sbc.bin + hexdump -v -e '16/1 "0x%02x," "\n"' $@ + rm -f src/sbc.* .c.elf: ${CROSS}-gcc -g -o $@ $< -Og blob - /dev/null blob + c45dfee3c45a53836ac02cd12653fa993a6c1252 (mode 644) --- /dev/null +++ examples/signal.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +void handle_signal (int sig) +{ + printf ("Signal: %d\n", sig); +} + +int main (void) +{ + struct sigaction act; + + memset (&act, 0, sizeof (act)); + act.sa_handler = handle_signal; + sigaction (SIGINT, &act, NULL); + + for (int i = 0; i < 3; ++i) { + puts ("sleeping..."); + sleep (5); + } +} blob - cf9d917af65dec853f6d7828188c8e3fe8ff08a2 blob + 684502fb6349e72eeefd9b684a72400af6d18215 --- src/cpu.c +++ src/cpu.c @@ -1,9 +1,16 @@ #include #include "linurv.h" +static volatile u64 pending_sig_handler; +static volatile int pending_sig = 0; static u64 regs[31]; u64 pc, next_pc; +__attribute__((aligned(4))) +static u8 signal_bootstrap_code[] = { +#include "signal-bootstrap-code.h" +}; + void cpu_set (size_t reg, u64 val) { if (reg > 0) @@ -213,8 +220,8 @@ void cpu_exec (u32 instr) break; case 0b011: // sd sx ("sd"); - write_u64 (b, a); - break; + write_u64 (b, a); + break; default: goto ud; } @@ -447,8 +454,17 @@ void cpu_exec (u32 instr) break; case 0b1110011: // ecall/ebreak if ((instr >> 20) & 1) { - vdebug ("ebreak"); - __builtin_trap (); + instr = read_u32 (next_pc); + if (instr == 0x40005013) { + // return from signal + vdebug ("sigret"); + next_pc = cpu_get (REG_ra); + cpu_set (REG_ra, read_u32 (cpu_get (REG_sp))); + cpu_set (REG_sp, cpu_get (REG_sp) + 8); + } else { + vdebug ("ebreak"); + __builtin_trap (); + } } else { ecall (); } @@ -458,5 +474,26 @@ void cpu_exec (u32 instr) error ("invalid instruction: %08x", instr); } pc = next_pc; + + if (pending_sig != 0) { + u64 sp; + + debug ("Entering signal. (sp=%#lx)", (long)cpu_get (REG_sp)); + cpu_set (REG_sp, cpu_get (REG_sp) - 24); + sp = cpu_get (REG_sp); + write_u64 (sp + 16, cpu_get (REG_ra)); + write_u64 (sp + 8, cpu_get (REG_t0)); + write_u64 (sp, cpu_get (REG_t1)); + cpu_set (REG_ra, pc); + cpu_set (REG_t0, pending_sig_handler); + cpu_set (REG_t1, pending_sig); + pc = (u64)signal_bootstrap_code; + pending_sig = 0; + } } +void cpu_enter_signal (int sig, u64 handler) +{ + pending_sig_handler = handler; + pending_sig = sig; +} blob - 850c540e4c5020fd1870815dda1c4fed964d743d blob + 5ce02ee3ef69ce0d1b9549ae62ac662c1fd8bbd2 --- src/ecall.c +++ src/ecall.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "syscalls.h" #include "linurv.h" @@ -48,6 +49,11 @@ struct linux_utsname { char release[65]; char version[65]; char machine[65]; +}; + +struct linux_timespec { + time_t ltv_sec; + long ltv_nsec; }; static int map_errno (int err) @@ -272,7 +278,20 @@ static int map_access (int x) o |= R_OK; return o; +} + +static void timespec_from_linux (struct timespec *out, const struct linux_timespec *in) +{ + out->tv_sec = in->ltv_sec; + out->tv_nsec = in->ltv_nsec; } +static void timespec_to_linux (struct linux_timespec *out, const struct timespec *in) +{ + out->ltv_sec = in->tv_sec; + out->ltv_nsec = in->tv_nsec; +} + +int mysigaction (int sig, const void *act, void *oact); #define ptr(T, x) ((T *)(x)) #define str(x) ptr (const char, x) @@ -590,8 +609,21 @@ void ecall (void) ret = enosys ("get_robust_limit"); break; case SYS_nanosleep: - ret = enosys ("nanosleep"); + { + const struct linux_timespec *lin = ptr (const void, a0); + struct linux_timespec *lout = ptr (void, a1); + struct timespec in, out; + + timespec_from_linux (&in, lin); + + ret = map (nanosleep (&in, lout != NULL ? &out : NULL)); + + if (lout != NULL) + timespec_to_linux (lout, &out); + + dbg ("nanosleep(%p, %p)", lin, lout); break; + } case SYS_gettimer: ret = enosys ("gettimer"); break; @@ -691,9 +723,11 @@ void ecall (void) break; case SYS_sigsuspend: ret = enosys ("sigsuspend"); + abort (); break; case SYS_sigaction: - ret = enosys ("sigaction"); + ret = map (mysigaction (i0, ptr (const void, a1), ptr (void, a2))); + dbg ("sigaction(%d, %p, %p)", i0, ptr (const void, a1), ptr (void, a2)); break; case SYS_rt_sigprocmask: ret = enosys ("rt_sigprocmask"); blob - e139c889771a6f6a503315250cf80acbad06cd75 blob + d565fc7a8b030875463a30066c3c5a63ca5fe009 --- src/linurv.h +++ src/linurv.h @@ -76,6 +76,7 @@ void ecall (void); int my_execve (const char *path, char **argv, char **envp); int is_executable (const Elf64_Ehdr *); void logger (enum log_level, const char *, int, const char *, ...); +void cpu_enter_signal (int sig, u64 handler); #define log(level, ...) logger ((level), __FILE__, __LINE__, __VA_ARGS__) #define fatal(...) log (LOG_SILENT, __VA_ARGS__) blob - /dev/null blob + 49cfdf3ad59c9937e6b42e18c60cdedaf15f2980 (mode 644) --- /dev/null +++ src/signal-bootstrap-code.S @@ -0,0 +1,41 @@ +# t0 - signal handler +# t1 - signal number + +addi sp, sp, -(8 * 14) +sd t2, 104(sp) +sd t3, 96(sp) +sd t4, 88(sp) +sd t5, 80(sp) +sd t6, 72(sp) +sd a0, 64(sp) +sd a1, 56(sp) +sd a2, 48(sp) +sd a3, 40(sp) +sd a4, 32(sp) +sd a5, 24(sp) +sd a6, 16(sp) +sd a7, 8(sp) +sd ra, 0(sp) + +mv a0, t1 +jalr t0 + +ld ra, 0(sp) +ld a7, 8(sp) +ld a6, 16(sp) +ld a5, 24(sp) +ld a4, 32(sp) +ld a3, 40(sp) +ld a2, 48(sp) +ld a1, 56(sp) +ld a0, 64(sp) +ld t6, 72(sp) +ld t5, 80(sp) +ld t4, 88(sp) +ld t3, 96(sp) +ld t2, 104(sp) +ld t1, 112(sp) +ld t0, 120(sp) +addi sp, sp, (8 * 16) +ebreak +srai x0, x0, 0 blob - /dev/null blob + 41f75f230737220be38dd77a1a156f1afa869114 (mode 644) --- /dev/null +++ src/signal.c @@ -0,0 +1,314 @@ +#include +#include +#include +#include +#include +#include "linurv.h" + +#define LSA_NOCLDSTOP 1 +#define LSA_NOCLDWAIT 2 +#define LSA_SIGINFO 4 +#define LSA_ONSTACK 0x08000000 +#define LSA_RESTART 0x10000000 +#define LSA_NODEFER 0x40000000 +#define LSA_RESETHAND 0x80000000 + +#define LSIG_ERR ((void (*)(int))-1) +#define LSIG_DFL ((void (*)(int)) 0) +#define LSIG_IGN ((void (*)(int)) 1) + +#define LSIGHUP 1 +#define LSIGINT 2 +#define LSIGQUIT 3 +#define LSIGILL 4 +#define LSIGTRAP 5 +#define LSIGABRT 6 +#define LSIGIOT SIGABRT +#define LSIGBUS 7 +#define LSIGFPE 8 +#define LSIGKILL 9 +#define LSIGUSR1 10 +#define LSIGSEGV 11 +#define LSIGUSR2 12 +#define LSIGPIPE 13 +#define LSIGALRM 14 +#define LSIGTERM 15 +#define LSIGSTKFLT 16 +#define LSIGCHLD 17 +#define LSIGCONT 18 +#define LSIGSTOP 19 +#define LSIGTSTP 20 +#define LSIGTTIN 21 +#define LSIGTTOU 22 +#define LSIGURG 23 +#define LSIGXCPU 24 +#define LSIGXFSZ 25 +#define LSIGVTALRM 26 +#define LSIGPROF 27 +#define LSIGWINCH 28 +#define LSIGIO 29 +#define LSIGPOLL SIGIO +#define LSIGPWR 30 +#define LSIGSYS 31 +#define LSIGUNUSED SIGSYS +#define L_NSIG 32 + +typedef int linux_pid_t; +typedef unsigned linux_uid_t; +typedef long linux_clock_t; + +typedef struct { + unsigned long bits[128 / sizeof (uint64_t)]; +} linux_sigset_t; + +union linux_sigval { + int sival_int; + void *sival_ptr; +}; + +typedef struct { + int32_t lsi_signo, lsi_errno, lsi_code; + union { + char __pad[128 - 2*sizeof(int32_t) - sizeof(int64_t)]; + struct { + union { + struct { + linux_pid_t lsi_pid; + linux_uid_t lsi_uid; + } __piduid; + struct { + int32_t lsi_timerid; + int32_t lsi_overrun; + } __timer; + } __first; + union { + union linux_sigval lsi_value; + struct { + int32_t lsi_status; + linux_clock_t lsi_utime, lsi_stime; + } __sigchld; + } __second; + } __si_common; + struct { + void *lsi_addr; + int16_t lsi_addr_lsb; + union { + struct { + void *lsi_lower; + void *lsi_upper; + } __addr_bnd; + uint32_t lsi_pkey; + } __first; + } __sigfault; + struct { + int64_t lsi_band; + int32_t lsi_fd; + } __sigpoll; + struct { + void *lsi_call_addr; + int32_t lsi_syscall; + uint32_t lsi_arch; + } __sigsys; + } __si_fields; +} linux_siginfo_t; + +struct linux_sigaction { + union { + void (*lsa_handler)(int); + void (*lsa_sigaction)(int, linux_siginfo_t *, void *); + } __lsa_handler; + linux_sigset_t lsa_mask; + int lsa_flags; + void *lsa_restorer; +}; + +struct signal_handler { + uint64_t handler; +}; + +static struct signal_handler signals[L_NSIG]; + +static void signal_handler (int sig); + +static void sigset_from_linux (sigset_t *out, const linux_sigset_t *in) +{ + (void)out; + (void)in; + // TODO +} + +static void sigset_to_linux (linux_sigset_t *out, const sigset_t *in) +{ + (void)out; + (void)in; + // TODO +} + +static int sa_flags_from_linux (int in) +{ + // TODO + return in; +} + +static int sa_flags_to_linux (int in) +{ + // TODO + return in; +} + +static void sigaction_from_linux (int lsig, struct sigaction *out, const struct linux_sigaction *in) +{ + void (*h)(int) = in->__lsa_handler.lsa_handler; + + if (h == LSIG_ERR) { + out->sa_handler = SIG_ERR; + } else if (h == LSIG_DFL) { + out->sa_handler = SIG_DFL; + } else if (h == LSIG_IGN) { + out->sa_handler = SIG_IGN; + } else { + signals[lsig].handler = (uint64_t)in->__lsa_handler.lsa_handler; + out->sa_handler = signal_handler; + } + + sigset_from_linux (&out->sa_mask, &in->lsa_mask); + out->sa_flags = sa_flags_from_linux (in->lsa_flags); +} + +static void sigaction_to_linux (int lsig, struct linux_sigaction *out, const struct sigaction *in) +{ + void (*h)(int) = in->sa_handler; + + if (h == SIG_ERR) { + out->__lsa_handler.lsa_handler = LSIG_ERR; + } else if (h == SIG_DFL) { + out->__lsa_handler.lsa_handler = LSIG_DFL; + } else if (h == SIG_IGN) { + out->__lsa_handler.lsa_handler = LSIG_IGN; + } else { + out->__lsa_handler.lsa_handler = (void *)signals[lsig].handler; + } + + sigset_to_linux (&out->lsa_mask, &in->sa_mask); + out->lsa_flags = sa_flags_to_linux (in->sa_flags); + out->lsa_restorer = NULL; +} + +static int signum_from_linux (int sig) +{ + static const int signals[L_NSIG] = { + [LSIGHUP] = SIGHUP, + [LSIGINT] = SIGINT, + [LSIGQUIT] = SIGQUIT, + [LSIGILL] = SIGILL, + [LSIGTRAP] = SIGTRAP, + [LSIGABRT] = SIGABRT, + [LSIGBUS] = SIGBUS, + [LSIGFPE] = SIGFPE, + [LSIGKILL] = SIGKILL, + [LSIGUSR1] = SIGUSR1, + [LSIGSEGV] = SIGSEGV, + [LSIGUSR2] = SIGUSR2, + [LSIGPIPE] = SIGPIPE, + [LSIGALRM] = SIGALRM, + [LSIGTERM] = SIGTERM, + [LSIGSTKFLT] = SIGSEGV, + [LSIGCHLD] = SIGCHLD, + [LSIGCONT] = SIGCONT, + [LSIGSTOP] = SIGSTOP, + [LSIGTSTP] = SIGTSTP, + [LSIGTTIN] = SIGTTIN, + [LSIGTTOU] = SIGTTOU, + [LSIGURG] = SIGURG, + [LSIGXCPU] = SIGXCPU, + [LSIGXFSZ] = SIGXFSZ, + [LSIGVTALRM] = SIGVTALRM, + [LSIGPROF] = SIGPROF, + [LSIGWINCH] = SIGWINCH, + [LSIGIO] = SIGIO, + [LSIGPWR] = SIGIO, + [LSIGSYS] = SIGSYS, + }; + + return sig >= 0 && sig <= L_NSIG ? signals[sig] : 0; +} + +static int signum_to_linux (int sig) +{ + static const int signals[] = { + [SIGHUP] = LSIGHUP, + [SIGINT] = LSIGINT, + [SIGQUIT] = LSIGQUIT, + [SIGILL] = LSIGILL, + [SIGTRAP] = LSIGTRAP, + [SIGABRT] = LSIGABRT, + [SIGBUS] = LSIGBUS, + [SIGFPE] = LSIGFPE, + [SIGKILL] = LSIGKILL, + [SIGUSR1] = LSIGUSR1, + [SIGSEGV] = LSIGSEGV, + [SIGUSR2] = LSIGUSR2, + [SIGPIPE] = LSIGPIPE, + [SIGALRM] = LSIGALRM, + [SIGTERM] = LSIGTERM, + //[SIGSTKFLT] = LSIGSTKFLT, + [SIGCHLD] = LSIGCHLD, + [SIGCONT] = LSIGCONT, + [SIGSTOP] = LSIGSTOP, + [SIGTSTP] = LSIGTSTP, + [SIGTTIN] = LSIGTTIN, + [SIGTTOU] = LSIGTTOU, + [SIGURG] = LSIGURG, + [SIGXCPU] = LSIGXCPU, + [SIGXFSZ] = LSIGXFSZ, + [SIGVTALRM] = LSIGVTALRM, + [SIGPROF] = LSIGPROF, + [SIGWINCH] = LSIGWINCH, + [SIGIO] = LSIGIO, + //[SIGPWR] = LSIGPWR, + [SIGSYS] = LSIGSYS, + }; + + return signals[sig]; +} + +// TODO: mask signals +static void signal_handler (int sig) +{ + int lsig; + + lsig = signum_to_linux (sig); + + debug ("Received signal %d (%d).", sig, lsig); + + cpu_enter_signal (lsig, signals[sig].handler); +} + +int mysigaction (int lsig, const void *xact, void *xoact) +{ + const struct linux_sigaction *lact = xact; + struct linux_sigaction *loact = xoact; + struct sigaction act, oact; + int sig, ret; + + + if ((lact->lsa_flags & LSA_SIGINFO) == LSA_SIGINFO) { + error ("sigaction() with SA_SIGINFO not supported"); + errno = ENOSYS; + return -1; + } + + sig = signum_from_linux (lsig); + + debug ("Registering signal %d (%d).", lsig, sig); + + if (lact != NULL) + sigaction_from_linux (lsig, &act, lact); + + ret = sigaction (sig, lact != NULL ? &act : NULL, loact != NULL ? &oact : NULL); + + if (loact != NULL) + sigaction_to_linux (lsig, loact, &oact); + + return ret; +}