Skip to content

Commit d8d2719

Browse files
committed
aarch64: implement signal handler
This patch implements the signal handling on aarch64. I will not be repeating the details of what it changes and why as it is quite well explained in the code changes. But in essence, this patch updates the build_signal_frame() which I believe was based on the x86_64 version of it with the changes specific to aarch64. It also adds missing handling of the SA_ONSTACK flag. Secondly, this patch also enhances entry.S to implement the call_signal_handler_thunk which is probably the most tricky part. The call_signal_handler_thunk is called on exit from a page fault as an effect of build_signal_frame() setting the field `elr` into the exception frame. Unlike on x86_64, the stack pointer register (sp) is not changed automatically based on the content of the frame on exit. To that end, the call_signal_handler_thunk has to carefully switch to SP_EL0 (exception stack) to read the value of the sp field from the exception frame in order to set SP_EL1 which is used normally for non-expection-handling by kernel and apps. Eventually, it calls the call_signal_handler() which is implemented logically in similar fashion as on x86_64 except for different registers. Finally, this patch also enables 3 unit tests to run on aarch64. Fixes #1154 Fixes #1151 Fixes #1152 Fixes #1153 Signed-off-by: Waldemar Kozaczuk <[email protected]>
1 parent fd24359 commit d8d2719

File tree

4 files changed

+152
-26
lines changed

4 files changed

+152
-26
lines changed

