Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
173d5c2d3a | ||
|
|
52c1cef064 | ||
|
|
4efa5d7dd0 | ||
|
|
275da52305 | ||
|
|
30fac905af | ||
|
|
78f7218c4f | ||
|
|
09c6aa29dd | ||
|
|
1019bc54e6 | ||
|
|
96631d8225 | ||
|
|
d32dd0f04a | ||
|
|
ddfc16731f | ||
|
|
26714c7f3b | ||
|
|
767e702a70 | ||
|
|
9d5e652fbd | ||
|
|
1d39e06f32 | ||
|
|
21b7884653 | ||
|
|
14ad2f0ba0 | ||
|
|
fcc7ce49d9 | ||
|
|
f98c7cba98 | ||
|
|
496adb0018 | ||
|
|
09074798cd | ||
|
|
e256261b80 | ||
|
|
47218f0da8 |
@@ -1,4 +1,29 @@
|
||||
# Changelog
|
||||
## 1.2.6
|
||||
+ Support was added for 13.2.1.
|
||||
+ A number of minor issues were fixed and improvements were made, including:
|
||||
+ A minor performance improvement was implemented in service table dispatch by sorting and binary-searching the service command table instead of using linear search.
|
||||
+ Static initialization logic in Atmosphere was made much more regular.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 1.2.5
|
||||
+ Support was added for 13.2.0.
|
||||
+ A number of minor issues were fixed and improvements were made, including:
|
||||
+ A bug was fixed that caused `mesosphère` to underreport the total memory size by 8MB for certain games which use newer system-resource-size memory management.
|
||||
+ This caused FIFA 19 to crash, and possibly other issues.
|
||||
+ Memory management changes were made to `sm` that save 0x5000 of memory.
|
||||
+ A microoptimization was made to the way `mesosphère` manages updating the debug register for hardware single-step support.
|
||||
+ Support was fixed for enabling `usb!usb30_force_enabled` on 13.0.0+.
|
||||
+ The work-in-progress unit testing framework was updated to use doctest instead of catch2.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 1.2.4
|
||||
+ Changes were made to the way fs.mitm builds images when providing a layeredfs romfs.
|
||||
+ Cache management (to avoid unnecessary rebuild) was revised, to add a grace period of ~500ms-1s between process closing romfs image and ams.mitm needing to rebuild if romfs is re-opened.
|
||||
+ This makes our cache much more effective, previously we were re-building romfs several times.
|
||||
+ RomFS image ownership was overhauled, with a new reference-counting implementation added (used to implement the above grace period).
|
||||
+ Certain games (e.g. Puyo Puyo Tetris 2, probably others) were sensitive to this timing, and could use access patterns which would trigger creation of romfs image while previous romfs image was in the middle of destructor.
|
||||
+ This could cause a fatal error, because the destructor for the old image could run simultaneously with building the new image.
|
||||
+ This also provides a speedup versus the 1.2.3 code, with Animal Crossing now taking ~8 fewer seconds to get past the Nintendo Switch logo.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 1.2.3
|
||||
+ Because ams.TMA is taking longer to develop than expected, experimental support for Atmosphère's gdbstub as a standalone is now available.
|
||||
+ To enable it, set `atmosphere!enable_standalone_gdbstub` = u8!0x1 in system_settings.ini.
|
||||
|
||||
@@ -249,6 +249,8 @@ namespace ams::nxboot {
|
||||
return ams::TargetFirmware_12_1_0;
|
||||
} else if (std::memcmp(package1 + 0x10, "20210805", 8) == 0) {
|
||||
return ams::TargetFirmware_13_0_0;
|
||||
} else if (std::memcmp(package1 + 0x10, "20220105", 8) == 0) {
|
||||
return ams::TargetFirmware_13_2_1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = e7b84767c6f3dd4c2a97fa00201342c237bf67b4
|
||||
parent = 0bbc907907f909f67c1ba1ce89f8efb3b79b704a
|
||||
commit = 0d161b8588aa6482b84f3c44dd001055b01a047f
|
||||
parent = 4efa5d7dd0bfbdf89a6261af0aef3878ca784b05
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace ams::pkg2 {
|
||||
|
||||
constexpr inline int PayloadCount = 3;
|
||||
|
||||
constexpr inline int MinimumValidDataVersion = 0; /* We allow older package2 to load; this value is currently 0x13 in Nintendo's code. */
|
||||
constexpr inline int CurrentBootloaderVersion = 0xF;
|
||||
constexpr inline int MinimumValidDataVersion = 0; /* We allow older package2 to load; this value is currently 0x14 in Nintendo's code. */
|
||||
constexpr inline int CurrentBootloaderVersion = 0x10;
|
||||
|
||||
struct Package2Meta {
|
||||
using Magic = util::FourCC<'P','K','2','1'>;
|
||||
|
||||
@@ -167,6 +167,7 @@ namespace ams::fuse {
|
||||
}
|
||||
|
||||
constexpr const TargetFirmware FuseVersionIncrementFirmwares[] = {
|
||||
TargetFirmware_13_2_1,
|
||||
TargetFirmware_12_0_2,
|
||||
TargetFirmware_11_0_0,
|
||||
TargetFirmware_10_0_0,
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace ams::kern::arch::arm {
|
||||
PriorityLevel_Scheduler = 2,
|
||||
};
|
||||
private:
|
||||
static inline u32 s_mask[cpu::NumCores];
|
||||
static constinit inline u32 s_mask[cpu::NumCores];
|
||||
private:
|
||||
volatile GicDistributor *m_gicd;
|
||||
volatile GicCpuInterface *m_gicc;
|
||||
|
||||
@@ -62,8 +62,7 @@ namespace ams::kern {
|
||||
KThread *m_idle_thread;
|
||||
util::Atomic<KThread *> m_current_thread;
|
||||
public:
|
||||
constexpr KScheduler() : m_state(), m_is_active(false), m_core_id(0), m_last_context_switch_time(0), m_idle_thread(nullptr), m_current_thread(nullptr)
|
||||
{
|
||||
constexpr KScheduler() : m_state(), m_is_active(false), m_core_id(0), m_last_context_switch_time(0), m_idle_thread(nullptr), m_current_thread(nullptr) {
|
||||
m_state.needs_scheduling = true;
|
||||
m_state.interrupt_task_runnable = false;
|
||||
m_state.should_count_idle = false;
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
|
||||
class KPerformanceCounterInterruptHandler : public KInterruptHandler {
|
||||
private:
|
||||
static inline KLightLock s_lock;
|
||||
static constinit inline KLightLock s_lock;
|
||||
private:
|
||||
u64 m_counter;
|
||||
s32 m_which;
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace ams::kern::svc {
|
||||
|
||||
/* Set omit-frame-pointer to prevent GCC from emitting MOV X29, SP instructions. */
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O2")
|
||||
#pragma GCC optimize ("-O3")
|
||||
#pragma GCC optimize ("omit-frame-pointer")
|
||||
|
||||
AMS_SVC_FOREACH_KERN_DEFINITION(DECLARE_SVC_STRUCT, _)
|
||||
|
||||
@@ -114,6 +114,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
ams::svc::DeviceName_Hda,
|
||||
ams::svc::DeviceName_Isp2,
|
||||
ams::svc::DeviceName_Sata,
|
||||
ams::svc::DeviceName_Vi,
|
||||
ams::svc::DeviceName_XusbHost,
|
||||
ams::svc::DeviceName_XusbDev,
|
||||
ams::svc::DeviceName_Tsec,
|
||||
@@ -548,11 +549,11 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
/* Print the interrupt. */
|
||||
{
|
||||
constexpr auto GetBits = [] ALWAYS_INLINE_LAMBDA (u32 value, size_t ofs, size_t count) {
|
||||
constexpr auto GetBits = [](u32 value, size_t ofs, size_t count) ALWAYS_INLINE_LAMBDA {
|
||||
return (value >> ofs) & ((1u << count) - 1);
|
||||
};
|
||||
|
||||
constexpr auto GetBit = [GetBits] ALWAYS_INLINE_LAMBDA (u32 value, size_t ofs) {
|
||||
constexpr auto GetBit = [GetBits](u32 value, size_t ofs) ALWAYS_INLINE_LAMBDA {
|
||||
return (value >> ofs) & 1u;
|
||||
};
|
||||
|
||||
|
||||
@@ -312,7 +312,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
size_t KSystemControl::Init::GetApplicationPoolSize() {
|
||||
/* Get the base pool size. */
|
||||
const size_t base_pool_size = [] ALWAYS_INLINE_LAMBDA () -> size_t {
|
||||
const size_t base_pool_size = []() ALWAYS_INLINE_LAMBDA -> size_t {
|
||||
switch (GetMemoryArrangeForInit()) {
|
||||
case smc::MemoryArrangement_4GB:
|
||||
default:
|
||||
@@ -336,7 +336,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
size_t KSystemControl::Init::GetAppletPoolSize() {
|
||||
/* Get the base pool size. */
|
||||
const size_t base_pool_size = [] ALWAYS_INLINE_LAMBDA () -> size_t {
|
||||
const size_t base_pool_size = []() ALWAYS_INLINE_LAMBDA -> size_t {
|
||||
switch (GetMemoryArrangeForInit()) {
|
||||
case smc::MemoryArrangement_4GB:
|
||||
default:
|
||||
@@ -490,7 +490,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
|
||||
if (AMS_LIKELY(s_initialized_random_generator)) {
|
||||
return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); });
|
||||
return KSystemControlBase::GenerateUniformRange(min, max, []() ALWAYS_INLINE_LAMBDA -> u64 { return s_random_generator.GenerateRandomU64(); });
|
||||
} else {
|
||||
return KSystemControlBase::GenerateUniformRange(min, max, GenerateRandomU64FromSmc);
|
||||
}
|
||||
@@ -680,4 +680,4 @@ namespace ams::kern::board::nintendo::nx {
|
||||
Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ namespace ams::kern {
|
||||
static_assert(KMemoryManager::Pool_Secure == KMemoryManager::Pool_System);
|
||||
|
||||
/* Get Secure pool size. */
|
||||
const size_t secure_pool_size = [] ALWAYS_INLINE_LAMBDA (auto target_firmware) -> size_t {
|
||||
const size_t secure_pool_size = [](auto target_firmware) ALWAYS_INLINE_LAMBDA -> size_t {
|
||||
constexpr size_t LegacySecureKernelSize = 8_MB; /* KPageBuffer pages, other small kernel allocations. */
|
||||
constexpr size_t LegacySecureMiscSize = 1_MB; /* Miscellaneous pages for secure process mapping. */
|
||||
constexpr size_t LegacySecureHeapSize = 24_MB; /* Heap pages for secure process mapping (fs). */
|
||||
@@ -274,4 +274,4 @@ namespace ams::kern {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2398,7 +2398,7 @@ namespace ams::kern {
|
||||
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||
size_t tot_size = cur_size;
|
||||
|
||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||
/* Ensure the address is linear mapped. */
|
||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
@@ -2484,7 +2484,7 @@ namespace ams::kern {
|
||||
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||
size_t tot_size = cur_size;
|
||||
|
||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||
/* Ensure the address is linear mapped. */
|
||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
@@ -2943,7 +2943,7 @@ namespace ams::kern {
|
||||
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||
size_t tot_size = cur_size;
|
||||
|
||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||
/* Ensure the address is linear mapped. */
|
||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
@@ -3023,7 +3023,7 @@ namespace ams::kern {
|
||||
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||
size_t tot_size = cur_size;
|
||||
|
||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||
/* Ensure the address is linear mapped. */
|
||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
@@ -3092,7 +3092,7 @@ namespace ams::kern {
|
||||
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||
size_t tot_size = cur_size;
|
||||
|
||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||
/* Ensure the address is linear mapped. */
|
||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
@@ -3172,7 +3172,7 @@ namespace ams::kern {
|
||||
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||
size_t tot_size = cur_size;
|
||||
|
||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||
/* Ensure the address is linear mapped. */
|
||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
|
||||
@@ -893,7 +893,7 @@ namespace ams::kern {
|
||||
size_t KProcess::GetTotalUserPhysicalMemorySize() const {
|
||||
/* Get the amount of free and used size. */
|
||||
const size_t free_size = m_resource_limit->GetFreeValue(ams::svc::LimitableResource_PhysicalMemoryMax);
|
||||
const size_t used_size = this->GetUsedNonSystemUserPhysicalMemorySize();
|
||||
const size_t used_size = this->GetUsedUserPhysicalMemorySize();
|
||||
const size_t max_size = m_max_process_memory;
|
||||
|
||||
if (used_size + free_size > max_size) {
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O3")
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O3")
|
||||
namespace ams::kern {
|
||||
|
||||
bool KScheduler::s_scheduler_update_needed;
|
||||
KScheduler::LockType KScheduler::s_scheduler_lock;
|
||||
@@ -249,22 +249,18 @@ namespace ams::kern {
|
||||
|
||||
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
|
||||
/* Ensure the single-step bit in mdscr reflects the correct single-step state for the new thread. */
|
||||
/* NOTE: Per ARM docs, changing the single-step bit requires a "context synchronization event" to */
|
||||
/* be sure that our new configuration takes. However, there are three types of synchronization event: */
|
||||
/* Taking an exception, returning from an exception, and ISB. The single-step bit change only matters */
|
||||
/* in EL0...which implies a return-from-exception has occurred since we set the bit. Thus, forcing */
|
||||
/* an ISB is unnecessary, and we can modify the register safely and be confident it will affect the next */
|
||||
/* userland instruction executed. */
|
||||
cpu::MonitorDebugSystemControlRegisterAccessor().SetSoftwareStep(next_thread->IsSingleStep()).Store();
|
||||
#endif
|
||||
|
||||
/* Switch the current process, if we're switching processes. */
|
||||
if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
|
||||
KProcess::Switch(cur_process, next_process);
|
||||
} else {
|
||||
/* The single-step bit set up above requires an instruction synchronization barrier, to ensure */
|
||||
/* the state change takes before we actually perform a return which might break-to-step. */
|
||||
/* KProcess::Switch performs an isb incidentally, and so when we're changing process we */
|
||||
/* can piggy-back off of that isb to avoid unnecessarily emptying the pipeline twice. */
|
||||
/* However, this means that when we're switching to thread in a different process, */
|
||||
/* we must ensure that we still isb. In practice, gcc will deduplicate into a single isb. */
|
||||
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
|
||||
cpu::InstructionMemoryBarrier();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Set the new thread. */
|
||||
@@ -602,6 +598,6 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O3")
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O3")
|
||||
namespace ams::kern {
|
||||
|
||||
namespace ipc {
|
||||
|
||||
@@ -1385,8 +1385,6 @@ namespace ams::kern {
|
||||
this->NotifyAvailable(svc::ResultSessionClosed());
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
void KServerSession::Dump() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
@@ -1420,3 +1418,5 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace ams::kern {
|
||||
s_initialized_random_generator = true;
|
||||
}
|
||||
|
||||
return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); });
|
||||
return KSystemControlBase::GenerateUniformRange(min, max, []() ALWAYS_INLINE_LAMBDA -> u64 { return s_random_generator.GenerateRandomU64(); });
|
||||
}
|
||||
|
||||
/* System Initialization. */
|
||||
@@ -194,7 +194,7 @@ namespace ams::kern {
|
||||
KScopedInterruptDisable intr_disable;
|
||||
KScopedSpinLock lk(s_random_lock);
|
||||
|
||||
return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); });
|
||||
return KSystemControlBase::GenerateUniformRange(min, max, []() ALWAYS_INLINE_LAMBDA -> u64 { return s_random_generator.GenerateRandomU64(); });
|
||||
}
|
||||
|
||||
u64 KSystemControlBase::GenerateRandomU64() {
|
||||
@@ -292,4 +292,4 @@ namespace ams::kern {
|
||||
Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1183,7 +1183,7 @@ namespace ams::kern {
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
/* Determine if this is the first termination request. */
|
||||
const bool first_request = [&] ALWAYS_INLINE_LAMBDA () -> bool {
|
||||
const bool first_request = [&]() ALWAYS_INLINE_LAMBDA -> bool {
|
||||
/* Perform an atomic compare-and-swap from false to true. */
|
||||
bool expected = false;
|
||||
return m_termination_requested.CompareExchangeStrong(expected, true);
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace ams::kern::svc {
|
||||
/* Check whether the address is aligned. */
|
||||
const bool aligned = util::IsAligned(phys_addr, PageSize);
|
||||
|
||||
auto QueryIoMappingFromPageTable = [&] ALWAYS_INLINE_LAMBDA (uint64_t phys_addr, size_t size) -> Result {
|
||||
auto QueryIoMappingFromPageTable = [&](uint64_t phys_addr, size_t size) ALWAYS_INLINE_LAMBDA -> Result {
|
||||
/* The size must be non-zero. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
|
||||
|
||||
@@ -277,6 +277,9 @@ namespace ams::kern::svc {
|
||||
}
|
||||
|
||||
Result WriteDebugProcessMemory(ams::svc::Handle debug_handle, uintptr_t buffer, uintptr_t address, size_t size) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||
|
||||
/* Validate address / size. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O3")
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O3")
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
@@ -315,6 +315,6 @@ namespace ams::kern::svc {
|
||||
return ReplyAndReceiveWithUserBuffer(out_index, message_buffer, message_buffer_size, handles, num_handles, reply_target, timeout_ns);
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace ams::kern::svc {
|
||||
KResourceLimit *process_resource_limit = resource_limit.IsNotNull() ? resource_limit.GetPointerUnsafe() : std::addressof(Kernel::GetSystemResourceLimit());
|
||||
|
||||
/* Get the pool for the process. */
|
||||
const auto pool = [] ALWAYS_INLINE_LAMBDA (u32 flags) -> KMemoryManager::Pool {
|
||||
const auto pool = [](u32 flags) ALWAYS_INLINE_LAMBDA -> KMemoryManager::Pool {
|
||||
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
|
||||
switch (flags & ams::svc::CreateProcessFlag_PoolPartitionMask) {
|
||||
case ams::svc::CreateProcessFlag_PoolPartitionApplication:
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace ams::impl {
|
||||
AMS_DEFINE_SYSTEM_THREAD(-7, mitm, InitializeThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(-1, mitm_sf, QueryServerProcessThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemInitializeThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemFinalizeThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm, DebugThrowThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, IpcServer);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
|
||||
|
||||
@@ -66,6 +66,9 @@ namespace ams::hos {
|
||||
Version_12_0_3 = ::ams::TargetFirmware_12_0_3,
|
||||
Version_12_1_0 = ::ams::TargetFirmware_12_1_0,
|
||||
Version_13_0_0 = ::ams::TargetFirmware_13_0_0,
|
||||
Version_13_1_0 = ::ams::TargetFirmware_13_1_0,
|
||||
Version_13_2_0 = ::ams::TargetFirmware_13_2_0,
|
||||
Version_13_2_1 = ::ams::TargetFirmware_13_2_1,
|
||||
|
||||
Version_Current = ::ams::TargetFirmware_Current,
|
||||
|
||||
|
||||
@@ -62,20 +62,16 @@ namespace ams::settings {
|
||||
|
||||
char name[MaxLength];
|
||||
|
||||
static constexpr LanguageCode Encode(const char *name, size_t name_size) {
|
||||
static constexpr LanguageCode Encode(util::string_view name) {
|
||||
LanguageCode out{};
|
||||
for (size_t i = 0; i < MaxLength && i < name_size; i++) {
|
||||
for (size_t i = 0; i < MaxLength && i < name.size(); i++) {
|
||||
out.name[i] = name[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static constexpr LanguageCode Encode(const char *name) {
|
||||
return Encode(name, std::strlen(name));
|
||||
}
|
||||
|
||||
template<Language Lang>
|
||||
static constexpr inline LanguageCode EncodeLanguage = [] {
|
||||
static constexpr inline LanguageCode EncodeLanguage() {
|
||||
if constexpr (false) { /* ... */ }
|
||||
#define AMS_MATCH_LANGUAGE(lang, enc) else if constexpr (Lang == Language_##lang) { return LanguageCode::Encode(enc); }
|
||||
AMS_MATCH_LANGUAGE(Japanese, "ja")
|
||||
@@ -98,28 +94,28 @@ namespace ams::settings {
|
||||
AMS_MATCH_LANGUAGE(TraditionalChinese, "zh-Hant")
|
||||
#undef AMS_MATCH_LANGUAGE
|
||||
else { static_assert(Lang != Language_Japanese); }
|
||||
}();
|
||||
}
|
||||
|
||||
static constexpr inline LanguageCode Encode(const Language language) {
|
||||
constexpr LanguageCode EncodedLanguages[Language_Count] = {
|
||||
EncodeLanguage<Language_Japanese>,
|
||||
EncodeLanguage<Language_AmericanEnglish>,
|
||||
EncodeLanguage<Language_French>,
|
||||
EncodeLanguage<Language_German>,
|
||||
EncodeLanguage<Language_Italian>,
|
||||
EncodeLanguage<Language_Spanish>,
|
||||
EncodeLanguage<Language_Chinese>,
|
||||
EncodeLanguage<Language_Korean>,
|
||||
EncodeLanguage<Language_Dutch>,
|
||||
EncodeLanguage<Language_Portuguese>,
|
||||
EncodeLanguage<Language_Russian>,
|
||||
EncodeLanguage<Language_Taiwanese>,
|
||||
EncodeLanguage<Language_BritishEnglish>,
|
||||
EncodeLanguage<Language_CanadianFrench>,
|
||||
EncodeLanguage<Language_LatinAmericanSpanish>,
|
||||
EncodeLanguage<Language_Japanese>(),
|
||||
EncodeLanguage<Language_AmericanEnglish>(),
|
||||
EncodeLanguage<Language_French>(),
|
||||
EncodeLanguage<Language_German>(),
|
||||
EncodeLanguage<Language_Italian>(),
|
||||
EncodeLanguage<Language_Spanish>(),
|
||||
EncodeLanguage<Language_Chinese>(),
|
||||
EncodeLanguage<Language_Korean>(),
|
||||
EncodeLanguage<Language_Dutch>(),
|
||||
EncodeLanguage<Language_Portuguese>(),
|
||||
EncodeLanguage<Language_Russian>(),
|
||||
EncodeLanguage<Language_Taiwanese>(),
|
||||
EncodeLanguage<Language_BritishEnglish>(),
|
||||
EncodeLanguage<Language_CanadianFrench>(),
|
||||
EncodeLanguage<Language_LatinAmericanSpanish>(),
|
||||
/* 4.0.0+ */
|
||||
EncodeLanguage<Language_SimplifiedChinese>,
|
||||
EncodeLanguage<Language_TraditionalChinese>,
|
||||
EncodeLanguage<Language_SimplifiedChinese>(),
|
||||
EncodeLanguage<Language_TraditionalChinese>(),
|
||||
};
|
||||
return EncodedLanguages[language];
|
||||
}
|
||||
|
||||
@@ -54,11 +54,11 @@ namespace ams::sf::cmif {
|
||||
|
||||
void DisposeImpl();
|
||||
|
||||
virtual void AddReference() {
|
||||
virtual void AddReference() override {
|
||||
ServiceObjectImplBase2::AddReferenceImpl();
|
||||
}
|
||||
|
||||
virtual void Release() {
|
||||
virtual void Release() override {
|
||||
if (ServiceObjectImplBase2::ReleaseImpl()) {
|
||||
this->DisposeImpl();
|
||||
}
|
||||
|
||||
@@ -53,16 +53,32 @@ namespace ams::sf::cmif {
|
||||
u32 cmd_id;
|
||||
Result (*handler)(CmifOutHeader **out_header_ptr, ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data);
|
||||
|
||||
constexpr inline bool Matches(u32 cmd_id, hos::Version hosver) const {
|
||||
constexpr inline bool MatchesVersion(hos::Version hosver) const {
|
||||
const bool min_valid = this->hosver_low == hos::Version_Min;
|
||||
const bool max_valid = this->hosver_high == hos::Version_Max;
|
||||
|
||||
return this->cmd_id == cmd_id && (min_valid || this->hosver_low <= hosver) && (max_valid || hosver <= this->hosver_high);
|
||||
return (min_valid || this->hosver_low <= hosver) && (max_valid || hosver <= this->hosver_high);
|
||||
}
|
||||
|
||||
constexpr inline bool Matches(u32 cmd_id, hos::Version hosver) const {
|
||||
return this->cmd_id == cmd_id && this->MatchesVersion(hosver);
|
||||
}
|
||||
|
||||
constexpr inline decltype(handler) GetHandler() const {
|
||||
return this->handler;
|
||||
}
|
||||
|
||||
constexpr inline bool operator>(const ServiceCommandMeta &rhs) const {
|
||||
if (this->cmd_id > rhs.cmd_id) {
|
||||
return true;
|
||||
} else if (this->cmd_id == rhs.cmd_id && this->hosver_low > rhs.hosver_low) {
|
||||
return true;
|
||||
} else if (this->cmd_id == rhs.cmd_id && this->hosver_low == rhs.hosver_low && this->hosver_high == rhs.hosver_high){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(util::is_pod<ServiceCommandMeta>::value && sizeof(ServiceCommandMeta) == 0x18, "sizeof(ServiceCommandMeta)");
|
||||
|
||||
|
||||
@@ -85,16 +85,41 @@ namespace ams::sf::impl {
|
||||
constexpr const auto &BaseEntries = ::ams::sf::cmif::ServiceDispatchTraits<BASECLASS>::DispatchTable.GetEntries(); \
|
||||
constexpr size_t BaseSize = BaseEntries.size(); \
|
||||
\
|
||||
std::array<::ams::sf::cmif::ServiceCommandMeta, BaseSize + CurSize> combined_entries{}; \
|
||||
for (size_t i = 0; i < BaseSize; ++i) { \
|
||||
combined_entries[i] = BaseEntries[i]; \
|
||||
constexpr size_t CombinedSize = BaseSize + CurSize; \
|
||||
\
|
||||
std::array<size_t, CombinedSize> map{}; \
|
||||
for (size_t i = 0; i < CombinedSize; ++i) { map[i] = i; } \
|
||||
\
|
||||
for (size_t i = 1; i < CombinedSize; ++i) { \
|
||||
size_t j = i; \
|
||||
while (j > 0) { \
|
||||
const auto li = map[j]; \
|
||||
const auto ri = map[j - 1]; \
|
||||
\
|
||||
const auto &lhs = (li < BaseSize) ? BaseEntries[li] : cur_entries[li - BaseSize]; \
|
||||
const auto &rhs = (ri < BaseSize) ? BaseEntries[ri] : cur_entries[ri - BaseSize]; \
|
||||
\
|
||||
if (!(rhs > lhs)) { \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
std::swap(map[j], map[j - 1]); \
|
||||
\
|
||||
--j; \
|
||||
} \
|
||||
} \
|
||||
for (size_t i = 0; i < CurSize; ++i) { \
|
||||
combined_entries[BaseSize + i] = cur_entries[i]; \
|
||||
\
|
||||
std::array<::ams::sf::cmif::ServiceCommandMeta, CombinedSize> combined_entries{}; \
|
||||
for (size_t i = 0; i < CombinedSize; ++i) { \
|
||||
if (map[i] < BaseSize) { \
|
||||
combined_entries[i] = BaseEntries[map[i]]; \
|
||||
} else { \
|
||||
combined_entries[i] = cur_entries[map[i] - BaseSize]; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return ::ams::sf::cmif::ServiceDispatchTable { combined_entries }; \
|
||||
} () \
|
||||
}() \
|
||||
}; \
|
||||
};
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace ams::sf {
|
||||
return lmem::FreeToExpHeap(m_handle, buffer);
|
||||
}
|
||||
|
||||
virtual bool IsEqualImpl(const MemoryResource &resource) const {
|
||||
virtual bool IsEqualImpl(const MemoryResource &resource) const override {
|
||||
return this == std::addressof(resource);
|
||||
}
|
||||
};
|
||||
@@ -76,9 +76,9 @@ namespace ams::sf {
|
||||
return lmem::FreeToUnitHeap(m_handle, buffer);
|
||||
}
|
||||
|
||||
virtual bool IsEqualImpl(const MemoryResource &resource) const {
|
||||
virtual bool IsEqualImpl(const MemoryResource &resource) const override {
|
||||
return this == std::addressof(resource);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,9 @@ namespace ams::sf {
|
||||
return m_standard_allocator->Free(buffer);
|
||||
}
|
||||
|
||||
virtual bool IsEqualImpl(const MemoryResource &resource) const {
|
||||
virtual bool IsEqualImpl(const MemoryResource &resource) const override {
|
||||
return this == std::addressof(resource);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace ams::sf {
|
||||
public:
|
||||
class Object;
|
||||
using Allocator = StatelessDummyAllocator;
|
||||
using StatelessAllocator = typename Policy::StatelessAllocator<Object>;
|
||||
using StatelessAllocator = typename Policy::template StatelessAllocator<Object>;
|
||||
|
||||
class Object final : private ::ams::sf::impl::ServiceObjectImplBase2, public Base {
|
||||
NON_COPYABLE(Object);
|
||||
|
||||
@@ -40,8 +40,8 @@ namespace ams::sm {
|
||||
return out;
|
||||
}
|
||||
|
||||
static constexpr ServiceName Encode(const char *name) {
|
||||
return Encode(name, std::strlen(name));
|
||||
static constexpr ServiceName Encode(util::string_view name) {
|
||||
return Encode(name.data(), name.size());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace ams::time {
|
||||
AMS_ASSERT(rhs.IsValid());
|
||||
}
|
||||
|
||||
constexpr auto ToUint64 = [] ALWAYS_INLINE_LAMBDA (const time::CalendarTime &time) {
|
||||
constexpr auto ToUint64 = [](const time::CalendarTime &time) ALWAYS_INLINE_LAMBDA {
|
||||
return (static_cast<u64>(time.year) << 40) |
|
||||
(static_cast<u64>(time.month) << 32) |
|
||||
(static_cast<u64>(time.day) << 24) |
|
||||
|
||||
@@ -40,12 +40,28 @@ namespace ams::tipc::impl {
|
||||
#define AMS_TIPC_IMPL_EXTRACT_SYNC_METHOD_ARGUMENTS(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
||||
using NAME##ArgumentsType = ::ams::tipc::impl::SyncFunctionArgsType<&NAME##ArgumentsFunctionHolder::f>;
|
||||
|
||||
#define AMS_TIPC_IMPL_GET_MAXIMUM_REQUEST_SIZE(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
||||
, ::ams::tipc::impl::CommandMetaInfo<CMD_ID + 0x10, NAME##ArgumentsType>::InMessageTotalSize
|
||||
|
||||
#define AMS_TIPC_IMPL_GET_MAXIMUM_RESPONSE_SIZE(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
||||
, ::ams::tipc::impl::CommandMetaInfo<CMD_ID + 0x10, NAME##ArgumentsType>::OutMessageTotalSize
|
||||
|
||||
#define AMS_TIPC_IMPL_DEFINE_INTERFACE(BASECLASS, CLASSNAME, CMD_MACRO) \
|
||||
class CLASSNAME : public BASECLASS { \
|
||||
private: \
|
||||
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_DEFINE_SYNC_METHOD_HOLDER) \
|
||||
public: \
|
||||
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_EXTRACT_SYNC_METHOD_ARGUMENTS) \
|
||||
public: \
|
||||
static constexpr size_t MaximumRequestSize = std::max<size_t>({ \
|
||||
0 \
|
||||
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_GET_MAXIMUM_REQUEST_SIZE) \
|
||||
}); \
|
||||
\
|
||||
static constexpr size_t MaximumResponseSize = std::max<size_t>({ \
|
||||
0 \
|
||||
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_GET_MAXIMUM_RESPONSE_SIZE) \
|
||||
}); \
|
||||
};
|
||||
|
||||
#define AMS_TIPC_IMPL_DEFINE_CONCEPT_HELPERS(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
||||
|
||||
@@ -319,6 +319,9 @@ namespace ams::tipc::impl {
|
||||
static constexpr auto OutMessageResultIndex = svc::ipc::MessageBuffer::GetRawDataIndex(OutMessageHeader, OutSpecialHeader);
|
||||
static constexpr auto OutMessageRawDataIndex = OutMessageResultIndex + 1;
|
||||
|
||||
static constexpr size_t InMessageTotalSize = (InMessageRawDataIndex * sizeof(u32)) + InDataSize;
|
||||
static constexpr size_t OutMessageTotalSize = (OutMessageRawDataIndex * sizeof(u32)) + OutDataSize;
|
||||
|
||||
/* Construction of argument serialization structs. */
|
||||
private:
|
||||
template<typename>
|
||||
@@ -472,7 +475,7 @@ namespace ams::tipc::impl {
|
||||
using OutRawHolderType = OutRawHolder<CommandMeta::OutDataSize, CommandMeta::OutDataAlign, CommandMeta::OutMessageRawDataIndex>;
|
||||
using OutHandleHolderType = OutHandleHolder<CommandMeta::NumOutMoveHandles, CommandMeta::NumOutCopyHandles, CommandMeta::OutMessageHandleIndex>;
|
||||
private:
|
||||
static consteval u64 GetMessageHeaderForCheck(const svc::ipc::MessageBuffer::MessageHeader &header) {
|
||||
static constexpr u64 GetMessageHeaderForCheck(const svc::ipc::MessageBuffer::MessageHeader &header) {
|
||||
using Value = util::BitPack32::Field<0, BITSIZEOF(util::BitPack32)>;
|
||||
|
||||
const util::BitPack32 *data = header.GetData();
|
||||
@@ -482,7 +485,7 @@ namespace ams::tipc::impl {
|
||||
return static_cast<u64>(lower) | (static_cast<u64>(upper) << BITSIZEOF(u32));
|
||||
}
|
||||
|
||||
static consteval u32 GetSpecialHeaderForCheck(const svc::ipc::MessageBuffer::SpecialHeader &header) {
|
||||
static constexpr u32 GetSpecialHeaderForCheck(const svc::ipc::MessageBuffer::SpecialHeader &header) {
|
||||
using Value = util::BitPack32::Field<0, BITSIZEOF(util::BitPack32)>;
|
||||
|
||||
return header.GetHeader()->Get<Value>();
|
||||
|
||||
@@ -44,16 +44,17 @@ namespace ams::tipc {
|
||||
|
||||
}
|
||||
|
||||
class DeferrableBase : public impl::DeferrableBaseTag {
|
||||
class DeferrableBaseImpl : public impl::DeferrableBaseTag {
|
||||
private:
|
||||
DeferralManagerBase *m_deferral_manager;
|
||||
ObjectHolder m_object_holder;
|
||||
uintptr_t m_resume_key;
|
||||
u8 m_message_buffer[svc::ipc::MessageBufferSize];
|
||||
const u32 m_message_buffer_size;
|
||||
u8 m_message_buffer_base[0];
|
||||
public:
|
||||
ALWAYS_INLINE DeferrableBase() : m_deferral_manager(nullptr), m_object_holder(), m_resume_key() { /* ... */ }
|
||||
ALWAYS_INLINE DeferrableBaseImpl(u32 mb_size) : m_deferral_manager(nullptr), m_object_holder(), m_resume_key(), m_message_buffer_size(mb_size) { /* ... */ }
|
||||
|
||||
~DeferrableBase();
|
||||
~DeferrableBaseImpl();
|
||||
|
||||
ALWAYS_INLINE void SetDeferralManager(DeferralManagerBase *manager, os::NativeHandle reply_target, ServiceObjectBase *object) {
|
||||
m_deferral_manager = manager;
|
||||
@@ -67,7 +68,7 @@ namespace ams::tipc {
|
||||
template<IsResumeKey ResumeKey>
|
||||
ALWAYS_INLINE void RegisterRetry(ResumeKey key) {
|
||||
m_resume_key = ConvertToInternalResumeKey(key);
|
||||
std::memcpy(m_message_buffer, svc::ipc::GetMessageBuffer(), sizeof(m_message_buffer));
|
||||
std::memcpy(m_message_buffer_base, svc::ipc::GetMessageBuffer(), m_message_buffer_size);
|
||||
}
|
||||
|
||||
template<IsResumeKey ResumeKey, typename F>
|
||||
@@ -85,11 +86,49 @@ namespace ams::tipc {
|
||||
m_resume_key = 0;
|
||||
|
||||
/* Restore message buffer. */
|
||||
std::memcpy(svc::ipc::GetMessageBuffer(), m_message_buffer, sizeof(m_message_buffer));
|
||||
std::memcpy(svc::ipc::GetMessageBuffer(), m_message_buffer_base, m_message_buffer_size);
|
||||
|
||||
/* Process the request. */
|
||||
return port_manager->ProcessDeferredRequest(m_object_holder);
|
||||
}
|
||||
protected:
|
||||
static consteval size_t GetMessageBufferOffsetBase();
|
||||
};
|
||||
static_assert(std::is_standard_layout<DeferrableBaseImpl>::value);
|
||||
|
||||
template<size_t _MessageBufferRequiredSize>
|
||||
class DeferrableBaseImplWithBuffer : public DeferrableBaseImpl {
|
||||
private:
|
||||
static constexpr size_t MessageBufferRequiredSize = _MessageBufferRequiredSize;
|
||||
private:
|
||||
u8 m_message_buffer[MessageBufferRequiredSize];
|
||||
public:
|
||||
DeferrableBaseImplWithBuffer();
|
||||
private:
|
||||
static consteval size_t GetMessageBufferOffset();
|
||||
};
|
||||
|
||||
consteval size_t DeferrableBaseImpl::GetMessageBufferOffsetBase() {
|
||||
return AMS_OFFSETOF(DeferrableBaseImpl, m_message_buffer_base);
|
||||
}
|
||||
|
||||
template<size_t _MessageBufferRequiredSize>
|
||||
consteval size_t DeferrableBaseImplWithBuffer<_MessageBufferRequiredSize>::GetMessageBufferOffset() {
|
||||
return AMS_OFFSETOF(DeferrableBaseImplWithBuffer<_MessageBufferRequiredSize>, m_message_buffer);
|
||||
}
|
||||
|
||||
template<size_t _MessageBufferRequiredSize>
|
||||
ALWAYS_INLINE DeferrableBaseImplWithBuffer<_MessageBufferRequiredSize>::DeferrableBaseImplWithBuffer() : DeferrableBaseImpl(MessageBufferRequiredSize) {
|
||||
static_assert(GetMessageBufferOffsetBase() == GetMessageBufferOffset());
|
||||
static_assert(sizeof(DeferrableBaseImplWithBuffer<_MessageBufferRequiredSize>) >= sizeof(DeferrableBaseImpl) + MessageBufferRequiredSize);
|
||||
}
|
||||
|
||||
template<typename Interface, size_t MaximumDefaultRequestSize = 0>
|
||||
class DeferrableBase : public DeferrableBaseImplWithBuffer<std::max(Interface::MaximumRequestSize, MaximumDefaultRequestSize)> {
|
||||
private:
|
||||
using BaseImpl = DeferrableBaseImplWithBuffer<std::max(Interface::MaximumRequestSize, MaximumDefaultRequestSize)>;
|
||||
public:
|
||||
using BaseImpl::BaseImpl;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
@@ -100,11 +139,11 @@ namespace ams::tipc {
|
||||
NON_MOVEABLE(DeferralManagerBase);
|
||||
private:
|
||||
size_t m_object_count;
|
||||
DeferrableBase *m_objects_base[0];
|
||||
DeferrableBaseImpl *m_objects_base[0];
|
||||
public:
|
||||
ALWAYS_INLINE DeferralManagerBase() : m_object_count(0) { /* ... */ }
|
||||
|
||||
void AddObject(DeferrableBase &object, os::NativeHandle reply_target, ServiceObjectBase *service_object) {
|
||||
void AddObject(DeferrableBaseImpl &object, os::NativeHandle reply_target, ServiceObjectBase *service_object) {
|
||||
/* Set ourselves as the manager for the object. */
|
||||
object.SetDeferralManager(this, reply_target, service_object);
|
||||
|
||||
@@ -113,7 +152,7 @@ namespace ams::tipc {
|
||||
m_objects_base[m_object_count++] = std::addressof(object);
|
||||
}
|
||||
|
||||
void RemoveObject(DeferrableBase *object) {
|
||||
void RemoveObject(DeferrableBaseImpl *object) {
|
||||
/* If the object is present, remove it. */
|
||||
for (size_t i = 0; i < m_object_count; ++i) {
|
||||
if (m_objects_base[i] == object) {
|
||||
@@ -148,7 +187,7 @@ namespace ams::tipc {
|
||||
};
|
||||
static_assert(std::is_standard_layout<DeferralManagerBase>::value);
|
||||
|
||||
inline DeferrableBase::~DeferrableBase() {
|
||||
inline DeferrableBaseImpl::~DeferrableBaseImpl() {
|
||||
AMS_ASSUME(m_deferral_manager != nullptr);
|
||||
m_deferral_manager->RemoveObject(this);
|
||||
}
|
||||
@@ -156,7 +195,7 @@ namespace ams::tipc {
|
||||
template<size_t N> requires (N > 0)
|
||||
class DeferralManager final : public DeferralManagerBase {
|
||||
private:
|
||||
DeferrableBase *m_objects[N];
|
||||
DeferrableBaseImpl *m_objects[N];
|
||||
public:
|
||||
DeferralManager();
|
||||
private:
|
||||
@@ -167,15 +206,15 @@ namespace ams::tipc {
|
||||
return AMS_OFFSETOF(DeferralManagerBase, m_objects_base);
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
template<size_t N> requires (N > 0)
|
||||
consteval size_t DeferralManager<N>::GetObjectPointersOffset() {
|
||||
return AMS_OFFSETOF(DeferralManager<N>, m_objects);
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
template<size_t N> requires (N > 0)
|
||||
inline DeferralManager<N>::DeferralManager() : DeferralManagerBase() {
|
||||
static_assert(GetObjectPointersOffset() == GetObjectPointersOffsetBase());
|
||||
static_assert(sizeof(DeferralManager<N>) == sizeof(DeferralManagerBase) + N * sizeof(DeferrableBase *));
|
||||
static_assert(sizeof(DeferralManager<N>) == sizeof(DeferralManagerBase) + N * sizeof(DeferrableBaseImpl *));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -686,7 +686,7 @@ namespace ams::tipc {
|
||||
}
|
||||
|
||||
/* Try to reply/receive. */
|
||||
const Result result = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||
const Result result = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||
os::MultiWaitHolderType *signaled_holder = nullptr;
|
||||
ON_SCOPE_EXIT { AMS_ABORT_UNLESS(signaled_holder == nullptr); };
|
||||
return m_object_manager->ReplyAndReceive(std::addressof(signaled_holder), out_object, reply_target, std::addressof(m_multi_wait));
|
||||
@@ -747,7 +747,7 @@ namespace ams::tipc {
|
||||
using PortManager = PortManagerImpl;
|
||||
private:
|
||||
PortManager m_port_manager;
|
||||
PortInfo::Allocator m_port_allocator;
|
||||
typename PortInfo::Allocator m_port_allocator;
|
||||
public:
|
||||
constexpr ServerManagerImpl() : m_port_manager(), m_port_allocator() { /* ... */ }
|
||||
|
||||
|
||||
@@ -18,41 +18,28 @@
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/os/os_sdk_mutex.hpp>
|
||||
|
||||
#define AMS_SINGLETON_TRAITS(_CLASSNAME_) \
|
||||
private: \
|
||||
NON_COPYABLE(_CLASSNAME_); \
|
||||
NON_MOVEABLE(_CLASSNAME_); \
|
||||
private: \
|
||||
_CLASSNAME_ (); \
|
||||
public: \
|
||||
static _CLASSNAME_ &GetInstance() { \
|
||||
/* Declare singleton instance variables. */ \
|
||||
static constinit ::ams::util::TypedStorage<_CLASSNAME_> s_singleton_storage; \
|
||||
static constinit ::ams::os::SdkMutex s_singleton_mutex; \
|
||||
static constinit bool s_initialized_singleton = false; \
|
||||
\
|
||||
/* Ensure the instance is created. */ \
|
||||
if (AMS_UNLIKELY(!s_initialized_singleton)) { \
|
||||
std::scoped_lock lk(s_singleton_mutex); \
|
||||
\
|
||||
if (AMS_LIKELY(!s_initialized_singleton)) { \
|
||||
new (::ams::util::GetPointer(s_singleton_storage)) _CLASSNAME_; \
|
||||
s_initialized_singleton = true; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return ::ams::util::GetReference(s_singleton_storage); \
|
||||
#define AMS_SINGLETON_TRAITS(_CLASSNAME_) \
|
||||
private: \
|
||||
NON_COPYABLE(_CLASSNAME_); \
|
||||
NON_MOVEABLE(_CLASSNAME_); \
|
||||
private: \
|
||||
_CLASSNAME_ (); \
|
||||
public: \
|
||||
static _CLASSNAME_ &GetInstance() { \
|
||||
AMS_FUNCTION_LOCAL_STATIC(_CLASSNAME_, s_singleton_instance); \
|
||||
\
|
||||
return s_singleton_instance; \
|
||||
}
|
||||
|
||||
#define AMS_CONSTINIT_SINGLETON_TRAITS(_CLASSNAME_) \
|
||||
private: \
|
||||
NON_COPYABLE(_CLASSNAME_); \
|
||||
NON_MOVEABLE(_CLASSNAME_); \
|
||||
private: \
|
||||
constexpr _CLASSNAME_ () = default; \
|
||||
public: \
|
||||
static _CLASSNAME_ &GetInstance() { \
|
||||
/* Declare singleton instance variables. */ \
|
||||
static constinit _CLASSNAME_ s_singleton_instance; \
|
||||
return s_singleton_instance; \
|
||||
#define AMS_CONSTINIT_SINGLETON_TRAITS(_CLASSNAME_) \
|
||||
private: \
|
||||
NON_COPYABLE(_CLASSNAME_); \
|
||||
NON_MOVEABLE(_CLASSNAME_); \
|
||||
private: \
|
||||
constexpr _CLASSNAME_ () = default; \
|
||||
public: \
|
||||
static _CLASSNAME_ &GetInstance() { \
|
||||
/* Declare singleton instance variables. */ \
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(_CLASSNAME_, s_singleton_instance); \
|
||||
return s_singleton_instance; \
|
||||
}
|
||||
|
||||
@@ -73,18 +73,7 @@ namespace ams::erpt::srv {
|
||||
}
|
||||
|
||||
bool IsProductionMode() {
|
||||
static constinit bool s_initialized = false;
|
||||
static constinit bool s_is_prod_mode = true;
|
||||
static constinit os::SdkMutex s_mutex;
|
||||
|
||||
if (AMS_UNLIKELY(!s_initialized)) {
|
||||
std::scoped_lock lk(s_mutex);
|
||||
|
||||
if (AMS_LIKELY(!s_initialized)) {
|
||||
s_is_prod_mode = IsProductionModeImpl();
|
||||
s_initialized = true;
|
||||
}
|
||||
}
|
||||
AMS_FUNCTION_LOCAL_STATIC(bool, s_is_prod_mode, IsProductionModeImpl());
|
||||
|
||||
return s_is_prod_mode;
|
||||
}
|
||||
|
||||
@@ -112,13 +112,13 @@ namespace ams::fssystem {
|
||||
NcaCryptoConfiguration g_nca_crypto_configuration_prod;
|
||||
|
||||
constexpr inline s32 KeySlotCacheEntryCount = 3;
|
||||
KeySlotCache g_key_slot_cache;
|
||||
util::optional<KeySlotCacheEntry> g_key_slot_cache_entry[KeySlotCacheEntryCount];
|
||||
constinit KeySlotCache g_key_slot_cache;
|
||||
constinit util::optional<KeySlotCacheEntry> g_key_slot_cache_entry[KeySlotCacheEntryCount];
|
||||
|
||||
spl::AccessKey &GetNcaKekAccessKey(s32 key_type) {
|
||||
static spl::AccessKey s_nca_kek_access_key_array[KeyAreaEncryptionKeyCount] = {};
|
||||
static spl::AccessKey s_nca_header_kek_access_key = {};
|
||||
static spl::AccessKey s_invalid_nca_kek_access_key = {};
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(spl::AccessKey, s_nca_kek_access_key_array[KeyAreaEncryptionKeyCount]);
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(spl::AccessKey, s_nca_header_kek_access_key);
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(spl::AccessKey, s_invalid_nca_kek_access_key);
|
||||
|
||||
if (key_type > static_cast<s32>(KeyType::NcaHeaderKey) || IsInvalidKeyTypeValue(key_type)) {
|
||||
return s_invalid_nca_kek_access_key;
|
||||
|
||||
@@ -26,42 +26,20 @@ namespace ams::gpio::driver::impl {
|
||||
alignas(os::MemoryPageSize) u8 g_interrupt_thread_stack[InterruptThreadStackSize];
|
||||
|
||||
gpio::driver::IGpioDriver::List &GetGpioDriverList() {
|
||||
static constinit gpio::driver::IGpioDriver::List s_gpio_driver_list;
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(gpio::driver::IGpioDriver::List, s_gpio_driver_list);
|
||||
return s_gpio_driver_list;
|
||||
}
|
||||
|
||||
ddsf::EventHandlerManager &GetInterruptHandlerManager() {
|
||||
static constinit util::TypedStorage<ddsf::EventHandlerManager> s_interrupt_handler_manager;
|
||||
static constinit bool s_initialized = false;
|
||||
static constinit os::SdkMutex s_mutex;
|
||||
AMS_FUNCTION_LOCAL_STATIC(ddsf::EventHandlerManager, s_interrupt_handler_manager);
|
||||
|
||||
if (AMS_UNLIKELY(!s_initialized)) {
|
||||
std::scoped_lock lk(s_mutex);
|
||||
|
||||
if (AMS_LIKELY(!s_initialized)) {
|
||||
util::ConstructAt(s_interrupt_handler_manager);
|
||||
s_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
return util::GetReference(s_interrupt_handler_manager);
|
||||
return s_interrupt_handler_manager;
|
||||
}
|
||||
|
||||
ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() {
|
||||
static constinit util::TypedStorage<ddsf::DeviceCodeEntryManager> s_device_code_entry_manager;
|
||||
static constinit bool s_initialized = false;
|
||||
static constinit os::SdkMutex s_mutex;
|
||||
AMS_FUNCTION_LOCAL_STATIC(ddsf::DeviceCodeEntryManager, s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
|
||||
|
||||
if (AMS_UNLIKELY(!s_initialized)) {
|
||||
std::scoped_lock lk(s_mutex);
|
||||
|
||||
if (AMS_LIKELY(!s_initialized)) {
|
||||
util::ConstructAt(s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
|
||||
s_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
return util::GetReference(s_device_code_entry_manager);
|
||||
return s_device_code_entry_manager;
|
||||
}
|
||||
|
||||
void InterruptThreadFunction(void *arg) {
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace ams::htclow::ctrl {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
auto IsSupportedServiceChannel = [] ALWAYS_INLINE_LAMBDA (const impl::ChannelInternalType &channel, const impl::ChannelInternalType *supported, int num_supported) -> bool {
|
||||
auto IsSupportedServiceChannel = [](const impl::ChannelInternalType &channel, const impl::ChannelInternalType *supported, int num_supported) ALWAYS_INLINE_LAMBDA -> bool {
|
||||
for (auto i = 0; i < num_supported; ++i) {
|
||||
if (channel.module_id == supported[i].module_id && channel.channel_id == supported[i].channel_id) {
|
||||
return true;
|
||||
|
||||
@@ -95,16 +95,17 @@ namespace ams::i2c::driver::board::nintendo::nx {
|
||||
}
|
||||
}
|
||||
|
||||
constinit util::TypedStorage<impl::I2cBusAccessorManager> g_bus_accessor_manager;
|
||||
constinit util::TypedStorage<impl::I2cDevicePropertyManager> g_device_manager;
|
||||
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
static constinit util::TypedStorage<impl::I2cBusAccessorManager> s_bus_accessor_manager;
|
||||
static constinit util::TypedStorage<impl::I2cDevicePropertyManager> s_device_manager;
|
||||
/* Initialize managers. */
|
||||
util::ConstructAt(g_bus_accessor_manager, ddsf::GetMemoryResource());
|
||||
util::ConstructAt(g_device_manager, ddsf::GetMemoryResource());
|
||||
|
||||
util::ConstructAt(s_bus_accessor_manager, ddsf::GetMemoryResource());
|
||||
util::ConstructAt(s_device_manager, ddsf::GetMemoryResource());
|
||||
|
||||
return Initialize(util::GetReference(s_bus_accessor_manager), util::GetReference(s_device_manager));
|
||||
return Initialize(util::GetReference(g_bus_accessor_manager), util::GetReference(g_device_manager));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,25 +24,14 @@ namespace ams::i2c::driver::impl {
|
||||
constinit int g_init_count = 0;
|
||||
|
||||
i2c::driver::II2cDriver::List &GetI2cDriverList() {
|
||||
static constinit i2c::driver::II2cDriver::List s_driver_list;
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(i2c::driver::II2cDriver::List, s_driver_list);
|
||||
return s_driver_list;
|
||||
}
|
||||
|
||||
ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() {
|
||||
static constinit util::TypedStorage<ddsf::DeviceCodeEntryManager> s_device_code_entry_manager;
|
||||
static constinit bool s_initialized = false;
|
||||
static constinit os::SdkMutex s_mutex;
|
||||
AMS_FUNCTION_LOCAL_STATIC(ddsf::DeviceCodeEntryManager, s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
|
||||
|
||||
if (AMS_UNLIKELY(!s_initialized)) {
|
||||
std::scoped_lock lk(s_mutex);
|
||||
|
||||
if (AMS_LIKELY(!s_initialized)) {
|
||||
util::ConstructAt(s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
|
||||
s_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
return util::GetReference(s_device_code_entry_manager);
|
||||
return s_device_code_entry_manager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ namespace ams::lm::srv {
|
||||
}
|
||||
|
||||
EventLogTransmitter &EventLogTransmitter::GetDefaultInstance() {
|
||||
static constinit EventLogTransmitter s_default_event_log_transmitter(DefaultFlushFunction);
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(EventLogTransmitter, s_default_event_log_transmitter, DefaultFlushFunction);
|
||||
|
||||
return s_default_event_log_transmitter;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,8 +63,10 @@ namespace ams::lm::srv {
|
||||
}
|
||||
|
||||
bool DefaultFlushFunction(const u8 *data, size_t size) {
|
||||
/* Declare persistent clock-updated state storage. */
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_is_user_system_clock_updated, false);
|
||||
|
||||
/* Update clock. */
|
||||
static constinit bool s_is_user_system_clock_updated = false;
|
||||
if (!s_is_user_system_clock_updated) {
|
||||
UpdateUserSystemClock(data, size);
|
||||
s_is_user_system_clock_updated = true;
|
||||
@@ -84,8 +86,8 @@ namespace ams::lm::srv {
|
||||
}
|
||||
|
||||
LogBuffer &LogBuffer::GetDefaultInstance() {
|
||||
static constinit u8 s_default_buffers[128_KB * 2];
|
||||
static constinit LogBuffer s_default_log_buffer(s_default_buffers, sizeof(s_default_buffers), DefaultFlushFunction);
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(u8, s_default_buffers[128_KB * 2]);
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(LogBuffer, s_default_log_buffer, s_default_buffers, sizeof(s_default_buffers), DefaultFlushFunction);
|
||||
|
||||
return s_default_log_buffer;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
namespace ams::lm::srv {
|
||||
|
||||
CustomSinkBuffer &LogGetterImpl::GetBuffer() {
|
||||
static constinit u8 s_buffer[32_KB];
|
||||
static constinit CustomSinkBuffer s_custom_sink_buffer(s_buffer, sizeof(s_buffer), FlushFunction);
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(u8, s_buffer[32_KB]);
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(CustomSinkBuffer, s_custom_sink_buffer, s_buffer, sizeof(s_buffer), FlushFunction);
|
||||
|
||||
return s_custom_sink_buffer;
|
||||
}
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ namespace ams::lm::srv {
|
||||
size_t carry_size;
|
||||
bool ends_with_text_log;
|
||||
};
|
||||
static constinit PreviousPacketContext s_previous_packet_context = {};
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(PreviousPacketContext, s_previous_packet_context);
|
||||
|
||||
/* Get the packet header. */
|
||||
auto *header = static_cast<const impl::LogPacketHeader *>(buffer);
|
||||
|
||||
@@ -308,7 +308,7 @@ namespace ams::ncm {
|
||||
out_orphaned[i] = true;
|
||||
}
|
||||
|
||||
auto IsOrphanedContent = [] ALWAYS_INLINE_LAMBDA (const sf::InArray<ContentId> &list, const ncm::ContentId &id) -> util::optional<size_t> {
|
||||
auto IsOrphanedContent = [](const sf::InArray<ContentId> &list, const ncm::ContentId &id) ALWAYS_INLINE_LAMBDA -> util::optional<size_t> {
|
||||
/* Check if any input content ids match our found content id. */
|
||||
for (size_t i = 0; i < list.GetSize(); i++) {
|
||||
if (list[i] == id) {
|
||||
|
||||
@@ -635,7 +635,7 @@ namespace ams::ncm {
|
||||
for (s32 i = 0; i < count; i++) {
|
||||
R_UNLESS(!this->IsCancelRequested(), ncm::ResultCreatePlaceHolderCancelled());
|
||||
|
||||
static constinit os::SdkMutex s_placeholder_mutex;
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(os::SdkMutex, s_placeholder_mutex);
|
||||
std::scoped_lock lk(s_placeholder_mutex);
|
||||
|
||||
InstallContentMeta content_meta;
|
||||
|
||||
@@ -44,7 +44,8 @@ namespace ams::ncm {
|
||||
}
|
||||
|
||||
HeapState &GetHeapState() {
|
||||
static HeapState s_heap_state = {};
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(HeapState, s_heap_state);
|
||||
|
||||
return s_heap_state;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,60 +20,44 @@ namespace ams::powctl::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
os::ThreadType g_interrupt_thread;
|
||||
constinit os::ThreadType g_interrupt_thread;
|
||||
|
||||
constexpr inline size_t InterruptThreadStackSize = os::MemoryPageSize;
|
||||
alignas(os::MemoryPageSize) u8 g_interrupt_thread_stack[InterruptThreadStackSize];
|
||||
|
||||
constinit u8 g_unit_heap_memory[2_KB];
|
||||
constinit lmem::HeapHandle g_unit_heap_handle;
|
||||
constinit sf::UnitHeapMemoryResource g_unit_heap_memory_resource;
|
||||
|
||||
IPowerControlDriver::List &GetDriverList() {
|
||||
static constinit IPowerControlDriver::List s_driver_list;
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(IPowerControlDriver::List, s_driver_list);
|
||||
return s_driver_list;
|
||||
}
|
||||
|
||||
ddsf::EventHandlerManager &GetInterruptHandlerManager() {
|
||||
static constinit util::TypedStorage<ddsf::EventHandlerManager> s_interrupt_handler_manager;
|
||||
static constinit bool s_initialized = false;
|
||||
static constinit os::SdkMutex s_mutex;
|
||||
AMS_FUNCTION_LOCAL_STATIC(ddsf::EventHandlerManager, s_interrupt_handler_manager);
|
||||
|
||||
if (AMS_UNLIKELY(!s_initialized)) {
|
||||
std::scoped_lock lk(s_mutex);
|
||||
|
||||
if (AMS_LIKELY(!s_initialized)) {
|
||||
util::ConstructAt(s_interrupt_handler_manager);
|
||||
s_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
return util::GetReference(s_interrupt_handler_manager);
|
||||
return s_interrupt_handler_manager;
|
||||
}
|
||||
|
||||
ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() {
|
||||
static constinit util::TypedStorage<ddsf::DeviceCodeEntryManager> s_device_code_entry_manager;
|
||||
static constinit bool s_initialized = false;
|
||||
static constinit os::SdkMutex s_mutex;
|
||||
class DeviceCodeEntryManagerWithUnitHeap {
|
||||
private:
|
||||
u8 m_heap_memory[2_KB];
|
||||
sf::UnitHeapMemoryResource m_memory_resource;
|
||||
util::TypedStorage<ddsf::DeviceCodeEntryManager> m_manager;
|
||||
public:
|
||||
DeviceCodeEntryManagerWithUnitHeap() {
|
||||
/* Initialize the memory resource. */
|
||||
m_memory_resource.Attach(lmem::CreateUnitHeap(m_heap_memory, sizeof(m_heap_memory), sizeof(ddsf::DeviceCodeEntryHolder), lmem::CreateOption_ThreadSafe));
|
||||
|
||||
if (AMS_UNLIKELY(!s_initialized)) {
|
||||
std::scoped_lock lk(s_mutex);
|
||||
/* Construct the entry manager. */
|
||||
util::ConstructAt(m_manager, std::addressof(m_memory_resource));
|
||||
}
|
||||
|
||||
if (AMS_LIKELY(!s_initialized)) {
|
||||
/* Initialize the entry code heap. */
|
||||
g_unit_heap_handle = lmem::CreateUnitHeap(g_unit_heap_memory, sizeof(g_unit_heap_memory), sizeof(ddsf::DeviceCodeEntryHolder), lmem::CreateOption_ThreadSafe);
|
||||
ALWAYS_INLINE operator ddsf::DeviceCodeEntryManager &() {
|
||||
return util::GetReference(m_manager);
|
||||
}
|
||||
};
|
||||
AMS_FUNCTION_LOCAL_STATIC(DeviceCodeEntryManagerWithUnitHeap, s_device_code_entry_manager_holder);
|
||||
|
||||
/* Initialize the entry code memory resource. */
|
||||
g_unit_heap_memory_resource.Attach(g_unit_heap_handle);
|
||||
|
||||
/* Make the entry manager using the newly initialized memory resource. */
|
||||
util::ConstructAt(s_device_code_entry_manager, std::addressof(g_unit_heap_memory_resource));
|
||||
|
||||
s_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
return util::GetReference(s_device_code_entry_manager);
|
||||
return s_device_code_entry_manager_holder;
|
||||
}
|
||||
|
||||
void InterruptThreadFunction(void *arg) {
|
||||
|
||||
@@ -24,25 +24,14 @@ namespace ams::pwm::driver::impl {
|
||||
constinit int g_init_count = 0;
|
||||
|
||||
pwm::driver::IPwmDriver::List &GetPwmDriverList() {
|
||||
static constinit pwm::driver::IPwmDriver::List s_driver_list;
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(pwm::driver::IPwmDriver::List, s_driver_list);
|
||||
return s_driver_list;
|
||||
}
|
||||
|
||||
ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() {
|
||||
static constinit util::TypedStorage<ddsf::DeviceCodeEntryManager> s_device_code_entry_manager;
|
||||
static constinit bool s_initialized = false;
|
||||
static constinit os::SdkMutex s_mutex;
|
||||
AMS_FUNCTION_LOCAL_STATIC(ddsf::DeviceCodeEntryManager, s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
|
||||
|
||||
if (AMS_UNLIKELY(!s_initialized)) {
|
||||
std::scoped_lock lk(s_mutex);
|
||||
|
||||
if (AMS_LIKELY(!s_initialized)) {
|
||||
util::ConstructAt(s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
|
||||
s_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
return util::GetReference(s_device_code_entry_manager);
|
||||
return s_device_code_entry_manager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -289,14 +289,8 @@ namespace ams::settings::impl {
|
||||
}
|
||||
|
||||
lmem::HeapHandle &GetHeapHandle() {
|
||||
static constinit bool s_is_initialized = false;
|
||||
static constinit lmem::HeapHandle s_heap_handle;
|
||||
static constinit u8 s_heap_memory[HeapMemorySize];
|
||||
|
||||
if (!s_is_initialized) {
|
||||
s_heap_handle = lmem::CreateExpHeap(s_heap_memory, sizeof(s_heap_memory), lmem::CreateOption_None);
|
||||
s_is_initialized = true;
|
||||
}
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(u8, s_heap_memory[HeapMemorySize]);
|
||||
AMS_FUNCTION_LOCAL_STATIC(lmem::HeapHandle, s_heap_handle, lmem::CreateExpHeap(s_heap_memory, sizeof(s_heap_memory), lmem::CreateOption_ThreadSafe));
|
||||
|
||||
return s_heap_handle;
|
||||
}
|
||||
@@ -314,24 +308,15 @@ namespace ams::settings::impl {
|
||||
AMS_ASSERT(out != nullptr);
|
||||
|
||||
/* Declare static instance variables. */
|
||||
static constinit util::TypedStorage<Map> s_storage = {};
|
||||
static constinit bool s_is_initialized = false;
|
||||
static constinit bool s_is_loaded = false;
|
||||
AMS_FUNCTION_LOCAL_STATIC(Map, s_map);
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_is_map_loaded, false);
|
||||
|
||||
/* Get pointer to the map. */
|
||||
Map *map = util::GetPointer(s_storage);
|
||||
|
||||
/* Construct the map, if we haven't already. */
|
||||
if (AMS_UNLIKELY(!s_is_initialized)) {
|
||||
/* Construct the instance. */
|
||||
util::ConstructAt(s_storage);
|
||||
|
||||
/* Note that we constructed. */
|
||||
s_is_initialized = true;
|
||||
}
|
||||
Map * const map = std::addressof(s_map);
|
||||
|
||||
/* TODO: Mutex? */
|
||||
/* Load the map, if we haven't already. */
|
||||
if (AMS_UNLIKELY(!s_is_loaded)) {
|
||||
if (AMS_UNLIKELY(!s_is_map_loaded)) {
|
||||
/* Attempt to load the map, allowing for failure if acceptable. */
|
||||
const auto result = LoadKeyValueStoreMap(map);
|
||||
|
||||
@@ -340,7 +325,7 @@ namespace ams::settings::impl {
|
||||
}
|
||||
|
||||
/* Note that the map is loaded. */
|
||||
s_is_loaded = true;
|
||||
s_is_map_loaded = true;
|
||||
}
|
||||
|
||||
/* Set the output pointer. */
|
||||
@@ -425,26 +410,13 @@ namespace ams::settings::impl {
|
||||
AMS_ASSERT(out_data != nullptr);
|
||||
|
||||
/* Declare static instance variables. */
|
||||
static constinit util::TypedStorage<SystemData> s_storage = {};
|
||||
static constinit bool s_initialized = false;
|
||||
static constinit bool s_mounted = false;
|
||||
AMS_FUNCTION_LOCAL_STATIC(SystemData, s_data, id, GetSystemDataMountName<T>());
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_mounted, false);
|
||||
|
||||
/* Get pointer to the system data. */
|
||||
SystemData *data = util::GetPointer(s_storage);
|
||||
|
||||
/* Construct the system data, if we haven't already. */
|
||||
if (AMS_UNLIKELY(!s_initialized)) {
|
||||
/* Construct the instance. */
|
||||
util::ConstructAt(s_storage);
|
||||
|
||||
/* Setup system data. */
|
||||
data->SetSystemDataId(id);
|
||||
data->SetMountName(GetSystemDataMountName<T>());
|
||||
|
||||
/* Note that we constructed. */
|
||||
s_initialized = true;
|
||||
}
|
||||
SystemData *data = std::addressof(s_data);
|
||||
|
||||
/* TODO: Mutex? */
|
||||
/* Mount the system data, if we haven't already. */
|
||||
if (AMS_UNLIKELY(!s_mounted)) {
|
||||
/* Mount the system data. */
|
||||
@@ -464,28 +436,11 @@ namespace ams::settings::impl {
|
||||
AMS_ASSERT(out_data != nullptr);
|
||||
|
||||
/* Declare static instance variables. */
|
||||
static constinit util::TypedStorage<SystemSaveData> s_storage = {};
|
||||
static constinit bool s_initialized = false;
|
||||
static constinit bool s_mounted = false;
|
||||
AMS_FUNCTION_LOCAL_STATIC(SystemSaveData, s_data, SystemSaveDataId, SystemSaveDataSize, SystemSaveDataJournalSize, SystemSaveDataFlags, SystemSaveDataMountName);
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_mounted, false);
|
||||
|
||||
/* Get pointer to the system data. */
|
||||
SystemSaveData *data = util::GetPointer(s_storage);
|
||||
|
||||
/* Construct the system data, if we haven't already. */
|
||||
if (AMS_UNLIKELY(!s_initialized)) {
|
||||
/* Construct the instance. */
|
||||
util::ConstructAt(s_storage);
|
||||
|
||||
/* Setup system data. */
|
||||
data->SetSystemSaveDataId(SystemSaveDataId);
|
||||
data->SetTotalSize(SystemSaveDataSize);
|
||||
data->SetJournalSize(SystemSaveDataJournalSize);
|
||||
data->SetFlags(SystemSaveDataFlags);
|
||||
data->SetMountName(SystemSaveDataMountName);
|
||||
|
||||
/* Note that we constructed. */
|
||||
s_initialized = true;
|
||||
}
|
||||
/* Get pointer to the system save data. */
|
||||
SystemSaveData *data = std::addressof(s_data);
|
||||
|
||||
/* Mount the system data, if we haven't already. */
|
||||
if (AMS_UNLIKELY(!s_mounted)) {
|
||||
|
||||
@@ -56,27 +56,33 @@ namespace ams::settings::impl {
|
||||
}
|
||||
|
||||
SplConfig GetSplConfig() {
|
||||
static constinit bool s_is_initialized = false;
|
||||
static constinit SplConfig s_config;
|
||||
class SplConfigHolder {
|
||||
NON_COPYABLE(SplConfigHolder);
|
||||
NON_MOVEABLE(SplConfigHolder);
|
||||
private:
|
||||
SplConfig m_config;
|
||||
public:
|
||||
SplConfigHolder() {
|
||||
/* Initialize spl. */
|
||||
spl::Initialize();
|
||||
ON_SCOPE_EXIT { spl::Finalize(); };
|
||||
|
||||
if (!s_is_initialized) {
|
||||
/* Initialize spl. */
|
||||
spl::Initialize();
|
||||
ON_SCOPE_EXIT { spl::Finalize(); };
|
||||
/* Create the config. */
|
||||
m_config = {
|
||||
.is_development = spl::IsDevelopment(),
|
||||
.hardware_type = ConvertToSplHardwareType(spl::GetHardwareType()),
|
||||
.is_quest = IsSplRetailInteractiveDisplayStateEnabled(spl::GetRetailInteractiveDisplayState()),
|
||||
.device_id_low = spl::GetDeviceIdLow(),
|
||||
};
|
||||
}
|
||||
|
||||
/* Create the config. */
|
||||
s_config = {
|
||||
.is_development = spl::IsDevelopment(),
|
||||
.hardware_type = ConvertToSplHardwareType(spl::GetHardwareType()),
|
||||
.is_quest = IsSplRetailInteractiveDisplayStateEnabled(spl::GetRetailInteractiveDisplayState()),
|
||||
.device_id_low = spl::GetDeviceIdLow(),
|
||||
};
|
||||
ALWAYS_INLINE operator SplConfig() {
|
||||
return m_config;
|
||||
}
|
||||
};
|
||||
|
||||
/* Mark as initialized. */
|
||||
s_is_initialized = true;
|
||||
}
|
||||
|
||||
return s_config;
|
||||
AMS_FUNCTION_LOCAL_STATIC(SplConfigHolder, s_config_holder);
|
||||
return s_config_holder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,27 +25,9 @@ namespace ams::settings::impl {
|
||||
StaticObject();
|
||||
public:
|
||||
static T &Get() {
|
||||
/* Declare static instance variables. */
|
||||
static constinit util::TypedStorage<T> s_storage = {};
|
||||
static constinit bool s_initialized = false;
|
||||
static constinit os::SdkMutex s_mutex;
|
||||
AMS_FUNCTION_LOCAL_STATIC(T, s_object);
|
||||
|
||||
/* If we haven't already done so, construct the instance. */
|
||||
if (AMS_UNLIKELY(!s_initialized)) {
|
||||
std::scoped_lock lk(s_mutex);
|
||||
|
||||
/* Check that we didn't concurrently construct the instance. */
|
||||
if (AMS_LIKELY(!s_initialized)) {
|
||||
/* Construct the instance. */
|
||||
util::ConstructAt(s_storage);
|
||||
|
||||
/* Note that we constructed. */
|
||||
s_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the constructed instance. */
|
||||
return util::GetReference(s_storage);
|
||||
return s_object;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,11 @@ namespace ams::settings::impl {
|
||||
public:
|
||||
SystemData() : m_system_data_id(), m_mount_name(), m_file_path() { /* ... */ }
|
||||
|
||||
SystemData(ncm::SystemDataId id, const char *mn) : SystemData() {
|
||||
this->SetSystemDataId(id);
|
||||
this->SetMountName(mn);
|
||||
}
|
||||
|
||||
void SetSystemDataId(ncm::SystemDataId id);
|
||||
void SetMountName(const char *name);
|
||||
Result Mount();
|
||||
|
||||
@@ -30,6 +30,14 @@ namespace ams::settings::impl {
|
||||
public:
|
||||
SystemSaveData() : m_system_save_data_id(0), m_save_data_space_id(fs::SaveDataSpaceId::System), m_total_size(0), m_journal_size(0), m_flags(0) { /* ... */ }
|
||||
|
||||
SystemSaveData(u64 id, s64 total_size, s64 journal_size, u32 flags, const char *mn) : SystemSaveData() {
|
||||
this->SetSystemSaveDataId(id);
|
||||
this->SetTotalSize(total_size);
|
||||
this->SetJournalSize(journal_size);
|
||||
this->SetFlags(flags);
|
||||
this->SetMountName(mn);
|
||||
}
|
||||
|
||||
void SetSystemSaveDataId(u64 id);
|
||||
void SetTotalSize(s64 size);
|
||||
void SetJournalSize(s64 size);
|
||||
|
||||
@@ -17,6 +17,65 @@
|
||||
|
||||
namespace ams::sf::cmif {
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE decltype(ServiceCommandMeta::handler) FindCommandHandlerByBinarySearch(const ServiceCommandMeta *entries, const size_t entry_count, const u32 cmd_id, const hos::Version hos_version) {
|
||||
/* Binary search for the handler. */
|
||||
ssize_t lo = 0;
|
||||
ssize_t hi = entry_count - 1;
|
||||
while (lo <= hi) {
|
||||
const size_t mid = (lo + hi) / 2;
|
||||
if (entries[mid].cmd_id < cmd_id) {
|
||||
lo = mid + 1;
|
||||
} else if (entries[mid].cmd_id > cmd_id) {
|
||||
hi = mid - 1;
|
||||
} else {
|
||||
/* Find start. */
|
||||
size_t start = mid;
|
||||
while (start > 0 && entries[start - 1].cmd_id == cmd_id) {
|
||||
--start;
|
||||
}
|
||||
|
||||
/* Find end. */
|
||||
size_t end = mid + 1;
|
||||
while (end < entry_count && entries[end].cmd_id == cmd_id) {
|
||||
++end;
|
||||
}
|
||||
|
||||
for (size_t idx = start; idx < end; ++idx) {
|
||||
if (entries[idx].MatchesVersion(hos_version)) {
|
||||
return entries[idx].GetHandler();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE decltype(ServiceCommandMeta::handler) FindCommandHandlerByLinearSearch(const ServiceCommandMeta *entries, const size_t entry_count, const u32 cmd_id, const hos::Version hos_version) {
|
||||
for (size_t i = 0; i < entry_count; ++i) {
|
||||
if (entries[i].Matches(cmd_id, hos_version)) {
|
||||
return entries[i].GetHandler();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE decltype(ServiceCommandMeta::handler) FindCommandHandler(const ServiceCommandMeta *entries, const size_t entry_count, const u32 cmd_id, const hos::Version hos_version) {
|
||||
if (entry_count >= 8) {
|
||||
return FindCommandHandlerByBinarySearch(entries, entry_count, cmd_id, hos_version);
|
||||
} else {
|
||||
return FindCommandHandlerByLinearSearch(entries, entry_count, cmd_id, hos_version);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result impl::ServiceDispatchTableBase::ProcessMessageImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count) const {
|
||||
/* Get versioning info. */
|
||||
const auto hos_version = hos::GetVersion();
|
||||
@@ -30,13 +89,7 @@ namespace ams::sf::cmif {
|
||||
const u32 cmd_id = in_header->command_id;
|
||||
|
||||
/* Find a handler. */
|
||||
decltype(ServiceCommandMeta::handler) cmd_handler = nullptr;
|
||||
for (size_t i = 0; i < entry_count; i++) {
|
||||
if (entries[i].Matches(cmd_id, hos_version)) {
|
||||
cmd_handler = entries[i].GetHandler();
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto cmd_handler = FindCommandHandler(entries, entry_count, cmd_id, hos_version);
|
||||
R_UNLESS(cmd_handler != nullptr, sf::cmif::ResultUnknownCommandId());
|
||||
|
||||
/* Invoke handler. */
|
||||
@@ -73,13 +126,7 @@ namespace ams::sf::cmif {
|
||||
const u32 cmd_id = in_header->command_id;
|
||||
|
||||
/* Find a handler. */
|
||||
decltype(ServiceCommandMeta::handler) cmd_handler = nullptr;
|
||||
for (size_t i = 0; i < entry_count; i++) {
|
||||
if (entries[i].Matches(cmd_id, hos_version)) {
|
||||
cmd_handler = entries[i].GetHandler();
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto cmd_handler = FindCommandHandler(entries, entry_count, cmd_id, hos_version);
|
||||
|
||||
/* If we didn't find a handler, forward the request. */
|
||||
if (cmd_handler == nullptr) {
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 2
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 3
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 6
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
||||
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 13
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 1
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 2
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 1
|
||||
|
||||
@@ -65,8 +65,10 @@
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_12_1_0 ATMOSPHERE_TARGET_FIRMWARE(12, 1, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_13_0_0 ATMOSPHERE_TARGET_FIRMWARE(13, 0, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_13_1_0 ATMOSPHERE_TARGET_FIRMWARE(13, 1, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_13_2_0 ATMOSPHERE_TARGET_FIRMWARE(13, 2, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_13_2_1 ATMOSPHERE_TARGET_FIRMWARE(13, 2, 1)
|
||||
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_13_1_0
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_13_2_1
|
||||
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
||||
@@ -124,6 +126,8 @@ namespace ams {
|
||||
TargetFirmware_12_1_0 = ATMOSPHERE_TARGET_FIRMWARE_12_1_0,
|
||||
TargetFirmware_13_0_0 = ATMOSPHERE_TARGET_FIRMWARE_13_0_0,
|
||||
TargetFirmware_13_1_0 = ATMOSPHERE_TARGET_FIRMWARE_13_1_0,
|
||||
TargetFirmware_13_2_0 = ATMOSPHERE_TARGET_FIRMWARE_13_2_0,
|
||||
TargetFirmware_13_2_1 = ATMOSPHERE_TARGET_FIRMWARE_13_2_1,
|
||||
|
||||
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,
|
||||
|
||||
|
||||
@@ -177,7 +177,11 @@ namespace ams {
|
||||
static_assert(Value != Base::SuccessValue, "Value != Base::SuccessValue");
|
||||
public:
|
||||
constexpr ALWAYS_INLINE operator Result() const { return MakeResult(Value); }
|
||||
constexpr ALWAYS_INLINE operator ResultSuccess() const { OnResultAbort(Value); }
|
||||
constexpr ALWAYS_INLINE operator ResultSuccess() const {
|
||||
OnResultAbort(Value);
|
||||
__builtin_unreachable();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsSuccess() const { return false; }
|
||||
constexpr ALWAYS_INLINE bool IsFailure() const { return !this->IsSuccess(); }
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
|
||||
#include <vapours/util/util_atomic.hpp>
|
||||
|
||||
#include <vapours/util/util_function_local_static.hpp>
|
||||
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include <vapours/util/util_mutex_utils.hpp>
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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/util/util_typed_storage.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
#define AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(_TYPE_, _NAME_, ...) static constinit _TYPE_ _NAME_ { __VA_ARGS__ }
|
||||
|
||||
/* NOTE: This must use placement new, to support private constructors. */
|
||||
#define AMS_FUNCTION_LOCAL_STATIC_IMPL(_LOCKTYPE_, _SCOPELOCKTYPE_, _TYPE_, _NAME_, ...) \
|
||||
static constinit ::ams::util::TypedStorage<_TYPE_> s_fls_storage_for_##_NAME_ {}; \
|
||||
static constinit bool s_fls_initialized_##_NAME_ = false; \
|
||||
static constinit _LOCKTYPE_ s_fls_init_lock_##_NAME_ {}; \
|
||||
if (AMS_UNLIKELY(!(s_fls_initialized_##_NAME_))) { \
|
||||
_SCOPELOCKTYPE_ sl_fls_for_##_NAME_ { s_fls_init_lock_##_NAME_ }; \
|
||||
if (AMS_LIKELY(!(s_fls_initialized_##_NAME_))) { \
|
||||
new (::ams::util::impl::GetPointerForConstructAt(s_fls_storage_for_##_NAME_)) _TYPE_( __VA_ARGS__ ); \
|
||||
s_fls_initialized_##_NAME_ = true; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
_TYPE_ & _NAME_ = util::GetReference(s_fls_storage_for_##_NAME_)
|
||||
|
||||
#if defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
|
||||
#define AMS_FUNCTION_LOCAL_STATIC(_TYPE_, _NAME_, ...) AMS_FUNCTION_LOCAL_STATIC_IMPL(KSpinLock, KScopedSpinLock, _TYPE_, _NAME_, ##__VA_ARGS__)
|
||||
|
||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
#define AMS_FUNCTION_LOCAL_STATIC(_TYPE_, _NAME_, ...) AMS_FUNCTION_LOCAL_STATIC_IMPL(os::SdkMutex, std::scoped_lock, _TYPE_, _NAME_, ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -295,7 +295,7 @@ namespace ams::util {
|
||||
private:
|
||||
constexpr explicit ALWAYS_INLINE Iterator(ImplIterator it) : m_impl(it) { /* ... */ }
|
||||
|
||||
constexpr explicit ALWAYS_INLINE Iterator(ImplIterator::pointer p) : m_impl(p) { /* ... */ }
|
||||
constexpr explicit ALWAYS_INLINE Iterator(typename ImplIterator::pointer p) : m_impl(p) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE ImplIterator GetImplIterator() const {
|
||||
return m_impl;
|
||||
|
||||
@@ -225,7 +225,7 @@ namespace ams::util {
|
||||
template<typename T, typename Derived>
|
||||
class OptionalBaseImpl {
|
||||
protected:
|
||||
using StoredType = std::remove_const<T>::type;
|
||||
using StoredType = std::remove_const_t<T>;
|
||||
|
||||
template<typename... Args>
|
||||
constexpr void ConstructImpl(Args &&... args) { static_cast<Derived *>(this)->m_payload.Construct(std::forward<Args>(args)...); }
|
||||
|
||||
@@ -45,9 +45,18 @@ namespace ams::util {
|
||||
return *GetPointer(ts);
|
||||
}
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T>
|
||||
static ALWAYS_INLINE T *GetPointerForConstructAt(TypedStorage<T> &ts) {
|
||||
return reinterpret_cast<T *>(std::addressof(ts._storage));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
static ALWAYS_INLINE T *ConstructAt(TypedStorage<T> &ts, Args &&... args) {
|
||||
return std::construct_at(reinterpret_cast<T *>(std::addressof(ts._storage)), std::forward<Args>(args)...);
|
||||
return std::construct_at(impl::GetPointerForConstructAt(ts), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -33,44 +33,6 @@ namespace ams::mitm::fs {
|
||||
constexpr const char AtmosphereHblWebContentDir[] = "/atmosphere/hbl_html/";
|
||||
constexpr const char ProgramWebContentDir[] = "/manual_html/";
|
||||
|
||||
constinit os::SdkMutex g_data_storage_lock;
|
||||
constinit os::SdkMutex g_storage_cache_lock;
|
||||
|
||||
class StorageCacheEntry : public util::IntrusiveRedBlackTreeBaseNode<StorageCacheEntry> {
|
||||
public:
|
||||
using RedBlackKeyType = u64;
|
||||
private:
|
||||
ncm::ProgramId m_program_id;
|
||||
std::weak_ptr<fs::IStorage> m_storage;
|
||||
public:
|
||||
StorageCacheEntry(ncm::ProgramId program_id, const std::shared_ptr<fs::IStorage> *sp) : m_program_id(program_id), m_storage(*sp) { /* ... */ }
|
||||
|
||||
constexpr ncm::ProgramId GetProgramId() const { return m_program_id; }
|
||||
constexpr const std::weak_ptr<fs::IStorage> &GetStorage() const { return m_storage; }
|
||||
|
||||
void SetStorage(const std::shared_ptr<fs::IStorage> *sp) { m_storage = *sp; }
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const RedBlackKeyType &lval, const StorageCacheEntry &rhs) {
|
||||
const auto rval = rhs.GetProgramId().value;
|
||||
|
||||
if (lval < rval) {
|
||||
return -1;
|
||||
} else if (lval == rval) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const StorageCacheEntry &lhs, const StorageCacheEntry &rhs) {
|
||||
return Compare(lhs.GetProgramId().value, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
using StorageCache = typename util::IntrusiveRedBlackTreeBaseTraits<StorageCacheEntry>::TreeType<StorageCacheEntry>;
|
||||
|
||||
constinit StorageCache g_storage_cache;
|
||||
|
||||
constinit os::SdkMutex g_boot0_detect_lock;
|
||||
constinit bool g_detected_boot0_kind = false;
|
||||
constinit bool g_is_boot0_custom_public_key = false;
|
||||
@@ -90,30 +52,6 @@ namespace ams::mitm::fs {
|
||||
return g_is_boot0_custom_public_key;
|
||||
}
|
||||
|
||||
std::shared_ptr<fs::IStorage> GetStorageCacheEntry(ncm::ProgramId program_id) {
|
||||
std::scoped_lock lk(g_storage_cache_lock);
|
||||
|
||||
if (const auto it = g_storage_cache.find_key(program_id.value); it != g_storage_cache.end()) {
|
||||
return it->GetStorage().lock();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SetStorageCacheEntry(ncm::ProgramId program_id, std::shared_ptr<fs::IStorage> *new_intf) {
|
||||
std::scoped_lock lk(g_storage_cache_lock);
|
||||
|
||||
if (auto it = g_storage_cache.find_key(program_id.value); it != g_storage_cache.end()) {
|
||||
if (auto cur_intf = it->GetStorage().lock(); cur_intf != nullptr) {
|
||||
*new_intf = cur_intf;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto *new_entry = new StorageCacheEntry(program_id, new_intf);
|
||||
g_storage_cache.insert(*new_entry);
|
||||
}
|
||||
|
||||
bool GetSettingsItemBooleanValue(const char *name, const char *key) {
|
||||
u8 tmp = 0;
|
||||
AMS_ABORT_UNLESS(settings::fwdbg::GetSettingsItemValue(std::addressof(tmp), sizeof(tmp), name, key) == sizeof(tmp));
|
||||
@@ -358,38 +296,8 @@ namespace ams::mitm::fs {
|
||||
R_TRY(fsOpenDataStorageByCurrentProcessFwd(m_forward_service.get(), std::addressof(data_storage)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a scoped lock. */
|
||||
std::scoped_lock lk(g_data_storage_lock);
|
||||
|
||||
/* Try to get a storage from the cache. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(m_client_info.program_id);
|
||||
if (cached_storage != nullptr) {
|
||||
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/* Make a new layered romfs, and cache to storage. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> new_storage = nullptr;
|
||||
|
||||
/* Create the layered storage. */
|
||||
FsFile data_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), m_client_info.program_id, "romfs.bin", OpenMode_Read))) {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), m_client_info.program_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
} else {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, m_client_info.program_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
}
|
||||
|
||||
SetStorageCacheEntry(m_client_info.program_id, std::addressof(new_storage));
|
||||
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
|
||||
}
|
||||
|
||||
/* Get a layered storage for the process romfs. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(m_client_info.program_id, data_storage, true)), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -403,43 +311,13 @@ namespace ams::mitm::fs {
|
||||
/* Only mitm if there is actually an override romfs. */
|
||||
R_UNLESS(mitm::fs::HasSdRomfsContent(data_id), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Try to open the process romfs. */
|
||||
/* Try to open the data id. */
|
||||
FsStorage data_storage;
|
||||
R_TRY(fsOpenDataStorageByDataIdFwd(m_forward_service.get(), std::addressof(data_storage), static_cast<u64>(data_id), static_cast<NcmStorageId>(storage_id)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a scoped lock. */
|
||||
std::scoped_lock lk(g_data_storage_lock);
|
||||
|
||||
/* Try to get a storage from the cache. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(data_id);
|
||||
if (cached_storage != nullptr) {
|
||||
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/* Make a new layered romfs, and cache to storage. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> new_storage = nullptr;
|
||||
|
||||
/* Create the layered storage. */
|
||||
FsFile data_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), data_id, "romfs.bin", OpenMode_Read))) {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), data_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
} else {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, data_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
}
|
||||
|
||||
SetStorageCacheEntry(data_id, std::addressof(new_storage));
|
||||
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
|
||||
}
|
||||
|
||||
/* Get a layered storage for the data id. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(data_id, data_storage, false)), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -459,38 +337,8 @@ namespace ams::mitm::fs {
|
||||
R_TRY(fsOpenDataStorageWithProgramIndexFwd(m_forward_service.get(), std::addressof(data_storage), program_index));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a scoped lock. */
|
||||
std::scoped_lock lk(g_data_storage_lock);
|
||||
|
||||
/* Try to get a storage from the cache. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(program_id);
|
||||
if (cached_storage != nullptr) {
|
||||
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/* Make a new layered romfs, and cache to storage. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> new_storage = nullptr;
|
||||
|
||||
/* Create the layered storage. */
|
||||
FsFile data_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), program_id, "romfs.bin", OpenMode_Read))) {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), program_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
} else {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, program_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
}
|
||||
|
||||
SetStorageCacheEntry(program_id, std::addressof(new_storage));
|
||||
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
|
||||
}
|
||||
|
||||
/* Get a layered storage for the process romfs. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(program_id, data_storage, true)), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,19 +28,121 @@ namespace ams::mitm::fs {
|
||||
os::MessageQueue g_req_mq(g_mq_storage + 0, 1);
|
||||
os::MessageQueue g_ack_mq(g_mq_storage + 1, 1);
|
||||
|
||||
class LayeredRomfsStorageHolder : public util::IntrusiveRedBlackTreeBaseNode<LayeredRomfsStorageHolder> {
|
||||
public:
|
||||
using RedBlackKeyType = u64;
|
||||
private:
|
||||
LayeredRomfsStorageImpl *m_impl;
|
||||
u32 m_reference_count;
|
||||
bool m_second_chance;
|
||||
bool m_process_romfs;
|
||||
public:
|
||||
LayeredRomfsStorageHolder(LayeredRomfsStorageImpl *impl, bool process_rom) : m_impl(impl), m_reference_count(1), m_second_chance(true), m_process_romfs(process_rom) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
~LayeredRomfsStorageHolder() {
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
constexpr LayeredRomfsStorageImpl *GetImpl() const { return m_impl; }
|
||||
constexpr ncm::ProgramId GetProgramId() const { return m_impl->GetProgramId(); }
|
||||
constexpr u32 GetReferenceCount() const { return m_reference_count; }
|
||||
|
||||
void OpenReferenceImpl() { ++m_reference_count; }
|
||||
void CloseReferenceImpl() { --m_reference_count; }
|
||||
|
||||
bool GetSecondChanceImpl() const { return m_second_chance; }
|
||||
void SetSecondChanceImpl(bool sc) { m_second_chance = sc; }
|
||||
|
||||
bool IsProcessRomfs() const { return m_process_romfs; }
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const RedBlackKeyType &lval, const LayeredRomfsStorageHolder &rhs) {
|
||||
const auto rval = rhs.GetProgramId().value;
|
||||
|
||||
if (lval < rval) {
|
||||
return -1;
|
||||
} else if (lval == rval) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const LayeredRomfsStorageHolder &lhs, const LayeredRomfsStorageHolder &rhs) {
|
||||
return Compare(lhs.GetProgramId().value, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
using LayeredRomfsStorageSet = typename util::IntrusiveRedBlackTreeBaseTraits<LayeredRomfsStorageHolder>::TreeType<LayeredRomfsStorageHolder>;
|
||||
|
||||
constinit os::SdkRecursiveMutex g_storage_set_mutex;
|
||||
constinit LayeredRomfsStorageSet g_storage_set;
|
||||
|
||||
void OpenReference(LayeredRomfsStorageImpl *impl) {
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.find_key(impl->GetProgramId().value);
|
||||
AMS_ABORT_UNLESS(it != g_storage_set.end());
|
||||
|
||||
it->OpenReferenceImpl();
|
||||
}
|
||||
|
||||
void CloseReference(LayeredRomfsStorageImpl *impl) {
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.find_key(impl->GetProgramId().value);
|
||||
AMS_ABORT_UNLESS(it != g_storage_set.end());
|
||||
|
||||
AMS_ABORT_UNLESS(it->GetReferenceCount() > 0);
|
||||
it->CloseReferenceImpl();
|
||||
}
|
||||
|
||||
void RomfsInitializerThreadFunction(void *) {
|
||||
while (true) {
|
||||
uintptr_t storage_uptr = 0;
|
||||
|
||||
g_req_mq.Receive(std::addressof(storage_uptr));
|
||||
std::shared_ptr<LayeredRomfsStorage> layered_storage = reinterpret_cast<LayeredRomfsStorage *>(storage_uptr)->GetShared();
|
||||
auto *impl = reinterpret_cast<LayeredRomfsStorageImpl *>(storage_uptr);
|
||||
g_ack_mq.Send(storage_uptr);
|
||||
layered_storage->InitializeImpl();
|
||||
|
||||
impl->InitializeImpl();
|
||||
|
||||
/* Close the initial reference. */
|
||||
CloseReference(impl);
|
||||
}
|
||||
}
|
||||
|
||||
void RomfsFinalizerThreadFunction(void *) {
|
||||
while (true) {
|
||||
{
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.begin();
|
||||
while (it != g_storage_set.end()) {
|
||||
if (it->GetReferenceCount() > 0) {
|
||||
it->SetSecondChanceImpl(true);
|
||||
++it;
|
||||
} else if (it->GetSecondChanceImpl()) {
|
||||
it->SetSecondChanceImpl(false);
|
||||
++it;
|
||||
} else {
|
||||
auto *holder = std::addressof(*it);
|
||||
it = g_storage_set.erase(it);
|
||||
delete holder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
os::SleepThread(TimeSpan::FromMilliSeconds(500));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t RomfsInitializerThreadStackSize = 0x8000;
|
||||
os::ThreadType g_romfs_initializer_thread;
|
||||
os::ThreadType g_romfs_finalizer_thread;
|
||||
alignas(os::ThreadStackAlignment) u8 g_romfs_initializer_thread_stack[RomfsInitializerThreadStackSize];
|
||||
alignas(os::ThreadStackAlignment) u8 g_romfs_finalizer_thread_stack[os::MemoryPageSize];
|
||||
|
||||
void RequestInitializeStorage(uintptr_t storage_uptr) {
|
||||
std::scoped_lock lk(g_mq_lock);
|
||||
@@ -49,6 +151,11 @@ namespace ams::mitm::fs {
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_initializer_thread), RomfsInitializerThreadFunction, nullptr, g_romfs_initializer_thread_stack, sizeof(g_romfs_initializer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_romfs_initializer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemInitializeThread));
|
||||
os::StartThread(std::addressof(g_romfs_initializer_thread));
|
||||
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_finalizer_thread), RomfsFinalizerThreadFunction, nullptr, g_romfs_finalizer_thread_stack, sizeof(g_romfs_finalizer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_romfs_finalizer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemFinalizeThread));
|
||||
os::StartThread(std::addressof(g_romfs_finalizer_thread));
|
||||
|
||||
g_started_req_thread = true;
|
||||
}
|
||||
|
||||
@@ -58,27 +165,113 @@ namespace ams::mitm::fs {
|
||||
AMS_ABORT_UNLESS(ack == storage_uptr);
|
||||
}
|
||||
|
||||
class LayeredRomfsStorage : public ams::fs::IStorage {
|
||||
private:
|
||||
LayeredRomfsStorageImpl *m_impl;
|
||||
public:
|
||||
LayeredRomfsStorage(LayeredRomfsStorageImpl *impl) : m_impl(impl) {
|
||||
OpenReference(m_impl);
|
||||
}
|
||||
|
||||
virtual ~LayeredRomfsStorage() {
|
||||
CloseReference(m_impl);
|
||||
}
|
||||
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
return m_impl->Read(offset, buffer, size);
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out_size) override {
|
||||
return m_impl->GetSize(out_size);
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return m_impl->Flush();
|
||||
}
|
||||
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||
return m_impl->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(offset, buffer, size);
|
||||
return ams::fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(size);
|
||||
return ams::fs::ResultUnsupportedOperation();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
LayeredRomfsStorage::LayeredRomfsStorage(std::unique_ptr<IStorage> s_r, std::unique_ptr<IStorage> f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) {
|
||||
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs) {
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
/* Find an existing storage. */
|
||||
if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) {
|
||||
return std::make_shared<LayeredRomfsStorage>(it->GetImpl());
|
||||
}
|
||||
|
||||
/* We don't have an existing storage. If we're creating process romfs, free any unreferenced process romfs. */
|
||||
/* This should help prevent too much memory in use at any time. */
|
||||
if (is_process_romfs) {
|
||||
auto it = g_storage_set.begin();
|
||||
while (it != g_storage_set.end()) {
|
||||
if (it->GetReferenceCount() > 0 || !it->IsProcessRomfs()) {
|
||||
++it;
|
||||
} else {
|
||||
auto *holder = std::addressof(*it);
|
||||
it = g_storage_set.erase(it);
|
||||
delete holder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a new storage. */
|
||||
LayeredRomfsStorageImpl *impl;
|
||||
{
|
||||
::FsFile data_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), program_id, "romfs.bin", OpenMode_Read))) {
|
||||
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), program_id);
|
||||
} else {
|
||||
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, program_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert holder. Reference count will now be one. */
|
||||
g_storage_set.insert(*(new LayeredRomfsStorageHolder(impl, is_process_romfs)));
|
||||
|
||||
/* Begin initialization. When this finishes, a decref will occur. */
|
||||
impl->BeginInitialize();
|
||||
|
||||
/* Return a new shared storage for the impl. */
|
||||
return std::make_shared<LayeredRomfsStorage>(impl);
|
||||
}
|
||||
|
||||
LayeredRomfsStorageImpl::LayeredRomfsStorageImpl(std::unique_ptr<IStorage> s_r, std::unique_ptr<IStorage> f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
LayeredRomfsStorage::~LayeredRomfsStorage() {
|
||||
LayeredRomfsStorageImpl::~LayeredRomfsStorageImpl() {
|
||||
for (size_t i = 0; i < m_source_infos.size(); i++) {
|
||||
m_source_infos[i].Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void LayeredRomfsStorage::BeginInitialize() {
|
||||
void LayeredRomfsStorageImpl::BeginInitialize() {
|
||||
AMS_ABORT_UNLESS(!m_started_initialize);
|
||||
RequestInitializeStorage(reinterpret_cast<uintptr_t>(this));
|
||||
m_started_initialize = true;
|
||||
}
|
||||
|
||||
void LayeredRomfsStorage::InitializeImpl() {
|
||||
void LayeredRomfsStorageImpl::InitializeImpl() {
|
||||
/* Build new virtual romfs. */
|
||||
romfs::Builder builder(m_program_id);
|
||||
|
||||
@@ -98,7 +291,7 @@ namespace ams::mitm::fs {
|
||||
m_initialize_event.Signal();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorage::Read(s64 offset, void *buffer, size_t size) {
|
||||
Result LayeredRomfsStorageImpl::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Check if we can succeed immediately. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
@@ -178,7 +371,7 @@ namespace ams::mitm::fs {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorage::GetSize(s64 *out_size) {
|
||||
Result LayeredRomfsStorageImpl::GetSize(s64 *out_size) {
|
||||
/* Ensure we're initialized. */
|
||||
if (!m_is_initialized) {
|
||||
m_initialize_event.Wait();
|
||||
@@ -188,11 +381,11 @@ namespace ams::mitm::fs {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorage::Flush() {
|
||||
Result LayeredRomfsStorageImpl::Flush() {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorage::OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
Result LayeredRomfsStorageImpl::OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
AMS_UNUSED(offset, src, src_size);
|
||||
|
||||
switch (op_id) {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
class LayeredRomfsStorage : public std::enable_shared_from_this<LayeredRomfsStorage>, public ams::fs::IStorage {
|
||||
class LayeredRomfsStorageImpl {
|
||||
private:
|
||||
std::vector<romfs::SourceInfo> m_source_infos;
|
||||
std::unique_ptr<ams::fs::IStorage> m_storage_romfs;
|
||||
@@ -35,32 +35,20 @@ namespace ams::mitm::fs {
|
||||
return back.virtual_offset + back.size;
|
||||
}
|
||||
public:
|
||||
LayeredRomfsStorage(std::unique_ptr<ams::fs::IStorage> s_r, std::unique_ptr<ams::fs::IStorage> f_r, ncm::ProgramId pr_id);
|
||||
virtual ~LayeredRomfsStorage();
|
||||
LayeredRomfsStorageImpl(std::unique_ptr<ams::fs::IStorage> s_r, std::unique_ptr<ams::fs::IStorage> f_r, ncm::ProgramId pr_id);
|
||||
~LayeredRomfsStorageImpl();
|
||||
|
||||
void BeginInitialize();
|
||||
void InitializeImpl();
|
||||
|
||||
std::shared_ptr<LayeredRomfsStorage> GetShared() {
|
||||
return this->shared_from_this();
|
||||
}
|
||||
constexpr ncm::ProgramId GetProgramId() const { return m_program_id; }
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
virtual Result GetSize(s64 *out_size) override;
|
||||
virtual Result Flush() override;
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(offset, buffer, size);
|
||||
return ams::fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(size);
|
||||
return ams::fs::ResultUnsupportedOperation();
|
||||
}
|
||||
Result Read(s64 offset, void *buffer, size_t size);
|
||||
Result GetSize(s64 *out_size);
|
||||
Result Flush();
|
||||
Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size);
|
||||
};
|
||||
|
||||
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs);
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dns_mitm/dnsmitm_debug.hpp"
|
||||
|
||||
namespace ams::mitm::fs::romfs {
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace ams::mitm::sysupdater {
|
||||
ON_SCOPE_EXIT { std::free(data_buffer); };
|
||||
|
||||
/* Declare helper for result validation. */
|
||||
auto ValidateResult = [&] ALWAYS_INLINE_LAMBDA (Result result) -> Result {
|
||||
auto ValidateResult = [&](Result result) ALWAYS_INLINE_LAMBDA -> Result {
|
||||
*out_result = result;
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -19,7 +19,6 @@ constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_9_0_0[] = {
|
||||
{ 0x521C, "\x20\x00\x80\x52", 4 },
|
||||
};
|
||||
|
||||
/* Patch fallback case to mov w0, #1 rather than retrieving settings flag. */
|
||||
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_10_0_0[] = {
|
||||
{ 0x5494, "\x20\x00\x80\x52", 4 },
|
||||
};
|
||||
@@ -30,16 +29,21 @@ constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_11_0_0[] = {
|
||||
{ 0x866C, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
|
||||
};
|
||||
|
||||
/* Patch getter functions to return 1. */
|
||||
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_12_0_0[] = {
|
||||
{ 0x840C, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
|
||||
{ 0x849C, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
|
||||
};
|
||||
|
||||
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_13_0_0[] = {
|
||||
{ 0x8424, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
|
||||
{ 0x84B4, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
|
||||
};
|
||||
|
||||
constexpr inline const EmbeddedPatch Usb30ForceEnablePatches[] = {
|
||||
{ ParseModuleId("C0D3F4E87E8B0FE9BBE9F1968A20767F3DC08E03"), util::size(Usb30ForceEnablePatches_9_0_0), Usb30ForceEnablePatches_9_0_0 },
|
||||
{ ParseModuleId("B9C700CA8335F8BAA0D2041D8D09F772890BA988"), util::size(Usb30ForceEnablePatches_10_0_0), Usb30ForceEnablePatches_10_0_0 },
|
||||
{ ParseModuleId("95BAF06A69650C215A5DD50CF8BD2A603E7AD3C2"), util::size(Usb30ForceEnablePatches_11_0_0), Usb30ForceEnablePatches_11_0_0 },
|
||||
{ ParseModuleId("F3F88D90EF6B3B6358EDEBAF87D56FA88D3A081C"), util::size(Usb30ForceEnablePatches_12_0_0), Usb30ForceEnablePatches_12_0_0 }, /* 12.0.0 - 12.0.3 */
|
||||
{ ParseModuleId("A109046278AA99353C20EC38B57C495B74A06D91"), util::size(Usb30ForceEnablePatches_12_0_0), Usb30ForceEnablePatches_12_0_0 }, /* 12.1.0 */
|
||||
};
|
||||
{ ParseModuleId("69384BF4A543C62C4342C94E5E2CED3C5A675251"), util::size(Usb30ForceEnablePatches_13_0_0), Usb30ForceEnablePatches_13_0_0 }, /* 13.0.0 - 13.1.0 */
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
namespace ams::sm {
|
||||
|
||||
/* Service definition. */
|
||||
class UserService : public tipc::DeferrableBase {
|
||||
class UserService : public tipc::DeferrableBase<sm::impl::IUserInterface, /* Maximum deferrable CMIF message size: */ 0x20 + util::AlignUp(sizeof(sm::ServiceName), sizeof(u32))> {
|
||||
private:
|
||||
os::ProcessId m_process_id;
|
||||
bool m_initialized;
|
||||
@@ -41,7 +41,7 @@ namespace ams::sm {
|
||||
|
||||
Result GetServiceHandle(tipc::OutMoveHandle out_h, ServiceName service) {
|
||||
R_UNLESS(m_initialized, sm::ResultInvalidClient());
|
||||
return this->RegisterRetryIfDeferred(service, [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||
return this->RegisterRetryIfDeferred(service, [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||
return impl::GetServiceHandle(out_h.GetPointer(), m_process_id, service);
|
||||
});
|
||||
}
|
||||
@@ -66,7 +66,7 @@ namespace ams::sm {
|
||||
/* Atmosphere commands. */
|
||||
Result AtmosphereInstallMitm(tipc::OutMoveHandle srv_h, tipc::OutMoveHandle qry_h, ServiceName service) {
|
||||
R_UNLESS(m_initialized, sm::ResultInvalidClient());
|
||||
return this->RegisterRetryIfDeferred(service, [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||
return this->RegisterRetryIfDeferred(service, [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||
return impl::InstallMitm(srv_h.GetPointer(), qry_h.GetPointer(), m_process_id, service);
|
||||
});
|
||||
}
|
||||
@@ -88,7 +88,7 @@ namespace ams::sm {
|
||||
|
||||
Result AtmosphereWaitMitm(ServiceName service) {
|
||||
R_UNLESS(m_initialized, sm::ResultInvalidClient());
|
||||
return this->RegisterRetryIfDeferred(service, [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||
return this->RegisterRetryIfDeferred(service, [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||
return impl::WaitMitm(service);
|
||||
});
|
||||
}
|
||||
@@ -110,7 +110,7 @@ namespace ams::sm {
|
||||
|
||||
Result AtmosphereWaitService(ServiceName service) {
|
||||
R_UNLESS(m_initialized, sm::ResultInvalidClient());
|
||||
return this->RegisterRetryIfDeferred(service, [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||
return this->RegisterRetryIfDeferred(service, [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||
return impl::WaitService(service);
|
||||
});
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
6583
tests/TestSvc/source/doctest.h
Normal file
6583
tests/TestSvc/source/doctest.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include "util_catch.hpp"
|
||||
#define DOCTEST_CONFIG_IMPLEMENT
|
||||
#include "util_test_framework.hpp"
|
||||
|
||||
namespace ams {
|
||||
|
||||
@@ -62,7 +62,13 @@ namespace ams {
|
||||
}
|
||||
|
||||
/* Run tests. */
|
||||
Catch::Session().run(os::GetHostArgc(), os::GetHostArgv());
|
||||
{
|
||||
doctest::Context ctx;
|
||||
|
||||
ctx.applyCommandLine(os::GetHostArgc(), os::GetHostArgv());
|
||||
|
||||
ctx.run();
|
||||
}
|
||||
|
||||
AMS_INFINITE_LOOP();
|
||||
|
||||
@@ -72,7 +78,7 @@ namespace ams {
|
||||
|
||||
}
|
||||
|
||||
namespace Catch {
|
||||
namespace doctest {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -89,14 +95,13 @@ namespace Catch {
|
||||
|
||||
}
|
||||
|
||||
std::ostream& cout() {
|
||||
std::ostream& get_cout() {
|
||||
static std::ostream ret(new OutputDebugStringStream);
|
||||
return ret;
|
||||
}
|
||||
std::ostream& clog() {
|
||||
return cout();
|
||||
}
|
||||
std::ostream& cerr() {
|
||||
return clog();
|
||||
|
||||
std::ostream& get_cerr() {
|
||||
return get_cout();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,12 +37,12 @@ namespace ams::test {
|
||||
|
||||
}
|
||||
|
||||
CATCH_TEST_CASE( "The scheduler is preemptive at the preemptive priority and cooperative for all other priorities" ) {
|
||||
DOCTEST_TEST_CASE( "The scheduler is preemptive at the preemptive priority and cooperative for all other priorities" ) {
|
||||
/* Create heap. */
|
||||
ScopedHeap heap(3 * os::MemoryPageSize);
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_None)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_None)));
|
||||
ON_SCOPE_EXIT {
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_ReadWrite)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_ReadWrite)));
|
||||
};
|
||||
const uintptr_t sp_0 = heap.GetAddress() + 1 * os::MemoryPageSize;
|
||||
const uintptr_t sp_1 = heap.GetAddress() + 3 * os::MemoryPageSize;
|
||||
@@ -56,21 +56,21 @@ namespace ams::test {
|
||||
g_spinloop = true;
|
||||
|
||||
/* Create threads. */
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestPreemptionPriorityThreadFunction), reinterpret_cast<uintptr_t>(thread_executed + 0), sp_0, priority, core)));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestPreemptionPriorityThreadFunction), reinterpret_cast<uintptr_t>(thread_executed + 1), sp_1, priority, core)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestPreemptionPriorityThreadFunction), reinterpret_cast<uintptr_t>(thread_executed + 0), sp_0, priority, core)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestPreemptionPriorityThreadFunction), reinterpret_cast<uintptr_t>(thread_executed + 1), sp_1, priority, core)));
|
||||
|
||||
/* Start threads. */
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
|
||||
|
||||
/* Wait long enough that we can be confident the threads have been balanced. */
|
||||
svc::SleepThread(PreemptionTimeSpan.GetNanoSeconds() * 10);
|
||||
|
||||
/* Check that we're in a coherent state. */
|
||||
if (IsPreemptionPriority(core, priority)) {
|
||||
CATCH_REQUIRE(thread_executed[0] & thread_executed[1]);
|
||||
DOCTEST_CHECK((thread_executed[0] & thread_executed[1]));
|
||||
} else {
|
||||
CATCH_REQUIRE(thread_executed[0] ^ thread_executed[1]);
|
||||
DOCTEST_CHECK((thread_executed[0] ^ thread_executed[1]));
|
||||
}
|
||||
|
||||
/* Stop spinlooping. */
|
||||
@@ -78,12 +78,12 @@ namespace ams::test {
|
||||
|
||||
/* Wait for threads to exit. */
|
||||
s32 dummy;
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::WaitSynchronization(std::addressof(dummy), thread_handles + 0, 1, -1)));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::WaitSynchronization(std::addressof(dummy), thread_handles + 1, 1, -1)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::WaitSynchronization(std::addressof(dummy), thread_handles + 0, 1, -1)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::WaitSynchronization(std::addressof(dummy), thread_handles + 1, 1, -1)));
|
||||
|
||||
/* Close thread handles. */
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,81 +52,81 @@ namespace ams::test {
|
||||
|
||||
}
|
||||
|
||||
CATCH_TEST_CASE("svc::SetHeapSize") {
|
||||
DOCTEST_TEST_CASE("svc::SetHeapSize") {
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
uintptr_t dummy;
|
||||
|
||||
/* Reset the heap. */
|
||||
uintptr_t addr;
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
|
||||
|
||||
/* Ensure that we don't leak memory. */
|
||||
const size_t initial_memory = GetPhysicalMemorySizeAvailable();
|
||||
ON_SCOPE_EXIT { CATCH_REQUIRE(initial_memory == GetPhysicalMemorySizeAvailable()); };
|
||||
ON_SCOPE_EXIT { DOCTEST_CHECK(initial_memory == GetPhysicalMemorySizeAvailable()); };
|
||||
|
||||
CATCH_SECTION("Unaligned and too big sizes fail") {
|
||||
DOCTEST_SUBCASE("Unaligned and too big sizes fail") {
|
||||
for (size_t i = 1; i < svc::HeapSizeAlignment; i = util::AlignUp(i + 1, os::MemoryPageSize)){
|
||||
CATCH_REQUIRE(svc::ResultInvalidSize::Includes(svc::SetHeapSize(std::addressof(dummy), i)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetHeapSize(std::addressof(dummy), i)));
|
||||
}
|
||||
CATCH_REQUIRE(svc::ResultInvalidSize::Includes(svc::SetHeapSize(std::addressof(dummy), 64_GB)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetHeapSize(std::addressof(dummy), 64_GB)));
|
||||
}
|
||||
|
||||
CATCH_SECTION("Larger size than address space fails") {
|
||||
CATCH_REQUIRE(svc::ResultOutOfMemory::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(svc::AddressMemoryRegionHeap39Size + 1, svc::HeapSizeAlignment))));
|
||||
DOCTEST_SUBCASE("Larger size than address space fails") {
|
||||
DOCTEST_CHECK(svc::ResultOutOfMemory::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(svc::AddressMemoryRegionHeap39Size + 1, svc::HeapSizeAlignment))));
|
||||
}
|
||||
|
||||
CATCH_SECTION("Bounded by resource limit") {
|
||||
CATCH_REQUIRE(svc::ResultLimitReached::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(GetPhysicalMemorySizeMax() + 1, svc::HeapSizeAlignment))));
|
||||
CATCH_REQUIRE(svc::ResultLimitReached::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(GetPhysicalMemorySizeAvailable() + 1, svc::HeapSizeAlignment))));
|
||||
DOCTEST_SUBCASE("Bounded by resource limit") {
|
||||
DOCTEST_CHECK(svc::ResultLimitReached::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(GetPhysicalMemorySizeMax() + 1, svc::HeapSizeAlignment))));
|
||||
DOCTEST_CHECK(svc::ResultLimitReached::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(GetPhysicalMemorySizeAvailable() + 1, svc::HeapSizeAlignment))));
|
||||
}
|
||||
|
||||
CATCH_SECTION("SetHeapSize gives heap memory") {
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
|
||||
DOCTEST_SUBCASE("SetHeapSize gives heap memory") {
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
|
||||
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
|
||||
}
|
||||
|
||||
CATCH_SECTION("SetHeapSize cannot remove read-only heap") {
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
|
||||
DOCTEST_SUBCASE("SetHeapSize cannot remove read-only heap") {
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
|
||||
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(addr, svc::HeapSizeAlignment, svc::MemoryPermission_Read)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(addr, svc::HeapSizeAlignment, svc::MemoryPermission_Read)));
|
||||
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_Read, 0);
|
||||
|
||||
CATCH_REQUIRE(svc::ResultInvalidCurrentMemory::Includes(svc::SetHeapSize(std::addressof(dummy), 0)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetHeapSize(std::addressof(dummy), 0)));
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(addr, svc::HeapSizeAlignment, svc::MemoryPermission_ReadWrite)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(addr, svc::HeapSizeAlignment, svc::MemoryPermission_ReadWrite)));
|
||||
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
|
||||
}
|
||||
|
||||
CATCH_SECTION("Heap memory does not survive unmap/re-map") {
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 2 * svc::HeapSizeAlignment)));
|
||||
DOCTEST_SUBCASE("Heap memory does not survive unmap/re-map") {
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 2 * svc::HeapSizeAlignment)));
|
||||
|
||||
u8 * const heap = reinterpret_cast<u8 *>(addr);
|
||||
|
||||
std::memset(heap, 0xAA, svc::HeapSizeAlignment);
|
||||
std::memset(heap + svc::HeapSizeAlignment, 0xBB, svc::HeapSizeAlignment);
|
||||
|
||||
CATCH_REQUIRE(heap[svc::HeapSizeAlignment] == 0xBB);
|
||||
CATCH_REQUIRE(std::memcmp(heap + svc::HeapSizeAlignment, heap + svc::HeapSizeAlignment + 1, svc::HeapSizeAlignment - 1) == 0);
|
||||
DOCTEST_CHECK(heap[svc::HeapSizeAlignment] == 0xBB);
|
||||
DOCTEST_CHECK(std::memcmp(heap + svc::HeapSizeAlignment, heap + svc::HeapSizeAlignment + 1, svc::HeapSizeAlignment - 1) == 0);
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
|
||||
|
||||
CATCH_REQUIRE(heap[0] == 0xAA);
|
||||
CATCH_REQUIRE(std::memcmp(heap, heap + 1, svc::HeapSizeAlignment - 1) == 0);
|
||||
DOCTEST_CHECK(heap[0] == 0xAA);
|
||||
DOCTEST_CHECK(std::memcmp(heap, heap + 1, svc::HeapSizeAlignment - 1) == 0);
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 2 * svc::HeapSizeAlignment)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 2 * svc::HeapSizeAlignment)));
|
||||
|
||||
CATCH_REQUIRE(heap[svc::HeapSizeAlignment] == 0x00);
|
||||
CATCH_REQUIRE(std::memcmp(heap + svc::HeapSizeAlignment, heap + svc::HeapSizeAlignment + 1, svc::HeapSizeAlignment - 1) == 0);
|
||||
DOCTEST_CHECK(heap[svc::HeapSizeAlignment] == 0x00);
|
||||
DOCTEST_CHECK(std::memcmp(heap + svc::HeapSizeAlignment, heap + svc::HeapSizeAlignment + 1, svc::HeapSizeAlignment - 1) == 0);
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,22 +30,22 @@ namespace ams::test {
|
||||
|
||||
alignas(os::MemoryPageSize) constinit u8 g_memory_permission_buffer[2 * os::MemoryPageSize];
|
||||
|
||||
CATCH_TEST_CASE("svc::SetMemoryPermission invalid arguments") {
|
||||
DOCTEST_TEST_CASE("svc::SetMemoryPermission invalid arguments") {
|
||||
const uintptr_t buffer = reinterpret_cast<uintptr_t>(g_memory_permission_buffer);
|
||||
|
||||
for (size_t i = 1; i < os::MemoryPageSize; ++i) {
|
||||
CATCH_REQUIRE(svc::ResultInvalidAddress::Includes(svc::SetMemoryPermission(buffer + i, os::MemoryPageSize, svc::MemoryPermission_Read)));
|
||||
CATCH_REQUIRE(svc::ResultInvalidSize::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize + i, svc::MemoryPermission_Read)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidAddress::Includes(svc::SetMemoryPermission(buffer + i, os::MemoryPageSize, svc::MemoryPermission_Read)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize + i, svc::MemoryPermission_Read)));
|
||||
}
|
||||
|
||||
CATCH_REQUIRE(svc::ResultInvalidSize::Includes(svc::SetMemoryPermission(buffer, 0, svc::MemoryPermission_Read)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetMemoryPermission(buffer, 0, svc::MemoryPermission_Read)));
|
||||
|
||||
{
|
||||
const u64 vmem_end = util::AlignDown(std::numeric_limits<u64>::max(), os::MemoryPageSize);
|
||||
CATCH_REQUIRE(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(vmem_end, 2 * os::MemoryPageSize, svc::MemoryPermission_Read)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(vmem_end, 2 * os::MemoryPageSize, svc::MemoryPermission_Read)));
|
||||
}
|
||||
|
||||
CATCH_REQUIRE(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(svc::AddressMap39End, os::MemoryPageSize, svc::MemoryPermission_Read)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(svc::AddressMap39End, os::MemoryPageSize, svc::MemoryPermission_Read)));
|
||||
|
||||
for (size_t i = 0; i < 0x100; ++i) {
|
||||
const auto perm = static_cast<svc::MemoryPermission>(i);
|
||||
@@ -53,14 +53,14 @@ namespace ams::test {
|
||||
continue;
|
||||
}
|
||||
|
||||
CATCH_REQUIRE(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, perm)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, perm)));
|
||||
}
|
||||
CATCH_REQUIRE(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_ReadExecute)));
|
||||
CATCH_REQUIRE(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_Write)));
|
||||
CATCH_REQUIRE(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_DontCare)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_ReadExecute)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_Write)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_DontCare)));
|
||||
}
|
||||
|
||||
CATCH_TEST_CASE("svc::SetMemoryPermission works on specific states") {
|
||||
DOCTEST_TEST_CASE("svc::SetMemoryPermission works on specific states") {
|
||||
/* Check that we have CodeData. */
|
||||
const uintptr_t bss_buffer = reinterpret_cast<uintptr_t>(g_memory_permission_buffer);
|
||||
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, 0);
|
||||
@@ -76,15 +76,15 @@ namespace ams::test {
|
||||
/* Get current mapping. */
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
|
||||
|
||||
/* Try to set permission. */
|
||||
if (CanSetMemoryPermission(mem_info.state) && mem_info.attribute == 0) {
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, svc::MemoryPermission_ReadWrite)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, svc::MemoryPermission_ReadWrite)));
|
||||
TestMemory(mem_info.base_address, mem_info.size, mem_info.state, svc::MemoryPermission_ReadWrite, mem_info.attribute);
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, mem_info.permission)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, mem_info.permission)));
|
||||
} else {
|
||||
CATCH_REQUIRE(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, svc::MemoryPermission_Read)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, svc::MemoryPermission_Read)));
|
||||
}
|
||||
|
||||
const uintptr_t next_address = mem_info.base_address + mem_info.size;
|
||||
@@ -96,25 +96,25 @@ namespace ams::test {
|
||||
}
|
||||
}
|
||||
|
||||
CATCH_TEST_CASE("svc::SetMemoryPermission allows for free movement between RW-, R--, ---") {
|
||||
DOCTEST_TEST_CASE("svc::SetMemoryPermission allows for free movement between RW-, R--, ---") {
|
||||
/* Define helper. */
|
||||
auto test_set_memory_permission = [](uintptr_t address, size_t size){
|
||||
/* Get the permission. */
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
|
||||
|
||||
const svc::MemoryPermission legal_states[] = { svc::MemoryPermission_None, svc::MemoryPermission_Read, svc::MemoryPermission_ReadWrite };
|
||||
for (const auto src_state : legal_states) {
|
||||
for (const auto dst_state : legal_states) {
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(address, size, svc::MemoryPermission_None)));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(address, size, src_state)));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(address, size, dst_state)));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(address, size, svc::MemoryPermission_None)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, svc::MemoryPermission_None)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, src_state)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, dst_state)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, svc::MemoryPermission_None)));
|
||||
}
|
||||
}
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(address, size, mem_info.permission)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, mem_info.permission)));
|
||||
};
|
||||
|
||||
/* Test that we can freely move about .bss buffers. */
|
||||
@@ -130,27 +130,27 @@ namespace ams::test {
|
||||
/* TODO: AliasCodeData */
|
||||
}
|
||||
|
||||
CATCH_TEST_CASE("svc::SetMemoryPermission fails when the memory has non-zero attribute") {
|
||||
DOCTEST_TEST_CASE("svc::SetMemoryPermission fails when the memory has non-zero attribute") {
|
||||
const uintptr_t bss_buffer = reinterpret_cast<uintptr_t>(g_memory_permission_buffer);
|
||||
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, 0);
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryAttribute(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryAttribute_Uncached, svc::MemoryAttribute_Uncached)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryAttribute(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryAttribute_Uncached, svc::MemoryAttribute_Uncached)));
|
||||
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, svc::MemoryAttribute_Uncached);
|
||||
|
||||
CATCH_REQUIRE(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
|
||||
CATCH_REQUIRE(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
|
||||
CATCH_REQUIRE(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
|
||||
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryAttribute(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryAttribute_Uncached, 0)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryAttribute(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryAttribute_Uncached, 0)));
|
||||
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, 0);
|
||||
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -86,26 +86,26 @@ namespace ams::test {
|
||||
svc::Handle thread_handles[2];
|
||||
|
||||
/* Create threads. */
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestYieldHigherOrSamePriorityThread), 0, sp_higher, priority, core)));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestYieldLowerOrSamePriorityThread), 0, sp_lower, priority, core)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestYieldHigherOrSamePriorityThread), 0, sp_higher, priority, core)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestYieldLowerOrSamePriorityThread), 0, sp_lower, priority, core)));
|
||||
|
||||
/* Start threads. */
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
|
||||
|
||||
/* Wait for higher priority thread. */
|
||||
WaitSynchronization(thread_handles[0]);
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
|
||||
|
||||
/* Signal the lower priority thread to exit. */
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SignalEvent(g_write_handles[2])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SignalEvent(g_write_handles[2])));
|
||||
|
||||
/* Wait for the lower priority thread. */
|
||||
WaitSynchronization(thread_handles[1]);
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
|
||||
|
||||
/* Check that the switch was correct. */
|
||||
CATCH_REQUIRE(g_correct_switch_threads);
|
||||
DOCTEST_CHECK(g_correct_switch_threads);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,55 +118,55 @@ namespace ams::test {
|
||||
svc::Handle thread_handles[2];
|
||||
|
||||
/* Create threads. */
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestYieldHigherOrSamePriorityThread), 0, sp_higher, priority, core)));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestYieldLowerOrSamePriorityThread), 0, sp_lower, priority + 1, core)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestYieldHigherOrSamePriorityThread), 0, sp_higher, priority, core)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestYieldLowerOrSamePriorityThread), 0, sp_lower, priority + 1, core)));
|
||||
|
||||
/* Start threads. */
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
|
||||
|
||||
/* Wait for higher priority thread. */
|
||||
WaitSynchronization(thread_handles[0]);
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
|
||||
|
||||
/* Signal the lower priority thread to exit. */
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SignalEvent(g_write_handles[2])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SignalEvent(g_write_handles[2])));
|
||||
|
||||
/* Wait for the lower priority thread. */
|
||||
WaitSynchronization(thread_handles[1]);
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
|
||||
|
||||
/* Check that the switch was correct. */
|
||||
CATCH_REQUIRE(g_correct_switch_threads);
|
||||
DOCTEST_CHECK(g_correct_switch_threads);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CATCH_TEST_CASE( "svc::SleepThread: Thread sleeps for time specified" ) {
|
||||
DOCTEST_TEST_CASE( "svc::SleepThread: Thread sleeps for time specified" ) {
|
||||
for (s64 ns = 1; ns < TimeSpan::FromSeconds(1).GetNanoSeconds(); ns *= 2) {
|
||||
const auto start = os::GetSystemTickOrdered();
|
||||
svc::SleepThread(ns);
|
||||
const auto end = os::GetSystemTickOrdered();
|
||||
|
||||
const s64 taken_ns = (end - start).ToTimeSpan().GetNanoSeconds();
|
||||
CATCH_REQUIRE( taken_ns >= ns );
|
||||
DOCTEST_CHECK( taken_ns >= ns );
|
||||
}
|
||||
}
|
||||
|
||||
CATCH_TEST_CASE( "svc::SleepThread: Yield is behaviorally correct" ) {
|
||||
DOCTEST_TEST_CASE( "svc::SleepThread: Yield is behaviorally correct" ) {
|
||||
/* Create events. */
|
||||
for (size_t i = 0; i < util::size(g_write_handles); ++i) {
|
||||
g_read_handles[i] = svc::InvalidHandle;
|
||||
g_write_handles[i] = svc::InvalidHandle;
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CreateEvent(g_write_handles + i, g_read_handles + i)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateEvent(g_write_handles + i, g_read_handles + i)));
|
||||
}
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
for (size_t i = 0; i < util::size(g_write_handles); ++i) {
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CloseHandle(g_read_handles[i])));
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::CloseHandle(g_write_handles[i])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(g_read_handles[i])));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(g_write_handles[i])));
|
||||
g_read_handles[i] = svc::InvalidHandle;
|
||||
g_write_handles[i] = svc::InvalidHandle;
|
||||
}
|
||||
@@ -174,14 +174,14 @@ namespace ams::test {
|
||||
|
||||
/* Create heap. */
|
||||
ScopedHeap heap(3 * os::MemoryPageSize);
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_None)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_None)));
|
||||
ON_SCOPE_EXIT {
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_ReadWrite)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_ReadWrite)));
|
||||
};
|
||||
const uintptr_t sp_higher = heap.GetAddress() + 1 * os::MemoryPageSize;
|
||||
const uintptr_t sp_lower = heap.GetAddress() + 3 * os::MemoryPageSize;
|
||||
|
||||
CATCH_SECTION("svc::SleepThread: Yields do not switch to a thread of lower priority.") {
|
||||
DOCTEST_SUBCASE("svc::SleepThread: Yields do not switch to a thread of lower priority.") {
|
||||
/* Test yield without migration. */
|
||||
{
|
||||
/* Configure for yield test. */
|
||||
@@ -201,7 +201,7 @@ namespace ams::test {
|
||||
}
|
||||
}
|
||||
|
||||
CATCH_SECTION("svc::SleepThread: ToAnyThread switches to a thread of same or lower priority.") {
|
||||
DOCTEST_SUBCASE("svc::SleepThread: ToAnyThread switches to a thread of same or lower priority.") {
|
||||
/* Test to same priority. */
|
||||
{
|
||||
/* Configure for yield test. */
|
||||
@@ -221,7 +221,7 @@ namespace ams::test {
|
||||
}
|
||||
}
|
||||
|
||||
CATCH_SECTION("svc::SleepThread: Yield switches to another thread of same priority.") {
|
||||
DOCTEST_SUBCASE("svc::SleepThread: Yield switches to another thread of same priority.") {
|
||||
/* Test yield without migration. */
|
||||
{
|
||||
/* Configure for yield test. */
|
||||
@@ -241,7 +241,7 @@ namespace ams::test {
|
||||
}
|
||||
}
|
||||
|
||||
CATCH_SECTION("svc::SleepThread: Yield with bogus timeout does not switch to another thread same priority") {
|
||||
DOCTEST_SUBCASE("svc::SleepThread: Yield with bogus timeout does not switch to another thread same priority") {
|
||||
/* Configure for yield test. */
|
||||
g_should_switch_threads = false;
|
||||
g_thread_wait_ns = INT64_C(-5);
|
||||
|
||||
43
tests/TestSvc/source/test_thread_creation.arch.arm64.s
Normal file
43
tests/TestSvc/source/test_thread_creation.arch.arm64.s
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/* ams::test::TestThreadCreateRegistersOnFunctionEntry(void *ctx) */
|
||||
.section .text._ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv, "ax", %progbits
|
||||
.global _ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv
|
||||
.type _ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv, %function
|
||||
_ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv:
|
||||
/* Save all registers to our context. */
|
||||
stp x0, x1, [x0, #0x00]
|
||||
stp x2, x3, [x0, #0x10]
|
||||
stp x4, x5, [x0, #0x20]
|
||||
stp x6, x7, [x0, #0x30]
|
||||
stp x8, x9, [x0, #0x40]
|
||||
stp x10, x11, [x0, #0x50]
|
||||
stp x12, x13, [x0, #0x60]
|
||||
stp x14, x15, [x0, #0x70]
|
||||
stp x16, x17, [x0, #0x80]
|
||||
stp x18, x19, [x0, #0x90]
|
||||
stp x20, x21, [x0, #0xA0]
|
||||
stp x22, x23, [x0, #0xB0]
|
||||
stp x24, x25, [x0, #0xC0]
|
||||
stp x26, x27, [x0, #0xD0]
|
||||
stp x28, x29, [x0, #0xE0]
|
||||
|
||||
mov x1, sp
|
||||
stp x30, x1, [x0, #0xF0]
|
||||
|
||||
/* Exit the thread. */
|
||||
svc 0xa
|
||||
65
tests/TestSvc/source/test_thread_creation.cpp
Normal file
65
tests/TestSvc/source/test_thread_creation.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "util_common.hpp"
|
||||
#include "util_scoped_heap.hpp"
|
||||
|
||||
namespace ams::test {
|
||||
|
||||
void TestThreadCreateRegistersOnFunctionEntry(void *ctx);
|
||||
|
||||
DOCTEST_TEST_CASE( "Creating a thread results in fixed register contents." ) {
|
||||
/* Create heap. */
|
||||
ScopedHeap heap(os::MemoryPageSize);
|
||||
|
||||
/* Create register buffer. */
|
||||
u64 thread_registers[32];
|
||||
std::memset(thread_registers, 0xCC, sizeof(thread_registers));
|
||||
|
||||
/* Create thread. */
|
||||
svc::Handle thread_handle;
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(std::addressof(thread_handle), reinterpret_cast<uintptr_t>(&TestThreadCreateRegistersOnFunctionEntry), reinterpret_cast<uintptr_t>(thread_registers), heap.GetAddress() + os::MemoryPageSize, HighestTestPriority, NumCores - 1)));
|
||||
|
||||
/* Start thread. */
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handle)));
|
||||
|
||||
/* Wait for thread to exit. */
|
||||
s32 dummy;
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::WaitSynchronization(std::addressof(dummy), std::addressof(thread_handle), 1, -1)));
|
||||
|
||||
/* Close thread handle. */
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handle)));
|
||||
|
||||
/* Check thread initial registers. */
|
||||
for (size_t i = 0; i < util::size(thread_registers); ++i) {
|
||||
if (i == 0) {
|
||||
/* X0 is argument. */
|
||||
DOCTEST_CHECK(thread_registers[i] == reinterpret_cast<uintptr_t>(thread_registers));
|
||||
} else if (i == 18) {
|
||||
/* X18 is an odd cfi value. */
|
||||
DOCTEST_CHECK(thread_registers[i] != 0);
|
||||
DOCTEST_CHECK((thread_registers[i] & 0x1) != 0);
|
||||
} else if (i == 31) {
|
||||
/* SP is user-provided sp. */
|
||||
DOCTEST_CHECK(thread_registers[i] == (heap.GetAddress() + os::MemoryPageSize));
|
||||
} else {
|
||||
/* All other registers are zero. */
|
||||
DOCTEST_CHECK(thread_registers[i] == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
28
tests/TestSvc/source/test_thread_pinning.cpp
Normal file
28
tests/TestSvc/source/test_thread_pinning.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "util_common.hpp"
|
||||
#include "util_scoped_heap.hpp"
|
||||
|
||||
namespace ams::test {
|
||||
|
||||
DOCTEST_TEST_CASE( "Setting a thread's disable count will cause it to become pinned." ) {
|
||||
DoWithThreadPinning([]() {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,33 +14,33 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "util_catch.hpp"
|
||||
#include "util_test_framework.hpp"
|
||||
|
||||
namespace ams::test {
|
||||
|
||||
inline void TestMemory(uintptr_t address, svc::MemoryState state, svc::MemoryPermission perm, u32 attr) {
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
|
||||
|
||||
CATCH_REQUIRE(mem_info.base_address <= address);
|
||||
CATCH_REQUIRE(address < (mem_info.base_address + mem_info.size));
|
||||
CATCH_REQUIRE(mem_info.state == state);
|
||||
CATCH_REQUIRE(mem_info.permission == perm);
|
||||
CATCH_REQUIRE(mem_info.attribute == attr);
|
||||
DOCTEST_CHECK(mem_info.base_address <= address);
|
||||
DOCTEST_CHECK(address < (mem_info.base_address + mem_info.size));
|
||||
DOCTEST_CHECK(mem_info.state == state);
|
||||
DOCTEST_CHECK(mem_info.permission == perm);
|
||||
DOCTEST_CHECK(mem_info.attribute == attr);
|
||||
}
|
||||
|
||||
inline void TestMemory(uintptr_t address, size_t size, svc::MemoryState state, svc::MemoryPermission perm, u32 attr) {
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
CATCH_REQUIRE(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
|
||||
|
||||
CATCH_REQUIRE(mem_info.base_address <= address);
|
||||
CATCH_REQUIRE(mem_info.base_address < (address + size));
|
||||
CATCH_REQUIRE((address + size) <= (mem_info.base_address + mem_info.size));
|
||||
CATCH_REQUIRE(mem_info.state == state);
|
||||
CATCH_REQUIRE(mem_info.permission == perm);
|
||||
CATCH_REQUIRE(mem_info.attribute == attr);
|
||||
DOCTEST_CHECK(mem_info.base_address <= address);
|
||||
DOCTEST_CHECK(mem_info.base_address < (address + size));
|
||||
DOCTEST_CHECK((address + size) <= (mem_info.base_address + mem_info.size));
|
||||
DOCTEST_CHECK(mem_info.state == state);
|
||||
DOCTEST_CHECK(mem_info.permission == perm);
|
||||
DOCTEST_CHECK(mem_info.attribute == attr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "util_catch.hpp"
|
||||
#include "util_test_framework.hpp"
|
||||
|
||||
namespace ams::test {
|
||||
|
||||
@@ -32,4 +32,70 @@ namespace ams::test {
|
||||
return priority == ((core == (NumCores - 1)) ? DpcManagerPreemptionThreadPriority : DpcManagerNormalThreadPriority);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void DoWithThreadPinning(F f) {
|
||||
/* Get the thread local region. */
|
||||
auto * const tlr = svc::GetThreadLocalRegion();
|
||||
|
||||
/* Require that we're not currently pinned. */
|
||||
DOCTEST_CHECK((tlr->disable_count == 0));
|
||||
DOCTEST_CHECK((!tlr->interrupt_flag));
|
||||
|
||||
/* Request to pin ourselves. */
|
||||
tlr->disable_count = 1;
|
||||
|
||||
/* Wait long enough that we can be confident preemption will occur, and therefore our interrupt flag will be set. */
|
||||
{
|
||||
constexpr auto MinimumTicksToGuaranteeInterruptFlag = ::ams::svc::Tick(PreemptionTimeSpan) + ::ams::svc::Tick(PreemptionTimeSpan) + 2;
|
||||
|
||||
auto GetSystemTickForPinnedThread = []() ALWAYS_INLINE_LAMBDA -> ::ams::svc::Tick {
|
||||
s64 v;
|
||||
__asm__ __volatile__ ("mrs %x[v], cntpct_el0" : [v]"=r"(v) :: "memory");
|
||||
return ::ams::svc::Tick(v);
|
||||
};
|
||||
|
||||
const auto start_tick = GetSystemTickForPinnedThread();
|
||||
while (true) {
|
||||
if (tlr->interrupt_flag) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (const auto cur_tick = GetSystemTickForPinnedThread(); (cur_tick - start_tick) > MinimumTicksToGuaranteeInterruptFlag) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We're pinned. Execute the user callback. */
|
||||
bool callback_succeeded = true;
|
||||
{
|
||||
if constexpr (requires { { f() } -> std::convertible_to<bool>; }) {
|
||||
callback_succeeded = f();
|
||||
} else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear our disable count. */
|
||||
tlr->disable_count = 0;
|
||||
|
||||
/* Get our interrupt flag. */
|
||||
const auto interrupt_flag_while_pinned = tlr->interrupt_flag;
|
||||
|
||||
/* Unpin ourselves. */
|
||||
if (interrupt_flag_while_pinned) {
|
||||
svc::SynchronizePreemptionState();
|
||||
}
|
||||
|
||||
/* Get our interrupt flag. */
|
||||
const auto interrupt_flag_after_unpin = tlr->interrupt_flag;
|
||||
|
||||
/* We have access to all SVCs again. Check that our pinning happened as expected. */
|
||||
DOCTEST_CHECK(interrupt_flag_while_pinned);
|
||||
DOCTEST_CHECK(!interrupt_flag_after_unpin);
|
||||
|
||||
/* Check that our callback succeeded. */
|
||||
DOCTEST_CHECK(callback_succeeded);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "util_catch.hpp"
|
||||
#include "util_test_framework.hpp"
|
||||
|
||||
namespace ams::test {
|
||||
|
||||
@@ -31,14 +31,14 @@ namespace ams::test {
|
||||
|
||||
~ScopedHeap() {
|
||||
const auto result = svc::SetHeapSize(std::addressof(m_address), 0);
|
||||
CATCH_REQUIRE(R_SUCCEEDED(result));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(result));
|
||||
}
|
||||
|
||||
void SetHeapSize(size_t size) {
|
||||
m_size = util::AlignUp(size, svc::HeapSizeAlignment);
|
||||
|
||||
const auto result = svc::SetHeapSize(std::addressof(m_address), m_size);
|
||||
CATCH_REQUIRE(R_SUCCEEDED(result));
|
||||
DOCTEST_CHECK(R_SUCCEEDED(result));
|
||||
}
|
||||
|
||||
uintptr_t GetAddress() const { return m_address; }
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define CATCH_CONFIG_NOSTDOUT
|
||||
#define CATCH_CONFIG_PREFIX_ALL
|
||||
#define CATCH_CONFIG_DISABLE_EXCEPTIONS
|
||||
#define CATCH_CONFIG_NO_POSIX_SIGNALS
|
||||
#include "catch.hpp"
|
||||
#define DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
|
||||
#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS
|
||||
#define DOCTEST_CONFIG_NO_EXCEPTIONS
|
||||
#define DOCTEST_CONFIG_NO_POSIX_SIGNALS
|
||||
#include "doctest.h"
|
||||
Reference in New Issue
Block a user