mirror of
https://github.com/citra-mirror/citra.git
synced 2024-08-19 10:03:22 +02:00
Merge pull request #4405 from wwylele/svc-global
SVC: new wrapper template & pass system reference across the SVC barrier
This commit is contained in:
commit
560df843b1
@ -103,7 +103,6 @@ add_library(core STATIC
|
|||||||
hle/applets/mint.h
|
hle/applets/mint.h
|
||||||
hle/applets/swkbd.cpp
|
hle/applets/swkbd.cpp
|
||||||
hle/applets/swkbd.h
|
hle/applets/swkbd.h
|
||||||
hle/function_wrappers.h
|
|
||||||
hle/ipc.h
|
hle/ipc.h
|
||||||
hle/ipc_helpers.h
|
hle/ipc_helpers.h
|
||||||
hle/kernel/address_arbiter.cpp
|
hle/kernel/address_arbiter.cpp
|
||||||
@ -148,6 +147,7 @@ add_library(core STATIC
|
|||||||
hle/kernel/shared_page.h
|
hle/kernel/shared_page.h
|
||||||
hle/kernel/svc.cpp
|
hle/kernel/svc.cpp
|
||||||
hle/kernel/svc.h
|
hle/kernel/svc.h
|
||||||
|
hle/kernel/svc_wrapper.h
|
||||||
hle/kernel/thread.cpp
|
hle/kernel/thread.cpp
|
||||||
hle/kernel/thread.h
|
hle/kernel/thread.h
|
||||||
hle/kernel/timer.cpp
|
hle/kernel/timer.cpp
|
||||||
|
@ -72,7 +72,7 @@ private:
|
|||||||
class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks {
|
class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks {
|
||||||
public:
|
public:
|
||||||
explicit DynarmicUserCallbacks(ARM_Dynarmic& parent)
|
explicit DynarmicUserCallbacks(ARM_Dynarmic& parent)
|
||||||
: parent(parent), timing(parent.system.CoreTiming()) {}
|
: parent(parent), timing(parent.system.CoreTiming()), svc_context(parent.system) {}
|
||||||
~DynarmicUserCallbacks() = default;
|
~DynarmicUserCallbacks() = default;
|
||||||
|
|
||||||
std::uint8_t MemoryRead8(VAddr vaddr) override {
|
std::uint8_t MemoryRead8(VAddr vaddr) override {
|
||||||
@ -123,7 +123,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CallSVC(std::uint32_t swi) override {
|
void CallSVC(std::uint32_t swi) override {
|
||||||
Kernel::CallSVC(swi);
|
svc_context.CallSVC(swi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExceptionRaised(VAddr pc, Dynarmic::A32::Exception exception) override {
|
void ExceptionRaised(VAddr pc, Dynarmic::A32::Exception exception) override {
|
||||||
@ -158,6 +158,7 @@ public:
|
|||||||
|
|
||||||
ARM_Dynarmic& parent;
|
ARM_Dynarmic& parent;
|
||||||
Core::Timing& timing;
|
Core::Timing& timing;
|
||||||
|
Kernel::SVCContext svc_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
ARM_Dynarmic::ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode)
|
ARM_Dynarmic::ARM_Dynarmic(Core::System& system, PrivilegeMode initial_mode)
|
||||||
|
@ -3864,7 +3864,7 @@ SWI_INST : {
|
|||||||
cpu->NumInstrsToExecute =
|
cpu->NumInstrsToExecute =
|
||||||
num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs;
|
num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs;
|
||||||
num_instrs = 0;
|
num_instrs = 0;
|
||||||
Kernel::CallSVC(inst_cream->num & 0xFFFF);
|
Kernel::SVCContext{Core::System::GetInstance()}.CallSVC(inst_cream->num & 0xFFFF);
|
||||||
// The kernel would call ERET to get here, which clears exclusive memory state.
|
// The kernel would call ERET to get here, which clears exclusive memory state.
|
||||||
cpu->UnsetExclusiveMemoryAddress();
|
cpu->UnsetExclusiveMemoryAddress();
|
||||||
}
|
}
|
||||||
|
@ -172,6 +172,8 @@ System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) {
|
|||||||
|
|
||||||
timing = std::make_unique<Timing>();
|
timing = std::make_unique<Timing>();
|
||||||
|
|
||||||
|
kernel = std::make_unique<Kernel::KernelSystem>(system_mode);
|
||||||
|
|
||||||
if (Settings::values.use_cpu_jit) {
|
if (Settings::values.use_cpu_jit) {
|
||||||
#ifdef ARCHITECTURE_x86_64
|
#ifdef ARCHITECTURE_x86_64
|
||||||
cpu_core = std::make_unique<ARM_Dynarmic>(*this, USER32MODE);
|
cpu_core = std::make_unique<ARM_Dynarmic>(*this, USER32MODE);
|
||||||
@ -197,7 +199,6 @@ System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) {
|
|||||||
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
|
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
|
||||||
|
|
||||||
HW::Init();
|
HW::Init();
|
||||||
kernel = std::make_unique<Kernel::KernelSystem>(system_mode);
|
|
||||||
Service::Init(*this);
|
Service::Init(*this);
|
||||||
GDBStub::Init();
|
GDBStub::Init();
|
||||||
|
|
||||||
|
@ -1,265 +0,0 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "core/arm/arm_interface.h"
|
|
||||||
#include "core/core.h"
|
|
||||||
#include "core/hle/kernel/kernel.h"
|
|
||||||
#include "core/hle/kernel/svc.h"
|
|
||||||
#include "core/hle/result.h"
|
|
||||||
#include "core/memory.h"
|
|
||||||
|
|
||||||
namespace HLE {
|
|
||||||
|
|
||||||
static inline u32 Param(int n) {
|
|
||||||
return Core::CPU().GetReg(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HLE a function return from the current ARM11 userland process
|
|
||||||
* @param res Result to return
|
|
||||||
*/
|
|
||||||
static inline void FuncReturn(u32 res) {
|
|
||||||
Core::CPU().SetReg(0, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HLE a function return (64-bit) from the current ARM11 userland process
|
|
||||||
* @param res Result to return (64-bit)
|
|
||||||
* @todo Verify that this function is correct
|
|
||||||
*/
|
|
||||||
static inline void FuncReturn64(u64 res) {
|
|
||||||
Core::CPU().SetReg(0, (u32)(res & 0xFFFFFFFF));
|
|
||||||
Core::CPU().SetReg(1, (u32)((res >> 32) & 0xFFFFFFFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Function wrappers that return type ResultCode
|
|
||||||
|
|
||||||
template <ResultCode func(u32, u32, u32, u32)>
|
|
||||||
void Wrap() {
|
|
||||||
FuncReturn(func(Param(0), Param(1), Param(2), Param(3)).raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(u32*, u32, u32, u32, u32, u32)>
|
|
||||||
void Wrap() {
|
|
||||||
u32 param_1 = 0;
|
|
||||||
u32 retval = func(¶m_1, Param(0), Param(1), Param(2), Param(3), Param(4)).raw;
|
|
||||||
Core::CPU().SetReg(1, param_1);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(u32*, u32, u32, u32, u32, s32)>
|
|
||||||
void Wrap() {
|
|
||||||
u32 param_1 = 0;
|
|
||||||
u32 retval = func(¶m_1, Param(0), Param(1), Param(2), Param(3), Param(4)).raw;
|
|
||||||
Core::CPU().SetReg(1, param_1);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(s32*, VAddr, s32, bool, s64)>
|
|
||||||
void Wrap() {
|
|
||||||
s32 param_1 = 0;
|
|
||||||
s32 retval =
|
|
||||||
func(¶m_1, Param(1), (s32)Param(2), (Param(3) != 0), (((s64)Param(4) << 32) | Param(0)))
|
|
||||||
.raw;
|
|
||||||
|
|
||||||
Core::CPU().SetReg(1, (u32)param_1);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(s32*, VAddr, s32, u32)>
|
|
||||||
void Wrap() {
|
|
||||||
s32 param_1 = 0;
|
|
||||||
u32 retval = func(¶m_1, Param(1), (s32)Param(2), Param(3)).raw;
|
|
||||||
|
|
||||||
Core::CPU().SetReg(1, (u32)param_1);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(u32, u32, u32, u32, s64)>
|
|
||||||
void Wrap() {
|
|
||||||
FuncReturn(
|
|
||||||
func(Param(0), Param(1), Param(2), Param(3), (((s64)Param(5) << 32) | Param(4))).raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(u32*)>
|
|
||||||
void Wrap() {
|
|
||||||
u32 param_1 = 0;
|
|
||||||
u32 retval = func(¶m_1).raw;
|
|
||||||
Core::CPU().SetReg(1, param_1);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(u32, s64)>
|
|
||||||
void Wrap() {
|
|
||||||
s32 retval = func(Param(0), (((s64)Param(3) << 32) | Param(2))).raw;
|
|
||||||
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(Kernel::MemoryInfo*, Kernel::PageInfo*, u32)>
|
|
||||||
void Wrap() {
|
|
||||||
Kernel::MemoryInfo memory_info = {};
|
|
||||||
Kernel::PageInfo page_info = {};
|
|
||||||
u32 retval = func(&memory_info, &page_info, Param(2)).raw;
|
|
||||||
Core::CPU().SetReg(1, memory_info.base_address);
|
|
||||||
Core::CPU().SetReg(2, memory_info.size);
|
|
||||||
Core::CPU().SetReg(3, memory_info.permission);
|
|
||||||
Core::CPU().SetReg(4, memory_info.state);
|
|
||||||
Core::CPU().SetReg(5, page_info.flags);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(Kernel::MemoryInfo*, Kernel::PageInfo*, Kernel::Handle, u32)>
|
|
||||||
void Wrap() {
|
|
||||||
Kernel::MemoryInfo memory_info = {};
|
|
||||||
Kernel::PageInfo page_info = {};
|
|
||||||
u32 retval = func(&memory_info, &page_info, Param(2), Param(3)).raw;
|
|
||||||
Core::CPU().SetReg(1, memory_info.base_address);
|
|
||||||
Core::CPU().SetReg(2, memory_info.size);
|
|
||||||
Core::CPU().SetReg(3, memory_info.permission);
|
|
||||||
Core::CPU().SetReg(4, memory_info.state);
|
|
||||||
Core::CPU().SetReg(5, page_info.flags);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(s32*, u32)>
|
|
||||||
void Wrap() {
|
|
||||||
s32 param_1 = 0;
|
|
||||||
u32 retval = func(¶m_1, Param(1)).raw;
|
|
||||||
Core::CPU().SetReg(1, param_1);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(u32, s32)>
|
|
||||||
void Wrap() {
|
|
||||||
FuncReturn(func(Param(0), (s32)Param(1)).raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(u32*, u32)>
|
|
||||||
void Wrap() {
|
|
||||||
u32 param_1 = 0;
|
|
||||||
u32 retval = func(¶m_1, Param(1)).raw;
|
|
||||||
Core::CPU().SetReg(1, param_1);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(u32)>
|
|
||||||
void Wrap() {
|
|
||||||
FuncReturn(func(Param(0)).raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(u32*, s32, s32)>
|
|
||||||
void Wrap() {
|
|
||||||
u32 param_1 = 0;
|
|
||||||
u32 retval = func(¶m_1, Param(1), Param(2)).raw;
|
|
||||||
Core::CPU().SetReg(1, param_1);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(s32*, u32, s32)>
|
|
||||||
void Wrap() {
|
|
||||||
s32 param_1 = 0;
|
|
||||||
u32 retval = func(¶m_1, Param(1), Param(2)).raw;
|
|
||||||
Core::CPU().SetReg(1, param_1);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(s64*, u32, s32)>
|
|
||||||
void Wrap() {
|
|
||||||
s64 param_1 = 0;
|
|
||||||
u32 retval = func(¶m_1, Param(1), Param(2)).raw;
|
|
||||||
Core::CPU().SetReg(1, (u32)param_1);
|
|
||||||
Core::CPU().SetReg(2, (u32)(param_1 >> 32));
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(u32*, u32, u32, u32, u32)>
|
|
||||||
void Wrap() {
|
|
||||||
u32 param_1 = 0;
|
|
||||||
// The last parameter is passed in R0 instead of R4
|
|
||||||
u32 retval = func(¶m_1, Param(1), Param(2), Param(3), Param(0)).raw;
|
|
||||||
Core::CPU().SetReg(1, param_1);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(u32, s64, s64)>
|
|
||||||
void Wrap() {
|
|
||||||
s64 param1 = ((u64)Param(3) << 32) | Param(2);
|
|
||||||
s64 param2 = ((u64)Param(4) << 32) | Param(1);
|
|
||||||
FuncReturn(func(Param(0), param1, param2).raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(s64*, Kernel::Handle, u32)>
|
|
||||||
void Wrap() {
|
|
||||||
s64 param_1 = 0;
|
|
||||||
u32 retval = func(¶m_1, Param(1), Param(2)).raw;
|
|
||||||
Core::CPU().SetReg(1, (u32)param_1);
|
|
||||||
Core::CPU().SetReg(2, (u32)(param_1 >> 32));
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(Kernel::Handle, u32)>
|
|
||||||
void Wrap() {
|
|
||||||
FuncReturn(func(Param(0), Param(1)).raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(Kernel::Handle*, Kernel::Handle*, VAddr, u32)>
|
|
||||||
void Wrap() {
|
|
||||||
Kernel::Handle param_1 = 0;
|
|
||||||
Kernel::Handle param_2 = 0;
|
|
||||||
u32 retval = func(¶m_1, ¶m_2, Param(2), Param(3)).raw;
|
|
||||||
Core::CPU().SetReg(1, param_1);
|
|
||||||
Core::CPU().SetReg(2, param_2);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ResultCode func(Kernel::Handle*, Kernel::Handle*)>
|
|
||||||
void Wrap() {
|
|
||||||
Kernel::Handle param_1 = 0;
|
|
||||||
Kernel::Handle param_2 = 0;
|
|
||||||
u32 retval = func(¶m_1, ¶m_2).raw;
|
|
||||||
Core::CPU().SetReg(1, param_1);
|
|
||||||
Core::CPU().SetReg(2, param_2);
|
|
||||||
FuncReturn(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Function wrappers that return type u32
|
|
||||||
|
|
||||||
template <u32 func()>
|
|
||||||
void Wrap() {
|
|
||||||
FuncReturn(func());
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Function wrappers that return type s64
|
|
||||||
|
|
||||||
template <s64 func()>
|
|
||||||
void Wrap() {
|
|
||||||
FuncReturn64(func());
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// Function wrappers that return type void
|
|
||||||
|
|
||||||
template <void func(s64)>
|
|
||||||
void Wrap() {
|
|
||||||
func(((s64)Param(1) << 32) | Param(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <void func(VAddr, int len)>
|
|
||||||
void Wrap() {
|
|
||||||
func(Param(0), Param(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <void func(u8)>
|
|
||||||
void Wrap() {
|
|
||||||
func((u8)Param(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace HLE
|
|
File diff suppressed because it is too large
Load Diff
@ -4,50 +4,25 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
} // namespace Core
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
struct MemoryInfo {
|
class SVC;
|
||||||
u32 base_address;
|
|
||||||
u32 size;
|
|
||||||
u32 permission;
|
|
||||||
u32 state;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PageInfo {
|
|
||||||
u32 flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Values accepted by svcGetSystemInfo's type parameter.
|
|
||||||
enum class SystemInfoType {
|
|
||||||
/**
|
|
||||||
* Reports total used memory for all regions or a specific one, according to the extra
|
|
||||||
* parameter. See `SystemInfoMemUsageRegion`.
|
|
||||||
*/
|
|
||||||
REGION_MEMORY_USAGE = 0,
|
|
||||||
/**
|
|
||||||
* Returns the memory usage for certain allocations done internally by the kernel.
|
|
||||||
*/
|
|
||||||
KERNEL_ALLOCATED_PAGES = 2,
|
|
||||||
/**
|
|
||||||
* "This returns the total number of processes which were launched directly by the kernel.
|
|
||||||
* For the ARM11 NATIVE_FIRM kernel, this is 5, for processes sm, fs, pm, loader, and pxi."
|
|
||||||
*/
|
|
||||||
KERNEL_SPAWNED_PIDS = 26,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accepted by svcGetSystemInfo param with REGION_MEMORY_USAGE type. Selects a region to query
|
|
||||||
* memory usage of.
|
|
||||||
*/
|
|
||||||
enum class SystemInfoMemUsageRegion {
|
|
||||||
ALL = 0,
|
|
||||||
APPLICATION = 1,
|
|
||||||
SYSTEM = 2,
|
|
||||||
BASE = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
class SVCContext {
|
||||||
|
public:
|
||||||
|
SVCContext(Core::System& system);
|
||||||
|
~SVCContext();
|
||||||
void CallSVC(u32 immediate);
|
void CallSVC(u32 immediate);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<SVC> impl;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
294
src/core/hle/kernel/svc_wrapper.h
Normal file
294
src/core/hle/kernel/svc_wrapper.h
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
#include <type_traits>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This class defines the SVC ABI, and provides a wrapper template for translating between ARM
|
||||||
|
* registers and SVC parameters and return value. The SVC context class should inherit from this
|
||||||
|
* class using CRTP (`class SVC : public SVCWrapper<SVC> {...}`), and use the Wrap() template to
|
||||||
|
* convert a SVC function interface to a void()-type function that interacts with registers
|
||||||
|
* interface GetReg() and SetReg().
|
||||||
|
*/
|
||||||
|
template <typename Context>
|
||||||
|
class SVCWrapper {
|
||||||
|
protected:
|
||||||
|
template <auto F>
|
||||||
|
void Wrap() {
|
||||||
|
WrapHelper<decltype(F)>::Call(*static_cast<Context*>(this), F);
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
// A special param index represents the return value
|
||||||
|
static constexpr std::size_t INDEX_RETURN = ~(std::size_t)0;
|
||||||
|
|
||||||
|
struct ParamSlot {
|
||||||
|
// whether the slot is used for a param
|
||||||
|
bool used;
|
||||||
|
|
||||||
|
// index of a param in the function signature, starting from 0, or special value
|
||||||
|
// INDEX_RETURN
|
||||||
|
std::size_t param_index;
|
||||||
|
|
||||||
|
// index of a 32-bit word within the param
|
||||||
|
std::size_t word_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Size in words of the given type in ARM ABI
|
||||||
|
template <typename T>
|
||||||
|
static constexpr std::size_t WordSize() {
|
||||||
|
static_assert(std::is_trivially_copyable_v<T>);
|
||||||
|
if constexpr (std::is_pointer_v<T>) {
|
||||||
|
return 1;
|
||||||
|
} else if constexpr (std::is_same_v<T, bool>) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return (sizeof(T) - 1) / 4 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size in words of the given type in ARM ABI with pointer removed
|
||||||
|
template <typename T>
|
||||||
|
static constexpr std::size_t OutputWordSize() {
|
||||||
|
if constexpr (std::is_pointer_v<T>) {
|
||||||
|
return WordSize<std::remove_pointer_t<T>>();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes the register assignments of a SVC
|
||||||
|
struct SVCABI {
|
||||||
|
static constexpr std::size_t RegCount = 8;
|
||||||
|
|
||||||
|
// Register assignments for input params.
|
||||||
|
// For example, in[0] records which input param and word r0 stores
|
||||||
|
std::array<ParamSlot, RegCount> in{};
|
||||||
|
|
||||||
|
// Register assignments for output params.
|
||||||
|
// For example, out[0] records which output param (with pointer removed) and word r0 stores
|
||||||
|
std::array<ParamSlot, RegCount> out{};
|
||||||
|
|
||||||
|
// Search the register assignments for a word of a param
|
||||||
|
constexpr std::size_t GetRegisterIndex(std::size_t param_index,
|
||||||
|
std::size_t word_index) const {
|
||||||
|
for (std::size_t slot = 0; slot < RegCount; ++slot) {
|
||||||
|
if (in[slot].used && in[slot].param_index == param_index &&
|
||||||
|
in[slot].word_index == word_index) {
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
if (out[slot].used && out[slot].param_index == param_index &&
|
||||||
|
out[slot].word_index == word_index) {
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// should throw, but gcc bugs out here
|
||||||
|
return 0x12345678;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generates ABI info for a given SVC signature R(Context::)(T...)
|
||||||
|
template <typename R, typename... T>
|
||||||
|
static constexpr SVCABI GetSVCABI() {
|
||||||
|
constexpr std::size_t param_count = sizeof...(T);
|
||||||
|
std::array<bool, param_count> param_is_output{{std::is_pointer_v<T>...}};
|
||||||
|
std::array<std::size_t, param_count> param_size{{WordSize<T>()...}};
|
||||||
|
std::array<std::size_t, param_count> output_param_size{{OutputWordSize<T>()...}};
|
||||||
|
std::array<bool, param_count> param_need_align{
|
||||||
|
{(std::is_same_v<T, u64> || std::is_same_v<T, s64>)...}};
|
||||||
|
|
||||||
|
// derives ARM ABI, which assigns all params to r0~r3 and then stack
|
||||||
|
std::array<ParamSlot, 4> armabi_reg{};
|
||||||
|
std::array<ParamSlot, 4> armabi_stack{};
|
||||||
|
std::size_t reg_pos = 0;
|
||||||
|
std::size_t stack_pos = 0;
|
||||||
|
for (std::size_t i = 0; i < param_count; ++i) {
|
||||||
|
if (param_need_align[i]) {
|
||||||
|
reg_pos += reg_pos % 2;
|
||||||
|
}
|
||||||
|
for (std::size_t j = 0; j < param_size[i]; ++j) {
|
||||||
|
if (reg_pos == 4) {
|
||||||
|
armabi_stack[stack_pos++] = ParamSlot{true, i, j};
|
||||||
|
} else {
|
||||||
|
armabi_reg[reg_pos++] = ParamSlot{true, i, j};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now derives SVC ABI which is a modified version of ARM ABI
|
||||||
|
|
||||||
|
SVCABI mod_abi{};
|
||||||
|
std::size_t out_pos = 0; // next available output register
|
||||||
|
|
||||||
|
// assign return value to output registers
|
||||||
|
if constexpr (!std::is_void_v<R>) {
|
||||||
|
for (std::size_t j = 0; j < WordSize<R>(); ++j) {
|
||||||
|
mod_abi.out[out_pos++] = {true, INDEX_RETURN, j};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign output params (pointer-type params) to output registers
|
||||||
|
for (std::size_t i = 0; i < param_count; ++i) {
|
||||||
|
if (param_is_output[i]) {
|
||||||
|
for (std::size_t j = 0; j < output_param_size[i]; ++j) {
|
||||||
|
mod_abi.out[out_pos++] = ParamSlot{true, i, j};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign input params (non-pointer-type params) to input registers
|
||||||
|
stack_pos = 0; // next available stack param
|
||||||
|
for (std::size_t k = 0; k < mod_abi.in.size(); ++k) {
|
||||||
|
if (k < armabi_reg.size() && armabi_reg[k].used &&
|
||||||
|
!param_is_output[armabi_reg[k].param_index]) {
|
||||||
|
// If this is within the ARM ABI register range and it is a used input param, use
|
||||||
|
// the same register position
|
||||||
|
mod_abi.in[k] = armabi_reg[k];
|
||||||
|
} else {
|
||||||
|
// Otherwise, assign it with the next available stack input
|
||||||
|
// If all stack inputs have been allocated, this would do nothing
|
||||||
|
// and leaves the slot unused.
|
||||||
|
// Loop until an input stack param is found
|
||||||
|
while (stack_pos < armabi_stack.size() &&
|
||||||
|
(!armabi_stack[stack_pos].used ||
|
||||||
|
param_is_output[armabi_stack[stack_pos].param_index])) {
|
||||||
|
++stack_pos;
|
||||||
|
}
|
||||||
|
// Reassign the stack param to the free register
|
||||||
|
if (stack_pos < armabi_stack.size()) {
|
||||||
|
mod_abi.in[k] = armabi_stack[stack_pos++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mod_abi;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t param_index, std::size_t word_size, std::size_t... indices>
|
||||||
|
static constexpr std::array<std::size_t, word_size> GetRegIndicesImpl(
|
||||||
|
SVCABI abi, std::index_sequence<indices...>) {
|
||||||
|
return {{abi.GetRegisterIndex(param_index, indices)...}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets assigned register index for the param_index-th param of word_size in a function with
|
||||||
|
// signature R(Context::)(Ts...)
|
||||||
|
template <std::size_t param_index, std::size_t word_size, typename R, typename... Ts>
|
||||||
|
static constexpr std::array<std::size_t, word_size> GetRegIndices() {
|
||||||
|
constexpr SVCABI abi = GetSVCABI<R, Ts...>();
|
||||||
|
return GetRegIndicesImpl<param_index, word_size>(abi,
|
||||||
|
std::make_index_sequence<word_size>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the value for the param_index-th param of word_size in a function with signature
|
||||||
|
// R(Context::)(Ts...)
|
||||||
|
// T is the param type, which must be a non-pointer as it is an input param.
|
||||||
|
// Must hold: Ts[param_index] == T
|
||||||
|
template <std::size_t param_index, typename T, typename R, typename... Ts>
|
||||||
|
static void GetParam(Context& context, T& value) {
|
||||||
|
static_assert(!std::is_pointer_v<T>, "T should not be a pointer");
|
||||||
|
constexpr auto regi = GetRegIndices<param_index, WordSize<T>(), R, Ts...>();
|
||||||
|
if constexpr (std::is_class_v<T> || std::is_union_v<T>) {
|
||||||
|
std::array<u32, WordSize<T>()> buf;
|
||||||
|
for (std::size_t i = 0; i < WordSize<T>(); ++i) {
|
||||||
|
buf[i] = context.GetReg(regi[i]);
|
||||||
|
}
|
||||||
|
std::memcpy(&value, &buf, sizeof(T));
|
||||||
|
} else if constexpr (WordSize<T>() == 2) {
|
||||||
|
u64 l = context.GetReg(regi[0]);
|
||||||
|
u64 h = context.GetReg(regi[1]);
|
||||||
|
value = static_cast<T>(l | (h << 32));
|
||||||
|
} else if constexpr (std::is_same_v<T, bool>) {
|
||||||
|
// TODO: Is this correct or should only test the lowest byte?
|
||||||
|
value = context.GetReg(regi[0]) != 0;
|
||||||
|
} else {
|
||||||
|
value = static_cast<T>(context.GetReg(regi[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the value for the param_index-th param of word_size in a function with signature
|
||||||
|
// R(Context::)(Ts...)
|
||||||
|
// T is the param type with pointer removed, which was originally a pointer-type output param
|
||||||
|
// Must hold: (Ts[param_index] == T*) || (R==T && param_index == INDEX_RETURN)
|
||||||
|
template <std::size_t param_index, typename T, typename R, typename... Ts>
|
||||||
|
static void SetParam(Context& context, const T& value) {
|
||||||
|
static_assert(!std::is_pointer_v<T>, "T should have pointer removed before passing in");
|
||||||
|
constexpr auto regi = GetRegIndices<param_index, WordSize<T>(), R, Ts...>();
|
||||||
|
if constexpr (std::is_class_v<T> || std::is_union_v<T>) {
|
||||||
|
std::array<u32, WordSize<T>()> buf;
|
||||||
|
std::memcpy(&buf, &value, sizeof(T));
|
||||||
|
for (std::size_t i = 0; i < WordSize<T>(); ++i) {
|
||||||
|
context.SetReg(regi[i], buf[i]);
|
||||||
|
}
|
||||||
|
} else if constexpr (WordSize<T>() == 2) {
|
||||||
|
u64 u = static_cast<u64>(value);
|
||||||
|
context.SetReg(regi[0], static_cast<u32>(u & 0xFFFFFFFF));
|
||||||
|
context.SetReg(regi[1], static_cast<u32>(u >> 32));
|
||||||
|
} else {
|
||||||
|
u32 u = static_cast<u32>(value);
|
||||||
|
context.SetReg(regi[0], u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SVCT, typename R, typename... Ts>
|
||||||
|
struct WrapPass;
|
||||||
|
|
||||||
|
template <typename SVCT, typename R, typename T, typename... Ts>
|
||||||
|
struct WrapPass<SVCT, R, T, Ts...> {
|
||||||
|
// Do I/O for the param T in function R(Context::svc)(Us..., T, Ts...) and then move on to
|
||||||
|
// the next param.
|
||||||
|
|
||||||
|
// Us are params whose I/O is already handled.
|
||||||
|
// T is the current param to do I/O.
|
||||||
|
// Ts are params whose I/O is not handled yet.
|
||||||
|
template <typename... Us>
|
||||||
|
static void Call(Context& context, SVCT svc, Us... u) {
|
||||||
|
static_assert(std::is_same_v<SVCT, R (Context::*)(Us..., T, Ts...)>);
|
||||||
|
constexpr std::size_t current_param_index = sizeof...(Us);
|
||||||
|
if constexpr (std::is_pointer_v<T>) {
|
||||||
|
using OutputT = std::remove_pointer_t<T>;
|
||||||
|
OutputT output;
|
||||||
|
WrapPass<SVCT, R, Ts...>::Call(context, svc, u..., &output);
|
||||||
|
SetParam<current_param_index, OutputT, R, Us..., T, Ts...>(context, output);
|
||||||
|
} else {
|
||||||
|
T input;
|
||||||
|
GetParam<current_param_index, T, R, Us..., T, Ts...>(context, input);
|
||||||
|
WrapPass<SVCT, R, Ts...>::Call(context, svc, u..., input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename SVCT, typename R>
|
||||||
|
struct WrapPass<SVCT, R /*empty for T, Ts...*/> {
|
||||||
|
// Call function R(Context::svc)(Us...) and transfer the return value to registers
|
||||||
|
template <typename... Us>
|
||||||
|
static void Call(Context& context, SVCT svc, Us... u) {
|
||||||
|
static_assert(std::is_same_v<SVCT, R (Context::*)(Us...)>);
|
||||||
|
if constexpr (std::is_void_v<R>) {
|
||||||
|
(context.*svc)(u...);
|
||||||
|
} else {
|
||||||
|
R r = (context.*svc)(u...);
|
||||||
|
SetParam<INDEX_RETURN, R, R, Us...>(context, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct WrapHelper;
|
||||||
|
|
||||||
|
template <typename R, typename... T>
|
||||||
|
struct WrapHelper<R (Context::*)(T...)> {
|
||||||
|
static void Call(Context& context, R (Context::*svc)(T...)) {
|
||||||
|
WrapPass<decltype(svc), R, T...>::Call(context, svc /*Empty for Us*/);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Kernel
|
@ -216,10 +216,7 @@ union ResultCode {
|
|||||||
: raw(description.FormatValue(description_) | module.FormatValue(module_) |
|
: raw(description.FormatValue(description_) | module.FormatValue(module_) |
|
||||||
summary.FormatValue(summary_) | level.FormatValue(level_)) {}
|
summary.FormatValue(summary_) | level.FormatValue(level_)) {}
|
||||||
|
|
||||||
constexpr ResultCode& operator=(const ResultCode& o) {
|
constexpr ResultCode& operator=(const ResultCode& o) = default;
|
||||||
raw = o.raw;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool IsSuccess() const {
|
constexpr bool IsSuccess() const {
|
||||||
return is_error.ExtractValue(raw) == 0;
|
return is_error.ExtractValue(raw) == 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user