717 lines
29 KiB
Python
717 lines
29 KiB
Python
|
# SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
|
||
|
# Raw SVC definitions from the kernel.
|
||
|
#
|
||
|
# Avoid modifying the prototypes; see below for how to customize generation
|
||
|
# for a given typename.
|
||
|
SVCS = [
|
||
|
[0x01, "Result SetHeapSize(Address* out_address, Size size);"],
|
||
|
[0x02, "Result SetMemoryPermission(Address address, Size size, MemoryPermission perm);"],
|
||
|
[0x03, "Result SetMemoryAttribute(Address address, Size size, uint32_t mask, uint32_t attr);"],
|
||
|
[0x04, "Result MapMemory(Address dst_address, Address src_address, Size size);"],
|
||
|
[0x05, "Result UnmapMemory(Address dst_address, Address src_address, Size size);"],
|
||
|
[0x06, "Result QueryMemory(Address out_memory_info, PageInfo* out_page_info, Address address);"],
|
||
|
[0x07, "void ExitProcess();"],
|
||
|
[0x08, "Result CreateThread(Handle* out_handle, ThreadFunc func, Address arg, Address stack_bottom, int32_t priority, int32_t core_id);"],
|
||
|
[0x09, "Result StartThread(Handle thread_handle);"],
|
||
|
[0x0A, "void ExitThread();"],
|
||
|
[0x0B, "void SleepThread(int64_t ns);"],
|
||
|
[0x0C, "Result GetThreadPriority(int32_t* out_priority, Handle thread_handle);"],
|
||
|
[0x0D, "Result SetThreadPriority(Handle thread_handle, int32_t priority);"],
|
||
|
[0x0E, "Result GetThreadCoreMask(int32_t* out_core_id, uint64_t* out_affinity_mask, Handle thread_handle);"],
|
||
|
[0x0F, "Result SetThreadCoreMask(Handle thread_handle, int32_t core_id, uint64_t affinity_mask);"],
|
||
|
[0x10, "int32_t GetCurrentProcessorNumber();"],
|
||
|
[0x11, "Result SignalEvent(Handle event_handle);"],
|
||
|
[0x12, "Result ClearEvent(Handle event_handle);"],
|
||
|
[0x13, "Result MapSharedMemory(Handle shmem_handle, Address address, Size size, MemoryPermission map_perm);"],
|
||
|
[0x14, "Result UnmapSharedMemory(Handle shmem_handle, Address address, Size size);"],
|
||
|
[0x15, "Result CreateTransferMemory(Handle* out_handle, Address address, Size size, MemoryPermission map_perm);"],
|
||
|
[0x16, "Result CloseHandle(Handle handle);"],
|
||
|
[0x17, "Result ResetSignal(Handle handle);"],
|
||
|
[0x18, "Result WaitSynchronization(int32_t* out_index, Address handles, int32_t num_handles, int64_t timeout_ns);"],
|
||
|
[0x19, "Result CancelSynchronization(Handle handle);"],
|
||
|
[0x1A, "Result ArbitrateLock(Handle thread_handle, Address address, uint32_t tag);"],
|
||
|
[0x1B, "Result ArbitrateUnlock(Address address);"],
|
||
|
[0x1C, "Result WaitProcessWideKeyAtomic(Address address, Address cv_key, uint32_t tag, int64_t timeout_ns);"],
|
||
|
[0x1D, "void SignalProcessWideKey(Address cv_key, int32_t count);"],
|
||
|
[0x1E, "int64_t GetSystemTick();"],
|
||
|
[0x1F, "Result ConnectToNamedPort(Handle* out_handle, Address name);"],
|
||
|
[0x20, "Result SendSyncRequestLight(Handle session_handle);"],
|
||
|
[0x21, "Result SendSyncRequest(Handle session_handle);"],
|
||
|
[0x22, "Result SendSyncRequestWithUserBuffer(Address message_buffer, Size message_buffer_size, Handle session_handle);"],
|
||
|
[0x23, "Result SendAsyncRequestWithUserBuffer(Handle* out_event_handle, Address message_buffer, Size message_buffer_size, Handle session_handle);"],
|
||
|
[0x24, "Result GetProcessId(uint64_t* out_process_id, Handle process_handle);"],
|
||
|
[0x25, "Result GetThreadId(uint64_t* out_thread_id, Handle thread_handle);"],
|
||
|
[0x26, "void Break(BreakReason break_reason, Address arg, Size size);"],
|
||
|
[0x27, "Result OutputDebugString(Address debug_str, Size len);"],
|
||
|
[0x28, "void ReturnFromException(Result result);"],
|
||
|
[0x29, "Result GetInfo(uint64_t* out, InfoType info_type, Handle handle, uint64_t info_subtype);"],
|
||
|
[0x2A, "void FlushEntireDataCache();"],
|
||
|
[0x2B, "Result FlushDataCache(Address address, Size size);"],
|
||
|
[0x2C, "Result MapPhysicalMemory(Address address, Size size);"],
|
||
|
[0x2D, "Result UnmapPhysicalMemory(Address address, Size size);"],
|
||
|
[0x2E, "Result GetDebugFutureThreadInfo(LastThreadContext* out_context, uint64_t* out_thread_id, Handle debug_handle, int64_t ns);"],
|
||
|
[0x2F, "Result GetLastThreadInfo(LastThreadContext* out_context, Address* out_tls_address, uint32_t* out_flags);"],
|
||
|
[0x30, "Result GetResourceLimitLimitValue(int64_t* out_limit_value, Handle resource_limit_handle, LimitableResource which);"],
|
||
|
[0x31, "Result GetResourceLimitCurrentValue(int64_t* out_current_value, Handle resource_limit_handle, LimitableResource which);"],
|
||
|
[0x32, "Result SetThreadActivity(Handle thread_handle, ThreadActivity thread_activity);"],
|
||
|
[0x33, "Result GetThreadContext3(Address out_context, Handle thread_handle);"],
|
||
|
[0x34, "Result WaitForAddress(Address address, ArbitrationType arb_type, int32_t value, int64_t timeout_ns);"],
|
||
|
[0x35, "Result SignalToAddress(Address address, SignalType signal_type, int32_t value, int32_t count);"],
|
||
|
[0x36, "void SynchronizePreemptionState();"],
|
||
|
[0x37, "Result GetResourceLimitPeakValue(int64_t* out_peak_value, Handle resource_limit_handle, LimitableResource which);"],
|
||
|
|
||
|
[0x39, "Result CreateIoPool(Handle* out_handle, IoPoolType which);"],
|
||
|
[0x3A, "Result CreateIoRegion(Handle* out_handle, Handle io_pool, PhysicalAddress physical_address, Size size, MemoryMapping mapping, MemoryPermission perm);"],
|
||
|
|
||
|
[0x3C, "void KernelDebug(KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2);"],
|
||
|
[0x3D, "void ChangeKernelTraceState(KernelTraceState kern_trace_state);"],
|
||
|
|
||
|
[0x40, "Result CreateSession(Handle* out_server_session_handle, Handle* out_client_session_handle, bool is_light, Address name);"],
|
||
|
[0x41, "Result AcceptSession(Handle* out_handle, Handle port);"],
|
||
|
[0x42, "Result ReplyAndReceiveLight(Handle handle);"],
|
||
|
[0x43, "Result ReplyAndReceive(int32_t* out_index, Address handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);"],
|
||
|
[0x44, "Result ReplyAndReceiveWithUserBuffer(int32_t* out_index, Address message_buffer, Size message_buffer_size, Address handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);"],
|
||
|
[0x45, "Result CreateEvent(Handle* out_write_handle, Handle* out_read_handle);"],
|
||
|
[0x46, "Result MapIoRegion(Handle io_region, Address address, Size size, MemoryPermission perm);"],
|
||
|
[0x47, "Result UnmapIoRegion(Handle io_region, Address address, Size size);"],
|
||
|
[0x48, "Result MapPhysicalMemoryUnsafe(Address address, Size size);"],
|
||
|
[0x49, "Result UnmapPhysicalMemoryUnsafe(Address address, Size size);"],
|
||
|
[0x4A, "Result SetUnsafeLimit(Size limit);"],
|
||
|
[0x4B, "Result CreateCodeMemory(Handle* out_handle, Address address, Size size);"],
|
||
|
[0x4C, "Result ControlCodeMemory(Handle code_memory_handle, CodeMemoryOperation operation, uint64_t address, uint64_t size, MemoryPermission perm);"],
|
||
|
[0x4D, "void SleepSystem();"],
|
||
|
[0x4E, "Result ReadWriteRegister(uint32_t* out_value, PhysicalAddress address, uint32_t mask, uint32_t value);"],
|
||
|
[0x4F, "Result SetProcessActivity(Handle process_handle, ProcessActivity process_activity);"],
|
||
|
[0x50, "Result CreateSharedMemory(Handle* out_handle, Size size, MemoryPermission owner_perm, MemoryPermission remote_perm);"],
|
||
|
[0x51, "Result MapTransferMemory(Handle trmem_handle, Address address, Size size, MemoryPermission owner_perm);"],
|
||
|
[0x52, "Result UnmapTransferMemory(Handle trmem_handle, Address address, Size size);"],
|
||
|
[0x53, "Result CreateInterruptEvent(Handle* out_read_handle, int32_t interrupt_id, InterruptType interrupt_type);"],
|
||
|
[0x54, "Result QueryPhysicalAddress(PhysicalMemoryInfo* out_info, Address address);"],
|
||
|
[0x55, "Result QueryIoMapping(Address* out_address, Size* out_size, PhysicalAddress physical_address, Size size);"],
|
||
|
[0x56, "Result CreateDeviceAddressSpace(Handle* out_handle, uint64_t das_address, uint64_t das_size);"],
|
||
|
[0x57, "Result AttachDeviceAddressSpace(DeviceName device_name, Handle das_handle);"],
|
||
|
[0x58, "Result DetachDeviceAddressSpace(DeviceName device_name, Handle das_handle);"],
|
||
|
[0x59, "Result MapDeviceAddressSpaceByForce(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address, uint32_t option);"],
|
||
|
[0x5A, "Result MapDeviceAddressSpaceAligned(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address, uint32_t option);"],
|
||
|
[0x5C, "Result UnmapDeviceAddressSpace(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address);"],
|
||
|
[0x5D, "Result InvalidateProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
|
||
|
[0x5E, "Result StoreProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
|
||
|
[0x5F, "Result FlushProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
|
||
|
[0x60, "Result DebugActiveProcess(Handle* out_handle, uint64_t process_id);"],
|
||
|
[0x61, "Result BreakDebugProcess(Handle debug_handle);"],
|
||
|
[0x62, "Result TerminateDebugProcess(Handle debug_handle);"],
|
||
|
[0x63, "Result GetDebugEvent(Address out_info, Handle debug_handle);"],
|
||
|
[0x64, "Result ContinueDebugEvent(Handle debug_handle, uint32_t flags, Address thread_ids, int32_t num_thread_ids);"],
|
||
|
[0x65, "Result GetProcessList(int32_t* out_num_processes, Address out_process_ids, int32_t max_out_count);"],
|
||
|
[0x66, "Result GetThreadList(int32_t* out_num_threads, Address out_thread_ids, int32_t max_out_count, Handle debug_handle);"],
|
||
|
[0x67, "Result GetDebugThreadContext(Address out_context, Handle debug_handle, uint64_t thread_id, uint32_t context_flags);"],
|
||
|
[0x68, "Result SetDebugThreadContext(Handle debug_handle, uint64_t thread_id, Address context, uint32_t context_flags);"],
|
||
|
[0x69, "Result QueryDebugProcessMemory(Address out_memory_info, PageInfo* out_page_info, Handle process_handle, Address address);"],
|
||
|
[0x6A, "Result ReadDebugProcessMemory(Address buffer, Handle debug_handle, Address address, Size size);"],
|
||
|
[0x6B, "Result WriteDebugProcessMemory(Handle debug_handle, Address buffer, Address address, Size size);"],
|
||
|
[0x6C, "Result SetHardwareBreakPoint(HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value);"],
|
||
|
[0x6D, "Result GetDebugThreadParam(uint64_t* out_64, uint32_t* out_32, Handle debug_handle, uint64_t thread_id, DebugThreadParam param);"],
|
||
|
|
||
|
[0x6F, "Result GetSystemInfo(uint64_t* out, SystemInfoType info_type, Handle handle, uint64_t info_subtype);"],
|
||
|
[0x70, "Result CreatePort(Handle* out_server_handle, Handle* out_client_handle, int32_t max_sessions, bool is_light, Address name);"],
|
||
|
[0x71, "Result ManageNamedPort(Handle* out_server_handle, Address name, int32_t max_sessions);"],
|
||
|
[0x72, "Result ConnectToPort(Handle* out_handle, Handle port);"],
|
||
|
[0x73, "Result SetProcessMemoryPermission(Handle process_handle, uint64_t address, uint64_t size, MemoryPermission perm);"],
|
||
|
[0x74, "Result MapProcessMemory(Address dst_address, Handle process_handle, uint64_t src_address, Size size);"],
|
||
|
[0x75, "Result UnmapProcessMemory(Address dst_address, Handle process_handle, uint64_t src_address, Size size);"],
|
||
|
[0x76, "Result QueryProcessMemory(Address out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);"],
|
||
|
[0x77, "Result MapProcessCodeMemory(Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);"],
|
||
|
[0x78, "Result UnmapProcessCodeMemory(Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);"],
|
||
|
[0x79, "Result CreateProcess(Handle* out_handle, Address parameters, Address caps, int32_t num_caps);"],
|
||
|
[0x7A, "Result StartProcess(Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size);"],
|
||
|
[0x7B, "Result TerminateProcess(Handle process_handle);"],
|
||
|
[0x7C, "Result GetProcessInfo(int64_t* out_info, Handle process_handle, ProcessInfoType info_type);"],
|
||
|
[0x7D, "Result CreateResourceLimit(Handle* out_handle);"],
|
||
|
[0x7E, "Result SetResourceLimitLimitValue(Handle resource_limit_handle, LimitableResource which, int64_t limit_value);"],
|
||
|
[0x7F, "void CallSecureMonitor(SecureMonitorArguments args);"],
|
||
|
|
||
|
[0x90, "Result MapInsecureMemory(Address address, Size size);"],
|
||
|
[0x91, "Result UnmapInsecureMemory(Address address, Size size);"],
|
||
|
]
|
||
|
|
||
|
# These use a custom ABI, and therefore require custom wrappers
|
||
|
SKIP_WRAPPERS = {
|
||
|
0x20: "SendSyncRequestLight",
|
||
|
0x42: "ReplyAndReceiveLight",
|
||
|
0x7F: "CallSecureMonitor",
|
||
|
}
|
||
|
|
||
|
BIT_32 = 0
|
||
|
BIT_64 = 1
|
||
|
|
||
|
REG_SIZES = [4, 8]
|
||
|
SUFFIX_NAMES = ["64From32", "64"]
|
||
|
TYPE_SIZES = {
|
||
|
# SVC types
|
||
|
"ArbitrationType": 4,
|
||
|
"BreakReason": 4,
|
||
|
"CodeMemoryOperation": 4,
|
||
|
"DebugThreadParam": 4,
|
||
|
"DeviceName": 4,
|
||
|
"HardwareBreakPointRegisterName": 4,
|
||
|
"Handle": 4,
|
||
|
"InfoType": 4,
|
||
|
"InterruptType": 4,
|
||
|
"IoPoolType": 4,
|
||
|
"KernelDebugType": 4,
|
||
|
"KernelTraceState": 4,
|
||
|
"LimitableResource": 4,
|
||
|
"MemoryMapping": 4,
|
||
|
"MemoryPermission": 4,
|
||
|
"PageInfo": 4,
|
||
|
"ProcessActivity": 4,
|
||
|
"ProcessInfoType": 4,
|
||
|
"Result": 4,
|
||
|
"SignalType": 4,
|
||
|
"SystemInfoType": 4,
|
||
|
"ThreadActivity": 4,
|
||
|
|
||
|
# Arch-specific types
|
||
|
"ilp32::LastThreadContext": 16,
|
||
|
"ilp32::PhysicalMemoryInfo": 16,
|
||
|
"ilp32::SecureMonitorArguments": 32,
|
||
|
"lp64::LastThreadContext": 32,
|
||
|
"lp64::PhysicalMemoryInfo": 24,
|
||
|
"lp64::SecureMonitorArguments": 64,
|
||
|
|
||
|
# Generic types
|
||
|
"bool": 1,
|
||
|
"int32_t": 4,
|
||
|
"int64_t": 8,
|
||
|
"uint32_t": 4,
|
||
|
"uint64_t": 8,
|
||
|
"void": 0,
|
||
|
}
|
||
|
|
||
|
TYPE_REPLACEMENTS = {
|
||
|
"Address": ["uint32_t", "uint64_t"],
|
||
|
"LastThreadContext": ["ilp32::LastThreadContext", "lp64::LastThreadContext"],
|
||
|
"PhysicalAddress": ["uint64_t", "uint64_t"],
|
||
|
"PhysicalMemoryInfo": ["ilp32::PhysicalMemoryInfo", "lp64::PhysicalMemoryInfo"],
|
||
|
"SecureMonitorArguments": ["ilp32::SecureMonitorArguments", "lp64::SecureMonitorArguments"],
|
||
|
"Size": ["uint32_t", "uint64_t"],
|
||
|
"ThreadFunc": ["uint32_t", "uint64_t"],
|
||
|
}
|
||
|
|
||
|
# Statically verify that the hardcoded sizes match the intended
|
||
|
# sizes in C++.
|
||
|
def emit_size_check():
|
||
|
lines = []
|
||
|
|
||
|
for type, size in TYPE_SIZES.items():
|
||
|
if type != "void":
|
||
|
lines.append(f"static_assert(sizeof({type}) == {size});")
|
||
|
|
||
|
return "\n".join(lines)
|
||
|
|
||
|
|
||
|
# Replaces a type with an arch-specific one, if it exists.
|
||
|
def substitute_type(name, bitness):
|
||
|
if name in TYPE_REPLACEMENTS:
|
||
|
return TYPE_REPLACEMENTS[name][bitness]
|
||
|
else:
|
||
|
return name
|
||
|
|
||
|
|
||
|
class Argument:
|
||
|
def __init__(self, type_name, var_name, is_output, is_outptr, is_address):
|
||
|
self.type_name = type_name
|
||
|
self.var_name = var_name
|
||
|
self.is_output = is_output
|
||
|
self.is_outptr = is_outptr
|
||
|
self.is_address = is_address
|
||
|
|
||
|
|
||
|
# Parses C-style string declarations for SVCs.
|
||
|
def parse_declaration(declaration, bitness):
|
||
|
return_type, rest = declaration.split(" ", 1)
|
||
|
func_name, rest = rest.split("(", 1)
|
||
|
arg_names, rest = rest.split(")", 1)
|
||
|
argument_types = []
|
||
|
|
||
|
return_type = substitute_type(return_type, bitness)
|
||
|
assert return_type in TYPE_SIZES, f"Unknown type '{return_type}'"
|
||
|
|
||
|
if arg_names:
|
||
|
for arg_name in arg_names.split(", "):
|
||
|
type_name, var_name = arg_name.replace("*", "").split(" ", 1)
|
||
|
|
||
|
# All outputs must contain out_ in the name.
|
||
|
is_output = var_name == "out" or var_name.find("out_") != -1
|
||
|
|
||
|
# User-pointer outputs are not written to registers.
|
||
|
is_outptr = is_output and arg_name.find("*") == -1
|
||
|
|
||
|
# Special handling is performed for output addresses to avoid awkwardness
|
||
|
# in conversion for the 32-bit equivalents.
|
||
|
is_address = is_output and not is_outptr and \
|
||
|
type_name in ["Address", "Size"]
|
||
|
type_name = substitute_type(type_name, bitness)
|
||
|
|
||
|
assert type_name in TYPE_SIZES, f"Unknown type '{type_name}'"
|
||
|
|
||
|
argument_types.append(
|
||
|
Argument(type_name, var_name, is_output, is_outptr, is_address))
|
||
|
|
||
|
return (return_type, func_name, argument_types)
|
||
|
|
||
|
|
||
|
class RegisterAllocator:
|
||
|
def __init__(self, num_regs, byte_size, parameter_count):
|
||
|
self.registers = {}
|
||
|
self.num_regs = num_regs
|
||
|
self.byte_size = byte_size
|
||
|
self.parameter_count = parameter_count
|
||
|
|
||
|
# Mark the given register as allocated, for use in layout
|
||
|
# calculation if the NGRN exceeds the ABI parameter count.
|
||
|
def allocate(self, i):
|
||
|
assert i not in self.registers, f"Register R{i} already allocated"
|
||
|
self.registers[i] = True
|
||
|
return i
|
||
|
|
||
|
# Calculate the next available location for a register;
|
||
|
# the NGRN has exceeded the ABI parameter count.
|
||
|
def allocate_first_free(self):
|
||
|
for i in range(0, self.num_regs):
|
||
|
if i in self.registers:
|
||
|
continue
|
||
|
|
||
|
self.allocate(i)
|
||
|
return i
|
||
|
|
||
|
assert False, "No registers available"
|
||
|
|
||
|
# Add a single register at the given NGRN.
|
||
|
# If the index exceeds the ABI parameter count, try to find a
|
||
|
# location to add it. Returns the output location and increment.
|
||
|
def add_single(self, ngrn):
|
||
|
if ngrn >= self.parameter_count:
|
||
|
return (self.allocate_first_free(), 0)
|
||
|
else:
|
||
|
return (self.allocate(ngrn), 1)
|
||
|
|
||
|
# Add registers at the given NGRN for a data type of
|
||
|
# the given size. Returns the output locations and increment.
|
||
|
def add(self, ngrn, data_size, align=True):
|
||
|
if data_size <= self.byte_size:
|
||
|
r, i = self.add_single(ngrn)
|
||
|
return ([r], i)
|
||
|
|
||
|
regs = []
|
||
|
inc = ngrn % 2 if align else 0
|
||
|
remaining_size = data_size
|
||
|
while remaining_size > 0:
|
||
|
r, i = self.add_single(ngrn + inc)
|
||
|
regs.append(r)
|
||
|
inc += i
|
||
|
remaining_size -= self.byte_size
|
||
|
|
||
|
return (regs, inc)
|
||
|
|
||
|
|
||
|
def reg_alloc(bitness):
|
||
|
if bitness == 0:
|
||
|
# aapcs32: 4 4-byte registers
|
||
|
return RegisterAllocator(8, 4, 4)
|
||
|
elif bitness == 1:
|
||
|
# aapcs64: 8 8-byte registers
|
||
|
return RegisterAllocator(8, 8, 8)
|
||
|
|
||
|
|
||
|
# Converts a parsed SVC declaration into register lists for
|
||
|
# the return value, outputs, and inputs.
|
||
|
def get_registers(parse_result, bitness):
|
||
|
output_alloc = reg_alloc(bitness)
|
||
|
input_alloc = reg_alloc(bitness)
|
||
|
return_type, _, arguments = parse_result
|
||
|
|
||
|
return_write = []
|
||
|
output_writes = []
|
||
|
input_reads = []
|
||
|
|
||
|
input_ngrn = 0
|
||
|
output_ngrn = 0
|
||
|
|
||
|
# Run the input calculation.
|
||
|
for arg in arguments:
|
||
|
if arg.is_output and not arg.is_outptr:
|
||
|
input_ngrn += 1
|
||
|
continue
|
||
|
|
||
|
regs, increment = input_alloc.add(
|
||
|
input_ngrn, TYPE_SIZES[arg.type_name], align=True)
|
||
|
input_reads.append([arg.type_name, arg.var_name, regs])
|
||
|
input_ngrn += increment
|
||
|
|
||
|
# Include the return value if this SVC returns a value.
|
||
|
if return_type != "void":
|
||
|
regs, increment = output_alloc.add(
|
||
|
output_ngrn, TYPE_SIZES[return_type], align=False)
|
||
|
return_write.append([return_type, regs])
|
||
|
output_ngrn += increment
|
||
|
|
||
|
# Run the output calculation.
|
||
|
for arg in arguments:
|
||
|
if not arg.is_output or arg.is_outptr:
|
||
|
continue
|
||
|
|
||
|
regs, increment = output_alloc.add(
|
||
|
output_ngrn, TYPE_SIZES[arg.type_name], align=False)
|
||
|
output_writes.append(
|
||
|
[arg.type_name, arg.var_name, regs, arg.is_address])
|
||
|
output_ngrn += increment
|
||
|
|
||
|
return (return_write, output_writes, input_reads)
|
||
|
|
||
|
|
||
|
# Collects possibly multiple source registers into the named C++ value.
|
||
|
def emit_gather(sources, name, type_name, reg_size):
|
||
|
get_fn = f"GetReg{reg_size*8}"
|
||
|
|
||
|
if len(sources) == 1:
|
||
|
s, = sources
|
||
|
line = f"{name} = Convert<{type_name}>({get_fn}(system, {s}));"
|
||
|
return [line]
|
||
|
|
||
|
var_type = f"std::array<uint{reg_size*8}_t, {len(sources)}>"
|
||
|
lines = [
|
||
|
f"{var_type} {name}_gather{{}};"
|
||
|
]
|
||
|
for i in range(0, len(sources)):
|
||
|
lines.append(
|
||
|
f"{name}_gather[{i}] = {get_fn}(system, {sources[i]});")
|
||
|
|
||
|
lines.append(f"{name} = Convert<{type_name}>({name}_gather);")
|
||
|
return lines
|
||
|
|
||
|
|
||
|
# Produces one or more statements which assign the named C++ value
|
||
|
# into possibly multiple registers.
|
||
|
def emit_scatter(destinations, name, reg_size):
|
||
|
set_fn = f"SetReg{reg_size*8}"
|
||
|
reg_type = f"uint{reg_size*8}_t"
|
||
|
|
||
|
if len(destinations) == 1:
|
||
|
d, = destinations
|
||
|
line = f"{set_fn}(system, {d}, Convert<{reg_type}>({name}));"
|
||
|
return [line]
|
||
|
|
||
|
var_type = f"std::array<{reg_type}, {len(destinations)}>"
|
||
|
lines = [
|
||
|
f"auto {name}_scatter = Convert<{var_type}>({name});"
|
||
|
]
|
||
|
|
||
|
for i in range(0, len(destinations)):
|
||
|
lines.append(
|
||
|
f"{set_fn}(system, {destinations[i]}, {name}_scatter[{i}]);")
|
||
|
|
||
|
return lines
|
||
|
|
||
|
|
||
|
def emit_lines(lines, indent=' '):
|
||
|
output_lines = []
|
||
|
first = True
|
||
|
for line in lines:
|
||
|
if line and not first:
|
||
|
output_lines.append(indent + line)
|
||
|
else:
|
||
|
output_lines.append(line)
|
||
|
first = False
|
||
|
|
||
|
return "\n".join(output_lines)
|
||
|
|
||
|
|
||
|
# Emit a C++ function to wrap a guest SVC.
|
||
|
def emit_wrapper(wrapped_fn, suffix, register_info, arguments, byte_size):
|
||
|
return_write, output_writes, input_reads = register_info
|
||
|
lines = [
|
||
|
f"static void SvcWrap_{wrapped_fn}{suffix}(Core::System& system) {{"
|
||
|
]
|
||
|
|
||
|
# Get everything ready.
|
||
|
for return_type, _ in return_write:
|
||
|
lines.append(f"{return_type} ret{{}};")
|
||
|
if return_write:
|
||
|
lines.append("")
|
||
|
|
||
|
for output_type, var_name, _, is_address in output_writes:
|
||
|
output_type = "uintptr_t" if is_address else output_type
|
||
|
lines.append(f"{output_type} {var_name}{{}};")
|
||
|
for input_type, var_name, _ in input_reads:
|
||
|
lines.append(f"{input_type} {var_name}{{}};")
|
||
|
|
||
|
if output_writes or input_reads:
|
||
|
lines.append("")
|
||
|
|
||
|
for input_type, var_name, sources in input_reads:
|
||
|
lines += emit_gather(sources, var_name, input_type, byte_size)
|
||
|
if input_reads:
|
||
|
lines.append("")
|
||
|
|
||
|
# Build the call.
|
||
|
call_arguments = ["system"]
|
||
|
for arg in arguments:
|
||
|
if arg.is_output and not arg.is_outptr:
|
||
|
call_arguments.append(f"&{arg.var_name}")
|
||
|
else:
|
||
|
call_arguments.append(arg.var_name)
|
||
|
|
||
|
line = ""
|
||
|
if return_write:
|
||
|
line += "ret = "
|
||
|
|
||
|
line += f"{wrapped_fn}{suffix}({', '.join(call_arguments)});"
|
||
|
lines.append(line)
|
||
|
|
||
|
if return_write or output_writes:
|
||
|
lines.append("")
|
||
|
|
||
|
# Write back the return value and outputs.
|
||
|
for _, destinations in return_write:
|
||
|
lines += emit_scatter(destinations, "ret", byte_size)
|
||
|
for _, var_name, destinations, _ in output_writes:
|
||
|
lines += emit_scatter(destinations, var_name, byte_size)
|
||
|
|
||
|
# Finish.
|
||
|
return emit_lines(lines) + "\n}"
|
||
|
|
||
|
|
||
|
COPYRIGHT = """\
|
||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
|
||
|
// This file is automatically generated using svc_generator.py.
|
||
|
"""
|
||
|
|
||
|
PROLOGUE_H = """
|
||
|
#pragma once
|
||
|
|
||
|
namespace Core {
|
||
|
class System;
|
||
|
}
|
||
|
|
||
|
#include "common/common_types.h"
|
||
|
#include "core/hle/kernel/svc_types.h"
|
||
|
#include "core/hle/result.h"
|
||
|
|
||
|
namespace Kernel::Svc {
|
||
|
|
||
|
// clang-format off
|
||
|
"""
|
||
|
|
||
|
EPILOGUE_H = """
|
||
|
// clang-format on
|
||
|
|
||
|
// Custom ABI.
|
||
|
Result ReplyAndReceiveLight(Core::System& system, Handle handle, uint32_t* args);
|
||
|
Result ReplyAndReceiveLight64From32(Core::System& system, Handle handle, uint32_t* args);
|
||
|
Result ReplyAndReceiveLight64(Core::System& system, Handle handle, uint32_t* args);
|
||
|
|
||
|
Result SendSyncRequestLight(Core::System& system, Handle session_handle, uint32_t* args);
|
||
|
Result SendSyncRequestLight64From32(Core::System& system, Handle session_handle, uint32_t* args);
|
||
|
Result SendSyncRequestLight64(Core::System& system, Handle session_handle, uint32_t* args);
|
||
|
|
||
|
void CallSecureMonitor(Core::System& system, lp64::SecureMonitorArguments* args);
|
||
|
void CallSecureMonitor64From32(Core::System& system, ilp32::SecureMonitorArguments* args);
|
||
|
void CallSecureMonitor64(Core::System& system, lp64::SecureMonitorArguments* args);
|
||
|
|
||
|
// Defined in svc_light_ipc.cpp.
|
||
|
void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system);
|
||
|
void SvcWrap_ReplyAndReceiveLight64(Core::System& system);
|
||
|
|
||
|
void SvcWrap_SendSyncRequestLight64From32(Core::System& system);
|
||
|
void SvcWrap_SendSyncRequestLight64(Core::System& system);
|
||
|
|
||
|
// Defined in svc_secure_monitor_call.cpp.
|
||
|
void SvcWrap_CallSecureMonitor64From32(Core::System& system);
|
||
|
void SvcWrap_CallSecureMonitor64(Core::System& system);
|
||
|
|
||
|
// Perform a supervisor call by index.
|
||
|
void Call(Core::System& system, u32 imm);
|
||
|
|
||
|
} // namespace Kernel::Svc
|
||
|
"""
|
||
|
|
||
|
PROLOGUE_CPP = """
|
||
|
#include <type_traits>
|
||
|
|
||
|
#include "core/arm/arm_interface.h"
|
||
|
#include "core/core.h"
|
||
|
#include "core/hle/kernel/k_process.h"
|
||
|
#include "core/hle/kernel/svc.h"
|
||
|
|
||
|
namespace Kernel::Svc {
|
||
|
|
||
|
static uint32_t GetReg32(Core::System& system, int n) {
|
||
|
return static_cast<uint32_t>(system.CurrentArmInterface().GetReg(n));
|
||
|
}
|
||
|
|
||
|
static void SetReg32(Core::System& system, int n, uint32_t result) {
|
||
|
system.CurrentArmInterface().SetReg(n, static_cast<uint64_t>(result));
|
||
|
}
|
||
|
|
||
|
static uint64_t GetReg64(Core::System& system, int n) {
|
||
|
return system.CurrentArmInterface().GetReg(n);
|
||
|
}
|
||
|
|
||
|
static void SetReg64(Core::System& system, int n, uint64_t result) {
|
||
|
system.CurrentArmInterface().SetReg(n, result);
|
||
|
}
|
||
|
|
||
|
// Like bit_cast, but handles the case when the source and dest
|
||
|
// are differently-sized.
|
||
|
template <typename To, typename From>
|
||
|
requires(std::is_trivial_v<To> && std::is_trivially_copyable_v<From>)
|
||
|
static To Convert(const From& from) {
|
||
|
To to{};
|
||
|
|
||
|
if constexpr (sizeof(To) >= sizeof(From)) {
|
||
|
std::memcpy(&to, &from, sizeof(From));
|
||
|
} else {
|
||
|
std::memcpy(&to, &from, sizeof(To));
|
||
|
}
|
||
|
|
||
|
return to;
|
||
|
}
|
||
|
|
||
|
// clang-format off
|
||
|
"""
|
||
|
|
||
|
EPILOGUE_CPP = """
|
||
|
// clang-format on
|
||
|
|
||
|
void Call(Core::System& system, u32 imm) {
|
||
|
auto& kernel = system.Kernel();
|
||
|
kernel.EnterSVCProfile();
|
||
|
|
||
|
if (system.CurrentProcess()->Is64BitProcess()) {
|
||
|
Call64(system, imm);
|
||
|
} else {
|
||
|
Call32(system, imm);
|
||
|
}
|
||
|
|
||
|
kernel.ExitSVCProfile();
|
||
|
}
|
||
|
|
||
|
} // namespace Kernel::Svc
|
||
|
"""
|
||
|
|
||
|
|
||
|
def emit_call(bitness, names, suffix):
|
||
|
bit_size = REG_SIZES[bitness]*8
|
||
|
indent = " "
|
||
|
lines = [
|
||
|
f"static void Call{bit_size}(Core::System& system, u32 imm) {{",
|
||
|
f"{indent}switch (static_cast<SvcId>(imm)) {{"
|
||
|
]
|
||
|
|
||
|
for _, name in names:
|
||
|
lines.append(f"{indent}case SvcId::{name}:")
|
||
|
lines.append(f"{indent*2}return SvcWrap_{name}{suffix}(system);")
|
||
|
|
||
|
lines.append(f"{indent}default:")
|
||
|
lines.append(
|
||
|
f"{indent*2}LOG_CRITICAL(Kernel_SVC, \"Unknown SVC {{:x}}!\", imm);")
|
||
|
lines.append(f"{indent*2}break;")
|
||
|
lines.append(f"{indent}}}")
|
||
|
lines.append("}")
|
||
|
|
||
|
return "\n".join(lines)
|
||
|
|
||
|
|
||
|
def build_fn_declaration(return_type, name, arguments):
|
||
|
arg_list = ["Core::System& system"]
|
||
|
for arg in arguments:
|
||
|
type_name = "uintptr_t" if arg.is_address else arg.type_name
|
||
|
pointer = "*" if arg.is_output and not arg.is_outptr else ""
|
||
|
arg_list.append(f"{type_name}{pointer} {arg.var_name}")
|
||
|
|
||
|
return f"{return_type} {name}({', '.join(arg_list)});"
|
||
|
|
||
|
|
||
|
def build_enum_declarations():
|
||
|
lines = ["enum class SvcId : u32 {"]
|
||
|
indent = " "
|
||
|
|
||
|
for imm, decl in SVCS:
|
||
|
_, name, _ = parse_declaration(decl, BIT_64)
|
||
|
lines.append(f"{indent}{name} = {hex(imm)},")
|
||
|
|
||
|
lines.append("};")
|
||
|
return "\n".join(lines)
|
||
|
|
||
|
|
||
|
def main():
|
||
|
arch_fw_declarations = [[], []]
|
||
|
svc_fw_declarations = []
|
||
|
wrapper_fns = []
|
||
|
names = []
|
||
|
|
||
|
for imm, decl in SVCS:
|
||
|
return_type, name, arguments = parse_declaration(decl, BIT_64)
|
||
|
|
||
|
if imm not in SKIP_WRAPPERS:
|
||
|
svc_fw_declarations.append(
|
||
|
build_fn_declaration(return_type, name, arguments))
|
||
|
|
||
|
names.append([imm, name])
|
||
|
|
||
|
for bitness in range(2):
|
||
|
byte_size = REG_SIZES[bitness]
|
||
|
suffix = SUFFIX_NAMES[bitness]
|
||
|
|
||
|
for imm, decl in SVCS:
|
||
|
if imm in SKIP_WRAPPERS:
|
||
|
continue
|
||
|
|
||
|
parse_result = parse_declaration(decl, bitness)
|
||
|
return_type, name, arguments = parse_result
|
||
|
|
||
|
register_info = get_registers(parse_result, bitness)
|
||
|
wrapper_fns.append(
|
||
|
emit_wrapper(name, suffix, register_info, arguments, byte_size))
|
||
|
arch_fw_declarations[bitness].append(
|
||
|
build_fn_declaration(return_type, name + suffix, arguments))
|
||
|
|
||
|
call_32 = emit_call(BIT_32, names, SUFFIX_NAMES[BIT_32])
|
||
|
call_64 = emit_call(BIT_64, names, SUFFIX_NAMES[BIT_64])
|
||
|
enum_decls = build_enum_declarations()
|
||
|
|
||
|
with open("svc.h", "w") as f:
|
||
|
f.write(COPYRIGHT)
|
||
|
f.write(PROLOGUE_H)
|
||
|
f.write("\n".join(svc_fw_declarations))
|
||
|
f.write("\n\n")
|
||
|
f.write("\n".join(arch_fw_declarations[BIT_32]))
|
||
|
f.write("\n\n")
|
||
|
f.write("\n".join(arch_fw_declarations[BIT_64]))
|
||
|
f.write("\n\n")
|
||
|
f.write(enum_decls)
|
||
|
f.write(EPILOGUE_H)
|
||
|
|
||
|
with open("svc.cpp", "w") as f:
|
||
|
f.write(COPYRIGHT)
|
||
|
f.write(PROLOGUE_CPP)
|
||
|
f.write(emit_size_check())
|
||
|
f.write("\n\n")
|
||
|
f.write("\n\n".join(wrapper_fns))
|
||
|
f.write("\n\n")
|
||
|
f.write(call_32)
|
||
|
f.write("\n\n")
|
||
|
f.write(call_64)
|
||
|
f.write(EPILOGUE_CPP)
|
||
|
|
||
|
print(f"Done (emitted {len(names)} definitions)")
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|