Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
|
||||
namespace ams::svc::arch::arm {
|
||||
|
||||
constexpr inline size_t NumTlsSlots = 16;
|
||||
constexpr inline size_t MessageBufferSize = 0x100;
|
||||
|
||||
struct ThreadLocalRegion {
|
||||
u32 message_buffer[MessageBufferSize / sizeof(u32)];
|
||||
volatile u16 disable_count;
|
||||
volatile u16 interrupt_flag;
|
||||
/* TODO: Should we bother adding the Nintendo aarch32 thread local context here? */
|
||||
uintptr_t TODO[(0x200 - 0x104) / sizeof(uintptr_t)];
|
||||
};
|
||||
|
||||
ALWAYS_INLINE ThreadLocalRegion *GetThreadLocalRegion() {
|
||||
ThreadLocalRegion *tlr;
|
||||
__asm__ __volatile__("mrc p15, 0, %[tlr], c13, c0, 3" : [tlr]"=&r"(tlr) :: "memory");
|
||||
return tlr;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
|
||||
namespace ams::svc::arch::arm64 {
|
||||
|
||||
constexpr inline size_t NumTlsSlots = 16;
|
||||
constexpr inline size_t MessageBufferSize = 0x100;
|
||||
|
||||
struct ThreadLocalRegion {
|
||||
u32 message_buffer[MessageBufferSize / sizeof(u32)];
|
||||
volatile u16 disable_count;
|
||||
volatile u16 interrupt_flag;
|
||||
volatile u8 cache_maintenance_flag;
|
||||
/* TODO: How should we handle libnx vs Nintendo user thread local space? */
|
||||
uintptr_t TODO[(0x200 - 0x108) / sizeof(uintptr_t)];
|
||||
};
|
||||
static_assert(__builtin_offsetof(ThreadLocalRegion, disable_count) == 0x100);
|
||||
static_assert(__builtin_offsetof(ThreadLocalRegion, interrupt_flag) == 0x102);
|
||||
static_assert(__builtin_offsetof(ThreadLocalRegion, cache_maintenance_flag) == 0x104);
|
||||
|
||||
ALWAYS_INLINE ThreadLocalRegion *GetThreadLocalRegion() {
|
||||
ThreadLocalRegion *tlr;
|
||||
__asm__ __volatile__("mrs %[tlr], tpidrro_el0" : [tlr]"=&r"(tlr));
|
||||
return tlr;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
|
||||
namespace ams::svc::board::generic {
|
||||
|
||||
enum DeviceName {
|
||||
/* If there is no smmu, there are no device names. */
|
||||
DeviceName_Count = 0,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
|
||||
namespace ams::svc::board::nintendo::nx {
|
||||
|
||||
enum DeviceName {
|
||||
DeviceName_Afi = 0,
|
||||
DeviceName_Avpc = 1,
|
||||
DeviceName_Dc = 2,
|
||||
DeviceName_Dcb = 3,
|
||||
DeviceName_Hc = 4,
|
||||
DeviceName_Hda = 5,
|
||||
DeviceName_Isp2 = 6,
|
||||
DeviceName_MsencNvenc = 7,
|
||||
DeviceName_Nv = 8,
|
||||
DeviceName_Nv2 = 9,
|
||||
DeviceName_Ppcs = 10,
|
||||
DeviceName_Sata = 11,
|
||||
DeviceName_Vi = 12,
|
||||
DeviceName_Vic = 13,
|
||||
DeviceName_XusbHost = 14,
|
||||
DeviceName_XusbDev = 15,
|
||||
DeviceName_Tsec = 16,
|
||||
DeviceName_Ppcs1 = 17,
|
||||
DeviceName_Dc1 = 18,
|
||||
DeviceName_Sdmmc1a = 19,
|
||||
DeviceName_Sdmmc2a = 20,
|
||||
DeviceName_Sdmmc3a = 21,
|
||||
DeviceName_Sdmmc4a = 22,
|
||||
DeviceName_Isp2b = 23,
|
||||
DeviceName_Gpu = 24,
|
||||
DeviceName_Gpub = 25,
|
||||
DeviceName_Ppcs2 = 26,
|
||||
DeviceName_Nvdec = 27,
|
||||
DeviceName_Ape = 28,
|
||||
DeviceName_Se = 29,
|
||||
DeviceName_Nvjpg = 30,
|
||||
DeviceName_Hc1 = 31,
|
||||
DeviceName_Se1 = 32,
|
||||
DeviceName_Axiap = 33,
|
||||
DeviceName_Etr = 34,
|
||||
DeviceName_Tsecb = 35,
|
||||
DeviceName_Tsec1 = 36,
|
||||
DeviceName_Tsecb1 = 37,
|
||||
DeviceName_Nvdec1 = 38,
|
||||
|
||||
DeviceName_Count,
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
constexpr inline const size_t RequiredNonSecureSystemMemorySizeVi = 0x2280 * 4_KB;
|
||||
constexpr inline const size_t RequiredNonSecureSystemMemorySizeViFatal = 0x200 * 4_KB;
|
||||
constexpr inline const size_t RequiredNonSecureSystemMemorySizeNvservices = 0x704 * 4_KB;
|
||||
constexpr inline const size_t RequiredNonSecureSystemMemorySizeMisc = 0x80 * 4_KB;
|
||||
|
||||
}
|
||||
|
||||
constexpr inline const size_t RequiredNonSecureSystemMemorySize = impl::RequiredNonSecureSystemMemorySizeVi +
|
||||
impl::RequiredNonSecureSystemMemorySizeNvservices +
|
||||
impl::RequiredNonSecureSystemMemorySizeMisc;
|
||||
|
||||
constexpr inline const size_t RequiredNonSecureSystemMemorySizeWithFatal = RequiredNonSecureSystemMemorySize + impl::RequiredNonSecureSystemMemorySizeViFatal;
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
|
||||
namespace ams::svc::board::nintendo::nx {
|
||||
|
||||
constexpr inline const s64 TicksPerSecond = 19'200'000;
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
|
||||
namespace ams::svc::board::nintendo::nx {
|
||||
|
||||
enum IoPoolType : u32 {
|
||||
IoPoolType_PcieA2 = 0, /* NOTE: Name is not official. */
|
||||
|
||||
IoPoolType_Count = 1,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
|
||||
namespace ams::svc::board::qemu::virt {
|
||||
|
||||
constexpr inline const s64 TicksPerSecond = 19'200'000;
|
||||
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_common.hpp>
|
||||
|
||||
namespace ams::svc::codegen::impl {
|
||||
|
||||
#define SVC_CODEGEN_FOR_I_FROM_0_TO_64(HANDLER, ...) \
|
||||
HANDLER( 0, ## __VA_ARGS__); HANDLER( 1, ## __VA_ARGS__); HANDLER( 2, ## __VA_ARGS__); HANDLER( 3, ## __VA_ARGS__); \
|
||||
HANDLER( 4, ## __VA_ARGS__); HANDLER( 5, ## __VA_ARGS__); HANDLER( 6, ## __VA_ARGS__); HANDLER( 7, ## __VA_ARGS__); \
|
||||
HANDLER( 8, ## __VA_ARGS__); HANDLER( 9, ## __VA_ARGS__); HANDLER(10, ## __VA_ARGS__); HANDLER(11, ## __VA_ARGS__); \
|
||||
HANDLER(12, ## __VA_ARGS__); HANDLER(13, ## __VA_ARGS__); HANDLER(14, ## __VA_ARGS__); HANDLER(15, ## __VA_ARGS__); \
|
||||
HANDLER(16, ## __VA_ARGS__); HANDLER(17, ## __VA_ARGS__); HANDLER(18, ## __VA_ARGS__); HANDLER(19, ## __VA_ARGS__); \
|
||||
HANDLER(20, ## __VA_ARGS__); HANDLER(21, ## __VA_ARGS__); HANDLER(22, ## __VA_ARGS__); HANDLER(23, ## __VA_ARGS__); \
|
||||
HANDLER(24, ## __VA_ARGS__); HANDLER(25, ## __VA_ARGS__); HANDLER(26, ## __VA_ARGS__); HANDLER(27, ## __VA_ARGS__); \
|
||||
HANDLER(28, ## __VA_ARGS__); HANDLER(29, ## __VA_ARGS__); HANDLER(30, ## __VA_ARGS__); HANDLER(31, ## __VA_ARGS__); \
|
||||
HANDLER(32, ## __VA_ARGS__); HANDLER(33, ## __VA_ARGS__); HANDLER(34, ## __VA_ARGS__); HANDLER(35, ## __VA_ARGS__); \
|
||||
HANDLER(36, ## __VA_ARGS__); HANDLER(37, ## __VA_ARGS__); HANDLER(38, ## __VA_ARGS__); HANDLER(39, ## __VA_ARGS__); \
|
||||
HANDLER(40, ## __VA_ARGS__); HANDLER(41, ## __VA_ARGS__); HANDLER(42, ## __VA_ARGS__); HANDLER(43, ## __VA_ARGS__); \
|
||||
HANDLER(44, ## __VA_ARGS__); HANDLER(45, ## __VA_ARGS__); HANDLER(46, ## __VA_ARGS__); HANDLER(47, ## __VA_ARGS__); \
|
||||
HANDLER(48, ## __VA_ARGS__); HANDLER(49, ## __VA_ARGS__); HANDLER(50, ## __VA_ARGS__); HANDLER(51, ## __VA_ARGS__); \
|
||||
HANDLER(52, ## __VA_ARGS__); HANDLER(53, ## __VA_ARGS__); HANDLER(54, ## __VA_ARGS__); HANDLER(55, ## __VA_ARGS__); \
|
||||
HANDLER(56, ## __VA_ARGS__); HANDLER(57, ## __VA_ARGS__); HANDLER(58, ## __VA_ARGS__); HANDLER(59, ## __VA_ARGS__); \
|
||||
HANDLER(60, ## __VA_ARGS__); HANDLER(61, ## __VA_ARGS__); HANDLER(62, ## __VA_ARGS__); HANDLER(63, ## __VA_ARGS__);
|
||||
|
||||
|
||||
class Aarch64CodeGenerator {
|
||||
private:
|
||||
struct RegisterPair {
|
||||
size_t First;
|
||||
size_t Second;
|
||||
};
|
||||
|
||||
template<size_t ...Registers>
|
||||
struct RegisterPairHelper;
|
||||
|
||||
template<size_t First, size_t Second, size_t ...Rest>
|
||||
struct RegisterPairHelper<First, Second, Rest...> {
|
||||
static constexpr size_t PairCount = 1 + RegisterPairHelper<Rest...>::PairCount;
|
||||
static constexpr std::array<RegisterPair, PairCount> Pairs = [] {
|
||||
std::array<RegisterPair, PairCount> pairs = {};
|
||||
pairs[0] = RegisterPair{First, Second};
|
||||
if constexpr (RegisterPairHelper<Rest...>::PairCount) {
|
||||
for (size_t i = 0; i < RegisterPairHelper<Rest...>::PairCount; i++) {
|
||||
pairs[1+i] = RegisterPairHelper<Rest...>::Pairs[i];
|
||||
}
|
||||
}
|
||||
return pairs;
|
||||
}();
|
||||
};
|
||||
|
||||
template<size_t First, size_t Second>
|
||||
struct RegisterPairHelper<First, Second> {
|
||||
static constexpr size_t PairCount = 1;
|
||||
static constexpr std::array<RegisterPair, PairCount> Pairs = { RegisterPair{First, Second} };
|
||||
};
|
||||
|
||||
template<size_t First>
|
||||
struct RegisterPairHelper<First> {
|
||||
static constexpr size_t PairCount = 0;
|
||||
static constexpr std::array<RegisterPair, 0> Pairs = {};
|
||||
};
|
||||
|
||||
template<size_t Reg>
|
||||
static ALWAYS_INLINE void ClearRegister() {
|
||||
__asm__ __volatile__("mov x%c[r], xzr" :: [r]"i"(Reg) : "memory");
|
||||
}
|
||||
|
||||
template<size_t Reg>
|
||||
static ALWAYS_INLINE void SaveRegister() {
|
||||
__asm__ __volatile__("str x%c[r], [sp, -16]!" :: [r]"i"(Reg) : "memory");
|
||||
}
|
||||
|
||||
template<size_t Reg>
|
||||
static ALWAYS_INLINE void RestoreRegister() {
|
||||
__asm__ __volatile__("ldr x%c[r], [sp], 16" :: [r]"i"(Reg) : "memory");
|
||||
}
|
||||
|
||||
template<size_t Reg0, size_t Reg1>
|
||||
static ALWAYS_INLINE void SaveRegisterPair() {
|
||||
__asm__ __volatile__("stp x%c[r0], x%c[r1], [sp, -16]!" :: [r0]"i"(Reg0), [r1]"i"(Reg1) : "memory");
|
||||
}
|
||||
|
||||
template<size_t Reg0, size_t Reg1>
|
||||
static ALWAYS_INLINE void RestoreRegisterPair() {
|
||||
__asm__ __volatile__("ldp x%c[r0], x%c[r1], [sp], 16" :: [r0]"i"(Reg0), [r1]"i"(Reg1) : "memory");
|
||||
}
|
||||
|
||||
template<size_t First, size_t... Rest>
|
||||
static ALWAYS_INLINE void SaveRegistersImpl() {
|
||||
#define SVC_CODEGEN_HANDLER(n) \
|
||||
do { if constexpr ((63 - n) < Pairs.size()) { SaveRegisterPair<Pairs[(63 - n)].First, Pairs[(63 - n)].Second>(); } } while (0)
|
||||
|
||||
if constexpr (sizeof...(Rest) % 2 == 1) {
|
||||
/* Even number of registers. */
|
||||
constexpr auto Pairs = RegisterPairHelper<First, Rest...>::Pairs;
|
||||
static_assert(Pairs.size() <= 8);
|
||||
SVC_CODEGEN_FOR_I_FROM_0_TO_64(SVC_CODEGEN_HANDLER)
|
||||
} else if constexpr (sizeof...(Rest) > 0) {
|
||||
/* Odd number of registers. */
|
||||
constexpr auto Pairs = RegisterPairHelper<Rest...>::Pairs;
|
||||
static_assert(Pairs.size() <= 8);
|
||||
SVC_CODEGEN_FOR_I_FROM_0_TO_64(SVC_CODEGEN_HANDLER)
|
||||
|
||||
SaveRegister<First>();
|
||||
} else {
|
||||
/* Only one register. */
|
||||
SaveRegister<First>();
|
||||
}
|
||||
|
||||
#undef SVC_CODEGEN_HANDLER
|
||||
}
|
||||
|
||||
template<size_t First, size_t... Rest>
|
||||
static ALWAYS_INLINE void RestoreRegistersImpl() {
|
||||
#define SVC_CODEGEN_HANDLER(n) \
|
||||
do { if constexpr (n < Pairs.size()) { RestoreRegisterPair<Pairs[n].First, Pairs[n].Second>(); } } while (0)
|
||||
|
||||
if constexpr (sizeof...(Rest) % 2 == 1) {
|
||||
/* Even number of registers. */
|
||||
constexpr auto Pairs = RegisterPairHelper<First, Rest...>::Pairs;
|
||||
static_assert(Pairs.size() <= 8);
|
||||
SVC_CODEGEN_FOR_I_FROM_0_TO_64(SVC_CODEGEN_HANDLER)
|
||||
} else if constexpr (sizeof...(Rest) > 0) {
|
||||
/* Odd number of registers. */
|
||||
RestoreRegister<First>();
|
||||
|
||||
constexpr auto Pairs = RegisterPairHelper<Rest...>::Pairs;
|
||||
static_assert(Pairs.size() <= 8);
|
||||
SVC_CODEGEN_FOR_I_FROM_0_TO_64(SVC_CODEGEN_HANDLER)
|
||||
} else {
|
||||
/* Only one register. */
|
||||
RestoreRegister<First>();
|
||||
}
|
||||
|
||||
#undef SVC_CODEGEN_HANDLER
|
||||
}
|
||||
|
||||
public:
|
||||
template<size_t... Registers>
|
||||
static ALWAYS_INLINE void SaveRegisters() {
|
||||
if constexpr (sizeof...(Registers) > 0) {
|
||||
SaveRegistersImpl<Registers...>();
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t... Registers>
|
||||
static ALWAYS_INLINE void RestoreRegisters() {
|
||||
if constexpr (sizeof...(Registers) > 0) {
|
||||
RestoreRegistersImpl<Registers...>();
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t... Registers>
|
||||
static ALWAYS_INLINE void ClearRegisters() {
|
||||
static_assert(sizeof...(Registers) <= 8);
|
||||
(ClearRegister<Registers>(), ...);
|
||||
}
|
||||
|
||||
template<size_t Size>
|
||||
static ALWAYS_INLINE void AllocateStackSpace() {
|
||||
if constexpr (Size > 0) {
|
||||
__asm__ __volatile__("sub sp, sp, %c[size]" :: [size]"i"(util::AlignUp(Size, 16)) : "memory");
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Size>
|
||||
static ALWAYS_INLINE void FreeStackSpace() {
|
||||
if constexpr (Size > 0) {
|
||||
__asm__ __volatile__("add sp, sp, %c[size]" :: [size]"i"(util::AlignUp(Size, 16)) : "memory");
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Dst, size_t Src>
|
||||
static ALWAYS_INLINE void MoveRegister() {
|
||||
__asm__ __volatile__("mov x%c[dst], x%c[src]" :: [dst]"i"(Dst), [src]"i"(Src) : "memory");
|
||||
}
|
||||
|
||||
template<size_t Reg>
|
||||
static ALWAYS_INLINE void ConvertToBoolean() {
|
||||
__asm__ __volatile__("and x%c[reg], x%c[reg], #1" :: [reg]"i"(Reg) : "memory");
|
||||
}
|
||||
|
||||
template<size_t Reg, size_t Offset, size_t Size>
|
||||
static ALWAYS_INLINE void LoadFromStack() {
|
||||
if constexpr (Size == 4) {
|
||||
__asm__ __volatile__("ldr w%c[r], [sp, %c[offset]]" :: [r]"i"(Reg), [offset]"i"(Offset) : "memory");
|
||||
} else if constexpr (Size == 8) {
|
||||
__asm__ __volatile__("ldr x%c[r], [sp, %c[offset]]" :: [r]"i"(Reg), [offset]"i"(Offset) : "memory");
|
||||
} else {
|
||||
static_assert(false, "Invalid Size");
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Reg0, size_t Reg1, size_t Offset, size_t Size>
|
||||
static ALWAYS_INLINE void LoadPairFromStack() {
|
||||
if constexpr (Size == 4) {
|
||||
__asm__ __volatile__("ldp w%c[r0], w%c[r1], [sp, %c[offset]]" :: [r0]"i"(Reg0), [r1]"i"(Reg1), [offset]"i"(Offset) : "memory");
|
||||
} else if constexpr (Size == 8) {
|
||||
__asm__ __volatile__("ldp x%c[r0], x%c[r1], [sp, %c[offset]]" :: [r0]"i"(Reg0), [r1]"i"(Reg1), [offset]"i"(Offset) : "memory");
|
||||
} else {
|
||||
static_assert(false, "Invalid Size");
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Reg, size_t Offset, size_t Size>
|
||||
static ALWAYS_INLINE void StoreToStack() {
|
||||
if constexpr (Size == 4) {
|
||||
__asm__ __volatile__("str w%c[r], [sp, %c[offset]]" :: [r]"i"(Reg), [offset]"i"(Offset) : "memory");
|
||||
} else if constexpr (Size == 8) {
|
||||
__asm__ __volatile__("str x%c[r], [sp, %c[offset]]" :: [r]"i"(Reg), [offset]"i"(Offset) : "memory");
|
||||
} else {
|
||||
static_assert(false, "Invalid Size");
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Reg0, size_t Reg1, size_t Offset, size_t Size>
|
||||
static ALWAYS_INLINE void StorePairToStack() {
|
||||
if constexpr (Size == 4) {
|
||||
__asm__ __volatile__("stp w%c[r0], w%c[r1], [sp, %c[offset]]" :: [r0]"i"(Reg0), [r1]"i"(Reg1), [offset]"i"(Offset) : "memory");
|
||||
} else if constexpr (Size == 8) {
|
||||
__asm__ __volatile__("stp x%c[r0], x%c[r1], [sp, %c[offset]]" :: [r0]"i"(Reg0), [r1]"i"(Reg1), [offset]"i"(Offset) : "memory");
|
||||
} else {
|
||||
static_assert(false, "Invalid Size");
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Dst, size_t Low, size_t High>
|
||||
static ALWAYS_INLINE void Pack() {
|
||||
__asm__ __volatile__("orr x%c[dst], x%c[low], x%c[high], lsl #32" :: [dst]"i"(Dst), [low]"i"(Low), [high]"i"(High) : "memory");
|
||||
}
|
||||
|
||||
template<size_t Low, size_t High, size_t Src>
|
||||
static ALWAYS_INLINE void Unpack() {
|
||||
if constexpr (Src != Low) {
|
||||
MoveRegister<Src, Low>();
|
||||
}
|
||||
|
||||
__asm__ __volatile__("lsr x%c[high], x%c[src], #32" :: [high]"i"(High), [src]"i"(Src) : "memory");
|
||||
}
|
||||
|
||||
template<size_t Dst, size_t Offset>
|
||||
static ALWAYS_INLINE void LoadStackAddress() {
|
||||
if constexpr (Offset > 0) {
|
||||
__asm__ __volatile__("add x%c[dst], sp, %c[offset]" :: [dst]"i"(Dst), [offset]"i"(Offset) : "memory");
|
||||
} else if constexpr (Offset == 0) {
|
||||
__asm__ __volatile__("mov x%c[dst], sp" :: [dst]"i"(Dst) : "memory");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Aarch32CodeGenerator {
|
||||
/* TODO */
|
||||
};
|
||||
|
||||
template<typename CodeGenerator, auto MetaCode>
|
||||
static ALWAYS_INLINE void GenerateCodeForMetaCode() {
|
||||
constexpr size_t NumOperations = MetaCode.GetNumOperations();
|
||||
static_assert(NumOperations <= 64);
|
||||
#define SVC_CODEGEN_HANDLER(n) do { if constexpr (n < NumOperations) { constexpr auto Operation = MetaCode.GetOperation(n); GenerateCodeForOperation<CodeGenerator, Operation>(); } } while (0)
|
||||
SVC_CODEGEN_FOR_I_FROM_0_TO_64(SVC_CODEGEN_HANDLER)
|
||||
#undef SVC_CODEGEN_HANDLER
|
||||
}
|
||||
|
||||
#undef SVC_CODEGEN_FOR_I_FROM_0_TO_64
|
||||
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/svc/svc_types.hpp>
|
||||
|
||||
namespace ams::svc::codegen::impl {
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsIntegral = std::is_integral<T>::value;
|
||||
|
||||
template<>
|
||||
constexpr inline bool IsIntegral<::ams::svc::Address> = true;
|
||||
|
||||
template<>
|
||||
constexpr inline bool IsIntegral<::ams::svc::Size> = true;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsKUserPointer = std::is_base_of<ams::kern::svc::impl::KUserPointerTag, T>::value;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsIntegralOrUserPointer = IsIntegral<T> || IsUserPointer<T> || IsKUserPointer<T>;
|
||||
|
||||
template<size_t... Is1, size_t... Is2>
|
||||
constexpr std::index_sequence<Is1..., Is2...> IndexSequenceCat(std::index_sequence<Is1...>, std::index_sequence<Is2...>) {
|
||||
return std::index_sequence<Is1..., Is2...>{};
|
||||
}
|
||||
|
||||
template<size_t... Is>
|
||||
constexpr inline std::array<size_t, sizeof...(Is)> ConvertToArray(std::index_sequence<Is...>) {
|
||||
return std::array<size_t, sizeof...(Is)>{ Is... };
|
||||
}
|
||||
|
||||
template<auto Function>
|
||||
class FunctionTraits {
|
||||
private:
|
||||
template<typename R, typename... A>
|
||||
static R GetReturnTypeImpl(R(*)(A...));
|
||||
|
||||
template<typename R, typename... A>
|
||||
static std::tuple<A...> GetArgsImpl(R(*)(A...));
|
||||
public:
|
||||
using ReturnType = decltype(GetReturnTypeImpl(Function));
|
||||
using ArgsType = decltype(GetArgsImpl(Function));
|
||||
};
|
||||
|
||||
enum class CodeGenerationKind {
|
||||
SvcInvocationToKernelProcedure,
|
||||
PrepareForKernelProcedureToSvcInvocation,
|
||||
KernelProcedureToSvcInvocation,
|
||||
Invalid,
|
||||
};
|
||||
|
||||
enum class ArgumentType {
|
||||
In,
|
||||
Out,
|
||||
InUserPointer,
|
||||
OutUserPointer,
|
||||
Invalid,
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr inline ArgumentType GetArgumentType = [] {
|
||||
static_assert(!std::is_reference<T>::value, "SVC ABI: Reference types not allowed.");
|
||||
static_assert(sizeof(T) <= sizeof(uint64_t), "SVC ABI: Type too large");
|
||||
if constexpr (std::is_pointer<T>::value) {
|
||||
static_assert(!std::is_const<typename std::remove_pointer<T>::type>::value, "SVC ABI: Output (T*) must not be const");
|
||||
return ArgumentType::Out;
|
||||
} else if constexpr (IsUserPointer<T> || IsKUserPointer<T>) {
|
||||
if constexpr (T::IsInput) {
|
||||
return ArgumentType::InUserPointer;
|
||||
} else {
|
||||
return ArgumentType::OutUserPointer;
|
||||
}
|
||||
} else {
|
||||
return ArgumentType::In;
|
||||
}
|
||||
}();
|
||||
|
||||
template<size_t RS, size_t RC, size_t ARC, size_t PC>
|
||||
struct AbiType {
|
||||
static constexpr size_t RegisterSize = RS;
|
||||
static constexpr size_t RegisterCount = RC;
|
||||
static constexpr size_t ArgumentRegisterCount = ARC;
|
||||
static constexpr size_t PointerSize = PC;
|
||||
|
||||
template<typename T>
|
||||
static constexpr size_t GetSize() {
|
||||
if constexpr (std::is_same<T, ::ams::svc::Address>::value || std::is_same<T, ::ams::svc::Size>::value || IsUserPointer<T> || IsKUserPointer<T>) {
|
||||
return PointerSize;
|
||||
} else if constexpr(std::is_pointer<T>::value) {
|
||||
/* Out parameter. */
|
||||
return GetSize<typename std::remove_pointer<T>::type>();
|
||||
} else if constexpr (std::is_same<T, void>::value) {
|
||||
return 0;
|
||||
} else {
|
||||
return sizeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr inline size_t Size = GetSize<T>();
|
||||
};
|
||||
|
||||
using Aarch64Lp64Abi = AbiType<8, 8, 8, 8>;
|
||||
using Aarch64Ilp32Abi = AbiType<8, 8, 8, 4>;
|
||||
using Aarch32Ilp32Abi = AbiType<4, 4, 4, 4>;
|
||||
|
||||
using Aarch64SvcInvokeAbi = AbiType<8, 8, 8, 8>;
|
||||
using Aarch32SvcInvokeAbi = AbiType<4, 8, 4, 4>;
|
||||
|
||||
struct Abi {
|
||||
size_t register_size;
|
||||
size_t register_count;
|
||||
size_t pointer_size;
|
||||
|
||||
template<typename AbiType>
|
||||
static constexpr Abi Convert() { return { AbiType::RegisterSize, AbiType::RegisterCount, AbiType::PointerSize }; }
|
||||
};
|
||||
|
||||
template<typename AbiType, typename ArgType>
|
||||
constexpr inline bool IsPassedByPointer = [] {
|
||||
if (GetArgumentType<ArgType> != ArgumentType::In) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (!IsIntegral<ArgType> && AbiType::template Size<ArgType> > AbiType::RegisterSize);
|
||||
}();
|
||||
|
||||
template<size_t N>
|
||||
class RegisterAllocator {
|
||||
public:
|
||||
std::array<bool, N> m_map;
|
||||
public:
|
||||
constexpr explicit RegisterAllocator() : m_map() { /* ... */ }
|
||||
|
||||
constexpr bool IsAllocated(size_t i) const { return m_map[i]; }
|
||||
constexpr bool IsFree(size_t i) const { return !this->IsAllocated(i); }
|
||||
|
||||
constexpr void Allocate(size_t i) {
|
||||
if (this->IsAllocated(i)) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
m_map[i] = true;
|
||||
}
|
||||
|
||||
constexpr bool TryAllocate(size_t i) {
|
||||
if (this->IsAllocated(i)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_map[i] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr size_t AllocateFirstFree() {
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (!this->IsAllocated(i)) {
|
||||
m_map[i] = true;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
std::abort();
|
||||
}
|
||||
|
||||
constexpr void Free(size_t i) {
|
||||
if (!this->IsAllocated(i)) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
m_map[i] = false;
|
||||
}
|
||||
|
||||
constexpr size_t GetRegisterCount() const {
|
||||
return N;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -1,585 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_common.hpp>
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_parameter.hpp>
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_layout.hpp>
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_meta_code.hpp>
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_layout_conversion.hpp>
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_code_generator.hpp>
|
||||
|
||||
namespace ams::svc::codegen::impl {
|
||||
|
||||
template<typename, typename, typename, typename, typename>
|
||||
class KernelSvcWrapperHelperImpl;
|
||||
|
||||
template<typename _SvcAbiType, typename _UserAbiType, typename _KernelAbiType, typename ReturnType, typename... ArgumentTypes>
|
||||
class KernelSvcWrapperHelperImpl<_SvcAbiType, _UserAbiType, _KernelAbiType, ReturnType, std::tuple<ArgumentTypes...>> {
|
||||
private:
|
||||
static constexpr bool TryToPerformCoalescingOptimizations = true;
|
||||
|
||||
template<MetaCode::OperationKind PairKind, MetaCode::OperationKind SingleKind, size_t InvalidRegisterId, size_t MaxStackIndex>
|
||||
static constexpr void CoalesceOperations(MetaCodeGenerator &out_mcg, const std::array<size_t, MaxStackIndex> stack_modified, size_t stack_top) {
|
||||
enum class State { WaitingForRegister, ParsingRegister, ParsedRegister, EmittingCode };
|
||||
State cur_state = State::WaitingForRegister;
|
||||
size_t num_regs = 0;
|
||||
size_t registers[2] = { InvalidRegisterId, InvalidRegisterId };
|
||||
size_t widths[2] = {};
|
||||
size_t index = 0;
|
||||
size_t store_base = 0;
|
||||
while (index < stack_top) {
|
||||
if (cur_state == State::WaitingForRegister) {
|
||||
while (stack_modified[index] == InvalidRegisterId && index < stack_top) {
|
||||
index++;
|
||||
}
|
||||
cur_state = State::ParsingRegister;
|
||||
} else if (cur_state == State::ParsingRegister) {
|
||||
const size_t start_index = index;
|
||||
if (num_regs == 0) {
|
||||
store_base = start_index;
|
||||
}
|
||||
const size_t reg = stack_modified[index];
|
||||
registers[num_regs] = reg;
|
||||
while (index < stack_top && index < start_index + KernelAbiType::RegisterSize && stack_modified[index] == reg) {
|
||||
widths[num_regs]++;
|
||||
index++;
|
||||
}
|
||||
num_regs++;
|
||||
cur_state = State::ParsedRegister;
|
||||
} else if (cur_state == State::ParsedRegister) {
|
||||
if (num_regs == 2 || stack_modified[index] == InvalidRegisterId) {
|
||||
cur_state = State::EmittingCode;
|
||||
} else {
|
||||
cur_state = State::ParsingRegister;
|
||||
}
|
||||
} else if (cur_state == State::EmittingCode) {
|
||||
/* Emit an operation! */
|
||||
MetaCode::Operation st_op = {};
|
||||
|
||||
if (num_regs == 2) {
|
||||
if (registers[0] == registers[1]) {
|
||||
std::abort();
|
||||
}
|
||||
if (widths[0] == widths[1]) {
|
||||
st_op.kind = PairKind;
|
||||
st_op.num_parameters = 4;
|
||||
st_op.parameters[0] = registers[0];
|
||||
st_op.parameters[1] = registers[1];
|
||||
st_op.parameters[2] = store_base;
|
||||
st_op.parameters[3] = widths[0];
|
||||
} else {
|
||||
std::abort();
|
||||
}
|
||||
} else if (num_regs == 1) {
|
||||
st_op.kind = SingleKind;
|
||||
st_op.num_parameters = 3;
|
||||
st_op.parameters[0] = registers[0];
|
||||
st_op.parameters[1] = store_base;
|
||||
st_op.parameters[2] = widths[0];
|
||||
} else {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
out_mcg.AddOperationDirectly(st_op);
|
||||
|
||||
/* Go back to beginning of parse. */
|
||||
for (size_t i = 0; i < num_regs; i++) {
|
||||
registers[i] = InvalidRegisterId;
|
||||
widths[i] = 0;
|
||||
}
|
||||
num_regs = 0;
|
||||
cur_state = State::WaitingForRegister;
|
||||
} else {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (cur_state == State::ParsedRegister) {
|
||||
/* Emit an operation! */
|
||||
if (num_regs == 2 && widths[0] == widths[1]) {
|
||||
MetaCode::Operation st_op = {};
|
||||
st_op.kind = PairKind;
|
||||
st_op.num_parameters = 4;
|
||||
st_op.parameters[0] = registers[0];
|
||||
st_op.parameters[1] = registers[1];
|
||||
st_op.parameters[2] = store_base;
|
||||
st_op.parameters[3] = widths[0];
|
||||
out_mcg.AddOperationDirectly(st_op);
|
||||
} else {
|
||||
for (size_t i = 0; i < num_regs; i++) {
|
||||
MetaCode::Operation st_op = {};
|
||||
st_op.kind = SingleKind;
|
||||
st_op.num_parameters = 3;
|
||||
st_op.parameters[0] = registers[i];
|
||||
st_op.parameters[1] = store_base;
|
||||
st_op.parameters[2] = widths[i];
|
||||
|
||||
store_base += widths[i];
|
||||
out_mcg.AddOperationDirectly(st_op);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Basic optimization of store coalescing. */
|
||||
template<typename Conversion, typename... OperationTypes, size_t N>
|
||||
static constexpr bool TryPrepareForKernelProcedureToSvcInvocationCoalescing(std::tuple<OperationTypes...>, MetaCodeGenerator &out_mcg, RegisterAllocator<N> &out_allocator) {
|
||||
/* For debugging, allow ourselves to disable these optimizations. */
|
||||
if constexpr (!TryToPerformCoalescingOptimizations) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Generate expected code. */
|
||||
MetaCodeGenerator mcg;
|
||||
RegisterAllocator<N> allocator = out_allocator;
|
||||
(Conversion::template GenerateCode<OperationTypes, CodeGenerationKind::PrepareForKernelProcedureToSvcInvocation>(mcg, allocator), ...);
|
||||
MetaCode mc = mcg.GetMetaCode();
|
||||
|
||||
/* This is a naive optimization pass. */
|
||||
/* We want to reorder code of the form: */
|
||||
/* - Store to Stack sequence 0... */
|
||||
/* - Load Stack Address 0 */
|
||||
/* - Store to Stack 1... */
|
||||
/* - Load Stack Address 1 */
|
||||
/* Into the form: */
|
||||
/* - Store to stack Sequence 0 + 1... */
|
||||
/* - Load Stack Address 0 + 1... */
|
||||
/* But only if they are semantically equivalent. */
|
||||
|
||||
/* We'll do a simple, naive pass to check if any registers are stored to stack that are modified. */
|
||||
/* This shouldn't happen in any cases we care about, so we can probably get away with it. */
|
||||
/* TODO: Eventually this should be e.g. operation.ModifiesRegister() / operation.CanReorderBefore() */
|
||||
/* However, this will be more work, and if it's not necessary it can be put off until it is. */
|
||||
constexpr size_t MaxStackIndex = 0x100;
|
||||
constexpr size_t InvalidRegisterId = N;
|
||||
bool register_modified[N] = {};
|
||||
std::array<size_t, N> stack_address_loaded = {};
|
||||
for (size_t i = 0; i < N; i++) { stack_address_loaded[i] = MaxStackIndex; }
|
||||
std::array<size_t, MaxStackIndex> stack_modified = {};
|
||||
for (size_t i = 0; i < MaxStackIndex; i++) { stack_modified[i] = InvalidRegisterId; }
|
||||
size_t stack_top = 0;
|
||||
for (size_t i = 0; i < mc.GetNumOperations(); i++) {
|
||||
const auto mco = mc.GetOperation(i);
|
||||
if (mco.kind == MetaCode::OperationKind::StoreToStack) {
|
||||
if (register_modified[mco.parameters[0]]) {
|
||||
return false;
|
||||
}
|
||||
const size_t offset = mco.parameters[1];
|
||||
const size_t width = mco.parameters[2] == 0 ? KernelAbiType::RegisterSize : mco.parameters[2];
|
||||
for (size_t j = 0; j < width; j++) {
|
||||
const size_t index = offset + j;
|
||||
if (index >= MaxStackIndex) {
|
||||
std::abort();
|
||||
}
|
||||
if (stack_modified[index] != InvalidRegisterId) {
|
||||
return false;
|
||||
}
|
||||
stack_modified[index] = mco.parameters[0];
|
||||
stack_top = std::max(index + 1, stack_top);
|
||||
}
|
||||
} else if (mco.kind == MetaCode::OperationKind::LoadStackAddress) {
|
||||
if (stack_address_loaded[mco.parameters[0]] != MaxStackIndex) {
|
||||
return false;
|
||||
}
|
||||
if (register_modified[mco.parameters[0]]) {
|
||||
return false;
|
||||
}
|
||||
if (mco.parameters[1] >= MaxStackIndex) {
|
||||
std::abort();
|
||||
}
|
||||
stack_address_loaded[mco.parameters[0]] = mco.parameters[1];
|
||||
register_modified[mco.parameters[0]] = true;
|
||||
} else {
|
||||
/* TODO: Better operation reasoning process. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Looks like we can reorder! */
|
||||
/* Okay, let's do this the naive way, too. */
|
||||
constexpr auto PairKind = MetaCode::OperationKind::StorePairToStack;
|
||||
constexpr auto SingleKind = MetaCode::OperationKind::StoreToStack;
|
||||
CoalesceOperations<PairKind, SingleKind, InvalidRegisterId>(out_mcg, stack_modified, stack_top);
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (stack_address_loaded[i] != MaxStackIndex) {
|
||||
MetaCode::Operation load_op = {};
|
||||
load_op.kind = MetaCode::OperationKind::LoadStackAddress;
|
||||
load_op.num_parameters = 2;
|
||||
load_op.parameters[0] = i;
|
||||
load_op.parameters[1] = stack_address_loaded[i];
|
||||
out_mcg.AddOperationDirectly(load_op);
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure the out allocator state is correct. */
|
||||
out_allocator = allocator;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Basic optimization of load coalescing. */
|
||||
template<typename Conversion, typename... OperationTypes, size_t N>
|
||||
static constexpr bool TryKernelProcedureToSvcInvocationCoalescing(std::tuple<OperationTypes...>, MetaCodeGenerator &out_mcg, RegisterAllocator<N> &out_allocator) {
|
||||
/* For debugging, allow ourselves to disable these optimizations. */
|
||||
if constexpr (!TryToPerformCoalescingOptimizations) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Generate expected code. */
|
||||
MetaCodeGenerator mcg;
|
||||
RegisterAllocator<N> allocator = out_allocator;
|
||||
(Conversion::template GenerateCode<OperationTypes, CodeGenerationKind::KernelProcedureToSvcInvocation>(mcg, allocator), ...);
|
||||
MetaCode mc = mcg.GetMetaCode();
|
||||
|
||||
/* This is a naive optimization pass. */
|
||||
/* We want to coalesce all sequential stack loads, if possible. */
|
||||
/* But only if they are semantically equivalent. */
|
||||
|
||||
/* We'll do a simple, naive pass to check if any registers are used after being loaded from stack that. */
|
||||
/* This shouldn't happen in any cases we care about, so we can probably get away with it. */
|
||||
/* TODO: Eventually this should be e.g. operation.ModifiesRegister() / operation.CanReorderBefore() */
|
||||
/* However, this will be more work, and if it's not necessary it can be put off until it is. */
|
||||
constexpr size_t MaxStackIndex = 0x100;
|
||||
constexpr size_t InvalidRegisterId = N;
|
||||
bool register_modified[N] = {};
|
||||
std::array<size_t, N> stack_offset_loaded = {};
|
||||
for (size_t i = 0; i < N; i++) { stack_offset_loaded[i] = MaxStackIndex; }
|
||||
std::array<size_t, MaxStackIndex> stack_modified = {};
|
||||
for (size_t i = 0; i < MaxStackIndex; i++) { stack_modified[i] = InvalidRegisterId; }
|
||||
size_t stack_top = 0;
|
||||
for (size_t i = 0; i < mc.GetNumOperations(); i++) {
|
||||
const auto mco = mc.GetOperation(i);
|
||||
if (mco.kind == MetaCode::OperationKind::Unpack) {
|
||||
if (register_modified[mco.parameters[0]] || register_modified[mco.parameters[1]] || register_modified[mco.parameters[2]]) {
|
||||
return false;
|
||||
}
|
||||
register_modified[mco.parameters[0]] = true;
|
||||
register_modified[mco.parameters[1]] = true;
|
||||
} else if (mco.kind == MetaCode::OperationKind::LoadFromStack) {
|
||||
if (stack_offset_loaded[mco.parameters[0]] != MaxStackIndex) {
|
||||
return false;
|
||||
}
|
||||
if (register_modified[mco.parameters[0]] != false) {
|
||||
return false;
|
||||
}
|
||||
if (mco.parameters[1] >= MaxStackIndex) {
|
||||
std::abort();
|
||||
}
|
||||
stack_offset_loaded[mco.parameters[0]] = mco.parameters[1];
|
||||
register_modified[mco.parameters[0]] = true;
|
||||
|
||||
const size_t offset = mco.parameters[1];
|
||||
const size_t width = mco.parameters[2] == 0 ? KernelAbiType::RegisterSize : mco.parameters[2];
|
||||
for (size_t j = 0; j < width; j++) {
|
||||
const size_t index = offset + j;
|
||||
if (index >= MaxStackIndex) {
|
||||
std::abort();
|
||||
}
|
||||
if (stack_modified[index] != InvalidRegisterId) {
|
||||
return false;
|
||||
}
|
||||
stack_modified[index] = mco.parameters[0];
|
||||
stack_top = std::max(index + 1, stack_top);
|
||||
}
|
||||
} else {
|
||||
/* TODO: Better operation reasoning process. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Any operations that don't load from stack, we can just re-add. */
|
||||
for (size_t i = 0; i < mc.GetNumOperations(); i++) {
|
||||
const auto mco = mc.GetOperation(i);
|
||||
if (mco.kind != MetaCode::OperationKind::LoadFromStack) {
|
||||
out_mcg.AddOperationDirectly(mco);
|
||||
}
|
||||
}
|
||||
constexpr auto PairKind = MetaCode::OperationKind::LoadPairFromStack;
|
||||
constexpr auto SingleKind = MetaCode::OperationKind::LoadFromStack;
|
||||
CoalesceOperations<PairKind, SingleKind, InvalidRegisterId>(out_mcg, stack_modified, stack_top);
|
||||
|
||||
/* Ensure the out allocator state is correct. */
|
||||
out_allocator = allocator;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Conversion, size_t ParameterIndex = 0>
|
||||
static constexpr void SanitizeInputBooleans(MetaCodeGenerator &mcg) {
|
||||
/* Get the input layout. */
|
||||
constexpr auto InputLayout = Conversion::LayoutForSvc.GetInputLayout();
|
||||
|
||||
/* Check if we're done. */
|
||||
if constexpr (ParameterIndex < InputLayout.GetNumParameters()) {
|
||||
/* Get the relevant parameter. */
|
||||
constexpr auto Param = InputLayout.GetParameter(ParameterIndex);
|
||||
|
||||
/* Handle the case where the parameter is a boolean. */
|
||||
if constexpr (Param.IsBoolean()) {
|
||||
/* Boolean parameters should have one location. */
|
||||
static_assert(Param.GetNumLocations() == 1);
|
||||
|
||||
/* Get the location. */
|
||||
constexpr auto Loc = Param.GetLocation(0);
|
||||
|
||||
/* TODO: Support boolean parameters passed-by-stack. */
|
||||
static_assert(Loc.GetStorage() == Storage::Register);
|
||||
|
||||
/* Convert the input to boolean. */
|
||||
mcg.template ConvertToBoolean<Loc.GetIndex()>();
|
||||
}
|
||||
|
||||
/* Handle the next parameter. */
|
||||
if constexpr (ParameterIndex + 1 < InputLayout.GetNumParameters()) {
|
||||
SanitizeInputBooleans<Conversion, ParameterIndex + 1>(mcg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
struct TypeIndexFilter {
|
||||
|
||||
template<auto UseArray, typename X, typename Y>
|
||||
struct Helper;
|
||||
|
||||
template<auto UseArray, size_t...Index>
|
||||
struct Helper<UseArray, std::tuple<>, std::index_sequence<Index...>> {
|
||||
using Type = std::tuple<>;
|
||||
};
|
||||
|
||||
template<auto UseArray, typename HeadType, typename... TailType, size_t HeadIndex, size_t... TailIndex>
|
||||
struct Helper<UseArray, std::tuple<HeadType, TailType...>, std::index_sequence<HeadIndex, TailIndex...>> {
|
||||
|
||||
using LastHeadType = std::tuple<HeadType>;
|
||||
using LastNullType = std::tuple<>;
|
||||
|
||||
using LastType = typename std::conditional<!UseArray[HeadIndex], LastHeadType, LastNullType>::type;
|
||||
|
||||
using NextTailType = std::tuple<TailType...>;
|
||||
using NextTailSequence = std::index_sequence<TailIndex...>;
|
||||
|
||||
using NextType = typename std::conditional<!UseArray[HeadIndex],
|
||||
decltype(std::tuple_cat(std::declval<LastHeadType>(), std::declval<typename Helper<UseArray, NextTailType, NextTailSequence>::Type>())),
|
||||
decltype(std::tuple_cat(std::declval<LastNullType>(), std::declval<typename Helper<UseArray, NextTailType, NextTailSequence>::Type>()))
|
||||
>::type;
|
||||
|
||||
using Type = typename std::conditional<sizeof...(TailType) == 0, LastType, NextType>::type;
|
||||
|
||||
};
|
||||
|
||||
template<auto UseArray>
|
||||
using FilteredTupleType = typename Helper<UseArray, std::tuple<T...>, decltype(std::make_index_sequence<sizeof...(T)>())>::Type;
|
||||
};
|
||||
|
||||
template<auto Allocator, typename FirstOperation, typename...OtherOperations>
|
||||
static constexpr auto GetModifiedOperations(std::tuple<FirstOperation, OtherOperations...>) {
|
||||
constexpr size_t ModifyRegister = [] {
|
||||
auto allocator = Allocator;
|
||||
return allocator.AllocateFirstFree();
|
||||
}();
|
||||
|
||||
using ModifiedFirstOperation = typename FirstOperation::template ModifiedType<ModifyRegister>;
|
||||
using NewMoveOperation = typename LayoutConversionBase::template OperationMove<FirstOperation::RegisterSize, FirstOperation::PassedSize, FirstOperation::ProcedureIndex, ModifyRegister>;
|
||||
return std::tuple<ModifiedFirstOperation, OtherOperations..., NewMoveOperation>{};
|
||||
}
|
||||
|
||||
template<typename Conversion, auto Allocator, typename FirstOperation, typename... OtherOperations>
|
||||
static constexpr auto GenerateBeforeOperations(MetaCodeGenerator &mcg, std::tuple<FirstOperation, OtherOperations...> ops) -> RegisterAllocator<Allocator.GetRegisterCount()> {
|
||||
constexpr size_t NumOperations = 1 + sizeof...(OtherOperations);
|
||||
using OperationsTuple = decltype(ops);
|
||||
using FilterHelper = TypeIndexFilter<FirstOperation, OtherOperations...>;
|
||||
|
||||
constexpr auto ProcessOperation = []<typename Operation>(MetaCodeGenerator &pr_mcg, auto &allocator) {
|
||||
if (Conversion::template CanGenerateCode<Operation, CodeGenerationKind::SvcInvocationToKernelProcedure>(allocator)) {
|
||||
Conversion::template GenerateCode<Operation, CodeGenerationKind::SvcInvocationToKernelProcedure>(pr_mcg, allocator);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
constexpr auto ProcessResults = []<auto AllocatorVal, auto ProcessOp, typename... Operations>(std::tuple<Operations...>) {
|
||||
auto allocator = AllocatorVal;
|
||||
MetaCodeGenerator pr_mcg;
|
||||
auto use_array = std::array<bool, NumOperations>{ ProcessOp.template operator()<Operations>(pr_mcg, allocator)... };
|
||||
return std::make_tuple(use_array, allocator, pr_mcg);
|
||||
}.template operator()<Allocator, ProcessOperation>(OperationsTuple{});
|
||||
|
||||
constexpr auto CanGenerate = std::get<0>(ProcessResults);
|
||||
constexpr auto AfterAllocator = std::get<1>(ProcessResults);
|
||||
constexpr auto GeneratedCode = std::get<2>(ProcessResults).GetMetaCode();
|
||||
|
||||
for (size_t i = 0; i < GeneratedCode.GetNumOperations(); i++) {
|
||||
mcg.AddOperationDirectly(GeneratedCode.GetOperation(i));
|
||||
}
|
||||
|
||||
using FilteredOperations = typename FilterHelper::FilteredTupleType<CanGenerate>;
|
||||
static_assert(std::tuple_size<FilteredOperations>::value <= NumOperations);
|
||||
if constexpr (std::tuple_size<FilteredOperations>::value > 0) {
|
||||
if constexpr (std::tuple_size<FilteredOperations>::value != NumOperations) {
|
||||
return GenerateBeforeOperations<Conversion, AfterAllocator>(mcg, FilteredOperations{});
|
||||
} else {
|
||||
/* No progress was made, so we need to make a change. */
|
||||
constexpr auto ModifiedOperations = GetModifiedOperations<AfterAllocator>(FilteredOperations{});
|
||||
return GenerateBeforeOperations<Conversion, AfterAllocator>(mcg, ModifiedOperations);
|
||||
}
|
||||
} else {
|
||||
return AfterAllocator;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr MetaCode GenerateOriginalBeforeMetaCode() {
|
||||
MetaCodeGenerator mcg;
|
||||
RegisterAllocator<KernelAbiType::RegisterCount> allocator;
|
||||
static_assert(SvcAbiType::RegisterCount == KernelAbiType::RegisterCount);
|
||||
|
||||
/* Reserve registers used by the input layout. */
|
||||
constexpr auto InitialAllocator = [] {
|
||||
RegisterAllocator<KernelAbiType::RegisterCount> initial_allocator;
|
||||
for (size_t i = 0; i < SvcAbiType::RegisterCount; i++) {
|
||||
if (Conversion::LayoutForSvc.GetInputLayout().UsesRegister(i)) {
|
||||
initial_allocator.Allocate(i);
|
||||
}
|
||||
}
|
||||
return initial_allocator;
|
||||
}();
|
||||
|
||||
/* Save every register that needs to be preserved to the stack. */
|
||||
if constexpr (Conversion::NumPreserveRegisters > 0) {
|
||||
[&mcg]<size_t... Is>(std::index_sequence<Is...>) {
|
||||
mcg.template SaveRegisters<Is...>();
|
||||
}(typename Conversion::PreserveRegisters{});
|
||||
}
|
||||
|
||||
/* Allocate space on the stack for parameters that need it. */
|
||||
if constexpr (UsedStackSpace > 0) {
|
||||
mcg.template AllocateStackSpace<UsedStackSpace>();
|
||||
}
|
||||
|
||||
/* Sanitize all input booleans. */
|
||||
SanitizeInputBooleans<Conversion>(mcg);
|
||||
|
||||
/* Generate code for before operations. */
|
||||
if constexpr (Conversion::NumBeforeOperations > 0) {
|
||||
allocator = GenerateBeforeOperations<Conversion, InitialAllocator>(mcg, typename Conversion::BeforeOperations{});
|
||||
} else {
|
||||
allocator = InitialAllocator;
|
||||
}
|
||||
|
||||
/* Generate code for after operations. */
|
||||
if constexpr (Conversion::NumAfterOperations > 0) {
|
||||
if (!TryPrepareForKernelProcedureToSvcInvocationCoalescing<Conversion>(typename Conversion::AfterOperations{}, mcg, allocator)) {
|
||||
/* We're not eligible for the straightforward optimization. */
|
||||
[&mcg, &allocator]<size_t... Is>(std::index_sequence<Is...>) {
|
||||
(Conversion::template GenerateCode<typename std::tuple_element<Is, typename Conversion::AfterOperations>::type, CodeGenerationKind::PrepareForKernelProcedureToSvcInvocation>(mcg, allocator), ...);
|
||||
}(std::make_index_sequence<Conversion::NumAfterOperations>());
|
||||
}
|
||||
}
|
||||
|
||||
return mcg.GetMetaCode();
|
||||
}
|
||||
public:
|
||||
using SvcAbiType = _SvcAbiType;
|
||||
using UserAbiType = _UserAbiType;
|
||||
using KernelAbiType = _KernelAbiType;
|
||||
|
||||
using Conversion = LayoutConversion<SvcAbiType, UserAbiType, KernelAbiType, ReturnType, ArgumentTypes...>;
|
||||
|
||||
static constexpr size_t UsedStackSpace = Conversion::NonAbiUsedStackIndices * KernelAbiType::RegisterSize;
|
||||
|
||||
static constexpr MetaCode OriginalBeforeMetaCode = [] {
|
||||
return GenerateOriginalBeforeMetaCode();
|
||||
}();
|
||||
|
||||
static constexpr MetaCode OriginalAfterMetaCode = [] {
|
||||
MetaCodeGenerator mcg;
|
||||
RegisterAllocator<KernelAbiType::RegisterCount> allocator;
|
||||
static_assert(SvcAbiType::RegisterCount == KernelAbiType::RegisterCount);
|
||||
|
||||
/* Generate code for after operations. */
|
||||
if constexpr (Conversion::NumAfterOperations > 0) {
|
||||
if (!TryKernelProcedureToSvcInvocationCoalescing<Conversion>(typename Conversion::AfterOperations{}, mcg, allocator)) {
|
||||
[&mcg, &allocator]<size_t... Is>(std::index_sequence<Is...>) {
|
||||
(Conversion::template GenerateCode<typename std::tuple_element<Is, typename Conversion::AfterOperations>::type, CodeGenerationKind::KernelProcedureToSvcInvocation>(mcg, allocator), ...);
|
||||
}(std::make_index_sequence<Conversion::NumAfterOperations>());
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate space on the stack for parameters that need it. */
|
||||
if constexpr (UsedStackSpace > 0) {
|
||||
mcg.template FreeStackSpace<UsedStackSpace>();
|
||||
}
|
||||
|
||||
if constexpr (Conversion::NumClearRegisters > 0) {
|
||||
[&mcg]<size_t... Is>(std::index_sequence<Is...>) {
|
||||
mcg.template ClearRegisters<Is...>();
|
||||
}(typename Conversion::ClearRegisters{});
|
||||
}
|
||||
|
||||
/* Restore registers we previously saved to the stack. */
|
||||
if constexpr (Conversion::NumPreserveRegisters > 0) {
|
||||
[&mcg]<size_t... Is>(std::index_sequence<Is...>) {
|
||||
mcg.template RestoreRegisters<Is...>();
|
||||
}(typename Conversion::PreserveRegisters{});
|
||||
}
|
||||
|
||||
return mcg.GetMetaCode();
|
||||
}();
|
||||
|
||||
/* TODO: Implement meta code optimization via separate layer. */
|
||||
/* Right now some basic optimizations are just implemented by the above generators. */
|
||||
static constexpr MetaCode OptimizedBeforeMetaCode = OriginalBeforeMetaCode;
|
||||
static constexpr MetaCode OptimizedAfterMetaCode = OriginalAfterMetaCode;
|
||||
};
|
||||
|
||||
template<typename _SvcAbiType, typename _UserAbiType, typename _KernelAbiType, auto Function>
|
||||
class KernelSvcWrapperHelper {
|
||||
private:
|
||||
using Traits = FunctionTraits<Function>;
|
||||
public:
|
||||
using Impl = KernelSvcWrapperHelperImpl<_SvcAbiType, _UserAbiType, _KernelAbiType, typename Traits::ReturnType, typename Traits::ArgsType>;
|
||||
using ReturnType = typename Traits::ReturnType;
|
||||
|
||||
static constexpr bool IsAarch64Kernel = std::is_same<_KernelAbiType, Aarch64Lp64Abi>::value;
|
||||
static constexpr bool IsAarch32Kernel = std::is_same<_KernelAbiType, Aarch32Ilp32Abi>::value;
|
||||
static_assert(IsAarch64Kernel || IsAarch32Kernel);
|
||||
|
||||
using CodeGenerator = typename std::conditional<IsAarch64Kernel, Aarch64CodeGenerator, Aarch32CodeGenerator>::type;
|
||||
|
||||
static constexpr auto BeforeMetaCode = Impl::OptimizedBeforeMetaCode;
|
||||
static constexpr auto AfterMetaCode = Impl::OptimizedAfterMetaCode;
|
||||
|
||||
/* Set omit-frame-pointer to prevent GCC from emitting MOV X29, SP instructions. */
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O2")
|
||||
#pragma GCC optimize ("omit-frame-pointer")
|
||||
|
||||
static ALWAYS_INLINE ReturnType WrapSvcFunction() {
|
||||
/* Generate appropriate assembly. */
|
||||
GenerateCodeForMetaCode<CodeGenerator, BeforeMetaCode>();
|
||||
ON_SCOPE_EXIT { GenerateCodeForMetaCode<CodeGenerator, AfterMetaCode>(); };
|
||||
|
||||
/* Cast the generated function to the generic funciton pointer type. */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
return reinterpret_cast<ReturnType (*)()>(Function)();
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_common.hpp>
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_parameter.hpp>
|
||||
|
||||
namespace ams::svc::codegen::impl {
|
||||
|
||||
class ParameterLayout {
|
||||
public:
|
||||
static constexpr size_t MaxParameters = 8;
|
||||
private:
|
||||
static constexpr size_t InvalidIndex = std::numeric_limits<size_t>::max();
|
||||
public:
|
||||
/* ABI parameters. */
|
||||
Abi m_abi;
|
||||
|
||||
/* Parameter storage. */
|
||||
size_t m_num_parameters;
|
||||
Parameter m_parameters[MaxParameters];
|
||||
public:
|
||||
constexpr explicit ParameterLayout(Abi a)
|
||||
: m_abi(a), m_num_parameters(0), m_parameters()
|
||||
{ /* ... */ }
|
||||
|
||||
constexpr void AddSingle(Parameter::Identifier id, ArgumentType type, size_t ts, size_t ps, bool p, bool b, Storage s, size_t idx) {
|
||||
for (size_t i = 0; i < m_num_parameters; i++) {
|
||||
if (m_parameters[i].Is(id)) {
|
||||
m_parameters[i].AddLocation(Location(s, idx));
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_parameters[m_num_parameters++] = Parameter(id, type, ts, ps, p, b, Location(s, idx));
|
||||
}
|
||||
|
||||
constexpr size_t Add(Parameter::Identifier id, ArgumentType type, size_t ts, size_t ps, bool p, bool b, Storage s, size_t i) {
|
||||
size_t required_registers = 0;
|
||||
|
||||
while (required_registers * m_abi.register_size < ps) {
|
||||
this->AddSingle(id, type, ts, ps, p, b, s, i++);
|
||||
required_registers++;
|
||||
}
|
||||
|
||||
return required_registers;
|
||||
}
|
||||
|
||||
constexpr bool UsesLocation(Location l) const {
|
||||
for (size_t i = 0; i < m_num_parameters; i++) {
|
||||
if (m_parameters[i].UsesLocation(l)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool UsesRegister(size_t i) const {
|
||||
return this->UsesLocation(Location(Storage::Register, i));
|
||||
}
|
||||
|
||||
constexpr bool IsRegisterFree(size_t i) const {
|
||||
return !(this->UsesRegister(i));
|
||||
}
|
||||
|
||||
constexpr size_t GetNumParameters() const {
|
||||
return m_num_parameters;
|
||||
}
|
||||
|
||||
constexpr Parameter GetParameter(size_t i) const {
|
||||
return m_parameters[i];
|
||||
}
|
||||
|
||||
constexpr bool HasParameter(Parameter::Identifier id) const {
|
||||
for (size_t i = 0; i < m_num_parameters; i++) {
|
||||
if (m_parameters[i].Is(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr Parameter GetParameter(Parameter::Identifier id) const {
|
||||
for (size_t i = 0; i < m_num_parameters; i++) {
|
||||
if (m_parameters[i].Is(id)) {
|
||||
return m_parameters[i];
|
||||
}
|
||||
}
|
||||
|
||||
AMS_ASSUME(false);
|
||||
}
|
||||
};
|
||||
|
||||
class ProcedureLayout {
|
||||
public:
|
||||
Abi m_abi;
|
||||
ParameterLayout m_input;
|
||||
ParameterLayout m_output;
|
||||
private:
|
||||
template<typename AbiType, typename ArgType>
|
||||
constexpr void ProcessArgument(size_t i, size_t &NGRN, size_t &NSAA) {
|
||||
/* We currently don't implement support for floating point types. */
|
||||
static_assert(!std::is_floating_point<ArgType>::value);
|
||||
static_assert(!std::is_same<ArgType, void>::value);
|
||||
|
||||
constexpr size_t ArgumentTypeSize = AbiType::template Size<ArgType>;
|
||||
constexpr bool PassedByPointer = IsPassedByPointer<AbiType, ArgType>;
|
||||
constexpr bool IsBoolean = std::same_as<ArgType, bool>;
|
||||
constexpr size_t ArgumentPassSize = PassedByPointer ? AbiType::PointerSize : ArgumentTypeSize;
|
||||
|
||||
/* TODO: Is there ever a case where this is not the correct alignment? */
|
||||
constexpr size_t ArgumentAlignment = ArgumentPassSize;
|
||||
|
||||
/* Ensure NGRN is aligned. */
|
||||
if constexpr (ArgumentAlignment > AbiType::RegisterSize) {
|
||||
NGRN += (NGRN & 1);
|
||||
}
|
||||
|
||||
/* TODO: We don't support splitting arguments between registers and stack, but AAPCS32 does. */
|
||||
/* Is this a problem? Nintendo seems to not ever do this. */
|
||||
|
||||
auto id = Parameter::Identifier("FunctionParameter", i);
|
||||
|
||||
/* Allocate integral types specially per aapcs. */
|
||||
constexpr ArgumentType Type = GetArgumentType<ArgType>;
|
||||
const size_t registers_available = AbiType::RegisterCount - NGRN;
|
||||
if constexpr (!PassedByPointer && IsIntegralOrUserPointer<ArgType> && ArgumentTypeSize > AbiType::RegisterSize) {
|
||||
if (registers_available >= 2) {
|
||||
m_input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, IsBoolean, Storage::Register, NGRN);
|
||||
NGRN += 2;
|
||||
} else {
|
||||
/* Argument went on stack, so stop allocating arguments in registers. */
|
||||
NGRN = AbiType::RegisterCount;
|
||||
|
||||
NSAA += (NSAA & 1);
|
||||
m_input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, IsBoolean, Storage::Stack, NSAA);
|
||||
NSAA += 2;
|
||||
}
|
||||
} else {
|
||||
if (ArgumentPassSize <= AbiType::RegisterSize * registers_available) {
|
||||
NGRN += m_input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, IsBoolean, Storage::Register, NGRN);
|
||||
} else {
|
||||
/* Argument went on stack, so stop allocating arguments in registers. */
|
||||
NGRN = AbiType::RegisterCount;
|
||||
|
||||
/* TODO: Stack pointer alignment is only ensured for aapcs64. */
|
||||
/* What should we do here? */
|
||||
|
||||
NSAA += m_input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, IsBoolean, Storage::Stack, NSAA);
|
||||
}
|
||||
}
|
||||
}
|
||||
public:
|
||||
constexpr explicit ProcedureLayout(Abi a) : m_abi(a), m_input(a), m_output(a) { /* ... */ }
|
||||
|
||||
template<typename AbiType, typename ReturnType, typename... ArgumentTypes>
|
||||
static constexpr ProcedureLayout Create() {
|
||||
ProcedureLayout layout(Abi::Convert<AbiType>());
|
||||
|
||||
/* 1. The Next General-purpose Register Number (NGRN) is set to zero. */
|
||||
[[maybe_unused]] size_t NGRN = 0;
|
||||
|
||||
/* 2. The next stacked argument address (NSAA) is set to the current stack-pointer value (SP). */
|
||||
[[maybe_unused]] size_t NSAA = 0; /* Should be considered an offset from stack pointer. */
|
||||
|
||||
/* 3. Handle the return type. */
|
||||
/* TODO: It's unclear how to handle the non-integral and too-large case. */
|
||||
if constexpr (!std::is_same<ReturnType, void>::value) {
|
||||
constexpr size_t ReturnTypeSize = AbiType::template Size<ReturnType>;
|
||||
layout.m_output.Add(Parameter::Identifier("ReturnType"), ArgumentType::Invalid, ReturnTypeSize, ReturnTypeSize, false, false /* TODO */, Storage::Register, 0);
|
||||
static_assert(IsIntegral<ReturnType> || ReturnTypeSize <= AbiType::RegisterSize);
|
||||
}
|
||||
|
||||
/* Process all arguments, in order. */
|
||||
size_t i = 0;
|
||||
(layout.ProcessArgument<AbiType, ArgumentTypes>(i++, NGRN, NSAA), ...);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
constexpr ParameterLayout GetInputLayout() const {
|
||||
return m_input;
|
||||
}
|
||||
|
||||
constexpr ParameterLayout GetOutputLayout() const {
|
||||
return m_output;
|
||||
}
|
||||
|
||||
constexpr Parameter GetParameter(Parameter::Identifier id) const {
|
||||
if (m_input.HasParameter(id)) {
|
||||
return m_input.GetParameter(id);
|
||||
} else {
|
||||
return m_output.GetParameter(id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SvcInvocationLayout {
|
||||
public:
|
||||
Abi m_abi;
|
||||
ParameterLayout m_input;
|
||||
ParameterLayout m_output;
|
||||
private:
|
||||
template<typename F>
|
||||
constexpr void ForEachInputArgument(ParameterLayout param_layout, F f) {
|
||||
/* We want to iterate over the parameters in sorted order. */
|
||||
std::array<size_t, ParameterLayout::MaxParameters> map = {};
|
||||
const size_t num_parameters = param_layout.GetNumParameters();
|
||||
for (size_t i = 0; i < num_parameters; i++) {
|
||||
map[i] = i;
|
||||
}
|
||||
for (size_t i = 1; i < num_parameters; i++) {
|
||||
for (size_t j = i; j > 0 && param_layout.GetParameter(map[j-1]).GetLocation(0) > param_layout.GetParameter(map[j]).GetLocation(0); j--) {
|
||||
std::swap(map[j], map[j-1]);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < param_layout.GetNumParameters(); i++) {
|
||||
const auto Parameter = param_layout.GetParameter(map[i]);
|
||||
if (Parameter.GetArgumentType() == ArgumentType::In && !Parameter.IsPassedByPointer()) {
|
||||
f(Parameter);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < param_layout.GetNumParameters(); i++) {
|
||||
const auto Parameter = param_layout.GetParameter(map[i]);
|
||||
if (Parameter.GetArgumentType() == ArgumentType::InUserPointer) {
|
||||
f(Parameter);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < param_layout.GetNumParameters(); i++) {
|
||||
const auto Parameter = param_layout.GetParameter(map[i]);
|
||||
if (Parameter.GetArgumentType() == ArgumentType::OutUserPointer) {
|
||||
f(Parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr void ForEachInputPointerArgument(ParameterLayout param_layout, F f) {
|
||||
for (size_t i = 0; i < param_layout.GetNumParameters(); i++) {
|
||||
const auto Parameter = param_layout.GetParameter(i);
|
||||
if (Parameter.GetArgumentType() == ArgumentType::In && Parameter.IsPassedByPointer()) {
|
||||
f(Parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr void ForEachOutputArgument(ParameterLayout param_layout, F f) {
|
||||
for (size_t i = 0; i < param_layout.GetNumParameters(); i++) {
|
||||
const auto Parameter = param_layout.GetParameter(i);
|
||||
if (Parameter.GetArgumentType() == ArgumentType::Out) {
|
||||
f(Parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
static constexpr void AddRegisterParameter(ParameterLayout &dst_layout, RegisterAllocator<N> ®_allocator, Parameter param) {
|
||||
for (size_t i = 0; i < param.GetNumLocations(); i++) {
|
||||
const auto location = param.GetLocation(i);
|
||||
if (location.GetStorage() == Storage::Register) {
|
||||
reg_allocator.Allocate(location.GetIndex());
|
||||
dst_layout.AddSingle(param.GetIdentifier(), param.GetArgumentType(), param.GetTypeSize(), param.GetPassedSize(), param.IsPassedByPointer(), param.IsBoolean(), Storage::Register, location.GetIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
static constexpr void AddStackParameter(ParameterLayout &dst_layout, RegisterAllocator<N> ®_allocator, Parameter param) {
|
||||
for (size_t i = 0; i < param.GetNumLocations(); i++) {
|
||||
const auto location = param.GetLocation(i);
|
||||
if (location.GetStorage() == Storage::Stack) {
|
||||
const size_t free_reg = reg_allocator.AllocateFirstFree();
|
||||
dst_layout.AddSingle(param.GetIdentifier(), param.GetArgumentType(), param.GetTypeSize(), param.GetPassedSize(), param.IsPassedByPointer(), param.IsBoolean(), Storage::Register, free_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename AbiType, size_t N>
|
||||
static constexpr void AddIndirectParameter(ParameterLayout &dst_layout, RegisterAllocator<N> ®_allocator, Parameter param) {
|
||||
const size_t type_size = param.GetTypeSize();
|
||||
for (size_t sz = 0; sz < type_size; sz += AbiType::RegisterSize) {
|
||||
const size_t free_reg = reg_allocator.AllocateFirstFree();
|
||||
dst_layout.AddSingle(param.GetIdentifier(), param.GetArgumentType(), type_size, type_size, false, param.IsBoolean(), Storage::Register, free_reg);
|
||||
}
|
||||
}
|
||||
public:
|
||||
constexpr explicit SvcInvocationLayout(Abi a) : m_abi(a), m_input(a), m_output(a) { /* ... */ }
|
||||
|
||||
template<typename AbiType>
|
||||
static constexpr SvcInvocationLayout Create(ProcedureLayout procedure_layout) {
|
||||
SvcInvocationLayout layout(Abi::Convert<AbiType>());
|
||||
RegisterAllocator<AbiType::RegisterCount> input_register_allocator, output_register_allocator;
|
||||
|
||||
/* Input first wants to map in register -> register */
|
||||
layout.ForEachInputArgument(procedure_layout.GetInputLayout(), [&](Parameter parameter) {
|
||||
AddRegisterParameter(layout.m_input, input_register_allocator, parameter);
|
||||
});
|
||||
|
||||
/* And then input wants to map in stack -> stack */
|
||||
layout.ForEachInputArgument(procedure_layout.GetInputLayout(), [&](Parameter parameter) {
|
||||
AddStackParameter(layout.m_input, input_register_allocator, parameter);
|
||||
});
|
||||
|
||||
/* And then input wants to map in indirects -> register */
|
||||
layout.ForEachInputPointerArgument(procedure_layout.GetInputLayout(), [&](Parameter parameter) {
|
||||
AddIndirectParameter<AbiType>(layout.m_input, input_register_allocator, parameter);
|
||||
});
|
||||
|
||||
/* Handle the return type. */
|
||||
if (procedure_layout.GetOutputLayout().GetNumParameters() > 0) {
|
||||
if (procedure_layout.GetOutputLayout().GetNumParameters() != 1) {
|
||||
std::abort();
|
||||
}
|
||||
const auto return_param = procedure_layout.GetOutputLayout().GetParameter(0);
|
||||
if (return_param.GetIdentifier() != Parameter::Identifier("ReturnType")) {
|
||||
std::abort();
|
||||
}
|
||||
AddRegisterParameter(layout.m_output, output_register_allocator, return_param);
|
||||
}
|
||||
|
||||
/* Handle other outputs. */
|
||||
layout.ForEachOutputArgument(procedure_layout.GetInputLayout(), [&](Parameter parameter) {
|
||||
AddIndirectParameter<AbiType>(layout.m_output, output_register_allocator, parameter);
|
||||
});
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
constexpr ParameterLayout GetInputLayout() const {
|
||||
return m_input;
|
||||
}
|
||||
|
||||
constexpr ParameterLayout GetOutputLayout() const {
|
||||
return m_output;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -1,483 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_common.hpp>
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_parameter.hpp>
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_layout.hpp>
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_meta_code.hpp>
|
||||
|
||||
namespace ams::svc::codegen::impl {
|
||||
|
||||
class LayoutConversionBase {
|
||||
public:
|
||||
enum class OperationKind {
|
||||
Move,
|
||||
LoadAndStore,
|
||||
PackAndUnpack,
|
||||
Scatter,
|
||||
Invalid,
|
||||
};
|
||||
|
||||
class OperationMoveImpl;
|
||||
class OperationLoadAndStoreImpl;
|
||||
class OperationPackAndUnpackImpl;
|
||||
class OperationScatterImpl;
|
||||
|
||||
class OperationBase{};
|
||||
|
||||
template<OperationKind _Kind, size_t RS, size_t PS, size_t SO, size_t PIdx, size_t... SIdx>
|
||||
class Operation : public OperationBase {
|
||||
public:
|
||||
static constexpr OperationKind Kind = _Kind;
|
||||
static constexpr size_t RegisterSize = RS;
|
||||
static constexpr size_t PassedSize = PS;
|
||||
static constexpr size_t StackOffset = SO;
|
||||
static constexpr size_t ProcedureIndex = PIdx;
|
||||
|
||||
static constexpr size_t NumSvcIndices = sizeof...(SIdx);
|
||||
static constexpr std::array<size_t, sizeof...(SIdx)> SvcIndices = { SIdx... };
|
||||
static constexpr std::index_sequence<SIdx...> SvcIndexSequence = {};
|
||||
|
||||
template<size_t I>
|
||||
static constexpr size_t SvcIndex = SvcIndices[I];
|
||||
|
||||
template<typename F>
|
||||
static void ForEachSvcIndex(F f) {
|
||||
(f(SIdx), ...);
|
||||
}
|
||||
|
||||
using ImplType = typename std::conditional<Kind == OperationKind::Move, OperationMoveImpl,
|
||||
typename std::conditional<Kind == OperationKind::LoadAndStore, OperationLoadAndStoreImpl,
|
||||
typename std::conditional<Kind == OperationKind::PackAndUnpack, OperationPackAndUnpackImpl,
|
||||
typename std::conditional<Kind == OperationKind::Scatter, OperationScatterImpl,
|
||||
void>::type>::type>::type>::type;
|
||||
|
||||
template<size_t NPIdx>
|
||||
using ModifiedType = Operation<Kind, RS, PS, SO, NPIdx, SIdx...>;
|
||||
};
|
||||
|
||||
template<size_t RS, size_t PS, size_t PIdx, size_t SIdx>
|
||||
using OperationMove = Operation<OperationKind::Move, RS, PS, 0, PIdx, SIdx>;
|
||||
|
||||
template<size_t RS, size_t PS, size_t PIdx, size_t SIdx>
|
||||
using OperationLoadAndStore = Operation<OperationKind::LoadAndStore, RS, PS, 0, PIdx, SIdx>;
|
||||
|
||||
template<size_t RS, size_t PS, size_t PIdx, size_t SIdx0, size_t SIdx1>
|
||||
using OperationPackAndUnpack = Operation<OperationKind::PackAndUnpack, RS, PS, 0, PIdx, SIdx0, SIdx1>;
|
||||
|
||||
template<size_t RS, size_t PS, size_t SO, size_t PIdx, size_t... SIdx>
|
||||
using OperationScatter = Operation<OperationKind::Scatter, RS, PS, SO, PIdx, SIdx...>;
|
||||
|
||||
class OperationMoveImpl {
|
||||
public:
|
||||
template<typename Operation, size_t N>
|
||||
static constexpr bool CanGenerateCodeForSvcInvocationToKernelProcedure(RegisterAllocator<N> allocator) {
|
||||
static_assert(Operation::Kind == OperationKind::Move);
|
||||
allocator.Free(Operation::template SvcIndex<0>);
|
||||
return allocator.TryAllocate(Operation::ProcedureIndex);
|
||||
}
|
||||
|
||||
template<typename Operation, size_t N>
|
||||
static constexpr void GenerateCodeForSvcInvocationToKernelProcedure(MetaCodeGenerator &mcg, RegisterAllocator<N> &allocator) {
|
||||
static_assert(Operation::Kind == OperationKind::Move);
|
||||
allocator.Free(Operation::template SvcIndex<0>);
|
||||
allocator.Allocate(Operation::ProcedureIndex);
|
||||
mcg.template MoveRegister<Operation::ProcedureIndex, Operation::template SvcIndex<0>>();
|
||||
}
|
||||
};
|
||||
|
||||
class OperationLoadAndStoreImpl {
|
||||
public:
|
||||
template<typename Operation, size_t N>
|
||||
static constexpr bool CanGenerateCodeForSvcInvocationToKernelProcedure(RegisterAllocator<N> allocator) {
|
||||
static_assert(Operation::Kind == OperationKind::LoadAndStore);
|
||||
allocator.Free(Operation::template SvcIndex<0>);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Operation, size_t N>
|
||||
static constexpr void GenerateCodeForSvcInvocationToKernelProcedure(MetaCodeGenerator &mcg, RegisterAllocator<N> &allocator) {
|
||||
static_assert(Operation::Kind == OperationKind::LoadAndStore);
|
||||
allocator.Free(Operation::template SvcIndex<0>);
|
||||
constexpr size_t StackOffset = Operation::ProcedureIndex * Operation::RegisterSize;
|
||||
mcg.template StoreToStack<Operation::template SvcIndex<0>, StackOffset>();
|
||||
}
|
||||
};
|
||||
|
||||
class OperationPackAndUnpackImpl {
|
||||
public:
|
||||
template<typename Operation, size_t N>
|
||||
static constexpr bool CanGenerateCodeForSvcInvocationToKernelProcedure(RegisterAllocator<N> allocator) {
|
||||
static_assert(Operation::Kind == OperationKind::PackAndUnpack);
|
||||
allocator.Free(Operation::template SvcIndex<0>);
|
||||
allocator.Free(Operation::template SvcIndex<1>);
|
||||
return allocator.TryAllocate(Operation::ProcedureIndex);
|
||||
}
|
||||
|
||||
template<typename Operation, size_t N>
|
||||
static constexpr void GenerateCodeForSvcInvocationToKernelProcedure(MetaCodeGenerator &mcg, RegisterAllocator<N> &allocator) {
|
||||
static_assert(Operation::Kind == OperationKind::PackAndUnpack);
|
||||
allocator.Free(Operation::template SvcIndex<0>);
|
||||
allocator.Free(Operation::template SvcIndex<1>);
|
||||
allocator.Allocate(Operation::ProcedureIndex);
|
||||
mcg.template Pack<Operation::ProcedureIndex, Operation::template SvcIndex<0>, Operation::template SvcIndex<1>>();
|
||||
}
|
||||
|
||||
template<typename Operation>
|
||||
static constexpr void GenerateCodeForPrepareForKernelProcedureToSvcInvocation(MetaCodeGenerator &mcg) {
|
||||
static_assert(Operation::Kind == OperationKind::PackAndUnpack);
|
||||
AMS_UNUSED(mcg);
|
||||
}
|
||||
|
||||
template<typename Operation>
|
||||
static constexpr void GenerateCodeForKernelProcedureToSvcInvocation(MetaCodeGenerator &mcg) {
|
||||
static_assert(Operation::Kind == OperationKind::PackAndUnpack);
|
||||
mcg.template Unpack<Operation::template SvcIndex<0>, Operation::template SvcIndex<1>, Operation::ProcedureIndex>();
|
||||
}
|
||||
};
|
||||
|
||||
class OperationScatterImpl {
|
||||
public:
|
||||
template<typename Operation, size_t N>
|
||||
static constexpr bool CanGenerateCodeForSvcInvocationToKernelProcedure(RegisterAllocator<N> allocator) {
|
||||
static_assert(Operation::Kind == OperationKind::Scatter);
|
||||
[&allocator]<size_t... SvcIndex>(std::index_sequence<SvcIndex...>) {
|
||||
(allocator.Free(SvcIndex), ...);
|
||||
}(Operation::SvcIndexSequence);
|
||||
return allocator.TryAllocate(Operation::ProcedureIndex);
|
||||
}
|
||||
|
||||
template<typename Operation, size_t N>
|
||||
static constexpr void GenerateCodeForSvcInvocationToKernelProcedure(MetaCodeGenerator &mcg, RegisterAllocator<N> &allocator) {
|
||||
static_assert(Operation::Kind == OperationKind::Scatter);
|
||||
[&allocator]<size_t... SvcIndex>(std::index_sequence<SvcIndex...>) {
|
||||
(allocator.Free(SvcIndex), ...);
|
||||
}(Operation::SvcIndexSequence);
|
||||
allocator.Allocate(Operation::ProcedureIndex);
|
||||
|
||||
[&mcg]<size_t... Is>(std::index_sequence<Is...>) {
|
||||
(mcg.template StoreToStack<Operation::template SvcIndex<Is>, Operation::StackOffset + Operation::RegisterSize * (Is), Operation::RegisterSize>(), ...);
|
||||
}(std::make_index_sequence<Operation::NumSvcIndices>());
|
||||
|
||||
mcg.template LoadStackAddress<Operation::ProcedureIndex, Operation::StackOffset>();
|
||||
}
|
||||
|
||||
template<typename Operation>
|
||||
static constexpr void GenerateCodeForPrepareForKernelProcedureToSvcInvocation(MetaCodeGenerator &mcg) {
|
||||
static_assert(Operation::Kind == OperationKind::Scatter);
|
||||
|
||||
[&mcg]<size_t... Is>(std::index_sequence<Is...>) {
|
||||
(mcg.template StoreToStack<Operation::template SvcIndex<Is>, Operation::StackOffset + Operation::RegisterSize * (Is), Operation::RegisterSize>(), ...);
|
||||
}(std::make_index_sequence<Operation::NumSvcIndices>());
|
||||
|
||||
mcg.template LoadStackAddress<Operation::ProcedureIndex, Operation::StackOffset>();
|
||||
}
|
||||
|
||||
template<typename Operation>
|
||||
static constexpr void GenerateCodeForKernelProcedureToSvcInvocation(MetaCodeGenerator &mcg) {
|
||||
static_assert(Operation::Kind == OperationKind::Scatter);
|
||||
|
||||
[&mcg]<size_t... Is>(std::index_sequence<Is...>) {
|
||||
(mcg.template LoadFromStack<Operation::template SvcIndex<Is>, Operation::StackOffset + Operation::RegisterSize * (Is), Operation::RegisterSize>(), ...);
|
||||
}(std::make_index_sequence<Operation::NumSvcIndices>());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template<typename _SvcAbiType, typename _UserAbiType, typename _KernelAbiType, typename ReturnType, typename... ArgumentTypes>
|
||||
class LayoutConversion {
|
||||
public:
|
||||
using SvcAbiType = _SvcAbiType;
|
||||
using UserAbiType = _UserAbiType;
|
||||
using KernelAbiType = _KernelAbiType;
|
||||
|
||||
static constexpr auto LayoutForUser = ProcedureLayout::Create<UserAbiType, ReturnType, ArgumentTypes...>();
|
||||
static constexpr auto LayoutForSvc = SvcInvocationLayout::Create<SvcAbiType>(LayoutForUser);
|
||||
static constexpr auto LayoutForKernel = ProcedureLayout::Create<KernelAbiType, ReturnType, ArgumentTypes...>();
|
||||
private:
|
||||
template<bool Input, size_t ParameterIndex = 0, size_t Used = 0>
|
||||
static constexpr size_t DetermineUsedStackIndices() {
|
||||
[[maybe_unused]] constexpr auto Procedure = LayoutForKernel;
|
||||
[[maybe_unused]] constexpr ParameterLayout Svc = Input ? LayoutForSvc.GetInputLayout() : LayoutForSvc.GetOutputLayout();
|
||||
|
||||
if constexpr (ParameterIndex >= Svc.GetNumParameters()) {
|
||||
/* Base case: we're done. */
|
||||
return Used;
|
||||
} else {
|
||||
/* We're processing more parameters. */
|
||||
constexpr Parameter SvcParam = Svc.GetParameter(ParameterIndex);
|
||||
constexpr Parameter ProcedureParam = Procedure.GetParameter(SvcParam.GetIdentifier());
|
||||
|
||||
if constexpr (SvcParam.IsPassedByPointer() == ProcedureParam.IsPassedByPointer()) {
|
||||
/* We're not scattering, so stack won't be used. */
|
||||
return DetermineUsedStackIndices<Input, ParameterIndex + 1, Used>();
|
||||
} else {
|
||||
/* We're scattering, and so we're using stack. */
|
||||
static_assert(ProcedureParam.GetNumLocations() == 1);
|
||||
|
||||
constexpr size_t IndicesPerRegister = KernelAbiType::RegisterSize / SvcAbiType::RegisterSize;
|
||||
static_assert(IndicesPerRegister > 0);
|
||||
|
||||
constexpr size_t RequiredCount = util::AlignUp(SvcParam.GetNumLocations(), IndicesPerRegister) / IndicesPerRegister;
|
||||
|
||||
return DetermineUsedStackIndices<Input, ParameterIndex + 1, Used + RequiredCount>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr size_t AbiUsedStackIndices = [] {
|
||||
constexpr auto KernLayout = LayoutForKernel.GetInputLayout();
|
||||
|
||||
size_t used = 0;
|
||||
for (size_t i = 0; i < KernLayout.GetNumParameters(); i++) {
|
||||
const auto Param = KernLayout.GetParameter(i);
|
||||
for (size_t j = 0; j < Param.GetNumLocations(); j++) {
|
||||
const auto Loc = Param.GetLocation(j);
|
||||
if (Loc.GetStorage() == Storage::Stack) {
|
||||
used = std::max(used, Loc.GetIndex() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return used;
|
||||
}();
|
||||
|
||||
static constexpr size_t BeforeUsedStackIndices = DetermineUsedStackIndices<true>();
|
||||
static constexpr size_t AfterUsedStackIndices = DetermineUsedStackIndices<false>();
|
||||
|
||||
template<bool Input, size_t ParameterIndex, size_t LocationIndex>
|
||||
static constexpr auto ZipMoveOperations() {
|
||||
constexpr auto Procedure = LayoutForKernel;
|
||||
constexpr ParameterLayout Svc = Input ? LayoutForSvc.GetInputLayout() : LayoutForSvc.GetOutputLayout();
|
||||
|
||||
static_assert(ParameterIndex < Svc.GetNumParameters());
|
||||
|
||||
constexpr Parameter SvcParam = Svc.GetParameter(ParameterIndex);
|
||||
constexpr Parameter ProcedureParam = Procedure.GetParameter(SvcParam.GetIdentifier());
|
||||
|
||||
static_assert(SvcParam.IsPassedByPointer() == ProcedureParam.IsPassedByPointer());
|
||||
static_assert(SvcParam.GetNumLocations() == ProcedureParam.GetNumLocations());
|
||||
|
||||
if constexpr (LocationIndex >= SvcParam.GetNumLocations()) {
|
||||
/* Base case: we're done. */
|
||||
return std::tuple<>{};
|
||||
} else {
|
||||
constexpr Location SvcLoc = SvcParam.GetLocation(LocationIndex);
|
||||
constexpr Location ProcedureLoc = ProcedureParam.GetLocation(LocationIndex);
|
||||
|
||||
if constexpr (SvcLoc == ProcedureLoc) {
|
||||
/* No need to emit an operation if we're not changing where we are. */
|
||||
return ZipMoveOperations<Input, ParameterIndex, LocationIndex + 1>();
|
||||
} else {
|
||||
/* Svc location needs to be in a register. */
|
||||
static_assert(SvcLoc.GetStorage() == Storage::Register);
|
||||
|
||||
constexpr size_t Size = KernelAbiType::RegisterSize;
|
||||
|
||||
if constexpr (ProcedureLoc.GetStorage() == Storage::Register) {
|
||||
using OperationType = LayoutConversionBase::OperationMove<Size, Size, ProcedureLoc.GetIndex(), SvcLoc.GetIndex()>;
|
||||
constexpr auto cur_op = std::make_tuple(OperationType{});
|
||||
return std::tuple_cat(cur_op, ZipMoveOperations<Input, ParameterIndex, LocationIndex + 1>());
|
||||
} else {
|
||||
using OperationType = LayoutConversionBase::OperationLoadAndStore<Size, Size, ProcedureLoc.GetIndex(), SvcLoc.GetIndex()>;
|
||||
constexpr auto cur_op = std::make_tuple(OperationType{});
|
||||
return std::tuple_cat(cur_op, ZipMoveOperations<Input, ParameterIndex, LocationIndex + 1>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Input, size_t ParameterIndex, size_t StackIndex>
|
||||
static constexpr auto DetermineConversionOperations() {
|
||||
[[maybe_unused]] constexpr auto Procedure = LayoutForKernel;
|
||||
[[maybe_unused]] constexpr ParameterLayout Svc = Input ? LayoutForSvc.GetInputLayout() : LayoutForSvc.GetOutputLayout();
|
||||
[[maybe_unused]] constexpr std::array<size_t, Svc.GetNumParameters()> ParameterMap = []<auto CapturedSvc>(){
|
||||
/* We want to iterate over the parameters in sorted order. */
|
||||
std::array<size_t, CapturedSvc.GetNumParameters()> map{};
|
||||
const size_t num_parameters = CapturedSvc.GetNumParameters();
|
||||
for (size_t i = 0; i < num_parameters; i++) {
|
||||
map[i] = i;
|
||||
}
|
||||
for (size_t i = 1; i < num_parameters; i++) {
|
||||
for (size_t j = i; j > 0 && CapturedSvc.GetParameter(map[j-1]).GetLocation(0) > CapturedSvc.GetParameter(map[j]).GetLocation(0); j--) {
|
||||
std::swap(map[j], map[j-1]);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}.template operator()<Svc>();
|
||||
|
||||
if constexpr (ParameterIndex >= Svc.GetNumParameters()) {
|
||||
/* Base case: we're done. */
|
||||
if constexpr (Input) {
|
||||
static_assert(StackIndex == BeforeUsedStackIndices + AbiUsedStackIndices);
|
||||
} else {
|
||||
static_assert(StackIndex == AfterUsedStackIndices + BeforeUsedStackIndices + AbiUsedStackIndices);
|
||||
}
|
||||
return std::tuple<>{};
|
||||
} else {
|
||||
/* We're processing more parameters. */
|
||||
constexpr Parameter SvcParam = Svc.GetParameter(ParameterMap[ParameterIndex]);
|
||||
constexpr Parameter ProcedureParam = Procedure.GetParameter(SvcParam.GetIdentifier());
|
||||
|
||||
if constexpr (SvcParam.IsPassedByPointer() == ProcedureParam.IsPassedByPointer()) {
|
||||
if constexpr (SvcParam.GetNumLocations() == ProcedureParam.GetNumLocations()) {
|
||||
/* Normal moves and loads/stores. */
|
||||
return std::tuple_cat(ZipMoveOperations<Input, ParameterMap[ParameterIndex], 0>(), DetermineConversionOperations<Input, ParameterIndex + 1, StackIndex>());
|
||||
} else {
|
||||
/* We're packing. */
|
||||
/* Make sure we're handling the 2 -> 1 case. */
|
||||
static_assert(SvcParam.GetNumLocations() == 2);
|
||||
static_assert(ProcedureParam.GetNumLocations() == 1);
|
||||
|
||||
constexpr Location ProcedureLoc = ProcedureParam.GetLocation(0);
|
||||
constexpr Location SvcLoc0 = SvcParam.GetLocation(0);
|
||||
constexpr Location SvcLoc1 = SvcParam.GetLocation(1);
|
||||
static_assert(ProcedureLoc.GetStorage() == Storage::Register);
|
||||
static_assert(SvcLoc0.GetStorage() == Storage::Register);
|
||||
static_assert(SvcLoc1.GetStorage() == Storage::Register);
|
||||
|
||||
constexpr size_t Size = KernelAbiType::RegisterSize;
|
||||
|
||||
using OperationType = LayoutConversionBase::OperationPackAndUnpack<Size, Size, ProcedureLoc.GetIndex(), SvcLoc0.GetIndex(), SvcLoc1.GetIndex()>;
|
||||
|
||||
constexpr auto cur_op = std::make_tuple(OperationType{});
|
||||
|
||||
return std::tuple_cat(cur_op, DetermineConversionOperations<Input, ParameterIndex + 1, StackIndex>());
|
||||
}
|
||||
} else {
|
||||
/* One operation, since we're scattering. */
|
||||
static_assert(ProcedureParam.GetNumLocations() == 1);
|
||||
constexpr Location ProcedureLoc = ProcedureParam.GetLocation(0);
|
||||
|
||||
constexpr size_t IndicesPerRegister = KernelAbiType::RegisterSize / SvcAbiType::RegisterSize;
|
||||
static_assert(IndicesPerRegister > 0);
|
||||
|
||||
constexpr size_t RequiredCount = util::AlignUp(SvcParam.GetNumLocations(), IndicesPerRegister) / IndicesPerRegister;
|
||||
|
||||
if constexpr (ProcedureLoc.GetStorage() == Storage::Register) {
|
||||
/* Scattering. In register during kernel call. */
|
||||
constexpr size_t RegisterSize = SvcAbiType::RegisterSize;
|
||||
constexpr size_t PassedSize = ProcedureParam.GetTypeSize();
|
||||
|
||||
constexpr auto SvcIndexSequence = []<auto CapturedSvcParam, size_t... Is>(std::index_sequence<Is...>) {
|
||||
return std::index_sequence<CapturedSvcParam.GetLocation(Is).GetIndex()...>{};
|
||||
}.template operator()<SvcParam>(std::make_index_sequence<SvcParam.GetNumLocations()>());
|
||||
|
||||
constexpr auto OperationValue = []<auto CapturedProcedureLoc, size_t... Is>(std::index_sequence<Is...>) {
|
||||
return LayoutConversionBase::OperationScatter<RegisterSize, PassedSize, StackIndex * KernelAbiType::RegisterSize, CapturedProcedureLoc.GetIndex(), Is...>{};
|
||||
}.template operator()<ProcedureLoc>(SvcIndexSequence);
|
||||
|
||||
constexpr auto cur_op = std::make_tuple(OperationValue);
|
||||
|
||||
return std::tuple_cat(cur_op, DetermineConversionOperations<Input, ParameterIndex + 1, StackIndex + RequiredCount>());
|
||||
} else {
|
||||
/* TODO: How should on-stack-during-kernel-call be handled? */
|
||||
static_assert(ProcedureLoc.GetStorage() == Storage::Register);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr size_t PreserveRegisterStartIndex = SvcAbiType::ArgumentRegisterCount;
|
||||
static constexpr size_t PreserveRegisterEndIndex = std::min(KernelAbiType::ArgumentRegisterCount, SvcAbiType::RegisterCount);
|
||||
static constexpr size_t ClearRegisterStartIndex = 0;
|
||||
static constexpr size_t ClearRegisterEndIndex = std::min(KernelAbiType::ArgumentRegisterCount, SvcAbiType::RegisterCount);
|
||||
|
||||
template<size_t Index>
|
||||
static constexpr bool ShouldPreserveRegister = (PreserveRegisterStartIndex <= Index && Index < PreserveRegisterEndIndex) &&
|
||||
LayoutForSvc.GetInputLayout().IsRegisterFree(Index) && LayoutForSvc.GetOutputLayout().IsRegisterFree(Index);
|
||||
|
||||
template<size_t Index>
|
||||
static constexpr bool ShouldClearRegister = (ClearRegisterStartIndex <= Index && Index < ClearRegisterEndIndex) &&
|
||||
LayoutForSvc.GetOutputLayout().IsRegisterFree(Index) && !ShouldPreserveRegister<Index>;
|
||||
|
||||
template<size_t Index = PreserveRegisterStartIndex>
|
||||
static constexpr auto DeterminePreserveRegisters() {
|
||||
static_assert(PreserveRegisterStartIndex <= Index && Index <= PreserveRegisterEndIndex);
|
||||
|
||||
if constexpr (Index >= PreserveRegisterEndIndex) {
|
||||
/* Base case: we're done. */
|
||||
return std::index_sequence<>{};
|
||||
} else {
|
||||
if constexpr (ShouldPreserveRegister<Index>) {
|
||||
/* Preserve this register. */
|
||||
return IndexSequenceCat(std::index_sequence<Index>{}, DeterminePreserveRegisters<Index + 1>());
|
||||
} else {
|
||||
/* We don't need to preserve register, so we can skip onwards. */
|
||||
return IndexSequenceCat(std::index_sequence<>{}, DeterminePreserveRegisters<Index + 1>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Index = ClearRegisterStartIndex>
|
||||
static constexpr auto DetermineClearRegisters() {
|
||||
static_assert(ClearRegisterStartIndex <= Index && Index <= ClearRegisterEndIndex);
|
||||
|
||||
if constexpr (Index >= ClearRegisterEndIndex) {
|
||||
/* Base case: we're done. */
|
||||
return std::index_sequence<>{};
|
||||
} else {
|
||||
if constexpr (ShouldClearRegister<Index>) {
|
||||
/* Clear this register. */
|
||||
return IndexSequenceCat(std::index_sequence<Index>{}, DetermineClearRegisters<Index + 1>());
|
||||
} else {
|
||||
/* We don't need to preserve register, so we can skip onwards. */
|
||||
return IndexSequenceCat(std::index_sequence<>{}, DetermineClearRegisters<Index + 1>());
|
||||
}
|
||||
}
|
||||
}
|
||||
public:
|
||||
static constexpr size_t NonAbiUsedStackIndices = AfterUsedStackIndices + BeforeUsedStackIndices;
|
||||
using BeforeOperations = decltype(DetermineConversionOperations<true, 0, AbiUsedStackIndices>());
|
||||
using AfterOperations = decltype(DetermineConversionOperations<false, 0, AbiUsedStackIndices + BeforeUsedStackIndices>());
|
||||
|
||||
static constexpr size_t NumBeforeOperations = std::tuple_size<BeforeOperations>::value;
|
||||
static constexpr size_t NumAfterOperations = std::tuple_size<AfterOperations>::value;
|
||||
|
||||
using PreserveRegisters = decltype(DeterminePreserveRegisters());
|
||||
using ClearRegisters = decltype(DetermineClearRegisters());
|
||||
|
||||
static constexpr size_t NumPreserveRegisters = PreserveRegisters::size();
|
||||
static constexpr size_t NumClearRegisters = ClearRegisters::size();
|
||||
|
||||
static constexpr auto PreserveRegistersArray = ConvertToArray(PreserveRegisters{});
|
||||
static constexpr auto ClearRegistersArray = ConvertToArray(ClearRegisters{});
|
||||
public:
|
||||
template<typename Operation, CodeGenerationKind CodeGenKind, size_t N>
|
||||
static constexpr bool CanGenerateCode(RegisterAllocator<N> allocator) {
|
||||
if constexpr (CodeGenKind == CodeGenerationKind::SvcInvocationToKernelProcedure) {
|
||||
return Operation::ImplType::template CanGenerateCodeForSvcInvocationToKernelProcedure<Operation>(allocator);
|
||||
} else {
|
||||
static_assert(false, "Invalid CodeGenerationKind");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Operation, CodeGenerationKind CodeGenKind, size_t N>
|
||||
static constexpr void GenerateCode(MetaCodeGenerator &mcg, RegisterAllocator<N> &allocator) {
|
||||
if constexpr (CodeGenKind == CodeGenerationKind::SvcInvocationToKernelProcedure) {
|
||||
Operation::ImplType::template GenerateCodeForSvcInvocationToKernelProcedure<Operation>(mcg, allocator);
|
||||
} else if constexpr (CodeGenKind == CodeGenerationKind::PrepareForKernelProcedureToSvcInvocation) {
|
||||
Operation::ImplType::template GenerateCodeForPrepareForKernelProcedureToSvcInvocation<Operation>(mcg);
|
||||
} else if constexpr (CodeGenKind == CodeGenerationKind::KernelProcedureToSvcInvocation) {
|
||||
Operation::ImplType::template GenerateCodeForKernelProcedureToSvcInvocation<Operation>(mcg);
|
||||
} else {
|
||||
static_assert(false, "Invalid CodeGenerationKind");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_common.hpp>
|
||||
|
||||
namespace ams::svc::codegen::impl {
|
||||
|
||||
class MetaCode {
|
||||
public:
|
||||
static constexpr size_t MaxOperations = 0x40;
|
||||
|
||||
enum class OperationKind {
|
||||
SaveRegisters,
|
||||
RestoreRegisters,
|
||||
ClearRegisters,
|
||||
AllocateStackSpace,
|
||||
FreeStackSpace,
|
||||
MoveRegister,
|
||||
ConvertToBoolean,
|
||||
LoadFromStack,
|
||||
LoadPairFromStack,
|
||||
StoreToStack,
|
||||
StorePairToStack,
|
||||
Pack,
|
||||
Unpack,
|
||||
LoadStackAddress,
|
||||
};
|
||||
|
||||
static constexpr const char *GetOperationKindName(OperationKind k) {
|
||||
#define META_CODE_OPERATION_KIND_ENUM_CASE(s) case OperationKind::s: return #s
|
||||
switch (k) {
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(SaveRegisters);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(RestoreRegisters);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(ClearRegisters);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(AllocateStackSpace);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(FreeStackSpace);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(MoveRegister);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(ConvertToBoolean);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(LoadFromStack);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(LoadPairFromStack);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(StoreToStack);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(StorePairToStack);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(Pack);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(Unpack);
|
||||
META_CODE_OPERATION_KIND_ENUM_CASE(LoadStackAddress);
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
#undef META_CODE_OPERATION_KIND_ENUM_CASE
|
||||
}
|
||||
|
||||
struct Operation {
|
||||
OperationKind kind;
|
||||
size_t num_parameters;
|
||||
size_t parameters[16];
|
||||
};
|
||||
|
||||
template<OperationKind Kind, size_t... Is>
|
||||
static constexpr inline Operation MakeOperation() {
|
||||
Operation op = {};
|
||||
static_assert(sizeof...(Is) <= sizeof(op.parameters) / sizeof(op.parameters[0]));
|
||||
|
||||
op.kind = Kind;
|
||||
op.num_parameters = sizeof...(Is);
|
||||
|
||||
size_t i = 0;
|
||||
((op.parameters[i++] = Is), ...);
|
||||
|
||||
return op;
|
||||
}
|
||||
public:
|
||||
size_t m_num_operations;
|
||||
std::array<Operation, MaxOperations> m_operations;
|
||||
public:
|
||||
constexpr explicit MetaCode() : m_num_operations(0), m_operations() { /* ... */ }
|
||||
|
||||
constexpr size_t GetNumOperations() const {
|
||||
return m_num_operations;
|
||||
}
|
||||
|
||||
constexpr Operation GetOperation(size_t i) const {
|
||||
return m_operations[i];
|
||||
}
|
||||
|
||||
constexpr void AddOperation(Operation op) {
|
||||
m_operations[m_num_operations++] = op;
|
||||
}
|
||||
};
|
||||
|
||||
template<auto Operation>
|
||||
static constexpr auto GetOperationParameterSequence() {
|
||||
constexpr size_t NumParameters = Operation.num_parameters;
|
||||
|
||||
return []<size_t... Is>(std::index_sequence<Is...>) {
|
||||
return std::index_sequence<Operation.parameters[Is]...>{};
|
||||
}(std::make_index_sequence<NumParameters>());
|
||||
}
|
||||
|
||||
template<typename CodeGenerator, MetaCode::OperationKind Kind, size_t... Parameters>
|
||||
static ALWAYS_INLINE void GenerateCodeForOperationImpl(std::index_sequence<Parameters...>) {
|
||||
#define META_CODE_OPERATION_KIND_GENERATE_CODE(KIND) else if constexpr (Kind == MetaCode::OperationKind::KIND) { CodeGenerator::template KIND<Parameters...>(); }
|
||||
if constexpr (false) { /* ... */ }
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(SaveRegisters)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(RestoreRegisters)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(ClearRegisters)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(AllocateStackSpace)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(FreeStackSpace)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(MoveRegister)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(ConvertToBoolean)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(LoadFromStack)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(LoadPairFromStack)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(StoreToStack)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(StorePairToStack)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(Pack)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(Unpack)
|
||||
META_CODE_OPERATION_KIND_GENERATE_CODE(LoadStackAddress)
|
||||
else { static_assert(false, "Unknown MetaOperationKind"); }
|
||||
#undef META_CODE_OPERATION_KIND_GENERATE_CODE
|
||||
}
|
||||
|
||||
template<typename CodeGenerator, auto Operation>
|
||||
static ALWAYS_INLINE void GenerateCodeForOperation() {
|
||||
GenerateCodeForOperationImpl<CodeGenerator, Operation.kind>(GetOperationParameterSequence<Operation>());
|
||||
}
|
||||
|
||||
class MetaCodeGenerator {
|
||||
private:
|
||||
using OperationKind = typename MetaCode::OperationKind;
|
||||
private:
|
||||
MetaCode m_meta_code;
|
||||
public:
|
||||
constexpr explicit MetaCodeGenerator() : m_meta_code() { /* ... */ }
|
||||
|
||||
constexpr MetaCode GetMetaCode() const {
|
||||
return m_meta_code;
|
||||
}
|
||||
|
||||
constexpr void AddOperationDirectly(MetaCode::Operation op) {
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t... Registers>
|
||||
constexpr void SaveRegisters() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::SaveRegisters, Registers...>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t... Registers>
|
||||
constexpr void RestoreRegisters() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::RestoreRegisters, Registers...>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t... Registers>
|
||||
constexpr void ClearRegisters() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::ClearRegisters, Registers...>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t Size>
|
||||
constexpr void AllocateStackSpace() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::AllocateStackSpace, Size>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t Size>
|
||||
constexpr void FreeStackSpace() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::FreeStackSpace, Size>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t Dst, size_t Src>
|
||||
constexpr void MoveRegister() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::MoveRegister, Dst, Src>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t Reg>
|
||||
constexpr void ConvertToBoolean() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::ConvertToBoolean, Reg>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t Reg, size_t Offset, size_t Size = 0>
|
||||
constexpr void LoadFromStack() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::LoadFromStack, Reg, Offset, Size>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t Reg0, size_t Reg1, size_t Offset, size_t Size>
|
||||
constexpr void LoadPairFromStack() {
|
||||
static_assert(Offset % Size == 0);
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::LoadPairFromStack, Reg0, Reg1, Offset, Size>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t Reg, size_t Offset, size_t Size = 0>
|
||||
constexpr void StoreToStack() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::StoreToStack, Reg, Offset, Size>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t Reg0, size_t Reg1, size_t Offset, size_t Size>
|
||||
constexpr void StorePairToStack() {
|
||||
static_assert(Offset % Size == 0);
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::StorePairToStack, Reg0, Reg1, Offset, Size>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t Dst, size_t Low, size_t High>
|
||||
constexpr void Pack() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::Pack, Dst, Low, High>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t Low, size_t High, size_t Src>
|
||||
constexpr void Unpack() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::Unpack, Low, High, Src>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
|
||||
template<size_t Dst, size_t Offset>
|
||||
constexpr void LoadStackAddress() {
|
||||
constexpr auto op = MetaCode::MakeOperation<OperationKind::LoadStackAddress, Dst, Offset>();
|
||||
m_meta_code.AddOperation(op);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_common.hpp>
|
||||
|
||||
namespace ams::svc::codegen::impl {
|
||||
|
||||
enum class Storage {
|
||||
Register,
|
||||
Stack,
|
||||
Invalid,
|
||||
};
|
||||
|
||||
class Location {
|
||||
private:
|
||||
static constexpr size_t InvalidIndex = std::numeric_limits<size_t>::max();
|
||||
public:
|
||||
Storage m_storage;
|
||||
size_t m_index;
|
||||
public:
|
||||
constexpr explicit Location() : m_storage(Storage::Invalid), m_index(InvalidIndex) { /* ... */ }
|
||||
constexpr explicit Location(Storage s, size_t i) : m_storage(s), m_index(i) { /* ... */ }
|
||||
|
||||
constexpr size_t GetIndex() const { return m_index; }
|
||||
constexpr Storage GetStorage() const { return m_storage; }
|
||||
|
||||
constexpr bool IsValid() const {
|
||||
return m_index != InvalidIndex && m_storage != Storage::Invalid;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const Location &rhs) const {
|
||||
return m_index == rhs.m_index && m_storage == rhs.m_storage;
|
||||
}
|
||||
|
||||
constexpr bool operator<(const Location &rhs) const {
|
||||
if (m_storage < rhs.m_storage) {
|
||||
return true;
|
||||
} else if (m_storage > rhs.m_storage) {
|
||||
return false;
|
||||
} else {
|
||||
return m_index < rhs.m_index;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool operator>(const Location &rhs) const {
|
||||
if (m_storage > rhs.m_storage) {
|
||||
return true;
|
||||
} else if (m_storage < rhs.m_storage) {
|
||||
return false;
|
||||
} else {
|
||||
return m_index > rhs.m_index;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const Location &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
class Parameter {
|
||||
public:
|
||||
static constexpr size_t MaxLocations = 8;
|
||||
static constexpr size_t IdentifierLengthMax = 0x40;
|
||||
class Identifier {
|
||||
public:
|
||||
char m_name[IdentifierLengthMax];
|
||||
size_t m_index;
|
||||
public:
|
||||
constexpr explicit Identifier() : m_name(), m_index() { /* ... */ }
|
||||
constexpr explicit Identifier(const char *nm, size_t idx = 0) : m_name(), m_index(idx) {
|
||||
for (size_t i = 0; i < IdentifierLengthMax && nm[i]; i++) {
|
||||
m_name[i] = nm[i];
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool operator==(const Identifier &rhs) const {
|
||||
for (size_t i = 0; i < IdentifierLengthMax; i++) {
|
||||
if (m_name[i] != rhs.m_name[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return m_index == rhs.m_index;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const Identifier &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
public:
|
||||
Identifier m_identifier;
|
||||
ArgumentType m_type;
|
||||
size_t m_type_size;
|
||||
size_t m_passed_size;
|
||||
bool m_passed_by_pointer;
|
||||
bool m_is_boolean;
|
||||
size_t m_num_locations;
|
||||
Location m_locations[MaxLocations];
|
||||
public:
|
||||
constexpr explicit Parameter()
|
||||
: m_identifier(), m_type(ArgumentType::Invalid), m_type_size(0), m_passed_size(0), m_passed_by_pointer(0), m_is_boolean(0), m_num_locations(0), m_locations()
|
||||
{ /* ... */ }
|
||||
|
||||
constexpr explicit Parameter(Identifier id, ArgumentType t, size_t ts, size_t ps, bool p, bool b, Location l)
|
||||
: m_identifier(id), m_type(t), m_type_size(ts), m_passed_size(ps), m_passed_by_pointer(p), m_is_boolean(b), m_num_locations(1), m_locations()
|
||||
{
|
||||
m_locations[0] = l;
|
||||
}
|
||||
|
||||
constexpr Identifier GetIdentifier() const {
|
||||
return m_identifier;
|
||||
}
|
||||
|
||||
constexpr bool Is(Identifier rhs) const {
|
||||
return m_identifier == rhs;
|
||||
}
|
||||
|
||||
constexpr ArgumentType GetArgumentType() const {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
constexpr size_t GetTypeSize() const {
|
||||
return m_type_size;
|
||||
}
|
||||
|
||||
constexpr size_t GetPassedSize() const {
|
||||
return m_passed_size;
|
||||
}
|
||||
|
||||
constexpr bool IsPassedByPointer() const {
|
||||
return m_passed_by_pointer;
|
||||
}
|
||||
|
||||
constexpr bool IsBoolean() const {
|
||||
return m_is_boolean;
|
||||
}
|
||||
|
||||
constexpr size_t GetNumLocations() const {
|
||||
return m_num_locations;
|
||||
}
|
||||
|
||||
constexpr Location GetLocation(size_t i) const {
|
||||
return m_locations[i];
|
||||
}
|
||||
|
||||
constexpr void AddLocation(Location l) {
|
||||
m_locations[m_num_locations++] = l;
|
||||
}
|
||||
|
||||
constexpr bool UsesLocation(Location l) const {
|
||||
for (size_t i = 0; i < m_num_locations; i++) {
|
||||
if (m_locations[i] == l) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const Parameter &rhs) const {
|
||||
if (!(m_identifier == rhs.m_identifier &&
|
||||
m_type == rhs.m_type &&
|
||||
m_type_size == rhs.m_type_size &&
|
||||
m_passed_size == rhs.m_passed_size &&
|
||||
m_passed_by_pointer == rhs.m_passed_by_pointer &&
|
||||
m_is_boolean == rhs.m_is_boolean &&
|
||||
m_num_locations == rhs.m_num_locations))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_num_locations; i++) {
|
||||
if (!(m_locations[i] == rhs.m_locations[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const Parameter &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp>
|
||||
|
||||
namespace ams::svc::codegen {
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64) || defined(ATMOSPHERE_ARCH_ARM)
|
||||
|
||||
template<auto &Function64, auto &Function64From32>
|
||||
class KernelSvcWrapper {
|
||||
private:
|
||||
/* TODO: using Aarch32 = */
|
||||
using Aarch64 = impl::KernelSvcWrapperHelper<impl::Aarch64SvcInvokeAbi, impl::Aarch64Lp64Abi, impl::Aarch64Lp64Abi, Function64>;
|
||||
using Aarch64From32 = impl::KernelSvcWrapperHelper<impl::Aarch32SvcInvokeAbi, impl::Aarch32Ilp32Abi, impl::Aarch64Lp64Abi, Function64From32>;
|
||||
public:
|
||||
/* Set omit-frame-pointer to prevent GCC from emitting MOV X29, SP instructions. */
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O2")
|
||||
#pragma GCC optimize ("omit-frame-pointer")
|
||||
|
||||
static ALWAYS_INLINE void Call64() {
|
||||
if constexpr (std::is_same<typename Aarch64::ReturnType, void>::value) {
|
||||
Aarch64::WrapSvcFunction();
|
||||
} else {
|
||||
const auto &res = Aarch64::WrapSvcFunction();
|
||||
__asm__ __volatile__("" :: [res]"r"(res));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE void Call64From32() {
|
||||
if constexpr (std::is_same<typename Aarch64::ReturnType, void>::value) {
|
||||
Aarch64From32::WrapSvcFunction();
|
||||
} else {
|
||||
const auto &res = Aarch64From32::WrapSvcFunction();
|
||||
__asm__ __volatile__("" :: [res]"r"(res));
|
||||
}
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#error "Unknown architecture for Kernel SVC Code Generation"
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,573 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
#include <vapours/svc/svc_select_thread_local_region.hpp>
|
||||
|
||||
namespace ams::svc::ipc {
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O3")
|
||||
|
||||
ALWAYS_INLINE u32 *GetMessageBuffer() {
|
||||
return GetThreadLocalRegion()->message_buffer;
|
||||
}
|
||||
|
||||
constexpr inline size_t MessageBufferSize = sizeof(::ams::svc::ThreadLocalRegion::message_buffer);
|
||||
|
||||
class MessageBuffer {
|
||||
public:
|
||||
class MessageHeader {
|
||||
private:
|
||||
/* Define fields for the first header word. */
|
||||
using Tag = util::BitPack32::Field<0, BITSIZEOF(u16), u16>;
|
||||
using PointerCount = util::BitPack32::Field<Tag::Next, 4, s32>;
|
||||
using SendCount = util::BitPack32::Field<PointerCount::Next, 4, s32>;
|
||||
using ReceiveCount = util::BitPack32::Field<SendCount::Next, 4, s32>;
|
||||
using ExchangeCount = util::BitPack32::Field<ReceiveCount::Next, 4, s32>;
|
||||
static_assert(ExchangeCount::Next == BITSIZEOF(u32));
|
||||
|
||||
/* Define fields for the second header word. */
|
||||
using RawCount = util::BitPack32::Field<0, 10, s32>;
|
||||
using ReceiveListCount = util::BitPack32::Field<RawCount::Next, 4, s32>;
|
||||
using Reserved0 = util::BitPack32::Field<ReceiveListCount::Next, 6, u32>;
|
||||
using ReceiveListOffset = util::BitPack32::Field<Reserved0::Next, 11, s32>;
|
||||
using HasSpecialHeader = util::BitPack32::Field<ReceiveListOffset::Next, 1, bool>;
|
||||
|
||||
static constexpr inline u64 NullTag = 0;
|
||||
static_assert(HasSpecialHeader::Next == BITSIZEOF(u32));
|
||||
public:
|
||||
enum ReceiveListCountType {
|
||||
ReceiveListCountType_None = 0,
|
||||
ReceiveListCountType_ToMessageBuffer = 1,
|
||||
ReceiveListCountType_ToSingleBuffer = 2,
|
||||
|
||||
ReceiveListCountType_CountOffset = 2,
|
||||
ReceiveListCountType_CountMax = 13,
|
||||
};
|
||||
private:
|
||||
util::BitPack32 m_header[2];
|
||||
public:
|
||||
constexpr ALWAYS_INLINE MessageHeader() : m_header{util::BitPack32{0}, util::BitPack32{0}} {
|
||||
m_header[0].Set<Tag>(NullTag);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE MessageHeader(u16 tag, bool special, s32 ptr, s32 send, s32 recv, s32 exch, s32 raw, s32 recv_list) : m_header{util::BitPack32{0}, util::BitPack32{0}} {
|
||||
m_header[0].Set<Tag>(tag);
|
||||
m_header[0].Set<PointerCount>(ptr);
|
||||
m_header[0].Set<SendCount>(send);
|
||||
m_header[0].Set<ReceiveCount>(recv);
|
||||
m_header[0].Set<ExchangeCount>(exch);
|
||||
|
||||
m_header[1].Set<RawCount>(raw);
|
||||
m_header[1].Set<ReceiveListCount>(recv_list);
|
||||
m_header[1].Set<HasSpecialHeader>(special);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE explicit MessageHeader(const MessageBuffer &buf) : m_header{util::BitPack32{0}, util::BitPack32{0}} {
|
||||
buf.Get(0, m_header, util::size(m_header));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE explicit MessageHeader(const u32 *msg) : m_header{util::BitPack32{msg[0]}, util::BitPack32{msg[1]}} { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE u16 GetTag() const {
|
||||
return m_header[0].Get<Tag>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetPointerCount() const {
|
||||
return m_header[0].Get<PointerCount>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetSendCount() const {
|
||||
return m_header[0].Get<SendCount>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetReceiveCount() const {
|
||||
return m_header[0].Get<ReceiveCount>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetExchangeCount() const {
|
||||
return m_header[0].Get<ExchangeCount>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetMapAliasCount() const {
|
||||
return this->GetSendCount() + this->GetReceiveCount() + this->GetExchangeCount();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetRawCount() const {
|
||||
return m_header[1].Get<RawCount>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetReceiveListCount() const {
|
||||
return m_header[1].Get<ReceiveListCount>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetReceiveListOffset() const {
|
||||
return m_header[1].Get<ReceiveListOffset>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool GetHasSpecialHeader() const {
|
||||
return m_header[1].Get<HasSpecialHeader>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void SetReceiveListCount(s32 recv_list) {
|
||||
m_header[1].Set<ReceiveListCount>(recv_list);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const util::BitPack32 *GetData() const {
|
||||
return m_header;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE size_t GetDataSize() {
|
||||
return sizeof(m_header);
|
||||
}
|
||||
};
|
||||
|
||||
class SpecialHeader {
|
||||
private:
|
||||
/* Define fields for the header word. */
|
||||
using HasProcessId = util::BitPack32::Field<0, 1, bool>;
|
||||
using CopyHandleCount = util::BitPack32::Field<HasProcessId::Next, 4, s32>;
|
||||
using MoveHandleCount = util::BitPack32::Field<CopyHandleCount::Next, 4, s32>;
|
||||
private:
|
||||
util::BitPack32 m_header;
|
||||
bool m_has_header;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE explicit SpecialHeader(bool pid, s32 copy, s32 move) : m_header{0}, m_has_header(true) {
|
||||
m_header.Set<HasProcessId>(pid);
|
||||
m_header.Set<CopyHandleCount>(copy);
|
||||
m_header.Set<MoveHandleCount>(move);
|
||||
}
|
||||
|
||||
consteval explicit SpecialHeader(bool pid, s32 copy, s32 move, bool _has_header) : m_header{0}, m_has_header(_has_header) {
|
||||
m_header.Set<HasProcessId>(pid);
|
||||
m_header.Set<CopyHandleCount>(copy);
|
||||
m_header.Set<MoveHandleCount>(move);
|
||||
|
||||
AMS_ASSUME(m_has_header == (this->GetHasProcessId() || this->GetCopyHandleCount() > 0 || this->GetMoveHandleCount() > 0));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE explicit SpecialHeader(const MessageBuffer &buf, const MessageHeader &hdr) : m_header{0}, m_has_header(hdr.GetHasSpecialHeader()) {
|
||||
if (m_has_header) {
|
||||
buf.Get(MessageHeader::GetDataSize() / sizeof(util::BitPack32), std::addressof(m_header), sizeof(m_header) / sizeof(util::BitPack32));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool GetHasProcessId() const {
|
||||
return m_header.Get<HasProcessId>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetCopyHandleCount() const {
|
||||
return m_header.Get<CopyHandleCount>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetMoveHandleCount() const {
|
||||
return m_header.Get<MoveHandleCount>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const util::BitPack32 *GetHeader() const {
|
||||
return std::addressof(m_header);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_t GetHeaderSize() const {
|
||||
if (m_has_header) {
|
||||
return sizeof(m_header);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_t GetDataSize() const {
|
||||
if (m_has_header) {
|
||||
return (this->GetHasProcessId() ? sizeof(u64) : 0) +
|
||||
(this->GetCopyHandleCount() * sizeof(Handle)) +
|
||||
(this->GetMoveHandleCount() * sizeof(Handle));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class MapAliasDescriptor {
|
||||
public:
|
||||
enum Attribute {
|
||||
Attribute_Ipc = 0,
|
||||
Attribute_NonSecureIpc = 1,
|
||||
Attribute_NonDeviceIpc = 3,
|
||||
};
|
||||
private:
|
||||
/* Define fields for the first two words. */
|
||||
using SizeLow = util::BitPack32::Field<0, BITSIZEOF(u32), u32>;
|
||||
using AddressLow = util::BitPack32::Field<0, BITSIZEOF(u32), u32>;
|
||||
|
||||
/* Define fields for the packed descriptor word. */
|
||||
using Attributes = util::BitPack32::Field<0, 2, Attribute>;
|
||||
using AddressHigh = util::BitPack32::Field<Attributes::Next, 3, u32>;
|
||||
using Reserved = util::BitPack32::Field<AddressHigh::Next, 19, u32>;
|
||||
using SizeHigh = util::BitPack32::Field<Reserved::Next, 4, u32>;
|
||||
using AddressMid = util::BitPack32::Field<SizeHigh::Next, 4, u32>;
|
||||
|
||||
constexpr ALWAYS_INLINE u32 GetAddressMid(u64 address) {
|
||||
return static_cast<u32>(address >> AddressLow::Count) & ((1u << AddressMid::Count) - 1);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u32 GetAddressHigh(u64 address) {
|
||||
return static_cast<u32>(address >> (AddressLow::Count + AddressMid::Count));
|
||||
}
|
||||
private:
|
||||
util::BitPack32 m_data[3];
|
||||
public:
|
||||
constexpr ALWAYS_INLINE MapAliasDescriptor() : m_data{util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}} { /* ... */ }
|
||||
|
||||
ALWAYS_INLINE MapAliasDescriptor(const void *buffer, size_t _size, Attribute attr = Attribute_Ipc) : m_data{util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}} {
|
||||
const u64 address = reinterpret_cast<u64>(buffer);
|
||||
const u64 size = static_cast<u64>(_size);
|
||||
m_data[0] = { static_cast<u32>(size) };
|
||||
m_data[1] = { static_cast<u32>(address) };
|
||||
|
||||
m_data[2].Set<Attributes>(attr);
|
||||
m_data[2].Set<AddressMid>(GetAddressMid(address));
|
||||
m_data[2].Set<SizeHigh>(static_cast<u32>(size >> SizeLow::Count));
|
||||
m_data[2].Set<AddressHigh>(GetAddressHigh(address));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE MapAliasDescriptor(const MessageBuffer &buf, s32 index) : m_data{util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}} {
|
||||
buf.Get(index, m_data, util::size(m_data));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE uintptr_t GetAddress() const {
|
||||
const u64 address = (static_cast<u64>((m_data[2].Get<AddressHigh>() << AddressMid::Count) | m_data[2].Get<AddressMid>()) << AddressLow::Count) | m_data[1].Get<AddressLow>();
|
||||
return address;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE uintptr_t GetSize() const {
|
||||
const u64 size = (static_cast<u64>(m_data[2].Get<SizeHigh>()) << SizeLow::Count) | m_data[0].Get<SizeLow>();
|
||||
return size;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Attribute GetAttribute() const {
|
||||
return m_data[2].Get<Attributes>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const util::BitPack32 *GetData() const {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE size_t GetDataSize() {
|
||||
return sizeof(m_data);
|
||||
}
|
||||
};
|
||||
|
||||
class PointerDescriptor {
|
||||
private:
|
||||
/* Define fields for the packed descriptor word. */
|
||||
using Index = util::BitPack32::Field<0, 4, s32>;
|
||||
using Reserved0 = util::BitPack32::Field<Index::Next, 2, u32>;
|
||||
using AddressHigh = util::BitPack32::Field<Reserved0::Next, 3, u32>;
|
||||
using Reserved1 = util::BitPack32::Field<AddressHigh::Next, 3, u32>;
|
||||
using AddressMid = util::BitPack32::Field<Reserved1::Next, 4, u32>;
|
||||
using Size = util::BitPack32::Field<AddressMid::Next, 16, u32>;
|
||||
|
||||
/* Define fields for the second word. */
|
||||
using AddressLow = util::BitPack32::Field<0, BITSIZEOF(u32), u32>;
|
||||
|
||||
constexpr ALWAYS_INLINE u32 GetAddressMid(u64 address) {
|
||||
return static_cast<u32>(address >> AddressLow::Count) & ((1u << AddressMid::Count) - 1);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u32 GetAddressHigh(u64 address) {
|
||||
return static_cast<u32>(address >> (AddressLow::Count + AddressMid::Count));
|
||||
}
|
||||
private:
|
||||
util::BitPack32 m_data[2];
|
||||
public:
|
||||
constexpr ALWAYS_INLINE PointerDescriptor() : m_data{util::BitPack32{0}, util::BitPack32{0}} { /* ... */ }
|
||||
|
||||
ALWAYS_INLINE PointerDescriptor(const void *buffer, size_t size, s32 index) : m_data{util::BitPack32{0}, util::BitPack32{0}} {
|
||||
const u64 address = reinterpret_cast<u64>(buffer);
|
||||
|
||||
m_data[0].Set<Index>(index);
|
||||
m_data[0].Set<AddressHigh>(GetAddressHigh(address));
|
||||
m_data[0].Set<AddressMid>(GetAddressMid(address));
|
||||
m_data[0].Set<Size>(size);
|
||||
|
||||
m_data[1] = { static_cast<u32>(address) };
|
||||
}
|
||||
|
||||
ALWAYS_INLINE PointerDescriptor(const MessageBuffer &buf, s32 index) : m_data{util::BitPack32{0}, util::BitPack32{0}} {
|
||||
buf.Get(index, m_data, util::size(m_data));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetIndex() const {
|
||||
return m_data[0].Get<Index>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE uintptr_t GetAddress() const {
|
||||
const u64 address = (static_cast<u64>((m_data[0].Get<AddressHigh>() << AddressMid::Count) | m_data[0].Get<AddressMid>()) << AddressLow::Count) | m_data[1].Get<AddressLow>();
|
||||
return address;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_t GetSize() const {
|
||||
return m_data[0].Get<Size>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const util::BitPack32 *GetData() const {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE size_t GetDataSize() {
|
||||
return sizeof(m_data);
|
||||
}
|
||||
};
|
||||
|
||||
class ReceiveListEntry {
|
||||
private:
|
||||
/* Define fields for the first word. */
|
||||
using AddressLow = util::BitPack32::Field<0, BITSIZEOF(u32), u32>;
|
||||
|
||||
/* Define fields for the packed descriptor word. */
|
||||
using AddressHigh = util::BitPack32::Field<0, 7, u32>;
|
||||
using Reserved = util::BitPack32::Field<AddressHigh::Next, 9, u32>;
|
||||
using Size = util::BitPack32::Field<Reserved::Next, 16, u32>;
|
||||
|
||||
constexpr ALWAYS_INLINE u32 GetAddressHigh(u64 address) {
|
||||
return static_cast<u32>(address >> (AddressLow::Count));
|
||||
}
|
||||
private:
|
||||
util::BitPack32 m_data[2];
|
||||
public:
|
||||
constexpr ALWAYS_INLINE ReceiveListEntry() : m_data{util::BitPack32{0}, util::BitPack32{0}} { /* ... */ }
|
||||
|
||||
ALWAYS_INLINE ReceiveListEntry(const void *buffer, size_t size) : m_data{util::BitPack32{0}, util::BitPack32{0}} {
|
||||
const u64 address = reinterpret_cast<u64>(buffer);
|
||||
|
||||
m_data[0] = { static_cast<u32>(address) };
|
||||
|
||||
m_data[1].Set<AddressHigh>(GetAddressHigh(address));
|
||||
m_data[1].Set<Size>(size);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ReceiveListEntry(u32 a, u32 b) : m_data{util::BitPack32{a}, util::BitPack32{b}} { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE uintptr_t GetAddress() const {
|
||||
const u64 address = (static_cast<u64>(m_data[1].Get<AddressHigh>()) << AddressLow::Count) | m_data[0].Get<AddressLow>();
|
||||
return address;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_t GetSize() const {
|
||||
return m_data[1].Get<Size>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const util::BitPack32 *GetData() const {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE size_t GetDataSize() {
|
||||
return sizeof(m_data);
|
||||
}
|
||||
};
|
||||
private:
|
||||
u32 *m_buffer;
|
||||
size_t m_size;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE MessageBuffer(u32 *b, size_t sz) : m_buffer(b), m_size(sz) { /* ... */ }
|
||||
constexpr explicit ALWAYS_INLINE MessageBuffer(u32 *b) : m_buffer(b), m_size(sizeof(::ams::svc::ThreadLocalRegion::message_buffer)) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE void *GetBufferForDebug() const {
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_t GetBufferSize() const {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void Get(s32 index, util::BitPack32 *dst, size_t count) const {
|
||||
/* Ensure that this doesn't get re-ordered. */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
|
||||
/* Get the words. */
|
||||
static_assert(sizeof(*dst) == sizeof(*m_buffer));
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
__builtin_memcpy(dst, m_buffer + index, count * sizeof(*dst));
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
ALWAYS_INLINE s32 Set(s32 index, const util::BitPack32 *src, size_t count) const {
|
||||
/* Ensure that this doesn't get re-ordered. */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
|
||||
/* Set the words. */
|
||||
__builtin_memcpy(m_buffer + index, src, count * sizeof(*src));
|
||||
|
||||
/* Ensure that this doesn't get re-ordered. */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
|
||||
return index + count;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ALWAYS_INLINE const T &GetRaw(s32 index) const {
|
||||
return *reinterpret_cast<const T *>(m_buffer + index);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ALWAYS_INLINE s32 SetRaw(s32 index, const T &val) const {
|
||||
*reinterpret_cast<const T *>(m_buffer + index) = val;
|
||||
return index + (util::AlignUp(sizeof(val), sizeof(*m_buffer)) / sizeof(*m_buffer));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void GetRawArray(s32 index, void *dst, size_t len) const {
|
||||
__builtin_memcpy(dst, m_buffer + index, len);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void SetRawArray(s32 index, const void *src, size_t len) const {
|
||||
__builtin_memcpy(m_buffer + index, src, len);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void SetNull() const {
|
||||
this->Set(MessageHeader());
|
||||
}
|
||||
|
||||
ALWAYS_INLINE s32 Set(const MessageHeader &hdr) const {
|
||||
__builtin_memcpy(m_buffer, hdr.GetData(), hdr.GetDataSize());
|
||||
return hdr.GetDataSize() / sizeof(*m_buffer);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE s32 Set(const SpecialHeader &spc) const {
|
||||
const s32 index = MessageHeader::GetDataSize() / sizeof(*m_buffer);
|
||||
__builtin_memcpy(m_buffer + index, spc.GetHeader(), spc.GetHeaderSize());
|
||||
return index + (spc.GetHeaderSize() / sizeof(*m_buffer));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE s32 SetHandle(s32 index, const ::ams::svc::Handle &hnd) const {
|
||||
static_assert(util::IsAligned(sizeof(hnd), sizeof(*m_buffer)));
|
||||
__builtin_memcpy(m_buffer + index, std::addressof(hnd), sizeof(hnd));
|
||||
return index + (sizeof(hnd) / sizeof(*m_buffer));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE s32 SetProcessId(s32 index, const u64 pid) const {
|
||||
static_assert(util::IsAligned(sizeof(pid), sizeof(*m_buffer)));
|
||||
__builtin_memcpy(m_buffer + index, std::addressof(pid), sizeof(pid));
|
||||
return index + (sizeof(pid) / sizeof(*m_buffer));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE s32 Set(s32 index, const MapAliasDescriptor &desc) const {
|
||||
__builtin_memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize());
|
||||
return index + (desc.GetDataSize() / sizeof(*m_buffer));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE s32 Set(s32 index, const PointerDescriptor &desc) const {
|
||||
__builtin_memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize());
|
||||
return index + (desc.GetDataSize() / sizeof(*m_buffer));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE s32 Set(s32 index, const ReceiveListEntry &desc) const {
|
||||
__builtin_memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize());
|
||||
return index + (desc.GetDataSize() / sizeof(*m_buffer));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE s32 Set(s32 index, const u32 val) const {
|
||||
static_assert(util::IsAligned(sizeof(val), sizeof(*m_buffer)));
|
||||
__builtin_memcpy(m_buffer + index, std::addressof(val), sizeof(val));
|
||||
return index + (sizeof(val) / sizeof(*m_buffer));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result GetAsyncResult() const {
|
||||
MessageHeader hdr(m_buffer);
|
||||
MessageHeader null{};
|
||||
R_SUCCEED_IF(AMS_UNLIKELY((__builtin_memcmp(hdr.GetData(), null.GetData(), MessageHeader::GetDataSize()) != 0)));
|
||||
return m_buffer[MessageHeader::GetDataSize() / sizeof(*m_buffer)];
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void SetAsyncResult(Result res) const {
|
||||
const s32 index = this->Set(MessageHeader());
|
||||
const auto value = res.GetValue();
|
||||
static_assert(util::IsAligned(sizeof(value), sizeof(*m_buffer)));
|
||||
__builtin_memcpy(m_buffer + index, std::addressof(value), sizeof(value));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 Get32(s32 index) const {
|
||||
return m_buffer[index];
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u64 Get64(s32 index) const {
|
||||
u64 value;
|
||||
__builtin_memcpy(std::addressof(value), m_buffer + index, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u64 GetProcessId(s32 index) const {
|
||||
return this->Get64(index);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ams::svc::Handle GetHandle(s32 index) const {
|
||||
static_assert(sizeof(ams::svc::Handle) == sizeof(*m_buffer));
|
||||
return ::ams::svc::Handle(m_buffer[index]);
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE s32 GetSpecialDataIndex(const MessageHeader &hdr, const SpecialHeader &spc) {
|
||||
AMS_UNUSED(hdr);
|
||||
return (MessageHeader::GetDataSize() / sizeof(util::BitPack32)) + (spc.GetHeaderSize() / sizeof(util::BitPack32));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE s32 GetPointerDescriptorIndex(const MessageHeader &hdr, const SpecialHeader &spc) {
|
||||
return GetSpecialDataIndex(hdr, spc) + (spc.GetDataSize() / sizeof(util::BitPack32));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE s32 GetMapAliasDescriptorIndex(const MessageHeader &hdr, const SpecialHeader &spc) {
|
||||
return GetPointerDescriptorIndex(hdr, spc) + (hdr.GetPointerCount() * PointerDescriptor::GetDataSize() / sizeof(util::BitPack32));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE s32 GetRawDataIndex(const MessageHeader &hdr, const SpecialHeader &spc) {
|
||||
return GetMapAliasDescriptorIndex(hdr, spc) + (hdr.GetMapAliasCount() * MapAliasDescriptor::GetDataSize() / sizeof(util::BitPack32));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE s32 GetReceiveListIndex(const MessageHeader &hdr, const SpecialHeader &spc) {
|
||||
if (const s32 recv_list_index = hdr.GetReceiveListOffset()) {
|
||||
return recv_list_index;
|
||||
} else {
|
||||
return GetRawDataIndex(hdr, spc) + hdr.GetRawCount();
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE size_t GetMessageBufferSize(const MessageHeader &hdr, const SpecialHeader &spc) {
|
||||
/* Get the size of the plain message. */
|
||||
size_t msg_size = GetReceiveListIndex(hdr, spc) * sizeof(util::BitPack32);
|
||||
|
||||
/* Add the size of the receive list. */
|
||||
const auto count = hdr.GetReceiveListCount();
|
||||
switch (count) {
|
||||
case MessageHeader::ReceiveListCountType_None:
|
||||
break;
|
||||
case MessageHeader::ReceiveListCountType_ToMessageBuffer:
|
||||
break;
|
||||
case MessageHeader::ReceiveListCountType_ToSingleBuffer:
|
||||
msg_size += ReceiveListEntry::GetDataSize();
|
||||
break;
|
||||
default:
|
||||
msg_size += (count - MessageHeader::ReceiveListCountType_CountOffset) * ReceiveListEntry::GetDataSize();
|
||||
break;
|
||||
}
|
||||
|
||||
return msg_size;
|
||||
}
|
||||
};
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/* NOTE: This header must not be included by svc.hpp. */
|
||||
#include <vapours/svc/svc_common.hpp>
|
||||
#include <vapours/svc/svc_types.hpp>
|
||||
#include <vapours/svc/svc_definitions.hpp>
|
||||
|
||||
#include <vapours/svc/codegen/svc_codegen_kernel_svc_wrapper.hpp>
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
/* TODO: C++ style handle? */
|
||||
using Handle = u32;
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
static_assert(std::same_as<::ams::svc::Handle, ::Handle>);
|
||||
#endif
|
||||
|
||||
enum {
|
||||
HandleWaitMask = (1u << 30),
|
||||
};
|
||||
|
||||
constexpr inline s32 ArgumentHandleCountMax = 0x40;
|
||||
|
||||
constexpr inline s64 WaitInfinite = -1;
|
||||
|
||||
enum PseudoHandle : Handle {
|
||||
CurrentThread = 0xFFFF8000,
|
||||
CurrentProcess = 0xFFFF8001,
|
||||
};
|
||||
|
||||
constexpr inline Handle InvalidHandle = Handle(0);
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator==(const Handle &lhs, const PseudoHandle &rhs) {
|
||||
return static_cast<Handle>(lhs) == static_cast<Handle>(rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator==(const PseudoHandle &lhs, const Handle &rhs) {
|
||||
return static_cast<Handle>(lhs) == static_cast<Handle>(rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator!=(const Handle &lhs, const PseudoHandle &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator!=(const PseudoHandle &lhs, const Handle &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsPseudoHandle(const Handle &handle) {
|
||||
return handle == PseudoHandle::CurrentProcess || handle == PseudoHandle::CurrentThread;
|
||||
}
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
|
||||
|
||||
namespace lp64 { /* ... */ }
|
||||
namespace aarch64 { /* ... */ }
|
||||
using namespace ::ams::svc::lp64;
|
||||
using namespace ::ams::svc::aarch64;
|
||||
|
||||
/* TODO: ifdef ATMOSPHERE_ABI_LP64 */
|
||||
#if 1
|
||||
namespace aarch64::lp64 { /* ... */ }
|
||||
using namespace ::ams::svc::aarch64::lp64;
|
||||
#else
|
||||
namespace aarch64::ilp32 { /* ... */ }
|
||||
using namespace ::ams::svc::aarch64::ilp32;
|
||||
#endif
|
||||
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM)
|
||||
|
||||
namespace ilp32 { /* ... */ }
|
||||
namespace aarch32 { /* ... */ }
|
||||
using namespace ::ams::svc::ilp32;
|
||||
using namespace ::ams::svc::aarch32;
|
||||
|
||||
#else
|
||||
|
||||
#error "Unknown Architecture"
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define AMS_SVC_FOREACH_DEFINITION_IMPL(HANDLER, NAMESPACE, INPUT, OUTPUT, INPTR, OUTPTR) \
|
||||
HANDLER(0x01, Result, SetHeapSize, OUTPUT(::ams::svc::Address, out_address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x02, Result, SetMemoryPermission, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size), INPUT(::ams::svc::MemoryPermission, perm)) \
|
||||
HANDLER(0x03, Result, SetMemoryAttribute, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size), INPUT(uint32_t, mask), INPUT(uint32_t, attr)) \
|
||||
HANDLER(0x04, Result, MapMemory, INPUT(::ams::svc::Address, dst_address), INPUT(::ams::svc::Address, src_address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x05, Result, UnmapMemory, INPUT(::ams::svc::Address, dst_address), INPUT(::ams::svc::Address, src_address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x06, Result, QueryMemory, OUTPTR(::ams::svc::NAMESPACE::MemoryInfo, out_memory_info), OUTPUT(::ams::svc::PageInfo, out_page_info), INPUT(::ams::svc::Address, address)) \
|
||||
HANDLER(0x07, void, ExitProcess) \
|
||||
HANDLER(0x08, Result, CreateThread, OUTPUT(::ams::svc::Handle, out_handle), INPUT(::ams::svc::ThreadFunc, func), INPUT(::ams::svc::Address, arg), INPUT(::ams::svc::Address, stack_bottom), INPUT(int32_t, priority), INPUT(int32_t, core_id)) \
|
||||
HANDLER(0x09, Result, StartThread, INPUT(::ams::svc::Handle, thread_handle)) \
|
||||
HANDLER(0x0A, void, ExitThread) \
|
||||
HANDLER(0x0B, void, SleepThread, INPUT(int64_t, ns)) \
|
||||
HANDLER(0x0C, Result, GetThreadPriority, OUTPUT(int32_t, out_priority), INPUT(::ams::svc::Handle, thread_handle)) \
|
||||
HANDLER(0x0D, Result, SetThreadPriority, INPUT(::ams::svc::Handle, thread_handle), INPUT(int32_t, priority)) \
|
||||
HANDLER(0x0E, Result, GetThreadCoreMask, OUTPUT(int32_t, out_core_id), OUTPUT(uint64_t, out_affinity_mask), INPUT(::ams::svc::Handle, thread_handle)) \
|
||||
HANDLER(0x0F, Result, SetThreadCoreMask, INPUT(::ams::svc::Handle, thread_handle), INPUT(int32_t, core_id), INPUT(uint64_t, affinity_mask)) \
|
||||
HANDLER(0x10, int32_t, GetCurrentProcessorNumber) \
|
||||
HANDLER(0x11, Result, SignalEvent, INPUT(::ams::svc::Handle, event_handle)) \
|
||||
HANDLER(0x12, Result, ClearEvent, INPUT(::ams::svc::Handle, event_handle)) \
|
||||
HANDLER(0x13, Result, MapSharedMemory, INPUT(::ams::svc::Handle, shmem_handle), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size), INPUT(::ams::svc::MemoryPermission, map_perm)) \
|
||||
HANDLER(0x14, Result, UnmapSharedMemory, INPUT(::ams::svc::Handle, shmem_handle), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x15, Result, CreateTransferMemory, OUTPUT(::ams::svc::Handle, out_handle), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size), INPUT(::ams::svc::MemoryPermission, map_perm)) \
|
||||
HANDLER(0x16, Result, CloseHandle, INPUT(::ams::svc::Handle, handle)) \
|
||||
HANDLER(0x17, Result, ResetSignal, INPUT(::ams::svc::Handle, handle)) \
|
||||
HANDLER(0x18, Result, WaitSynchronization, OUTPUT(int32_t, out_index), INPTR(::ams::svc::Handle, handles), INPUT(int32_t, num_handles), INPUT(int64_t, timeout_ns)) \
|
||||
HANDLER(0x19, Result, CancelSynchronization, INPUT(::ams::svc::Handle, handle)) \
|
||||
HANDLER(0x1A, Result, ArbitrateLock, INPUT(::ams::svc::Handle, thread_handle), INPUT(::ams::svc::Address, address), INPUT(uint32_t, tag)) \
|
||||
HANDLER(0x1B, Result, ArbitrateUnlock, INPUT(::ams::svc::Address, address)) \
|
||||
HANDLER(0x1C, Result, WaitProcessWideKeyAtomic, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Address, cv_key), INPUT(uint32_t, tag), INPUT(int64_t, timeout_ns)) \
|
||||
HANDLER(0x1D, void, SignalProcessWideKey, INPUT(::ams::svc::Address, cv_key), INPUT(int32_t, count)) \
|
||||
HANDLER(0x1E, int64_t, GetSystemTick) \
|
||||
HANDLER(0x1F, Result, ConnectToNamedPort, OUTPUT(::ams::svc::Handle, out_handle), INPTR(char, name)) \
|
||||
HANDLER(0x20, Result, SendSyncRequestLight, INPUT(::ams::svc::Handle, session_handle)) \
|
||||
HANDLER(0x21, Result, SendSyncRequest, INPUT(::ams::svc::Handle, session_handle)) \
|
||||
HANDLER(0x22, Result, SendSyncRequestWithUserBuffer, INPUT(::ams::svc::Address, message_buffer), INPUT(::ams::svc::Size, message_buffer_size), INPUT(::ams::svc::Handle, session_handle)) \
|
||||
HANDLER(0x23, Result, SendAsyncRequestWithUserBuffer, OUTPUT(::ams::svc::Handle, out_event_handle), INPUT(::ams::svc::Address, message_buffer), INPUT(::ams::svc::Size, message_buffer_size), INPUT(::ams::svc::Handle, session_handle)) \
|
||||
HANDLER(0x24, Result, GetProcessId, OUTPUT(uint64_t, out_process_id), INPUT(::ams::svc::Handle, process_handle)) \
|
||||
HANDLER(0x25, Result, GetThreadId, OUTPUT(uint64_t, out_thread_id), INPUT(::ams::svc::Handle, thread_handle)) \
|
||||
HANDLER(0x26, void, Break, INPUT(::ams::svc::BreakReason, break_reason), INPUT(::ams::svc::Address, arg), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x27, Result, OutputDebugString, INPTR(char, debug_str), INPUT(::ams::svc::Size, len)) \
|
||||
HANDLER(0x28, void, ReturnFromException, INPUT(::ams::Result, result)) \
|
||||
HANDLER(0x29, Result, GetInfo, OUTPUT(uint64_t, out), INPUT(::ams::svc::InfoType, info_type), INPUT(::ams::svc::Handle, handle), INPUT(uint64_t, info_subtype)) \
|
||||
HANDLER(0x2A, void, FlushEntireDataCache) \
|
||||
HANDLER(0x2B, Result, FlushDataCache, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x2C, Result, MapPhysicalMemory, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x2D, Result, UnmapPhysicalMemory, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x2E, Result, GetDebugFutureThreadInfo, OUTPUT(::ams::svc::NAMESPACE::LastThreadContext, out_context), OUTPUT(uint64_t, thread_id), INPUT(::ams::svc::Handle, debug_handle), INPUT(int64_t, ns)) \
|
||||
HANDLER(0x2F, Result, GetLastThreadInfo, OUTPUT(::ams::svc::NAMESPACE::LastThreadContext, out_context), OUTPUT(::ams::svc::Address, out_tls_address), OUTPUT(uint32_t, out_flags)) \
|
||||
HANDLER(0x30, Result, GetResourceLimitLimitValue, OUTPUT(int64_t, out_limit_value), INPUT(::ams::svc::Handle, resource_limit_handle), INPUT(::ams::svc::LimitableResource, which)) \
|
||||
HANDLER(0x31, Result, GetResourceLimitCurrentValue, OUTPUT(int64_t, out_current_value), INPUT(::ams::svc::Handle, resource_limit_handle), INPUT(::ams::svc::LimitableResource, which)) \
|
||||
HANDLER(0x32, Result, SetThreadActivity, INPUT(::ams::svc::Handle, thread_handle), INPUT(::ams::svc::ThreadActivity, thread_activity)) \
|
||||
HANDLER(0x33, Result, GetThreadContext3, OUTPTR(::ams::svc::ThreadContext, out_context), INPUT(::ams::svc::Handle, thread_handle)) \
|
||||
HANDLER(0x34, Result, WaitForAddress, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::ArbitrationType, arb_type), INPUT(int64_t, value), INPUT(int64_t, timeout_ns)) \
|
||||
HANDLER(0x35, Result, SignalToAddress, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::SignalType, signal_type), INPUT(int32_t, value), INPUT(int32_t, count)) \
|
||||
HANDLER(0x36, void, SynchronizePreemptionState) \
|
||||
HANDLER(0x37, Result, GetResourceLimitPeakValue, OUTPUT(int64_t, out_peak_value), INPUT(::ams::svc::Handle, resource_limit_handle), INPUT(::ams::svc::LimitableResource, which)) \
|
||||
\
|
||||
HANDLER(0x39, Result, CreateIoPool, OUTPUT(::ams::svc::Handle, out_handle), INPUT(::ams::svc::IoPoolType, which)) \
|
||||
HANDLER(0x3A, Result, CreateIoRegion, OUTPUT(::ams::svc::Handle, out_handle), INPUT(::ams::svc::Handle, io_pool), INPUT(::ams::svc::PhysicalAddress, physical_address), INPUT(::ams::svc::Size, size), INPUT(::ams::svc::MemoryMapping, mapping), INPUT(::ams::svc::MemoryPermission, perm)) \
|
||||
\
|
||||
HANDLER(0x3C, void, KernelDebug, INPUT(::ams::svc::KernelDebugType, kern_debug_type), INPUT(uint64_t, arg0), INPUT(uint64_t, arg1), INPUT(uint64_t, arg2)) \
|
||||
HANDLER(0x3D, void, ChangeKernelTraceState, INPUT(::ams::svc::KernelTraceState, kern_trace_state)) \
|
||||
\
|
||||
HANDLER(0x40, Result, CreateSession, OUTPUT(::ams::svc::Handle, out_server_session_handle), OUTPUT(::ams::svc::Handle, out_client_session_handle), INPUT(bool, is_light), INPUT(::ams::svc::Address, name)) \
|
||||
HANDLER(0x41, Result, AcceptSession, OUTPUT(::ams::svc::Handle, out_handle), INPUT(::ams::svc::Handle, port)) \
|
||||
HANDLER(0x42, Result, ReplyAndReceiveLight, INPUT(::ams::svc::Handle, handle)) \
|
||||
HANDLER(0x43, Result, ReplyAndReceive, OUTPUT(int32_t, out_index), INPTR(::ams::svc::Handle, handles), INPUT(int32_t, num_handles), INPUT(::ams::svc::Handle, reply_target), INPUT(int64_t, timeout_ns)) \
|
||||
HANDLER(0x44, Result, ReplyAndReceiveWithUserBuffer, OUTPUT(int32_t, out_index), INPUT(::ams::svc::Address, message_buffer), INPUT(::ams::svc::Size, message_buffer_size), INPTR(::ams::svc::Handle, handles), INPUT(int32_t, num_handles), INPUT(::ams::svc::Handle, reply_target), INPUT(int64_t, timeout_ns)) \
|
||||
HANDLER(0x45, Result, CreateEvent, OUTPUT(::ams::svc::Handle, out_write_handle), OUTPUT(::ams::svc::Handle, out_read_handle)) \
|
||||
HANDLER(0x46, Result, MapIoRegion, INPUT(::ams::svc::Handle, io_region), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size), INPUT(::ams::svc::MemoryPermission, perm)) \
|
||||
HANDLER(0x47, Result, UnmapIoRegion, INPUT(::ams::svc::Handle, io_region), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x48, Result, MapPhysicalMemoryUnsafe, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x49, Result, UnmapPhysicalMemoryUnsafe, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x4A, Result, SetUnsafeLimit, INPUT(::ams::svc::Size, limit)) \
|
||||
HANDLER(0x4B, Result, CreateCodeMemory, OUTPUT(::ams::svc::Handle, out_handle), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x4C, Result, ControlCodeMemory, INPUT(::ams::svc::Handle, code_memory_handle), INPUT(::ams::svc::CodeMemoryOperation, operation), INPUT(uint64_t, address), INPUT(uint64_t, size), INPUT(::ams::svc::MemoryPermission, perm)) \
|
||||
HANDLER(0x4D, void, SleepSystem) \
|
||||
HANDLER(0x4E, Result, ReadWriteRegister, OUTPUT(uint32_t, out_value), INPUT(::ams::svc::PhysicalAddress, address), INPUT(uint32_t, mask), INPUT(uint32_t, value)) \
|
||||
HANDLER(0x4F, Result, SetProcessActivity, INPUT(::ams::svc::Handle, process_handle), INPUT(::ams::svc::ProcessActivity, process_activity)) \
|
||||
HANDLER(0x50, Result, CreateSharedMemory, OUTPUT(::ams::svc::Handle, out_handle), INPUT(::ams::svc::Size, size), INPUT(::ams::svc::MemoryPermission, owner_perm), INPUT(::ams::svc::MemoryPermission, remote_perm)) \
|
||||
HANDLER(0x51, Result, MapTransferMemory, INPUT(::ams::svc::Handle, trmem_handle), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size), INPUT(::ams::svc::MemoryPermission, owner_perm)) \
|
||||
HANDLER(0x52, Result, UnmapTransferMemory, INPUT(::ams::svc::Handle, trmem_handle), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x53, Result, CreateInterruptEvent, OUTPUT(::ams::svc::Handle, out_read_handle), INPUT(int32_t, interrupt_id), INPUT(::ams::svc::InterruptType, interrupt_type)) \
|
||||
HANDLER(0x54, Result, QueryPhysicalAddress, OUTPUT(::ams::svc::NAMESPACE::PhysicalMemoryInfo, out_info), INPUT(::ams::svc::Address, address)) \
|
||||
HANDLER(0x55, Result, QueryMemoryMapping, OUTPUT(::ams::svc::Address, out_address), OUTPUT(::ams::svc::Size, out_size), INPUT(::ams::svc::PhysicalAddress, physical_address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x56, Result, CreateDeviceAddressSpace, OUTPUT(::ams::svc::Handle, out_handle), INPUT(uint64_t, das_address), INPUT(uint64_t, das_size)) \
|
||||
HANDLER(0x57, Result, AttachDeviceAddressSpace, INPUT(::ams::svc::DeviceName, device_name), INPUT(::ams::svc::Handle, das_handle)) \
|
||||
HANDLER(0x58, Result, DetachDeviceAddressSpace, INPUT(::ams::svc::DeviceName, device_name), INPUT(::ams::svc::Handle, das_handle)) \
|
||||
HANDLER(0x59, Result, MapDeviceAddressSpaceByForce, INPUT(::ams::svc::Handle, das_handle), INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, process_address), INPUT(::ams::svc::Size, size), INPUT(uint64_t, device_address), INPUT(uint32_t, option)) \
|
||||
HANDLER(0x5A, Result, MapDeviceAddressSpaceAligned, INPUT(::ams::svc::Handle, das_handle), INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, process_address), INPUT(::ams::svc::Size, size), INPUT(uint64_t, device_address), INPUT(uint32_t, option)) \
|
||||
HANDLER(0x5C, Result, UnmapDeviceAddressSpace, INPUT(::ams::svc::Handle, das_handle), INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, process_address), INPUT(::ams::svc::Size, size), INPUT(uint64_t, device_address)) \
|
||||
HANDLER(0x5D, Result, InvalidateProcessDataCache, INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, address), INPUT(uint64_t, size)) \
|
||||
HANDLER(0x5E, Result, StoreProcessDataCache, INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, address), INPUT(uint64_t, size)) \
|
||||
HANDLER(0x5F, Result, FlushProcessDataCache, INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, address), INPUT(uint64_t, size)) \
|
||||
HANDLER(0x60, Result, DebugActiveProcess, OUTPUT(::ams::svc::Handle, out_handle), INPUT(uint64_t, process_id)) \
|
||||
HANDLER(0x61, Result, BreakDebugProcess, INPUT(::ams::svc::Handle, debug_handle)) \
|
||||
HANDLER(0x62, Result, TerminateDebugProcess, INPUT(::ams::svc::Handle, debug_handle)) \
|
||||
HANDLER(0x63, Result, GetDebugEvent, OUTPTR(::ams::svc::NAMESPACE::DebugEventInfo, out_info), INPUT(::ams::svc::Handle, debug_handle)) \
|
||||
HANDLER(0x64, Result, ContinueDebugEvent, INPUT(::ams::svc::Handle, debug_handle), INPUT(uint32_t, flags), INPTR(uint64_t, thread_ids), INPUT(int32_t, num_thread_ids)) \
|
||||
HANDLER(0x65, Result, GetProcessList, OUTPUT(int32_t, out_num_processes), OUTPTR(uint64_t, out_process_ids), INPUT(int32_t, max_out_count)) \
|
||||
HANDLER(0x66, Result, GetThreadList, OUTPUT(int32_t, out_num_threads), OUTPTR(uint64_t, out_thread_ids), INPUT(int32_t, max_out_count), INPUT(::ams::svc::Handle, debug_handle)) \
|
||||
HANDLER(0x67, Result, GetDebugThreadContext, OUTPTR(::ams::svc::ThreadContext, out_context), INPUT(::ams::svc::Handle, debug_handle), INPUT(uint64_t, thread_id), INPUT(uint32_t, context_flags)) \
|
||||
HANDLER(0x68, Result, SetDebugThreadContext, INPUT(::ams::svc::Handle, debug_handle), INPUT(uint64_t, thread_id), INPTR(::ams::svc::ThreadContext, context), INPUT(uint32_t, context_flags)) \
|
||||
HANDLER(0x69, Result, QueryDebugProcessMemory, OUTPTR(::ams::svc::NAMESPACE::MemoryInfo, out_memory_info), OUTPUT(::ams::svc::PageInfo, out_page_info), INPUT(::ams::svc::Handle, process_handle), INPUT(::ams::svc::Address, address)) \
|
||||
HANDLER(0x6A, Result, ReadDebugProcessMemory, INPUT(::ams::svc::Address, buffer), INPUT(::ams::svc::Handle, debug_handle), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x6B, Result, WriteDebugProcessMemory, INPUT(::ams::svc::Handle, debug_handle), INPUT(::ams::svc::Address, buffer), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x6C, Result, SetHardwareBreakPoint, INPUT(::ams::svc::HardwareBreakPointRegisterName, name), INPUT(uint64_t, flags), INPUT(uint64_t, value)) \
|
||||
HANDLER(0x6D, Result, GetDebugThreadParam, OUTPUT(uint64_t, out_64), OUTPUT(uint32_t, out_32), INPUT(::ams::svc::Handle, debug_handle), INPUT(uint64_t, thread_id), INPUT(::ams::svc::DebugThreadParam, param)) \
|
||||
\
|
||||
HANDLER(0x6F, Result, GetSystemInfo, OUTPUT(uint64_t, out), INPUT(::ams::svc::SystemInfoType, info_type), INPUT(::ams::svc::Handle, handle), INPUT(uint64_t, info_subtype)) \
|
||||
HANDLER(0x70, Result, CreatePort, OUTPUT(::ams::svc::Handle, out_server_handle), OUTPUT(::ams::svc::Handle, out_client_handle), INPUT(int32_t, max_sessions), INPUT(bool, is_light), INPUT(::ams::svc::Address, name)) \
|
||||
HANDLER(0x71, Result, ManageNamedPort, OUTPUT(::ams::svc::Handle, out_server_handle), INPTR(char, name), INPUT(int32_t, max_sessions)) \
|
||||
HANDLER(0x72, Result, ConnectToPort, OUTPUT(::ams::svc::Handle, out_handle), INPUT(::ams::svc::Handle, port)) \
|
||||
HANDLER(0x73, Result, SetProcessMemoryPermission, INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, address), INPUT(uint64_t, size), INPUT(::ams::svc::MemoryPermission, perm)) \
|
||||
HANDLER(0x74, Result, MapProcessMemory, INPUT(::ams::svc::Address, dst_address), INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, src_address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x75, Result, UnmapProcessMemory, INPUT(::ams::svc::Address, dst_address), INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, src_address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x76, Result, QueryProcessMemory, OUTPTR(::ams::svc::NAMESPACE::MemoryInfo, out_memory_info), OUTPUT(::ams::svc::PageInfo, out_page_info), INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, address)) \
|
||||
HANDLER(0x77, Result, MapProcessCodeMemory, INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, dst_address), INPUT(uint64_t, src_address), INPUT(uint64_t, size)) \
|
||||
HANDLER(0x78, Result, UnmapProcessCodeMemory, INPUT(::ams::svc::Handle, process_handle), INPUT(uint64_t, dst_address), INPUT(uint64_t, src_address), INPUT(uint64_t, size)) \
|
||||
HANDLER(0x79, Result, CreateProcess, OUTPUT(::ams::svc::Handle, out_handle), INPTR(::ams::svc::NAMESPACE::CreateProcessParameter, parameters), INPTR(uint32_t, caps), INPUT(int32_t, num_caps)) \
|
||||
HANDLER(0x7A, Result, StartProcess, INPUT(::ams::svc::Handle, process_handle), INPUT(int32_t, priority), INPUT(int32_t, core_id), INPUT(uint64_t, main_thread_stack_size)) \
|
||||
HANDLER(0x7B, Result, TerminateProcess, INPUT(::ams::svc::Handle, process_handle)) \
|
||||
HANDLER(0x7C, Result, GetProcessInfo, OUTPUT(int64_t, out_info), INPUT(::ams::svc::Handle, process_handle), INPUT(::ams::svc::ProcessInfoType, info_type)) \
|
||||
HANDLER(0x7D, Result, CreateResourceLimit, OUTPUT(::ams::svc::Handle, out_handle)) \
|
||||
HANDLER(0x7E, Result, SetResourceLimitLimitValue, INPUT(::ams::svc::Handle, resource_limit_handle), INPUT(::ams::svc::LimitableResource, which), INPUT(int64_t, limit_value)) \
|
||||
HANDLER(0x7F, void, CallSecureMonitor, OUTPUT(::ams::svc::NAMESPACE::SecureMonitorArguments, args)) \
|
||||
\
|
||||
HANDLER(0x90, Result, MapInsecurePhysicalMemory, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x91, Result, UnmapInsecurePhysicalMemory, INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size)) \
|
||||
\
|
||||
HANDLER(0x2E, Result, LegacyGetFutureThreadInfo, OUTPUT(::ams::svc::NAMESPACE::LastThreadContext, out_context), OUTPUT(::ams::svc::Address, out_tls_address), OUTPUT(uint32_t, out_flags), INPUT(int64_t, ns)) \
|
||||
HANDLER(0x55, Result, LegacyQueryIoMapping, OUTPUT(::ams::svc::Address, out_address), INPUT(::ams::svc::PhysicalAddress, physical_address), INPUT(::ams::svc::Size, size)) \
|
||||
HANDLER(0x64, Result, LegacyContinueDebugEvent, INPUT(::ams::svc::Handle, debug_handle), INPUT(uint32_t, flags), INPUT(uint64_t, thread_id))
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_common.hpp>
|
||||
#include <vapours/svc/svc_types.hpp>
|
||||
#include <vapours/svc/svc_definition_macro.hpp>
|
||||
|
||||
#define AMS_SVC_KERN_INPUT_HANDLER(TYPE, NAME) TYPE NAME
|
||||
#define AMS_SVC_KERN_OUTPUT_HANDLER(TYPE, NAME) TYPE *NAME
|
||||
#define AMS_SVC_KERN_INPTR_HANDLER(TYPE, NAME) ::ams::kern::svc::KUserPointer<const TYPE *> NAME
|
||||
#define AMS_SVC_KERN_OUTPTR_HANDLER(TYPE, NAME) ::ams::kern::svc::KUserPointer<TYPE *> NAME
|
||||
|
||||
#define AMS_SVC_USER_INPUT_HANDLER(TYPE, NAME) TYPE NAME
|
||||
#define AMS_SVC_USER_OUTPUT_HANDLER(TYPE, NAME) TYPE *NAME
|
||||
#define AMS_SVC_USER_INPTR_HANDLER(TYPE, NAME) ::ams::svc::UserPointer<const TYPE *> NAME
|
||||
#define AMS_SVC_USER_OUTPTR_HANDLER(TYPE, NAME) ::ams::svc::UserPointer<TYPE *> NAME
|
||||
|
||||
#define AMS_SVC_FOREACH_USER_DEFINITION(HANDLER, NAMESPACE) AMS_SVC_FOREACH_DEFINITION_IMPL(HANDLER, NAMESPACE, AMS_SVC_USER_INPUT_HANDLER, AMS_SVC_USER_OUTPUT_HANDLER, AMS_SVC_USER_INPTR_HANDLER, AMS_SVC_USER_OUTPTR_HANDLER)
|
||||
#define AMS_SVC_FOREACH_KERN_DEFINITION(HANDLER, NAMESPACE) AMS_SVC_FOREACH_DEFINITION_IMPL(HANDLER, NAMESPACE, AMS_SVC_KERN_INPUT_HANDLER, AMS_SVC_KERN_OUTPUT_HANDLER, AMS_SVC_KERN_INPTR_HANDLER, AMS_SVC_KERN_OUTPTR_HANDLER)
|
||||
|
||||
#define AMS_SVC_DECLARE_FUNCTION_PROTOTYPE(ID, RETURN_TYPE, NAME, ...) \
|
||||
RETURN_TYPE NAME(__VA_ARGS__);
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
#define AMS_SVC_DEFINE_ID_ENUM_MEMBER(ID, RETURN_TYPE, NAME, ...) \
|
||||
SvcId_##NAME = ID,
|
||||
|
||||
enum SvcId : u32 {
|
||||
AMS_SVC_FOREACH_KERN_DEFINITION(AMS_SVC_DEFINE_ID_ENUM_MEMBER, _)
|
||||
};
|
||||
|
||||
#undef AMS_SVC_DEFINE_ID_ENUM_MEMBER
|
||||
|
||||
}
|
||||
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
namespace aarch64::lp64 {
|
||||
|
||||
AMS_SVC_FOREACH_USER_DEFINITION(AMS_SVC_DECLARE_FUNCTION_PROTOTYPE, lp64)
|
||||
|
||||
}
|
||||
|
||||
namespace aarch64::ilp32 {
|
||||
|
||||
AMS_SVC_FOREACH_USER_DEFINITION(AMS_SVC_DECLARE_FUNCTION_PROTOTYPE, ilp32)
|
||||
|
||||
}
|
||||
|
||||
namespace aarch32 {
|
||||
|
||||
AMS_SVC_FOREACH_USER_DEFINITION(AMS_SVC_DECLARE_FUNCTION_PROTOTYPE, ilp32)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* NOTE: Change this to 1 to test the SVC definitions for user-pointer validity. */
|
||||
#if 0
|
||||
namespace ams::svc::test {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename... Ts>
|
||||
struct Validator {
|
||||
private:
|
||||
std::array<bool, sizeof...(Ts)> m_valid;
|
||||
public:
|
||||
constexpr Validator(Ts... args) : m_valid{static_cast<bool>(args)...} { /* ... */ }
|
||||
|
||||
constexpr bool IsValid() const {
|
||||
for (size_t i = 0; i < sizeof...(Ts); i++) {
|
||||
if (!m_valid[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#define AMS_SVC_TEST_EMPTY_HANDLER(TYPE, NAME) true
|
||||
#define AMS_SVC_TEST_INPTR_HANDLER(TYPE, NAME) (sizeof(::ams::svc::UserPointer<const TYPE *>) == sizeof(uintptr_t) && std::is_trivially_destructible<::ams::svc::UserPointer<const TYPE *>>::value)
|
||||
#define AMS_SVC_TEST_OUTPTR_HANDLER(TYPE, NAME) (sizeof(::ams::svc::UserPointer<TYPE *>) == sizeof(uintptr_t) && std::is_trivially_destructible<::ams::svc::UserPointer<TYPE *>>::value)
|
||||
|
||||
#define AMS_SVC_TEST_VERIFY_USER_POINTERS(ID, RETURN_TYPE, NAME, ...) \
|
||||
static_assert(impl::Validator(__VA_ARGS__).IsValid(), "Invalid User Pointer in svc::" #NAME);
|
||||
|
||||
AMS_SVC_FOREACH_DEFINITION_IMPL(AMS_SVC_TEST_VERIFY_USER_POINTERS, lp64, AMS_SVC_TEST_EMPTY_HANDLER, AMS_SVC_TEST_EMPTY_HANDLER, AMS_SVC_TEST_INPTR_HANDLER, AMS_SVC_TEST_OUTPTR_HANDLER);
|
||||
AMS_SVC_FOREACH_DEFINITION_IMPL(AMS_SVC_TEST_VERIFY_USER_POINTERS, ilp32, AMS_SVC_TEST_EMPTY_HANDLER, AMS_SVC_TEST_EMPTY_HANDLER, AMS_SVC_TEST_INPTR_HANDLER, AMS_SVC_TEST_OUTPTR_HANDLER);
|
||||
|
||||
#undef AMS_SVC_TEST_VERIFY_USER_POINTERS
|
||||
#undef AMS_SVC_TEST_INPTR_HANDLER
|
||||
#undef AMS_SVC_TEST_OUTPTR_HANDLER
|
||||
#undef AMS_SVC_TEST_EMPTY_HANDLER
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ATMOSPHERE_IS_STRATOSPHERE */
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
|
||||
constexpr inline size_t AddressMemoryRegionSmall32Size = 1_GB;
|
||||
constexpr inline size_t AddressMemoryRegionLarge32Size = 4_GB - AddressMemoryRegionSmall32Size;
|
||||
constexpr inline size_t AddressMemoryRegionHeap32Size = 1_GB;
|
||||
constexpr inline size_t AddressMemoryRegionAlias32Size = 1_GB;
|
||||
|
||||
constexpr inline size_t AddressMemoryRegionSmall36Size = 2_GB;
|
||||
constexpr inline size_t AddressMemoryRegionLarge36Size = 64_GB - AddressMemoryRegionSmall36Size;
|
||||
constexpr inline size_t AddressMemoryRegionHeap36Size = 8_GB;
|
||||
constexpr inline size_t AddressMemoryRegionAlias36Size = 6_GB;
|
||||
|
||||
constexpr inline size_t AddressMemoryRegionSmall39Size = 64_GB;
|
||||
constexpr inline size_t AddressMemoryRegionHeap39Size = 8_GB;
|
||||
constexpr inline size_t AddressMemoryRegionAlias39Size = 64_GB;
|
||||
constexpr inline size_t AddressMemoryRegionStack39Size = 2_GB;
|
||||
|
||||
constexpr inline size_t AddressMemoryRegion39Size = 512_GB;
|
||||
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM)
|
||||
|
||||
constexpr inline size_t AddressMemoryRegionSmall32Size = 512_MB;
|
||||
constexpr inline size_t AddressMemoryRegionLarge32Size = 2_GB - AddressMemoryRegionSmall32Size;
|
||||
constexpr inline size_t AddressMemoryRegionHeap32Size = 1_GB;
|
||||
constexpr inline size_t AddressMemoryRegionAlias32Size = 512_MB;
|
||||
|
||||
constexpr inline size_t AddressMemoryRegionSmall36Size = 0;
|
||||
constexpr inline size_t AddressMemoryRegionLarge36Size = 0;
|
||||
constexpr inline size_t AddressMemoryRegionHeap36Size = 0;
|
||||
constexpr inline size_t AddressMemoryRegionAlias36Size = 0;
|
||||
|
||||
constexpr inline size_t AddressMemoryRegionSmall39Size = 0;
|
||||
constexpr inline size_t AddressMemoryRegionHeap39Size = 0;
|
||||
constexpr inline size_t AddressMemoryRegionAlias39Size = 0;
|
||||
constexpr inline size_t AddressMemoryRegionStack39Size = 0;
|
||||
|
||||
constexpr inline size_t AddressMemoryRegion39Size = 0;
|
||||
|
||||
#else
|
||||
|
||||
#error "Unknown architecture for svc::AddressMemoryRegion*Size"
|
||||
|
||||
#endif
|
||||
|
||||
constexpr inline size_t AddressNullGuard32Size = 2_MB;
|
||||
constexpr inline size_t AddressNullGuard64Size = 128_MB;
|
||||
|
||||
constexpr inline uintptr_t AddressMemoryRegionSmall32Start = 0;
|
||||
constexpr inline uintptr_t AddressMemoryRegionSmall32End = AddressMemoryRegionSmall32Start + AddressMemoryRegionSmall32Size;
|
||||
|
||||
constexpr inline uintptr_t AddressMemoryRegionLarge32Start = AddressMemoryRegionSmall32End;
|
||||
constexpr inline uintptr_t AddressMemoryRegionLarge32End = AddressMemoryRegionLarge32Start + AddressMemoryRegionLarge32Size;
|
||||
|
||||
constexpr inline uintptr_t AddressSmallMap32Start = AddressMemoryRegionSmall32Start + AddressNullGuard32Size;
|
||||
constexpr inline uintptr_t AddressSmallMap32End = AddressMemoryRegionSmall32End;
|
||||
constexpr inline size_t AddressSmallMap32Size = AddressSmallMap32End - AddressSmallMap32Start;
|
||||
|
||||
constexpr inline uintptr_t AddressLargeMap32Start = AddressMemoryRegionLarge32Start;
|
||||
constexpr inline uintptr_t AddressLargeMap32End = AddressMemoryRegionLarge32End;
|
||||
constexpr inline size_t AddressLargeMap32Size = AddressLargeMap32End - AddressLargeMap32Start;
|
||||
|
||||
constexpr inline uintptr_t AddressMemoryRegionSmall36Start = 0;
|
||||
constexpr inline uintptr_t AddressMemoryRegionSmall36End = AddressMemoryRegionSmall36Start + AddressMemoryRegionSmall36Size;
|
||||
|
||||
constexpr inline uintptr_t AddressMemoryRegionLarge36Start = AddressMemoryRegionSmall36End;
|
||||
constexpr inline uintptr_t AddressMemoryRegionLarge36End = AddressMemoryRegionLarge36Start + AddressMemoryRegionLarge36Size;
|
||||
|
||||
constexpr inline uintptr_t AddressSmallMap36Start = AddressMemoryRegionSmall36Start + AddressNullGuard64Size;
|
||||
constexpr inline uintptr_t AddressSmallMap36End = AddressMemoryRegionSmall36End;
|
||||
constexpr inline size_t AddressSmallMap36Size = AddressSmallMap36End - AddressSmallMap36Start;
|
||||
|
||||
constexpr inline uintptr_t AddressLargeMap36Start = AddressMemoryRegionLarge36Start;
|
||||
constexpr inline uintptr_t AddressLargeMap36End = AddressMemoryRegionLarge36End;
|
||||
constexpr inline size_t AddressLargeMap36Size = AddressLargeMap36End - AddressLargeMap36Start;
|
||||
|
||||
constexpr inline uintptr_t AddressMap39Start = 0 + AddressNullGuard64Size;
|
||||
constexpr inline uintptr_t AddressMap39End = AddressMemoryRegion39Size;
|
||||
constexpr inline size_t AddressMap39Size = AddressMap39End - AddressMap39Start;
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_common.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
|
||||
#include <vapours/svc/board/nintendo/nx/svc_device_name.hpp>
|
||||
namespace ams::svc {
|
||||
using namespace ams::svc::board::nintendo::nx;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <vapours/svc/board/generic/svc_device_name.hpp>
|
||||
namespace ams::svc {
|
||||
using namespace ams::svc::board::generic;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_common.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
|
||||
#include <vapours/svc/board/nintendo/nx/svc_hardware_constants.hpp>
|
||||
namespace ams::svc {
|
||||
using namespace ams::svc::board::nintendo::nx;
|
||||
}
|
||||
|
||||
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
|
||||
|
||||
#include <vapours/svc/board/qemu/virt/svc_hardware_constants.hpp>
|
||||
namespace ams::svc {
|
||||
using namespace ams::svc::board::qemu::virt;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error "Unknown board for svc Hardware Constants"
|
||||
|
||||
#endif
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_common.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
|
||||
#include <vapours/svc/board/nintendo/nx/svc_io_pool_type.hpp>
|
||||
namespace ams::svc {
|
||||
|
||||
using IoPoolType = ::ams::svc::board::nintendo::nx::IoPoolType;
|
||||
using enum ::ams::svc::board::nintendo::nx::IoPoolType;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SVC_IO_POOL_NOT_SUPPORTED
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
enum IoPoolType : u32 {
|
||||
/* Not supported. */
|
||||
IoPoolType_Count = 0,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_common.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
|
||||
#include <vapours/svc/arch/arm64/svc_thread_local_region.hpp>
|
||||
namespace ams::svc {
|
||||
using ams::svc::arch::arm64::ThreadLocalRegion;
|
||||
using ams::svc::arch::arm64::GetThreadLocalRegion;
|
||||
}
|
||||
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM)
|
||||
|
||||
#include <vapours/svc/arch/arm/svc_thread_local_region.hpp>
|
||||
namespace ams::svc {
|
||||
using ams::svc::arch::arm::ThreadLocalRegion;
|
||||
using ams::svc::arch::arm::GetThreadLocalRegion;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error "Unknown architecture for svc::ThreadLocalRegion"
|
||||
|
||||
#endif
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
constexpr inline size_t ThreadLocalRegionSize = 0x200;
|
||||
static_assert(sizeof(::ams::svc::ThreadLocalRegion) == ThreadLocalRegionSize);
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_common.hpp>
|
||||
#include <vapours/svc/svc_select_hardware_constants.hpp>
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
class Tick {
|
||||
public:
|
||||
static constexpr s64 TicksPerSecond = ::ams::svc::TicksPerSecond;
|
||||
static consteval s64 GetTicksPerSecond() { return TicksPerSecond; }
|
||||
private:
|
||||
s64 m_tick;
|
||||
private:
|
||||
static constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds();
|
||||
|
||||
static constexpr ALWAYS_INLINE s64 ConvertTimeSpanToTickImpl(TimeSpan ts) {
|
||||
/* Get nano-seconds. */
|
||||
const s64 ns = ts.GetNanoSeconds();
|
||||
|
||||
/* Special-case optimize arm64/nintendo-nx value. */
|
||||
if (!std::is_constant_evaluated()) {
|
||||
if constexpr (TicksPerSecond == 19'200'000) {
|
||||
#if defined(ATMOSPHERE_IS_MESOSPHERE) && defined(ATMOSPHERE_ARCH_ARM64)
|
||||
s64 t0, t1, t2, t3;
|
||||
__asm__ __volatile__("mov %[t1], #0x5A53\n"
|
||||
"movk %[t1], #0xA09B, lsl #16\n"
|
||||
"lsr %[t0], %[ns], #9\n"
|
||||
"movk %[t1], #0xB82F, lsl #32\n"
|
||||
"movk %[t1], #0x0044, lsl #48\n"
|
||||
"umulh %[t0], %[t0], %[t1]\n"
|
||||
"mov %[t1], #0xFFFFFFFFFFFF3600\n"
|
||||
"movk %[t1], #0xC465, lsl #16\n"
|
||||
"lsr %[t0], %[t0], #0xB\n"
|
||||
"madd %[t1], %[t0], %[t1], %[ns]\n"
|
||||
"mov %w[t2], #0xF800\n"
|
||||
"movk %w[t2], #0x0124, lsl #16\n"
|
||||
"mov %w[t3], #0xCA00\n"
|
||||
"movk %w[t3], #0x3B9A, lsl #16\n"
|
||||
"madd %[t1], %[t1], %[t2], %[t3]\n"
|
||||
"mov %[t3], #0x94B3\n"
|
||||
"movk %[t3], #0x26D6, lsl #16\n"
|
||||
"movk %[t3], #0x0BE8, lsl #32\n"
|
||||
"movk %[t3], #0x112E, lsl #48\n"
|
||||
"sub %[t1], %[t1], #1\n"
|
||||
"smulh %[t1], %[t1], %[t3]\n"
|
||||
"asr %[t3], %[t1], #26\n"
|
||||
"add %[t1], %[t3], %[t1], lsr #63\n"
|
||||
"madd %[t0], %[t0], %[t2], %[t1]\n"
|
||||
: [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3)
|
||||
: [ns]"r"(ns)
|
||||
: "cc");
|
||||
return t0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return util::ScaleByConstantFactorUp<s64, TicksPerSecond, NanoSecondsPerSecond>(ns);
|
||||
}
|
||||
public:
|
||||
constexpr ALWAYS_INLINE explicit Tick(s64 t = 0) : m_tick(t) { /* ... */ }
|
||||
constexpr ALWAYS_INLINE Tick(TimeSpan ts) : m_tick(ConvertTimeSpanToTickImpl(ts)) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE operator s64() const { return m_tick; }
|
||||
|
||||
/* Tick arithmetic. */
|
||||
constexpr ALWAYS_INLINE Tick &operator+=(Tick rhs) { m_tick += rhs.m_tick; return *this; }
|
||||
constexpr ALWAYS_INLINE Tick &operator-=(Tick rhs) { m_tick -= rhs.m_tick; return *this; }
|
||||
constexpr ALWAYS_INLINE Tick operator+(Tick rhs) const { Tick r(*this); return r += rhs; }
|
||||
constexpr ALWAYS_INLINE Tick operator-(Tick rhs) const { Tick r(*this); return r -= rhs; }
|
||||
|
||||
constexpr ALWAYS_INLINE Tick &operator+=(TimeSpan rhs) { m_tick += Tick(rhs).m_tick; return *this; }
|
||||
constexpr ALWAYS_INLINE Tick &operator-=(TimeSpan rhs) { m_tick -= Tick(rhs).m_tick; return *this; }
|
||||
constexpr ALWAYS_INLINE Tick operator+(TimeSpan rhs) const { Tick r(*this); return r += rhs; }
|
||||
constexpr ALWAYS_INLINE Tick operator-(TimeSpan rhs) const { Tick r(*this); return r -= rhs; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vapours/svc/svc_common.hpp>
|
||||
#include <vapours/svc/svc_tick.hpp>
|
||||
#include <vapours/svc/svc_select_thread_local_region.hpp>
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
#include <vapours/svc/svc_types_base.hpp>
|
||||
#include <vapours/svc/svc_types_dd.hpp>
|
||||
#include <vapours/svc/svc_types_dmnt.hpp>
|
||||
#include <vapours/svc/svc_types_priv.hpp>
|
||||
#include <vapours/svc/svc_select_io_pool_type.hpp>
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
namespace lp64 {
|
||||
|
||||
struct MemoryInfo {
|
||||
u64 base_address;
|
||||
u64 size;
|
||||
MemoryState state;
|
||||
MemoryAttribute attribute;
|
||||
MemoryPermission permission;
|
||||
u32 ipc_count;
|
||||
u32 device_count;
|
||||
u32 padding;
|
||||
};
|
||||
|
||||
struct LastThreadContext {
|
||||
u64 fp;
|
||||
u64 sp;
|
||||
u64 lr;
|
||||
u64 pc;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace ilp32 {
|
||||
|
||||
struct MemoryInfo {
|
||||
u64 base_address;
|
||||
u64 size;
|
||||
MemoryState state;
|
||||
MemoryAttribute attribute;
|
||||
MemoryPermission permission;
|
||||
u32 ipc_count;
|
||||
u32 device_count;
|
||||
u32 padding;
|
||||
};
|
||||
|
||||
struct LastThreadContext {
|
||||
u32 fp;
|
||||
u32 sp;
|
||||
u32 lr;
|
||||
u32 pc;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,671 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_common.hpp>
|
||||
|
||||
namespace ams::kern::svc::impl {
|
||||
|
||||
struct KUserPointerTag{};
|
||||
|
||||
}
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
/* Utility classes required to encode information into the type system for SVC veneers. */
|
||||
class Size {
|
||||
private:
|
||||
size_t m_size;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE Size(size_t s) : m_size(s) { /* ... */ }
|
||||
constexpr ALWAYS_INLINE operator size_t() { return m_size; }
|
||||
};
|
||||
static_assert(sizeof(Size) == sizeof(size_t));
|
||||
static_assert(std::is_trivially_destructible<Size>::value);
|
||||
|
||||
class Address {
|
||||
private:
|
||||
uintptr_t m_uintptr;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE Address(uintptr_t u) : m_uintptr(u) { /* ... */ }
|
||||
constexpr ALWAYS_INLINE operator uintptr_t() { return m_uintptr; }
|
||||
};
|
||||
static_assert(sizeof(Address) == sizeof(uintptr_t));
|
||||
static_assert(std::is_trivially_destructible<Address>::value);
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct UserPointerTag{};
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct UserPointer : impl::UserPointerTag {
|
||||
public:
|
||||
static_assert(std::is_pointer<T>::value);
|
||||
static constexpr bool IsInput = std::is_const<typename std::remove_pointer<T>::type>::value;
|
||||
private:
|
||||
T m_pointer;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE UserPointer(T p) : m_pointer(p) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE T GetPointerUnsafe() { return m_pointer; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static constexpr inline bool IsUserPointer = std::is_base_of<impl::UserPointerTag, T>::value;
|
||||
|
||||
using ProgramId = u64;
|
||||
using PhysicalAddress = u64;
|
||||
|
||||
/* Memory types. */
|
||||
enum MemoryState : u32 {
|
||||
MemoryState_Free = 0x00,
|
||||
MemoryState_Io = 0x01,
|
||||
MemoryState_Static = 0x02,
|
||||
MemoryState_Code = 0x03,
|
||||
MemoryState_CodeData = 0x04,
|
||||
MemoryState_Normal = 0x05,
|
||||
MemoryState_Shared = 0x06,
|
||||
MemoryState_Alias = 0x07,
|
||||
MemoryState_AliasCode = 0x08,
|
||||
MemoryState_AliasCodeData = 0x09,
|
||||
MemoryState_Ipc = 0x0A,
|
||||
MemoryState_Stack = 0x0B,
|
||||
MemoryState_ThreadLocal = 0x0C,
|
||||
MemoryState_Transfered = 0x0D,
|
||||
MemoryState_SharedTransfered = 0x0E,
|
||||
MemoryState_SharedCode = 0x0F,
|
||||
MemoryState_Inaccessible = 0x10,
|
||||
MemoryState_NonSecureIpc = 0x11,
|
||||
MemoryState_NonDeviceIpc = 0x12,
|
||||
MemoryState_Kernel = 0x13,
|
||||
MemoryState_GeneratedCode = 0x14,
|
||||
MemoryState_CodeOut = 0x15,
|
||||
MemoryState_Coverage = 0x16,
|
||||
MemoryState_Insecure = 0x17,
|
||||
};
|
||||
|
||||
enum MemoryPermission : u32 {
|
||||
MemoryPermission_None = (0 << 0),
|
||||
|
||||
MemoryPermission_Read = (1 << 0),
|
||||
MemoryPermission_Write = (1 << 1),
|
||||
MemoryPermission_Execute = (1 << 2),
|
||||
|
||||
MemoryPermission_ReadWrite = MemoryPermission_Read | MemoryPermission_Write,
|
||||
MemoryPermission_ReadExecute = MemoryPermission_Read | MemoryPermission_Execute,
|
||||
|
||||
MemoryPermission_DontCare = (1 << 28), /* For SharedMemory */
|
||||
};
|
||||
|
||||
enum MemoryAttribute : u32 {
|
||||
MemoryAttribute_Locked = (1 << 0),
|
||||
MemoryAttribute_IpcLocked = (1 << 1),
|
||||
MemoryAttribute_DeviceShared = (1 << 2),
|
||||
MemoryAttribute_Uncached = (1 << 3),
|
||||
MemoryAttribute_PermissionLocked = (1 << 4),
|
||||
};
|
||||
|
||||
enum MemoryMapping : u32 {
|
||||
MemoryMapping_IoRegister = 0,
|
||||
MemoryMapping_Uncached = 1,
|
||||
MemoryMapping_Memory = 2,
|
||||
};
|
||||
|
||||
constexpr inline size_t HeapSizeAlignment = 2_MB;
|
||||
|
||||
struct PageInfo {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
enum MemoryRegionType {
|
||||
MemoryRegionType_None = 0,
|
||||
MemoryRegionType_KernelTraceBuffer = 1,
|
||||
MemoryRegionType_OnMemoryBootImage = 2,
|
||||
MemoryRegionType_DTB = 3,
|
||||
MemoryRegionType_Count,
|
||||
};
|
||||
|
||||
enum MapDeviceAddressSpaceFlag : u32 {
|
||||
MapDeviceAddressSpaceFlag_None = (0 << 0),
|
||||
MapDeviceAddressSpaceFlag_NotIoRegister = (1 << 0),
|
||||
};
|
||||
|
||||
struct MapDeviceAddressSpaceOption {
|
||||
using Permission = util::BitPack32::Field<0, 16, MemoryPermission>;
|
||||
using Flags = util::BitPack32::Field<Permission::Next, 1, MapDeviceAddressSpaceFlag>;
|
||||
using Reserved = util::BitPack32::Field<Flags::Next, 15, u32>;
|
||||
|
||||
static constexpr ALWAYS_INLINE u32 Encode(MemoryPermission perm, u32 flags) {
|
||||
util::BitPack32 pack{};
|
||||
|
||||
pack.Set<Permission>(perm);
|
||||
pack.Set<Flags>(static_cast<svc::MapDeviceAddressSpaceFlag>(flags));
|
||||
pack.Set<Reserved>(0);
|
||||
|
||||
return pack.value;
|
||||
}
|
||||
};
|
||||
|
||||
/* Info Types. */
|
||||
enum InfoType : u32 {
|
||||
InfoType_CoreMask = 0,
|
||||
InfoType_PriorityMask = 1,
|
||||
InfoType_AliasRegionAddress = 2,
|
||||
InfoType_AliasRegionSize = 3,
|
||||
InfoType_HeapRegionAddress = 4,
|
||||
InfoType_HeapRegionSize = 5,
|
||||
InfoType_TotalMemorySize = 6,
|
||||
InfoType_UsedMemorySize = 7,
|
||||
InfoType_DebuggerAttached = 8,
|
||||
InfoType_ResourceLimit = 9,
|
||||
InfoType_IdleTickCount = 10,
|
||||
InfoType_RandomEntropy = 11,
|
||||
InfoType_AslrRegionAddress = 12,
|
||||
InfoType_AslrRegionSize = 13,
|
||||
InfoType_StackRegionAddress = 14,
|
||||
InfoType_StackRegionSize = 15,
|
||||
InfoType_SystemResourceSizeTotal = 16,
|
||||
InfoType_SystemResourceSizeUsed = 17,
|
||||
InfoType_ProgramId = 18,
|
||||
InfoType_InitialProcessIdRange = 19,
|
||||
InfoType_UserExceptionContextAddress = 20,
|
||||
InfoType_TotalNonSystemMemorySize = 21,
|
||||
InfoType_UsedNonSystemMemorySize = 22,
|
||||
InfoType_IsApplication = 23,
|
||||
InfoType_FreeThreadCount = 24,
|
||||
InfoType_ThreadTickCount = 25,
|
||||
InfoType_IsSvcPermitted = 26,
|
||||
InfoType_IoRegionHint = 27,
|
||||
InfoType_AliasRegionExtraSize = 28,
|
||||
/* ... */
|
||||
InfoType_TransferMemoryHint = 34,
|
||||
|
||||
InfoType_MesosphereMeta = 65000,
|
||||
InfoType_MesosphereCurrentProcess = 65001,
|
||||
};
|
||||
|
||||
enum TickCountInfo : u64 {
|
||||
TickCountInfo_Core0 = 0,
|
||||
TickCountInfo_Core1 = 1,
|
||||
TickCountInfo_Core2 = 2,
|
||||
TickCountInfo_Core3 = 3,
|
||||
|
||||
TickCountInfo_Total = std::numeric_limits<s64>::max(),
|
||||
};
|
||||
|
||||
enum MesosphereMetaInfo : u64 {
|
||||
MesosphereMetaInfo_KernelVersion = 0,
|
||||
MesosphereMetaInfo_IsKTraceEnabled = 1,
|
||||
MesosphereMetaInfo_IsSingleStepEnabled = 2,
|
||||
};
|
||||
|
||||
enum SystemInfoType : u32 {
|
||||
SystemInfoType_TotalPhysicalMemorySize = 0,
|
||||
SystemInfoType_UsedPhysicalMemorySize = 1,
|
||||
SystemInfoType_InitialProcessIdRange = 2,
|
||||
};
|
||||
|
||||
enum InitialProcessIdRangeInfo : u64 {
|
||||
InitialProcessIdRangeInfo_Minimum = 0,
|
||||
InitialProcessIdRangeInfo_Maximum = 1,
|
||||
};
|
||||
|
||||
enum PhysicalMemorySystemInfo : u64 {
|
||||
PhysicalMemorySystemInfo_Application = 0,
|
||||
PhysicalMemorySystemInfo_Applet = 1,
|
||||
PhysicalMemorySystemInfo_System = 2,
|
||||
PhysicalMemorySystemInfo_SystemUnsafe = 3,
|
||||
};
|
||||
|
||||
enum LastThreadInfoFlag : u32 {
|
||||
LastThreadInfoFlag_ThreadInSystemCall = (1u << 0),
|
||||
};
|
||||
|
||||
enum LimitableResource : u32 {
|
||||
LimitableResource_PhysicalMemoryMax = 0,
|
||||
LimitableResource_ThreadCountMax = 1,
|
||||
LimitableResource_EventCountMax = 2,
|
||||
LimitableResource_TransferMemoryCountMax = 3,
|
||||
LimitableResource_SessionCountMax = 4,
|
||||
|
||||
LimitableResource_Count,
|
||||
};
|
||||
|
||||
enum CodeMemoryOperation : u32 {
|
||||
CodeMemoryOperation_Map = 0,
|
||||
CodeMemoryOperation_MapToOwner = 1,
|
||||
CodeMemoryOperation_Unmap = 2,
|
||||
CodeMemoryOperation_UnmapFromOwner = 3,
|
||||
};
|
||||
|
||||
/* Synchronization types. */
|
||||
enum SignalType : u32 {
|
||||
SignalType_Signal = 0,
|
||||
SignalType_SignalAndIncrementIfEqual = 1,
|
||||
SignalType_SignalAndModifyByWaitingCountIfEqual = 2,
|
||||
};
|
||||
|
||||
enum ArbitrationType : u32 {
|
||||
ArbitrationType_WaitIfLessThan = 0,
|
||||
ArbitrationType_DecrementAndWaitIfLessThan = 1,
|
||||
ArbitrationType_WaitIfEqual = 2,
|
||||
ArbitrationType_WaitIfEqual64 = 3,
|
||||
};
|
||||
|
||||
enum YieldType : s64 {
|
||||
YieldType_WithoutCoreMigration = 0,
|
||||
YieldType_WithCoreMigration = -1,
|
||||
YieldType_ToAnyThread = -2,
|
||||
};
|
||||
|
||||
enum InterruptType : u32 {
|
||||
InterruptType_Edge = 0,
|
||||
InterruptType_Level = 1,
|
||||
};
|
||||
|
||||
/* Thread types. */
|
||||
using ThreadFunc = ams::svc::Address;
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM_V8A)
|
||||
|
||||
struct ThreadContext {
|
||||
u64 r[29];
|
||||
u64 fp;
|
||||
u64 lr;
|
||||
u64 sp;
|
||||
u64 pc;
|
||||
u32 pstate;
|
||||
u32 padding;
|
||||
u128 v[32];
|
||||
u32 fpcr;
|
||||
u32 fpsr;
|
||||
u64 tpidr;
|
||||
};
|
||||
static_assert(sizeof(ThreadContext) == 0x320);
|
||||
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM_V7A)
|
||||
|
||||
struct ThreadContext {
|
||||
u32 r[13];
|
||||
u32 sp;
|
||||
u32 lr;
|
||||
u32 pc;
|
||||
u32 cpsr;
|
||||
u32 padding;
|
||||
u64 fpu_registers[32];
|
||||
u32 fpscr;
|
||||
u32 fpexc;
|
||||
u32 tpidr;
|
||||
};
|
||||
static_assert(sizeof(ThreadContext) == 0x158);
|
||||
|
||||
#else
|
||||
|
||||
#if !defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#error "Unknown Architecture for ams::svc::ThreadContext"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
enum ThreadSuspend : u32 {
|
||||
ThreadSuspend_Debug = (1 << 0),
|
||||
ThreadSuspend_User = (1 << 1),
|
||||
};
|
||||
|
||||
enum ThreadState : u32 {
|
||||
ThreadState_Waiting = 0,
|
||||
ThreadState_Running = 1,
|
||||
ThreadState_Terminated = 4,
|
||||
ThreadState_Initializing = 5,
|
||||
};
|
||||
|
||||
enum ThreadContextFlag : u32 {
|
||||
ThreadContextFlag_General = (1 << 0),
|
||||
ThreadContextFlag_Control = (1 << 1),
|
||||
ThreadContextFlag_Fpu = (1 << 2),
|
||||
ThreadContextFlag_FpuControl = (1 << 3),
|
||||
|
||||
ThreadContextFlag_All = (ThreadContextFlag_General | ThreadContextFlag_Control | ThreadContextFlag_Fpu | ThreadContextFlag_FpuControl),
|
||||
|
||||
ThreadContextFlag_SetSingleStep = (1u << 30),
|
||||
ThreadContextFlag_ClearSingleStep = (1u << 31),
|
||||
};
|
||||
|
||||
enum ContinueFlag : u32 {
|
||||
ContinueFlag_ExceptionHandled = (1u << 0),
|
||||
ContinueFlag_EnableExceptionEvent = (1u << 1),
|
||||
ContinueFlag_ContinueAll = (1u << 2),
|
||||
ContinueFlag_ContinueOthers = (1u << 3),
|
||||
|
||||
ContinueFlag_AllMask = (1u << 4) - 1,
|
||||
};
|
||||
|
||||
enum ThreadExitReason : u32 {
|
||||
ThreadExitReason_ExitThread = 0,
|
||||
ThreadExitReason_TerminateThread = 1,
|
||||
ThreadExitReason_ExitProcess = 2,
|
||||
ThreadExitReason_TerminateProcess = 3,
|
||||
};
|
||||
|
||||
enum ThreadActivity : u32 {
|
||||
ThreadActivity_Runnable = 0,
|
||||
ThreadActivity_Paused = 1,
|
||||
};
|
||||
|
||||
constexpr inline s32 IdealCoreDontCare = -1;
|
||||
constexpr inline s32 IdealCoreUseProcessValue = -2;
|
||||
constexpr inline s32 IdealCoreNoUpdate = -3;
|
||||
|
||||
constexpr inline s32 LowestThreadPriority = 63;
|
||||
constexpr inline s32 HighestThreadPriority = 0;
|
||||
|
||||
constexpr inline s32 SystemThreadPriorityHighest = 16;
|
||||
|
||||
/* Process types. */
|
||||
enum ProcessInfoType : u32 {
|
||||
ProcessInfoType_ProcessState = 0,
|
||||
};
|
||||
|
||||
enum ProcessState : u32 {
|
||||
ProcessState_Created = 0,
|
||||
ProcessState_CreatedAttached = 1,
|
||||
ProcessState_Running = 2,
|
||||
ProcessState_Crashed = 3,
|
||||
ProcessState_RunningAttached = 4,
|
||||
ProcessState_Terminating = 5,
|
||||
ProcessState_Terminated = 6,
|
||||
ProcessState_DebugBreak = 7,
|
||||
};
|
||||
|
||||
enum ProcessExitReason : u32 {
|
||||
ProcessExitReason_ExitProcess = 0,
|
||||
ProcessExitReason_TerminateProcess = 1,
|
||||
ProcessExitReason_Exception = 2,
|
||||
};
|
||||
|
||||
enum ProcessActivity : u32 {
|
||||
ProcessActivity_Runnable = 0,
|
||||
ProcessActivity_Paused = 1,
|
||||
};
|
||||
|
||||
enum CreateProcessFlag : u32 {
|
||||
/* Is 64 bit? */
|
||||
CreateProcessFlag_Is64Bit = (1 << 0),
|
||||
|
||||
/* What kind of address space? */
|
||||
CreateProcessFlag_AddressSpaceShift = 1,
|
||||
CreateProcessFlag_AddressSpaceMask = (7 << CreateProcessFlag_AddressSpaceShift),
|
||||
CreateProcessFlag_AddressSpace32Bit = (0 << CreateProcessFlag_AddressSpaceShift),
|
||||
CreateProcessFlag_AddressSpace64BitDeprecated = (1 << CreateProcessFlag_AddressSpaceShift),
|
||||
CreateProcessFlag_AddressSpace32BitWithoutAlias = (2 << CreateProcessFlag_AddressSpaceShift),
|
||||
CreateProcessFlag_AddressSpace64Bit = (3 << CreateProcessFlag_AddressSpaceShift),
|
||||
|
||||
/* Should JIT debug be done on crash? */
|
||||
CreateProcessFlag_EnableDebug = (1 << 4),
|
||||
|
||||
/* Should ASLR be enabled for the process? */
|
||||
CreateProcessFlag_EnableAslr = (1 << 5),
|
||||
|
||||
/* Is the process an application? */
|
||||
CreateProcessFlag_IsApplication = (1 << 6),
|
||||
|
||||
/* 4.x deprecated: Should use secure memory? */
|
||||
CreateProcessFlag_DeprecatedUseSecureMemory = (1 << 7),
|
||||
|
||||
/* 5.x+ Pool partition type. */
|
||||
CreateProcessFlag_PoolPartitionShift = 7,
|
||||
CreateProcessFlag_PoolPartitionMask = (0xF << CreateProcessFlag_PoolPartitionShift),
|
||||
CreateProcessFlag_PoolPartitionApplication = (0 << CreateProcessFlag_PoolPartitionShift),
|
||||
CreateProcessFlag_PoolPartitionApplet = (1 << CreateProcessFlag_PoolPartitionShift),
|
||||
CreateProcessFlag_PoolPartitionSystem = (2 << CreateProcessFlag_PoolPartitionShift),
|
||||
CreateProcessFlag_PoolPartitionSystemNonSecure = (3 << CreateProcessFlag_PoolPartitionShift),
|
||||
|
||||
/* 7.x+ Should memory allocation be optimized? This requires IsApplication. */
|
||||
CreateProcessFlag_OptimizeMemoryAllocation = (1 << 11),
|
||||
|
||||
/* 11.x+ DisableDeviceAddressSpaceMerge. */
|
||||
CreateProcessFlag_DisableDeviceAddressSpaceMerge = (1 << 12),
|
||||
|
||||
/* 18.x EnableAliasRegionExtraSize. */
|
||||
CreateProcessFlag_EnableAliasRegionExtraSize = (1 << 13),
|
||||
|
||||
/* Mask of all flags. */
|
||||
CreateProcessFlag_All = CreateProcessFlag_Is64Bit |
|
||||
CreateProcessFlag_AddressSpaceMask |
|
||||
CreateProcessFlag_EnableDebug |
|
||||
CreateProcessFlag_EnableAslr |
|
||||
CreateProcessFlag_IsApplication |
|
||||
CreateProcessFlag_PoolPartitionMask |
|
||||
CreateProcessFlag_OptimizeMemoryAllocation |
|
||||
CreateProcessFlag_DisableDeviceAddressSpaceMerge |
|
||||
CreateProcessFlag_EnableAliasRegionExtraSize,
|
||||
};
|
||||
|
||||
/* Debug types. */
|
||||
enum DebugEvent : u32 {
|
||||
DebugEvent_CreateProcess = 0,
|
||||
DebugEvent_CreateThread = 1,
|
||||
DebugEvent_ExitProcess = 2,
|
||||
DebugEvent_ExitThread = 3,
|
||||
DebugEvent_Exception = 4,
|
||||
};
|
||||
|
||||
enum DebugThreadParam : u32 {
|
||||
DebugThreadParam_Priority = 0,
|
||||
DebugThreadParam_State = 1,
|
||||
DebugThreadParam_IdealCore = 2,
|
||||
DebugThreadParam_CurrentCore = 3,
|
||||
DebugThreadParam_AffinityMask = 4,
|
||||
};
|
||||
|
||||
enum DebugException : u32 {
|
||||
DebugException_UndefinedInstruction = 0,
|
||||
DebugException_InstructionAbort = 1,
|
||||
DebugException_DataAbort = 2,
|
||||
DebugException_AlignmentFault = 3,
|
||||
DebugException_DebuggerAttached = 4,
|
||||
DebugException_BreakPoint = 5,
|
||||
DebugException_UserBreak = 6,
|
||||
DebugException_DebuggerBreak = 7,
|
||||
DebugException_UndefinedSystemCall = 8,
|
||||
DebugException_MemorySystemError = 9,
|
||||
};
|
||||
|
||||
enum DebugEventFlag : u32 {
|
||||
DebugEventFlag_Stopped = (1u << 0),
|
||||
};
|
||||
|
||||
enum ExceptionType : u32 {
|
||||
ExceptionType_Init = 0x000,
|
||||
ExceptionType_InstructionAbort = 0x100,
|
||||
ExceptionType_DataAbort = 0x101,
|
||||
ExceptionType_UnalignedInstruction = 0x102,
|
||||
ExceptionType_UnalignedData = 0x103,
|
||||
ExceptionType_UndefinedInstruction = 0x104,
|
||||
ExceptionType_ExceptionInstruction = 0x105,
|
||||
ExceptionType_MemorySystemError = 0x106,
|
||||
ExceptionType_FpuException = 0x200,
|
||||
ExceptionType_InvalidSystemCall = 0x301,
|
||||
ExceptionType_SystemCallBreak = 0x302,
|
||||
|
||||
ExceptionType_AtmosphereStdAbort = 0xFFE,
|
||||
};
|
||||
|
||||
enum BreakReason : u32 {
|
||||
BreakReason_Panic = 0,
|
||||
BreakReason_Assert = 1,
|
||||
BreakReason_User = 2,
|
||||
BreakReason_PreLoadDll = 3,
|
||||
BreakReason_PostLoadDll = 4,
|
||||
BreakReason_PreUnloadDll = 5,
|
||||
BreakReason_PostUnloadDll = 6,
|
||||
BreakReason_CppException = 7,
|
||||
|
||||
BreakReason_NotificationOnlyFlag = 0x80000000,
|
||||
};
|
||||
|
||||
enum KernelDebugType : u32 {
|
||||
KernelDebugType_Thread = 0,
|
||||
KernelDebugType_ThreadCallStack = 1,
|
||||
KernelDebugType_KernelObject = 2,
|
||||
KernelDebugType_Handle = 3,
|
||||
KernelDebugType_Memory = 4,
|
||||
KernelDebugType_PageTable = 5,
|
||||
KernelDebugType_CpuUtilization = 6,
|
||||
KernelDebugType_Process = 7,
|
||||
KernelDebugType_SuspendProcess = 8,
|
||||
KernelDebugType_ResumeProcess = 9,
|
||||
KernelDebugType_Port = 10,
|
||||
};
|
||||
|
||||
enum KernelTraceState : u32 {
|
||||
KernelTraceState_Disabled = 0,
|
||||
KernelTraceState_Enabled = 1,
|
||||
};
|
||||
|
||||
enum BreakPointType : u32 {
|
||||
BreakPointType_HardwareInstruction = 0,
|
||||
BreakPointType_HardwareData = 1,
|
||||
};
|
||||
|
||||
enum HardwareBreakPointRegisterName : u32 {
|
||||
HardwareBreakPointRegisterName_I0 = 0,
|
||||
HardwareBreakPointRegisterName_I1 = 1,
|
||||
HardwareBreakPointRegisterName_I2 = 2,
|
||||
HardwareBreakPointRegisterName_I3 = 3,
|
||||
HardwareBreakPointRegisterName_I4 = 4,
|
||||
HardwareBreakPointRegisterName_I5 = 5,
|
||||
HardwareBreakPointRegisterName_I6 = 6,
|
||||
HardwareBreakPointRegisterName_I7 = 7,
|
||||
HardwareBreakPointRegisterName_I8 = 8,
|
||||
HardwareBreakPointRegisterName_I9 = 9,
|
||||
HardwareBreakPointRegisterName_I10 = 10,
|
||||
HardwareBreakPointRegisterName_I11 = 11,
|
||||
HardwareBreakPointRegisterName_I12 = 12,
|
||||
HardwareBreakPointRegisterName_I13 = 13,
|
||||
HardwareBreakPointRegisterName_I14 = 14,
|
||||
HardwareBreakPointRegisterName_I15 = 15,
|
||||
HardwareBreakPointRegisterName_D0 = 16,
|
||||
HardwareBreakPointRegisterName_D1 = 17,
|
||||
HardwareBreakPointRegisterName_D2 = 18,
|
||||
HardwareBreakPointRegisterName_D3 = 19,
|
||||
HardwareBreakPointRegisterName_D4 = 20,
|
||||
HardwareBreakPointRegisterName_D5 = 21,
|
||||
HardwareBreakPointRegisterName_D6 = 22,
|
||||
HardwareBreakPointRegisterName_D7 = 23,
|
||||
HardwareBreakPointRegisterName_D8 = 24,
|
||||
HardwareBreakPointRegisterName_D9 = 25,
|
||||
HardwareBreakPointRegisterName_D10 = 26,
|
||||
HardwareBreakPointRegisterName_D11 = 27,
|
||||
HardwareBreakPointRegisterName_D12 = 28,
|
||||
HardwareBreakPointRegisterName_D13 = 29,
|
||||
HardwareBreakPointRegisterName_D14 = 30,
|
||||
HardwareBreakPointRegisterName_D15 = 31,
|
||||
};
|
||||
|
||||
/* Architecture specific types. */
|
||||
namespace aarch64 {
|
||||
|
||||
struct ExceptionInfo {
|
||||
u64 r[9];
|
||||
u64 lr;
|
||||
u64 sp;
|
||||
u64 pc;
|
||||
u32 pstate;
|
||||
u32 afsr0;
|
||||
u32 afsr1;
|
||||
u32 esr;
|
||||
u64 far;
|
||||
};
|
||||
static_assert(sizeof(ExceptionInfo) == 0x78);
|
||||
|
||||
struct ProcessLocalRegion {
|
||||
u64 data[(0x1C0 - sizeof(ExceptionInfo)) / sizeof(u64)];
|
||||
ExceptionInfo exception_info;
|
||||
u64 dying_message_region_address;
|
||||
u64 dying_message_region_size;
|
||||
u64 padding[6];
|
||||
};
|
||||
static_assert(sizeof(ProcessLocalRegion) == 0x200);
|
||||
static_assert(AMS_OFFSETOF(ProcessLocalRegion, dying_message_region_address) == 0x1C0);
|
||||
|
||||
}
|
||||
|
||||
namespace aarch32 {
|
||||
|
||||
struct ExceptionInfoStatus32 {
|
||||
u32 cpsr;
|
||||
u32 fsr;
|
||||
u32 far;
|
||||
u32 fpexc;
|
||||
u32 fpinst;
|
||||
u32 fpinst2;
|
||||
};
|
||||
|
||||
struct ExceptionInfoStatus64 {
|
||||
u32 pstate;
|
||||
u32 afsr0;
|
||||
u32 afsr1;
|
||||
u32 esr;
|
||||
u32 far;
|
||||
};
|
||||
|
||||
struct ExceptionInfo {
|
||||
u32 r[8];
|
||||
u32 sp;
|
||||
u32 lr;
|
||||
u32 pc;
|
||||
u32 flags;
|
||||
union {
|
||||
ExceptionInfoStatus32 status_32;
|
||||
ExceptionInfoStatus64 status_64;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(ExceptionInfo) == 0x48);
|
||||
|
||||
struct ProcessLocalRegion {
|
||||
u32 data[(0x1C0 - sizeof(ExceptionInfo)) / sizeof(u32)];
|
||||
ExceptionInfo exception_info;
|
||||
u64 dying_message_region_address;
|
||||
u64 dying_message_region_size;
|
||||
u64 padding[6];
|
||||
};
|
||||
static_assert(sizeof(ProcessLocalRegion) == 0x200);
|
||||
static_assert(AMS_OFFSETOF(ProcessLocalRegion, dying_message_region_address) == 0x1C0);
|
||||
|
||||
}
|
||||
|
||||
/* Secure monitor argument shims. */
|
||||
namespace lp64 {
|
||||
|
||||
struct SecureMonitorArguments {
|
||||
u64 r[8];
|
||||
};
|
||||
static_assert(sizeof(SecureMonitorArguments) == 0x40);
|
||||
|
||||
}
|
||||
|
||||
namespace ilp32 {
|
||||
|
||||
struct SecureMonitorArguments {
|
||||
u32 r[8];
|
||||
};
|
||||
static_assert(sizeof(SecureMonitorArguments) == 0x20);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
#include <vapours/svc/svc_select_hardware_constants.hpp>
|
||||
#include <vapours/svc/svc_select_device_name.hpp>
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
namespace lp64 {
|
||||
|
||||
struct PhysicalMemoryInfo {
|
||||
PhysicalAddress physical_address;
|
||||
u64 virtual_address;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace ilp32 {
|
||||
|
||||
struct PhysicalMemoryInfo {
|
||||
PhysicalAddress physical_address;
|
||||
u32 virtual_address;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
namespace lp64 {
|
||||
|
||||
struct DebugInfoCreateProcess {
|
||||
u64 program_id;
|
||||
u64 process_id;
|
||||
char name[0xC];
|
||||
u32 flags;
|
||||
u64 user_exception_context_address; /* 5.0.0+ */
|
||||
};
|
||||
|
||||
struct DebugInfoCreateThread {
|
||||
u64 thread_id;
|
||||
u64 tls_address;
|
||||
/* Removed in 11.0.0 u64 entrypoint; */
|
||||
};
|
||||
|
||||
struct DebugInfoExitProcess {
|
||||
ProcessExitReason reason;
|
||||
};
|
||||
|
||||
struct DebugInfoExitThread {
|
||||
ThreadExitReason reason;
|
||||
};
|
||||
|
||||
struct DebugInfoUndefinedInstructionException {
|
||||
u32 insn;
|
||||
};
|
||||
|
||||
struct DebugInfoDataAbortException {
|
||||
u64 address;
|
||||
};
|
||||
|
||||
struct DebugInfoAlignmentFaultException {
|
||||
u64 address;
|
||||
};
|
||||
|
||||
struct DebugInfoBreakPointException {
|
||||
BreakPointType type;
|
||||
u64 address;
|
||||
};
|
||||
|
||||
struct DebugInfoUserBreakException {
|
||||
BreakReason break_reason;
|
||||
u64 address;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
struct DebugInfoDebuggerBreakException {
|
||||
u64 active_thread_ids[4];
|
||||
};
|
||||
|
||||
struct DebugInfoUndefinedSystemCallException {
|
||||
u32 id;
|
||||
};
|
||||
|
||||
union DebugInfoSpecificException {
|
||||
DebugInfoUndefinedInstructionException undefined_instruction;
|
||||
DebugInfoDataAbortException data_abort;
|
||||
DebugInfoAlignmentFaultException alignment_fault;
|
||||
DebugInfoBreakPointException break_point;
|
||||
DebugInfoUserBreakException user_break;
|
||||
DebugInfoDebuggerBreakException debugger_break;
|
||||
DebugInfoUndefinedSystemCallException undefined_system_call;
|
||||
u64 raw;
|
||||
};
|
||||
|
||||
struct DebugInfoException {
|
||||
DebugException type;
|
||||
u64 address;
|
||||
DebugInfoSpecificException specific;
|
||||
};
|
||||
|
||||
union DebugInfo {
|
||||
DebugInfoCreateProcess create_process;
|
||||
DebugInfoCreateThread create_thread;
|
||||
DebugInfoExitProcess exit_process;
|
||||
DebugInfoExitThread exit_thread;
|
||||
DebugInfoException exception;
|
||||
};
|
||||
|
||||
struct DebugEventInfo {
|
||||
DebugEvent type;
|
||||
u32 flags;
|
||||
u64 thread_id;
|
||||
DebugInfo info;
|
||||
};
|
||||
static_assert(sizeof(DebugEventInfo) >= 0x40);
|
||||
|
||||
}
|
||||
|
||||
namespace ilp32 {
|
||||
|
||||
struct DebugInfoCreateProcess {
|
||||
u64 program_id;
|
||||
u64 process_id;
|
||||
char name[0xC];
|
||||
u32 flags;
|
||||
u32 user_exception_context_address; /* 5.0.0+ */
|
||||
};
|
||||
|
||||
struct DebugInfoCreateThread {
|
||||
u64 thread_id;
|
||||
u32 tls_address;
|
||||
/* Removed in 11.0.0 u32 entrypoint; */
|
||||
};
|
||||
|
||||
struct DebugInfoExitProcess {
|
||||
ProcessExitReason reason;
|
||||
};
|
||||
|
||||
struct DebugInfoExitThread {
|
||||
ThreadExitReason reason;
|
||||
};
|
||||
|
||||
struct DebugInfoUndefinedInstructionException {
|
||||
u32 insn;
|
||||
};
|
||||
|
||||
struct DebugInfoDataAbortException {
|
||||
u32 address;
|
||||
};
|
||||
|
||||
struct DebugInfoAlignmentFaultException {
|
||||
u32 address;
|
||||
};
|
||||
|
||||
struct DebugInfoBreakPointException {
|
||||
BreakPointType type;
|
||||
u32 address;
|
||||
};
|
||||
|
||||
struct DebugInfoUserBreakException {
|
||||
BreakReason break_reason;
|
||||
u32 address;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct DebugInfoDebuggerBreakException {
|
||||
u64 active_thread_ids[4];
|
||||
};
|
||||
|
||||
struct DebugInfoUndefinedSystemCallException {
|
||||
u32 id;
|
||||
};
|
||||
|
||||
union DebugInfoSpecificException {
|
||||
DebugInfoUndefinedInstructionException undefined_instruction;
|
||||
DebugInfoDataAbortException data_abort;
|
||||
DebugInfoAlignmentFaultException alignment_fault;
|
||||
DebugInfoBreakPointException break_point;
|
||||
DebugInfoUserBreakException user_break;
|
||||
DebugInfoDebuggerBreakException debugger_break;
|
||||
DebugInfoUndefinedSystemCallException undefined_system_call;
|
||||
u64 raw;
|
||||
};
|
||||
|
||||
struct DebugInfoException {
|
||||
DebugException type;
|
||||
u32 address;
|
||||
DebugInfoSpecificException specific;
|
||||
};
|
||||
|
||||
union DebugInfo {
|
||||
DebugInfoCreateProcess create_process;
|
||||
DebugInfoCreateThread create_thread;
|
||||
DebugInfoExitProcess exit_process;
|
||||
DebugInfoExitThread exit_thread;
|
||||
DebugInfoException exception;
|
||||
};
|
||||
|
||||
struct DebugEventInfo {
|
||||
DebugEvent type;
|
||||
u32 flags;
|
||||
u64 thread_id;
|
||||
DebugInfo info;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_types_common.hpp>
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
namespace lp64 {
|
||||
|
||||
struct CreateProcessParameter {
|
||||
char name[12];
|
||||
u32 version;
|
||||
u64 program_id;
|
||||
u64 code_address;
|
||||
s32 code_num_pages;
|
||||
u32 flags;
|
||||
Handle reslimit;
|
||||
s32 system_resource_num_pages;
|
||||
};
|
||||
static_assert(sizeof(CreateProcessParameter) == 0x30);
|
||||
|
||||
}
|
||||
|
||||
namespace ilp32 {
|
||||
|
||||
struct CreateProcessParameter {
|
||||
char name[12];
|
||||
u32 version;
|
||||
u64 program_id;
|
||||
u64 code_address;
|
||||
s32 code_num_pages;
|
||||
u32 flags;
|
||||
Handle reslimit;
|
||||
s32 system_resource_num_pages;
|
||||
};
|
||||
static_assert(sizeof(CreateProcessParameter) == 0x30);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/svc/svc_common.hpp>
|
||||
#include <vapours/svc/svc_select_hardware_constants.hpp>
|
||||
#include <vapours/util/util_bitpack.hpp>
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
constexpr inline u32 ConvertToSvcMajorVersion(u32 sdk) { return sdk + 4; }
|
||||
constexpr inline u32 ConvertToSdkMajorVersion(u32 svc) { return svc - 4; }
|
||||
|
||||
constexpr inline u32 ConvertToSvcMinorVersion(u32 sdk) { return sdk; }
|
||||
constexpr inline u32 ConvertToSdkMinorVersion(u32 svc) { return svc; }
|
||||
|
||||
struct KernelVersion {
|
||||
using MinorVersion = util::BitPack32::Field<0, 4>;
|
||||
using MajorVersion = util::BitPack32::Field<MinorVersion::Next, 13>;
|
||||
};
|
||||
|
||||
constexpr inline u32 EncodeKernelVersion(u32 major, u32 minor) {
|
||||
util::BitPack32 pack = {};
|
||||
pack.Set<KernelVersion::MinorVersion>(minor);
|
||||
pack.Set<KernelVersion::MajorVersion>(major);
|
||||
return pack.value;
|
||||
}
|
||||
|
||||
constexpr inline u32 GetKernelMajorVersion(u32 encoded) {
|
||||
const util::BitPack32 pack = { encoded };
|
||||
return pack.Get<KernelVersion::MajorVersion>();
|
||||
}
|
||||
|
||||
constexpr inline u32 GetKernelMinorVersion(u32 encoded) {
|
||||
const util::BitPack32 pack = { encoded };
|
||||
return pack.Get<KernelVersion::MinorVersion>();
|
||||
}
|
||||
|
||||
/* Nintendo doesn't support programs targeting SVC versions < 3.0. */
|
||||
constexpr inline u32 RequiredKernelMajorVersion = 3;
|
||||
constexpr inline u32 RequiredKernelMinorVersion = 0;
|
||||
|
||||
constexpr inline u32 RequiredKernelVersion = EncodeKernelVersion(RequiredKernelMajorVersion, RequiredKernelMinorVersion);
|
||||
|
||||
/* This is the highest SVC version supported by Atmosphere, to be updated on new kernel releases. */
|
||||
/* NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor. */
|
||||
constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(20);
|
||||
constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion( 5);
|
||||
|
||||
constexpr inline u32 SupportedKernelVersion = EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user