Compare commits
10 Commits
0.11.1
...
dmnt_new_f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79d7e9197c | ||
|
|
11b8a92458 | ||
|
|
332dbdd497 | ||
|
|
98cc051387 | ||
|
|
f2944d36ba | ||
|
|
46d79387e8 | ||
|
|
0bb2c0a04f | ||
|
|
eca2b453ae | ||
|
|
e14dc18bd3 | ||
|
|
c7743c6098 |
@@ -27,7 +27,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
|
||||
You can find a copy of the license in the [LICENSE file](LICENSE).
|
||||
|
||||
Exemptions:
|
||||
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project as GPLv2 or later.
|
||||
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the Atmosphère project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
|
||||
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project under the Zero-Clause BSD license.
|
||||
|
||||
Credits
|
||||
|
||||
21
docs/licensing_exemptions/MIT_LICENSE
Normal file
21
docs/licensing_exemptions/MIT_LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018-2020 Atmosphère-NX
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = da6eac986d7f67ca3dcc3a8df0c191ffbea4686f
|
||||
parent = eb48e7cc5943bd594b3166cb888d3dadb0474107
|
||||
commit = c1fe12fcbd661ae47673af5f224804c92eebdbb5
|
||||
parent = 98cc051387515977dcdede8ab0f1b09077f2f24b
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -10,7 +10,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
|
||||
You can find a copy of the license in the [LICENSE file](LICENSE).
|
||||
|
||||
Exemptions:
|
||||
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project as GPLv2 or later.
|
||||
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the atmosphere-libs project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
|
||||
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project under the Zero-Clause BSD license.
|
||||
|
||||
Credits
|
||||
|
||||
@@ -10,7 +10,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
|
||||
You can find a copy of the license in the [LICENSE file](LICENSE).
|
||||
|
||||
Exemptions:
|
||||
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project as GPLv2 or later.
|
||||
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the libmesosphere project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
|
||||
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project under the Zero-Clause BSD license.
|
||||
|
||||
Credits
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace ams::kern {
|
||||
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages, size_t num_pages, size_t alignment, size_t offset, size_t guard_pages) const;
|
||||
|
||||
void Update(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr);
|
||||
void UpdateLock(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, void (KMemoryBlock::*lock_func)(KMemoryPermission new_perm), KMemoryPermission perm);
|
||||
|
||||
iterator FindIterator(KProcessAddress address) const {
|
||||
return this->memory_block_tree.find(KMemoryBlock(address, 1, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None));
|
||||
|
||||
@@ -197,6 +197,7 @@ namespace ams::kern {
|
||||
cur_address += cur_info.GetSize();
|
||||
remaining_pages -= cur_info.GetNumPages();
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
/* Find the iterator now that we've updated. */
|
||||
@@ -227,6 +228,86 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, void (KMemoryBlock::*lock_func)(KMemoryPermission new_perm), KMemoryPermission perm) {
|
||||
/* Ensure for auditing that we never end up with an invalid tree. */
|
||||
KScopedMemoryBlockManagerAuditor auditor(this);
|
||||
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(address), PageSize));
|
||||
|
||||
KProcessAddress cur_address = address;
|
||||
size_t remaining_pages = num_pages;
|
||||
iterator it = this->FindIterator(address);
|
||||
iterator prev = it, next = it;
|
||||
bool check_coalesce_prev = false, check_coalesce_next = false;
|
||||
|
||||
while (remaining_pages > 0) {
|
||||
const size_t remaining_size = remaining_pages * PageSize;
|
||||
KMemoryInfo cur_info = it->GetMemoryInfo();
|
||||
|
||||
/* If we need to, create a new block before and insert it. */
|
||||
if (cur_info.address != GetInteger(cur_address)) {
|
||||
KMemoryBlock *new_block = allocator->Allocate();
|
||||
|
||||
it->Split(new_block, cur_address);
|
||||
it = this->memory_block_tree.insert(*new_block);
|
||||
it++;
|
||||
|
||||
cur_info = it->GetMemoryInfo();
|
||||
cur_address = cur_info.GetAddress();
|
||||
} else if (cur_address == address && cur_address != this->start_address) {
|
||||
/* If there's a previous, we should check for coalescing. */
|
||||
check_coalesce_prev = true;
|
||||
prev--;
|
||||
} else if (cur_info.GetSize() > remaining_size) {
|
||||
/* If we need to, create a new block after and insert it. */
|
||||
KMemoryBlock *new_block = allocator->Allocate();
|
||||
|
||||
it->Split(new_block, cur_address + remaining_size);
|
||||
it = this->memory_block_tree.insert(*new_block);
|
||||
|
||||
cur_info = it->GetMemoryInfo();
|
||||
} else if (cur_info.GetSize() == remaining_size) {
|
||||
/* Otherwise if we can map precisely, we may need to check for coalescing against next block. */
|
||||
next = it;
|
||||
++next;
|
||||
if (next != this->memory_block_tree.end()) {
|
||||
check_coalesce_next = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Call the locked update function. */
|
||||
(std::addressof(*it)->*lock_func)(perm);
|
||||
cur_address += cur_info.GetSize();
|
||||
remaining_pages -= cur_info.GetNumPages();
|
||||
it++;
|
||||
}
|
||||
|
||||
/* If we should try to coalesce prev, do so. */
|
||||
if (check_coalesce_prev) {
|
||||
it = prev;
|
||||
it++;
|
||||
if (prev->HasSameProperties(*it)) {
|
||||
KMemoryBlock *block = std::addressof(*it);
|
||||
const size_t pages = it->GetNumPages();
|
||||
this->memory_block_tree.erase(it);
|
||||
allocator->Free(block);
|
||||
prev->Add(pages);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we should try to coalesce next, do so. */
|
||||
if (check_coalesce_next) {
|
||||
it = next;
|
||||
it--;
|
||||
if (it->HasSameProperties(*next)) {
|
||||
KMemoryBlock *block = std::addressof(*next);
|
||||
const size_t pages = next->GetNumPages();
|
||||
this->memory_block_tree.erase(next);
|
||||
allocator->Free(block);
|
||||
it->Add(pages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Debug. */
|
||||
bool KMemoryBlockManager::CheckState() const {
|
||||
/* If we fail, we should dump blocks. */
|
||||
|
||||
@@ -14,7 +14,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
|
||||
You can find a copy of the license in the [LICENSE file](LICENSE).
|
||||
|
||||
Exemptions:
|
||||
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libstratosphere project as GPLv2 or later.
|
||||
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the libstratosphere project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
|
||||
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libstratosphere project under the Zero-Clause BSD license.
|
||||
|
||||
Credits
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include <stratosphere/ncm.hpp>
|
||||
#include <stratosphere/nim.hpp>
|
||||
#include <stratosphere/patcher.hpp>
|
||||
#include <stratosphere/pgl.hpp>
|
||||
#include <stratosphere/psc.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
#include <stratosphere/reg.hpp>
|
||||
|
||||
@@ -16,5 +16,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ldr/ldr_types.hpp"
|
||||
#include "ldr/ldr_pm_api.hpp"
|
||||
#include <stratosphere/ldr/ldr_types.hpp>
|
||||
#include <stratosphere/ldr/ldr_shell_api.hpp>
|
||||
#include <stratosphere/ldr/ldr_pm_api.hpp>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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.hpp>
|
||||
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||
#include <stratosphere/ldr/ldr_types.hpp>
|
||||
|
||||
namespace ams::ldr {
|
||||
|
||||
/* Shell API. */
|
||||
Result InitializeForShell();
|
||||
Result FinalizeForShell();
|
||||
|
||||
Result SetProgramArgument(ncm::ProgramId program_id, const void *arg, size_t size);
|
||||
Result FlushArguments();
|
||||
|
||||
}
|
||||
@@ -402,6 +402,34 @@ namespace ams::ncm {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct SystemDebugAppletId {
|
||||
u64 value;
|
||||
|
||||
constexpr operator ProgramId() const {
|
||||
return { this->value };
|
||||
}
|
||||
|
||||
static const SystemDebugAppletId Start;
|
||||
|
||||
static const SystemDebugAppletId SnapShotDumper;
|
||||
|
||||
static const SystemDebugAppletId End;
|
||||
};
|
||||
|
||||
inline constexpr const SystemDebugAppletId SystemDebugAppletId::Start = { 0x0100000000002000ul };
|
||||
|
||||
inline constexpr const SystemDebugAppletId SystemDebugAppletId::SnapShotDumper = { 0x0100000000002071ul };
|
||||
|
||||
inline constexpr const SystemDebugAppletId SystemDebugAppletId::End = { 0x0100000000002FFFul };
|
||||
|
||||
inline constexpr bool IsSystemDebugAppletId(const ProgramId &program_id) {
|
||||
return SystemDebugAppletId::Start <= program_id && program_id <= SystemDebugAppletId::End;
|
||||
}
|
||||
|
||||
inline constexpr bool IsSystemDebugAppletId(const SystemDebugAppletId &program_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct LibraryAppletId {
|
||||
u64 value;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <stratosphere/os/os_random.hpp>
|
||||
#include <stratosphere/os/os_mutex.hpp>
|
||||
#include <stratosphere/os/os_condition_variable.hpp>
|
||||
#include <stratosphere/os/os_sdk_mutex.hpp>
|
||||
#include <stratosphere/os/os_rw_lock.hpp>
|
||||
#include <stratosphere/os/os_semaphore.hpp>
|
||||
#include <stratosphere/os/os_event.hpp>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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.hpp>
|
||||
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
class SdkConditionVariable;
|
||||
|
||||
struct SdkMutexType {
|
||||
union {
|
||||
s32 _arr[sizeof(impl::InternalCriticalSectionStorage) / sizeof(s32)];
|
||||
impl::InternalCriticalSectionStorage _storage;
|
||||
};
|
||||
};
|
||||
static_assert(std::is_trivial<SdkMutexType>::value);
|
||||
|
||||
void InitializeSdkMutex(SdkMutexType *mutex);
|
||||
|
||||
void LockSdkMutex(SdkMutexType *mutex);
|
||||
bool TryLockSdkMutex(SdkMutexType *mutex);
|
||||
void UnlockSdkMutex(SdkMutexType *mutex);
|
||||
|
||||
bool IsSdkMutexLockedByCurrentThread(const SdkMutexType *mutex);
|
||||
|
||||
class SdkMutex {
|
||||
private:
|
||||
friend class SdkConditionVariable;
|
||||
private:
|
||||
SdkMutexType mutex;
|
||||
public:
|
||||
constexpr SdkMutex() : mutex{{0}} { /* ... */ }
|
||||
|
||||
ALWAYS_INLINE void Lock() { return os::LockSdkMutex(std::addressof(this->mutex)); }
|
||||
ALWAYS_INLINE bool TryLock() { return os::TryLockSdkMutex(std::addressof(this->mutex)); }
|
||||
ALWAYS_INLINE void Unlock() { return os::UnlockSdkMutex(std::addressof(this->mutex)); }
|
||||
|
||||
ALWAYS_INLINE bool IsLockedByCurrentThread() const { return os::IsSdkMutexLockedByCurrentThread(std::addressof(this->mutex)); }
|
||||
|
||||
ALWAYS_INLINE void lock() { return this->Lock(); }
|
||||
ALWAYS_INLINE bool try_lock() { return this->TryLock(); }
|
||||
ALWAYS_INLINE void unlock() { return this->Unlock(); }
|
||||
};
|
||||
|
||||
}
|
||||
23
libraries/libstratosphere/include/stratosphere/pgl.hpp
Normal file
23
libraries/libstratosphere/include/stratosphere/pgl.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <stratosphere/pgl/pgl_types.hpp>
|
||||
#include <stratosphere/pgl/pgl_event_observer.hpp>
|
||||
#include <stratosphere/pgl/pgl_shell_api.hpp>
|
||||
#include <stratosphere/pgl/pgl_shell_api.hpp>
|
||||
#include <stratosphere/pgl/srv/pgl_srv_api.hpp>
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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 <stratosphere/os.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
#include <stratosphere/pgl/pgl_types.hpp>
|
||||
#include <stratosphere/pgl/sf/pgl_sf_i_event_observer.hpp>
|
||||
|
||||
namespace ams::pgl {
|
||||
|
||||
class EventObserver {
|
||||
NON_COPYABLE(EventObserver);
|
||||
private:
|
||||
std::shared_ptr<pgl::sf::IEventObserver> interface;
|
||||
public:
|
||||
EventObserver() { /* ... */ }
|
||||
explicit EventObserver(std::shared_ptr<pgl::sf::IEventObserver> intf) : interface(std::move(intf)) { /* ... */ }
|
||||
|
||||
EventObserver(EventObserver &&rhs) {
|
||||
this->interface = std::move(rhs.interface);
|
||||
}
|
||||
|
||||
EventObserver &operator=(EventObserver &&rhs) {
|
||||
EventObserver(std::move(rhs)).Swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Swap(EventObserver &rhs) {
|
||||
std::swap(this->interface, rhs.interface);
|
||||
}
|
||||
public:
|
||||
Result GetSystemEvent(os::SystemEventType *out) {
|
||||
ams::sf::CopyHandle handle;
|
||||
R_TRY(this->interface->GetProcessEventHandle(std::addressof(handle)));
|
||||
os::AttachSystemEvent(out, handle.GetValue(), true, svc::InvalidHandle, false, os::EventClearMode_AutoClear);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetProcessEventInfo(pm::ProcessEventInfo *out) {
|
||||
return this->interface->GetProcessEventInfo(out);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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.hpp>
|
||||
#include <stratosphere/pgl/pgl_types.hpp>
|
||||
#include <stratosphere/pgl/pgl_event_observer.hpp>
|
||||
|
||||
namespace ams::pgl {
|
||||
|
||||
Result Initialize();
|
||||
void Finalize();
|
||||
|
||||
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 process_flags, u8 pgl_flags);
|
||||
Result TerminateProcess(os::ProcessId process_id);
|
||||
Result LaunchProgramFromHost(os::ProcessId *out, const char *content_path, u32 process_flags);
|
||||
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *content_path);
|
||||
Result GetApplicationProcessId(os::ProcessId *out);
|
||||
Result BoostSystemMemoryResourceLimit(u64 size);
|
||||
Result IsProcessTracked(bool *out, os::ProcessId process_id);
|
||||
Result EnableApplicationCrashReport(bool enabled);
|
||||
Result IsApplicationCrashReportEnabled(bool *out);
|
||||
Result EnableApplicationAllThreadDumpOnCrash(bool enabled);
|
||||
Result TriggerApplicationSnapShotDumper(const char *arg, SnapShotDumpType dump_type);
|
||||
|
||||
Result GetEventObserver(pgl::EventObserver *out);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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.hpp>
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
#include <stratosphere/ncm.hpp>
|
||||
|
||||
namespace ams::pgl {
|
||||
|
||||
enum LaunchFlags : u8 {
|
||||
LaunchFlags_None = 0,
|
||||
LaunchFlags_EnableDetailedCrashReport = (1 << 0),
|
||||
LaunchFlags_EnableCrashReportScreenShotForProduction = (1 << 1),
|
||||
LaunchFlags_EnableCrashReportScreenShotForDevelop = (1 << 2),
|
||||
};
|
||||
|
||||
enum class SnapShotDumpType : u32 {
|
||||
None = 0,
|
||||
Auto = 1,
|
||||
Full = 2,
|
||||
};
|
||||
|
||||
/* TODO: Is this really nn::ncm::Content<Something>Info? */
|
||||
struct ContentMetaInfo {
|
||||
u64 id;
|
||||
u32 version;
|
||||
ncm::ContentType content_type;
|
||||
u8 id_offset;
|
||||
u8 reserved_0E[2];
|
||||
|
||||
static constexpr ContentMetaInfo Make(u64 id, u32 version, ncm::ContentType content_type, u8 id_offset) {
|
||||
return {
|
||||
.id = id,
|
||||
.version = version,
|
||||
.content_type = content_type,
|
||||
.id_offset = id_offset,
|
||||
};
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ContentMetaInfo) == 0x10 && std::is_pod<ContentMetaInfo>::value);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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.hpp>
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
#include <stratosphere/pgl/pgl_types.hpp>
|
||||
|
||||
namespace ams::pgl::sf {
|
||||
|
||||
class IEventObserver : public ams::sf::IServiceObject {
|
||||
protected:
|
||||
enum class CommandId {
|
||||
GetProcessEventHandle = 0,
|
||||
GetProcessEventInfo = 1,
|
||||
};
|
||||
public:
|
||||
/* Actual commands. */
|
||||
virtual Result GetProcessEventHandle(ams::sf::OutCopyHandle out) = 0;
|
||||
virtual Result GetProcessEventInfo(ams::sf::Out<pm::ProcessEventInfo> out) = 0;
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
MAKE_SERVICE_COMMAND_META(GetProcessEventHandle),
|
||||
MAKE_SERVICE_COMMAND_META(GetProcessEventInfo),
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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.hpp>
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
#include <stratosphere/pgl/pgl_types.hpp>
|
||||
#include <stratosphere/pgl/sf/pgl_sf_i_event_observer.hpp>
|
||||
|
||||
namespace ams::pgl::sf {
|
||||
|
||||
class IShellInterface : public ams::sf::IServiceObject {
|
||||
protected:
|
||||
enum class CommandId {
|
||||
LaunchProgram = 0,
|
||||
TerminateProcess = 1,
|
||||
LaunchProgramFromHost = 2,
|
||||
GetHostContentMetaInfo = 4,
|
||||
GetApplicationProcessId = 5,
|
||||
BoostSystemMemoryResourceLimit = 6,
|
||||
IsProcessTracked = 7,
|
||||
EnableApplicationCrashReport = 8,
|
||||
IsApplicationCrashReportEnabled = 9,
|
||||
EnableApplicationAllThreadDumpOnCrash = 10,
|
||||
TriggerApplicationSnapShotDumper = 12,
|
||||
GetShellEventObserver = 20,
|
||||
};
|
||||
public:
|
||||
/* Actual commands. */
|
||||
virtual Result LaunchProgram(ams::sf::Out<os::ProcessId> out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) = 0;
|
||||
virtual Result TerminateProcess(os::ProcessId process_id) = 0;
|
||||
virtual Result LaunchProgramFromHost(ams::sf::Out<os::ProcessId> out, const ams::sf::InBuffer &content_path, u32 pm_flags) = 0;
|
||||
virtual Result GetHostContentMetaInfo(ams::sf::Out<pgl::ContentMetaInfo> out, const ams::sf::InBuffer &content_path) = 0;
|
||||
virtual Result GetApplicationProcessId(ams::sf::Out<os::ProcessId> out) = 0;
|
||||
virtual Result BoostSystemMemoryResourceLimit(u64 size) = 0;
|
||||
virtual Result IsProcessTracked(ams::sf::Out<bool> out, os::ProcessId process_id) = 0;
|
||||
virtual Result EnableApplicationCrashReport(bool enabled) = 0;
|
||||
virtual Result IsApplicationCrashReportEnabled(ams::sf::Out<bool> out) = 0;
|
||||
virtual Result EnableApplicationAllThreadDumpOnCrash(bool enabled) = 0;
|
||||
virtual Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const ams::sf::InBuffer &arg) = 0;
|
||||
|
||||
virtual Result GetShellEventObserver(ams::sf::Out<std::shared_ptr<pgl::sf::IEventObserver>> out) = 0;
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
MAKE_SERVICE_COMMAND_META(LaunchProgram),
|
||||
MAKE_SERVICE_COMMAND_META(TerminateProcess),
|
||||
MAKE_SERVICE_COMMAND_META(LaunchProgramFromHost),
|
||||
MAKE_SERVICE_COMMAND_META(GetHostContentMetaInfo),
|
||||
MAKE_SERVICE_COMMAND_META(GetApplicationProcessId),
|
||||
MAKE_SERVICE_COMMAND_META(BoostSystemMemoryResourceLimit),
|
||||
MAKE_SERVICE_COMMAND_META(IsProcessTracked),
|
||||
MAKE_SERVICE_COMMAND_META(EnableApplicationCrashReport),
|
||||
MAKE_SERVICE_COMMAND_META(IsApplicationCrashReportEnabled),
|
||||
MAKE_SERVICE_COMMAND_META(EnableApplicationAllThreadDumpOnCrash),
|
||||
MAKE_SERVICE_COMMAND_META(TriggerApplicationSnapShotDumper),
|
||||
MAKE_SERVICE_COMMAND_META(GetShellEventObserver),
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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.hpp>
|
||||
#include <stratosphere/pgl/pgl_types.hpp>
|
||||
#include <stratosphere/pgl/srv/pgl_srv_shell_interface.hpp>
|
||||
|
||||
namespace ams::pgl::srv {
|
||||
|
||||
void Initialize(ShellInterface *interface, MemoryResource *mr);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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.hpp>
|
||||
#include <stratosphere/pgl/pgl_types.hpp>
|
||||
#include <stratosphere/pgl/sf/pgl_sf_i_shell_interface.hpp>
|
||||
|
||||
namespace ams::pgl::srv {
|
||||
|
||||
class ShellInterface final : public pgl::sf::IShellInterface {
|
||||
NON_COPYABLE(ShellInterface);
|
||||
NON_MOVEABLE(ShellInterface);
|
||||
private:
|
||||
MemoryResource *memory_resource;
|
||||
public:
|
||||
constexpr ShellInterface() : memory_resource(nullptr) { /* ... */ }
|
||||
|
||||
void Initialize(MemoryResource *mr) {
|
||||
AMS_ASSERT(this->memory_resource == nullptr);
|
||||
this->memory_resource = mr;
|
||||
}
|
||||
public:
|
||||
/* Interface commands. */
|
||||
virtual Result LaunchProgram(ams::sf::Out<os::ProcessId> out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) override final;
|
||||
virtual Result TerminateProcess(os::ProcessId process_id) override final;
|
||||
virtual Result LaunchProgramFromHost(ams::sf::Out<os::ProcessId> out, const ams::sf::InBuffer &content_path, u32 pm_flags) override final;
|
||||
virtual Result GetHostContentMetaInfo(ams::sf::Out<pgl::ContentMetaInfo> out, const ams::sf::InBuffer &content_path) override final;
|
||||
virtual Result GetApplicationProcessId(ams::sf::Out<os::ProcessId> out) override final;
|
||||
virtual Result BoostSystemMemoryResourceLimit(u64 size) override final;
|
||||
virtual Result IsProcessTracked(ams::sf::Out<bool> out, os::ProcessId process_id) override final;
|
||||
virtual Result EnableApplicationCrashReport(bool enabled) override final;
|
||||
virtual Result IsApplicationCrashReportEnabled(ams::sf::Out<bool> out) override final;
|
||||
virtual Result EnableApplicationAllThreadDumpOnCrash(bool enabled) override final;
|
||||
virtual Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const ams::sf::InBuffer &arg) override final;
|
||||
|
||||
virtual Result GetShellEventObserver(ams::sf::Out<std::shared_ptr<pgl::sf::IEventObserver>> out) override final;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -23,6 +23,12 @@
|
||||
namespace ams::pm::shell {
|
||||
|
||||
/* Shell API. */
|
||||
Result LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 launch_flags);
|
||||
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 launch_flags);
|
||||
Result TerminateProcess(os::ProcessId process_id);
|
||||
Result GetProcessEventEvent(os::SystemEvent *out);
|
||||
Result GetProcessEventInfo(ProcessEventInfo *out);
|
||||
Result GetApplicationProcessIdForShell(os::ProcessId *out);
|
||||
Result BoostSystemMemoryResourceLimit(u64 size);
|
||||
Result EnableApplicationExtraThread();
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,66 @@ namespace ams::pm {
|
||||
ResourceLimitGroup_Count,
|
||||
};
|
||||
|
||||
using LimitableResource = ::LimitableResource;
|
||||
enum LaunchFlags : u32 {
|
||||
LaunchFlags_None = 0,
|
||||
LaunchFlags_SignalOnExit = (1 << 0),
|
||||
LaunchFlags_SignalOnStart = (1 << 1),
|
||||
LaunchFlags_SignalOnException = (1 << 2),
|
||||
LaunchFlags_SignalOnDebugEvent = (1 << 3),
|
||||
LaunchFlags_StartSuspended = (1 << 4),
|
||||
LaunchFlags_DisableAslr = (1 << 5),
|
||||
};
|
||||
|
||||
enum LaunchFlagsDeprecated : u32 {
|
||||
LaunchFlagsDeprecated_None = 0,
|
||||
LaunchFlagsDeprecated_SignalOnExit = (1 << 0),
|
||||
LaunchFlagsDeprecated_StartSuspended = (1 << 1),
|
||||
LaunchFlagsDeprecated_SignalOnException = (1 << 2),
|
||||
LaunchFlagsDeprecated_DisableAslr = (1 << 3),
|
||||
LaunchFlagsDeprecated_SignalOnDebugEvent = (1 << 4),
|
||||
LaunchFlagsDeprecated_SignalOnStart = (1 << 5),
|
||||
};
|
||||
|
||||
constexpr inline u32 LaunchFlagsMask = (1 << 6) - 1;
|
||||
|
||||
enum class ProcessEvent : u32 {
|
||||
None = 0,
|
||||
Exited = 1,
|
||||
Started = 2,
|
||||
Exception = 3,
|
||||
DebugRunning = 4,
|
||||
DebugBreak = 5,
|
||||
};
|
||||
|
||||
enum class ProcessEventDeprecated : u32 {
|
||||
None = 0,
|
||||
Exception = 1,
|
||||
Exited = 2,
|
||||
DebugRunning = 3,
|
||||
DebugBreak = 4,
|
||||
Started = 5,
|
||||
};
|
||||
|
||||
inline u32 GetProcessEventValue(ProcessEvent event) {
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
return static_cast<u32>(event);
|
||||
}
|
||||
switch (event) {
|
||||
case ProcessEvent::None:
|
||||
return static_cast<u32>(ProcessEventDeprecated::None);
|
||||
case ProcessEvent::Exited:
|
||||
return static_cast<u32>(ProcessEventDeprecated::Exited);
|
||||
case ProcessEvent::Started:
|
||||
return static_cast<u32>(ProcessEventDeprecated::Started);
|
||||
case ProcessEvent::Exception:
|
||||
return static_cast<u32>(ProcessEventDeprecated::Exception);
|
||||
case ProcessEvent::DebugRunning:
|
||||
return static_cast<u32>(ProcessEventDeprecated::DebugRunning);
|
||||
case ProcessEvent::DebugBreak:
|
||||
return static_cast<u32>(ProcessEventDeprecated::DebugBreak);
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
struct ProcessEventInfo {
|
||||
u32 event;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "settings/settings_types.hpp"
|
||||
#include "settings/settings_fwdbg_types.hpp"
|
||||
#include "settings/settings_fwdbg_api.hpp"
|
||||
#include "settings/system/settings_error_report.hpp"
|
||||
#include "settings/system/settings_firmware_version.hpp"
|
||||
#include "settings/system/settings_product_model.hpp"
|
||||
#include "settings/system/settings_region.hpp"
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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.hpp>
|
||||
#include <stratosphere/settings/settings_types.hpp>
|
||||
|
||||
namespace ams::settings::system {
|
||||
|
||||
enum ErrorReportSharePermission {
|
||||
ErrorReportSharePermission_NotConfirmed = 0,
|
||||
ErrorReportSharePermission_Granted = 1,
|
||||
ErrorReportSharePermission_Denied = 2,
|
||||
};
|
||||
|
||||
ErrorReportSharePermission GetErrorReportSharePermission();
|
||||
|
||||
}
|
||||
@@ -16,15 +16,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sf/sf_common.hpp"
|
||||
#include "sf/sf_mem_utility.hpp"
|
||||
#include "sf/sf_service_object.hpp"
|
||||
#include "sf/hipc/sf_hipc_server_session_manager.hpp"
|
||||
#include <stratosphere/sf/sf_common.hpp>
|
||||
#include <stratosphere/sf/sf_lmem_utility.hpp>
|
||||
#include <stratosphere/sf/sf_mem_utility.hpp>
|
||||
#include <stratosphere/sf/sf_service_object.hpp>
|
||||
#include <stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp>
|
||||
|
||||
#include "sf/sf_out.hpp"
|
||||
#include "sf/sf_buffers.hpp"
|
||||
#include "sf/impl/sf_impl_command_serialization.hpp"
|
||||
#include <stratosphere/sf/sf_out.hpp>
|
||||
#include <stratosphere/sf/sf_buffers.hpp>
|
||||
#include <stratosphere/sf/impl/sf_impl_command_serialization.hpp>
|
||||
|
||||
#include "sf/hipc/sf_hipc_server_manager.hpp"
|
||||
#include <stratosphere/sf/hipc/sf_hipc_server_manager.hpp>
|
||||
|
||||
#include "sf/sf_mitm_dispatch.h"
|
||||
#include <stratosphere/sf/sf_mitm_dispatch.h>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <stratosphere/sf/sf_common.hpp>
|
||||
#include <stratosphere/lmem.hpp>
|
||||
|
||||
namespace ams::sf {
|
||||
|
||||
class ExpHeapMemoryResource : public MemoryResource {
|
||||
private:
|
||||
lmem::HeapHandle handle;
|
||||
public:
|
||||
explicit ExpHeapMemoryResource(lmem::HeapHandle h) : handle(h) { /* ... */ }
|
||||
|
||||
lmem::HeapHandle GetHandle() const { return this->handle; }
|
||||
private:
|
||||
virtual void *AllocateImpl(size_t size, size_t alignment) override {
|
||||
return lmem::AllocateFromExpHeap(this->handle, size, static_cast<int>(alignment));
|
||||
}
|
||||
|
||||
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override {
|
||||
return lmem::FreeToExpHeap(this->handle, buffer);
|
||||
}
|
||||
|
||||
virtual bool IsEqualImpl(const MemoryResource &resource) const {
|
||||
return this == std::addressof(resource);
|
||||
}
|
||||
};
|
||||
|
||||
class UnitHeapMemoryResource : public MemoryResource {
|
||||
private:
|
||||
lmem::HeapHandle handle;
|
||||
public:
|
||||
explicit UnitHeapMemoryResource(lmem::HeapHandle h) : handle(h) { /* ... */ }
|
||||
|
||||
lmem::HeapHandle GetHandle() const { return this->handle; }
|
||||
private:
|
||||
virtual void *AllocateImpl(size_t size, size_t alignment) override {
|
||||
AMS_ASSERT(size <= lmem::GetUnitHeapUnitSize(this->handle));
|
||||
AMS_ASSERT(alignment <= static_cast<size_t>(lmem::GetUnitHeapAlignment(this->handle)));
|
||||
return lmem::AllocateFromUnitHeap(this->handle);
|
||||
}
|
||||
|
||||
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override {
|
||||
return lmem::FreeToUnitHeap(this->handle, buffer);
|
||||
}
|
||||
|
||||
virtual bool IsEqualImpl(const MemoryResource &resource) const {
|
||||
return this == std::addressof(resource);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -44,7 +44,7 @@ namespace ams::diag {
|
||||
inline void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
#ifdef AMS_ENABLE_DEBUG_PRINT
|
||||
os::Mutex g_debug_log_lock;
|
||||
os::Mutex g_debug_log_lock(true);
|
||||
char g_debug_buffer[0x400];
|
||||
|
||||
void DebugLogImpl(const char *format, ::std::va_list vl) {
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace ams::fssystem {
|
||||
if (this->size > ideal_size) {
|
||||
/* If we do, we need to have a buffer allocated from the heap. */
|
||||
AMS_ASSERT(this->buffer != nullptr);
|
||||
AMS_ASSERT(g_heap.GetBlockSize(), HeapBlockSize);
|
||||
AMS_ASSERT(g_heap.GetBlockSize() == HeapBlockSize);
|
||||
|
||||
const size_t new_size = util::AlignUp(ideal_size, HeapBlockSize);
|
||||
|
||||
|
||||
37
libraries/libstratosphere/source/ldr/ldr_shell_api.cpp
Normal file
37
libraries/libstratosphere/source/ldr/ldr_shell_api.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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>
|
||||
|
||||
namespace ams::ldr {
|
||||
|
||||
Result InitializeForShell() {
|
||||
return ::ldrShellInitialize();
|
||||
}
|
||||
|
||||
Result FinalizeForShell() {
|
||||
::ldrShellExit();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result SetProgramArgument(ncm::ProgramId program_id, const void *arg, size_t size) {
|
||||
return ::ldrShellSetProgramArguments(static_cast<u64>(program_id), arg, size);
|
||||
}
|
||||
|
||||
Result FlushArguments() {
|
||||
return ::ldrShellFlushArguments();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,19 +28,19 @@ namespace ams::lr {
|
||||
public:
|
||||
/* Actual commands. */
|
||||
virtual Result ResolveProgramPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
||||
return lrLrResolveProgramPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
|
||||
return lrLrResolveProgramPath(std::addressof(this->srv), id.value, out->str);
|
||||
}
|
||||
|
||||
virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id) override {
|
||||
return lrLrRedirectProgramPath(std::addressof(this->srv), static_cast<u64>(id), path.str);
|
||||
return lrLrRedirectProgramPath(std::addressof(this->srv), id.value, path.str);
|
||||
}
|
||||
|
||||
virtual Result ResolveApplicationControlPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
||||
return lrLrResolveApplicationControlPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
|
||||
return lrLrResolveApplicationControlPath(std::addressof(this->srv), id.value, out->str);
|
||||
}
|
||||
|
||||
virtual Result ResolveApplicationHtmlDocumentPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
||||
return lrLrResolveApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
|
||||
return lrLrResolveApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, out->str);
|
||||
}
|
||||
|
||||
virtual Result ResolveDataPath(sf::Out<Path> out, ncm::DataId id) override {
|
||||
@@ -48,31 +48,31 @@ namespace ams::lr {
|
||||
}
|
||||
|
||||
virtual Result RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) override {
|
||||
return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast<u64>(id), 0, path.str);
|
||||
return lrLrRedirectApplicationControlPath(std::addressof(this->srv), id.value, 0, path.str);
|
||||
}
|
||||
|
||||
virtual Result RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override {
|
||||
return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast<u64>(id), static_cast<u64>(owner_id), path.str);
|
||||
return lrLrRedirectApplicationControlPath(std::addressof(this->srv), id.value, owner_id.value, path.str);
|
||||
}
|
||||
|
||||
virtual Result RedirectApplicationHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override {
|
||||
return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast<u64>(id), 0, path.str);
|
||||
return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, 0, path.str);
|
||||
}
|
||||
|
||||
virtual Result RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override {
|
||||
return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast<u64>(id), static_cast<u64>(owner_id), path.str);
|
||||
return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, owner_id.value, path.str);
|
||||
}
|
||||
|
||||
virtual Result ResolveApplicationLegalInformationPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
||||
return lrLrResolveApplicationLegalInformationPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
|
||||
return lrLrResolveApplicationLegalInformationPath(std::addressof(this->srv), id.value, out->str);
|
||||
}
|
||||
|
||||
virtual Result RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) override {
|
||||
return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast<u64>(id), 0, path.str);
|
||||
return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), id.value, 0, path.str);
|
||||
}
|
||||
|
||||
virtual Result RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override {
|
||||
return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast<u64>(id), static_cast<u64>(owner_id), path.str);
|
||||
return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), id.value, owner_id.value, path.str);
|
||||
}
|
||||
|
||||
virtual Result Refresh() override {
|
||||
@@ -100,8 +100,7 @@ namespace ams::lr {
|
||||
}
|
||||
|
||||
virtual Result EraseProgramRedirection(ncm::ProgramId id) override {
|
||||
/* TODO: libnx bindings */
|
||||
AMS_ABORT();
|
||||
return lrLrEraseProgramRedirection(std::addressof(this->srv), id.value);
|
||||
}
|
||||
|
||||
virtual Result EraseApplicationControlRedirection(ncm::ProgramId id) override {
|
||||
|
||||
43
libraries/libstratosphere/source/os/os_sdk_mutex.cpp
Normal file
43
libraries/libstratosphere/source/os/os_sdk_mutex.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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>
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
void InitializeSdkMutex(SdkMutexType *mutex) {
|
||||
GetReference(mutex->_storage).Initialize();
|
||||
}
|
||||
|
||||
bool IsSdkMutexLockedByCurrentThread(const SdkMutexType *mutex) {
|
||||
return GetReference(mutex->_storage).IsLockedByCurrentThread();
|
||||
}
|
||||
|
||||
void LockSdkMutex(SdkMutexType *mutex) {
|
||||
AMS_ABORT_UNLESS(!IsSdkMutexLockedByCurrentThread(mutex));
|
||||
return GetReference(mutex->_storage).Enter();
|
||||
}
|
||||
|
||||
bool TryLockSdkMutex(SdkMutexType *mutex) {
|
||||
AMS_ABORT_UNLESS(!IsSdkMutexLockedByCurrentThread(mutex));
|
||||
return GetReference(mutex->_storage).TryEnter();
|
||||
}
|
||||
|
||||
void UnlockSdkMutex(SdkMutexType *mutex) {
|
||||
AMS_ABORT_UNLESS(IsSdkMutexLockedByCurrentThread(mutex));
|
||||
return GetReference(mutex->_storage).Leave();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::pgl {
|
||||
|
||||
class RemoteEventObserver final : public pgl::sf::IEventObserver {
|
||||
NON_COPYABLE(RemoteEventObserver);
|
||||
NON_MOVEABLE(RemoteEventObserver);
|
||||
private:
|
||||
::PglEventObserver observer;
|
||||
public:
|
||||
constexpr RemoteEventObserver(const ::PglEventObserver &o) : observer(o) { /* ... */ }
|
||||
virtual ~RemoteEventObserver() override {
|
||||
::pglEventObserverClose(std::addressof(this->observer));
|
||||
}
|
||||
|
||||
virtual Result GetProcessEventHandle(ams::sf::OutCopyHandle out) override {
|
||||
::Event ev;
|
||||
R_TRY(::pglEventObserverGetProcessEvent(std::addressof(this->observer), std::addressof(ev)));
|
||||
out.SetValue(ev.revent);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result GetProcessEventInfo(ams::sf::Out<pm::ProcessEventInfo> out) override {
|
||||
static_assert(sizeof(*out.GetPointer()) == sizeof(::PmProcessEventInfo));
|
||||
return ::pglEventObserverGetProcessEventInfo(std::addressof(this->observer), reinterpret_cast<::PmProcessEventInfo *>(out.GetPointer()));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
89
libraries/libstratosphere/source/pgl/pgl_shell_api.cpp
Normal file
89
libraries/libstratosphere/source/pgl/pgl_shell_api.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 "pgl_remote_event_observer.hpp"
|
||||
|
||||
namespace ams::pgl {
|
||||
|
||||
Result Initialize() {
|
||||
return ::pglInitialize();
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
return pglExit();
|
||||
}
|
||||
|
||||
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 process_flags, u8 pgl_flags) {
|
||||
static_assert(sizeof(*out) == sizeof(u64));
|
||||
static_assert(sizeof(loc) == sizeof(::NcmProgramLocation));
|
||||
return ::pglLaunchProgram(reinterpret_cast<u64 *>(out), reinterpret_cast<const ::NcmProgramLocation *>(std::addressof(loc)), process_flags, pgl_flags);
|
||||
}
|
||||
|
||||
Result TerminateProcess(os::ProcessId process_id) {
|
||||
return ::pglTerminateProcess(static_cast<u64>(process_id));
|
||||
}
|
||||
|
||||
Result LaunchProgramFromHost(os::ProcessId *out, const char *content_path, u32 process_flags) {
|
||||
static_assert(sizeof(*out) == sizeof(u64));
|
||||
return ::pglLaunchProgramFromHost(reinterpret_cast<u64 *>(out), content_path, process_flags);
|
||||
}
|
||||
|
||||
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *content_path) {
|
||||
static_assert(sizeof(*out) == sizeof(::PglContentMetaInfo));
|
||||
return ::pglGetHostContentMetaInfo(reinterpret_cast<::PglContentMetaInfo *>(out), content_path);
|
||||
}
|
||||
|
||||
Result GetApplicationProcessId(os::ProcessId *out) {
|
||||
static_assert(sizeof(*out) == sizeof(u64));
|
||||
return ::pglGetApplicationProcessId(reinterpret_cast<u64 *>(out));
|
||||
}
|
||||
|
||||
Result BoostSystemMemoryResourceLimit(u64 size) {
|
||||
return ::pglBoostSystemMemoryResourceLimit(size);
|
||||
}
|
||||
|
||||
Result IsProcessTracked(bool *out, os::ProcessId process_id) {
|
||||
return ::pglIsProcessTracked(out, static_cast<u64>(process_id));
|
||||
}
|
||||
|
||||
Result EnableApplicationCrashReport(bool enabled) {
|
||||
return ::pglEnableApplicationCrashReport(enabled);
|
||||
}
|
||||
|
||||
Result IsApplicationCrashReportEnabled(bool *out) {
|
||||
return ::pglIsApplicationCrashReportEnabled(out);
|
||||
}
|
||||
|
||||
Result EnableApplicationAllThreadDumpOnCrash(bool enabled) {
|
||||
return ::pglEnableApplicationAllThreadDumpOnCrash(enabled);
|
||||
}
|
||||
|
||||
Result TriggerApplicationSnapShotDumper(const char *arg, SnapShotDumpType dump_type) {
|
||||
return ::pglTriggerApplicationSnapShotDumper(static_cast<::PglSnapShotDumpType>(dump_type), arg);
|
||||
}
|
||||
|
||||
Result GetEventObserver(pgl::EventObserver *out) {
|
||||
::PglEventObserver obs;
|
||||
R_TRY(::pglGetEventObserver(std::addressof(obs)));
|
||||
|
||||
auto remote_observer = std::make_shared<RemoteEventObserver>(obs);
|
||||
AMS_ABORT_UNLESS(remote_observer != nullptr);
|
||||
|
||||
*out = pgl::EventObserver(remote_observer);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
38
libraries/libstratosphere/source/pgl/srv/pgl_srv_api.cpp
Normal file
38
libraries/libstratosphere/source/pgl/srv/pgl_srv_api.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 "pgl_srv_shell.hpp"
|
||||
|
||||
namespace ams::pgl::srv {
|
||||
|
||||
void Initialize(ShellInterface *interface, MemoryResource *mr) {
|
||||
/* Set the memory resource for the interface. */
|
||||
interface->Initialize(mr);
|
||||
|
||||
/* Enable extra application threads, if we should. */
|
||||
u8 enable_application_extra_thread;
|
||||
const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(enable_application_extra_thread), sizeof(enable_application_extra_thread), "application_extra_thread", "enable_application_extra_thread");
|
||||
if (sz == sizeof(enable_application_extra_thread) && enable_application_extra_thread != 0) {
|
||||
/* NOTE: Nintendo does not check that this succeeds. */
|
||||
pm::shell::EnableApplicationExtraThread();
|
||||
}
|
||||
|
||||
/* Start the Process Tracking thread. */
|
||||
pgl::srv::InitializeProcessControlTask();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
485
libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp
Normal file
485
libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 "pgl_srv_shell.hpp"
|
||||
#include "pgl_srv_shell_event_observer.hpp"
|
||||
|
||||
namespace ams::pgl::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline size_t ProcessDataCount = 0x20;
|
||||
|
||||
struct ProcessData {
|
||||
os::ProcessId process_id;
|
||||
u32 flags;
|
||||
};
|
||||
static_assert(std::is_pod<ProcessData>::value);
|
||||
|
||||
enum ProcessDataFlag : u32 {
|
||||
ProcessDataFlag_None = 0,
|
||||
ProcessDataFlag_DetailedCrashReportAllowed = (1 << 0),
|
||||
ProcessDataFlag_DetailedCrashReportEnabled = (1 << 1),
|
||||
ProcessDataFlag_HasLogOption = (1 << 2),
|
||||
ProcessDataFlag_OutputAllLog = (1 << 3),
|
||||
ProcessDataFlag_EnableCrashReportScreenShot = (1 << 4),
|
||||
};
|
||||
|
||||
bool g_is_production = true;
|
||||
bool g_enable_crash_report_screenshot = true;
|
||||
bool g_enable_jit_debug = false;
|
||||
|
||||
constexpr inline size_t ProcessControlTaskStackSize = 8_KB;
|
||||
constexpr inline s32 ProcessControlTaskPriority = 21;
|
||||
os::ThreadType g_process_control_task_thread;
|
||||
alignas(os::ThreadStackAlignment) u8 g_process_control_task_stack[ProcessControlTaskStackSize];
|
||||
|
||||
os::SdkMutex g_observer_list_mutex;
|
||||
util::IntrusiveListBaseTraits<ShellEventObserverHolder>::ListType g_observer_list;
|
||||
|
||||
os::SdkMutex g_process_data_mutex;
|
||||
ProcessData g_process_data[ProcessDataCount];
|
||||
|
||||
ProcessData *FindProcessData(os::ProcessId process_id) {
|
||||
for (auto &data : g_process_data) {
|
||||
if (data.process_id == process_id) {
|
||||
return std::addressof(data);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 ConvertToProcessDataFlags(u8 pgl_flags) {
|
||||
if ((pgl_flags & pgl::LaunchFlags_EnableDetailedCrashReport) == 0) {
|
||||
/* If we shouldn't generate detailed crash reports, set no flags. */
|
||||
return ProcessDataFlag_None;
|
||||
} else {
|
||||
/* We can and should generate detailed crash reports. */
|
||||
u32 data_flags = ProcessDataFlag_DetailedCrashReportAllowed | ProcessDataFlag_DetailedCrashReportEnabled;
|
||||
|
||||
/* If we should enable crash report screenshots, check the correct flag. */
|
||||
if (g_enable_crash_report_screenshot) {
|
||||
const u32 test_flag = g_is_production ? pgl::LaunchFlags_EnableCrashReportScreenShotForProduction : pgl::LaunchFlags_EnableCrashReportScreenShotForDevelop;
|
||||
if ((pgl_flags & test_flag) != 0) {
|
||||
data_flags |= ProcessDataFlag_EnableCrashReportScreenShot;
|
||||
}
|
||||
}
|
||||
|
||||
return data_flags;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<os::ProcessId> GetRunningApplicationProcessId() {
|
||||
os::ProcessId process_id;
|
||||
if (R_SUCCEEDED(pm::shell::GetApplicationProcessIdForShell(std::addressof(process_id)))) {
|
||||
return process_id;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
s32 ConvertDumpTypeToArgument(SnapShotDumpType dump_type) {
|
||||
switch (dump_type) {
|
||||
case SnapShotDumpType::None: return -1;
|
||||
case SnapShotDumpType::Auto: return 0;
|
||||
case SnapShotDumpType::Full: return 1;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
bool GetSnapShotDumpOutputAllLog(os::ProcessId process_id) {
|
||||
/* Check if we have an option set for the process. */
|
||||
{
|
||||
std::scoped_lock lk(g_process_data_mutex);
|
||||
if (ProcessData *data = FindProcessData(process_id); data != nullptr) {
|
||||
if ((data->flags & ProcessDataFlag_HasLogOption) != 0) {
|
||||
return ((data->flags & ProcessDataFlag_OutputAllLog) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we don't have an option for the process, fall back to settings. */
|
||||
u8 log_option;
|
||||
const size_t option_size = settings::fwdbg::GetSettingsItemValue(std::addressof(log_option), sizeof(log_option), "snap_shot_dump", "output_all_log");
|
||||
return (option_size == sizeof(log_option) && log_option != 0);
|
||||
}
|
||||
|
||||
size_t CreateSnapShotDumpArguments(char *dst, size_t dst_size, os::ProcessId process_id, SnapShotDumpType dump_type, const char *str_arg) {
|
||||
const s32 dump_arg = ConvertDumpTypeToArgument(dump_type);
|
||||
const s32 log_arg = GetSnapShotDumpOutputAllLog(process_id) ? 1 : 0;
|
||||
if (str_arg != nullptr) {
|
||||
return std::snprintf(dst, dst_size, "D %010llu \"%s\" -log %d -dump %d", static_cast<unsigned long long>(static_cast<u64>(process_id)), str_arg, log_arg, dump_arg);
|
||||
} else {
|
||||
return std::snprintf(dst, dst_size, "D %010llu -log %d -dump %d", static_cast<unsigned long long>(static_cast<u64>(process_id)), log_arg, dump_arg);
|
||||
}
|
||||
}
|
||||
|
||||
Result TriggerSnapShotDumper(os::ProcessId process_id, SnapShotDumpType dump_type, const char *arg) {
|
||||
/* Create the arguments. */
|
||||
char process_arguments[800];
|
||||
const size_t arg_len = CreateSnapShotDumpArguments(process_arguments, sizeof(process_arguments), process_id, dump_type, arg);
|
||||
|
||||
/* Set the arguments. */
|
||||
R_TRY(ldr::SetProgramArgument(ncm::SystemDebugAppletId::SnapShotDumper, process_arguments, arg_len + 1));
|
||||
|
||||
/* Launch the process. */
|
||||
os::ProcessId dummy_process_id;
|
||||
return pm::shell::LaunchProgram(std::addressof(dummy_process_id), ncm::ProgramLocation::Make(ncm::SystemDebugAppletId::SnapShotDumper, ncm::StorageId::BuiltInSystem), pm::LaunchFlags_None);
|
||||
}
|
||||
|
||||
bool ShouldSnapShotAutoDump() {
|
||||
bool dump;
|
||||
const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(dump), sizeof(dump), "snap_shot_dump", "auto_dump");
|
||||
return sz == sizeof(dump) && dump;
|
||||
}
|
||||
|
||||
bool ShouldSnapShotFullDump() {
|
||||
bool dump;
|
||||
const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(dump), sizeof(dump), "snap_shot_dump", "full_dump");
|
||||
return sz == sizeof(dump) && dump;
|
||||
}
|
||||
|
||||
SnapShotDumpType GetSnapShotDumpType() {
|
||||
if (ShouldSnapShotAutoDump()) {
|
||||
if (ShouldSnapShotFullDump()) {
|
||||
return SnapShotDumpType::Full;
|
||||
} else {
|
||||
return SnapShotDumpType::Auto;
|
||||
}
|
||||
} else {
|
||||
return SnapShotDumpType::None;
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerSnapShotDumper(os::ProcessId process_id) {
|
||||
TriggerSnapShotDumper(process_id, GetSnapShotDumpType(), nullptr);
|
||||
}
|
||||
|
||||
s32 GetCrashReportDetailedArgument(u32 data_flags) {
|
||||
if (((data_flags & ProcessDataFlag_DetailedCrashReportAllowed) != 0) && ((data_flags & ProcessDataFlag_DetailedCrashReportEnabled) != 0)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
s32 GetCrashReportScreenShotArgument(u32 data_flags) {
|
||||
if (settings::system::GetErrorReportSharePermission() == settings::system::ErrorReportSharePermission_Granted) {
|
||||
return ((data_flags & ProcessDataFlag_EnableCrashReportScreenShot) != 0) ? 1 : 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerCrashReport(os::ProcessId process_id) {
|
||||
static os::ProcessId s_crashed_process_id = os::InvalidProcessId;
|
||||
static os::ProcessId s_creport_process_id = os::InvalidProcessId;
|
||||
|
||||
/* If the program that crashed is creport, we should just terminate both processes and return. */
|
||||
if (process_id == s_creport_process_id) {
|
||||
TerminateProcess(s_crashed_process_id);
|
||||
TerminateProcess(s_creport_process_id);
|
||||
s_crashed_process_id = os::InvalidProcessId;
|
||||
s_creport_process_id = os::InvalidProcessId;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the data flags for the process. */
|
||||
u32 data_flags;
|
||||
{
|
||||
std::scoped_lock lk(g_process_data_mutex);
|
||||
if (auto *data = FindProcessData(process_id); data != nullptr) {
|
||||
data_flags = data->flags;
|
||||
} else {
|
||||
data_flags = ProcessDataFlag_None;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate arguments. */
|
||||
char arguments[0x40];
|
||||
const size_t len = std::snprintf(arguments, sizeof(arguments), "%ld %d %d", static_cast<s64>(static_cast<u64>(process_id)), GetCrashReportDetailedArgument(data_flags), GetCrashReportScreenShotArgument(data_flags));
|
||||
if (R_FAILED(ldr::SetProgramArgument(ncm::SystemProgramId::Creport, arguments, len + 1))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Launch creport. */
|
||||
os::ProcessId creport_process_id;
|
||||
if (R_FAILED(pm::shell::LaunchProgram(std::addressof(creport_process_id), ncm::ProgramLocation::Make(ncm::SystemProgramId::Creport, ncm::StorageId::BuiltInSystem), pm::LaunchFlags_None))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the globals. */
|
||||
s_crashed_process_id = process_id;
|
||||
s_creport_process_id = creport_process_id;
|
||||
}
|
||||
|
||||
void HandleException(os::ProcessId process_id) {
|
||||
if (g_enable_jit_debug) {
|
||||
/* If jit debug is enabled, we want to try to launch snap shot dumper. */
|
||||
ProcessData *data = nullptr;
|
||||
{
|
||||
std::scoped_lock lk(g_process_data_mutex);
|
||||
data = FindProcessData(process_id);
|
||||
}
|
||||
|
||||
/* If we're tracking the process, we can launch dumper. Otherwise we should just terminate. */
|
||||
if (data != nullptr) {
|
||||
TriggerSnapShotDumper(process_id);
|
||||
} else {
|
||||
TerminateProcess(process_id);
|
||||
}
|
||||
} else {
|
||||
/* Otherwise, we want to launch creport. */
|
||||
TriggerCrashReport(process_id);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleExit(os::ProcessId process_id) {
|
||||
std::scoped_lock lk(g_process_data_mutex);
|
||||
if (auto *data = FindProcessData(process_id); data != nullptr) {
|
||||
data->process_id = os::InvalidProcessId;
|
||||
}
|
||||
}
|
||||
|
||||
void OnProcessEvent(const pm::ProcessEventInfo &event_info) {
|
||||
/* Determine if we're tracking the process. */
|
||||
ProcessData *data = nullptr;
|
||||
{
|
||||
std::scoped_lock lk(g_process_data_mutex);
|
||||
data = FindProcessData(event_info.process_id);
|
||||
}
|
||||
|
||||
/* If we are, we're going to want to notify our listeners. */
|
||||
if (data != nullptr) {
|
||||
/* If we closed the process, note that. */
|
||||
if (static_cast<pm::ProcessEvent>(event_info.event) == pm::ProcessEvent::Exited) {
|
||||
HandleExit(event_info.process_id);
|
||||
}
|
||||
|
||||
/* Notify all observers. */
|
||||
std::scoped_lock lk(g_observer_list_mutex);
|
||||
for (auto &observer : g_observer_list) {
|
||||
observer.Notify(event_info);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the process crashed, handle that. */
|
||||
if (static_cast<pm::ProcessEvent>(event_info.event) == pm::ProcessEvent::Exception) {
|
||||
HandleException(event_info.process_id);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessControlTask(void *) {
|
||||
/* Get the process event event from pm. */
|
||||
os::SystemEvent process_event;
|
||||
R_ABORT_UNLESS(pm::shell::GetProcessEventEvent(std::addressof(process_event)));
|
||||
|
||||
while (true) {
|
||||
/* Wait for an event to come in, and clear our signal. */
|
||||
process_event.Wait();
|
||||
process_event.Clear();
|
||||
|
||||
bool continue_getting_event = true;
|
||||
while (continue_getting_event) {
|
||||
/* Try to get an event info. */
|
||||
pm::ProcessEventInfo event_info;
|
||||
if (R_FAILED(pm::shell::GetProcessEventInfo(std::addressof(event_info)))) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Process the event. */
|
||||
switch (static_cast<pm::ProcessEvent>(event_info.event)) {
|
||||
case pm::ProcessEvent::None:
|
||||
continue_getting_event = false;
|
||||
break;
|
||||
case pm::ProcessEvent::Exited:
|
||||
case pm::ProcessEvent::Started:
|
||||
case pm::ProcessEvent::Exception:
|
||||
case pm::ProcessEvent::DebugRunning:
|
||||
case pm::ProcessEvent::DebugBreak:
|
||||
OnProcessEvent(event_info);
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializeProcessControlTask() {
|
||||
/* Create the task thread. */
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_process_control_task_thread), ProcessControlTask, nullptr, g_process_control_task_stack, sizeof(g_process_control_task_stack), ProcessControlTaskPriority));
|
||||
|
||||
/* Retrieve settings. */
|
||||
settings::fwdbg::GetSettingsItemValue(std::addressof(g_enable_jit_debug), sizeof(g_enable_jit_debug), "jit_debug", "enable_jit_debug");
|
||||
settings::fwdbg::GetSettingsItemValue(std::addressof(g_enable_crash_report_screenshot), sizeof(g_enable_crash_report_screenshot), "creport", "crash_screen_shot");
|
||||
g_is_production = !settings::fwdbg::IsDebugModeEnabled();
|
||||
|
||||
/* Clear all process data. */
|
||||
{
|
||||
for (size_t i = 0; i < util::size(g_process_data); i++) {
|
||||
g_process_data[i].process_id = os::InvalidProcessId;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start the thread. */
|
||||
os::StartThread(std::addressof(g_process_control_task_thread));
|
||||
}
|
||||
|
||||
void RegisterShellEventObserver(ShellEventObserverHolder *holder) {
|
||||
std::scoped_lock lk(g_observer_list_mutex);
|
||||
|
||||
g_observer_list.push_back(*holder);
|
||||
}
|
||||
|
||||
void UnregisterShellEventObserver(ShellEventObserverHolder *holder) {
|
||||
std::scoped_lock lk(g_observer_list_mutex);
|
||||
|
||||
for (auto &observer : g_observer_list) {
|
||||
if (std::addressof(observer) == holder) {
|
||||
g_observer_list.erase(g_observer_list.iterator_to(observer));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) {
|
||||
/* Convert the input flags to the internal format. */
|
||||
const u32 data_flags = ConvertToProcessDataFlags(pgl_flags);
|
||||
|
||||
/* If jit debug is enabled, we want to be signaled on crash. */
|
||||
if (g_enable_jit_debug) {
|
||||
pm_flags |= pm::LaunchFlags_SignalOnException;
|
||||
}
|
||||
|
||||
/* Launch the process. */
|
||||
os::ProcessId process_id;
|
||||
R_TRY(pm::shell::LaunchProgram(std::addressof(process_id), loc, pm_flags & pm::LaunchFlagsMask));
|
||||
|
||||
/* Create a ProcessData for the process. */
|
||||
{
|
||||
std::scoped_lock lk(g_process_data_mutex);
|
||||
ProcessData *new_data = FindProcessData(os::InvalidProcessId);
|
||||
AMS_ABORT_UNLESS(new_data != nullptr);
|
||||
|
||||
new_data->process_id = process_id;
|
||||
new_data->flags = data_flags;
|
||||
}
|
||||
|
||||
/* We succeeded. */
|
||||
*out = process_id;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result TerminateProcess(os::ProcessId process_id) {
|
||||
/* Ask PM to terminate the process. */
|
||||
return pm::shell::TerminateProcess(process_id);
|
||||
}
|
||||
|
||||
Result GetApplicationProcessId(os::ProcessId *out) {
|
||||
/* Get the application process id. */
|
||||
auto application_process_id = GetRunningApplicationProcessId();
|
||||
R_UNLESS(application_process_id, pgl::ResultApplicationNotRunning());
|
||||
|
||||
/* Return the id. */
|
||||
*out = *application_process_id;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BoostSystemMemoryResourceLimit(u64 size) {
|
||||
/* Ask PM to boost the limit. */
|
||||
return pm::shell::BoostSystemMemoryResourceLimit(size);
|
||||
}
|
||||
|
||||
bool IsProcessTracked(os::ProcessId process_id) {
|
||||
/* Check whether a ProcessData exists for the process. */
|
||||
std::scoped_lock lk(g_process_data_mutex);
|
||||
return FindProcessData(process_id) != nullptr;
|
||||
}
|
||||
|
||||
void EnableApplicationCrashReport(bool enabled) {
|
||||
/* Get the application process id. */
|
||||
auto application_process_id = GetRunningApplicationProcessId();
|
||||
if (application_process_id) {
|
||||
/* Find the data for the application process. */
|
||||
std::scoped_lock lk(g_process_data_mutex);
|
||||
ProcessData *data = FindProcessData(*application_process_id);
|
||||
|
||||
/* It's okay if we aren't tracking the process. */
|
||||
if (data != nullptr) {
|
||||
/* Set or clear the flag. */
|
||||
if (enabled) {
|
||||
data->flags |= ProcessDataFlag_DetailedCrashReportEnabled;
|
||||
} else {
|
||||
data->flags &= ~ProcessDataFlag_DetailedCrashReportEnabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsApplicationCrashReportEnabled() {
|
||||
/* Get the application process id. */
|
||||
auto application_process_id = GetRunningApplicationProcessId();
|
||||
if (!application_process_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find the data for the process. */
|
||||
std::scoped_lock lk(g_process_data_mutex);
|
||||
if (ProcessData *data = FindProcessData(*application_process_id); data != nullptr) {
|
||||
return (data->flags & ProcessDataFlag_DetailedCrashReportEnabled) != 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void EnableApplicationAllThreadDumpOnCrash(bool enabled) {
|
||||
/* Get the application process id. */
|
||||
auto application_process_id = GetRunningApplicationProcessId();
|
||||
if (application_process_id) {
|
||||
/* Find the data for the application process. */
|
||||
std::scoped_lock lk(g_process_data_mutex);
|
||||
ProcessData *data = FindProcessData(*application_process_id);
|
||||
|
||||
/* It's okay if we aren't tracking the process. */
|
||||
if (data != nullptr) {
|
||||
/* Set or clear the flag. */
|
||||
if (enabled) {
|
||||
data->flags |= ProcessDataFlag_OutputAllLog;
|
||||
} else {
|
||||
data->flags &= ~ProcessDataFlag_OutputAllLog;
|
||||
}
|
||||
|
||||
/* NOTE: Here Nintendo releases the lock, re-takes the lock, and re-finds the process data. */
|
||||
/* This is unnecessary and less efficient, so we will not bother. */
|
||||
|
||||
/* Note that the flag bit has a meaningful value. */
|
||||
data->flags |= ProcessDataFlag_HasLogOption;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const char *arg) {
|
||||
/* Try to get the application process id. */
|
||||
os::ProcessId process_id;
|
||||
R_TRY(pm::shell::GetApplicationProcessIdForShell(std::addressof(process_id)));
|
||||
|
||||
/* Launch the snapshot dumper. */
|
||||
return TriggerSnapShotDumper(process_id, dump_type, arg);
|
||||
}
|
||||
|
||||
}
|
||||
38
libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.hpp
Normal file
38
libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::pgl::srv {
|
||||
|
||||
void InitializeProcessControlTask();
|
||||
|
||||
class ShellEventObserverHolder;
|
||||
|
||||
void RegisterShellEventObserver(ShellEventObserverHolder *holder);
|
||||
void UnregisterShellEventObserver(ShellEventObserverHolder *holder);
|
||||
|
||||
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags);
|
||||
Result TerminateProcess(os::ProcessId process_id);
|
||||
Result GetApplicationProcessId(os::ProcessId *out);
|
||||
Result BoostSystemMemoryResourceLimit(u64 size);
|
||||
bool IsProcessTracked(os::ProcessId process_id);
|
||||
void EnableApplicationCrashReport(bool enabled);
|
||||
bool IsApplicationCrashReportEnabled();
|
||||
void EnableApplicationAllThreadDumpOnCrash(bool enabled);
|
||||
Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const char *arg);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 "pgl_srv_shell_event_observer.hpp"
|
||||
#include "pgl_srv_shell.hpp"
|
||||
|
||||
namespace ams::pgl::srv {
|
||||
|
||||
ShellEventObserver::ShellEventObserver() : message_queue(queue_buffer, QueueCapacity), event(os::EventClearMode_AutoClear, true) {
|
||||
this->heap_handle = lmem::CreateUnitHeap(this->event_info_data, sizeof(this->event_info_data), sizeof(this->event_info_data[0]), lmem::CreateOption_ThreadSafe, 8, GetPointer(this->heap_head));
|
||||
|
||||
new (GetPointer(this->holder)) ShellEventObserverHolder(this);
|
||||
RegisterShellEventObserver(GetPointer(this->holder));
|
||||
}
|
||||
|
||||
ShellEventObserver::~ShellEventObserver() {
|
||||
UnregisterShellEventObserver(GetPointer(this->holder));
|
||||
GetReference(this->holder).~ShellEventObserverHolder();
|
||||
}
|
||||
|
||||
Result ShellEventObserver::PopEventInfo(pm::ProcessEventInfo *out) {
|
||||
/* Receive an info from the queue. */
|
||||
uintptr_t info_address;
|
||||
R_UNLESS(this->message_queue.TryReceive(std::addressof(info_address)), pgl::ResultNotAvailable());
|
||||
pm::ProcessEventInfo *info = reinterpret_cast<pm::ProcessEventInfo *>(info_address);
|
||||
|
||||
/* Set the output. */
|
||||
*out = *info;
|
||||
|
||||
/* Free the received info. */
|
||||
lmem::FreeToUnitHeap(this->heap_handle, info);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void ShellEventObserver::Notify(const pm::ProcessEventInfo &info) {
|
||||
/* Allocate a new info. */
|
||||
auto allocated = reinterpret_cast<pm::ProcessEventInfo *>(lmem::AllocateFromUnitHeap(this->heap_handle));
|
||||
if (!allocated) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set it to the notification. */
|
||||
*allocated = info;
|
||||
|
||||
/* Try to send it. */
|
||||
if (!this->message_queue.TrySend(reinterpret_cast<uintptr_t>(allocated))) {
|
||||
lmem::FreeToUnitHeap(this->heap_handle, allocated);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Notify that we have a new info available. */
|
||||
this->event.Signal();
|
||||
}
|
||||
|
||||
Result EventObserverInterface::GetProcessEventHandle(ams::sf::OutCopyHandle out) {
|
||||
out.SetValue(GetReference(this->observer).GetEvent().GetReadableHandle());
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result EventObserverInterface::GetProcessEventInfo(ams::sf::Out<pm::ProcessEventInfo> out) {
|
||||
return GetReference(this->observer).PopEventInfo(out.GetPointer());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::pgl::srv {
|
||||
|
||||
class IShellEventObserver {
|
||||
public:
|
||||
virtual void Notify(const pm::ProcessEventInfo &info) = 0;
|
||||
};
|
||||
|
||||
class ShellEventObserverHolder : public util::IntrusiveListBaseNode<ShellEventObserverHolder> {
|
||||
private:
|
||||
IShellEventObserver *observer;
|
||||
public:
|
||||
explicit ShellEventObserverHolder(IShellEventObserver *observer) : observer(observer) { /* ... */ }
|
||||
|
||||
void Notify(const pm::ProcessEventInfo &info) {
|
||||
this->observer->Notify(info);
|
||||
}
|
||||
};
|
||||
|
||||
class ShellEventObserver final : public IShellEventObserver {
|
||||
private:
|
||||
static constexpr size_t QueueCapacity = 0x20;
|
||||
private:
|
||||
os::MessageQueue message_queue;
|
||||
uintptr_t queue_buffer[QueueCapacity];
|
||||
os::SystemEvent event;
|
||||
TYPED_STORAGE(lmem::HeapCommonHead) heap_head;
|
||||
lmem::HeapHandle heap_handle;
|
||||
pm::ProcessEventInfo event_info_data[QueueCapacity];
|
||||
TYPED_STORAGE(ShellEventObserverHolder) holder;
|
||||
public:
|
||||
ShellEventObserver();
|
||||
~ShellEventObserver();
|
||||
|
||||
os::SystemEvent &GetEvent() {
|
||||
return this->event;
|
||||
}
|
||||
|
||||
Result PopEventInfo(pm::ProcessEventInfo *out);
|
||||
|
||||
virtual void Notify(const pm::ProcessEventInfo &info) override final;
|
||||
};
|
||||
|
||||
class EventObserverInterface final : public pgl::sf::IEventObserver {
|
||||
private:
|
||||
TYPED_STORAGE(ShellEventObserver) observer;
|
||||
public:
|
||||
EventObserverInterface() {
|
||||
std::memset(std::addressof(this->observer), 0, sizeof(this->observer));
|
||||
new (GetPointer(this->observer)) ShellEventObserver;
|
||||
}
|
||||
|
||||
~EventObserverInterface() {
|
||||
GetReference(this->observer).~ShellEventObserver();
|
||||
}
|
||||
public:
|
||||
virtual Result GetProcessEventHandle(ams::sf::OutCopyHandle out) override final;
|
||||
virtual Result GetProcessEventInfo(ams::sf::Out<pm::ProcessEventInfo> out) override final;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 "pgl_srv_shell_host_utils.hpp"
|
||||
#include "pgl_srv_shell.hpp"
|
||||
|
||||
namespace ams::pgl::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline char HostPackageMountName[] = "HostPackageRead";
|
||||
static_assert(sizeof(HostPackageMountName) - 1 <= fs::MountNameLengthMax);
|
||||
|
||||
struct CaseInsensitiveCharTraits : public std::char_traits<char> {
|
||||
static char to_upper(char c) {
|
||||
return std::toupper(static_cast<unsigned char>(c));
|
||||
}
|
||||
static bool eq(char c1, char c2) {
|
||||
return to_upper(c1) == to_upper(c2);
|
||||
}
|
||||
static bool lt(char c1, char c2) {
|
||||
return to_upper(c1) < to_upper(c2);
|
||||
}
|
||||
static int compare(const char *s1, const char *s2, size_t n) {
|
||||
while ( n-- != 0 ) {
|
||||
if ( to_upper(*s1) < to_upper(*s2) ) return -1;
|
||||
if ( to_upper(*s1) > to_upper(*s2) ) return 1;
|
||||
++s1; ++s2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static const char *find(const char *s, int n, char a) {
|
||||
auto const ua (to_upper(a));
|
||||
while ( n-- != 0 )
|
||||
{
|
||||
if (to_upper(*s) == ua)
|
||||
return s;
|
||||
s++;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
using PathView = std::basic_string_view<char, CaseInsensitiveCharTraits>;
|
||||
|
||||
enum class ExtensionType {
|
||||
None = 0,
|
||||
Nsp = 1,
|
||||
Nspd = 2,
|
||||
};
|
||||
|
||||
bool HasSuffix(const char *str, const char *suffix) {
|
||||
const size_t suffix_len = std::strlen(suffix);
|
||||
const size_t str_len = std::strlen(str);
|
||||
if (suffix_len > str_len) {
|
||||
return false;
|
||||
}
|
||||
return (PathView(str).substr(str_len - suffix_len) == PathView(suffix));
|
||||
}
|
||||
|
||||
class HostPackageReader {
|
||||
NON_COPYABLE(HostPackageReader);
|
||||
NON_MOVEABLE(HostPackageReader);
|
||||
private:
|
||||
char content_path[fs::EntryNameLengthMax] = {};
|
||||
ExtensionType extension_type = ExtensionType::None;
|
||||
char mount_name[fs::MountNameLengthMax] = {};
|
||||
bool is_mounted = false;
|
||||
ncm::AutoBuffer content_meta_buffer;
|
||||
ncm::ProgramId program_id = ncm::InvalidProgramId;
|
||||
u32 program_version = 0;
|
||||
ncm::ContentMetaType content_meta_type = static_cast<ncm::ContentMetaType>(0);
|
||||
u8 program_index = 0;
|
||||
public:
|
||||
HostPackageReader() : content_meta_buffer() { /* ... */ }
|
||||
~HostPackageReader() {
|
||||
if (this->is_mounted) {
|
||||
fs::Unmount(this->mount_name);
|
||||
}
|
||||
}
|
||||
|
||||
Result Initialize(const char *package, const char *mount) {
|
||||
/* Copy in the content path. */
|
||||
R_UNLESS(strlen(package) <= sizeof(this->content_path) - 1, pgl::ResultBufferNotEnough());
|
||||
std::strcpy(this->content_path, package);
|
||||
|
||||
/* Set the extension type. */
|
||||
R_TRY(this->SetExtensionType());
|
||||
|
||||
/* Copy in mount name. */
|
||||
R_UNLESS(strlen(mount) <= sizeof(this->mount_name) - 1, pgl::ResultBufferNotEnough());
|
||||
std::strcpy(this->mount_name, mount);
|
||||
|
||||
/* Mount the package. */
|
||||
R_TRY(fs::MountApplicationPackage(this->mount_name, this->content_path));
|
||||
this->is_mounted = true;
|
||||
|
||||
/* Set the content meta buffer. */
|
||||
R_TRY(this->SetContentMetaBuffer());
|
||||
|
||||
/* Ensure we have a content meta buffer. */
|
||||
R_UNLESS(this->content_meta_buffer.Get() != nullptr, pgl::ResultContentMetaNotFound());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ReadProgramInfo() {
|
||||
/* First, read the program index. */
|
||||
R_TRY(this->GetProgramIndex(std::addressof(this->program_index)));
|
||||
|
||||
/* Next, create a key for the rest of the fields. */
|
||||
const auto key = ncm::PackagedContentMetaReader(this->content_meta_buffer.Get(), this->content_meta_buffer.GetSize()).GetKey();
|
||||
|
||||
/* Set fields. */
|
||||
this->program_id = {key.id};
|
||||
this->program_version = key.version;
|
||||
this->content_meta_type = key.type;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetContentPath(lr::Path *out, ncm::ContentType type, std::optional<u8> index) const {
|
||||
switch (this->extension_type) {
|
||||
case ExtensionType::Nsp: return this->GetContentPathInNsp(out, type, index);
|
||||
case ExtensionType::Nspd: return this->GetContentPathInNspd(out, type, index);
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
ncm::ProgramId GetProgramId() const {
|
||||
return this->program_id;
|
||||
}
|
||||
|
||||
u32 GetProgramVersion() const {
|
||||
return this->program_version;
|
||||
}
|
||||
|
||||
ncm::ContentMetaType GetContentMetaType() const {
|
||||
return this->content_meta_type;
|
||||
}
|
||||
|
||||
u8 GetProgramIndex() const {
|
||||
return this->program_index;
|
||||
}
|
||||
private:
|
||||
Result GetContentPathInNsp(lr::Path *out, ncm::ContentType type, std::optional<u8> index) const {
|
||||
/* Create a reader. */
|
||||
auto reader = ncm::PackagedContentMetaReader(this->content_meta_buffer.Get(), this->content_meta_buffer.GetSize());
|
||||
|
||||
/* Get the content info. */
|
||||
const ncm::PackagedContentInfo *content_info = nullptr;
|
||||
if (index) {
|
||||
content_info = reader.GetContentInfo(type, *index);
|
||||
} else {
|
||||
content_info = reader.GetContentInfo(type);
|
||||
}
|
||||
R_UNLESS(content_info != nullptr, pgl::ResultApplicationContentNotFound());
|
||||
|
||||
/* Get the content id string. */
|
||||
ncm::ContentIdString id_str;
|
||||
ncm::GetStringFromContentId(id_str.data, sizeof(id_str.data), content_info->GetId());
|
||||
|
||||
/* Get the file name. */
|
||||
char file_name[ncm::ContentIdStringLength + 5];
|
||||
const size_t len = std::snprintf(file_name, sizeof(file_name), "%s.nca", id_str.data);
|
||||
R_UNLESS(len + 1 == sizeof(file_name), pgl::ResultBufferNotEnough());
|
||||
|
||||
/* Ensure we have the content. */
|
||||
bool has_content;
|
||||
R_TRY(this->SearchContent(std::addressof(has_content), out, file_name, fs::OpenDirectoryMode_File));
|
||||
R_UNLESS(has_content, pgl::ResultApplicationContentNotFound());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetContentPathInNspd(lr::Path *out, ncm::ContentType type, std::optional<u8> index) const {
|
||||
/* Get the content name. */
|
||||
const char *content_name = nullptr;
|
||||
switch (type) {
|
||||
case ncm::ContentType::Program: content_name = "program"; break;
|
||||
case ncm::ContentType::Control: content_name = "control"; break;
|
||||
case ncm::ContentType::HtmlDocument: content_name = "htmlDocument"; break;
|
||||
case ncm::ContentType::LegalInformation: content_name = "legalInformation"; break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Get the file name. */
|
||||
/* NSPD does not support indexed content, so we always use 0 as the index. */
|
||||
char file_name[0x20];
|
||||
const size_t len = std::snprintf(file_name, sizeof(file_name), "%s%d.ncd", content_name, 0);
|
||||
R_UNLESS(len + 1 <= sizeof(file_name), pgl::ResultBufferNotEnough());
|
||||
|
||||
/* Ensure we have the content. */
|
||||
bool has_content;
|
||||
R_TRY(this->SearchContent(std::addressof(has_content), out, file_name, fs::OpenDirectoryMode_Directory));
|
||||
R_UNLESS(has_content, pgl::ResultApplicationContentNotFound());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetProgramIndex(u8 *out) {
|
||||
/* Nspd programs do not have indices. */
|
||||
if (this->extension_type == ExtensionType::Nspd) {
|
||||
*out = 0;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Create a reader. */
|
||||
auto reader = ncm::PackagedContentMetaReader(this->content_meta_buffer.Get(), this->content_meta_buffer.GetSize());
|
||||
|
||||
/* Get the program content info. */
|
||||
auto program_content_info = reader.GetContentInfo(ncm::ContentType::Program);
|
||||
R_UNLESS(program_content_info, pgl::ResultApplicationContentNotFound());
|
||||
|
||||
/* Return the index. */
|
||||
*out = program_content_info->GetIdOffset();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result SetExtensionType() {
|
||||
/* First, clear the suffix if the path is a program ncd. */
|
||||
if (HasSuffix(this->content_path, "program0.ncd/")) {
|
||||
this->content_path[strnlen(this->content_path, sizeof(this->content_path)) - std::strlen("program0.ncd/")] = 0;
|
||||
}
|
||||
|
||||
if (HasSuffix(this->content_path, ".nsp")) {
|
||||
this->extension_type = ExtensionType::Nsp;
|
||||
return ResultSuccess();
|
||||
} else if (HasSuffix(this->content_path, ".nspd") || HasSuffix(this->content_path, ".nspd/")) {
|
||||
this->extension_type = ExtensionType::Nspd;
|
||||
return ResultSuccess();
|
||||
} else {
|
||||
return fs::ResultPathNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
Result SetContentMetaBuffer() {
|
||||
constexpr const char ContentMetaFileExtension[] = ".cnmt.nca";
|
||||
constexpr const char ContentMetaDirectoryExtension[] = "meta0.ncd";
|
||||
|
||||
/* Find the Content meta path. */
|
||||
bool has_content = false;
|
||||
lr::Path meta_path;
|
||||
switch (this->extension_type) {
|
||||
case ExtensionType::Nsp: R_TRY(this->SearchContent(std::addressof(has_content), std::addressof(meta_path), ContentMetaFileExtension, fs::OpenDirectoryMode_File)); break;
|
||||
case ExtensionType::Nspd: R_TRY(this->SearchContent(std::addressof(has_content), std::addressof(meta_path), ContentMetaDirectoryExtension, fs::OpenDirectoryMode_Directory)); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
R_UNLESS(has_content, pgl::ResultContentMetaNotFound());
|
||||
|
||||
/* Read the content meta buffer. */
|
||||
return ncm::ReadContentMetaPath(std::addressof(this->content_meta_buffer), meta_path.str);
|
||||
}
|
||||
|
||||
Result SearchContent(bool *out, lr::Path *out_path, const char *extension, fs::OpenDirectoryMode mode) const {
|
||||
/* Generate the root directory path. */
|
||||
char root_dir[sizeof(this->mount_name) + 2];
|
||||
std::snprintf(root_dir, sizeof(root_dir), "%s:/", this->mount_name);
|
||||
|
||||
/* Open the root directory. */
|
||||
fs::DirectoryHandle dir;
|
||||
R_TRY(fs::OpenDirectory(std::addressof(dir), root_dir, mode));
|
||||
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
|
||||
|
||||
/* Iterate over directory entries. */
|
||||
while (true) {
|
||||
fs::DirectoryEntry entry;
|
||||
s64 count;
|
||||
R_TRY(fs::ReadDirectory(std::addressof(count), std::addressof(entry), dir, 1));
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if we match the suffix. */
|
||||
if (HasSuffix(entry.name, extension)) {
|
||||
*out = true;
|
||||
if (out_path) {
|
||||
const size_t len = std::snprintf(out_path->str, sizeof(out_path->str), "%s/%s", this->content_path, entry.name);
|
||||
R_UNLESS(len + 1 < sizeof(out_path->str), pgl::ResultBufferNotEnough());
|
||||
if (entry.type == fs::DirectoryEntryType_Directory) {
|
||||
out_path->str[len] = '/';
|
||||
out_path->str[len + 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/* We didn't find a match. */
|
||||
*out = false;
|
||||
return ResultSuccess();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Result LaunchProgramFromHost(os::ProcessId *out, const char *package_path, u32 pm_flags) {
|
||||
/* Read the package. */
|
||||
HostPackageReader reader;
|
||||
R_TRY(reader.Initialize(package_path, HostPackageMountName));
|
||||
|
||||
/* Read the program info. */
|
||||
R_TRY(reader.ReadProgramInfo());
|
||||
|
||||
/* Open a host location resolver. */
|
||||
lr::LocationResolver host_resolver;
|
||||
R_TRY(lr::OpenLocationResolver(std::addressof(host_resolver), ncm::StorageId::Host));
|
||||
|
||||
/* Get the content path. */
|
||||
lr::Path content_path;
|
||||
R_TRY(reader.GetContentPath(std::addressof(content_path), ncm::ContentType::Program, reader.GetProgramIndex()));
|
||||
|
||||
/* Erase the program redirection. */
|
||||
R_TRY(host_resolver.EraseProgramRedirection(reader.GetProgramId()));
|
||||
|
||||
/* Redirect the program path to point to the new path. */
|
||||
host_resolver.RedirectProgramPath(content_path, reader.GetProgramId());
|
||||
|
||||
/* Launch the program. */
|
||||
return pgl::srv::LaunchProgram(out, ncm::ProgramLocation::Make(reader.GetProgramId(), ncm::StorageId::Host), pm_flags, pgl::LaunchFlags_None);
|
||||
}
|
||||
|
||||
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *package_path) {
|
||||
/* Read the package. */
|
||||
HostPackageReader reader;
|
||||
R_TRY(reader.Initialize(package_path, HostPackageMountName));
|
||||
|
||||
/* Read the program info. */
|
||||
R_TRY(reader.ReadProgramInfo());
|
||||
|
||||
/* Get the content meta info. */
|
||||
*out = {
|
||||
.id = reader.GetProgramId().value,
|
||||
.version = reader.GetProgramVersion(),
|
||||
.content_type = ncm::ContentType::Program,
|
||||
.id_offset = reader.GetProgramIndex(),
|
||||
};
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::pgl::srv {
|
||||
|
||||
Result LaunchProgramFromHost(os::ProcessId *out, const char *content_path, u32 pm_flags);
|
||||
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *content_path);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 "pgl_srv_shell.hpp"
|
||||
#include "pgl_srv_shell_event_observer.hpp"
|
||||
#include "pgl_srv_shell_host_utils.hpp"
|
||||
|
||||
namespace ams::pgl::srv {
|
||||
|
||||
Result ShellInterface::LaunchProgram(ams::sf::Out<os::ProcessId> out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) {
|
||||
return pgl::srv::LaunchProgram(out.GetPointer(), loc, pm_flags, pgl_flags);
|
||||
}
|
||||
|
||||
Result ShellInterface::TerminateProcess(os::ProcessId process_id) {
|
||||
return pgl::srv::TerminateProcess(process_id);
|
||||
}
|
||||
|
||||
Result ShellInterface::LaunchProgramFromHost(ams::sf::Out<os::ProcessId> out, const ams::sf::InBuffer &content_path, u32 pm_flags) {
|
||||
return pgl::srv::LaunchProgramFromHost(out.GetPointer(), reinterpret_cast<const char *>(content_path.GetPointer()), pm_flags);
|
||||
}
|
||||
|
||||
Result ShellInterface::GetHostContentMetaInfo(ams::sf::Out<pgl::ContentMetaInfo> out, const ams::sf::InBuffer &content_path) {
|
||||
return pgl::srv::GetHostContentMetaInfo(out.GetPointer(), reinterpret_cast<const char *>(content_path.GetPointer()));
|
||||
}
|
||||
|
||||
Result ShellInterface::GetApplicationProcessId(ams::sf::Out<os::ProcessId> out) {
|
||||
return pgl::srv::GetApplicationProcessId(out.GetPointer());
|
||||
}
|
||||
|
||||
Result ShellInterface::BoostSystemMemoryResourceLimit(u64 size) {
|
||||
return pgl::srv::BoostSystemMemoryResourceLimit(size);
|
||||
}
|
||||
|
||||
Result ShellInterface::IsProcessTracked(ams::sf::Out<bool> out, os::ProcessId process_id) {
|
||||
out.SetValue(pgl::srv::IsProcessTracked(process_id));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ShellInterface::EnableApplicationCrashReport(bool enabled) {
|
||||
pgl::srv::EnableApplicationCrashReport(enabled);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ShellInterface::IsApplicationCrashReportEnabled(ams::sf::Out<bool> out) {
|
||||
out.SetValue(pgl::srv::IsApplicationCrashReportEnabled());
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ShellInterface::EnableApplicationAllThreadDumpOnCrash(bool enabled) {
|
||||
pgl::srv::EnableApplicationAllThreadDumpOnCrash(enabled);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ShellInterface::TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const ams::sf::InBuffer &arg) {
|
||||
return pgl::srv::TriggerApplicationSnapShotDumper(dump_type, reinterpret_cast<const char *>(arg.GetPointer()));
|
||||
}
|
||||
|
||||
Result ShellInterface::GetShellEventObserver(ams::sf::Out<std::shared_ptr<pgl::sf::IEventObserver>> out) {
|
||||
/* Allocate a new interface. */
|
||||
auto *observer_memory = this->memory_resource->Allocate(sizeof(EventObserverInterface), alignof(EventObserverInterface));
|
||||
AMS_ABORT_UNLESS(observer_memory != nullptr);
|
||||
|
||||
/* Create the interface object. */
|
||||
new (observer_memory) EventObserverInterface;
|
||||
|
||||
/* Set the output. */
|
||||
out.SetValue(std::shared_ptr<EventObserverInterface>(reinterpret_cast<EventObserverInterface *>(observer_memory), [&](EventObserverInterface *obj) {
|
||||
/* Destroy the object. */
|
||||
obj->~EventObserverInterface();
|
||||
|
||||
/* Custom deleter: use the memory resource to free. */
|
||||
this->memory_resource->Deallocate(obj, sizeof(EventObserverInterface), alignof(EventObserverInterface));
|
||||
}));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,10 +18,39 @@
|
||||
namespace ams::pm::shell {
|
||||
|
||||
/* Shell API. */
|
||||
Result WEAK_SYMBOL LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 launch_flags) {
|
||||
Result WEAK_SYMBOL LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 launch_flags) {
|
||||
static_assert(sizeof(ncm::ProgramLocation) == sizeof(NcmProgramLocation));
|
||||
static_assert(alignof(ncm::ProgramLocation) == alignof(NcmProgramLocation));
|
||||
return pmshellLaunchProgram(launch_flags, reinterpret_cast<const NcmProgramLocation *>(&loc), reinterpret_cast<u64 *>(out_process_id));
|
||||
return pmshellLaunchProgram(launch_flags, reinterpret_cast<const NcmProgramLocation *>(&loc), reinterpret_cast<u64 *>(out));
|
||||
}
|
||||
|
||||
Result TerminateProcess(os::ProcessId process_id) {
|
||||
return ::pmshellTerminateProcess(static_cast<u64>(process_id));
|
||||
}
|
||||
|
||||
Result GetProcessEventEvent(os::SystemEvent *out) {
|
||||
::Event evt;
|
||||
R_TRY(::pmshellGetProcessEventHandle(std::addressof(evt)));
|
||||
out->Attach(evt.revent, true, svc::InvalidHandle, false, os::EventClearMode_ManualClear);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetProcessEventInfo(ProcessEventInfo *out) {
|
||||
static_assert(sizeof(*out) == sizeof(::PmProcessEventInfo));
|
||||
return ::pmshellGetProcessEventInfo(reinterpret_cast<::PmProcessEventInfo *>(out));
|
||||
}
|
||||
|
||||
Result GetApplicationProcessIdForShell(os::ProcessId *out) {
|
||||
static_assert(sizeof(*out) == sizeof(u64));
|
||||
return ::pmshellGetApplicationProcessIdForShell(reinterpret_cast<u64 *>(out));
|
||||
}
|
||||
|
||||
Result BoostSystemMemoryResourceLimit(u64 size) {
|
||||
return ::pmshellBoostSystemMemoryResourceLimit(size);
|
||||
}
|
||||
|
||||
Result EnableApplicationExtraThread() {
|
||||
return ::pmshellEnableApplicationExtraThread();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 "settings_error_report_impl.hpp"
|
||||
|
||||
namespace ams::settings::impl {
|
||||
|
||||
Result GetErrorReportSharePermission(s32 *out) {
|
||||
static_assert(sizeof(*out) == sizeof(::SetSysErrorReportSharePermission));
|
||||
return ::setsysGetErrorReportSharePermission(reinterpret_cast<::SetSysErrorReportSharePermission *>(out));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::settings::impl {
|
||||
|
||||
Result GetErrorReportSharePermission(s32 *out);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 "impl/settings_error_report_impl.hpp"
|
||||
|
||||
namespace ams::settings::system {
|
||||
|
||||
ErrorReportSharePermission GetErrorReportSharePermission() {
|
||||
s32 perm = 0;
|
||||
R_ABORT_UNLESS(settings::impl::GetErrorReportSharePermission(std::addressof(perm)));
|
||||
return static_cast<ErrorReportSharePermission>(perm);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,8 +17,11 @@
|
||||
|
||||
namespace ams::settings::fwdbg {
|
||||
|
||||
/* TODO: Implement when libnx wrapper is added. */
|
||||
bool IsDebugModeEnabled();
|
||||
bool IsDebugModeEnabled() {
|
||||
bool value = false;
|
||||
R_ABORT_UNLESS(::setsysGetDebugModeFlag(std::addressof(value)));
|
||||
return value;
|
||||
}
|
||||
|
||||
size_t WEAK_SYMBOL GetSettingsItemValueSize(const char *name, const char *key) {
|
||||
u64 size = 0;
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <vapours/results/lr_results.hpp>
|
||||
#include <vapours/results/os_results.hpp>
|
||||
#include <vapours/results/ncm_results.hpp>
|
||||
#include <vapours/results/pgl_results.hpp>
|
||||
#include <vapours/results/pm_results.hpp>
|
||||
#include <vapours/results/psc_results.hpp>
|
||||
#include <vapours/results/ro_results.hpp>
|
||||
|
||||
30
libraries/libvapours/include/vapours/results/pgl_results.hpp
Normal file
30
libraries/libvapours/include/vapours/results/pgl_results.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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/results/results_common.hpp>
|
||||
|
||||
namespace ams::pgl {
|
||||
|
||||
R_DEFINE_NAMESPACE_RESULT_MODULE(228);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(NotAvailable, 2);
|
||||
R_DEFINE_ERROR_RESULT(ApplicationNotRunning, 3);
|
||||
R_DEFINE_ERROR_RESULT(BufferNotEnough, 4);
|
||||
R_DEFINE_ERROR_RESULT(ApplicationContentNotFound, 5);
|
||||
R_DEFINE_ERROR_RESULT(ContentMetaNotFound, 6);
|
||||
|
||||
}
|
||||
@@ -110,9 +110,9 @@ int main(int argc, char **argv) {
|
||||
/* Try to terminate the process. */
|
||||
if (hos::GetVersion() >= hos::Version_10_0_0) {
|
||||
/* On 10.0.0+, use pgl to terminate. */
|
||||
sm::ScopedServiceHolder<pglInitialize, pglExit> pgl_holder;
|
||||
sm::ScopedServiceHolder<pgl::Initialize, pgl::Finalize> pgl_holder;
|
||||
if (pgl_holder) {
|
||||
pglTerminateProcess(static_cast<u64>(crashed_pid));
|
||||
pgl::TerminateProcess(crashed_pid);
|
||||
}
|
||||
} else {
|
||||
/* On < 10.0.0, use ns:dev to terminate. */
|
||||
|
||||
@@ -66,6 +66,14 @@ namespace ams::dmnt::cheat {
|
||||
return dmnt::cheat::impl::QueryCheatProcessMemory(mapping.GetPointer(), address);
|
||||
}
|
||||
|
||||
Result CheatService::BreakCheatProcess() {
|
||||
return dmnt::cheat::impl::BreakCheatProcess();
|
||||
}
|
||||
|
||||
Result CheatService::ContinueCheatProcess() {
|
||||
return dmnt::cheat::impl::ContinueCheatProcess();
|
||||
}
|
||||
|
||||
/* ========================================================================================= */
|
||||
/* =================================== Cheat Commands ==================================== */
|
||||
/* ========================================================================================= */
|
||||
@@ -95,6 +103,18 @@ namespace ams::dmnt::cheat {
|
||||
return dmnt::cheat::impl::RemoveCheat(cheat_id);
|
||||
}
|
||||
|
||||
Result CheatService::ReadStaticRegister(sf::Out<u64> out, u8 which) {
|
||||
return dmnt::cheat::impl::ReadStaticRegister(out.GetPointer(), which);
|
||||
}
|
||||
|
||||
Result CheatService::WriteStaticRegister(u8 which, u64 value) {
|
||||
return dmnt::cheat::impl::WriteStaticRegister(which, value);
|
||||
}
|
||||
|
||||
Result CheatService::ResetStaticRegisters() {
|
||||
return dmnt::cheat::impl::ResetStaticRegisters();
|
||||
}
|
||||
|
||||
/* ========================================================================================= */
|
||||
/* =================================== Address Commands ================================== */
|
||||
/* ========================================================================================= */
|
||||
|
||||
@@ -33,14 +33,19 @@ namespace ams::dmnt::cheat {
|
||||
ReadCheatProcessMemory = 65102,
|
||||
WriteCheatProcessMemory = 65103,
|
||||
QueryCheatProcessMemory = 65104,
|
||||
BreakCheatProcess = 65105,
|
||||
ContinueCheatProcess = 65106,
|
||||
|
||||
/* Interact with Cheats */
|
||||
GetCheatCount = 65200,
|
||||
GetCheats = 65201,
|
||||
GetCheatById = 65202,
|
||||
ToggleCheat = 65203,
|
||||
AddCheat = 65204,
|
||||
RemoveCheat = 65205,
|
||||
GetCheatCount = 65200,
|
||||
GetCheats = 65201,
|
||||
GetCheatById = 65202,
|
||||
ToggleCheat = 65203,
|
||||
AddCheat = 65204,
|
||||
RemoveCheat = 65205,
|
||||
ReadStaticRegister = 65206,
|
||||
WriteStaticRegister = 65207,
|
||||
ResetStaticRegisters = 65208,
|
||||
|
||||
/* Interact with Frozen Addresses */
|
||||
GetFrozenAddressCount = 65300,
|
||||
@@ -60,6 +65,8 @@ namespace ams::dmnt::cheat {
|
||||
Result ReadCheatProcessMemory(const sf::OutBuffer &buffer, u64 address, u64 out_size);
|
||||
Result WriteCheatProcessMemory(const sf::InBuffer &buffer, u64 address, u64 in_size);
|
||||
Result QueryCheatProcessMemory(sf::Out<MemoryInfo> mapping, u64 address);
|
||||
Result BreakCheatProcess();
|
||||
Result ContinueCheatProcess();
|
||||
|
||||
Result GetCheatCount(sf::Out<u64> out_count);
|
||||
Result GetCheats(const sf::OutArray<CheatEntry> &cheats, sf::Out<u64> out_count, u64 offset);
|
||||
@@ -67,6 +74,9 @@ namespace ams::dmnt::cheat {
|
||||
Result ToggleCheat(u32 cheat_id);
|
||||
Result AddCheat(const CheatDefinition &cheat, sf::Out<u32> out_cheat_id, bool enabled);
|
||||
Result RemoveCheat(u32 cheat_id);
|
||||
Result ReadStaticRegister(sf::Out<u64> out, u8 which);
|
||||
Result WriteStaticRegister(u8 which, u64 value);
|
||||
Result ResetStaticRegisters();
|
||||
|
||||
Result GetFrozenAddressCount(sf::Out<u64> out_count);
|
||||
Result GetFrozenAddresses(const sf::OutArray<FrozenAddressEntry> &addresses, sf::Out<u64> out_count, u64 offset);
|
||||
@@ -86,6 +96,8 @@ namespace ams::dmnt::cheat {
|
||||
MAKE_SERVICE_COMMAND_META(ReadCheatProcessMemory),
|
||||
MAKE_SERVICE_COMMAND_META(WriteCheatProcessMemory),
|
||||
MAKE_SERVICE_COMMAND_META(QueryCheatProcessMemory),
|
||||
MAKE_SERVICE_COMMAND_META(BreakCheatProcess),
|
||||
MAKE_SERVICE_COMMAND_META(ContinueCheatProcess),
|
||||
|
||||
MAKE_SERVICE_COMMAND_META(GetCheatCount),
|
||||
MAKE_SERVICE_COMMAND_META(GetCheats),
|
||||
@@ -93,6 +105,9 @@ namespace ams::dmnt::cheat {
|
||||
MAKE_SERVICE_COMMAND_META(ToggleCheat),
|
||||
MAKE_SERVICE_COMMAND_META(AddCheat),
|
||||
MAKE_SERVICE_COMMAND_META(RemoveCheat),
|
||||
MAKE_SERVICE_COMMAND_META(ReadStaticRegister),
|
||||
MAKE_SERVICE_COMMAND_META(WriteStaticRegister),
|
||||
MAKE_SERVICE_COMMAND_META(ResetStaticRegisters),
|
||||
|
||||
MAKE_SERVICE_COMMAND_META(GetFrozenAddressCount),
|
||||
MAKE_SERVICE_COMMAND_META(GetFrozenAddresses),
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace ams::dmnt::cheat::impl {
|
||||
static constexpr s32 DebugEventsThreadPriority = -1;
|
||||
private:
|
||||
os::Mutex cheat_lock;
|
||||
os::Event unsafe_break_event;
|
||||
os::Event debug_events_event; /* Autoclear. */
|
||||
os::ThreadType detect_thread, debug_events_thread;
|
||||
os::SystemEvent cheat_process_event;
|
||||
@@ -41,6 +42,7 @@ namespace ams::dmnt::cheat::impl {
|
||||
CheatProcessMetadata cheat_process_metadata = {};
|
||||
|
||||
os::ThreadType vm_thread;
|
||||
bool broken_unsafe = false;
|
||||
bool needs_reload_vm = false;
|
||||
CheatVirtualMachine cheat_vm;
|
||||
|
||||
@@ -88,6 +90,8 @@ namespace ams::dmnt::cheat::impl {
|
||||
for (size_t i = 0; i < MaxCheatCount; i++) {
|
||||
this->ResetCheatEntry(i);
|
||||
}
|
||||
|
||||
this->cheat_vm.ResetStaticRegisters();
|
||||
}
|
||||
|
||||
CheatEntry *GetCheatEntryById(size_t i) {
|
||||
@@ -122,6 +126,10 @@ namespace ams::dmnt::cheat::impl {
|
||||
|
||||
void CloseActiveCheatProcess() {
|
||||
if (this->cheat_process_debug_handle != svc::InvalidHandle) {
|
||||
/* We don't need to do any unsafe brekaing. */
|
||||
this->broken_unsafe = false;
|
||||
this->unsafe_break_event.Signal();
|
||||
|
||||
/* Knock out the debug events thread. */
|
||||
os::CancelThreadSynchronization(std::addressof(this->debug_events_thread));
|
||||
|
||||
@@ -185,7 +193,7 @@ namespace ams::dmnt::cheat::impl {
|
||||
}
|
||||
|
||||
public:
|
||||
CheatProcessManager() : cheat_lock(false), debug_events_event(os::EventClearMode_AutoClear), cheat_process_event(os::EventClearMode_AutoClear, true) {
|
||||
CheatProcessManager() : cheat_lock(false), unsafe_break_event(os::EventClearMode_ManualClear), debug_events_event(os::EventClearMode_AutoClear), cheat_process_event(os::EventClearMode_AutoClear, true) {
|
||||
/* Learn whether we should enable cheats by default. */
|
||||
{
|
||||
u8 en = 0;
|
||||
@@ -257,6 +265,19 @@ namespace ams::dmnt::cheat::impl {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BreakCheatProcessUnsafe() {
|
||||
this->broken_unsafe = true;
|
||||
this->unsafe_break_event.Clear();
|
||||
return svcBreakDebugProcess(this->GetCheatProcessHandle());
|
||||
}
|
||||
|
||||
Result ContinueCheatProcessUnsafe() {
|
||||
this->broken_unsafe = false;
|
||||
this->unsafe_break_event.Signal();
|
||||
dmnt::cheat::impl::ContinueCheatProcess(this->GetCheatProcessHandle());
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetCheatProcessMappingCount(u64 *out_count) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
@@ -335,6 +356,22 @@ namespace ams::dmnt::cheat::impl {
|
||||
return svcQueryDebugProcessMemory(mapping, &tmp, this->GetCheatProcessHandle(), address);
|
||||
}
|
||||
|
||||
Result BreakCheatProcess() {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
return this->BreakCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result ContinueCheatProcess() {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
return this->ContinueCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result GetCheatCount(u64 *out_count) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
@@ -436,6 +473,35 @@ namespace ams::dmnt::cheat::impl {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ReadStaticRegister(u64 *out, size_t which) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
R_UNLESS(which < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid());
|
||||
|
||||
*out = this->cheat_vm.GetStaticRegister(which);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result WriteStaticRegister(size_t which, u64 value) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
R_UNLESS(which < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid());
|
||||
|
||||
this->cheat_vm.SetStaticRegister(which, value);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ResetStaticRegisters() {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
this->cheat_vm.ResetStaticRegisters();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetFrozenAddressCount(u64 *out_count) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
@@ -533,7 +599,20 @@ namespace ams::dmnt::cheat::impl {
|
||||
this_ptr->debug_events_event.Wait();
|
||||
while (true) {
|
||||
while (R_SUCCEEDED(svcWaitSynchronizationSingle(this_ptr->GetCheatProcessHandle(), std::numeric_limits<u64>::max()))) {
|
||||
std::scoped_lock lk(this_ptr->cheat_lock);
|
||||
this_ptr->cheat_lock.Lock();
|
||||
ON_SCOPE_EXIT { this_ptr->cheat_lock.Unlock(); };
|
||||
|
||||
/* If we did an unsafe break, wait until we're not broken. */
|
||||
if (this_ptr->broken_unsafe) {
|
||||
this_ptr->cheat_lock.Unlock();
|
||||
this_ptr->unsafe_break_event.Wait();
|
||||
this_ptr->cheat_lock.Lock();
|
||||
if (this_ptr->GetCheatProcessHandle() != svc::InvalidHandle) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle any pending debug events. */
|
||||
if (this_ptr->HasActiveCheatProcess()) {
|
||||
@@ -680,6 +759,10 @@ namespace ams::dmnt::cheat::impl {
|
||||
/* Cancel process guard. */
|
||||
proc_guard.Cancel();
|
||||
|
||||
/* Reset broken state. */
|
||||
this->broken_unsafe = false;
|
||||
this->unsafe_break_event.Signal();
|
||||
|
||||
/* If new process, start the process. */
|
||||
if (on_process_launch) {
|
||||
this->StartProcess(this->cheat_process_metadata.process_id);
|
||||
@@ -1021,6 +1104,14 @@ namespace ams::dmnt::cheat::impl {
|
||||
return GetReference(g_cheat_process_manager).WriteCheatProcessMemoryUnsafe(process_addr, data, size);
|
||||
}
|
||||
|
||||
Result BreakCheatProcessUnsafe() {
|
||||
return GetReference(g_cheat_process_manager).BreakCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result ContinueCheatProcessUnsafe() {
|
||||
return GetReference(g_cheat_process_manager).ContinueCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result GetCheatProcessMappingCount(u64 *out_count) {
|
||||
return GetReference(g_cheat_process_manager).GetCheatProcessMappingCount(out_count);
|
||||
}
|
||||
@@ -1041,6 +1132,14 @@ namespace ams::dmnt::cheat::impl {
|
||||
return GetReference(g_cheat_process_manager).QueryCheatProcessMemory(mapping, address);
|
||||
}
|
||||
|
||||
Result BreakCheatProcess() {
|
||||
return GetReference(g_cheat_process_manager).BreakCheatProcess();
|
||||
}
|
||||
|
||||
Result ContinueCheatProcess() {
|
||||
return GetReference(g_cheat_process_manager).ContinueCheatProcess();
|
||||
}
|
||||
|
||||
Result GetCheatCount(u64 *out_count) {
|
||||
return GetReference(g_cheat_process_manager).GetCheatCount(out_count);
|
||||
}
|
||||
@@ -1065,6 +1164,18 @@ namespace ams::dmnt::cheat::impl {
|
||||
return GetReference(g_cheat_process_manager).RemoveCheat(cheat_id);
|
||||
}
|
||||
|
||||
Result ReadStaticRegister(u64 *out, size_t which) {
|
||||
return GetReference(g_cheat_process_manager).ReadStaticRegister(out, which);
|
||||
}
|
||||
|
||||
Result WriteStaticRegister(size_t which, u64 value) {
|
||||
return GetReference(g_cheat_process_manager).WriteStaticRegister(which, value);
|
||||
}
|
||||
|
||||
Result ResetStaticRegisters() {
|
||||
return GetReference(g_cheat_process_manager).ResetStaticRegisters();
|
||||
}
|
||||
|
||||
Result GetFrozenAddressCount(u64 *out_count) {
|
||||
return GetReference(g_cheat_process_manager).GetFrozenAddressCount(out_count);
|
||||
}
|
||||
|
||||
@@ -28,11 +28,16 @@ namespace ams::dmnt::cheat::impl {
|
||||
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size);
|
||||
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, void *data, size_t size);
|
||||
|
||||
Result BreakCheatProcessUnsafe();
|
||||
Result ContinueCheatProcessUnsafe();
|
||||
|
||||
Result GetCheatProcessMappingCount(u64 *out_count);
|
||||
Result GetCheatProcessMappings(MemoryInfo *mappings, size_t max_count, u64 *out_count, u64 offset);
|
||||
Result ReadCheatProcessMemory(u64 proc_addr, void *out_data, size_t size);
|
||||
Result WriteCheatProcessMemory(u64 proc_addr, const void *data, size_t size);
|
||||
Result QueryCheatProcessMemory(MemoryInfo *mapping, u64 address);
|
||||
Result BreakCheatProcess();
|
||||
Result ContinueCheatProcess();
|
||||
|
||||
Result GetCheatCount(u64 *out_count);
|
||||
Result GetCheats(CheatEntry *cheats, size_t max_count, u64 *out_count, u64 offset);
|
||||
@@ -40,6 +45,9 @@ namespace ams::dmnt::cheat::impl {
|
||||
Result ToggleCheat(u32 cheat_id);
|
||||
Result AddCheat(u32 *out_id, const CheatDefinition &def, bool enabled);
|
||||
Result RemoveCheat(u32 cheat_id);
|
||||
Result ReadStaticRegister(u64 *out, size_t which);
|
||||
Result WriteStaticRegister(size_t which, u64 value);
|
||||
Result ResetStaticRegisters();
|
||||
|
||||
Result GetFrozenAddressCount(u64 *out_count);
|
||||
Result GetFrozenAddresses(FrozenAddressEntry *frz_addrs, size_t max_count, u64 *out_count, u64 offset);
|
||||
|
||||
@@ -237,6 +237,22 @@ namespace ams::dmnt::cheat::impl {
|
||||
this->LogToDebugFile("Act[%02x]: %d\n", i, opcode->save_restore_regmask.should_operate[i]);
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ReadWriteStaticRegister:
|
||||
this->LogToDebugFile("Opcode: Read/Write Static Register\n");
|
||||
if (opcode->rw_static_reg.static_idx < NumReadableStaticRegisters) {
|
||||
this->LogToDebugFile("Op Type: ReadStaticRegister\n");
|
||||
} else {
|
||||
this->LogToDebugFile("Op Type: WriteStaticRegister\n");
|
||||
}
|
||||
this->LogToDebugFile("Reg Idx: %x\n", opcode->rw_static_reg.idx);
|
||||
this->LogToDebugFile("Stc Idx: %x\n", opcode->rw_static_reg.static_idx);
|
||||
break;
|
||||
case CheatVmOpcodeType_BreakProcess:
|
||||
this->LogToDebugFile("Opcode: Break Cheat Process\n");
|
||||
break;
|
||||
case CheatVmOpcodeType_ContinueProcess:
|
||||
this->LogToDebugFile("Opcode: Continue Cheat Process\n");
|
||||
break;
|
||||
case CheatVmOpcodeType_DebugLog:
|
||||
this->LogToDebugFile("Opcode: Debug Log\n");
|
||||
this->LogToDebugFile("Bit Width: %x\n", opcode->debug_log.bit_width);
|
||||
@@ -570,6 +586,30 @@ namespace ams::dmnt::cheat::impl {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ReadWriteStaticRegister:
|
||||
{
|
||||
/* C3000XXx */
|
||||
/* C3 = opcode 0xC3. */
|
||||
/* XX = static register index. */
|
||||
/* x = register index. */
|
||||
opcode.rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF);
|
||||
opcode.rw_static_reg.idx = (first_dword & 0xF);
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_BreakProcess:
|
||||
{
|
||||
/* FF0????? */
|
||||
/* FF0 = opcode 0xFF0 */
|
||||
/* Breaks the current process. */
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ContinueProcess:
|
||||
{
|
||||
/* FF1????? */
|
||||
/* FF1 = opcode 0xFF1 */
|
||||
/* Continues the current process. */
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_DebugLog:
|
||||
{
|
||||
/* FFFTIX## */
|
||||
@@ -1174,6 +1214,21 @@ namespace ams::dmnt::cheat::impl {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ReadWriteStaticRegister:
|
||||
if (cur_opcode.rw_static_reg.static_idx < NumReadableStaticRegisters) {
|
||||
/* Load a register with a static register. */
|
||||
this->registers[cur_opcode.rw_static_reg.idx] = this->static_registers[cur_opcode.rw_static_reg.static_idx];
|
||||
} else {
|
||||
/* Store a register to a static register. */
|
||||
this->static_registers[cur_opcode.rw_static_reg.static_idx] = this->registers[cur_opcode.rw_static_reg.idx];
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_BreakProcess:
|
||||
dmnt::cheat::impl::BreakCheatProcessUnsafe();
|
||||
break;
|
||||
case CheatVmOpcodeType_ContinueProcess:
|
||||
dmnt::cheat::impl::ContinueCheatProcessUnsafe();
|
||||
break;
|
||||
case CheatVmOpcodeType_DebugLog:
|
||||
{
|
||||
/* Read value from memory. */
|
||||
|
||||
@@ -42,12 +42,15 @@ namespace ams::dmnt::cheat::impl {
|
||||
CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0,
|
||||
CheatVmOpcodeType_SaveRestoreRegister = 0xC1,
|
||||
CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2,
|
||||
CheatVmOpcodeType_ReadWriteStaticRegister = 0xC3,
|
||||
|
||||
/* This is a meta entry, and not a real opcode. */
|
||||
/* This is to facilitate multi-nybble instruction decoding. */
|
||||
CheatVmOpcodeType_DoubleExtendedWidth = 0xF0,
|
||||
|
||||
/* Double-extended width opcodes. */
|
||||
CheatVmOpcodeType_BreakProcess = 0xFF0,
|
||||
CheatVmOpcodeType_ContinueProcess = 0xFF1,
|
||||
CheatVmOpcodeType_DebugLog = 0xFFF,
|
||||
};
|
||||
|
||||
@@ -223,6 +226,11 @@ namespace ams::dmnt::cheat::impl {
|
||||
bool should_operate[0x10];
|
||||
};
|
||||
|
||||
struct ReadWriteStaticRegisterOpcode {
|
||||
u32 static_idx;
|
||||
u32 idx;
|
||||
};
|
||||
|
||||
struct DebugLogOpcode {
|
||||
u32 bit_width;
|
||||
u32 log_id;
|
||||
@@ -252,6 +260,7 @@ namespace ams::dmnt::cheat::impl {
|
||||
BeginRegisterConditionalOpcode begin_reg_cond;
|
||||
SaveRestoreRegisterOpcode save_restore_reg;
|
||||
SaveRestoreRegisterMaskOpcode save_restore_regmask;
|
||||
ReadWriteStaticRegisterOpcode rw_static_reg;
|
||||
DebugLogOpcode debug_log;
|
||||
};
|
||||
};
|
||||
@@ -260,6 +269,9 @@ namespace ams::dmnt::cheat::impl {
|
||||
public:
|
||||
constexpr static size_t MaximumProgramOpcodeCount = 0x400;
|
||||
constexpr static size_t NumRegisters = 0x10;
|
||||
constexpr static size_t NumReadableStaticRegisters = 0x80;
|
||||
constexpr static size_t NumWritableStaticRegisters = 0x80;
|
||||
constexpr static size_t NumStaticRegisters = NumReadableStaticRegisters + NumWritableStaticRegisters;
|
||||
private:
|
||||
size_t num_opcodes = 0;
|
||||
size_t instruction_ptr = 0;
|
||||
@@ -268,6 +280,7 @@ namespace ams::dmnt::cheat::impl {
|
||||
u32 program[MaximumProgramOpcodeCount] = {0};
|
||||
u64 registers[NumRegisters] = {0};
|
||||
u64 saved_values[NumRegisters] = {0};
|
||||
u64 static_registers[NumStaticRegisters] = {0};
|
||||
size_t loop_tops[NumRegisters] = {0};
|
||||
private:
|
||||
bool DecodeNextOpcode(CheatVmOpcode *out);
|
||||
@@ -294,6 +307,18 @@ namespace ams::dmnt::cheat::impl {
|
||||
|
||||
bool LoadProgram(const CheatEntry *cheats, size_t num_cheats);
|
||||
void Execute(const CheatProcessMetadata *metadata);
|
||||
|
||||
u64 GetStaticRegister(size_t which) const {
|
||||
return this->static_registers[which];
|
||||
}
|
||||
|
||||
void SetStaticRegister(size_t which, u64 value) {
|
||||
this->static_registers[which] = value;
|
||||
}
|
||||
|
||||
void ResetStaticRegisters() {
|
||||
std::memset(this->static_registers, 0, sizeof(this->static_registers));
|
||||
}
|
||||
#ifdef DMNT_CHEAT_VM_DEBUG_LOG
|
||||
private:
|
||||
fs::FileHandle debug_log_file;
|
||||
|
||||
128
stratosphere/pgl/Makefile
Normal file
128
stratosphere/pgl/Makefile
Normal file
@@ -0,0 +1,128 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
# pull in common stratosphere sysmodule configuration
|
||||
#---------------------------------------------------------------------------------
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \
|
||||
$(notdir $(wildcard $(dir)/*.c))))
|
||||
CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c)))
|
||||
CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c)))
|
||||
CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c)))
|
||||
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \
|
||||
$(notdir $(wildcard $(dir)/*.cpp))))
|
||||
CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp)))
|
||||
CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp)))
|
||||
CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp)))
|
||||
|
||||
SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \
|
||||
$(notdir $(wildcard $(dir)/*.s))))
|
||||
SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s)))
|
||||
SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s)))
|
||||
SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s)))
|
||||
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
|
||||
|
||||
ifeq ($(strip $(CONFIG_JSON)),)
|
||||
jsons := $(wildcard *.json)
|
||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||
else
|
||||
ifneq (,$(findstring config.json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/config.json
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||
endif
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).nsp
|
||||
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso
|
||||
else
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||
endif
|
||||
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
88
stratosphere/pgl/pgl.json
Normal file
88
stratosphere/pgl/pgl.json
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"name": "pgl",
|
||||
"title_id": "0x0100000000000042",
|
||||
"title_id_range_min": "0x0100000000000042",
|
||||
"title_id_range_max": "0x0100000000000042",
|
||||
"main_thread_stack_size": "0x00004000",
|
||||
"main_thread_priority": 49,
|
||||
"default_cpu_id": 3,
|
||||
"process_category": 0,
|
||||
"is_retail": true,
|
||||
"pool_partition": 2,
|
||||
"is_64_bit": true,
|
||||
"address_space_type": 3,
|
||||
"filesystem_access": {
|
||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||
},
|
||||
"service_access": ["erpt:c", "fatal:u", "fsp-srv", "ldr:shel", "lm", "lr", "pm:shell", "set", "set:sys"],
|
||||
"service_host": ["pgl"],
|
||||
"kernel_capabilities": [{
|
||||
"type": "kernel_flags",
|
||||
"value": {
|
||||
"highest_thread_priority": 63,
|
||||
"lowest_thread_priority": 24,
|
||||
"lowest_cpu_id": 3,
|
||||
"highest_cpu_id": 3
|
||||
}
|
||||
}, {
|
||||
"type": "syscalls",
|
||||
"value": {
|
||||
"svcSetHeapSize": "0x01",
|
||||
"svcSetMemoryPermission": "0x02",
|
||||
"svcSetMemoryAttribute": "0x03",
|
||||
"svcMapMemory": "0x04",
|
||||
"svcUnmapMemory": "0x05",
|
||||
"svcQueryMemory": "0x06",
|
||||
"svcExitProcess": "0x07",
|
||||
"svcCreateThread": "0x08",
|
||||
"svcStartThread": "0x09",
|
||||
"svcExitThread": "0x0a",
|
||||
"svcSleepThread": "0x0b",
|
||||
"svcGetThreadPriority": "0x0c",
|
||||
"svcSetThreadPriority": "0x0d",
|
||||
"svcGetThreadCoreMask": "0x0e",
|
||||
"svcSetThreadCoreMask": "0x0f",
|
||||
"svcGetCurrentProcessorNumber": "0x10",
|
||||
"svcSignalEvent": "0x11",
|
||||
"svcClearEvent": "0x12",
|
||||
"svcMapSharedMemory": "0x13",
|
||||
"svcUnmapSharedMemory": "0x14",
|
||||
"svcCreateTransferMemory": "0x15",
|
||||
"svcCloseHandle": "0x16",
|
||||
"svcResetSignal": "0x17",
|
||||
"svcWaitSynchronization": "0x18",
|
||||
"svcCancelSynchronization": "0x19",
|
||||
"svcArbitrateLock": "0x1a",
|
||||
"svcArbitrateUnlock": "0x1b",
|
||||
"svcWaitProcessWideKeyAtomic": "0x1c",
|
||||
"svcSignalProcessWideKey": "0x1d",
|
||||
"svcGetSystemTick": "0x1e",
|
||||
"svcConnectToNamedPort": "0x1f",
|
||||
"svcSendSyncRequestLight": "0x20",
|
||||
"svcSendSyncRequest": "0x21",
|
||||
"svcSendSyncRequestWithUserBuffer": "0x22",
|
||||
"svcSendAsyncRequestWithUserBuffer": "0x23",
|
||||
"svcGetProcessId": "0x24",
|
||||
"svcGetThreadId": "0x25",
|
||||
"svcBreak": "0x26",
|
||||
"svcOutputDebugString": "0x27",
|
||||
"svcReturnFromException": "0x28",
|
||||
"svcGetInfo": "0x29",
|
||||
"svcWaitForAddress": "0x34",
|
||||
"svcSignalToAddress": "0x35",
|
||||
"svcCreateSession": "0x40",
|
||||
"svcAcceptSession": "0x41",
|
||||
"svcReplyAndReceiveLight": "0x42",
|
||||
"svcReplyAndReceive": "0x43",
|
||||
"svcReplyAndReceiveWithUserBuffer": "0x44",
|
||||
"svcCreateEvent": "0x45",
|
||||
"svcCallSecureMonitor": "0x7F"
|
||||
}
|
||||
}, {
|
||||
"type": "min_kernel_version",
|
||||
"value": "0x0091"
|
||||
}, {
|
||||
"type": "handle_table_size",
|
||||
"value": 256
|
||||
}]
|
||||
}
|
||||
160
stratosphere/pgl/source/pgl_main.cpp
Normal file
160
stratosphere/pgl/source/pgl_main.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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>
|
||||
|
||||
extern "C" {
|
||||
extern u32 __start__;
|
||||
|
||||
u32 __nx_applet_type = AppletType_None;
|
||||
u32 __nx_fs_num_sessions = 1;
|
||||
|
||||
#define INNER_HEAP_SIZE 0x4000
|
||||
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
||||
char nx_inner_heap[INNER_HEAP_SIZE];
|
||||
|
||||
void __libnx_initheap(void);
|
||||
void __appInit(void);
|
||||
void __appExit(void);
|
||||
|
||||
/* Exception handling. */
|
||||
alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize];
|
||||
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
|
||||
void __libnx_exception_handler(ThreadExceptionDump *ctx);
|
||||
}
|
||||
|
||||
namespace ams {
|
||||
|
||||
ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Pgl;
|
||||
|
||||
namespace result {
|
||||
|
||||
bool CallFatalOnResultAssertion = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using namespace ams;
|
||||
|
||||
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
|
||||
ams::CrashHandler(ctx);
|
||||
}
|
||||
|
||||
namespace ams::pgl {
|
||||
|
||||
namespace {
|
||||
|
||||
/* pgl. */
|
||||
constexpr size_t NumServers = 1;
|
||||
ams::sf::hipc::ServerManager<NumServers> g_server_manager;
|
||||
|
||||
constexpr sm::ServiceName ShellServiceName = sm::ServiceName::Encode("pgl");
|
||||
constexpr size_t ShellMaxSessions = 8; /* Official maximum is 8. */
|
||||
|
||||
/* TODO: C++20 constinit */ pgl::srv::ShellInterface g_shell_interface;
|
||||
|
||||
ALWAYS_INLINE std::shared_ptr<pgl::srv::ShellInterface> GetSharedPointerToShellInterface() {
|
||||
return ams::sf::ServiceObjectTraits<pgl::srv::ShellInterface>::SharedPointerHelper::GetEmptyDeleteSharedPointer(std::addressof(g_shell_interface));
|
||||
}
|
||||
|
||||
void RegisterServiceSession() {
|
||||
R_ABORT_UNLESS(g_server_manager.RegisterServer<pgl::srv::ShellInterface>(ShellServiceName, ShellMaxSessions, GetSharedPointerToShellInterface()));
|
||||
}
|
||||
|
||||
void LoopProcess() {
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
/* NOTE: Nintendo reserves only 0x2000 bytes for this heap, which is used "mostly" to allocate shell event observers. */
|
||||
/* However, we would like very much for homebrew sysmodules to be able to subscribe to events if they so choose */
|
||||
/* And so we will use a larger heap (32 KB). */
|
||||
/* We should have a smaller memory footprint than N in the end, regardless. */
|
||||
u8 g_heap_memory[32_KB];
|
||||
TYPED_STORAGE(ams::sf::ExpHeapMemoryResource) g_heap_memory_resource;
|
||||
|
||||
void *Allocate(size_t size) {
|
||||
return lmem::AllocateFromExpHeap(GetReference(g_heap_memory_resource).GetHandle(), size);
|
||||
}
|
||||
|
||||
void Deallocate(void *p, size_t size) {
|
||||
return lmem::FreeToExpHeap(GetReference(g_heap_memory_resource).GetHandle(), p);
|
||||
}
|
||||
|
||||
void InitializeHeap() {
|
||||
auto heap_handle = lmem::CreateExpHeap(g_heap_memory, sizeof(g_heap_memory), lmem::CreateOption_ThreadSafe);
|
||||
new (GetPointer(g_heap_memory_resource)) ams::sf::ExpHeapMemoryResource(heap_handle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void __libnx_initheap(void) {
|
||||
void* addr = nx_inner_heap;
|
||||
size_t size = nx_inner_heap_size;
|
||||
|
||||
/* Newlib */
|
||||
extern char* fake_heap_start;
|
||||
extern char* fake_heap_end;
|
||||
|
||||
fake_heap_start = (char*)addr;
|
||||
fake_heap_end = (char*)addr + size;
|
||||
|
||||
ams::pgl::InitializeHeap();
|
||||
}
|
||||
|
||||
void __appInit(void) {
|
||||
hos::SetVersionForLibnx();
|
||||
|
||||
fs::SetAllocator(pgl::Allocate, pgl::Deallocate);
|
||||
|
||||
sm::DoWithSession([&]() {
|
||||
R_ABORT_UNLESS(setInitialize());
|
||||
R_ABORT_UNLESS(setsysInitialize());
|
||||
R_ABORT_UNLESS(pmshellInitialize());
|
||||
R_ABORT_UNLESS(ldrShellInitialize());
|
||||
R_ABORT_UNLESS(lrInitialize());
|
||||
R_ABORT_UNLESS(fsInitialize());
|
||||
});
|
||||
|
||||
ams::CheckApiVersion();
|
||||
}
|
||||
|
||||
void __appExit(void) {
|
||||
fsExit();
|
||||
lrExit();
|
||||
ldrShellExit();
|
||||
pmshellExit();
|
||||
setsysExit();
|
||||
setExit();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* Register the pgl service. */
|
||||
pgl::RegisterServiceSession();
|
||||
|
||||
/* Initialize the server library. */
|
||||
pgl::srv::Initialize(std::addressof(pgl::g_shell_interface), GetPointer(pgl::g_heap_memory_resource));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
pgl::LoopProcess();
|
||||
|
||||
/* Cleanup */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -34,26 +34,6 @@ namespace ams::pm::impl {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
enum LaunchFlags {
|
||||
LaunchFlags_None = 0,
|
||||
LaunchFlags_SignalOnExit = (1 << 0),
|
||||
LaunchFlags_SignalOnStart = (1 << 1),
|
||||
LaunchFlags_SignalOnException = (1 << 2),
|
||||
LaunchFlags_SignalOnDebugEvent = (1 << 3),
|
||||
LaunchFlags_StartSuspended = (1 << 4),
|
||||
LaunchFlags_DisableAslr = (1 << 5),
|
||||
};
|
||||
|
||||
enum LaunchFlagsDeprecated {
|
||||
LaunchFlagsDeprecated_None = 0,
|
||||
LaunchFlagsDeprecated_SignalOnExit = (1 << 0),
|
||||
LaunchFlagsDeprecated_StartSuspended = (1 << 1),
|
||||
LaunchFlagsDeprecated_SignalOnException = (1 << 2),
|
||||
LaunchFlagsDeprecated_DisableAslr = (1 << 3),
|
||||
LaunchFlagsDeprecated_SignalOnDebugEvent = (1 << 4),
|
||||
LaunchFlagsDeprecated_SignalOnStart = (1 << 5),
|
||||
};
|
||||
|
||||
#define GET_FLAG_MASK(flag) (hos_version >= hos::Version_5_0_0 ? static_cast<u32>(LaunchFlags_##flag) : static_cast<u32>(LaunchFlagsDeprecated_##flag))
|
||||
|
||||
inline bool ShouldSignalOnExit(u32 launch_flags) {
|
||||
@@ -91,45 +71,6 @@ namespace ams::pm::impl {
|
||||
|
||||
#undef GET_FLAG_MASK
|
||||
|
||||
enum class ProcessEvent {
|
||||
None = 0,
|
||||
Exited = 1,
|
||||
Started = 2,
|
||||
Exception = 3,
|
||||
DebugRunning = 4,
|
||||
DebugBreak = 5,
|
||||
};
|
||||
|
||||
enum class ProcessEventDeprecated {
|
||||
None = 0,
|
||||
Exception = 1,
|
||||
Exited = 2,
|
||||
DebugRunning = 3,
|
||||
DebugBreak = 4,
|
||||
Started = 5,
|
||||
};
|
||||
|
||||
inline u32 GetProcessEventValue(ProcessEvent event) {
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
return static_cast<u32>(event);
|
||||
}
|
||||
switch (event) {
|
||||
case ProcessEvent::None:
|
||||
return static_cast<u32>(ProcessEventDeprecated::None);
|
||||
case ProcessEvent::Exited:
|
||||
return static_cast<u32>(ProcessEventDeprecated::Exited);
|
||||
case ProcessEvent::Started:
|
||||
return static_cast<u32>(ProcessEventDeprecated::Started);
|
||||
case ProcessEvent::Exception:
|
||||
return static_cast<u32>(ProcessEventDeprecated::Exception);
|
||||
case ProcessEvent::DebugRunning:
|
||||
return static_cast<u32>(ProcessEventDeprecated::DebugRunning);
|
||||
case ProcessEvent::DebugBreak:
|
||||
return static_cast<u32>(ProcessEventDeprecated::DebugBreak);
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t MaxProcessInfos>
|
||||
class ProcessInfoAllocator {
|
||||
NON_COPYABLE(ProcessInfoAllocator);
|
||||
|
||||
Reference in New Issue
Block a user