[PATCH] aarch64: handle system calls

10 views
Skip to first unread message

Waldemar Kozaczuk

unread,
Apr 29, 2022, 12:09:48 AM4/29/22
to osv...@googlegroups.com, Waldemar Kozaczuk
This patch enhances the aarch64 port to support handling system
call instruction - SVC.

On aarch64 the system calls are handled as synchronous exceptions
triggered by executing the SVC instruction. Per syscall specification
described in https://man7.org/linux/man-pages/man2/syscall.2.html,
the caller needs to pass arguments using the x0-x5 registers, set the
syscall number in x8 register and finally execute svc instruction.

To handle this on OSv side, this patch enhances existing synchronous
exception handler in entry.S to detect if the exception class set in ESR
register matches the one for SVC (0x15) and then retrieve the arguments
from the registers x0-x5 and syscall number from x8 and invoke the
syscall_wrapper function in linux.cc. We also need to enable exceptions
before calling syscall_wrapper so that functions called downstream
may sleep which is similar to what we do for page faults.

Please note the resulting enhancements are enough to make
tst-syscall.so pass.

Fixes #1156

Signed-off-by: Waldemar Kozaczuk <jwkoz...@gmail.com>
---
arch/aarch64/entry.S | 26 ++++++++++++++++++++++++++
linux.cc | 10 ++++++++++
modules/tests/Makefile | 4 ++--
3 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/arch/aarch64/entry.S b/arch/aarch64/entry.S
index 03266f9c..25354359 100644
--- a/arch/aarch64/entry.S
+++ b/arch/aarch64/entry.S
@@ -120,6 +120,7 @@ thread_main:
.equ ESR_EC_END,31 // Exception Class field end in ESR
.equ ESR_EC_DATA_ABORT,0x25 // Exception Class Data Abort value
.equ ESR_EC_INSN_ABORT,0x21 // Exception Class Instruction Abort value
+.equ ESR_EC_SVC64,0x15 // Exception Class for SVC (System Call) in 64-bit state

