zephyr/arch/xtensa/core/syscall_helper.c
Daniel Leung 42b209f816 xtensa: optimize syscall helper functions
The original idea of the syscall helpers is to workaround
an issue where the compiler could not correctly model register
usage with inline functions. It was supposed to be only used
when there are more then 3 arguments to syscalls. However,
during the original MMU code development, the helper got
expanded to cover "less then 2 arguments syscalls" as a way
for debugging syscall handling code and was never removed.
So fix that now by limiting the helper for syscalls with
more than 3 arguments.

Moreover, instead of using one helper with 6 arguments, now
we have separate implementations for both 4 and 5 arguments
syscall helpers. Now the compiler does not have to generate
code to always handle 6 arguments which saves a few code bytes
as there is no need to pass extra zeroes as arguments.

This has been verified to work with xt-clang RI-2022.10.

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
2024-12-04 14:16:15 -05:00

188 lines
5.1 KiB
C

/*
* Copyright (c) 2022 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/arch/xtensa/syscall.h>
#include <zephyr/internal/syscall_handler.h>
#include <xtensa_internal.h>
#ifdef CONFIG_XTENSA_SYSCALL_USE_HELPER
uintptr_t xtensa_syscall_helper_args_6(uintptr_t arg1, uintptr_t arg2,
uintptr_t arg3, uintptr_t arg4,
uintptr_t arg5, uintptr_t arg6,
uintptr_t call_id)
{
register uintptr_t a2 __asm__("%a2") = call_id;
register uintptr_t a6 __asm__("%a6") = arg1;
register uintptr_t a3 __asm__("%a3") = arg2;
register uintptr_t a4 __asm__("%a4") = arg3;
register uintptr_t a5 __asm__("%a5") = arg4;
register uintptr_t a8 __asm__("%a8") = arg5;
register uintptr_t a9 __asm__("%a9") = arg6;
__asm__ volatile("syscall\n\t"
: "=r" (a2)
: "r" (a2), "r" (a6), "r" (a3), "r" (a4),
"r" (a5), "r" (a8), "r" (a9)
: "memory");
return a2;
}
uintptr_t xtensa_syscall_helper_args_5(uintptr_t arg1, uintptr_t arg2,
uintptr_t arg3, uintptr_t arg4,
uintptr_t arg5, uintptr_t call_id)
{
register uintptr_t a2 __asm__("%a2") = call_id;
register uintptr_t a6 __asm__("%a6") = arg1;
register uintptr_t a3 __asm__("%a3") = arg2;
register uintptr_t a4 __asm__("%a4") = arg3;
register uintptr_t a5 __asm__("%a5") = arg4;
register uintptr_t a8 __asm__("%a8") = arg5;
__asm__ volatile("syscall\n\t"
: "=r" (a2)
: "r" (a2), "r" (a6), "r" (a3), "r" (a4),
"r" (a5), "r" (a8)
: "memory");
return a2;
}
uintptr_t xtensa_syscall_helper_args_4(uintptr_t arg1, uintptr_t arg2,
uintptr_t arg3, uintptr_t arg4,
uintptr_t call_id)
{
register uintptr_t a2 __asm__("%a2") = call_id;
register uintptr_t a6 __asm__("%a6") = arg1;
register uintptr_t a3 __asm__("%a3") = arg2;
register uintptr_t a4 __asm__("%a4") = arg3;
register uintptr_t a5 __asm__("%a5") = arg4;
__asm__ volatile("syscall\n\t"
: "=r" (a2)
: "r" (a2), "r" (a6), "r" (a3), "r" (a4),
"r" (a5)
: "memory");
return a2;
}
uintptr_t xtensa_syscall_helper_args_3(uintptr_t arg1, uintptr_t arg2,
uintptr_t arg3, uintptr_t call_id)
{
register uintptr_t a2 __asm__("%a2") = call_id;
register uintptr_t a6 __asm__("%a6") = arg1;
register uintptr_t a3 __asm__("%a3") = arg2;
register uintptr_t a4 __asm__("%a4") = arg3;
__asm__ volatile("syscall\n\t"
: "=r" (a2)
: "r" (a2), "r" (a6), "r" (a3), "r" (a4)
: "memory");
return a2;
}
uintptr_t xtensa_syscall_helper_args_2(uintptr_t arg1, uintptr_t arg2,
uintptr_t call_id)
{
register uintptr_t a2 __asm__("%a2") = call_id;
register uintptr_t a6 __asm__("%a6") = arg1;
register uintptr_t a3 __asm__("%a3") = arg2;
__asm__ volatile("syscall\n\t"
: "=r" (a2)
: "r" (a2), "r" (a6), "r" (a3)
: "memory");
return a2;
}
uintptr_t xtensa_syscall_helper_args_1(uintptr_t arg1, uintptr_t call_id)
{
register uintptr_t a2 __asm__("%a2") = call_id;
register uintptr_t a6 __asm__("%a6") = arg1;
__asm__ volatile("syscall\n\t"
: "=r" (a2)
: "r" (a2), "r" (a6)
: "memory");
return a2;
}
uintptr_t xtensa_syscall_helper_args_0(uintptr_t call_id)
{
register uintptr_t a2 __asm__("%a2") = call_id;
__asm__ volatile("syscall\n\t"
: "=r" (a2)
: "r" (a2)
: "memory");
return a2;
}
#endif /* CONFIG_XTENSA_SYSCALL_USE_HELPER */
#if XCHAL_HAVE_THREADPTR == 0
#include <xtensa/config/core-isa.h>
#include <xtensa/config/core.h>
bool xtensa_is_user_context(void)
{
uint32_t ret;
__asm__ volatile(".global xtensa_is_user_context_epc\n"
" xtensa_is_user_context_epc:\n"
" syscall\n"
" mov %0, a2\n"
: "=r"(ret) : : "a2");
return ret != 0;
}
#endif /* XCHAL_HAVE_THREADPTR */
size_t arch_user_string_nlen(const char *s, size_t maxsize, int *err_arg)
{
/* Check if we can actually read the whole length.
*
* arch_user_string_nlen() is supposed to naively go through
* the string passed from user thread, and relies on page faults
* to catch inaccessible strings, such that user thread can pass
* a string that is shorter than the max length this function
* caller expects. So at least we want to make sure kernel has
* access to the whole length, aka. memory being mapped.
* Note that arch_user_string_nlen() should never result in
* thread termination due to page faults, and must always
* return to the caller with err_arg set or cleared.
* For MMU systems, unmapped memory will result in a DTLB miss
* and that might trigger an infinite DTLB miss storm if
* the corresponding L2 page table never exists in the first
* place (which would result in DTLB misses through L1 page
* table), until some other exceptions occur to break
* the cycle.
* For MPU systems, this would simply results in access errors
* and the exception handler will terminate the thread.
*/
if (!xtensa_mem_kernel_has_access((void *)s, maxsize, 0)) {
/*
* API says we need to set err_arg to -1 if there are
* any errors.
*/
*err_arg = -1;
return 0;
}
/* No error and we can proceed to getting the string length. */
*err_arg = 0;
return strnlen(s, maxsize);
}