Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/muteki.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <muteki/ini.h>
#include <muteki/loader.h>
#include <muteki/memory.h>
#include <muteki/syscall.h>
#include <muteki/system.h>
#include <muteki/threading.h>
#include <muteki/ui.h>
Expand Down
35 changes: 35 additions & 0 deletions include/muteki/syscall.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef __MUTEKI_SYSCALL_H__
#define __MUTEKI_SYSCALL_H__

#include <muteki/common.h>

#ifdef __cplusplus
extern "C" {
#endif

#define SYSCALL_HAS_STACK_ARGS (0x80000000ul)

/**
* @brief Call arbitrary syscall with arbitrary number of arguments.
* @details
* This will reformat the stack frame, write an SVC instruction to an unused space of the stack, and jump to it.
*
* The higher 8 bits of the syscall number can encode special meanings. Currently setting the highest bit
* (SYSCALL_HAS_STACK_ARGS) signals the function to take account of arguments allocated on the stack. That is,
* that bit should be set whenever there's more than 3 syscall arguments passed to this function.
*
* @warning Due to limitation of the Besta syscall scheme, this function is not thread-safe. Therefore to call it
* across multiple threads, a lock must be created and used across all threads that use this function.
*
* @param number The syscall number.
* @param ... Syscall arguments.
* @return Return value of the syscall. Can be casted to recover values of different type. Note that 64-bit values
* are not supported and only the lower 32-bit of such values can be retrieved.
*/
long syscall(long number, ...);

#ifdef __cplusplus
} // extern "C"
#endif

#endif // __MUTEKI_SYSCALL_H__
3 changes: 2 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ syscalls_split = custom_target('syscalls_split',
# Static library that bundles both sdk and krnl shims.
static_library(
'muteki-shims',
syscalls_split,
[syscalls_split, 'src/shims/syscall.c'],
include_directories: ['include/'],
install : true,
c_args : c_flags,
link_args : ld_flags,
Expand Down
75 changes: 75 additions & 0 deletions src/shims/syscall.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include "muteki/syscall.h"

#if defined(__clang__)

// clang: Ignore unused parameter warning and clangd's warning on asm().
#define NO_EXPECTED_WARNINGS_BEGIN \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wunused-parameter\"") \
_Pragma("clang diagnostic ignored \"-Wlanguage-extension-token\"")
#define NO_EXPECTED_WARNINGS_END \
_Pragma("clang diagnostic pop")

#elif defined(__GNUC__) // !defined(__clang__)

// gcc: Ignore unused parameter warning.
#define NO_EXPECTED_WARNINGS_BEGIN \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wunused-parameter\"")
#define NO_EXPECTED_WARNINGS_END \
_Pragma("GCC diagnostic pop")

#else // !defined(__GNUC__)

// No-op
#define NO_EXPECTED_WARNINGS_BEGIN
#define NO_EXPECTED_WARNINGS_END

#endif // defined(__clang__) || defined(__GNUC__)

__attribute__((used)) static unsigned int trampoline_area[] = {0xe1a00000};

NO_EXPECTED_WARNINGS_BEGIN
__attribute__((naked, target("arch=armv4t")))
long syscall(long number, ...) {
// Starts as sysno a0 a1 a2 | a3 ... or sysno a0 a1 a2 | ...
asm(
// sysno x a1 a2 | a0 a3 ... or sysno x a1 a2 | a0 ...
"push {r1}\n\t"
// sysno a1 a2 x | a0 a3 ... or sysno a1 a2 x | a0 ...
"mov r1, r2\n\t"
"mov r2, r3\n\t"
// If passing 3+ arguments
"tst r0, #0x80000000\n\t"
"beq 1f\n\t"
// x a1 a2 x | sysno a0 a3 ...
"push {r0}\n\t"
// a0 a1 a2 x | sysno x a3 ...
"ldr r0, [sp, #4]\n\t"
// a0 a1 a2 a3 | sysno x x ...
"ldr r3, [sp, #8]\n\t"
// x a1 a2 a3 | sysno x a0 ...
"str r0, [sp, #8]\n\t"
// sysno a1 a2 a3 | x x a0 ...
"ldr r0, [sp]\n\t"
// sysno a1 a2 a3 | a0 ...
"add sp, sp, #8\n"
// Fall through

"1:\n\t"
// sysno x a2 a3? | a1 lr a0 ...
"push {r1, lr}\n\t"

"bic r0, r0, #0xff000000\n\t"
"orr r1, r0, #0xef000000\n\t"

// sysno svc a2 a3? | a1 lr a0 ...
"ldr r0, =trampoline_area\n\t"
// trampoline svc a2 a3? | a1 lr a0 ...
"str r1, [r0]\n\t"
// trampoline a1 a2 a3? | lr a0 ...
"pop {r1}\n\t"
"bx r0"
);
}
NO_EXPECTED_WARNINGS_END
47 changes: 47 additions & 0 deletions src/syscall.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.arm
.cpu arm7tdmi
.global syscall
.type syscall, %function

@ Starts as sysno a0 a1 a2 | a3 ... or sysno a0 a1 a2 | ...
syscall:
@ sysno x a1 a2 | a0 a3 ... or sysno x a1 a2 | a0 ...
push {r1}
@ sysno a1 a2 x | a0 a3 ... or sysno a1 a2 x | a0 ...
mov r1, r2
mov r2, r3
@ If passing 3+ arguments
tst r0, #0x80000000
beq _syscall_insert_lr
@ x a1 a2 x | sysno a0 a3 ...
push {r0}
@ a0 a1 a2 x | sysno x a3 ...
ldr r0, [sp, #4]
@ a0 a1 a2 a3 | sysno x x ...
ldr r3, [sp, #8]
@ x a1 a2 a3 | sysno x a0 ...
str r0, [sp, #8]
@ sysno a1 a2 a3 | x x a0 ...
ldr r0, [sp]
@ sysno a1 a2 a3 | a0 ...
add sp, sp, #8
@ Fall through

_syscall_insert_lr:
@ sysno a1 a2 a3? | lr a0 ...
push {lr}

bic r0, r0, #0xff000000
orr r0, r0, #0xef000000

@ sysno a1 a2 a3? | trampoline[(svc sysno) nop nop] lr a0 ...
str r0, [sp, #-12]
ldr r0, =_nop
str r0, [sp, #-8]
str r0, [sp, #-4]
@ trampoline a1 a2 a3 | trampoline[(svc sysno) nop nop] lr a0 ...
sub r0, sp, #12
bx r0

_nop:
mov r0, r0