.equ ESR_ISS_BEG,0 // Instruction-Specific Syndrome field begin in ESR
.equ ESR_ISS_END,23 // Instruction-Specific Syndrome field end in ESR
@@ -197,6 +198,8 @@ entry_curr_el_spx_sync:
str w1, [sp, #272] // Store Exception Syndrom Register in the frame
ubfm x2, x1, #ESR_EC_BEG, #ESR_EC_END // Exception Class -> X2
ubfm x3, x1, #ESR_FLT_BEG, #ESR_FLT_END // FLT -> X3
+ cmp x2, #ESR_EC_SVC64
+ b.eq handle_system_call
cmp x2, #ESR_EC_DATA_ABORT
b.eq handle_mem_abort
cmp x2, #ESR_EC_INSN_ABORT
@@ -211,6 +214,29 @@ handle_mem_abort:
pop_state_from_exception_frame
eret
.cfi_endproc
+handle_system_call:
+ .cfi_startproc
+ //see https://man7.org/linux/man-pages/man2/syscall.2.html for details
+ //about calling convention for arm64
+
+ //because we used x1, x2, x3 and x4 above we need to restore them from the frame
+ ldp x1, x2, [sp, #8]
+ ldp x3, x4, [sp, #24]
+
+ mov x6, x8 // copy syscall number passed in x8 to the last 7th argument of the syscall_wrapper
+
+ msr daifclr, #2 // enable interrupts, so that the functions called by syscall_wrapper can sleep
+ isb
+
+ bl syscall_wrapper
+
+ msr daifset, #2 // disable interrupts
+ isb
+
+ str x0, [sp, #0] // copy the result in x0 directly into the frame so that it can be restored
+ pop_state_from_exception_frame
+ eret
+ .cfi_endproc
unexpected_sync_exception:
.cfi_startproc
mov x0, sp // save exception_frame to x0
diff --git a/linux.cc b/linux.cc
index 1c2eea24..d3823d00 100644
--- a/linux.cc
+++ b/linux.cc
@@ -498,12 +498,22 @@ OSV_LIBC_API long syscall(long number, ...)
}
long __syscall(long number, ...) __attribute__((alias("syscall")));

+#ifdef __x86_64__
// In x86-64, a SYSCALL instruction has exactly 6 parameters, because this is the number of registers
// alloted for passing them (additional parameters *cannot* be passed on the stack). So we can get
// 7 arguments to this function (syscall number plus its 6 parameters). Because in the x86-64 ABI the
// seventh argument is on the stack, we must pass the arguments explicitly to the syscall() function
// and can't just call it without any arguments and hope everything will be passed on
extern "C" long syscall_wrapper(long number, long p1, long p2, long p3, long p4, long p5, long p6)
+#endif
+#ifdef __aarch64__
+// In aarch64, the first 8 parameters to a procedure call are passed in the x0-x7 registers and
+// the parameters of syscall call (SVC intruction) in are passed in x0-x5 registers and syscall number
+// in x8 register before. To avoid shuffling the arguments around we make syscall_wrapper()
+// accept the syscall parameters as is but accept the syscall number as the last 7th argument which
+// the code in entry.S arranges.
+extern "C" long syscall_wrapper(long p1, long p2, long p3, long p4, long p5, long p6, long number)
+#endif
{
int errno_backup = errno;
// syscall and function return value are in rax
diff --git a/modules/tests/Makefile b/modules/tests/Makefile
index bcf4d609..9ea648f7 100644
--- a/modules/tests/Makefile
+++ b/modules/tests/Makefile
@@ -133,7 +133,7 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so tst-bsd-evh.so \
tst-getopt.so tst-getopt-pie.so tst-non-pie.so tst-semaphore.so \
tst-elf-init.so tst-realloc.so tst-setjmp.so \
libtls.so libtls_gold.so tst-tls.so tst-tls-gold.so tst-tls-pie.so \
- tst-sigaction.so
+ tst-sigaction.so tst-syscall.so
# libstatic-thread-variable.so tst-static-thread-variable.so \

#TODO For now let us disable these tests for aarch64 until
@@ -141,7 +141,7 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so tst-bsd-evh.so \
# The tst-ifaddrs.so is an exception and it does not compile due to some
# missing headers
ifeq ($(arch),x64)
-tests += tst-syscall.so tst-ifaddrs.so tst-mmx-fpu.so
+tests += tst-ifaddrs.so tst-mmx-fpu.so
endif

tests += testrunner.so
--
2.27.0

Commit Bot

unread,
May 4, 2022, 9:33:00 PM5/4/22
to osv...@googlegroups.com, Waldemar Kozaczuk
From: Waldemar Kozaczuk <jwkoz...@gmail.com>
Committer: Waldemar Kozaczuk <jwkoz...@gmail.com>
Branch: master

aarch64: handle system calls

This patch enhances the aarch64 port to support handling system
call instruction - SVC.

On aarch64 the system calls are handled as synchronous exceptions
triggered by executing the SVC instruction. Per syscall specification
described in https://man7.org/linux/man-pages/man2/syscall.2.html,
the caller needs to pass arguments using the x0-x5 registers, set the
syscall number in x8 register and finally execute svc instruction.

To handle this on OSv side, this patch enhances existing synchronous
exception handler in entry.S to detect if the exception class set in ESR
register matches the one for SVC (0x15) and then retrieve the arguments
from the registers x0-x5 and syscall number from x8 and invoke the
syscall_wrapper function in linux.cc. We also need to enable exceptions
before calling syscall_wrapper so that functions called downstream
may sleep which is similar to what we do for page faults.

Please note the resulting enhancements are enough to make
tst-syscall.so pass.

Fixes #1156

Signed-off-by: Waldemar Kozaczuk <jwkoz...@gmail.com>

---
diff --git a/arch/aarch64/entry.S b/arch/aarch64/entry.S
--- a/linux.cc
+++ b/linux.cc
@@ -498,12 +498,22 @@ OSV_LIBC_API long syscall(long number, ...)
}
long __syscall(long number, ...) __attribute__((alias("syscall")));

+#ifdef __x86_64__
// In x86-64, a SYSCALL instruction has exactly 6 parameters, because this is the number of registers
// alloted for passing them (additional parameters *cannot* be passed on the stack). So we can get
// 7 arguments to this function (syscall number plus its 6 parameters). Because in the x86-64 ABI the
// seventh argument is on the stack, we must pass the arguments explicitly to the syscall() function
// and can't just call it without any arguments and hope everything will be passed on
extern "C" long syscall_wrapper(long number, long p1, long p2, long p3, long p4, long p5, long p6)
+#endif
+#ifdef __aarch64__
+// In aarch64, the first 8 parameters to a procedure call are passed in the x0-x7 registers and
+// the parameters of syscall call (SVC intruction) in are passed in x0-x5 registers and syscall number
+// in x8 register before. To avoid shuffling the arguments around we make syscall_wrapper()
+// accept the syscall parameters as is but accept the syscall number as the last 7th argument which
+// the code in entry.S arranges.
+extern "C" long syscall_wrapper(long p1, long p2, long p3, long p4, long p5, long p6, long number)
+#endif
{
int errno_backup = errno;
// syscall and function return value are in rax
diff --git a/modules/tests/Makefile b/modules/tests/Makefile
--- a/modules/tests/Makefile
+++ b/modules/tests/Makefile
@@ -133,15 +133,15 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so tst-bsd-evh.so \
tst-getopt.so tst-getopt-pie.so tst-non-pie.so tst-semaphore.so \
tst-elf-init.so tst-realloc.so tst-setjmp.so \
libtls.so libtls_gold.so tst-tls.so tst-tls-gold.so tst-tls-pie.so \
- tst-sigaction.so
+ tst-sigaction.so tst-syscall.so
# libstatic-thread-variable.so tst-static-thread-variable.so \

#TODO For now let us disable these tests for aarch64 until
# we support floating point numbers, TLS and correct syscall handling
Reply all
Reply to author
Forward
0 new messages