AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
signal.c
Go to the documentation of this file.
1
27
28#include <core/system.h>
29#include <core/arch.h>
30#include <core/string.h>
31#include <cpu/cpu.h>
32#include <ds/hashmap.h>
33#include <mm/mm.h>
34#include <mm/pmap.h>
35#include <mm/vm.h>
36#include <sys/proc.h>
37#include <sys/sched.h>
38#include <sys/signal.h>
39#include <sys/uaccess.h>
40
41#include <aquila/syscall.h>
42
43#if ARCH_BITS==32
44#define REG_IP(r) ((r)->eip)
45#define REG_SP(r) ((r)->esp)
46#define REG_AX(r) ((r)->eax)
47#define REG_FLAGS(r) ((r)->eflags)
48#else
49#define REG_IP(r) ((r)->rip)
50#define REG_SP(r) ((r)->rsp)
51#define REG_AX(r) ((r)->rax)
52#define REG_FLAGS(r) ((r)->rflags)
53#endif
54
55/* the EFLAGS bits that user code may set: the arithmetic ones, TF, DF,
56 * AC and ID (not IF, IOPL, NT, VM, ...) */
57#define USER_FLAGS 0x00240DD5UL
58
60struct sigframe {
61 struct aquila_siginfo info;
62 sigset_t mask; /* the mask that sigreturn restores */
63 uint32_t fpu; /* fxsave below has the thread's FPU state */
64 struct x86_regs regs; /* the interrupted context */
66} __aligned(16);
67
68/* On i386, below the frame: the return address and the handler's
69 * arguments (signal, siginfo, ucontext), padded so that the handler is
70 * entered as a call would enter it (the stack 16-byte aligned before the
71 * call). */
72#define I386_ARGS 20
73
74/*
75 * The sigcode page's code: mov $SYS_sigreturn, %eax; int $0x80; ud2
76 */
77static const uint8_t sigcode[] = {
78 0xB8, SYS_sigreturn & 0xff, (SYS_sigreturn >> 8) & 0xff, 0, 0,
79 0xCD, 0x80,
80 0x0F, 0x0B,
81};
82
84static int vm_page_eq(void *a, void *b)
85{
86 return ((struct vm_page *) a)->off == *(size_t *) b;
87}
88
91static struct vm_page *sigcode_in(struct vm_object *vm_object, size_t off)
92{
93 char *buf = kmalloc(PAGE_SIZE, &M_BUFFER, M_ZERO);
94 struct vm_page *vm_page = buf ? mm_page_alloc() : NULL;
95
96 if (!vm_page) {
97 kfree(buf);
98 return NULL;
99 }
100
101 memcpy(buf, sigcode, sizeof(sigcode));
103 kfree(buf);
104
106 vm_page->off = 0;
107 vm_page->ref = 1;
108
111 return NULL;
112 }
113
114 return vm_page;
115}
116
117static struct vm_pager sigcode_pager = {
118 .in = sigcode_in,
119};
120
121static struct vm_object sigcode_object = {
122 .pager = &sigcode_pager,
123 .ref = 1,
124};
125
126/*
127 * Maps the sigcode page in a process's new address space.
128 */
130{
131 if (!sigcode_object.pages && !(sigcode_object.pages = hashmap_new(0, vm_page_eq)))
132 return -ENOMEM;
133
134 struct vm_entry *vm_entry = kmalloc(sizeof(struct vm_entry), &M_VM_ENTRY, M_ZERO);
135
136 if (!vm_entry)
137 return -ENOMEM;
138
143
145
146 if (err) {
148 return err;
149 }
150
152 return 0;
153}
154
157static void deliver(struct x86_regs *r, int sig)
158{
159 struct thread *thread = curthread;
160 struct proc *proc = curproc;
161 struct aquila_sigaction *sa = &proc->sigaction[sig];
162 uintptr_t handler = sa->sa_handler;
163
164 /* the mask to restore afterwards; for sigsuspend() and pselect(),
165 * it is the one they replaced */
168
169#if ARCH_BITS==32
170 uintptr_t sp = REG_SP(r);
171#else
172 /* below the red zone, the 128 bytes the code may use below its stack
173 * pointer */
174 uintptr_t sp = REG_SP(r) - 128;
175#endif
176
177 uintptr_t f = (sp - sizeof(struct sigframe)) & ~(uintptr_t) 15;
178
179#if ARCH_BITS==32
180 uintptr_t new_sp = f - I386_ARGS;
181#else
182 uintptr_t new_sp = f - sizeof(uintptr_t);
183#endif
184
185 /* make the frame here, zeroed, so that nothing of the kernel's goes
186 * out */
187 struct sigframe frame;
188 struct sigframe *uframe = (struct sigframe *) f;
189 int fault = sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || sig == SIGFPE;
190
191 memset(&frame, 0, sizeof(frame));
192 frame.info = (struct aquila_siginfo) {
193 .si_signo = sig,
194 .si_addr = fault ? thread->fault_addr : 0,
195 };
196 frame.mask = mask;
197 frame.regs = *r;
198 frame.fpu = x86_fpu_save_to(frame.fxsave);
199
200 /* below it, what the handler returns to (and its arguments) */
201#if ARCH_BITS==32
202 uintptr_t args[I386_ARGS / sizeof(uintptr_t)] = {
203 SIGCODE_ADDR, sig, (uintptr_t) &uframe->info, 0, 0,
204 };
205#else
206 uintptr_t args[1] = {SIGCODE_ADDR};
207#endif
208
209 /* if there's nowhere to put it, the process can't go on, as after a
210 * fault */
211 if (sp < 4096 || copyout(uframe, &frame, sizeof(frame)) ||
212 copyout((void *) new_sp, args, sizeof(args)))
213 signal_exit(SIGSEGV);
214
215 /* the handler starts with a clean FPU state */
217
218#if ARCH_BITS==64
219 r->rdi = sig;
220 r->rsi = (uintptr_t) &uframe->info;
221 r->rdx = 0;
222#endif
223
224 REG_IP(r) = handler;
225 REG_SP(r) = new_sp;
226 REG_FLAGS(r) = (REG_FLAGS(r) & USER_FLAGS & ~0x400UL) | X86_EFLAGS; /* DF clear */
227
228 /* while it runs, its mask is blocked, and the signal itself (unless
229 * SA_NODEFER) */
230 sigset_t block = sa->sa_mask | (sa->sa_flags & SA_NODEFER ? 0 : 1UL << sig);
231 proc->sigmask = (mask | block) & ~SIG_UNMASKABLE;
232
233 if (sa->sa_flags & SA_RESETHAND)
234 sa->sa_handler = SIG_DFL;
235}
236
246void x86_return_to_user(struct x86_regs *r, int syscall)
247{
248 struct thread *thread = curthread;
249
250 if (!thread || !thread->arch || (r->cs & 3) != 3)
251 return;
252
253 struct proc *proc = thread->owner;
254 int sig = signal_take();
255
256 /* a system call that a signal interrupted is made again (if no
257 * handler runs, or the handler says SA_RESTART), or fails with EINTR */
258 if (syscall) {
259 intptr_t ret = (intptr_t) REG_AX(r);
260
261 if (ret == -ERESTARTSYS || ret == -ERESTARTNOHAND) {
262 int restart = !sig || (ret == -ERESTARTSYS && (proc->sigaction[sig].sa_flags & SA_RESTART));
263
264 if (restart) {
265 /* back to the int $0x80, with the number in eax again */
266 REG_IP(r) -= 2;
268
269 /* straight back into the same call, with the same
270 * arguments: it may go on where it was (if a handler runs
271 * first, its calls aren't the restart) */
272 thread->restarting = !sig;
273 } else {
274 REG_AX(r) = -EINTR;
275 }
276 }
277 }
278
279 if (sig) {
280 deliver(r, sig);
281 } else if (thread->sig_oldmask_set) {
282 /* sigsuspend() restarted: restore the mask it replaced */
285 }
286}
287
288/* Returns from a signal handler (`sigreturn`): restores the interrupted
289 * context, the mask and the FPU state from the handler's frame. A bad
290 * frame raises `SIGSEGV`. */
292{
293 struct x86_thread *arch = curthread->arch;
294 struct x86_regs *r = arch->regs;
295
296#if ARCH_BITS==32
297 uintptr_t f = REG_SP(r) + I386_ARGS - sizeof(uintptr_t);
298#else
299 uintptr_t f = REG_SP(r);
300#endif
301
302 struct sigframe frame;
303
304 if ((f & 15) || copyin(&frame, (void *) f, sizeof(frame))) {
305 signal_force(curproc, SIGSEGV);
306 return;
307 }
308
309 /* the frame's registers, but with user mode's segments, and no flags
310 * that user mode couldn't set itself */
311 uintptr_t flags = (REG_FLAGS(&frame.regs) & USER_FLAGS) | X86_EFLAGS;
312
313 *r = frame.regs;
314 REG_FLAGS(r) = flags;
315 r->cs = X86_CS;
316 r->ss = X86_SS;
317
318#if ARCH_BITS==32
319 r->ds = r->es = r->fs = r->gs = X86_SS;
320#endif
321
322 curproc->sigmask = frame.mask & ~SIG_UNMASKABLE;
323 x86_fpu_load_from(frame.fxsave, frame.fpu);
324}
#define SIGCODE_ADDR
Definition arch.h:65
#define PAGE_SIZE
Definition system.h:7
static const uint32_t sigcode[]
Definition signal.c:48
static void deliver(struct aarch64_regs *r, int sig)
Sets up the user context r so that the handler of sig runs when it's returned to.
Definition signal.c:136
static int vm_page_eq(void *a, void *b)
Tells whether a page is at the offset that b points to (for the object's hashmap of pages).
Definition signal.c:56
static struct vm_object sigcode_object
Definition signal.c:99
static struct vm_pager sigcode_pager
Definition signal.c:95
void arch_sigreturn(void)
Returns from a signal handler, for sigreturn(): restores the registers, the signal mask and the FPU s...
Definition signal.c:247
int arch_sigcode_map(struct proc *proc)
Maps the page with the code that calls sigreturn() (the sigcode) into a process's new address space.
Definition signal.c:107
#define USER_FLAGS
Definition signal.c:36
static struct vm_page * sigcode_in(struct vm_object *vm_object, size_t off)
Makes the sigcode page, the first time a process touches it.
Definition signal.c:64
#define X86_SS
Definition arch.h:11
#define X86_EFLAGS
Definition arch.h:12
#define X86_CS
Definition arch.h:13
void x86_return_to_user(struct x86_regs *r, int syscall)
Delivers the current process's pending signals on the way back to user mode: stops and default action...
Definition signal.c:246
#define REG_FLAGS(r)
Definition signal.c:52
#define I386_ARGS
Definition signal.c:72
#define REG_IP(r)
Definition signal.c:49
#define REG_AX(r)
Definition signal.c:51
#define REG_SP(r)
Definition signal.c:50
void x86_fpu_load_from(const void *buf, int valid)
Gives the current thread back the FPU state saved in a signal frame, at sigreturn.
Definition fpu.c:239
int x86_fpu_save_to(void *buf)
Copies the current thread's FPU state, for a signal handler's frame (sys/signal.c).
Definition fpu.c:198
void x86_fpu_reset(void)
Makes the current thread start with a clean FPU state at its next use of the FPU, as a signal handler...
Definition fpu.c:218
#define __aligned(n)
Definition system.h:31
struct vm_page * mm_page_alloc(void)
Allocates a free page of physical memory.
Definition mm.c:78
void mm_page_dealloc(paddr_t paddr)
Frees a page of physical memory, back to the zone it came from.
Definition mm.c:103
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
#define M_ZERO
Definition kvmem.h:26
void kfree(void *)
Frees an object that kmalloc() gave.
Definition kvmem.c:146
void * memset(void *dst, int c, size_t n)
Sets n bytes to c.
Definition libc.c:23
void * memcpy(void *dst, const void *src, size_t n)
Copies n bytes; dst and src must not overlap.
Definition libc.c:11
int pmap_page_write(paddr_t paddr, off_t off, size_t size, void *buf)
Writes to a page of physical memory, through the kernel's mapping.
Definition pmap.c:452
#define curproc
Definition sched.h:13
#define curthread
Definition sched.h:12
void signal_exit(int sig)
Ends the current process because of a signal.
Definition signal.c:245
#define SIG_DFL
Definition signal.h:26
void signal_force(struct proc *proc, int signal)
Sends a signal that can't be blocked or ignored, such as the SIGSEGV of a fault: if the process block...
Definition signal.c:230
#define ERESTARTNOHAND
Definition signal.h:82
#define ERESTARTSYS
Definition signal.h:81
int signal_take(void)
Takes the current process's pending signals, on the way back to user mode: ignored ones are dropped,...
Definition signal.c:262
#define SIG_UNMASKABLE
Definition signal.h:85
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned int uint32_t
Definition stddef.h:22
unsigned char uint8_t
Definition stddef.h:20
__PTRDIFF_TYPE__ intptr_t
Definition stddef.h:11
An installed handler, on its interrupt's list.
Definition irq.c:19
A process.
Definition proc.h:75
struct aquila_sigaction sigaction[SIG_MAX+1]
its signal actions
Definition proc.h:157
struct vm_space vm_space
its address space
Definition proc.h:129
sigset_t sigmask
its blocked signals, a bit (1 << sig) each
Definition proc.h:151
A signal handler's frame, on the user stack.
Definition signal.c:39
sigset_t mask
Definition signal.c:41
uint32_t fpu
Definition signal.c:63
uint8_t fxsave[512]
Definition signal.c:65
struct aarch64_regs regs
Definition signal.c:43
struct aquila_siginfo info
Definition signal.c:40
A thread.
Definition thread.h:23
sigset_t sig_oldmask
Definition thread.h:64
int syscall_nr
the system call it's in (the last one it made), to restart it
Definition thread.h:49
int sig_oldmask_set
in sigsuspend() or pselect(): the signal mask they replaced, which the handler's frame keeps (sigretu...
Definition thread.h:63
int restarting
whether the call is a restart (a stop interrupted it, and no handler ran); restart_until is the deadl...
Definition thread.h:54
void * arch
the architecture's own data: its registers, its kernel stack
Definition thread.h:84
struct proc * owner
its process
Definition thread.h:31
uintptr_t fault_addr
the address of its last fault, the si_addr of the signal it raised
Definition thread.h:72
A mapping (map entry): a range of an address space, its permissions, and where its pages come from.
Definition vm.h:66
uint32_t flags
its permissions and flags (VM_*)
Definition vm.h:78
vaddr_t base
where the mapping starts in its address space
Definition vm.h:72
struct vm_object * vm_object
its object: what its other pages come from (a file)
Definition vm.h:84
size_t size
its size
Definition vm.h:75
A vm object: what a mapping's pages come from (a file), and its pages that have been read in.
Definition vm.h:132
struct hashmap * pages
its pages that have been read in, by their offset
Definition vm.h:134
A page of physical memory (pages[] has one for every page of RAM).
Definition vm.h:150
size_t off
its offset in its object or anon
Definition vm.h:155
size_t ref
how many references it has
Definition vm.h:157
struct vm_object * vm_object
the object it belongs to, or NULL
Definition vm.h:153
paddr_t paddr
its physical address
Definition vm.h:151
A vm object's pager: reads its pages in, and writes them out.
Definition vm.h:94
The registers an interrupt or exception saved, on the kernel stack, as cpu.S's stubs push them: the g...
Definition cpu.h:12
uint64_t rsi
Definition cpu.h:22
uint64_t cs
Definition cpu.h:24
uint64_t rdx
Definition cpu.h:22
uint64_t ss
Definition cpu.h:24
uint64_t rdi
Definition cpu.h:22
A thread's x86 state: its kernel stack, and where it resumes.
Definition arch.h:16
struct x86_regs * regs
Definition arch.h:34
unsigned long sigset_t
Definition types.h:27
int vm_space_insert(struct vm_space *vm_space, struct vm_entry *vm_entry)
Adds a map entry to an address space, in address order.
Definition vm_space.c:27
int vm_object_page_insert(struct vm_object *vm_object, struct vm_page *vm_page)
Adds a page to a vm object, at the page's offset (vm_page->off).
Definition vm_object.c:52
#define VM_URX
user read/execute
Definition vm.h:54
void vm_object_incref(struct vm_object *vm_object)
Adds a reference to a vm object: one more mapping of it.
Definition vm_object.c:25