Commit Diff


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"' <src/sbc.bin  >$@
+	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 <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+
+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 <inttypes.h>
 #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 <fcntl.h>
 #include <sched.h>
 #include <errno.h>
+#include <time.h>
 #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 <signal.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#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;
+}