arch/aarch64/entry.S

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ exception_vectors:
9292
.endif
9393
mrs x2, elr_el1
9494
mrs x3, spsr_el1
95+
mrs x4, far_el1
9596
stp x30, x1, [sp, #240] // store lr, old SP
9697
stp x2, x3, [sp, #256] // store elr_el1, spsr_el1
98+
str x4, [sp, #280] // store far_el1
9799
.endm /* push_state_to_exception_frame */
98100

99101
.macro pop_state_from_exception_frame
@@ -294,18 +296,44 @@ entry_curr_el_irq x, 1 // the asynchronous exception handler used when the SP_EL
294296
call_signal_handler_thunk:
295297
.type call_signal_handler_thunk, @function
296298
.cfi_startproc simple
297-
# stack contains a signal_frame
298-
/*
299-
.cfi_offset reg, offset
300-
...
301-
mov x0, sp
302-
call call_signal_handler
303-
# FIXME: fpu
304-
305-
pop_pair...
306-
add sp, sp, 16 # error_code
307-
*/
308-
ret
299+
.cfi_signal_frame
300+
.cfi_def_cfa %sp, 0
301+
.cfi_offset x30, -32 // Point to the elr register located at the -32 offset
302+
// of the exception frame to help gdb link to the
303+
// address when interrupt was raised
304+
305+
# The call_signal_handler_thunk gets called on exit from the synchronous exception
306+
# (most likely page fault handler) as a result of build_signal_frame placing the address
307+
# of call_signal_handler_thunk into elr field of the exception frame.
308+
309+
# On exit from the exception, the stack selector is reset to point to SP_EL1 which
310+
# is where we are now. However the build_signal_frame() placed the address of the stack
311+
# we are supposed to use in the field 'sp' of the original exception frame still present
312+
# on the exception stack (please note the exception have been disabled). So in order
313+
# to read the value of the 'sp' field we need to switch back briefly to the exception
314+
# stack.
315+
mrs x1, SPsel
316+
msr SPsel, #0 // switch back to SP_EL0 so we can see original exception frame
317+
ldr x0, [sp, #-40] // read 'sp' field placed by build_signal_frame() in the original exception frame
318+
msr SPsel, x1 // switch stack selector to the original value
319+
mov sp, x0 // set sp to the stack setup by build_signal_frame()
320+
// sp points to the signal frame and original exception frame at the same time
321+
//TODO: Fix cfa to help debugger
322+
msr daifclr, #2 // enable interrupts which were disabled by build_signal_frame()
323+
isb
324+
325+
bl call_signal_handler //x0 (1st argument) points to the signal frame
326+
327+
pop_state_from_exception_frame
328+
# Adjust stack pointer by the remaining part of the signal frame to get back
329+
# to the position in the stack we should be according to the logic in build_signal_frame().
330+
add sp, sp, #288
331+
# Please note we may not be on the original stack when exception was triggered.
332+
# We would be IF the signal handler was executed on the same stack. However if user set
333+
# up his own stack and passed using sigalstack() with SA_ONSTACK to make it handle
334+
# out of stack page fault, we would instead stay on that user stack rather than restore
335+
# to the one that was exhausted.
336+
eret
309337
.cfi_endproc
310338

311339
// Keep fpu_state_save/load in sync with struct fpu_state in arch/aarch64/processor.hh

arch/aarch64/exceptions.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ struct exception_frame {
2727
u64 spsr;
2828
u32 esr;
2929
u32 align1;
30-
u64 align2; /* align to 16 */
30+
u64 far;
3131

3232
void *get_pc(void) { return (void *)elr; }
3333
unsigned int get_error(void) { return esr; }

arch/aarch64/signal.cc

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
#include <arch-cpu.hh>
1515
#include <osv/debug.hh>
1616

17+
namespace osv {
18+
extern struct sigaction signal_actions[];
19+
};
20+
1721
namespace arch {
1822

1923
struct signal_frame {
@@ -35,20 +39,123 @@ void build_signal_frame(exception_frame* ef,
3539
const siginfo_t& si,
3640
const struct sigaction& sa)
3741
{
38-
void* sp = reinterpret_cast<void*>(ef->sp);
39-
sp -= sizeof(signal_frame);
42+
// If an alternative signal stack was defined for this thread with
43+
// sigaltstack() and the SA_ONSTACK flag was specified, we should run
44+
// the signal handler on that stack. Otherwise, we need to run further
45+
// down the same stack the thread was using when it received the signal:
46+
void *sp = nullptr;
47+
if (sa.sa_flags & SA_ONSTACK) {
48+
stack_t sigstack;
49+
sigaltstack(nullptr, &sigstack);
50+
if (!(sigstack.ss_flags & SS_DISABLE)) {
51+
// ss_sp points to the beginning of the stack region, but aarch64
52+
// stacks grow downward, from the end of the region
53+
sp = sigstack.ss_sp + sigstack.ss_size;
54+
}
55+
}
56+
if (!sp) {
57+
sp = reinterpret_cast<void*>(ef->sp);
58+
}
59+
sp -= sizeof(signal_frame); // Make space for signal frame on stack
4060
sp = align_down(sp, 16);
4161
signal_frame* frame = static_cast<signal_frame*>(sp);
42-
frame->state = *ef;
62+
frame->state = *ef; // Save original exception frame and si and sa on stack
4363
frame->si = si;
4464
frame->sa = sa;
65+
// Exit from this exception will make it call call_signal_handler_thunk
4566
ef->elr = reinterpret_cast<ulong>(call_signal_handler_thunk);
67+
// On x86_64 exiting from exception sets stack pointer to the value we
68+
// would adjust in the exception frame. On aarch64 the stack pointer is not reset
69+
// automatically to the value of the field sp and we have to rely on
70+
// call_signal_handler_thunk to manually set it.
4671
ef->sp = reinterpret_cast<ulong>(sp);
72+
// To make sure it happens correctly we need to disable exceptions
73+
// so that call_signal_handler_thunk has a chance to execute this logic.
74+
ef->spsr |= processor::daif_i;
4775
}
4876

4977
}
5078

79+
// This is called on exit from a synchronous exception after build_signal_frame()
5180
void call_signal_handler(arch::signal_frame* frame)
5281
{
53-
processor::halt_no_interrupts();
82+
sched::fpu_lock fpu;
83+
SCOPE_LOCK(fpu);
84+
if (frame->sa.sa_flags & SA_SIGINFO) {
85+
ucontext_t uc = {};
86+
auto& mcontext = uc.uc_mcontext;
87+
auto& f = frame->state;
88+
mcontext.regs[0] = f.regs[0];
89+
mcontext.regs[1] = f.regs[1];
90+
mcontext.regs[2] = f.regs[2];
91+
mcontext.regs[3] = f.regs[3];
92+
mcontext.regs[4] = f.regs[4];
93+
mcontext.regs[5] = f.regs[5];
94+
mcontext.regs[6] = f.regs[6];
95+
mcontext.regs[7] = f.regs[7];
96+
mcontext.regs[8] = f.regs[8];
97+
mcontext.regs[9] = f.regs[9];
98+
mcontext.regs[10] = f.regs[10];
99+
mcontext.regs[11] = f.regs[11];
100+
mcontext.regs[12] = f.regs[12];
101+
mcontext.regs[13] = f.regs[13];
102+
mcontext.regs[14] = f.regs[14];
103+
mcontext.regs[15] = f.regs[15];
104+
mcontext.regs[16] = f.regs[16];
105+
mcontext.regs[17] = f.regs[17];
106+
mcontext.regs[18] = f.regs[18];
107+
mcontext.regs[19] = f.regs[19];
108+
mcontext.regs[20] = f.regs[20];
109+
mcontext.regs[21] = f.regs[21];
110+
mcontext.regs[22] = f.regs[22];
111+
mcontext.regs[23] = f.regs[23];
112+
mcontext.regs[24] = f.regs[24];
113+
mcontext.regs[25] = f.regs[25];
114+
mcontext.regs[26] = f.regs[26];
115+
mcontext.regs[27] = f.regs[27];
116+
mcontext.regs[28] = f.regs[28];
117+
mcontext.regs[29] = f.regs[29];
118+
mcontext.regs[30] = f.regs[30];
119+
mcontext.sp = f.sp;
120+
mcontext.pc = f.elr;
121+
mcontext.pstate = f.spsr;
122+
mcontext.fault_address = f.far;
123+
frame->sa.sa_sigaction(frame->si.si_signo, &frame->si, &uc);
124+
f.regs[0] = mcontext.regs[0];
125+
f.regs[1] = mcontext.regs[1];
126+
f.regs[2] = mcontext.regs[2];
127+
f.regs[3] = mcontext.regs[3];
128+
f.regs[4] = mcontext.regs[4];
129+
f.regs[5] = mcontext.regs[5];
130+
f.regs[6] = mcontext.regs[6];
131+
f.regs[7] = mcontext.regs[7];
132+
f.regs[8] = mcontext.regs[8];
133+
f.regs[9] = mcontext.regs[9];
134+
f.regs[10] = mcontext.regs[10];
135+
f.regs[11] = mcontext.regs[11];
136+
f.regs[12] = mcontext.regs[12];
137+
f.regs[13] = mcontext.regs[13];
138+
f.regs[14] = mcontext.regs[14];
139+
f.regs[15] = mcontext.regs[15];
140+
f.regs[16] = mcontext.regs[16];
141+
f.regs[17] = mcontext.regs[17];
142+
f.regs[18] = mcontext.regs[18];
143+
f.regs[19] = mcontext.regs[19];
144+
f.regs[20] = mcontext.regs[20];
145+
f.regs[21] = mcontext.regs[21];
146+
f.regs[22] = mcontext.regs[22];
147+
f.regs[23] = mcontext.regs[23];
148+
f.regs[24] = mcontext.regs[24];
149+
f.regs[25] = mcontext.regs[25];
150+
f.regs[26] = mcontext.regs[26];
151+
f.regs[27] = mcontext.regs[27];
152+
f.regs[28] = mcontext.regs[28];
153+
f.regs[29] = mcontext.regs[29];
154+
f.regs[30] = mcontext.regs[30];
155+
f.sp = mcontext.sp;
156+
f.elr = mcontext.pc;
157+
f.spsr = mcontext.pstate;
158+
} else {
159+
frame->sa.sa_handler(frame->si.si_signo);
160+
}
54161
}

scripts/test.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,6 @@
3131
"tcp_close_without_reading_on_qemu"
3232
]
3333

34-
#At this point there are 128 out of 134 unit tests that pass on aarch64.
35-
#The remaining ones are disabled below until we fix the issues that prevent them from passing.
36-
aarch64_disabled_list= [
37-
"tst-sigaltstack.so", #Most likely fails due to the lack of signals support on aarch64 - see #1151
38-
"tst-elf-permissions.so", #Hangs - most likely fails due to the lack of signals support on aarch64 - see #1152
39-
"tst-mmap.so", #Hangs - most likely fails due to the lack of signals support on aarch64 - see #1153
40-
]
41-
4234
class TestRunnerTest(SingleCommandTest):
4335
def __init__(self, name):
4436
super(TestRunnerTest, self).__init__(name, '/tests/%s' % name)
@@ -185,7 +177,6 @@ def main():
185177
disabled_list.extend(qemu_disabled_list)
186178

187179
if cmdargs.arch == 'aarch64':
188-
disabled_list.extend(aarch64_disabled_list)
189180
if host_arch != cmdargs.arch:
190181
#Until the issue #1143 is resolved, we need to force running with 2 CPUs in TCG mode
191182
run_py_args = run_py_args + ['-c', '2']

0 commit comments

Comments
 (0)