ams-mtc: add ams-mtc
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidThreadActivity(ams::svc::ThreadActivity thread_activity) {
|
||||
switch (thread_activity) {
|
||||
case ams::svc::ThreadActivity_Runnable:
|
||||
case ams::svc::ThreadActivity_Paused:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsValidProcessActivity(ams::svc::ProcessActivity process_activity) {
|
||||
switch (process_activity) {
|
||||
case ams::svc::ProcessActivity_Runnable:
|
||||
case ams::svc::ProcessActivity_Paused:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result SetThreadActivity(ams::svc::Handle thread_handle, ams::svc::ThreadActivity thread_activity) {
|
||||
/* Validate the activity. */
|
||||
R_UNLESS(IsValidThreadActivity(thread_activity), svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Get the thread from its handle. */
|
||||
KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject<KThread>(thread_handle);
|
||||
R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Check that the activity is being set on a non-current thread for the current process. */
|
||||
R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(), svc::ResultInvalidHandle());
|
||||
R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(), svc::ResultBusy());
|
||||
|
||||
/* Set the activity. */
|
||||
R_TRY(thread->SetActivity(thread_activity));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetProcessActivity(ams::svc::Handle process_handle, ams::svc::ProcessActivity process_activity) {
|
||||
/* Validate the activity. */
|
||||
R_UNLESS(IsValidProcessActivity(process_activity), svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Get the process from its handle. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Check that the activity isn't being set on the current process. */
|
||||
R_UNLESS(process.GetPointerUnsafe() != GetCurrentProcessPointer(), svc::ResultBusy());
|
||||
|
||||
/* Set the activity. */
|
||||
R_TRY(process->SetActivity(process_activity));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result SetThreadActivity64(ams::svc::Handle thread_handle, ams::svc::ThreadActivity thread_activity) {
|
||||
R_RETURN(SetThreadActivity(thread_handle, thread_activity));
|
||||
}
|
||||
|
||||
Result SetProcessActivity64(ams::svc::Handle process_handle, ams::svc::ProcessActivity process_activity) {
|
||||
R_RETURN(SetProcessActivity(process_handle, process_activity));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result SetThreadActivity64From32(ams::svc::Handle thread_handle, ams::svc::ThreadActivity thread_activity) {
|
||||
R_RETURN(SetThreadActivity(thread_handle, thread_activity));
|
||||
}
|
||||
|
||||
Result SetProcessActivity64From32(ams::svc::Handle process_handle, ams::svc::ProcessActivity process_activity) {
|
||||
R_RETURN(SetProcessActivity(process_handle, process_activity));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsKernelAddress(uintptr_t address) {
|
||||
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
|
||||
}
|
||||
|
||||
constexpr bool IsValidSignalType(ams::svc::SignalType type) {
|
||||
switch (type) {
|
||||
case ams::svc::SignalType_Signal:
|
||||
case ams::svc::SignalType_SignalAndIncrementIfEqual:
|
||||
case ams::svc::SignalType_SignalAndModifyByWaitingCountIfEqual:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsValidArbitrationType(ams::svc::ArbitrationType type) {
|
||||
switch (type) {
|
||||
case ams::svc::ArbitrationType_WaitIfLessThan:
|
||||
case ams::svc::ArbitrationType_DecrementAndWaitIfLessThan:
|
||||
case ams::svc::ArbitrationType_WaitIfEqual:
|
||||
case ams::svc::ArbitrationType_WaitIfEqual64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result WaitForAddress(uintptr_t address, ams::svc::ArbitrationType arb_type, int64_t value, int64_t timeout_ns) {
|
||||
/* Validate input. */
|
||||
R_UNLESS(AMS_LIKELY(!IsKernelAddress(address)), svc::ResultInvalidCurrentMemory());
|
||||
if (arb_type == ams::svc::ArbitrationType_WaitIfEqual64) {
|
||||
R_UNLESS(util::IsAligned(address, sizeof(int64_t)), svc::ResultInvalidAddress());
|
||||
} else {
|
||||
R_UNLESS(util::IsAligned(address, sizeof(int32_t)), svc::ResultInvalidAddress());
|
||||
}
|
||||
R_UNLESS(IsValidArbitrationType(arb_type), svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Convert timeout from nanoseconds to ticks. */
|
||||
s64 timeout;
|
||||
if (timeout_ns > 0) {
|
||||
const ams::svc::Tick offset_tick(TimeSpan::FromNanoSeconds(timeout_ns));
|
||||
if (AMS_LIKELY(offset_tick > 0)) {
|
||||
timeout = KHardwareTimer::GetTick() + offset_tick + 2;
|
||||
if (AMS_UNLIKELY(timeout <= 0)) {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
|
||||
R_RETURN(GetCurrentProcess().WaitAddressArbiter(address, arb_type, value, timeout));
|
||||
}
|
||||
|
||||
Result SignalToAddress(uintptr_t address, ams::svc::SignalType signal_type, int32_t value, int32_t count) {
|
||||
/* Validate input. */
|
||||
R_UNLESS(AMS_LIKELY(!IsKernelAddress(address)), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(util::IsAligned(address, sizeof(int32_t)), svc::ResultInvalidAddress());
|
||||
R_UNLESS(IsValidSignalType(signal_type), svc::ResultInvalidEnumValue());
|
||||
|
||||
R_RETURN(GetCurrentProcess().SignalAddressArbiter(address, signal_type, value, count));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result WaitForAddress64(ams::svc::Address address, ams::svc::ArbitrationType arb_type, int64_t value, int64_t timeout_ns) {
|
||||
R_RETURN(WaitForAddress(address, arb_type, value, timeout_ns));
|
||||
}
|
||||
|
||||
Result SignalToAddress64(ams::svc::Address address, ams::svc::SignalType signal_type, int32_t value, int32_t count) {
|
||||
R_RETURN(SignalToAddress(address, signal_type, value, count));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result WaitForAddress64From32(ams::svc::Address address, ams::svc::ArbitrationType arb_type, int64_t value, int64_t timeout_ns) {
|
||||
R_RETURN(WaitForAddress(address, arb_type, value, timeout_ns));
|
||||
}
|
||||
|
||||
Result SignalToAddress64From32(ams::svc::Address address, ams::svc::SignalType signal_type, int32_t value, int32_t count) {
|
||||
R_RETURN(SignalToAddress(address, signal_type, value, count));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
Result QueryPhysicalAddress(ams::svc::PhysicalMemoryInfo *out_info, uintptr_t address) {
|
||||
/* NOTE: In 10.0.0, Nintendo stubbed this SVC. Should we do so? */
|
||||
/* R_UNLESS(GetTargetFirmware() < TargetFirmware_10_0_0, svc::ResultInvalidCurrentMemory()); */
|
||||
|
||||
/* Get reference to page table. */
|
||||
auto &pt = GetCurrentProcess().GetPageTable();
|
||||
|
||||
/* Check that the address is valid. */
|
||||
R_UNLESS(pt.Contains(address, 1), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Query the physical mapping. */
|
||||
R_TRY(pt.QueryPhysicalAddress(out_info, address));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result QueryMemoryMapping(uintptr_t *out_address, size_t *out_size, uint64_t phys_addr, size_t size) {
|
||||
/* Declare variables we'll populate. */
|
||||
KProcessAddress found_address = Null<KProcessAddress>;
|
||||
size_t found_size = 0;
|
||||
|
||||
/* Get reference to page table. */
|
||||
auto &pt = GetCurrentProcess().GetPageTable();
|
||||
|
||||
/* Check whether the address is aligned. */
|
||||
const bool aligned = util::IsAligned(phys_addr, PageSize);
|
||||
|
||||
auto QueryMappingFromPageTable = [&](uint64_t phys_addr, size_t size) ALWAYS_INLINE_LAMBDA -> Result {
|
||||
/* The size must be non-zero. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
|
||||
/* The request must not overflow. */
|
||||
R_UNLESS((phys_addr < phys_addr + size), svc::ResultNotFound());
|
||||
|
||||
/* Query the mapping. */
|
||||
R_TRY_CATCH(pt.QueryIoMapping(std::addressof(found_address), phys_addr, size)) {
|
||||
R_CATCH(svc::ResultNotFound) {
|
||||
/* If we failed to find an io mapping, check if the address is a static mapping. */
|
||||
R_TRY(pt.QueryStaticMapping(std::addressof(found_address), phys_addr, size));
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Use the size as the found size. */
|
||||
found_size = size;
|
||||
|
||||
R_SUCCEED();
|
||||
};
|
||||
|
||||
if (aligned) {
|
||||
/* Query the input. */
|
||||
R_TRY(QueryMappingFromPageTable(phys_addr, size));
|
||||
} else {
|
||||
if (kern::GetTargetFirmware() < TargetFirmware_8_0_0 && phys_addr >= PageSize) {
|
||||
/* Query the aligned-down page. */
|
||||
const size_t offset = phys_addr & (PageSize - 1);
|
||||
R_TRY(QueryMappingFromPageTable(phys_addr - offset, size + offset));
|
||||
|
||||
/* Adjust the output address. */
|
||||
found_address += offset;
|
||||
} else {
|
||||
/* Newer kernel only allows unaligned addresses when they're special enum members. */
|
||||
R_UNLESS(phys_addr < PageSize, svc::ResultNotFound());
|
||||
|
||||
/* Try to find the memory region. */
|
||||
const KMemoryRegion * const region = [] ALWAYS_INLINE_LAMBDA (ams::svc::MemoryRegionType type) -> const KMemoryRegion * {
|
||||
switch (type) {
|
||||
case ams::svc::MemoryRegionType_KernelTraceBuffer: return KMemoryLayout::GetPhysicalKernelTraceBufferRegion();
|
||||
case ams::svc::MemoryRegionType_OnMemoryBootImage: return KMemoryLayout::GetPhysicalOnMemoryBootImageRegion();
|
||||
case ams::svc::MemoryRegionType_DTB: return KMemoryLayout::GetPhysicalDTBRegion();
|
||||
default: return nullptr;
|
||||
}
|
||||
}(static_cast<ams::svc::MemoryRegionType>(phys_addr));
|
||||
|
||||
/* Ensure that we found the region. */
|
||||
R_UNLESS(region != nullptr, svc::ResultNotFound());
|
||||
|
||||
/* Chcek that the region is valid. */
|
||||
MESOSPHERE_ABORT_UNLESS(region->GetEndAddress() != 0);
|
||||
|
||||
R_TRY(pt.QueryStaticMapping(std::addressof(found_address), region->GetAddress(), region->GetSize()));
|
||||
found_size = region->GetSize();
|
||||
}
|
||||
}
|
||||
|
||||
/* We succeeded. */
|
||||
MESOSPHERE_ASSERT(found_address != Null<KProcessAddress>);
|
||||
MESOSPHERE_ASSERT(found_size != 0);
|
||||
if (out_address != nullptr) {
|
||||
*out_address = GetInteger(found_address);
|
||||
}
|
||||
if (out_size != nullptr) {
|
||||
*out_size = found_size;
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result QueryPhysicalAddress64(ams::svc::lp64::PhysicalMemoryInfo *out_info, ams::svc::Address address) {
|
||||
R_RETURN(QueryPhysicalAddress(out_info, address));
|
||||
}
|
||||
|
||||
Result QueryMemoryMapping64(ams::svc::Address *out_address, ams::svc::Size *out_size, ams::svc::PhysicalAddress physical_address, ams::svc::Size size) {
|
||||
static_assert(sizeof(*out_address) == sizeof(uintptr_t));
|
||||
static_assert(sizeof(*out_size) == sizeof(size_t));
|
||||
R_RETURN(QueryMemoryMapping(reinterpret_cast<uintptr_t *>(out_address), reinterpret_cast<size_t *>(out_size), physical_address, size));
|
||||
}
|
||||
|
||||
Result LegacyQueryIoMapping64(ams::svc::Address *out_address, ams::svc::PhysicalAddress physical_address, ams::svc::Size size) {
|
||||
static_assert(sizeof(*out_address) == sizeof(uintptr_t));
|
||||
R_RETURN(QueryMemoryMapping(reinterpret_cast<uintptr_t *>(out_address), nullptr, physical_address, size));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result QueryPhysicalAddress64From32(ams::svc::ilp32::PhysicalMemoryInfo *out_info, ams::svc::Address address) {
|
||||
ams::svc::PhysicalMemoryInfo info = {};
|
||||
R_TRY(QueryPhysicalAddress(std::addressof(info), address));
|
||||
|
||||
*out_info = {
|
||||
.physical_address = info.physical_address,
|
||||
.virtual_address = static_cast<u32>(info.virtual_address),
|
||||
.size = static_cast<u32>(info.size),
|
||||
};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result QueryMemoryMapping64From32(ams::svc::Address *out_address, ams::svc::Size *out_size, ams::svc::PhysicalAddress physical_address, ams::svc::Size size) {
|
||||
static_assert(sizeof(*out_address) == sizeof(uintptr_t));
|
||||
static_assert(sizeof(*out_size) == sizeof(size_t));
|
||||
R_RETURN(QueryMemoryMapping(reinterpret_cast<uintptr_t *>(out_address), reinterpret_cast<size_t *>(out_size), physical_address, size));
|
||||
}
|
||||
|
||||
Result LegacyQueryIoMapping64From32(ams::svc::Address *out_address, ams::svc::PhysicalAddress physical_address, ams::svc::Size size) {
|
||||
static_assert(sizeof(*out_address) == sizeof(uintptr_t));
|
||||
R_RETURN(QueryMemoryMapping(reinterpret_cast<uintptr_t *>(out_address), nullptr, physical_address, size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
class CacheOperation {
|
||||
public:
|
||||
virtual void Operate(void *address, size_t size) const = 0;
|
||||
};
|
||||
|
||||
Result DoProcessCacheOperation(const CacheOperation &operation, KProcessPageTable &page_table, uintptr_t address, size_t size) {
|
||||
/* Determine aligned extents. */
|
||||
const uintptr_t aligned_start = util::AlignDown(address, PageSize);
|
||||
const uintptr_t aligned_end = util::AlignUp(address + size, PageSize);
|
||||
|
||||
/* Iterate over and operate on contiguous ranges. */
|
||||
uintptr_t cur_address = aligned_start;
|
||||
size_t remaining = size;
|
||||
while (remaining > 0) {
|
||||
/* Get a contiguous range to operate on. */
|
||||
KPageTableBase::MemoryRange contig_range;
|
||||
R_TRY(page_table.OpenMemoryRangeForProcessCacheOperation(std::addressof(contig_range), cur_address, aligned_end - cur_address));
|
||||
|
||||
/* Close the range when we're done operating on it. */
|
||||
ON_SCOPE_EXIT { contig_range.Close(); };
|
||||
|
||||
/* Adjust to remain within range. */
|
||||
KVirtualAddress operate_address = KMemoryLayout::GetLinearVirtualAddress(contig_range.GetAddress());
|
||||
size_t operate_size = contig_range.GetSize();
|
||||
if (cur_address < address) {
|
||||
operate_address += (address - cur_address);
|
||||
operate_size -= (address - cur_address);
|
||||
}
|
||||
if (operate_size > remaining) {
|
||||
operate_size = remaining;
|
||||
}
|
||||
|
||||
/* Operate. */
|
||||
operation.Operate(GetVoidPointer(operate_address), operate_size);
|
||||
|
||||
/* Advance. */
|
||||
cur_address += contig_range.GetSize();
|
||||
remaining -= operate_size;
|
||||
}
|
||||
MESOSPHERE_ASSERT(remaining == 0);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void FlushEntireDataCache() {
|
||||
/* Flushing cache takes up to 1ms, so determine our minimum end tick. */
|
||||
const s64 timeout = KHardwareTimer::GetTick() + ams::svc::Tick(TimeSpan::FromMilliSeconds(1));
|
||||
|
||||
/* Flush the entire data cache. */
|
||||
cpu::FlushEntireDataCache();
|
||||
|
||||
/* Wait for 1ms to have passed. */
|
||||
while (KHardwareTimer::GetTick() < timeout) {
|
||||
cpu::Yield();
|
||||
}
|
||||
}
|
||||
|
||||
Result FlushDataCache(uintptr_t address, size_t size) {
|
||||
/* Succeed if there's nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Validate that the region is within range. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Flush the cache. */
|
||||
R_TRY(cpu::FlushDataCache(reinterpret_cast<void *>(address), size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result InvalidateProcessDataCache(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
||||
/* Validate address/size. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(address == static_cast<uintptr_t>(address), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(size == static_cast<size_t>(size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the process from its handle. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Invalidate the cache. */
|
||||
if (process.GetPointerUnsafe() == GetCurrentProcessPointer()) {
|
||||
R_TRY(process->GetPageTable().InvalidateCurrentProcessDataCache(address, size));
|
||||
} else {
|
||||
R_TRY(process->GetPageTable().InvalidateProcessDataCache(address, size));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result StoreProcessDataCache(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
||||
/* Validate address/size. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(address == static_cast<uintptr_t>(address), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(size == static_cast<size_t>(size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the process from its handle. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify the region is within range. */
|
||||
auto &page_table = process->GetPageTable();
|
||||
R_UNLESS(page_table.Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Perform the operation. */
|
||||
if (process.GetPointerUnsafe() == GetCurrentProcessPointer()) {
|
||||
R_RETURN(cpu::StoreDataCache(reinterpret_cast<void *>(address), size));
|
||||
} else {
|
||||
class StoreCacheOperation : public CacheOperation {
|
||||
public:
|
||||
virtual void Operate(void *address, size_t size) const override { cpu::StoreDataCache(address, size); }
|
||||
} operation;
|
||||
|
||||
R_RETURN(DoProcessCacheOperation(operation, page_table, address, size));
|
||||
}
|
||||
}
|
||||
|
||||
Result FlushProcessDataCache(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
||||
/* Validate address/size. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(address == static_cast<uintptr_t>(address), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(size == static_cast<size_t>(size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the process from its handle. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify the region is within range. */
|
||||
auto &page_table = process->GetPageTable();
|
||||
R_UNLESS(page_table.Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Perform the operation. */
|
||||
if (process.GetPointerUnsafe() == GetCurrentProcessPointer()) {
|
||||
R_RETURN(cpu::FlushDataCache(reinterpret_cast<void *>(address), size));
|
||||
} else {
|
||||
class FlushCacheOperation : public CacheOperation {
|
||||
public:
|
||||
virtual void Operate(void *address, size_t size) const override { cpu::FlushDataCache(address, size); }
|
||||
} operation;
|
||||
|
||||
R_RETURN(DoProcessCacheOperation(operation, page_table, address, size));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
void FlushEntireDataCache64() {
|
||||
return FlushEntireDataCache();
|
||||
}
|
||||
|
||||
Result FlushDataCache64(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(FlushDataCache(address, size));
|
||||
}
|
||||
|
||||
Result InvalidateProcessDataCache64(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
||||
R_RETURN(InvalidateProcessDataCache(process_handle, address, size));
|
||||
}
|
||||
|
||||
Result StoreProcessDataCache64(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
||||
R_RETURN(StoreProcessDataCache(process_handle, address, size));
|
||||
}
|
||||
|
||||
Result FlushProcessDataCache64(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
||||
R_RETURN(FlushProcessDataCache(process_handle, address, size));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
void FlushEntireDataCache64From32() {
|
||||
return FlushEntireDataCache();
|
||||
}
|
||||
|
||||
Result FlushDataCache64From32(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(FlushDataCache(address, size));
|
||||
}
|
||||
|
||||
Result InvalidateProcessDataCache64From32(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
||||
R_RETURN(InvalidateProcessDataCache(process_handle, address, size));
|
||||
}
|
||||
|
||||
Result StoreProcessDataCache64From32(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
||||
R_RETURN(StoreProcessDataCache(process_handle, address, size));
|
||||
}
|
||||
|
||||
Result FlushProcessDataCache64From32(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
||||
R_RETURN(FlushProcessDataCache(process_handle, address, size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidMapCodeMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||
return perm == ams::svc::MemoryPermission_ReadWrite;
|
||||
}
|
||||
|
||||
constexpr bool IsValidMapToOwnerCodeMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||
return perm == ams::svc::MemoryPermission_Read || perm == ams::svc::MemoryPermission_ReadExecute;
|
||||
}
|
||||
|
||||
constexpr bool IsValidUnmapCodeMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||
return perm == ams::svc::MemoryPermission_None;
|
||||
}
|
||||
|
||||
constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||
return perm == ams::svc::MemoryPermission_None;
|
||||
}
|
||||
|
||||
Result CreateCodeMemory(ams::svc::Handle *out, uintptr_t address, size_t size) {
|
||||
/* Validate address / size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Create the code memory. */
|
||||
KCodeMemory *code_mem = KCodeMemory::Create();
|
||||
R_UNLESS(code_mem != nullptr, svc::ResultOutOfResource());
|
||||
ON_SCOPE_EXIT { code_mem->Close(); };
|
||||
|
||||
/* Verify that the region is in range. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Initialize the code memory. */
|
||||
R_TRY(code_mem->Initialize(address, size));
|
||||
|
||||
/* Register the code memory. */
|
||||
KCodeMemory::Register(code_mem);
|
||||
|
||||
/* Add the code memory to the handle table. */
|
||||
R_TRY(GetCurrentProcess().GetHandleTable().Add(out, code_mem));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ControlCodeMemory(ams::svc::Handle code_memory_handle, ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) {
|
||||
/* Validate the address / size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(address == static_cast<uintptr_t>(address), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(size == static_cast<size_t>(size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the code memory from its handle. */
|
||||
KScopedAutoObject code_mem = GetCurrentProcess().GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
|
||||
R_UNLESS(code_mem.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. */
|
||||
/* This enables homebrew usage of these SVCs for JIT. */
|
||||
/* R_UNLESS(code_mem->GetOwner() != GetCurrentProcessPointer(), svc::ResultInvalidHandle()); */
|
||||
|
||||
/* Perform the operation. */
|
||||
switch (operation) {
|
||||
case ams::svc::CodeMemoryOperation_Map:
|
||||
{
|
||||
/* Check that the region is in range. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_CodeOut), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Check the memory permission. */
|
||||
R_UNLESS(IsValidMapCodeMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission());
|
||||
|
||||
/* Map the memory. */
|
||||
R_TRY(code_mem->Map(address, size));
|
||||
}
|
||||
break;
|
||||
case ams::svc::CodeMemoryOperation_Unmap:
|
||||
{
|
||||
/* Check that the region is in range. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_CodeOut), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Check the memory permission. */
|
||||
R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission());
|
||||
|
||||
/* Unmap the memory. */
|
||||
R_TRY(code_mem->Unmap(address, size));
|
||||
}
|
||||
break;
|
||||
case ams::svc::CodeMemoryOperation_MapToOwner:
|
||||
{
|
||||
/* Check that the region is in range. */
|
||||
R_UNLESS(code_mem->GetOwner()->GetPageTable().CanContain(address, size, KMemoryState_GeneratedCode), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Check the memory permission. */
|
||||
R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission());
|
||||
|
||||
/* Map the memory to its owner. */
|
||||
R_TRY(code_mem->MapToOwner(address, size, perm));
|
||||
}
|
||||
break;
|
||||
case ams::svc::CodeMemoryOperation_UnmapFromOwner:
|
||||
{
|
||||
/* Check that the region is in range. */
|
||||
R_UNLESS(code_mem->GetOwner()->GetPageTable().CanContain(address, size, KMemoryState_GeneratedCode), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Check the memory permission. */
|
||||
R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission());
|
||||
|
||||
/* Unmap the memory from its owner. */
|
||||
R_TRY(code_mem->UnmapFromOwner(address, size));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
R_THROW(svc::ResultInvalidEnumValue());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result CreateCodeMemory64(ams::svc::Handle *out_handle, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(CreateCodeMemory(out_handle, address, size));
|
||||
}
|
||||
|
||||
Result ControlCodeMemory64(ams::svc::Handle code_memory_handle, ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) {
|
||||
R_RETURN(ControlCodeMemory(code_memory_handle, operation, address, size, perm));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result CreateCodeMemory64From32(ams::svc::Handle *out_handle, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(CreateCodeMemory(out_handle, address, size));
|
||||
}
|
||||
|
||||
Result ControlCodeMemory64From32(ams::svc::Handle code_memory_handle, ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) {
|
||||
R_RETURN(ControlCodeMemory(code_memory_handle, operation, address, size, perm));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsKernelAddress(uintptr_t address) {
|
||||
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
|
||||
}
|
||||
|
||||
Result WaitProcessWideKeyAtomic(uintptr_t address, uintptr_t cv_key, uint32_t tag, int64_t timeout_ns) {
|
||||
/* Validate input. */
|
||||
R_UNLESS(AMS_LIKELY(!IsKernelAddress(address)), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(util::IsAligned(address, sizeof(int32_t)), svc::ResultInvalidAddress());
|
||||
|
||||
/* Convert timeout from nanoseconds to ticks. */
|
||||
s64 timeout;
|
||||
if (timeout_ns > 0) {
|
||||
const ams::svc::Tick offset_tick(TimeSpan::FromNanoSeconds(timeout_ns));
|
||||
if (AMS_LIKELY(offset_tick > 0)) {
|
||||
timeout = KHardwareTimer::GetTick() + offset_tick + 2;
|
||||
if (AMS_UNLIKELY(timeout <= 0)) {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
|
||||
/* Wait on the condition variable. */
|
||||
R_RETURN(GetCurrentProcess().WaitConditionVariable(address, util::AlignDown(cv_key, sizeof(u32)), tag, timeout));
|
||||
}
|
||||
|
||||
void SignalProcessWideKey(uintptr_t cv_key, int32_t count) {
|
||||
/* Signal the condition variable. */
|
||||
return GetCurrentProcess().SignalConditionVariable(util::AlignDown(cv_key, sizeof(u32)), count);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result WaitProcessWideKeyAtomic64(ams::svc::Address address, ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) {
|
||||
R_RETURN(WaitProcessWideKeyAtomic(address, cv_key, tag, timeout_ns));
|
||||
}
|
||||
|
||||
void SignalProcessWideKey64(ams::svc::Address cv_key, int32_t count) {
|
||||
return SignalProcessWideKey(cv_key, count);
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result WaitProcessWideKeyAtomic64From32(ams::svc::Address address, ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) {
|
||||
R_RETURN(WaitProcessWideKeyAtomic(address, cv_key, tag, timeout_ns));
|
||||
}
|
||||
|
||||
void SignalProcessWideKey64From32(ams::svc::Address cv_key, int32_t count) {
|
||||
return SignalProcessWideKey(cv_key, count);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline int32_t MaximumDebuggableThreadCount = 0x60;
|
||||
|
||||
Result DebugActiveProcess(ams::svc::Handle *out_handle, uint64_t process_id) {
|
||||
/* Check that the SVC can be used. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
|
||||
|
||||
/* Get the process from its id. */
|
||||
KProcess *process = KProcess::GetProcessFromId(process_id);
|
||||
R_UNLESS(process != nullptr, svc::ResultInvalidProcessId());
|
||||
|
||||
/* Close the reference we opened to the process on scope exit. */
|
||||
ON_SCOPE_EXIT { process->Close(); };
|
||||
|
||||
/* Check that the debugging is allowed. */
|
||||
const bool allowable = process->IsPermittedDebug() || GetCurrentProcess().CanForceDebug() || GetCurrentProcess().CanForceDebugProd();
|
||||
R_UNLESS(allowable, svc::ResultInvalidState());
|
||||
|
||||
/* Disallow debugging one's own processs, to prevent softlocks. */
|
||||
R_UNLESS(process != GetCurrentProcessPointer(), svc::ResultInvalidState());
|
||||
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Create a new debug object. */
|
||||
KDebug *debug = KDebug::Create();
|
||||
R_UNLESS(debug != nullptr, svc::ResultOutOfResource());
|
||||
ON_SCOPE_EXIT { debug->Close(); };
|
||||
|
||||
/* Initialize the debug object. */
|
||||
debug->Initialize();
|
||||
|
||||
/* Register the debug object. */
|
||||
KDebug::Register(debug);
|
||||
|
||||
/* Try to attach to the target process. */
|
||||
R_TRY(debug->Attach(process));
|
||||
|
||||
/* Add the new debug object to the handle table. */
|
||||
R_TRY(handle_table.Add(out_handle, debug));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BreakDebugProcess(ams::svc::Handle debug_handle) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Break the process. */
|
||||
R_TRY(debug->BreakProcess());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result TerminateDebugProcess(ams::svc::Handle debug_handle) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Terminate the process. */
|
||||
R_TRY(debug->TerminateProcess());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename EventInfoType>
|
||||
Result GetDebugEvent(KUserPointer<EventInfoType *> out_info, ams::svc::Handle debug_handle) {
|
||||
/* Only allow invoking the svc on development hardware or if force debug prod. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
|
||||
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Create and clear a new event info. */
|
||||
EventInfoType info;
|
||||
std::memset(std::addressof(info), 0, sizeof(info));
|
||||
|
||||
/* Get the next info from the debug object. */
|
||||
R_TRY(debug->GetDebugEventInfo(std::addressof(info)));
|
||||
|
||||
/* Copy the info out to the user. */
|
||||
R_TRY(out_info.CopyFrom(std::addressof(info)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContinueDebugEventImpl(ams::svc::Handle debug_handle, uint32_t flags, const uint64_t *thread_ids, int32_t num_thread_ids) {
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Continue the event. */
|
||||
R_TRY(debug->ContinueDebug(flags, thread_ids, num_thread_ids));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContinueDebugEvent(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer<const uint64_t *> user_thread_ids, int32_t num_thread_ids) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||
|
||||
/* Verify that the flags are valid. */
|
||||
R_UNLESS((flags | ams::svc::ContinueFlag_AllMask) == ams::svc::ContinueFlag_AllMask, svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Verify that continue all and continue others flags are exclusive. */
|
||||
constexpr u32 AllAndOthersMask = ams::svc::ContinueFlag_ContinueAll | ams::svc::ContinueFlag_ContinueOthers;
|
||||
R_UNLESS((flags & AllAndOthersMask) != AllAndOthersMask, svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Verify that the number of thread ids is valid. */
|
||||
R_UNLESS((0 <= num_thread_ids && num_thread_ids <= MaximumDebuggableThreadCount), svc::ResultOutOfRange());
|
||||
|
||||
/* Copy the threads from userspace. */
|
||||
uint64_t thread_ids[MaximumDebuggableThreadCount];
|
||||
if (num_thread_ids > 0) {
|
||||
R_TRY(user_thread_ids.CopyArrayTo(thread_ids, num_thread_ids));
|
||||
}
|
||||
|
||||
/* Continue the event. */
|
||||
R_TRY(ContinueDebugEventImpl(debug_handle, flags, thread_ids, num_thread_ids));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LegacyContinueDebugEvent(ams::svc::Handle debug_handle, uint32_t flags, uint64_t thread_id) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||
|
||||
/* Verify that the flags are valid. */
|
||||
R_UNLESS((flags | ams::svc::ContinueFlag_AllMask) == ams::svc::ContinueFlag_AllMask, svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Verify that continue all and continue others flags are exclusive. */
|
||||
constexpr u32 AllAndOthersMask = ams::svc::ContinueFlag_ContinueAll | ams::svc::ContinueFlag_ContinueOthers;
|
||||
R_UNLESS((flags & AllAndOthersMask) != AllAndOthersMask, svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Continue the event. */
|
||||
R_TRY(ContinueDebugEventImpl(debug_handle, flags, std::addressof(thread_id), 1));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetDebugThreadContext(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) {
|
||||
/* Only allow invoking the svc on development hardware or if force debug prod. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
|
||||
|
||||
/* Validate the context flags. */
|
||||
R_UNLESS((context_flags | ams::svc::ThreadContextFlag_All) == ams::svc::ThreadContextFlag_All, svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the thread context. */
|
||||
ams::svc::ThreadContext context = {};
|
||||
R_TRY(debug->GetThreadContext(std::addressof(context), thread_id, context_flags));
|
||||
|
||||
/* Copy the context to userspace. */
|
||||
R_TRY(out_context.CopyFrom(std::addressof(context)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetDebugThreadContext(ams::svc::Handle debug_handle, uint64_t thread_id, KUserPointer<const ams::svc::ThreadContext *> user_context, uint32_t context_flags) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||
|
||||
/* Validate the context flags. */
|
||||
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
|
||||
{
|
||||
/* Check that the flags are a subset of the allowable. */
|
||||
constexpr u32 AllFlagsMask = ams::svc::ThreadContextFlag_All | ams::svc::ThreadContextFlag_SetSingleStep | ams::svc::ThreadContextFlag_ClearSingleStep;
|
||||
R_UNLESS((context_flags | AllFlagsMask) == AllFlagsMask, svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Check that thread isn't both setting and clearing single step. */
|
||||
const bool set_ss = (context_flags & ams::svc::ThreadContextFlag_SetSingleStep) != 0;
|
||||
const bool clear_ss = (context_flags & ams::svc::ThreadContextFlag_ClearSingleStep) != 0;
|
||||
|
||||
R_UNLESS(!(set_ss && clear_ss), svc::ResultInvalidEnumValue());
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* Check that the flags are a subset of the allowable. */
|
||||
R_UNLESS((context_flags | ams::svc::ThreadContextFlag_All) == ams::svc::ThreadContextFlag_All, svc::ResultInvalidEnumValue());
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Copy the thread context from userspace. */
|
||||
ams::svc::ThreadContext context;
|
||||
R_TRY(user_context.CopyTo(std::addressof(context)));
|
||||
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Set the thread context. */
|
||||
R_TRY(debug->SetThreadContext(context, thread_id, context_flags));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result QueryDebugProcessMemory(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle debug_handle, uintptr_t address) {
|
||||
/* Only allow invoking the svc on development hardware or if force debug prod. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
|
||||
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Query the mapping's info. */
|
||||
R_TRY(debug->QueryMemoryInfo(out_memory_info, out_page_info, address));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result QueryDebugProcessMemory(KUserPointer<T *> out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle debug_handle, uint64_t address) {
|
||||
/* Get an ams::svc::MemoryInfo for the region. */
|
||||
ams::svc::MemoryInfo info = {};
|
||||
R_TRY(QueryDebugProcessMemory(std::addressof(info), out_page_info, debug_handle, address));
|
||||
|
||||
/* Copy the info to userspace. */
|
||||
if constexpr (std::same_as<T, ams::svc::MemoryInfo>) {
|
||||
R_TRY(out_memory_info.CopyFrom(std::addressof(info)));
|
||||
} else {
|
||||
/* Convert the info. */
|
||||
T converted_info = {};
|
||||
static_assert(std::same_as<decltype(T{}.base_address), decltype(ams::svc::MemoryInfo{}.base_address)>);
|
||||
static_assert(std::same_as<decltype(T{}.size), decltype(ams::svc::MemoryInfo{}.size)>);
|
||||
|
||||
converted_info.base_address = info.base_address;
|
||||
converted_info.size = info.size;
|
||||
converted_info.state = info.state;
|
||||
converted_info.attribute = info.attribute;
|
||||
converted_info.permission = info.permission;
|
||||
converted_info.ipc_count = info.ipc_count;
|
||||
converted_info.device_count = info.device_count;
|
||||
|
||||
/* Copy it. */
|
||||
R_TRY(out_memory_info.CopyFrom(std::addressof(converted_info)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ReadDebugProcessMemory(uintptr_t buffer, ams::svc::Handle debug_handle, uintptr_t address, size_t size) {
|
||||
/* Only allow invoking the svc on development hardware or if force debug prod. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
|
||||
|
||||
/* Validate address / size. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS((buffer < buffer + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Read the memory. */
|
||||
R_TRY(debug->ReadMemory(buffer, address, size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result WriteDebugProcessMemory(ams::svc::Handle debug_handle, uintptr_t buffer, uintptr_t address, size_t size) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||
|
||||
/* Validate address / size. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS((buffer < buffer + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Write the memory. */
|
||||
R_TRY(debug->WriteMemory(buffer, address, size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetHardwareBreakPoint(ams::svc::HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||
|
||||
/* Set the breakpoint. */
|
||||
R_TRY(KDebug::SetHardwareBreakPoint(name, flags, value));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetDebugThreadParam(uint64_t *out_64, uint32_t *out_32, ams::svc::Handle debug_handle, uint64_t thread_id, ams::svc::DebugThreadParam param) {
|
||||
/* Only allow invoking the svc on development hardware or if force debug prod. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode() || GetCurrentProcess().CanForceDebugProd(), svc::ResultNotImplemented());
|
||||
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the thread from its id. */
|
||||
KThread *thread = KThread::GetThreadFromId(thread_id);
|
||||
R_UNLESS(thread != nullptr, svc::ResultInvalidThreadId());
|
||||
ON_SCOPE_EXIT { thread->Close(); };
|
||||
|
||||
/* Get the process from the debug object. */
|
||||
R_UNLESS(debug->IsAttached(), svc::ResultProcessTerminated());
|
||||
R_UNLESS(debug->OpenProcess(), svc::ResultProcessTerminated());
|
||||
|
||||
/* Close the process when we're done. */
|
||||
ON_SCOPE_EXIT { debug->CloseProcess(); };
|
||||
|
||||
/* Get the proces. */
|
||||
KProcess * const process = debug->GetProcessUnsafe();
|
||||
|
||||
/* Verify that the process is the thread's parent. */
|
||||
R_UNLESS(process == thread->GetOwnerProcess(), svc::ResultInvalidThreadId());
|
||||
|
||||
/* Get the parameter. */
|
||||
switch (param) {
|
||||
case ams::svc::DebugThreadParam_Priority:
|
||||
{
|
||||
/* Get the priority. */
|
||||
*out_32 = thread->GetPriority();
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugThreadParam_State:
|
||||
{
|
||||
/* Get the thread state and suspend status. */
|
||||
KThread::ThreadState state;
|
||||
bool suspended_user;
|
||||
bool suspended_debug;
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
state = thread->GetState();
|
||||
suspended_user = thread->IsSuspendRequested(KThread::SuspendType_Thread);
|
||||
suspended_debug = thread->IsSuspendRequested(KThread::SuspendType_Debug);
|
||||
}
|
||||
|
||||
/* Set the suspend flags. */
|
||||
*out_32 = 0;
|
||||
if (suspended_user) {
|
||||
*out_32 |= ams::svc::ThreadSuspend_User;
|
||||
}
|
||||
if (suspended_debug) {
|
||||
*out_32 |= ams::svc::ThreadSuspend_Debug;
|
||||
}
|
||||
|
||||
/* Set the state. */
|
||||
switch (state) {
|
||||
case KThread::ThreadState_Initialized:
|
||||
{
|
||||
*out_64 = ams::svc::ThreadState_Initializing;
|
||||
}
|
||||
break;
|
||||
case KThread::ThreadState_Waiting:
|
||||
{
|
||||
*out_64 = ams::svc::ThreadState_Waiting;
|
||||
}
|
||||
break;
|
||||
case KThread::ThreadState_Runnable:
|
||||
{
|
||||
*out_64 = ams::svc::ThreadState_Running;
|
||||
}
|
||||
break;
|
||||
case KThread::ThreadState_Terminated:
|
||||
{
|
||||
*out_64 = ams::svc::ThreadState_Terminated;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
R_THROW(svc::ResultInvalidState());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugThreadParam_IdealCore:
|
||||
{
|
||||
/* Get the ideal core. */
|
||||
s32 core_id;
|
||||
u64 affinity_mask;
|
||||
thread->GetPhysicalCoreMask(std::addressof(core_id), std::addressof(affinity_mask));
|
||||
|
||||
*out_32 = core_id;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugThreadParam_CurrentCore:
|
||||
{
|
||||
/* Get the current core. */
|
||||
*out_32 = thread->GetActiveCore();
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugThreadParam_AffinityMask:
|
||||
{
|
||||
/* Get the affinity mask. */
|
||||
s32 core_id;
|
||||
u64 affinity_mask;
|
||||
thread->GetPhysicalCoreMask(std::addressof(core_id), std::addressof(affinity_mask));
|
||||
|
||||
*out_32 = affinity_mask;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
R_THROW(ams::svc::ResultInvalidEnumValue());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result DebugActiveProcess64(ams::svc::Handle *out_handle, uint64_t process_id) {
|
||||
R_RETURN(DebugActiveProcess(out_handle, process_id));
|
||||
}
|
||||
|
||||
Result BreakDebugProcess64(ams::svc::Handle debug_handle) {
|
||||
R_RETURN(BreakDebugProcess(debug_handle));
|
||||
}
|
||||
|
||||
Result TerminateDebugProcess64(ams::svc::Handle debug_handle) {
|
||||
R_RETURN(TerminateDebugProcess(debug_handle));
|
||||
}
|
||||
|
||||
Result GetDebugEvent64(KUserPointer<ams::svc::lp64::DebugEventInfo *> out_info, ams::svc::Handle debug_handle) {
|
||||
R_RETURN(GetDebugEvent(out_info, debug_handle));
|
||||
}
|
||||
|
||||
Result ContinueDebugEvent64(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer<const uint64_t *> thread_ids, int32_t num_thread_ids) {
|
||||
R_RETURN(ContinueDebugEvent(debug_handle, flags, thread_ids, num_thread_ids));
|
||||
}
|
||||
|
||||
Result LegacyContinueDebugEvent64(ams::svc::Handle debug_handle, uint32_t flags, uint64_t thread_id) {
|
||||
R_RETURN(LegacyContinueDebugEvent(debug_handle, flags, thread_id));
|
||||
}
|
||||
|
||||
Result GetDebugThreadContext64(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) {
|
||||
R_RETURN(GetDebugThreadContext(out_context, debug_handle, thread_id, context_flags));
|
||||
}
|
||||
|
||||
Result SetDebugThreadContext64(ams::svc::Handle debug_handle, uint64_t thread_id, KUserPointer<const ams::svc::ThreadContext *> context, uint32_t context_flags) {
|
||||
R_RETURN(SetDebugThreadContext(debug_handle, thread_id, context, context_flags));
|
||||
}
|
||||
|
||||
Result QueryDebugProcessMemory64(KUserPointer<ams::svc::lp64::MemoryInfo *> out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle debug_handle, ams::svc::Address address) {
|
||||
R_RETURN(QueryDebugProcessMemory(out_memory_info, out_page_info, debug_handle, address));
|
||||
}
|
||||
|
||||
Result ReadDebugProcessMemory64(ams::svc::Address buffer, ams::svc::Handle debug_handle, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(ReadDebugProcessMemory(buffer, debug_handle, address, size));
|
||||
}
|
||||
|
||||
Result WriteDebugProcessMemory64(ams::svc::Handle debug_handle, ams::svc::Address buffer, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(WriteDebugProcessMemory(debug_handle, buffer, address, size));
|
||||
}
|
||||
|
||||
Result SetHardwareBreakPoint64(ams::svc::HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value) {
|
||||
R_RETURN(SetHardwareBreakPoint(name, flags, value));
|
||||
}
|
||||
|
||||
Result GetDebugThreadParam64(uint64_t *out_64, uint32_t *out_32, ams::svc::Handle debug_handle, uint64_t thread_id, ams::svc::DebugThreadParam param) {
|
||||
R_RETURN(GetDebugThreadParam(out_64, out_32, debug_handle, thread_id, param));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result DebugActiveProcess64From32(ams::svc::Handle *out_handle, uint64_t process_id) {
|
||||
R_RETURN(DebugActiveProcess(out_handle, process_id));
|
||||
}
|
||||
|
||||
Result BreakDebugProcess64From32(ams::svc::Handle debug_handle) {
|
||||
R_RETURN(BreakDebugProcess(debug_handle));
|
||||
}
|
||||
|
||||
Result TerminateDebugProcess64From32(ams::svc::Handle debug_handle) {
|
||||
R_RETURN(TerminateDebugProcess(debug_handle));
|
||||
}
|
||||
|
||||
Result GetDebugEvent64From32(KUserPointer<ams::svc::ilp32::DebugEventInfo *> out_info, ams::svc::Handle debug_handle) {
|
||||
R_RETURN(GetDebugEvent(out_info, debug_handle));
|
||||
}
|
||||
|
||||
Result ContinueDebugEvent64From32(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer<const uint64_t *> thread_ids, int32_t num_thread_ids) {
|
||||
R_RETURN(ContinueDebugEvent(debug_handle, flags, thread_ids, num_thread_ids));
|
||||
}
|
||||
|
||||
Result LegacyContinueDebugEvent64From32(ams::svc::Handle debug_handle, uint32_t flags, uint64_t thread_id) {
|
||||
R_RETURN(LegacyContinueDebugEvent(debug_handle, flags, thread_id));
|
||||
}
|
||||
|
||||
Result GetDebugThreadContext64From32(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) {
|
||||
R_RETURN(GetDebugThreadContext(out_context, debug_handle, thread_id, context_flags));
|
||||
}
|
||||
|
||||
Result SetDebugThreadContext64From32(ams::svc::Handle debug_handle, uint64_t thread_id, KUserPointer<const ams::svc::ThreadContext *> context, uint32_t context_flags) {
|
||||
R_RETURN(SetDebugThreadContext(debug_handle, thread_id, context, context_flags));
|
||||
}
|
||||
|
||||
Result QueryDebugProcessMemory64From32(KUserPointer<ams::svc::ilp32::MemoryInfo *> out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle debug_handle, ams::svc::Address address) {
|
||||
R_RETURN(QueryDebugProcessMemory(out_memory_info, out_page_info, debug_handle, address));
|
||||
}
|
||||
|
||||
Result ReadDebugProcessMemory64From32(ams::svc::Address buffer, ams::svc::Handle debug_handle, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(ReadDebugProcessMemory(buffer, debug_handle, address, size));
|
||||
}
|
||||
|
||||
Result WriteDebugProcessMemory64From32(ams::svc::Handle debug_handle, ams::svc::Address buffer, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(WriteDebugProcessMemory(debug_handle, buffer, address, size));
|
||||
}
|
||||
|
||||
Result SetHardwareBreakPoint64From32(ams::svc::HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value) {
|
||||
R_RETURN(SetHardwareBreakPoint(name, flags, value));
|
||||
}
|
||||
|
||||
Result GetDebugThreadParam64From32(uint64_t *out_64, uint32_t *out_32, ams::svc::Handle debug_handle, uint64_t thread_id, ams::svc::DebugThreadParam param) {
|
||||
R_RETURN(GetDebugThreadParam(out_64, out_32, debug_handle, thread_id, param));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
Result OutputDebugString(KUserPointer<const char *> debug_str, size_t len) {
|
||||
/* Succeed immediately if there's nothing to output. */
|
||||
R_SUCCEED_IF(len == 0);
|
||||
|
||||
/* Ensure that the data being output is in range. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(KProcessAddress(debug_str.GetUnsafePointer()), len), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Output the string. */
|
||||
R_RETURN(KDebugLog::PrintUserString(debug_str, len));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result OutputDebugString64(KUserPointer<const char *> debug_str, ams::svc::Size len) {
|
||||
R_RETURN(OutputDebugString(debug_str, len));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result OutputDebugString64From32(KUserPointer<const char *> debug_str, ams::svc::Size len) {
|
||||
R_RETURN(OutputDebugString(debug_str, len));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u64 DeviceAddressSpaceAlignMask = (1ul << 22) - 1;
|
||||
|
||||
constexpr bool IsProcessAndDeviceAligned(uint64_t process_address, uint64_t device_address) {
|
||||
return (process_address & DeviceAddressSpaceAlignMask) == (device_address & DeviceAddressSpaceAlignMask);
|
||||
}
|
||||
|
||||
Result CreateDeviceAddressSpace(ams::svc::Handle *out, uint64_t das_address, uint64_t das_size) {
|
||||
/* Validate input. */
|
||||
R_UNLESS(util::IsAligned(das_address, PageSize), svc::ResultInvalidMemoryRegion());
|
||||
R_UNLESS(util::IsAligned(das_size, PageSize), svc::ResultInvalidMemoryRegion());
|
||||
R_UNLESS(das_size > 0, svc::ResultInvalidMemoryRegion());
|
||||
R_UNLESS((das_address < das_address + das_size), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Create the device address space. */
|
||||
KDeviceAddressSpace *das = KDeviceAddressSpace::Create();
|
||||
R_UNLESS(das != nullptr, svc::ResultOutOfResource());
|
||||
ON_SCOPE_EXIT { das->Close(); };
|
||||
|
||||
/* Initialize the device address space. */
|
||||
R_TRY(das->Initialize(das_address, das_size));
|
||||
|
||||
/* Register the device address space. */
|
||||
KDeviceAddressSpace::Register(das);
|
||||
|
||||
/* Add to the handle table. */
|
||||
R_TRY(GetCurrentProcess().GetHandleTable().Add(out, das));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result AttachDeviceAddressSpace(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
|
||||
/* Get the device address space. */
|
||||
KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
|
||||
R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Attach. */
|
||||
R_RETURN(das->Attach(device_name));
|
||||
}
|
||||
|
||||
Result DetachDeviceAddressSpace(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
|
||||
/* Get the device address space. */
|
||||
KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
|
||||
R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Detach. */
|
||||
R_RETURN(das->Detach(device_name));
|
||||
}
|
||||
|
||||
constexpr bool IsValidDeviceMemoryPermission(ams::svc::MemoryPermission device_perm) {
|
||||
switch (device_perm) {
|
||||
case ams::svc::MemoryPermission_Read:
|
||||
case ams::svc::MemoryPermission_Write:
|
||||
case ams::svc::MemoryPermission_ReadWrite:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result MapDeviceAddressSpaceByForce(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, size_t size, uint64_t device_address, u32 option) {
|
||||
/* Decode the option. */
|
||||
const util::BitPack32 option_pack = { option };
|
||||
const auto device_perm = option_pack.Get<ams::svc::MapDeviceAddressSpaceOption::Permission>();
|
||||
const auto reserved = option_pack.Get<ams::svc::MapDeviceAddressSpaceOption::Reserved>();
|
||||
|
||||
/* Validate input. */
|
||||
R_UNLESS(util::IsAligned(process_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(device_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((process_address < process_address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS((device_address < device_address + size), svc::ResultInvalidMemoryRegion());
|
||||
R_UNLESS((process_address == static_cast<uintptr_t>(process_address)), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(IsValidDeviceMemoryPermission(device_perm), svc::ResultInvalidNewMemoryPermission());
|
||||
R_UNLESS(reserved == 0, svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Get the device address space. */
|
||||
KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
|
||||
R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the process. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Validate that the process address is within range. */
|
||||
auto &page_table = process->GetPageTable();
|
||||
R_UNLESS(page_table.Contains(process_address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Map. */
|
||||
R_RETURN(das->MapByForce(std::addressof(page_table), KProcessAddress(process_address), size, device_address, option));
|
||||
}
|
||||
|
||||
Result MapDeviceAddressSpaceAligned(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, size_t size, uint64_t device_address, u32 option) {
|
||||
/* Decode the option. */
|
||||
const util::BitPack32 option_pack = { option };
|
||||
const auto device_perm = option_pack.Get<ams::svc::MapDeviceAddressSpaceOption::Permission>();
|
||||
const auto reserved = option_pack.Get<ams::svc::MapDeviceAddressSpaceOption::Reserved>();
|
||||
|
||||
/* Validate input. */
|
||||
R_UNLESS(util::IsAligned(process_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(device_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(IsProcessAndDeviceAligned(process_address, device_address), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((process_address < process_address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS((device_address < device_address + size), svc::ResultInvalidMemoryRegion());
|
||||
R_UNLESS((process_address == static_cast<uintptr_t>(process_address)), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(IsValidDeviceMemoryPermission(device_perm), svc::ResultInvalidNewMemoryPermission());
|
||||
R_UNLESS(reserved == 0, svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Get the device address space. */
|
||||
KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
|
||||
R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the process. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Validate that the process address is within range. */
|
||||
auto &page_table = process->GetPageTable();
|
||||
R_UNLESS(page_table.Contains(process_address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Map. */
|
||||
R_RETURN(das->MapAligned(std::addressof(page_table), KProcessAddress(process_address), size, device_address, option));
|
||||
}
|
||||
|
||||
Result UnmapDeviceAddressSpace(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, size_t size, uint64_t device_address) {
|
||||
/* Validate input. */
|
||||
R_UNLESS(util::IsAligned(process_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(device_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((process_address < process_address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS((device_address < device_address + size), svc::ResultInvalidMemoryRegion());
|
||||
R_UNLESS((process_address == static_cast<uintptr_t>(process_address)), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the device address space. */
|
||||
KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
|
||||
R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the process. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Validate that the process address is within range. */
|
||||
auto &page_table = process->GetPageTable();
|
||||
R_UNLESS(page_table.Contains(process_address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
R_RETURN(das->Unmap(std::addressof(page_table), KProcessAddress(process_address), size, device_address));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result CreateDeviceAddressSpace64(ams::svc::Handle *out_handle, uint64_t das_address, uint64_t das_size) {
|
||||
R_RETURN(CreateDeviceAddressSpace(out_handle, das_address, das_size));
|
||||
}
|
||||
|
||||
Result AttachDeviceAddressSpace64(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
|
||||
R_RETURN(AttachDeviceAddressSpace(device_name, das_handle));
|
||||
}
|
||||
|
||||
Result DetachDeviceAddressSpace64(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
|
||||
R_RETURN(DetachDeviceAddressSpace(device_name, das_handle));
|
||||
}
|
||||
|
||||
Result MapDeviceAddressSpaceByForce64(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, u32 option) {
|
||||
R_RETURN(MapDeviceAddressSpaceByForce(das_handle, process_handle, process_address, size, device_address, option));
|
||||
}
|
||||
|
||||
Result MapDeviceAddressSpaceAligned64(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, u32 option) {
|
||||
R_RETURN(MapDeviceAddressSpaceAligned(das_handle, process_handle, process_address, size, device_address, option));
|
||||
}
|
||||
|
||||
Result UnmapDeviceAddressSpace64(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address) {
|
||||
R_RETURN(UnmapDeviceAddressSpace(das_handle, process_handle, process_address, size, device_address));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result CreateDeviceAddressSpace64From32(ams::svc::Handle *out_handle, uint64_t das_address, uint64_t das_size) {
|
||||
R_RETURN(CreateDeviceAddressSpace(out_handle, das_address, das_size));
|
||||
}
|
||||
|
||||
Result AttachDeviceAddressSpace64From32(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
|
||||
R_RETURN(AttachDeviceAddressSpace(device_name, das_handle));
|
||||
}
|
||||
|
||||
Result DetachDeviceAddressSpace64From32(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
|
||||
R_RETURN(DetachDeviceAddressSpace(device_name, das_handle));
|
||||
}
|
||||
|
||||
Result MapDeviceAddressSpaceByForce64From32(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, u32 option) {
|
||||
R_RETURN(MapDeviceAddressSpaceByForce(das_handle, process_handle, process_address, size, device_address, option));
|
||||
}
|
||||
|
||||
Result MapDeviceAddressSpaceAligned64From32(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, u32 option) {
|
||||
R_RETURN(MapDeviceAddressSpaceAligned(das_handle, process_handle, process_address, size, device_address, option));
|
||||
}
|
||||
|
||||
Result UnmapDeviceAddressSpace64From32(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address) {
|
||||
R_RETURN(UnmapDeviceAddressSpace(das_handle, process_handle, process_address, size, device_address));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
Result SignalEvent(ams::svc::Handle event_handle) {
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Get the writable event. */
|
||||
KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
|
||||
R_UNLESS(event.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
R_RETURN(event->Signal());
|
||||
}
|
||||
|
||||
Result ClearEvent(ams::svc::Handle event_handle) {
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Try to clear the writable event. */
|
||||
{
|
||||
KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
|
||||
if (event.IsNotNull()) {
|
||||
R_RETURN(event->Clear());
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to clear the readable event. */
|
||||
{
|
||||
KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle);
|
||||
if (readable_event.IsNotNull()) {
|
||||
if (auto * const interrupt_event = readable_event->DynamicCast<KInterruptEvent *>(); interrupt_event != nullptr) {
|
||||
R_RETURN(interrupt_event->Clear());
|
||||
} else {
|
||||
R_RETURN(readable_event->Clear());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
R_THROW(svc::ResultInvalidHandle());
|
||||
}
|
||||
|
||||
Result CreateEvent(ams::svc::Handle *out_write, ams::svc::Handle *out_read) {
|
||||
/* Get the current process and handle table. */
|
||||
auto &process = GetCurrentProcess();
|
||||
auto &handle_table = process.GetHandleTable();
|
||||
|
||||
/* Declare the event we're going to allocate. */
|
||||
KEvent *event;
|
||||
|
||||
/* Reserve a new event from the process resource limit. */
|
||||
KScopedResourceReservation event_reservation(std::addressof(process), ams::svc::LimitableResource_EventCountMax);
|
||||
if (event_reservation.Succeeded()) {
|
||||
/* Allocate an event normally. */
|
||||
event = KEvent::Create();
|
||||
} else {
|
||||
/* We couldn't reserve an event. Check that we support dynamically expanding the resource limit. */
|
||||
R_UNLESS(process.GetResourceLimit() == std::addressof(Kernel::GetSystemResourceLimit()), svc::ResultLimitReached());
|
||||
R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), svc::ResultLimitReached());
|
||||
|
||||
/* Try to allocate an event from unused slab memory. */
|
||||
event = KEvent::CreateFromUnusedSlabMemory();
|
||||
R_UNLESS(event != nullptr, svc::ResultLimitReached());
|
||||
|
||||
/* We successfully allocated an event, so add the object we allocated to the resource limit. */
|
||||
Kernel::GetSystemResourceLimit().Add(ams::svc::LimitableResource_EventCountMax, 1);
|
||||
}
|
||||
|
||||
/* Check that we successfully created an event. */
|
||||
R_UNLESS(event != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
/* Initialize the event. */
|
||||
event->Initialize();
|
||||
|
||||
/* Commit the event reservation. */
|
||||
event_reservation.Commit();
|
||||
|
||||
/* Ensure that we clean up the event (and its only references are handle table) on function end. */
|
||||
ON_SCOPE_EXIT {
|
||||
event->GetReadableEvent().Close();
|
||||
event->Close();
|
||||
};
|
||||
|
||||
/* Register the event. */
|
||||
KEvent::Register(event);
|
||||
|
||||
/* Add the event to the handle table. */
|
||||
R_TRY(handle_table.Add(out_write, event));
|
||||
|
||||
/* Ensure that we maintaing a clean handle state on exit. */
|
||||
ON_RESULT_FAILURE { handle_table.Remove(*out_write); };
|
||||
|
||||
/* Add the readable event to the handle table. */
|
||||
R_RETURN(handle_table.Add(out_read, std::addressof(event->GetReadableEvent())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result SignalEvent64(ams::svc::Handle event_handle) {
|
||||
R_RETURN(SignalEvent(event_handle));
|
||||
}
|
||||
|
||||
Result ClearEvent64(ams::svc::Handle event_handle) {
|
||||
R_RETURN(ClearEvent(event_handle));
|
||||
}
|
||||
|
||||
Result CreateEvent64(ams::svc::Handle *out_write_handle, ams::svc::Handle *out_read_handle) {
|
||||
R_RETURN(CreateEvent(out_write_handle, out_read_handle));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result SignalEvent64From32(ams::svc::Handle event_handle) {
|
||||
R_RETURN(SignalEvent(event_handle));
|
||||
}
|
||||
|
||||
Result ClearEvent64From32(ams::svc::Handle event_handle) {
|
||||
R_RETURN(ClearEvent(event_handle));
|
||||
}
|
||||
|
||||
Result CreateEvent64From32(ams::svc::Handle *out_write_handle, ams::svc::Handle *out_read_handle) {
|
||||
R_RETURN(CreateEvent(out_write_handle, out_read_handle));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
|
||||
void PrintBreak(ams::svc::BreakReason break_reason) {
|
||||
/* Print that break was called. */
|
||||
MESOSPHERE_RELEASE_LOG("%s: svc::Break(%d) was called, pid=%ld, tid=%ld\n", GetCurrentProcess().GetName(), static_cast<s32>(break_reason), GetCurrentProcess().GetId(), GetCurrentThread().GetId());
|
||||
|
||||
/* Print the current thread's registers. */
|
||||
KDebug::PrintRegister();
|
||||
|
||||
/* Print a backtrace. */
|
||||
KDebug::PrintBacktrace();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Break(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) {
|
||||
/* Determine whether the break is only a notification. */
|
||||
const bool is_notification = (break_reason & ams::svc::BreakReason_NotificationOnlyFlag) != 0;
|
||||
|
||||
/* If the break isn't a notification, print it. */
|
||||
if (!is_notification) {
|
||||
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
|
||||
PrintBreak(break_reason);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* If the current process is attached to debugger, try to notify it. */
|
||||
if (GetCurrentProcess().IsAttachedToDebugger()) {
|
||||
if (R_SUCCEEDED(KDebug::BreakIfAttached(break_reason, address, size))) {
|
||||
/* If we attached, set the pc to the instruction before the current one and return. */
|
||||
KDebug::SetPreviousProgramCounter();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the break is only a notification, we're done. */
|
||||
if (is_notification) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Print that break was called. */
|
||||
MESOSPHERE_EXCEPTION_LOG("Break() called. ");
|
||||
|
||||
/* Try to enter JIT debug state. */
|
||||
if (GetCurrentProcess().EnterJitDebug(ams::svc::DebugEvent_Exception, ams::svc::DebugException_UserBreak, KDebug::GetProgramCounter(GetCurrentThread()), break_reason, address, size)) {
|
||||
/* We entered JIT debug, so set the pc to the instruction before the current one and return. */
|
||||
KDebug::SetPreviousProgramCounter();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Exit the current process. */
|
||||
GetCurrentProcess().Exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
void Break64(ams::svc::BreakReason break_reason, ams::svc::Address arg, ams::svc::Size size) {
|
||||
return Break(break_reason, arg, size);
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
void Break64From32(ams::svc::BreakReason break_reason, ams::svc::Address arg, ams::svc::Size size) {
|
||||
return Break(break_reason, arg, size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
Result GetInitialProcessIdRange(u64 *out, ams::svc::InitialProcessIdRangeInfo info) {
|
||||
switch (info) {
|
||||
case ams::svc::InitialProcessIdRangeInfo_Minimum:
|
||||
MESOSPHERE_ABORT_UNLESS(GetInitialProcessIdMin() <= GetInitialProcessIdMax());
|
||||
*out = GetInitialProcessIdMin();
|
||||
break;
|
||||
case ams::svc::InitialProcessIdRangeInfo_Maximum:
|
||||
MESOSPHERE_ABORT_UNLESS(GetInitialProcessIdMin() <= GetInitialProcessIdMax());
|
||||
*out = GetInitialProcessIdMax();
|
||||
break;
|
||||
default:
|
||||
R_THROW(svc::ResultInvalidCombination());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetInfoImpl(u64 *out, ams::svc::InfoType info_type, KProcess *process) {
|
||||
switch (info_type) {
|
||||
case ams::svc::InfoType_CoreMask:
|
||||
*out = process->GetCoreMask();
|
||||
break;
|
||||
case ams::svc::InfoType_PriorityMask:
|
||||
*out = process->GetPriorityMask();
|
||||
break;
|
||||
case ams::svc::InfoType_AliasRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetAliasRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_AliasRegionSize:
|
||||
*out = process->GetPageTable().GetAliasRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_HeapRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetHeapRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_HeapRegionSize:
|
||||
*out = process->GetPageTable().GetHeapRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_TotalMemorySize:
|
||||
*out = process->GetTotalUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_UsedMemorySize:
|
||||
*out = process->GetUsedUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_AslrRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_AslrRegionSize:
|
||||
*out = process->GetPageTable().GetAliasCodeRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_StackRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetStackRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_StackRegionSize:
|
||||
*out = process->GetPageTable().GetStackRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_SystemResourceSizeTotal:
|
||||
*out = process->GetTotalSystemResourceSize();
|
||||
break;
|
||||
case ams::svc::InfoType_SystemResourceSizeUsed:
|
||||
*out = process->GetUsedSystemResourceSize();
|
||||
break;
|
||||
case ams::svc::InfoType_ProgramId:
|
||||
*out = process->GetProgramId();
|
||||
break;
|
||||
case ams::svc::InfoType_UserExceptionContextAddress:
|
||||
*out = GetInteger(process->GetProcessLocalRegionAddress());
|
||||
break;
|
||||
case ams::svc::InfoType_TotalNonSystemMemorySize:
|
||||
*out = process->GetTotalNonSystemUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_UsedNonSystemMemorySize:
|
||||
*out = process->GetUsedNonSystemUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_IsApplication:
|
||||
*out = process->IsApplication();
|
||||
break;
|
||||
case ams::svc::InfoType_FreeThreadCount:
|
||||
if (KResourceLimit *resource_limit = process->GetResourceLimit(); resource_limit != nullptr) {
|
||||
const auto current_value = resource_limit->GetCurrentValue(ams::svc::LimitableResource_ThreadCountMax);
|
||||
const auto limit_value = resource_limit->GetLimitValue(ams::svc::LimitableResource_ThreadCountMax);
|
||||
*out = limit_value - current_value;
|
||||
} else {
|
||||
*out = 0;
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_AliasRegionExtraSize:
|
||||
*out = process->GetPageTable().GetAliasRegionExtraSize();
|
||||
break;
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetInfo(u64 *out, ams::svc::InfoType info_type, ams::svc::Handle handle, u64 info_subtype) {
|
||||
switch (info_type) {
|
||||
case ams::svc::InfoType_CoreMask:
|
||||
case ams::svc::InfoType_PriorityMask:
|
||||
case ams::svc::InfoType_AliasRegionAddress:
|
||||
case ams::svc::InfoType_AliasRegionSize:
|
||||
case ams::svc::InfoType_HeapRegionAddress:
|
||||
case ams::svc::InfoType_HeapRegionSize:
|
||||
case ams::svc::InfoType_TotalMemorySize:
|
||||
case ams::svc::InfoType_UsedMemorySize:
|
||||
case ams::svc::InfoType_AslrRegionAddress:
|
||||
case ams::svc::InfoType_AslrRegionSize:
|
||||
case ams::svc::InfoType_StackRegionAddress:
|
||||
case ams::svc::InfoType_StackRegionSize:
|
||||
case ams::svc::InfoType_SystemResourceSizeTotal:
|
||||
case ams::svc::InfoType_SystemResourceSizeUsed:
|
||||
case ams::svc::InfoType_ProgramId:
|
||||
case ams::svc::InfoType_UserExceptionContextAddress:
|
||||
case ams::svc::InfoType_TotalNonSystemMemorySize:
|
||||
case ams::svc::InfoType_UsedNonSystemMemorySize:
|
||||
case ams::svc::InfoType_IsApplication:
|
||||
case ams::svc::InfoType_FreeThreadCount:
|
||||
case ams::svc::InfoType_AliasRegionExtraSize:
|
||||
{
|
||||
/* These info types don't support non-zero subtypes. */
|
||||
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get the process from its handle. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(handle);
|
||||
|
||||
#if defined(MESOSPHERE_ENABLE_GET_INFO_OF_DEBUG_PROCESS)
|
||||
/* If we the process is valid, use it. */
|
||||
if (process.IsNotNull()) {
|
||||
R_RETURN(GetInfoImpl(out, info_type, process.GetPointerUnsafe()));
|
||||
}
|
||||
|
||||
/* Otherwise, as a mesosphere extension check if we were passed a usable KDebug. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the process from the debug object. */
|
||||
/* TODO: ResultInvalidHandle()? */
|
||||
R_UNLESS(debug->IsAttached(), svc::ResultProcessTerminated());
|
||||
R_UNLESS(debug->OpenProcess(), svc::ResultProcessTerminated());
|
||||
|
||||
/* Close the process when we're done. */
|
||||
ON_SCOPE_EXIT { debug->CloseProcess(); };
|
||||
|
||||
/* Return the info. */
|
||||
R_RETURN(GetInfoImpl(out, info_type, debug->GetProcessUnsafe()));
|
||||
#else
|
||||
/* Verify that the process is valid. */
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Return the relevant info. */
|
||||
R_RETURN(GetInfoImpl(out, info_type, process.GetPointerUnsafe()));
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_DebuggerAttached:
|
||||
{
|
||||
/* Verify the input handle is invalid. */
|
||||
R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify the sub-type is valid. */
|
||||
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get whether debugger is attached. */
|
||||
*out = GetCurrentProcess().GetDebugObject() != nullptr;
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_ResourceLimit:
|
||||
{
|
||||
/* Verify the input handle is invalid. */
|
||||
R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify the sub-type is valid. */
|
||||
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get the handle table and resource limit. */
|
||||
KHandleTable &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
KResourceLimit *resource_limit = GetCurrentProcess().GetResourceLimit();
|
||||
|
||||
if (resource_limit != nullptr) {
|
||||
/* Get a new handle for the resource limit. */
|
||||
ams::svc::Handle tmp;
|
||||
R_TRY(handle_table.Add(std::addressof(tmp), resource_limit));
|
||||
|
||||
/* Set the output. */
|
||||
*out = tmp;
|
||||
} else {
|
||||
/* Set the output. */
|
||||
*out = ams::svc::InvalidHandle;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_IdleTickCount:
|
||||
{
|
||||
/* Verify the input handle is invalid. */
|
||||
R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
/* Disable dispatch while we get the tick count. */
|
||||
KScopedDisableDispatch dd;
|
||||
|
||||
/* Verify the requested core is valid. */
|
||||
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype == static_cast<u64>(GetCurrentCoreId()));
|
||||
R_UNLESS(core_valid, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get the idle tick count. */
|
||||
*out = Kernel::GetScheduler().GetIdleThread()->GetCpuTime() - Kernel::GetInterruptTaskManager().GetCpuTime();
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_RandomEntropy:
|
||||
{
|
||||
/* Verify the input handle is invalid. */
|
||||
R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify the requested entropy is valid. */
|
||||
R_UNLESS(info_subtype < 4, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get the entropy. */
|
||||
*out = GetCurrentProcess().GetRandomEntropy(info_subtype);
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_InitialProcessIdRange:
|
||||
{
|
||||
/* NOTE: This info type was added in 4.0.0, and removed in 5.0.0. */
|
||||
R_UNLESS(GetTargetFirmware() < TargetFirmware_5_0_0, svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Verify the input handle is invalid. */
|
||||
R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the process id range. */
|
||||
R_TRY(GetInitialProcessIdRange(out, static_cast<ams::svc::InitialProcessIdRangeInfo>(info_subtype)));
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_ThreadTickCount:
|
||||
{
|
||||
/* Verify the requested core is valid. */
|
||||
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype < cpu::NumVirtualCores);
|
||||
R_UNLESS(core_valid, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get the thread from its handle. */
|
||||
KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject<KThread>(handle);
|
||||
R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Disable dispatch while we get the tick count. */
|
||||
KScopedDisableDispatch dd;
|
||||
|
||||
/* Determine the tick count. */
|
||||
s64 tick_count;
|
||||
if (info_subtype == static_cast<u64>(-1ul)) {
|
||||
tick_count = thread->GetCpuTime();
|
||||
if (GetCurrentThreadPointer() == thread.GetPointerUnsafe()) {
|
||||
const s64 cur_tick = KHardwareTimer::GetTick();
|
||||
const s64 prev_switch = Kernel::GetScheduler().GetLastContextSwitchTime();
|
||||
tick_count += (cur_tick - prev_switch);
|
||||
}
|
||||
} else {
|
||||
const s32 phys_core = cpu::VirtualToPhysicalCoreMap[info_subtype];
|
||||
MESOSPHERE_ABORT_UNLESS(phys_core < static_cast<s32>(cpu::NumCores));
|
||||
|
||||
tick_count = thread->GetCpuTime(phys_core);
|
||||
if (GetCurrentThreadPointer() == thread.GetPointerUnsafe() && phys_core == GetCurrentCoreId()) {
|
||||
const s64 cur_tick = KHardwareTimer::GetTick();
|
||||
const s64 prev_switch = Kernel::GetScheduler().GetLastContextSwitchTime();
|
||||
tick_count += (cur_tick - prev_switch);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the output. */
|
||||
*out = tick_count;
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_IsSvcPermitted:
|
||||
{
|
||||
/* Verify the input handle is invalid. */
|
||||
R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify the sub-type is valid. */
|
||||
R_UNLESS(info_subtype == svc::SvcId_SynchronizePreemptionState, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get whether the svc is permitted. */
|
||||
*out = GetCurrentProcess().IsPermittedSvc(static_cast<svc::SvcId>(info_subtype));
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_IoRegionHint:
|
||||
{
|
||||
/* Verify the sub-type is valid. */
|
||||
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get the io region from its handle. */
|
||||
KScopedAutoObject io_region = GetCurrentProcess().GetHandleTable().GetObject<KIoRegion>(handle);
|
||||
R_UNLESS(io_region.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the io region's address hint. */
|
||||
*out = io_region->GetHint();
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_TransferMemoryHint:
|
||||
{
|
||||
/* Verify the sub-type is valid. */
|
||||
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get the transfer memory from its handle. */
|
||||
KScopedAutoObject transfer_memory = GetCurrentProcess().GetHandleTable().GetObject<KTransferMemory>(handle);
|
||||
R_UNLESS(transfer_memory.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the transfer memory's address hint. */
|
||||
*out = transfer_memory->GetHint();
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_MesosphereMeta:
|
||||
{
|
||||
/* Verify the handle is invalid. */
|
||||
R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
switch (static_cast<ams::svc::MesosphereMetaInfo>(info_subtype)) {
|
||||
case ams::svc::MesosphereMetaInfo_KernelVersion:
|
||||
{
|
||||
/* Return the supported kernel version. */
|
||||
*out = ams::svc::SupportedKernelVersion;
|
||||
}
|
||||
break;
|
||||
case ams::svc::MesosphereMetaInfo_IsKTraceEnabled:
|
||||
{
|
||||
/* Return whether the kernel supports tracing. */
|
||||
constexpr u64 KTraceValue = ams::kern::IsKTraceEnabled ? 1 : 0;
|
||||
*out = KTraceValue;
|
||||
}
|
||||
break;
|
||||
case ams::svc::MesosphereMetaInfo_IsSingleStepEnabled:
|
||||
{
|
||||
/* Return whether the kernel supports hardware single step. */
|
||||
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
|
||||
*out = 1;
|
||||
#else
|
||||
*out = 0;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
default:
|
||||
R_THROW(svc::ResultInvalidCombination());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_MesosphereCurrentProcess:
|
||||
{
|
||||
/* Verify the input handle is invalid. */
|
||||
R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify the sub-type is valid. */
|
||||
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get the handle table. */
|
||||
KHandleTable &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Get a new handle for the current process. */
|
||||
ams::svc::Handle tmp;
|
||||
R_TRY(handle_table.Add(std::addressof(tmp), GetCurrentProcessPointer()));
|
||||
|
||||
/* Set the output. */
|
||||
*out = tmp;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
/* For debug, log the invalid info call. */
|
||||
MESOSPHERE_LOG("GetInfo(%p, %u, %08x, %lu) was called\n", out, static_cast<u32>(info_type), static_cast<u32>(handle), info_subtype);
|
||||
}
|
||||
R_THROW(svc::ResultInvalidEnumValue());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
constexpr bool IsValidMemoryPool(u64 pool) {
|
||||
switch (static_cast<KMemoryManager::Pool>(pool)) {
|
||||
case KMemoryManager::Pool_Application:
|
||||
case KMemoryManager::Pool_Applet:
|
||||
case KMemoryManager::Pool_System:
|
||||
case KMemoryManager::Pool_SystemNonSecure:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result GetSystemInfo(u64 *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, u64 info_subtype) {
|
||||
switch (info_type) {
|
||||
case ams::svc::SystemInfoType_TotalPhysicalMemorySize:
|
||||
case ams::svc::SystemInfoType_UsedPhysicalMemorySize:
|
||||
{
|
||||
/* Verify the input handle is invalid. */
|
||||
R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify the sub-type is valid. */
|
||||
R_UNLESS(IsValidMemoryPool(info_subtype), svc::ResultInvalidCombination());
|
||||
|
||||
/* Convert to pool. */
|
||||
const auto pool = static_cast<KMemoryManager::Pool>(info_subtype);
|
||||
|
||||
/* Get the memory size. */
|
||||
auto &mm = Kernel::GetMemoryManager();
|
||||
switch (info_type) {
|
||||
case ams::svc::SystemInfoType_TotalPhysicalMemorySize:
|
||||
*out = mm.GetSize(pool);
|
||||
break;
|
||||
case ams::svc::SystemInfoType_UsedPhysicalMemorySize:
|
||||
*out = mm.GetSize(pool) - mm.GetFreeSize(pool);
|
||||
break;
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ams::svc::SystemInfoType_InitialProcessIdRange:
|
||||
{
|
||||
/* Verify the handle is invalid. */
|
||||
R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the process id range. */
|
||||
R_TRY(GetInitialProcessIdRange(out, static_cast<ams::svc::InitialProcessIdRangeInfo>(info_subtype)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
R_THROW(svc::ResultInvalidEnumValue());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result GetInfo64(uint64_t *out, ams::svc::InfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
||||
R_RETURN(GetInfo(out, info_type, handle, info_subtype));
|
||||
}
|
||||
|
||||
Result GetSystemInfo64(uint64_t *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
||||
R_RETURN(GetSystemInfo(out, info_type, handle, info_subtype));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result GetInfo64From32(uint64_t *out, ams::svc::InfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
||||
R_RETURN(GetInfo(out, info_type, handle, info_subtype));
|
||||
}
|
||||
|
||||
Result GetSystemInfo64From32(uint64_t *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
||||
R_RETURN(GetSystemInfo(out, info_type, handle, info_subtype));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
Result MapInsecurePhysicalMemory(uintptr_t address, size_t size) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Verify that the mapping is in range. */
|
||||
auto &pt = GetCurrentProcess().GetPageTable();
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_Insecure), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Map the insecure memory. */
|
||||
R_RETURN(pt.MapInsecurePhysicalMemory(address, size));
|
||||
}
|
||||
|
||||
Result UnmapInsecurePhysicalMemory(uintptr_t address, size_t size) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Verify that the mapping is in range. */
|
||||
auto &pt = GetCurrentProcess().GetPageTable();
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_Insecure), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Map the insecure memory. */
|
||||
R_RETURN(pt.UnmapInsecurePhysicalMemory(address, size));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result MapInsecurePhysicalMemory64(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(MapInsecurePhysicalMemory(address, size));
|
||||
}
|
||||
|
||||
Result UnmapInsecurePhysicalMemory64(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapInsecurePhysicalMemory(address, size));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result MapInsecurePhysicalMemory64From32(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(MapInsecurePhysicalMemory(address, size));
|
||||
}
|
||||
|
||||
Result UnmapInsecurePhysicalMemory64From32(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapInsecurePhysicalMemory(address, size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidInterruptType(ams::svc::InterruptType type) {
|
||||
switch (type) {
|
||||
case ams::svc::InterruptType_Edge:
|
||||
case ams::svc::InterruptType_Level:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result CreateInterruptEvent(ams::svc::Handle *out, int32_t interrupt_id, ams::svc::InterruptType type) {
|
||||
/* Validate the type. */
|
||||
R_UNLESS(IsValidInterruptType(type), svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Check whether the interrupt is allowed. */
|
||||
auto &process = GetCurrentProcess();
|
||||
R_UNLESS(process.IsPermittedInterrupt(interrupt_id), svc::ResultNotFound());
|
||||
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = process.GetHandleTable();
|
||||
|
||||
/* Create the interrupt event. */
|
||||
KInterruptEvent *event = KInterruptEvent::Create();
|
||||
R_UNLESS(event != nullptr, svc::ResultOutOfResource());
|
||||
ON_SCOPE_EXIT { event->Close(); };
|
||||
|
||||
/* Initialize the event. */
|
||||
R_TRY(event->Initialize(interrupt_id, type));
|
||||
|
||||
/* Register the event. */
|
||||
KInterruptEvent::Register(event);
|
||||
|
||||
/* Add the event to the handle table. */
|
||||
R_TRY(handle_table.Add(out, event));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result CreateInterruptEvent64(ams::svc::Handle *out_read_handle, int32_t interrupt_id, ams::svc::InterruptType interrupt_type) {
|
||||
R_RETURN(CreateInterruptEvent(out_read_handle, interrupt_id, interrupt_type));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result CreateInterruptEvent64From32(ams::svc::Handle *out_read_handle, int32_t interrupt_id, ams::svc::InterruptType interrupt_type) {
|
||||
R_RETURN(CreateInterruptEvent(out_read_handle, interrupt_id, interrupt_type));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(AMS_SVC_IO_POOL_NOT_SUPPORTED)
|
||||
constexpr bool IsIoPoolApiSupported = false;
|
||||
#else
|
||||
constexpr bool IsIoPoolApiSupported = true;
|
||||
#endif
|
||||
|
||||
[[maybe_unused]] constexpr bool IsValidIoRegionMapping(ams::svc::MemoryMapping mapping) {
|
||||
switch (mapping) {
|
||||
case ams::svc::MemoryMapping_IoRegister:
|
||||
case ams::svc::MemoryMapping_Uncached:
|
||||
case ams::svc::MemoryMapping_Memory:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]] constexpr bool IsValidIoRegionPermission(ams::svc::MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case ams::svc::MemoryPermission_Read:
|
||||
case ams::svc::MemoryPermission_ReadWrite:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result CreateIoPool(ams::svc::Handle *out, ams::svc::IoPoolType pool_type) {
|
||||
if constexpr (IsIoPoolApiSupported) {
|
||||
/* Validate that we're allowed to create a pool for the given type. */
|
||||
R_UNLESS(KIoPool::IsValidIoPoolType(pool_type), svc::ResultNotFound());
|
||||
|
||||
/* Create the io pool. */
|
||||
KIoPool *io_pool = KIoPool::Create();
|
||||
R_UNLESS(io_pool != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
/* Ensure the only reference is in the handle table when we're done. */
|
||||
ON_SCOPE_EXIT { io_pool->Close(); };
|
||||
|
||||
/* Initialize the io pool. */
|
||||
R_TRY(io_pool->Initialize(pool_type));
|
||||
|
||||
/* Register the io pool. */
|
||||
KIoPool::Register(io_pool);
|
||||
|
||||
/* Add the io pool to the handle table. */
|
||||
R_TRY(GetCurrentProcess().GetHandleTable().Add(out, io_pool));
|
||||
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
MESOSPHERE_UNUSED(out, pool_type);
|
||||
R_THROW(svc::ResultNotImplemented());
|
||||
}
|
||||
}
|
||||
|
||||
Result CreateIoRegion(ams::svc::Handle *out, ams::svc::Handle io_pool_handle, uint64_t phys_addr, size_t size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission perm) {
|
||||
if constexpr (IsIoPoolApiSupported) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(phys_addr, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS((phys_addr < phys_addr + size), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Validate the mapping/permissions. */
|
||||
R_UNLESS(IsValidIoRegionMapping(mapping), svc::ResultInvalidEnumValue());
|
||||
R_UNLESS(IsValidIoRegionPermission(perm), svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Get the io pool. */
|
||||
KScopedAutoObject io_pool = handle_table.GetObject<KIoPool>(io_pool_handle);
|
||||
R_UNLESS(io_pool.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Create the io region. */
|
||||
KIoRegion *io_region = KIoRegion::Create();
|
||||
R_UNLESS(io_region != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
/* Ensure the only reference is in the handle table when we're done. */
|
||||
ON_SCOPE_EXIT { io_region->Close(); };
|
||||
|
||||
/* Initialize the io region. */
|
||||
R_TRY(io_region->Initialize(io_pool.GetPointerUnsafe(), phys_addr, size, mapping, perm));
|
||||
|
||||
/* Register the io region. */
|
||||
KIoRegion::Register(io_region);
|
||||
|
||||
/* Add the io region to the handle table. */
|
||||
R_TRY(handle_table.Add(out, io_region));
|
||||
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
MESOSPHERE_UNUSED(out, io_pool_handle, phys_addr, size, mapping, perm);
|
||||
R_THROW(svc::ResultNotImplemented());
|
||||
}
|
||||
}
|
||||
|
||||
Result MapIoRegion(ams::svc::Handle io_region_handle, uintptr_t address, size_t size, ams::svc::MemoryPermission map_perm) {
|
||||
if constexpr (IsIoPoolApiSupported) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Verify that the mapping is in range. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, ams::svc::MemoryState_Io), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Validate the map permission. */
|
||||
R_UNLESS(IsValidIoRegionPermission(map_perm), svc::ResultInvalidNewMemoryPermission());
|
||||
|
||||
/* Get the io region. */
|
||||
KScopedAutoObject io_region = GetCurrentProcess().GetHandleTable().GetObject<KIoRegion>(io_region_handle);
|
||||
R_UNLESS(io_region.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Map the io region. */
|
||||
R_TRY(io_region->Map(address, size, map_perm));
|
||||
|
||||
/* We succeeded. */
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
MESOSPHERE_UNUSED(io_region_handle, address, size, map_perm);
|
||||
R_THROW(svc::ResultNotImplemented());
|
||||
}
|
||||
}
|
||||
|
||||
Result UnmapIoRegion(ams::svc::Handle io_region_handle, uintptr_t address, size_t size) {
|
||||
if constexpr (IsIoPoolApiSupported) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Verify that the mapping is in range. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, ams::svc::MemoryState_Io), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Get the io region. */
|
||||
KScopedAutoObject io_region = GetCurrentProcess().GetHandleTable().GetObject<KIoRegion>(io_region_handle);
|
||||
R_UNLESS(io_region.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Unmap the io region. */
|
||||
R_TRY(io_region->Unmap(address, size));
|
||||
|
||||
/* We succeeded. */
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
MESOSPHERE_UNUSED(io_region_handle, address, size);
|
||||
R_THROW(svc::ResultNotImplemented());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result CreateIoPool64(ams::svc::Handle *out_handle, ams::svc::IoPoolType pool_type) {
|
||||
R_RETURN(CreateIoPool(out_handle, pool_type));
|
||||
}
|
||||
|
||||
Result CreateIoRegion64(ams::svc::Handle *out_handle, ams::svc::Handle io_pool, ams::svc::PhysicalAddress physical_address, ams::svc::Size size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission perm) {
|
||||
R_RETURN(CreateIoRegion(out_handle, io_pool, physical_address, size, mapping, perm));
|
||||
}
|
||||
|
||||
Result MapIoRegion64(ams::svc::Handle io_region, ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission perm) {
|
||||
R_RETURN(MapIoRegion(io_region, address, size, perm));
|
||||
}
|
||||
|
||||
Result UnmapIoRegion64(ams::svc::Handle io_region, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapIoRegion(io_region, address, size));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result CreateIoPool64From32(ams::svc::Handle *out_handle, ams::svc::IoPoolType pool_type) {
|
||||
R_RETURN(CreateIoPool(out_handle, pool_type));
|
||||
}
|
||||
|
||||
Result CreateIoRegion64From32(ams::svc::Handle *out_handle, ams::svc::Handle io_pool, ams::svc::PhysicalAddress physical_address, ams::svc::Size size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission perm) {
|
||||
R_RETURN(CreateIoRegion(out_handle, io_pool, physical_address, size, mapping, perm));
|
||||
}
|
||||
|
||||
Result MapIoRegion64From32(ams::svc::Handle io_region, ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission perm) {
|
||||
R_RETURN(MapIoRegion(io_region, address, size, perm));
|
||||
}
|
||||
|
||||
Result UnmapIoRegion64From32(ams::svc::Handle io_region, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapIoRegion(io_region, address, size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O3")
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE Result SendSyncRequestImpl(uintptr_t message, size_t buffer_size, ams::svc::Handle session_handle) {
|
||||
/* Get the client session. */
|
||||
KScopedAutoObject session = GetCurrentProcess().GetHandleTable().GetObject<KClientSession>(session_handle);
|
||||
R_UNLESS(session.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the parent, and persist a reference to it until we're done. */
|
||||
KScopedAutoObject parent = session->GetParent();
|
||||
MESOSPHERE_ASSERT(parent.IsNotNull());
|
||||
|
||||
/* Send the request. */
|
||||
R_RETURN(session->SendSyncRequest(message, buffer_size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ReplyAndReceiveImpl(int32_t *out_index, uintptr_t message, size_t buffer_size, KPhysicalAddress message_paddr, KSynchronizationObject **objs, int32_t num_objects, ams::svc::Handle reply_target, int64_t timeout_ns) {
|
||||
/* Reply to the target, if one is specified. */
|
||||
if (reply_target != ams::svc::InvalidHandle) {
|
||||
KScopedAutoObject session = GetCurrentProcess().GetHandleTable().GetObject<KServerSession>(reply_target);
|
||||
R_UNLESS(session.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* If we fail to reply, we want to set the output index to -1. */
|
||||
ON_RESULT_FAILURE { *out_index = -1; };
|
||||
|
||||
/* Send the reply. */
|
||||
R_TRY(session->SendReply(message, buffer_size, message_paddr));
|
||||
}
|
||||
|
||||
/* Receive a message. */
|
||||
{
|
||||
/* Convert the timeout from nanoseconds to ticks. */
|
||||
/* NOTE: Nintendo does not use this conversion logic in WaitSynchronization... */
|
||||
s64 timeout;
|
||||
if (timeout_ns > 0) {
|
||||
const ams::svc::Tick offset_tick(TimeSpan::FromNanoSeconds(timeout_ns));
|
||||
if (AMS_LIKELY(offset_tick > 0)) {
|
||||
timeout = KHardwareTimer::GetTick() + offset_tick + 2;
|
||||
if (AMS_UNLIKELY(timeout <= 0)) {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
|
||||
/* Wait for a message. */
|
||||
while (true) {
|
||||
/* Close any pending objects before we wait. */
|
||||
GetCurrentThread().DestroyClosedObjects();
|
||||
|
||||
/* Wait for an object. */
|
||||
s32 index;
|
||||
Result result = KSynchronizationObject::Wait(std::addressof(index), objs, num_objects, timeout);
|
||||
if (svc::ResultTimedOut::Includes(result)) {
|
||||
R_THROW(result);
|
||||
}
|
||||
|
||||
/* Receive the request. */
|
||||
if (R_SUCCEEDED(result)) {
|
||||
KServerSession *session = objs[index]->DynamicCast<KServerSession *>();
|
||||
if (session != nullptr) {
|
||||
result = session->ReceiveRequest(message, buffer_size, message_paddr);
|
||||
if (svc::ResultNotFound::Includes(result)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_index = index;
|
||||
R_RETURN(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ReplyAndReceiveImpl(int32_t *out_index, uintptr_t message, size_t buffer_size, KPhysicalAddress message_paddr, KUserPointer<const ams::svc::Handle *> user_handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) {
|
||||
/* Ensure number of handles is valid. */
|
||||
R_UNLESS(0 <= num_handles && num_handles <= ams::svc::ArgumentHandleCountMax, svc::ResultOutOfRange());
|
||||
|
||||
/* Get the synchronization context. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
KSynchronizationObject **objs = GetCurrentThread().GetSynchronizationObjectBuffer();
|
||||
ams::svc::Handle *handles = GetCurrentThread().GetHandleBuffer();
|
||||
|
||||
/* Copy user handles. */
|
||||
if (num_handles > 0) {
|
||||
/* Ensure that we can try to get the handles. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(KProcessAddress(user_handles.GetUnsafePointer()), num_handles * sizeof(ams::svc::Handle)), svc::ResultInvalidPointer());
|
||||
|
||||
/* Get the handles. */
|
||||
R_TRY(user_handles.CopyArrayTo(handles, num_handles));
|
||||
|
||||
/* Convert the handles to objects. */
|
||||
R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles), svc::ResultInvalidHandle());
|
||||
}
|
||||
|
||||
/* Ensure handles are closed when we're done. */
|
||||
ON_SCOPE_EXIT {
|
||||
for (auto i = 0; i < num_handles; ++i) {
|
||||
objs[i]->Close();
|
||||
}
|
||||
};
|
||||
|
||||
R_RETURN(ReplyAndReceiveImpl(out_index, message, buffer_size, message_paddr, objs, num_handles, reply_target, timeout_ns));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SendSyncRequest(ams::svc::Handle session_handle) {
|
||||
R_RETURN(SendSyncRequestImpl(0, 0, session_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SendSyncRequestWithUserBuffer(uintptr_t message, size_t buffer_size, ams::svc::Handle session_handle) {
|
||||
/* Validate that the message buffer is page aligned and does not overflow. */
|
||||
R_UNLESS(util::IsAligned(message, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(buffer_size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(buffer_size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(message < message + buffer_size, svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the process page table. */
|
||||
auto &page_table = GetCurrentProcess().GetPageTable();
|
||||
|
||||
/* Lock the message buffer. */
|
||||
R_TRY(page_table.LockForIpcUserBuffer(nullptr, message, buffer_size));
|
||||
|
||||
{
|
||||
/* If we fail to send the message, unlock the message buffer. */
|
||||
ON_RESULT_FAILURE { page_table.UnlockForIpcUserBuffer(message, buffer_size); };
|
||||
|
||||
/* Send the request. */
|
||||
MESOSPHERE_ASSERT(message != 0);
|
||||
R_TRY(SendSyncRequestImpl(message, buffer_size, session_handle));
|
||||
}
|
||||
|
||||
/* We successfully processed, so try to unlock the message buffer. */
|
||||
R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SendAsyncRequestWithUserBufferImpl(ams::svc::Handle *out_event_handle, uintptr_t message, size_t buffer_size, ams::svc::Handle session_handle) {
|
||||
/* Get the process and handle table. */
|
||||
auto &process = GetCurrentProcess();
|
||||
auto &handle_table = process.GetHandleTable();
|
||||
|
||||
/* Reserve a new event from the process resource limit. */
|
||||
KScopedResourceReservation event_reservation(std::addressof(process), ams::svc::LimitableResource_EventCountMax);
|
||||
R_UNLESS(event_reservation.Succeeded(), svc::ResultLimitReached());
|
||||
|
||||
/* Get the client session. */
|
||||
KScopedAutoObject session = GetCurrentProcess().GetHandleTable().GetObject<KClientSession>(session_handle);
|
||||
R_UNLESS(session.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the parent, and persist a reference to it until we're done. */
|
||||
KScopedAutoObject parent = session->GetParent();
|
||||
MESOSPHERE_ASSERT(parent.IsNotNull());
|
||||
|
||||
/* Create a new event. */
|
||||
KEvent *event = KEvent::Create();
|
||||
R_UNLESS(event != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
/* Initialize the event. */
|
||||
event->Initialize();
|
||||
|
||||
/* Commit our reservation. */
|
||||
event_reservation.Commit();
|
||||
|
||||
/* At end of scope, kill the standing event references. */
|
||||
ON_SCOPE_EXIT {
|
||||
event->GetReadableEvent().Close();
|
||||
event->Close();
|
||||
};
|
||||
|
||||
/* Register the event. */
|
||||
KEvent::Register(event);
|
||||
|
||||
/* Add the readable event to the handle table. */
|
||||
R_TRY(handle_table.Add(out_event_handle, std::addressof(event->GetReadableEvent())));
|
||||
|
||||
/* Ensure that if we fail to send the request, we close the readable handle. */
|
||||
ON_RESULT_FAILURE { handle_table.Remove(*out_event_handle); };
|
||||
|
||||
/* Send the async request. */
|
||||
R_RETURN(session->SendAsyncRequest(event, message, buffer_size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SendAsyncRequestWithUserBuffer(ams::svc::Handle *out_event_handle, uintptr_t message, size_t buffer_size, ams::svc::Handle session_handle) {
|
||||
/* Validate that the message buffer is page aligned and does not overflow. */
|
||||
R_UNLESS(util::IsAligned(message, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(buffer_size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(buffer_size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(message < message + buffer_size, svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the process page table. */
|
||||
auto &page_table = GetCurrentProcess().GetPageTable();
|
||||
|
||||
/* Lock the message buffer. */
|
||||
R_TRY(page_table.LockForIpcUserBuffer(nullptr, message, buffer_size));
|
||||
|
||||
/* Ensure that if we fail and aren't terminating that we unlock the user buffer. */
|
||||
ON_RESULT_FAILURE_BESIDES(svc::ResultTerminationRequested) {
|
||||
page_table.UnlockForIpcUserBuffer(message, buffer_size);
|
||||
};
|
||||
|
||||
/* Send the request. */
|
||||
MESOSPHERE_ASSERT(message != 0);
|
||||
R_RETURN(SendAsyncRequestWithUserBufferImpl(out_event_handle, message, buffer_size, session_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ReplyAndReceive(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) {
|
||||
R_RETURN(ReplyAndReceiveImpl(out_index, 0, 0, Null<KPhysicalAddress>, handles, num_handles, reply_target, timeout_ns));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ReplyAndReceiveWithUserBuffer(int32_t *out_index, uintptr_t message, size_t buffer_size, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) {
|
||||
/* Validate that the message buffer is page aligned and does not overflow. */
|
||||
R_UNLESS(util::IsAligned(message, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(buffer_size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(buffer_size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(message < message + buffer_size, svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the process page table. */
|
||||
auto &page_table = GetCurrentProcess().GetPageTable();
|
||||
|
||||
/* Lock the message buffer, getting its physical address. */
|
||||
KPhysicalAddress message_paddr;
|
||||
R_TRY(page_table.LockForIpcUserBuffer(std::addressof(message_paddr), message, buffer_size));
|
||||
|
||||
{
|
||||
/* If we fail to send the message, unlock the message buffer. */
|
||||
ON_RESULT_FAILURE { page_table.UnlockForIpcUserBuffer(message, buffer_size); };
|
||||
|
||||
/* Reply/Receive the request. */
|
||||
MESOSPHERE_ASSERT(message != 0);
|
||||
R_TRY(ReplyAndReceiveImpl(out_index, message, buffer_size, message_paddr, handles, num_handles, reply_target, timeout_ns));
|
||||
}
|
||||
|
||||
/* We successfully processed, so try to unlock the message buffer. */
|
||||
R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result SendSyncRequest64(ams::svc::Handle session_handle) {
|
||||
R_RETURN(SendSyncRequest(session_handle));
|
||||
}
|
||||
|
||||
Result SendSyncRequestWithUserBuffer64(ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, ams::svc::Handle session_handle) {
|
||||
R_RETURN(SendSyncRequestWithUserBuffer(message_buffer, message_buffer_size, session_handle));
|
||||
}
|
||||
|
||||
Result SendAsyncRequestWithUserBuffer64(ams::svc::Handle *out_event_handle, ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, ams::svc::Handle session_handle) {
|
||||
R_RETURN(SendAsyncRequestWithUserBuffer(out_event_handle, message_buffer, message_buffer_size, session_handle));
|
||||
}
|
||||
|
||||
Result ReplyAndReceive64(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) {
|
||||
R_RETURN(ReplyAndReceive(out_index, handles, num_handles, reply_target, timeout_ns));
|
||||
}
|
||||
|
||||
Result ReplyAndReceiveWithUserBuffer64(int32_t *out_index, ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) {
|
||||
R_RETURN(ReplyAndReceiveWithUserBuffer(out_index, message_buffer, message_buffer_size, handles, num_handles, reply_target, timeout_ns));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result SendSyncRequest64From32(ams::svc::Handle session_handle) {
|
||||
R_RETURN(SendSyncRequest(session_handle));
|
||||
}
|
||||
|
||||
Result SendSyncRequestWithUserBuffer64From32(ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, ams::svc::Handle session_handle) {
|
||||
R_RETURN(SendSyncRequestWithUserBuffer(message_buffer, message_buffer_size, session_handle));
|
||||
}
|
||||
|
||||
Result SendAsyncRequestWithUserBuffer64From32(ams::svc::Handle *out_event_handle, ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, ams::svc::Handle session_handle) {
|
||||
R_RETURN(SendAsyncRequestWithUserBuffer(out_event_handle, message_buffer, message_buffer_size, session_handle));
|
||||
}
|
||||
|
||||
Result ReplyAndReceive64From32(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) {
|
||||
R_RETURN(ReplyAndReceive(out_index, handles, num_handles, reply_target, timeout_ns));
|
||||
}
|
||||
|
||||
Result ReplyAndReceiveWithUserBuffer64From32(int32_t *out_index, ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) {
|
||||
R_RETURN(ReplyAndReceiveWithUserBuffer(out_index, message_buffer, message_buffer_size, handles, num_handles, reply_target, timeout_ns));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
void KernelDebug(ams::svc::KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2) {
|
||||
MESOSPHERE_UNUSED(kern_debug_type, arg0, arg1, arg2);
|
||||
|
||||
#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING
|
||||
{
|
||||
switch (kern_debug_type) {
|
||||
case ams::svc::KernelDebugType_Thread:
|
||||
if (arg0 == static_cast<u64>(-1)) {
|
||||
KDumpObject::DumpThread();
|
||||
} else {
|
||||
KDumpObject::DumpThread(arg0);
|
||||
}
|
||||
break;
|
||||
case ams::svc::KernelDebugType_ThreadCallStack:
|
||||
if (arg0 == static_cast<u64>(-1)) {
|
||||
KDumpObject::DumpThreadCallStack();
|
||||
} else {
|
||||
KDumpObject::DumpThreadCallStack(arg0);
|
||||
}
|
||||
break;
|
||||
case ams::svc::KernelDebugType_KernelObject:
|
||||
KDumpObject::DumpKernelObject();
|
||||
break;
|
||||
case ams::svc::KernelDebugType_Handle:
|
||||
if (arg0 == static_cast<u64>(-1)) {
|
||||
KDumpObject::DumpHandle();
|
||||
} else {
|
||||
KDumpObject::DumpHandle(arg0);
|
||||
}
|
||||
break;
|
||||
case ams::svc::KernelDebugType_Memory:
|
||||
if (arg0 == static_cast<u64>(-2)) {
|
||||
KDumpObject::DumpKernelMemory();
|
||||
} else if (arg0 == static_cast<u64>(-1)) {
|
||||
KDumpObject::DumpMemory();
|
||||
} else {
|
||||
KDumpObject::DumpMemory(arg0);
|
||||
}
|
||||
break;
|
||||
case ams::svc::KernelDebugType_PageTable:
|
||||
if (arg0 == static_cast<u64>(-2)) {
|
||||
KDumpObject::DumpKernelPageTable();
|
||||
} else if (arg0 == static_cast<u64>(-1)) {
|
||||
KDumpObject::DumpPageTable();
|
||||
} else {
|
||||
KDumpObject::DumpPageTable(arg0);
|
||||
}
|
||||
break;
|
||||
case ams::svc::KernelDebugType_CpuUtilization:
|
||||
{
|
||||
const auto old_prio = GetCurrentThread().GetBasePriority();
|
||||
GetCurrentThread().SetBasePriority(3);
|
||||
|
||||
if (arg0 == static_cast<u64>(-2)) {
|
||||
KDumpObject::DumpKernelCpuUtilization();
|
||||
} else if (arg0 == static_cast<u64>(-1)) {
|
||||
KDumpObject::DumpCpuUtilization();
|
||||
} else {
|
||||
KDumpObject::DumpCpuUtilization(arg0);
|
||||
}
|
||||
|
||||
GetCurrentThread().SetBasePriority(old_prio);
|
||||
}
|
||||
break;
|
||||
case ams::svc::KernelDebugType_Process:
|
||||
if (arg0 == static_cast<u64>(-1)) {
|
||||
KDumpObject::DumpProcess();
|
||||
} else {
|
||||
KDumpObject::DumpProcess(arg0);
|
||||
}
|
||||
break;
|
||||
case ams::svc::KernelDebugType_SuspendProcess:
|
||||
if (KProcess *process = KProcess::GetProcessFromId(arg0); process != nullptr) {
|
||||
ON_SCOPE_EXIT { process->Close(); };
|
||||
|
||||
if (R_SUCCEEDED(process->SetActivity(ams::svc::ProcessActivity_Paused))) {
|
||||
MESOSPHERE_RELEASE_LOG("Suspend Process ID=%3lu\n", process->GetId());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ams::svc::KernelDebugType_ResumeProcess:
|
||||
if (KProcess *process = KProcess::GetProcessFromId(arg0); process != nullptr) {
|
||||
ON_SCOPE_EXIT { process->Close(); };
|
||||
|
||||
if (R_SUCCEEDED(process->SetActivity(ams::svc::ProcessActivity_Runnable))) {
|
||||
MESOSPHERE_RELEASE_LOG("Resume Process ID=%3lu\n", process->GetId());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ams::svc::KernelDebugType_Port:
|
||||
if (arg0 == static_cast<u64>(-1)) {
|
||||
KDumpObject::DumpPort();
|
||||
} else {
|
||||
KDumpObject::DumpPort(arg0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ChangeKernelTraceState(ams::svc::KernelTraceState kern_trace_state) {
|
||||
#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING
|
||||
{
|
||||
switch (kern_trace_state) {
|
||||
case ams::svc::KernelTraceState_Enabled:
|
||||
{
|
||||
MESOSPHERE_KTRACE_RESUME();
|
||||
}
|
||||
break;
|
||||
case ams::svc::KernelTraceState_Disabled:
|
||||
{
|
||||
MESOSPHERE_KTRACE_PAUSE();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
MESOSPHERE_UNUSED(kern_trace_state);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
void KernelDebug64(ams::svc::KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2) {
|
||||
return KernelDebug(kern_debug_type, arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
void ChangeKernelTraceState64(ams::svc::KernelTraceState kern_trace_state) {
|
||||
return ChangeKernelTraceState(kern_trace_state);
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
void KernelDebug64From32(ams::svc::KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2) {
|
||||
return KernelDebug(kern_debug_type, arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
void ChangeKernelTraceState64From32(ams::svc::KernelTraceState kern_trace_state) {
|
||||
return ChangeKernelTraceState(kern_trace_state);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE Result SendSyncRequestLight(ams::svc::Handle session_handle, u32 *args) {
|
||||
/* Get the light client session from its handle. */
|
||||
KScopedAutoObject session = GetCurrentProcess().GetHandleTable().GetObject<KLightClientSession>(session_handle);
|
||||
R_UNLESS(session.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Send the request. */
|
||||
R_TRY(session->SendSyncRequest(args));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ReplyAndReceiveLight(ams::svc::Handle session_handle, u32 *args) {
|
||||
/* Get the light server session from its handle. */
|
||||
KScopedAutoObject session = GetCurrentProcess().GetHandleTable().GetObject<KLightServerSession>(session_handle);
|
||||
R_UNLESS(session.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Handle the request. */
|
||||
R_TRY(session->ReplyAndReceive(args));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result SendSyncRequestLight64(ams::svc::Handle session_handle, u32 *args) {
|
||||
R_RETURN(SendSyncRequestLight(session_handle, args));
|
||||
}
|
||||
|
||||
Result ReplyAndReceiveLight64(ams::svc::Handle session_handle, u32 *args) {
|
||||
R_RETURN(ReplyAndReceiveLight(session_handle, args));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result SendSyncRequestLight64From32(ams::svc::Handle session_handle, u32 *args) {
|
||||
R_RETURN(SendSyncRequestLight(session_handle, args));
|
||||
}
|
||||
|
||||
Result ReplyAndReceiveLight64From32(ams::svc::Handle session_handle, u32 *args) {
|
||||
R_RETURN(ReplyAndReceiveLight(session_handle, args));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsKernelAddress(uintptr_t address) {
|
||||
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
|
||||
}
|
||||
|
||||
Result ArbitrateLock(ams::svc::Handle thread_handle, uintptr_t address, uint32_t tag) {
|
||||
/* Validate the input address. */
|
||||
R_UNLESS(!IsKernelAddress(address), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(util::IsAligned(address, sizeof(u32)), svc::ResultInvalidAddress());
|
||||
|
||||
R_RETURN(KConditionVariable::WaitForAddress(thread_handle, address, tag));
|
||||
}
|
||||
|
||||
Result ArbitrateUnlock(uintptr_t address) {
|
||||
/* Validate the input address. */
|
||||
R_UNLESS(!IsKernelAddress(address), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(util::IsAligned(address, sizeof(u32)), svc::ResultInvalidAddress());
|
||||
|
||||
R_RETURN(KConditionVariable::SignalToAddress(address));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result ArbitrateLock64(ams::svc::Handle thread_handle, ams::svc::Address address, uint32_t tag) {
|
||||
R_RETURN(ArbitrateLock(thread_handle, address, tag));
|
||||
}
|
||||
|
||||
Result ArbitrateUnlock64(ams::svc::Address address) {
|
||||
R_RETURN(ArbitrateUnlock(address));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result ArbitrateLock64From32(ams::svc::Handle thread_handle, ams::svc::Address address, uint32_t tag) {
|
||||
R_RETURN(ArbitrateLock(thread_handle, address, tag));
|
||||
}
|
||||
|
||||
Result ArbitrateUnlock64From32(ams::svc::Address address) {
|
||||
R_RETURN(ArbitrateUnlock(address));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidSetMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case ams::svc::MemoryPermission_None:
|
||||
case ams::svc::MemoryPermission_Read:
|
||||
case ams::svc::MemoryPermission_ReadWrite:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result SetMemoryPermission(uintptr_t address, size_t size, ams::svc::MemoryPermission perm) {
|
||||
/* Validate address / size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Validate the permission. */
|
||||
R_UNLESS(IsValidSetMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission());
|
||||
|
||||
/* Validate that the region is in range for the current process. */
|
||||
auto &page_table = GetCurrentProcess().GetPageTable();
|
||||
R_UNLESS(page_table.Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Set the memory attribute. */
|
||||
R_RETURN(page_table.SetMemoryPermission(address, size, perm));
|
||||
}
|
||||
|
||||
Result SetMemoryAttribute(uintptr_t address, size_t size, uint32_t mask, uint32_t attr) {
|
||||
/* Validate address / size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Validate the attribute and mask. */
|
||||
constexpr u32 SupportedMask = ams::svc::MemoryAttribute_Uncached | ams::svc::MemoryAttribute_PermissionLocked;
|
||||
R_UNLESS((mask | attr) == mask, svc::ResultInvalidCombination());
|
||||
R_UNLESS((mask | attr | SupportedMask) == SupportedMask, svc::ResultInvalidCombination());
|
||||
|
||||
/* Check that permission locked is either being set or not masked. */
|
||||
R_UNLESS((mask & ams::svc::MemoryAttribute_PermissionLocked) == (attr & ams::svc::MemoryAttribute_PermissionLocked), svc::ResultInvalidCombination());
|
||||
|
||||
/* Validate that the region is in range for the current process. */
|
||||
auto &page_table = GetCurrentProcess().GetPageTable();
|
||||
R_UNLESS(page_table.Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Set the memory attribute. */
|
||||
R_RETURN(page_table.SetMemoryAttribute(address, size, mask, attr));
|
||||
}
|
||||
|
||||
Result MapMemory(uintptr_t dst_address, uintptr_t src_address, size_t size) {
|
||||
/* Validate that addresses are page aligned. */
|
||||
R_UNLESS(util::IsAligned(dst_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(src_address, PageSize), svc::ResultInvalidAddress());
|
||||
|
||||
/* Validate that size is positive and page aligned. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
|
||||
/* Ensure that neither mapping overflows. */
|
||||
R_UNLESS(src_address < src_address + size, svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(dst_address < dst_address + size, svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the page table we're operating on. */
|
||||
auto &page_table = GetCurrentProcess().GetPageTable();
|
||||
|
||||
/* Ensure that the memory we're mapping is in range. */
|
||||
R_UNLESS(page_table.Contains(src_address, size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(page_table.CanContain(dst_address, size, KMemoryState_Stack), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Map the memory. */
|
||||
R_RETURN(page_table.MapMemory(dst_address, src_address, size));
|
||||
}
|
||||
|
||||
Result UnmapMemory(uintptr_t dst_address, uintptr_t src_address, size_t size) {
|
||||
/* Validate that addresses are page aligned. */
|
||||
R_UNLESS(util::IsAligned(dst_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(src_address, PageSize), svc::ResultInvalidAddress());
|
||||
|
||||
/* Validate that size is positive and page aligned. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
|
||||
/* Ensure that neither mapping overflows. */
|
||||
R_UNLESS(src_address < src_address + size, svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(dst_address < dst_address + size, svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the page table we're operating on. */
|
||||
auto &page_table = GetCurrentProcess().GetPageTable();
|
||||
|
||||
/* Ensure that the memory we're unmapping is in range. */
|
||||
R_UNLESS(page_table.Contains(src_address, size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(page_table.CanContain(dst_address, size, KMemoryState_Stack), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Unmap the memory. */
|
||||
R_RETURN(page_table.UnmapMemory(dst_address, src_address, size));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result SetMemoryPermission64(ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission perm) {
|
||||
R_RETURN(SetMemoryPermission(address, size, perm));
|
||||
}
|
||||
|
||||
Result SetMemoryAttribute64(ams::svc::Address address, ams::svc::Size size, uint32_t mask, uint32_t attr) {
|
||||
R_RETURN(SetMemoryAttribute(address, size, mask, attr));
|
||||
}
|
||||
|
||||
Result MapMemory64(ams::svc::Address dst_address, ams::svc::Address src_address, ams::svc::Size size) {
|
||||
R_RETURN(MapMemory(dst_address, src_address, size));
|
||||
}
|
||||
|
||||
Result UnmapMemory64(ams::svc::Address dst_address, ams::svc::Address src_address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapMemory(dst_address, src_address, size));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result SetMemoryPermission64From32(ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission perm) {
|
||||
R_RETURN(SetMemoryPermission(address, size, perm));
|
||||
}
|
||||
|
||||
Result SetMemoryAttribute64From32(ams::svc::Address address, ams::svc::Size size, uint32_t mask, uint32_t attr) {
|
||||
R_RETURN(SetMemoryAttribute(address, size, mask, attr));
|
||||
}
|
||||
|
||||
Result MapMemory64From32(ams::svc::Address dst_address, ams::svc::Address src_address, ams::svc::Size size) {
|
||||
R_RETURN(MapMemory(dst_address, src_address, size));
|
||||
}
|
||||
|
||||
Result UnmapMemory64From32(ams::svc::Address dst_address, ams::svc::Address src_address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapMemory(dst_address, src_address, size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
Result SetHeapSize(uintptr_t *out_address, size_t size) {
|
||||
/* Validate size. */
|
||||
R_UNLESS(util::IsAligned(size, ams::svc::HeapSizeAlignment), svc::ResultInvalidSize());
|
||||
R_UNLESS(size < ams::kern::MainMemorySizeMax, svc::ResultInvalidSize());
|
||||
|
||||
/* Set the heap size. */
|
||||
KProcessAddress address = Null<KProcessAddress>;
|
||||
R_TRY(GetCurrentProcess().GetPageTable().SetHeapSize(std::addressof(address), size));
|
||||
|
||||
/* Set the output. */
|
||||
*out_address = GetInteger(address);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetUnsafeLimit(size_t limit) {
|
||||
/* Ensure the size is aligned. */
|
||||
R_UNLESS(util::IsAligned(limit, PageSize), svc::ResultInvalidSize());
|
||||
|
||||
/* Ensure that the size is not bigger than we can accommodate. */
|
||||
R_UNLESS(limit <= Kernel::GetMemoryManager().GetSize(KMemoryManager::Pool_Unsafe), svc::ResultOutOfRange());
|
||||
|
||||
/* Set the size. */
|
||||
R_RETURN(Kernel::GetUnsafeMemory().SetLimitSize(limit));
|
||||
}
|
||||
|
||||
Result MapPhysicalMemory(uintptr_t address, size_t size) {
|
||||
/* Validate address / size. */
|
||||
const size_t min_alignment = Kernel::GetMemoryManager().GetMinimumAlignment(GetCurrentProcess().GetMemoryPool());
|
||||
R_UNLESS(util::IsAligned(address, min_alignment), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, min_alignment), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Verify that the process has system resource. */
|
||||
auto &process = GetCurrentProcess();
|
||||
R_UNLESS(process.GetTotalSystemResourceSize() > 0, svc::ResultInvalidState());
|
||||
|
||||
/* Verify that the region is in range. */
|
||||
auto &page_table = process.GetPageTable();
|
||||
R_UNLESS(page_table.IsInAliasRegion(address, size), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Map the memory. */
|
||||
R_TRY(page_table.MapPhysicalMemory(address, size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result UnmapPhysicalMemory(uintptr_t address, size_t size) {
|
||||
/* Validate address / size. */
|
||||
const size_t min_alignment = Kernel::GetMemoryManager().GetMinimumAlignment(GetCurrentProcess().GetMemoryPool());
|
||||
R_UNLESS(util::IsAligned(address, min_alignment), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, min_alignment), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Verify that the process has system resource. */
|
||||
auto &process = GetCurrentProcess();
|
||||
R_UNLESS(process.GetTotalSystemResourceSize() > 0, svc::ResultInvalidState());
|
||||
|
||||
/* Verify that the region is in range. */
|
||||
auto &page_table = process.GetPageTable();
|
||||
R_UNLESS(page_table.IsInAliasRegion(address, size), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Unmap the memory. */
|
||||
R_TRY(page_table.UnmapPhysicalMemory(address, size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MapPhysicalMemoryUnsafe(uintptr_t address, size_t size) {
|
||||
/* Validate address / size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Verify that the region is in range. */
|
||||
auto &process = GetCurrentProcess();
|
||||
auto &page_table = process.GetPageTable();
|
||||
R_UNLESS(page_table.IsInUnsafeAliasRegion(address, size), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Verify that the process isn't already using the unsafe pool. */
|
||||
R_UNLESS(process.GetMemoryPool() != KMemoryManager::Pool_Unsafe, svc::ResultInvalidMemoryPool());
|
||||
|
||||
/* Map the memory. */
|
||||
R_TRY(page_table.MapPhysicalMemoryUnsafe(address, size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result UnmapPhysicalMemoryUnsafe(uintptr_t address, size_t size) {
|
||||
/* Validate address / size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Verify that the region is in range. */
|
||||
auto &process = GetCurrentProcess();
|
||||
auto &page_table = process.GetPageTable();
|
||||
R_UNLESS(page_table.IsInUnsafeAliasRegion(address, size), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Unmap the memory. */
|
||||
R_TRY(page_table.UnmapPhysicalMemoryUnsafe(address, size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result SetHeapSize64(ams::svc::Address *out_address, ams::svc::Size size) {
|
||||
static_assert(sizeof(*out_address) == sizeof(uintptr_t));
|
||||
R_RETURN(SetHeapSize(reinterpret_cast<uintptr_t *>(out_address), size));
|
||||
}
|
||||
|
||||
Result MapPhysicalMemory64(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(MapPhysicalMemory(address, size));
|
||||
}
|
||||
|
||||
Result UnmapPhysicalMemory64(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapPhysicalMemory(address, size));
|
||||
}
|
||||
|
||||
Result MapPhysicalMemoryUnsafe64(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(MapPhysicalMemoryUnsafe(address, size));
|
||||
}
|
||||
|
||||
Result UnmapPhysicalMemoryUnsafe64(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapPhysicalMemoryUnsafe(address, size));
|
||||
}
|
||||
|
||||
Result SetUnsafeLimit64(ams::svc::Size limit) {
|
||||
R_RETURN(SetUnsafeLimit(limit));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result SetHeapSize64From32(ams::svc::Address *out_address, ams::svc::Size size) {
|
||||
static_assert(sizeof(*out_address) == sizeof(uintptr_t));
|
||||
R_RETURN(SetHeapSize(reinterpret_cast<uintptr_t *>(out_address), size));
|
||||
}
|
||||
|
||||
Result MapPhysicalMemory64From32(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(MapPhysicalMemory(address, size));
|
||||
}
|
||||
|
||||
Result UnmapPhysicalMemory64From32(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapPhysicalMemory(address, size));
|
||||
}
|
||||
|
||||
Result MapPhysicalMemoryUnsafe64From32(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(MapPhysicalMemoryUnsafe(address, size));
|
||||
}
|
||||
|
||||
Result UnmapPhysicalMemoryUnsafe64From32(ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapPhysicalMemoryUnsafe(address, size));
|
||||
}
|
||||
|
||||
Result SetUnsafeLimit64From32(ams::svc::Size limit) {
|
||||
R_RETURN(SetUnsafeLimit(limit));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
Result ManageNamedPort(ams::svc::Handle *out_server_handle, KUserPointer<const char *> user_name, s32 max_sessions) {
|
||||
/* Copy the provided name from user memory to kernel memory. */
|
||||
char name[KObjectName::NameLengthMax] = {};
|
||||
R_TRY(user_name.CopyStringTo(name, sizeof(name)));
|
||||
|
||||
/* Validate that sessions and name are valid. */
|
||||
R_UNLESS(max_sessions >= 0, svc::ResultOutOfRange());
|
||||
R_UNLESS(name[sizeof(name) - 1] == '\x00', svc::ResultOutOfRange());
|
||||
|
||||
if (max_sessions > 0) {
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Create a new port. */
|
||||
KPort *port = KPort::Create();
|
||||
R_UNLESS(port != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
/* Initialize the new port. */
|
||||
port->Initialize(max_sessions, false, 0);
|
||||
|
||||
/* Register the port. */
|
||||
KPort::Register(port);
|
||||
|
||||
/* Ensure that our only reference to the port is in the handle table when we're done. */
|
||||
ON_SCOPE_EXIT {
|
||||
port->GetClientPort().Close();
|
||||
port->GetServerPort().Close();
|
||||
};
|
||||
|
||||
/* Register the handle in the table. */
|
||||
R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort())));
|
||||
ON_RESULT_FAILURE { handle_table.Remove(*out_server_handle); };
|
||||
|
||||
/* Create a new object name. */
|
||||
R_TRY(KObjectName::NewFromName(std::addressof(port->GetClientPort()), name));
|
||||
} else /* if (max_sessions == 0) */ {
|
||||
/* Ensure that this else case is correct. */
|
||||
MESOSPHERE_AUDIT(max_sessions == 0);
|
||||
|
||||
/* If we're closing, there's no server handle. */
|
||||
*out_server_handle = ams::svc::InvalidHandle;
|
||||
|
||||
/* Delete the object. */
|
||||
R_TRY(KObjectName::Delete<KClientPort>(name));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreatePort(ams::svc::Handle *out_server, ams::svc::Handle *out_client, int32_t max_sessions, bool is_light, uintptr_t name) {
|
||||
/* Ensure max sessions is valid. */
|
||||
R_UNLESS(max_sessions > 0, svc::ResultOutOfRange());
|
||||
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Create a new port. */
|
||||
KPort *port = KPort::Create();
|
||||
R_UNLESS(port != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
/* Initialize the port. */
|
||||
port->Initialize(max_sessions, is_light, name);
|
||||
|
||||
/* Ensure that we clean up the port (and its only references are handle table) on function end. */
|
||||
ON_SCOPE_EXIT {
|
||||
port->GetServerPort().Close();
|
||||
port->GetClientPort().Close();
|
||||
};
|
||||
|
||||
/* Register the port. */
|
||||
KPort::Register(port);
|
||||
|
||||
/* Add the client to the handle table. */
|
||||
R_TRY(handle_table.Add(out_client, std::addressof(port->GetClientPort())));
|
||||
|
||||
/* Ensure that we maintain a clean handle state on exit. */
|
||||
ON_RESULT_FAILURE { handle_table.Remove(*out_client); };
|
||||
|
||||
/* Add the server to the handle table. */
|
||||
R_RETURN(handle_table.Add(out_server, std::addressof(port->GetServerPort())));
|
||||
}
|
||||
|
||||
Result ConnectToNamedPort(ams::svc::Handle *out, KUserPointer<const char *> user_name) {
|
||||
/* Copy the provided name from user memory to kernel memory. */
|
||||
char name[KObjectName::NameLengthMax] = {};
|
||||
R_TRY(user_name.CopyStringTo(name, sizeof(name)));
|
||||
|
||||
/* Validate that name is valid. */
|
||||
R_UNLESS(name[sizeof(name) - 1] == '\x00', svc::ResultOutOfRange());
|
||||
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Find the client port. */
|
||||
auto port = KObjectName::Find<KClientPort>(name);
|
||||
R_UNLESS(port.IsNotNull(), svc::ResultNotFound());
|
||||
|
||||
/* Reserve a handle for the port. */
|
||||
/* NOTE: Nintendo really does write directly to the output handle here. */
|
||||
R_TRY(handle_table.Reserve(out));
|
||||
ON_RESULT_FAILURE { handle_table.Unreserve(*out); };
|
||||
|
||||
/* Create a session. */
|
||||
KClientSession *session;
|
||||
R_TRY(port->CreateSession(std::addressof(session)));
|
||||
|
||||
/* Register the session in the table, close the extra reference. */
|
||||
handle_table.Register(*out, session);
|
||||
session->Close();
|
||||
|
||||
/* We succeeded. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ConnectToPort(ams::svc::Handle *out, ams::svc::Handle port) {
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Get the client port. */
|
||||
KScopedAutoObject client_port = handle_table.GetObject<KClientPort>(port);
|
||||
R_UNLESS(client_port.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Reserve a handle for the port. */
|
||||
/* NOTE: Nintendo really does write directly to the output handle here. */
|
||||
R_TRY(handle_table.Reserve(out));
|
||||
ON_RESULT_FAILURE { handle_table.Unreserve(*out); };
|
||||
|
||||
/* Create the session. */
|
||||
KAutoObject *session;
|
||||
if (client_port->IsLight()) {
|
||||
R_TRY(client_port->CreateLightSession(reinterpret_cast<KLightClientSession **>(std::addressof(session))));
|
||||
} else {
|
||||
R_TRY(client_port->CreateSession(reinterpret_cast<KClientSession **>(std::addressof(session))));
|
||||
}
|
||||
|
||||
/* Register the session. */
|
||||
handle_table.Register(*out, session);
|
||||
session->Close();
|
||||
|
||||
/* We succeeded. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result ConnectToNamedPort64(ams::svc::Handle *out_handle, KUserPointer<const char *> name) {
|
||||
R_RETURN(ConnectToNamedPort(out_handle, name));
|
||||
}
|
||||
|
||||
Result CreatePort64(ams::svc::Handle *out_server_handle, ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ams::svc::Address name) {
|
||||
R_RETURN(CreatePort(out_server_handle, out_client_handle, max_sessions, is_light, name));
|
||||
}
|
||||
|
||||
Result ManageNamedPort64(ams::svc::Handle *out_server_handle, KUserPointer<const char *> name, int32_t max_sessions) {
|
||||
R_RETURN(ManageNamedPort(out_server_handle, name, max_sessions));
|
||||
}
|
||||
|
||||
Result ConnectToPort64(ams::svc::Handle *out_handle, ams::svc::Handle port) {
|
||||
R_RETURN(ConnectToPort(out_handle, port));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result ConnectToNamedPort64From32(ams::svc::Handle *out_handle, KUserPointer<const char *> name) {
|
||||
R_RETURN(ConnectToNamedPort(out_handle, name));
|
||||
}
|
||||
|
||||
Result CreatePort64From32(ams::svc::Handle *out_server_handle, ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ams::svc::Address name) {
|
||||
R_RETURN(CreatePort(out_server_handle, out_client_handle, max_sessions, is_light, name));
|
||||
}
|
||||
|
||||
Result ManageNamedPort64From32(ams::svc::Handle *out_server_handle, KUserPointer<const char *> name, int32_t max_sessions) {
|
||||
R_RETURN(ManageNamedPort(out_server_handle, name, max_sessions));
|
||||
}
|
||||
|
||||
Result ConnectToPort64From32(ams::svc::Handle *out_handle, ams::svc::Handle port) {
|
||||
R_RETURN(ConnectToPort(out_handle, port));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
void SleepSystem() {
|
||||
return KSystemControl::SleepSystem();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
void SleepSystem64() {
|
||||
return SleepSystem();
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
void SleepSystem64From32() {
|
||||
return SleepSystem();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,445 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
|
||||
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
|
||||
}
|
||||
|
||||
void ExitProcess() {
|
||||
GetCurrentProcess().Exit();
|
||||
MESOSPHERE_PANIC("Process survived call to exit");
|
||||
}
|
||||
|
||||
Result GetProcessId(u64 *out_process_id, ams::svc::Handle handle) {
|
||||
/* Get the object from the handle table. */
|
||||
KScopedAutoObject obj = GetCurrentProcess().GetHandleTable().GetObject<KAutoObject>(handle);
|
||||
R_UNLESS(obj.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the process from the object. */
|
||||
if (KProcess *process = obj->DynamicCast<KProcess *>(); process != nullptr) {
|
||||
/* The object is a process, so we can use it directly. */
|
||||
|
||||
/* Make sure the target process exists. */
|
||||
R_UNLESS(process != nullptr, svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the process id. */
|
||||
*out_process_id = process->GetId();
|
||||
} else if (KThread *t = obj->DynamicCast<KThread *>(); t != nullptr) {
|
||||
/* The object is a thread, so we want to use its parent. */
|
||||
KProcess *process = t->GetOwnerProcess();
|
||||
|
||||
/* Make sure the target process exists. */
|
||||
R_UNLESS(process != nullptr, svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the process id. */
|
||||
*out_process_id = process->GetId();
|
||||
} else if (KDebug *d = obj->DynamicCast<KDebug *>(); d != nullptr) {
|
||||
/* The object is a debug, so we want to use the process it's attached to. */
|
||||
|
||||
/* Make sure the target process exists. */
|
||||
R_UNLESS(d->IsAttached(), svc::ResultInvalidHandle());
|
||||
R_UNLESS(d->OpenProcess(), svc::ResultInvalidHandle());
|
||||
ON_SCOPE_EXIT { d->CloseProcess(); };
|
||||
|
||||
/* Get the process id. */
|
||||
*out_process_id = d->GetProcessUnsafe()->GetProcessId();
|
||||
} else {
|
||||
R_THROW(svc::ResultInvalidHandle());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProcessList(int32_t *out_num_processes, KUserPointer<uint64_t *> out_process_ids, int32_t max_out_count) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||
|
||||
/* Validate that the out count is valid. */
|
||||
R_UNLESS((0 <= max_out_count && max_out_count <= static_cast<int32_t>(std::numeric_limits<int32_t>::max() / sizeof(u64))), svc::ResultOutOfRange());
|
||||
|
||||
/* Validate that the pointer is in range. */
|
||||
if (max_out_count > 0) {
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(KProcessAddress(out_process_ids.GetUnsafePointer()), max_out_count * sizeof(u64)), svc::ResultInvalidCurrentMemory());
|
||||
}
|
||||
|
||||
/* Get the process list. */
|
||||
R_RETURN(KProcess::GetProcessList(out_num_processes, out_process_ids, max_out_count));
|
||||
}
|
||||
|
||||
Result CreateProcess(ams::svc::Handle *out, const ams::svc::CreateProcessParameter ¶ms, KUserPointer<const uint32_t *> user_caps, int32_t num_caps) {
|
||||
/* Validate the capabilities pointer. */
|
||||
R_UNLESS(num_caps >= 0, svc::ResultInvalidPointer());
|
||||
if (num_caps > 0) {
|
||||
/* Check for overflow. */
|
||||
R_UNLESS(((num_caps * sizeof(u32)) / sizeof(u32)) == static_cast<size_t>(num_caps), svc::ResultInvalidPointer());
|
||||
|
||||
/* Validate that the pointer is in range. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(KProcessAddress(user_caps.GetUnsafePointer()), num_caps * sizeof(u32)), svc::ResultInvalidPointer());
|
||||
}
|
||||
|
||||
/* Validate that the parameter flags are valid. */
|
||||
R_UNLESS((params.flags & ~ams::svc::CreateProcessFlag_All) == 0, svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Validate that 64-bit process is okay. */
|
||||
const bool is_64_bit = (params.flags & ams::svc::CreateProcessFlag_Is64Bit) != 0;
|
||||
if constexpr (sizeof(void *) < sizeof(u64)) {
|
||||
R_UNLESS(!is_64_bit, svc::ResultInvalidCombination());
|
||||
}
|
||||
|
||||
/* Decide on an address space map region. */
|
||||
uintptr_t map_start, map_end;
|
||||
size_t map_size;
|
||||
const size_t code_size = params.code_num_pages * PageSize;
|
||||
switch (params.flags & ams::svc::CreateProcessFlag_AddressSpaceMask) {
|
||||
case ams::svc::CreateProcessFlag_AddressSpace32Bit:
|
||||
case ams::svc::CreateProcessFlag_AddressSpace32BitWithoutAlias:
|
||||
{
|
||||
map_start = KAddressSpaceInfo::GetAddressSpaceStart(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_MapSmall, code_size);
|
||||
map_size = KAddressSpaceInfo::GetAddressSpaceSize(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_MapSmall);
|
||||
map_end = map_start + map_size;
|
||||
}
|
||||
break;
|
||||
case ams::svc::CreateProcessFlag_AddressSpace64BitDeprecated:
|
||||
{
|
||||
/* 64-bit address space requires 64-bit process. */
|
||||
R_UNLESS(is_64_bit, svc::ResultInvalidCombination());
|
||||
|
||||
map_start = KAddressSpaceInfo::GetAddressSpaceStart(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_MapSmall, code_size);
|
||||
map_size = KAddressSpaceInfo::GetAddressSpaceSize(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_MapSmall);
|
||||
map_end = map_start + map_size;
|
||||
}
|
||||
break;
|
||||
case ams::svc::CreateProcessFlag_AddressSpace64Bit:
|
||||
{
|
||||
/* 64-bit address space requires 64-bit process. */
|
||||
R_UNLESS(is_64_bit, svc::ResultInvalidCombination());
|
||||
|
||||
map_start = KAddressSpaceInfo::GetAddressSpaceStart(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_Map39Bit, code_size);
|
||||
map_end = map_start + KAddressSpaceInfo::GetAddressSpaceSize(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_Map39Bit);
|
||||
|
||||
map_size = KAddressSpaceInfo::GetAddressSpaceSize(static_cast<ams::svc::CreateProcessFlag>(params.flags), KAddressSpaceInfo::Type_Heap);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
R_THROW(svc::ResultInvalidEnumValue());
|
||||
}
|
||||
|
||||
/* Validate the pool partition. */
|
||||
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
|
||||
switch (params.flags & ams::svc::CreateProcessFlag_PoolPartitionMask) {
|
||||
case ams::svc::CreateProcessFlag_PoolPartitionApplication:
|
||||
case ams::svc::CreateProcessFlag_PoolPartitionApplet:
|
||||
case ams::svc::CreateProcessFlag_PoolPartitionSystem:
|
||||
case ams::svc::CreateProcessFlag_PoolPartitionSystemNonSecure:
|
||||
break;
|
||||
default:
|
||||
R_THROW(svc::ResultInvalidEnumValue());
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that the code address is aligned. */
|
||||
R_UNLESS(util::IsAligned(params.code_address, KProcess::AslrAlignment), svc::ResultInvalidAddress());
|
||||
|
||||
/* Check that the number of code pages is >= 0. */
|
||||
R_UNLESS(params.code_num_pages >= 0, svc::ResultInvalidSize());
|
||||
|
||||
/* Check that the number of extra resource pages is >= 0. */
|
||||
R_UNLESS(params.system_resource_num_pages >= 0, svc::ResultInvalidSize());
|
||||
|
||||
/* Validate that the alias region extra size is allowed, if enabled. */
|
||||
if (params.flags & ams::svc::CreateProcessFlag_EnableAliasRegionExtraSize) {
|
||||
/* Check that we have a 64-bit address space. */
|
||||
R_UNLESS((params.flags & ams::svc::CreateProcessFlag_AddressSpaceMask) == ams::svc::CreateProcessFlag_AddressSpace64Bit, svc::ResultInvalidState());
|
||||
|
||||
/* Check that the system resource page count is non-zero. */
|
||||
R_UNLESS(params.system_resource_num_pages > 0, svc::ResultInvalidState());
|
||||
|
||||
/* Check that debug mode is enabled. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultInvalidState());
|
||||
}
|
||||
|
||||
/* Convert to sizes. */
|
||||
const size_t code_num_pages = params.code_num_pages;
|
||||
const size_t system_resource_num_pages = params.system_resource_num_pages;
|
||||
const size_t total_pages = code_num_pages + system_resource_num_pages;
|
||||
const size_t system_resource_size = system_resource_num_pages * PageSize;
|
||||
const size_t total_size = code_size + system_resource_size;
|
||||
|
||||
/* Check for overflow. */
|
||||
R_UNLESS((code_size / PageSize) == code_num_pages, svc::ResultInvalidSize());
|
||||
R_UNLESS((system_resource_size / PageSize) == system_resource_num_pages, svc::ResultInvalidSize());
|
||||
R_UNLESS((code_num_pages + system_resource_num_pages) >= code_num_pages, svc::ResultOutOfMemory());
|
||||
R_UNLESS((total_size / PageSize) == total_pages, svc::ResultInvalidSize());
|
||||
|
||||
/* Check that the number of pages is valid. */
|
||||
R_UNLESS(code_num_pages < (map_size / PageSize), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Validate that the code falls within the map reigon. */
|
||||
R_UNLESS(map_start <= params.code_address, svc::ResultInvalidMemoryRegion());
|
||||
R_UNLESS(params.code_address < params.code_address + code_size, svc::ResultInvalidMemoryRegion());
|
||||
R_UNLESS(params.code_address + code_size - 1 <= map_end - 1, svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Check that the number of pages is valid for the kernel address space. */
|
||||
R_UNLESS(code_num_pages < (kern::MainMemorySizeMax / PageSize), svc::ResultOutOfMemory());
|
||||
R_UNLESS(system_resource_num_pages < (kern::MainMemorySizeMax / PageSize), svc::ResultOutOfMemory());
|
||||
R_UNLESS(total_pages < (kern::MainMemorySizeMax / PageSize), svc::ResultOutOfMemory());
|
||||
|
||||
/* Check that optimized memory allocation is used only for applications. */
|
||||
const bool optimize_allocs = (params.flags & ams::svc::CreateProcessFlag_OptimizeMemoryAllocation) != 0;
|
||||
const bool is_application = (params.flags & ams::svc::CreateProcessFlag_IsApplication) != 0;
|
||||
R_UNLESS(!optimize_allocs || is_application, svc::ResultBusy());
|
||||
|
||||
/* Check that the user-provided capabilities are accessible and refer to valid regions. */
|
||||
R_TRY(KCapabilities::CheckCapabilities(user_caps, num_caps));
|
||||
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Create the new process. */
|
||||
KProcess *process = KProcess::Create();
|
||||
R_UNLESS(process != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
/* Ensure that the only reference to the process is in the handle table when we're done. */
|
||||
ON_SCOPE_EXIT { process->Close(); };
|
||||
|
||||
/* Get the resource limit from the handle. */
|
||||
KScopedAutoObject resource_limit = handle_table.GetObject<KResourceLimit>(params.reslimit);
|
||||
R_UNLESS(resource_limit.IsNotNull() || params.reslimit == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
/* Decide on a resource limit for the process. */
|
||||
KResourceLimit *process_resource_limit = resource_limit.IsNotNull() ? resource_limit.GetPointerUnsafe() : std::addressof(Kernel::GetSystemResourceLimit());
|
||||
|
||||
/* Get the pool for the process. */
|
||||
const auto pool = [](u32 flags) ALWAYS_INLINE_LAMBDA -> KMemoryManager::Pool {
|
||||
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
|
||||
switch (flags & ams::svc::CreateProcessFlag_PoolPartitionMask) {
|
||||
case ams::svc::CreateProcessFlag_PoolPartitionApplication:
|
||||
return KMemoryManager::Pool_Application;
|
||||
case ams::svc::CreateProcessFlag_PoolPartitionApplet:
|
||||
return KMemoryManager::Pool_Applet;
|
||||
case ams::svc::CreateProcessFlag_PoolPartitionSystem:
|
||||
return KMemoryManager::Pool_System;
|
||||
case ams::svc::CreateProcessFlag_PoolPartitionSystemNonSecure:
|
||||
default:
|
||||
return KMemoryManager::Pool_SystemNonSecure;
|
||||
}
|
||||
} else if (GetTargetFirmware() >= TargetFirmware_4_0_0) {
|
||||
if ((flags & ams::svc::CreateProcessFlag_DeprecatedUseSecureMemory) != 0) {
|
||||
return KMemoryManager::Pool_Secure;
|
||||
} else {
|
||||
return static_cast<KMemoryManager::Pool>(KSystemControl::GetCreateProcessMemoryPool());
|
||||
}
|
||||
} else {
|
||||
return static_cast<KMemoryManager::Pool>(KSystemControl::GetCreateProcessMemoryPool());
|
||||
}
|
||||
}(params.flags);
|
||||
|
||||
/* Initialize the process. */
|
||||
R_TRY(process->Initialize(params, user_caps, num_caps, process_resource_limit, pool));
|
||||
|
||||
/* Register the process. */
|
||||
KProcess::Register(process);
|
||||
|
||||
/* Add the process to the handle table. */
|
||||
R_TRY(handle_table.Add(out, process));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result CreateProcess(ams::svc::Handle *out, KUserPointer<const T *> user_parameters, KUserPointer<const uint32_t *> user_caps, int32_t num_caps) {
|
||||
/* Read the parameters from user space. */
|
||||
T params;
|
||||
R_TRY(user_parameters.CopyTo(std::addressof(params)));
|
||||
|
||||
/* Invoke the implementation. */
|
||||
if constexpr (std::same_as<T, ams::svc::CreateProcessParameter>) {
|
||||
R_RETURN(CreateProcess(out, params, user_caps, num_caps));
|
||||
} else {
|
||||
/* Convert the parameters. */
|
||||
ams::svc::CreateProcessParameter converted_params;
|
||||
static_assert(sizeof(T{}.name) == sizeof(ams::svc::CreateProcessParameter{}.name));
|
||||
|
||||
std::memcpy(converted_params.name, params.name, sizeof(converted_params.name));
|
||||
converted_params.version = params.version;
|
||||
converted_params.program_id = params.program_id;
|
||||
converted_params.code_address = params.code_address;
|
||||
converted_params.code_num_pages = params.code_num_pages;
|
||||
converted_params.flags = params.flags;
|
||||
converted_params.reslimit = params.reslimit;
|
||||
converted_params.system_resource_num_pages = params.system_resource_num_pages;
|
||||
|
||||
/* Invoke. */
|
||||
R_RETURN(CreateProcess(out, converted_params, user_caps, num_caps));
|
||||
}
|
||||
}
|
||||
|
||||
Result StartProcess(ams::svc::Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size) {
|
||||
/* Validate stack size. */
|
||||
const uint64_t aligned_stack_size = util::AlignUp(main_thread_stack_size, Kernel::GetMemoryManager().GetMinimumAlignment(GetCurrentProcess().GetMemoryPool()));
|
||||
R_UNLESS(aligned_stack_size >= main_thread_stack_size, svc::ResultOutOfMemory());
|
||||
R_UNLESS(aligned_stack_size == static_cast<size_t>(aligned_stack_size), svc::ResultOutOfMemory());
|
||||
|
||||
/* Get the target process. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Validate the core id. */
|
||||
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
|
||||
R_UNLESS(((1ul << core_id) & process->GetCoreMask()) != 0, svc::ResultInvalidCoreId());
|
||||
|
||||
/* Validate the priority. */
|
||||
R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority());
|
||||
R_UNLESS(process->CheckThreadPriority(priority), svc::ResultInvalidPriority());
|
||||
|
||||
/* Set the process's ideal processor. */
|
||||
process->SetIdealCoreId(core_id);
|
||||
|
||||
/* Run the process. */
|
||||
R_RETURN(process->Run(priority, static_cast<size_t>(aligned_stack_size)));
|
||||
}
|
||||
|
||||
Result TerminateProcess(ams::svc::Handle process_handle) {
|
||||
/* Get the target process. */
|
||||
KProcess *process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle).ReleasePointerUnsafe();
|
||||
R_UNLESS(process != nullptr, svc::ResultInvalidHandle());
|
||||
|
||||
if (process != GetCurrentProcessPointer()) {
|
||||
/* We're terminating another process. Close our reference after terminating the process. */
|
||||
ON_SCOPE_EXIT { process->Close(); };
|
||||
|
||||
/* Terminate the process. */
|
||||
R_TRY(process->Terminate());
|
||||
} else {
|
||||
/* We're terminating ourselves. Close our reference immediately. */
|
||||
process->Close();
|
||||
|
||||
/* Exit. */
|
||||
ExitProcess();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProcessInfo(int64_t *out, ams::svc::Handle process_handle, ams::svc::ProcessInfoType info_type) {
|
||||
/* Get the target process. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the info. */
|
||||
switch (info_type) {
|
||||
case ams::svc::ProcessInfoType_ProcessState:
|
||||
{
|
||||
/* Get the process's state. */
|
||||
KProcess::State state;
|
||||
{
|
||||
KScopedLightLock proc_lk(process->GetStateLock());
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
state = process->GetState();
|
||||
}
|
||||
|
||||
/* Convert to svc state. */
|
||||
switch (state) {
|
||||
case KProcess::State_Created: *out = ams::svc::ProcessState_Created; break;
|
||||
case KProcess::State_CreatedAttached: *out = ams::svc::ProcessState_CreatedAttached; break;
|
||||
case KProcess::State_Running: *out = ams::svc::ProcessState_Running; break;
|
||||
case KProcess::State_Crashed: *out = ams::svc::ProcessState_Crashed; break;
|
||||
case KProcess::State_RunningAttached: *out = ams::svc::ProcessState_RunningAttached; break;
|
||||
case KProcess::State_Terminating: *out = ams::svc::ProcessState_Terminating; break;
|
||||
case KProcess::State_Terminated: *out = ams::svc::ProcessState_Terminated; break;
|
||||
case KProcess::State_DebugBreak: *out = ams::svc::ProcessState_DebugBreak; break;
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
R_THROW(svc::ResultInvalidEnumValue());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
void ExitProcess64() {
|
||||
return ExitProcess();
|
||||
}
|
||||
|
||||
Result GetProcessId64(uint64_t *out_process_id, ams::svc::Handle process_handle) {
|
||||
R_RETURN(GetProcessId(out_process_id, process_handle));
|
||||
}
|
||||
|
||||
Result GetProcessList64(int32_t *out_num_processes, KUserPointer<uint64_t *> out_process_ids, int32_t max_out_count) {
|
||||
R_RETURN(GetProcessList(out_num_processes, out_process_ids, max_out_count));
|
||||
}
|
||||
|
||||
Result CreateProcess64(ams::svc::Handle *out_handle, KUserPointer<const ams::svc::lp64::CreateProcessParameter *> parameters, KUserPointer<const uint32_t *> caps, int32_t num_caps) {
|
||||
R_RETURN(CreateProcess(out_handle, parameters, caps, num_caps));
|
||||
}
|
||||
|
||||
Result StartProcess64(ams::svc::Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size) {
|
||||
R_RETURN(StartProcess(process_handle, priority, core_id, main_thread_stack_size));
|
||||
}
|
||||
|
||||
Result TerminateProcess64(ams::svc::Handle process_handle) {
|
||||
R_RETURN(TerminateProcess(process_handle));
|
||||
}
|
||||
|
||||
Result GetProcessInfo64(int64_t *out_info, ams::svc::Handle process_handle, ams::svc::ProcessInfoType info_type) {
|
||||
R_RETURN(GetProcessInfo(out_info, process_handle, info_type));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
void ExitProcess64From32() {
|
||||
return ExitProcess();
|
||||
}
|
||||
|
||||
Result GetProcessId64From32(uint64_t *out_process_id, ams::svc::Handle process_handle) {
|
||||
R_RETURN(GetProcessId(out_process_id, process_handle));
|
||||
}
|
||||
|
||||
Result GetProcessList64From32(int32_t *out_num_processes, KUserPointer<uint64_t *> out_process_ids, int32_t max_out_count) {
|
||||
R_RETURN(GetProcessList(out_num_processes, out_process_ids, max_out_count));
|
||||
}
|
||||
|
||||
Result CreateProcess64From32(ams::svc::Handle *out_handle, KUserPointer<const ams::svc::ilp32::CreateProcessParameter *> parameters, KUserPointer<const uint32_t *> caps, int32_t num_caps) {
|
||||
R_RETURN(CreateProcess(out_handle, parameters, caps, num_caps));
|
||||
}
|
||||
|
||||
Result StartProcess64From32(ams::svc::Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size) {
|
||||
R_RETURN(StartProcess(process_handle, priority, core_id, main_thread_stack_size));
|
||||
}
|
||||
|
||||
Result TerminateProcess64From32(ams::svc::Handle process_handle) {
|
||||
R_RETURN(TerminateProcess(process_handle));
|
||||
}
|
||||
|
||||
Result GetProcessInfo64From32(int64_t *out_info, ams::svc::Handle process_handle, ams::svc::ProcessInfoType info_type) {
|
||||
R_RETURN(GetProcessInfo(out_info, process_handle, info_type));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidProcessMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case ams::svc::MemoryPermission_None:
|
||||
case ams::svc::MemoryPermission_Read:
|
||||
case ams::svc::MemoryPermission_ReadWrite:
|
||||
case ams::svc::MemoryPermission_ReadExecute:
|
||||
case ams::svc::MemoryPermission_Execute:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result SetProcessMemoryPermission(ams::svc::Handle process_handle, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(address == static_cast<uintptr_t>(address), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(size == static_cast<size_t>(size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Validate the memory permission. */
|
||||
R_UNLESS(IsValidProcessMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission());
|
||||
|
||||
/* Get the process from its handle. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Validate that the address is in range. */
|
||||
auto &page_table = process->GetPageTable();
|
||||
R_UNLESS(page_table.Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Set the memory permission. */
|
||||
R_RETURN(page_table.SetProcessMemoryPermission(address, size, perm));
|
||||
}
|
||||
|
||||
Result MapProcessMemory(uintptr_t dst_address, ams::svc::Handle process_handle, uint64_t src_address, size_t size) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(util::IsAligned(dst_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(src_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((dst_address < dst_address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS((src_address < src_address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(src_address == static_cast<uintptr_t>(src_address), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the processes. */
|
||||
KProcess *dst_process = GetCurrentProcessPointer();
|
||||
KScopedAutoObject src_process = dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
|
||||
R_UNLESS(src_process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the page tables. */
|
||||
auto &dst_pt = dst_process->GetPageTable();
|
||||
auto &src_pt = src_process->GetPageTable();
|
||||
|
||||
/* Validate that the mapping is in range. */
|
||||
R_UNLESS(src_pt.Contains(src_address, size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState_SharedCode), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Create a new page group. */
|
||||
KPageGroup pg(dst_pt.GetBlockInfoManager());
|
||||
|
||||
/* Make the page group. */
|
||||
R_TRY(src_pt.MakeAndOpenPageGroup(std::addressof(pg),
|
||||
src_address, size / PageSize,
|
||||
KMemoryState_FlagCanMapProcess, KMemoryState_FlagCanMapProcess,
|
||||
KMemoryPermission_None, KMemoryPermission_None,
|
||||
KMemoryAttribute_All, KMemoryAttribute_None));
|
||||
|
||||
/* Close the page group when we're done. */
|
||||
ON_SCOPE_EXIT { pg.Close(); };
|
||||
|
||||
/* Map the group. */
|
||||
R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState_SharedCode, KMemoryPermission_UserReadWrite));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result UnmapProcessMemory(uintptr_t dst_address, ams::svc::Handle process_handle, uint64_t src_address, size_t size) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(util::IsAligned(dst_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(src_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((dst_address < dst_address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS((src_address < src_address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(src_address == static_cast<uintptr_t>(src_address), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the processes. */
|
||||
KProcess *dst_process = GetCurrentProcessPointer();
|
||||
KScopedAutoObject src_process = dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
|
||||
R_UNLESS(src_process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the page tables. */
|
||||
auto &dst_pt = dst_process->GetPageTable();
|
||||
auto &src_pt = src_process->GetPageTable();
|
||||
|
||||
/* Validate that the mapping is in range. */
|
||||
R_UNLESS(src_pt.Contains(src_address, size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState_SharedCode), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Unmap the memory. */
|
||||
R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MapProcessCodeMemory(ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(util::IsAligned(dst_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(src_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((dst_address < dst_address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS((src_address < src_address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(src_address == static_cast<uintptr_t>(src_address), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(dst_address == static_cast<uintptr_t>(dst_address), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(size == static_cast<size_t>(size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the process from its handle. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Validate that the mapping is in range. */
|
||||
auto &page_table = process->GetPageTable();
|
||||
R_UNLESS(page_table.Contains(src_address, size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(page_table.CanContain(dst_address, size, KMemoryState_AliasCode), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Map the memory. */
|
||||
R_TRY(page_table.MapCodeMemory(dst_address, src_address, size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result UnmapProcessCodeMemory(ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(util::IsAligned(dst_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(src_address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((dst_address < dst_address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS((src_address < src_address + size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(src_address == static_cast<uintptr_t>(src_address), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(dst_address == static_cast<uintptr_t>(dst_address), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(size == static_cast<size_t>(size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the process from its handle. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Validate that the mapping is in range. */
|
||||
auto &page_table = process->GetPageTable();
|
||||
R_UNLESS(page_table.Contains(src_address, size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(page_table.CanContain(dst_address, size, KMemoryState_AliasCode), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Unmap the memory. */
|
||||
R_TRY(page_table.UnmapCodeMemory(dst_address, src_address, size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result SetProcessMemoryPermission64(ams::svc::Handle process_handle, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) {
|
||||
R_RETURN(SetProcessMemoryPermission(process_handle, address, size, perm));
|
||||
}
|
||||
|
||||
Result MapProcessMemory64(ams::svc::Address dst_address, ams::svc::Handle process_handle, uint64_t src_address, ams::svc::Size size) {
|
||||
R_RETURN(MapProcessMemory(dst_address, process_handle, src_address, size));
|
||||
}
|
||||
|
||||
Result UnmapProcessMemory64(ams::svc::Address dst_address, ams::svc::Handle process_handle, uint64_t src_address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapProcessMemory(dst_address, process_handle, src_address, size));
|
||||
}
|
||||
|
||||
Result MapProcessCodeMemory64(ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) {
|
||||
R_RETURN(MapProcessCodeMemory(process_handle, dst_address, src_address, size));
|
||||
}
|
||||
|
||||
Result UnmapProcessCodeMemory64(ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) {
|
||||
R_RETURN(UnmapProcessCodeMemory(process_handle, dst_address, src_address, size));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result SetProcessMemoryPermission64From32(ams::svc::Handle process_handle, uint64_t address, uint64_t size, ams::svc::MemoryPermission perm) {
|
||||
R_RETURN(SetProcessMemoryPermission(process_handle, address, size, perm));
|
||||
}
|
||||
|
||||
Result MapProcessMemory64From32(ams::svc::Address dst_address, ams::svc::Handle process_handle, uint64_t src_address, ams::svc::Size size) {
|
||||
R_RETURN(MapProcessMemory(dst_address, process_handle, src_address, size));
|
||||
}
|
||||
|
||||
Result UnmapProcessMemory64From32(ams::svc::Address dst_address, ams::svc::Handle process_handle, uint64_t src_address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapProcessMemory(dst_address, process_handle, src_address, size));
|
||||
}
|
||||
|
||||
Result MapProcessCodeMemory64From32(ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) {
|
||||
R_RETURN(MapProcessCodeMemory(process_handle, dst_address, src_address, size));
|
||||
}
|
||||
|
||||
Result UnmapProcessCodeMemory64From32(ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) {
|
||||
R_RETURN(UnmapProcessCodeMemory(process_handle, dst_address, src_address, size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
int32_t GetCurrentProcessorNumber() {
|
||||
/* Setup variables to track affinity information. */
|
||||
s32 current_phys_core;
|
||||
u64 v_affinity_mask = 0;
|
||||
|
||||
/* Forever try to get the affinity. */
|
||||
while (true) {
|
||||
/* Update affinity information if we've run out. */
|
||||
while (v_affinity_mask == 0) {
|
||||
current_phys_core = GetCurrentCoreId();
|
||||
v_affinity_mask = GetCurrentThread().GetVirtualAffinityMask();
|
||||
if ((v_affinity_mask & (1ul << current_phys_core)) != 0) {
|
||||
return current_phys_core;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the next virtual bit. */
|
||||
do {
|
||||
const s32 next_virt_core = static_cast<s32>(__builtin_ctzll(v_affinity_mask));
|
||||
if (current_phys_core == cpu::VirtualToPhysicalCoreMap[next_virt_core]) {
|
||||
return next_virt_core;
|
||||
}
|
||||
|
||||
v_affinity_mask &= ~(1ul << next_virt_core);
|
||||
} while (v_affinity_mask != 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
int32_t GetCurrentProcessorNumber64() {
|
||||
return GetCurrentProcessorNumber();
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
int32_t GetCurrentProcessorNumber64From32() {
|
||||
return GetCurrentProcessorNumber();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
Result QueryProcessMemory(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle process_handle, uintptr_t address) {
|
||||
/* Get the process. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Query the mapping's info. */
|
||||
KMemoryInfo info;
|
||||
R_TRY(process->GetPageTable().QueryInfo(std::addressof(info), out_page_info, address));
|
||||
|
||||
/* Write output. */
|
||||
*out_memory_info = info.GetSvcMemoryInfo();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result QueryProcessMemory(KUserPointer<T *> out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle process_handle, uint64_t address) {
|
||||
/* Get an ams::svc::MemoryInfo for the region. */
|
||||
ams::svc::MemoryInfo info = {};
|
||||
R_TRY(QueryProcessMemory(std::addressof(info), out_page_info, process_handle, address));
|
||||
|
||||
/* Copy the info to userspace. */
|
||||
if constexpr (std::same_as<T, ams::svc::MemoryInfo>) {
|
||||
R_TRY(out_memory_info.CopyFrom(std::addressof(info)));
|
||||
} else {
|
||||
/* Convert the info. */
|
||||
T converted_info = {};
|
||||
static_assert(std::same_as<decltype(T{}.base_address), decltype(ams::svc::MemoryInfo{}.base_address)>);
|
||||
static_assert(std::same_as<decltype(T{}.size), decltype(ams::svc::MemoryInfo{}.size)>);
|
||||
|
||||
converted_info.base_address = info.base_address;
|
||||
converted_info.size = info.size;
|
||||
converted_info.state = info.state;
|
||||
converted_info.attribute = info.attribute;
|
||||
converted_info.permission = info.permission;
|
||||
converted_info.ipc_count = info.ipc_count;
|
||||
converted_info.device_count = info.device_count;
|
||||
|
||||
/* Copy it. */
|
||||
R_TRY(out_memory_info.CopyFrom(std::addressof(converted_info)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
Result QueryMemory(KUserPointer<T *> out_memory_info, ams::svc::PageInfo *out_page_info, uintptr_t address) {
|
||||
/* Query memory is just QueryProcessMemory on the current process. */
|
||||
R_RETURN(QueryProcessMemory(out_memory_info, out_page_info, ams::svc::PseudoHandle::CurrentProcess, address));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result QueryMemory64(KUserPointer<ams::svc::lp64::MemoryInfo *> out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Address address) {
|
||||
R_RETURN(QueryMemory(out_memory_info, out_page_info, address));
|
||||
}
|
||||
|
||||
Result QueryProcessMemory64(KUserPointer<ams::svc::lp64::MemoryInfo *> out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle process_handle, uint64_t address) {
|
||||
R_RETURN(QueryProcessMemory(out_memory_info, out_page_info, process_handle, address));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result QueryMemory64From32(KUserPointer<ams::svc::ilp32::MemoryInfo *> out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Address address) {
|
||||
R_RETURN(QueryMemory(out_memory_info, out_page_info, address));
|
||||
}
|
||||
|
||||
Result QueryProcessMemory64From32(KUserPointer<ams::svc::ilp32::MemoryInfo *> out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle process_handle, uint64_t address) {
|
||||
R_RETURN(QueryProcessMemory(out_memory_info, out_page_info, process_handle, address));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
Result ReadWriteRegister(uint32_t *out, ams::svc::PhysicalAddress address, uint32_t mask, uint32_t value) {
|
||||
/* Clear the output unconditionally. */
|
||||
*out = 0;
|
||||
|
||||
/* Read/write the register. */
|
||||
R_RETURN(KSystemControl::ReadWriteRegister(out, address, mask, value));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result ReadWriteRegister64(uint32_t *out_value, ams::svc::PhysicalAddress address, uint32_t mask, uint32_t value) {
|
||||
R_RETURN(ReadWriteRegister(out_value, address, mask, value));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result ReadWriteRegister64From32(uint32_t *out_value, ams::svc::PhysicalAddress address, uint32_t mask, uint32_t value) {
|
||||
R_RETURN(ReadWriteRegister(out_value, address, mask, value));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidLimitableResource(ams::svc::LimitableResource which) {
|
||||
return which < ams::svc::LimitableResource_Count;
|
||||
}
|
||||
|
||||
Result GetResourceLimitLimitValue(int64_t *out_limit_value, ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which) {
|
||||
/* Validate the resource. */
|
||||
R_UNLESS(IsValidLimitableResource(which), svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Get the resource limit. */
|
||||
KScopedAutoObject resource_limit = GetCurrentProcess().GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
|
||||
R_UNLESS(resource_limit.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the limit value. */
|
||||
*out_limit_value = resource_limit->GetLimitValue(which);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetResourceLimitCurrentValue(int64_t *out_current_value, ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which) {
|
||||
/* Validate the resource. */
|
||||
R_UNLESS(IsValidLimitableResource(which), svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Get the resource limit. */
|
||||
KScopedAutoObject resource_limit = GetCurrentProcess().GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
|
||||
R_UNLESS(resource_limit.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the current value. */
|
||||
*out_current_value = resource_limit->GetCurrentValue(which);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetResourceLimitPeakValue(int64_t *out_peak_value, ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which) {
|
||||
/* Validate the resource. */
|
||||
R_UNLESS(IsValidLimitableResource(which), svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Get the resource limit. */
|
||||
KScopedAutoObject resource_limit = GetCurrentProcess().GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
|
||||
R_UNLESS(resource_limit.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the peak value. */
|
||||
*out_peak_value = resource_limit->GetPeakValue(which);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateResourceLimit(ams::svc::Handle *out_handle) {
|
||||
/* Create a new resource limit. */
|
||||
KResourceLimit *resource_limit = KResourceLimit::Create();
|
||||
R_UNLESS(resource_limit != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
/* Ensure we don't leak a reference to the limit. */
|
||||
ON_SCOPE_EXIT { resource_limit->Close(); };
|
||||
|
||||
/* Initialize the resource limit. */
|
||||
resource_limit->Initialize();
|
||||
|
||||
/* Register the limit. */
|
||||
KResourceLimit::Register(resource_limit);
|
||||
|
||||
/* Add the limit to the handle table. */
|
||||
R_TRY(GetCurrentProcess().GetHandleTable().Add(out_handle, resource_limit));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetResourceLimitLimitValue(ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which, int64_t limit_value) {
|
||||
/* Validate the resource. */
|
||||
R_UNLESS(IsValidLimitableResource(which), svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Get the resource limit. */
|
||||
KScopedAutoObject resource_limit = GetCurrentProcess().GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
|
||||
R_UNLESS(resource_limit.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Set the limit value. */
|
||||
R_TRY(resource_limit->SetLimitValue(which, limit_value));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result GetResourceLimitLimitValue64(int64_t *out_limit_value, ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which) {
|
||||
R_RETURN(GetResourceLimitLimitValue(out_limit_value, resource_limit_handle, which));
|
||||
}
|
||||
|
||||
Result GetResourceLimitCurrentValue64(int64_t *out_current_value, ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which) {
|
||||
R_RETURN(GetResourceLimitCurrentValue(out_current_value, resource_limit_handle, which));
|
||||
}
|
||||
|
||||
Result GetResourceLimitPeakValue64(int64_t *out_peak_value, ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which) {
|
||||
R_RETURN(GetResourceLimitPeakValue(out_peak_value, resource_limit_handle, which));
|
||||
}
|
||||
|
||||
Result CreateResourceLimit64(ams::svc::Handle *out_handle) {
|
||||
R_RETURN(CreateResourceLimit(out_handle));
|
||||
}
|
||||
|
||||
Result SetResourceLimitLimitValue64(ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which, int64_t limit_value) {
|
||||
R_RETURN(SetResourceLimitLimitValue(resource_limit_handle, which, limit_value));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result GetResourceLimitLimitValue64From32(int64_t *out_limit_value, ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which) {
|
||||
R_RETURN(GetResourceLimitLimitValue(out_limit_value, resource_limit_handle, which));
|
||||
}
|
||||
|
||||
Result GetResourceLimitCurrentValue64From32(int64_t *out_current_value, ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which) {
|
||||
R_RETURN(GetResourceLimitCurrentValue(out_current_value, resource_limit_handle, which));
|
||||
}
|
||||
|
||||
Result GetResourceLimitPeakValue64From32(int64_t *out_peak_value, ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which) {
|
||||
R_RETURN(GetResourceLimitPeakValue(out_peak_value, resource_limit_handle, which));
|
||||
}
|
||||
|
||||
Result CreateResourceLimit64From32(ams::svc::Handle *out_handle) {
|
||||
R_RETURN(CreateResourceLimit(out_handle));
|
||||
}
|
||||
|
||||
Result SetResourceLimitLimitValue64From32(ams::svc::Handle resource_limit_handle, ams::svc::LimitableResource which, int64_t limit_value) {
|
||||
R_RETURN(SetResourceLimitLimitValue(resource_limit_handle, which, limit_value));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
void CallSecureMonitor64(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||
KSystemControl::CallSecureMonitorFromUser(args);
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
/* CallSecureMonitor64From32 is not supported. */
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
Result CreateSession(ams::svc::Handle *out_server, ams::svc::Handle *out_client, uintptr_t name) {
|
||||
/* Get the current process and handle table. */
|
||||
auto &process = GetCurrentProcess();
|
||||
auto &handle_table = process.GetHandleTable();
|
||||
|
||||
/* Declare the session we're going to allocate. */
|
||||
T *session;
|
||||
|
||||
/* Reserve a new session from the process resource limit. */
|
||||
KScopedResourceReservation session_reservation(std::addressof(process), ams::svc::LimitableResource_SessionCountMax);
|
||||
if (session_reservation.Succeeded()) {
|
||||
/* Allocate a session normally. */
|
||||
session = T::Create();
|
||||
} else {
|
||||
/* We couldn't reserve a session. Check that we support dynamically expanding the resource limit. */
|
||||
R_UNLESS(process.GetResourceLimit() == std::addressof(Kernel::GetSystemResourceLimit()), svc::ResultLimitReached());
|
||||
R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), svc::ResultLimitReached());
|
||||
|
||||
/* Try to allocate a session from unused slab memory. */
|
||||
session = T::CreateFromUnusedSlabMemory();
|
||||
R_UNLESS(session != nullptr, svc::ResultLimitReached());
|
||||
ON_RESULT_FAILURE { session->Close(); };
|
||||
|
||||
/* If we're creating a KSession, we want to add two KSessionRequests to the heap, to prevent request exhaustion. */
|
||||
/* NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's no reason to not do this statically. */
|
||||
if constexpr (std::same_as<T, KSession>) {
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
KSessionRequest *request = KSessionRequest::CreateFromUnusedSlabMemory();
|
||||
R_UNLESS(request != nullptr, svc::ResultLimitReached());
|
||||
|
||||
request->Close();
|
||||
}
|
||||
}
|
||||
|
||||
/* We successfully allocated a session, so add the object we allocated to the resource limit. */
|
||||
Kernel::GetSystemResourceLimit().Add(ams::svc::LimitableResource_SessionCountMax, 1);
|
||||
}
|
||||
|
||||
/* Check that we successfully created a session. */
|
||||
R_UNLESS(session != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
/* Initialize the session. */
|
||||
session->Initialize(nullptr, name);
|
||||
|
||||
/* Commit the session reservation. */
|
||||
session_reservation.Commit();
|
||||
|
||||
/* Ensure that we clean up the session (and its only references are handle table) on function end. */
|
||||
ON_SCOPE_EXIT {
|
||||
session->GetClientSession().Close();
|
||||
session->GetServerSession().Close();
|
||||
};
|
||||
|
||||
/* Register the session. */
|
||||
T::Register(session);
|
||||
|
||||
/* Add the server session to the handle table. */
|
||||
R_TRY(handle_table.Add(out_server, std::addressof(session->GetServerSession())));
|
||||
|
||||
/* Ensure that we maintaing a clean handle state on exit. */
|
||||
ON_RESULT_FAILURE { handle_table.Remove(*out_server); };
|
||||
|
||||
/* Add the client session to the handle table. */
|
||||
R_RETURN(handle_table.Add(out_client, std::addressof(session->GetClientSession())));
|
||||
}
|
||||
|
||||
Result CreateSession(ams::svc::Handle *out_server, ams::svc::Handle *out_client, bool is_light, uintptr_t name) {
|
||||
if (is_light) {
|
||||
R_RETURN(CreateSession<KLightSession>(out_server, out_client, name));
|
||||
} else {
|
||||
R_RETURN(CreateSession<KSession>(out_server, out_client, name));
|
||||
}
|
||||
}
|
||||
|
||||
Result AcceptSession(ams::svc::Handle *out, ams::svc::Handle port_handle) {
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Get the server port. */
|
||||
KScopedAutoObject port = handle_table.GetObject<KServerPort>(port_handle);
|
||||
R_UNLESS(port.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Reserve an entry for the new session. */
|
||||
R_TRY(handle_table.Reserve(out));
|
||||
ON_RESULT_FAILURE { handle_table.Unreserve(*out); };
|
||||
|
||||
/* Accept the session. */
|
||||
KAutoObject *session;
|
||||
if (port->IsLight()) {
|
||||
session = port->AcceptLightSession();
|
||||
} else {
|
||||
session = port->AcceptSession();
|
||||
}
|
||||
|
||||
/* Ensure we accepted successfully. */
|
||||
R_UNLESS(session != nullptr, svc::ResultNotFound());
|
||||
|
||||
/* Register the session. */
|
||||
handle_table.Register(*out, session);
|
||||
session->Close();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result CreateSession64(ams::svc::Handle *out_server_session_handle, ams::svc::Handle *out_client_session_handle, bool is_light, ams::svc::Address name) {
|
||||
R_RETURN(CreateSession(out_server_session_handle, out_client_session_handle, is_light, name));
|
||||
}
|
||||
|
||||
Result AcceptSession64(ams::svc::Handle *out_handle, ams::svc::Handle port) {
|
||||
R_RETURN(AcceptSession(out_handle, port));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result CreateSession64From32(ams::svc::Handle *out_server_session_handle, ams::svc::Handle *out_client_session_handle, bool is_light, ams::svc::Address name) {
|
||||
R_RETURN(CreateSession(out_server_session_handle, out_client_session_handle, is_light, name));
|
||||
}
|
||||
|
||||
Result AcceptSession64From32(ams::svc::Handle *out_handle, ams::svc::Handle port) {
|
||||
R_RETURN(AcceptSession(out_handle, port));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidSharedMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case ams::svc::MemoryPermission_Read:
|
||||
case ams::svc::MemoryPermission_ReadWrite:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsValidRemoteSharedMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||
return IsValidSharedMemoryPermission(perm) || perm == ams::svc::MemoryPermission_DontCare;
|
||||
}
|
||||
|
||||
Result MapSharedMemory(ams::svc::Handle shmem_handle, uintptr_t address, size_t size, ams::svc::MemoryPermission map_perm) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Validate the permission. */
|
||||
R_UNLESS(IsValidSharedMemoryPermission(map_perm), svc::ResultInvalidNewMemoryPermission());
|
||||
|
||||
/* Get the current process. */
|
||||
auto &process = GetCurrentProcess();
|
||||
auto &page_table = process.GetPageTable();
|
||||
|
||||
/* Get the shared memory. */
|
||||
KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
|
||||
R_UNLESS(shmem.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify that the mapping is in range. */
|
||||
R_UNLESS(page_table.CanContain(address, size, KMemoryState_Shared), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Add the shared memory to the process. */
|
||||
R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size));
|
||||
|
||||
/* Ensure that we clean up the shared memory if we fail to map it. */
|
||||
ON_RESULT_FAILURE { process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); };
|
||||
|
||||
/* Map the shared memory. */
|
||||
R_RETURN(shmem->Map(std::addressof(page_table), address, size, std::addressof(process), map_perm));
|
||||
}
|
||||
|
||||
Result UnmapSharedMemory(ams::svc::Handle shmem_handle, uintptr_t address, size_t size) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the current process. */
|
||||
auto &process = GetCurrentProcess();
|
||||
auto &page_table = process.GetPageTable();
|
||||
|
||||
/* Get the shared memory. */
|
||||
KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
|
||||
R_UNLESS(shmem.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify that the mapping is in range. */
|
||||
R_UNLESS(page_table.CanContain(address, size, KMemoryState_Shared), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Unmap the shared memory. */
|
||||
R_TRY(shmem->Unmap(std::addressof(page_table), address, size, std::addressof(process)));
|
||||
|
||||
/* Remove the shared memory from the process. */
|
||||
process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateSharedMemory(ams::svc::Handle *out, size_t size, ams::svc::MemoryPermission owner_perm, ams::svc::MemoryPermission remote_perm) {
|
||||
/* Validate the size. */
|
||||
R_UNLESS(0 < size && size < kern::MainMemorySizeMax, svc::ResultInvalidSize());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
|
||||
/* Validate the permissions. */
|
||||
R_UNLESS(IsValidSharedMemoryPermission(owner_perm), svc::ResultInvalidNewMemoryPermission());
|
||||
R_UNLESS(IsValidRemoteSharedMemoryPermission(remote_perm), svc::ResultInvalidNewMemoryPermission());
|
||||
|
||||
/* Create the shared memory. */
|
||||
KSharedMemory *shmem = KSharedMemory::Create();
|
||||
R_UNLESS(shmem != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
/* Ensure the only reference is in the handle table when we're done. */
|
||||
ON_SCOPE_EXIT { shmem->Close(); };
|
||||
|
||||
/* Initialize the shared memory. */
|
||||
R_TRY(shmem->Initialize(GetCurrentProcessPointer(), size, owner_perm, remote_perm));
|
||||
|
||||
/* Register the shared memory. */
|
||||
KSharedMemory::Register(shmem);
|
||||
|
||||
/* Add the shared memory to the handle table. */
|
||||
R_TRY(GetCurrentProcess().GetHandleTable().Add(out, shmem));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result MapSharedMemory64(ams::svc::Handle shmem_handle, ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission map_perm) {
|
||||
R_RETURN(MapSharedMemory(shmem_handle, address, size, map_perm));
|
||||
}
|
||||
|
||||
Result UnmapSharedMemory64(ams::svc::Handle shmem_handle, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapSharedMemory(shmem_handle, address, size));
|
||||
}
|
||||
|
||||
Result CreateSharedMemory64(ams::svc::Handle *out_handle, ams::svc::Size size, ams::svc::MemoryPermission owner_perm, ams::svc::MemoryPermission remote_perm) {
|
||||
R_RETURN(CreateSharedMemory(out_handle, size, owner_perm, remote_perm));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result MapSharedMemory64From32(ams::svc::Handle shmem_handle, ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission map_perm) {
|
||||
R_RETURN(MapSharedMemory(shmem_handle, address, size, map_perm));
|
||||
}
|
||||
|
||||
Result UnmapSharedMemory64From32(ams::svc::Handle shmem_handle, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapSharedMemory(shmem_handle, address, size));
|
||||
}
|
||||
|
||||
Result CreateSharedMemory64From32(ams::svc::Handle *out_handle, ams::svc::Size size, ams::svc::MemoryPermission owner_perm, ams::svc::MemoryPermission remote_perm) {
|
||||
R_RETURN(CreateSharedMemory(out_handle, size, owner_perm, remote_perm));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
Result CloseHandle(ams::svc::Handle handle) {
|
||||
/* Remove the handle. */
|
||||
R_UNLESS(GetCurrentProcess().GetHandleTable().Remove(handle), svc::ResultInvalidHandle());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ResetSignal(ams::svc::Handle handle) {
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Try to reset as readable event. */
|
||||
{
|
||||
KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle);
|
||||
if (readable_event.IsNotNull()) {
|
||||
if (auto * const interrupt_event = readable_event->DynamicCast<KInterruptEvent *>(); interrupt_event != nullptr) {
|
||||
R_RETURN(interrupt_event->Reset());
|
||||
} else {
|
||||
R_RETURN(readable_event->Reset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to reset as process. */
|
||||
{
|
||||
KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
|
||||
if (process.IsNotNull()) {
|
||||
R_RETURN(process->Reset());
|
||||
}
|
||||
}
|
||||
|
||||
R_THROW(svc::ResultInvalidHandle());
|
||||
}
|
||||
|
||||
Result WaitSynchronizationImpl(int32_t *out_index, KSynchronizationObject **objs, int32_t num_handles, int64_t timeout_ns) {
|
||||
/* Convert the timeout from nanoseconds to ticks. */
|
||||
s64 timeout;
|
||||
if (timeout_ns > 0) {
|
||||
u64 ticks = KHardwareTimer::GetTick();
|
||||
ticks += ams::svc::Tick(TimeSpan::FromNanoSeconds(timeout_ns));
|
||||
ticks += 2;
|
||||
|
||||
timeout = ticks;
|
||||
} else {
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
|
||||
R_RETURN(KSynchronizationObject::Wait(out_index, objs, num_handles, timeout));
|
||||
}
|
||||
|
||||
Result WaitSynchronization(int32_t *out_index, KUserPointer<const ams::svc::Handle *> user_handles, int32_t num_handles, int64_t timeout_ns) {
|
||||
/* Ensure number of handles is valid. */
|
||||
R_UNLESS(0 <= num_handles && num_handles <= ams::svc::ArgumentHandleCountMax, svc::ResultOutOfRange());
|
||||
|
||||
/* Get the synchronization context. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
KSynchronizationObject **objs = GetCurrentThread().GetSynchronizationObjectBuffer();
|
||||
ams::svc::Handle *handles = GetCurrentThread().GetHandleBuffer();
|
||||
|
||||
/* Copy user handles. */
|
||||
if (num_handles > 0) {
|
||||
/* Ensure that we can try to get the handles. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(KProcessAddress(user_handles.GetUnsafePointer()), num_handles * sizeof(ams::svc::Handle)), svc::ResultInvalidPointer());
|
||||
|
||||
/* Get the handles. */
|
||||
R_TRY(user_handles.CopyArrayTo(handles, num_handles));
|
||||
|
||||
/* Convert the handles to objects. */
|
||||
R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles), svc::ResultInvalidHandle());
|
||||
}
|
||||
|
||||
/* Ensure handles are closed when we're done. */
|
||||
ON_SCOPE_EXIT {
|
||||
for (auto i = 0; i < num_handles; ++i) {
|
||||
objs[i]->Close();
|
||||
}
|
||||
};
|
||||
|
||||
/* Wait on the objects. */
|
||||
R_TRY_CATCH(WaitSynchronizationImpl(out_index, objs, num_handles, timeout_ns)) {
|
||||
R_CONVERT(svc::ResultSessionClosed, ResultSuccess())
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CancelSynchronization(ams::svc::Handle handle) {
|
||||
/* Get the thread from its handle. */
|
||||
KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject<KThread>(handle);
|
||||
R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Cancel the thread's wait. */
|
||||
thread->WaitCancel();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SynchronizePreemptionState() {
|
||||
/* Lock the scheduler. */
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
/* If the current thread is pinned, unpin it. */
|
||||
KProcess *cur_process = GetCurrentProcessPointer();
|
||||
if (cur_process->GetPinnedThread(GetCurrentCoreId()) == GetCurrentThreadPointer()) {
|
||||
/* Clear the current thread's interrupt flag. */
|
||||
GetCurrentThread().ClearInterruptFlag();
|
||||
|
||||
/* Unpin the current thread. */
|
||||
cur_process->UnpinCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result CloseHandle64(ams::svc::Handle handle) {
|
||||
R_RETURN(CloseHandle(handle));
|
||||
}
|
||||
|
||||
Result ResetSignal64(ams::svc::Handle handle) {
|
||||
R_RETURN(ResetSignal(handle));
|
||||
}
|
||||
|
||||
Result WaitSynchronization64(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, int64_t timeout_ns) {
|
||||
R_RETURN(WaitSynchronization(out_index, handles, num_handles, timeout_ns));
|
||||
}
|
||||
|
||||
Result CancelSynchronization64(ams::svc::Handle handle) {
|
||||
R_RETURN(CancelSynchronization(handle));
|
||||
}
|
||||
|
||||
void SynchronizePreemptionState64() {
|
||||
return SynchronizePreemptionState();
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result CloseHandle64From32(ams::svc::Handle handle) {
|
||||
R_RETURN(CloseHandle(handle));
|
||||
}
|
||||
|
||||
Result ResetSignal64From32(ams::svc::Handle handle) {
|
||||
R_RETURN(ResetSignal(handle));
|
||||
}
|
||||
|
||||
Result WaitSynchronization64From32(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, int64_t timeout_ns) {
|
||||
R_RETURN(WaitSynchronization(out_index, handles, num_handles, timeout_ns));
|
||||
}
|
||||
|
||||
Result CancelSynchronization64From32(ams::svc::Handle handle) {
|
||||
R_RETURN(CancelSynchronization(handle));
|
||||
}
|
||||
|
||||
void SynchronizePreemptionState64From32() {
|
||||
return SynchronizePreemptionState();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
|
||||
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
|
||||
}
|
||||
|
||||
Result CreateThread(ams::svc::Handle *out, ams::svc::ThreadFunc f, uintptr_t arg, uintptr_t stack_bottom, int32_t priority, int32_t core_id) {
|
||||
/* Adjust core id, if it's the default magic. */
|
||||
KProcess &process = GetCurrentProcess();
|
||||
if (core_id == ams::svc::IdealCoreUseProcessValue) {
|
||||
core_id = process.GetIdealCoreId();
|
||||
}
|
||||
|
||||
/* Validate arguments. */
|
||||
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
|
||||
R_UNLESS(((1ul << core_id) & process.GetCoreMask()) != 0, svc::ResultInvalidCoreId());
|
||||
|
||||
R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority());
|
||||
R_UNLESS(process.CheckThreadPriority(priority), svc::ResultInvalidPriority());
|
||||
|
||||
/* Reserve a new thread from the process resource limit (waiting up to 100ms). */
|
||||
KScopedResourceReservation thread_reservation(std::addressof(process), ams::svc::LimitableResource_ThreadCountMax, 1, KHardwareTimer::GetTick() + ams::svc::Tick(TimeSpan::FromMilliSeconds(100)));
|
||||
R_UNLESS(thread_reservation.Succeeded(), svc::ResultLimitReached());
|
||||
|
||||
/* Create the thread. */
|
||||
KThread *thread = KThread::Create();
|
||||
R_UNLESS(thread != nullptr, svc::ResultOutOfResource());
|
||||
ON_SCOPE_EXIT { thread->Close(); };
|
||||
|
||||
/* Initialize the thread. */
|
||||
{
|
||||
KScopedLightLock lk(process.GetStateLock());
|
||||
R_TRY(KThread::InitializeUserThread(thread, reinterpret_cast<KThreadFunction>(static_cast<uintptr_t>(f)), arg, stack_bottom, priority, core_id, std::addressof(process)));
|
||||
}
|
||||
|
||||
/* Commit the thread reservation. */
|
||||
thread_reservation.Commit();
|
||||
|
||||
/* Clone the current fpu status to the new thread. */
|
||||
thread->GetContext().CloneFpuStatus();
|
||||
|
||||
/* Register the new thread. */
|
||||
KThread::Register(thread);
|
||||
|
||||
/* Add the thread to the handle table. */
|
||||
R_TRY(process.GetHandleTable().Add(out, thread));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result StartThread(ams::svc::Handle thread_handle) {
|
||||
/* Get the thread from its handle. */
|
||||
KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject<KThread>(thread_handle);
|
||||
R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Try to start the thread. */
|
||||
R_RETURN(thread->Run());
|
||||
}
|
||||
|
||||
void ExitThread() {
|
||||
GetCurrentThread().Exit();
|
||||
MESOSPHERE_PANIC("Thread survived call to exit");
|
||||
}
|
||||
|
||||
void SleepThread(int64_t ns) {
|
||||
/* When the input tick is positive, sleep. */
|
||||
if (AMS_LIKELY(ns > 0)) {
|
||||
/* Convert the timeout from nanoseconds to ticks. */
|
||||
/* NOTE: Nintendo does not use this conversion logic in WaitSynchronization... */
|
||||
s64 timeout;
|
||||
|
||||
const ams::svc::Tick offset_tick(TimeSpan::FromNanoSeconds(ns));
|
||||
if (AMS_LIKELY(offset_tick > 0)) {
|
||||
timeout = KHardwareTimer::GetTick() + offset_tick + 2;
|
||||
if (AMS_UNLIKELY(timeout <= 0)) {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
|
||||
/* Sleep. */
|
||||
/* NOTE: Nintendo does not check the result of this sleep. */
|
||||
GetCurrentThread().Sleep(timeout);
|
||||
} else if (ns == ams::svc::YieldType_WithoutCoreMigration) {
|
||||
KScheduler::YieldWithoutCoreMigration();
|
||||
} else if (ns == ams::svc::YieldType_WithCoreMigration) {
|
||||
KScheduler::YieldWithCoreMigration();
|
||||
} else if (ns == ams::svc::YieldType_ToAnyThread) {
|
||||
KScheduler::YieldToAnyThread();
|
||||
} else {
|
||||
/* Nintendo does nothing at all if an otherwise invalid value is passed. */
|
||||
}
|
||||
}
|
||||
|
||||
Result GetThreadPriority(int32_t *out_priority, ams::svc::Handle thread_handle) {
|
||||
/* Get the thread from its handle. */
|
||||
KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject<KThread>(thread_handle);
|
||||
R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the thread's priority. */
|
||||
*out_priority = thread->GetPriority();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetThreadPriority(ams::svc::Handle thread_handle, int32_t priority) {
|
||||
/* Get the current process. */
|
||||
KProcess &process = GetCurrentProcess();
|
||||
|
||||
/* Get the thread from its handle. */
|
||||
KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
|
||||
R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Validate the thread is owned by the current process. */
|
||||
R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Validate the priority. */
|
||||
R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority());
|
||||
R_UNLESS(process.CheckThreadPriority(priority), svc::ResultInvalidPriority());
|
||||
|
||||
/* Set the thread priority. */
|
||||
thread->SetBasePriority(priority);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetThreadCoreMask(int32_t *out_core_id, uint64_t *out_affinity_mask, ams::svc::Handle thread_handle) {
|
||||
/* Get the thread from its handle. */
|
||||
KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject<KThread>(thread_handle);
|
||||
R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the core mask. */
|
||||
R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetThreadCoreMask(ams::svc::Handle thread_handle, int32_t core_id, uint64_t affinity_mask) {
|
||||
/* Get the thread from its handle. */
|
||||
KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject<KThread>(thread_handle);
|
||||
R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Validate the thread is owned by the current process. */
|
||||
R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Determine the core id/affinity mask. */
|
||||
if (core_id == ams::svc::IdealCoreUseProcessValue) {
|
||||
core_id = GetCurrentProcess().GetIdealCoreId();
|
||||
affinity_mask = (1ul << core_id);
|
||||
} else {
|
||||
/* Validate the affinity mask. */
|
||||
const u64 process_core_mask = GetCurrentProcess().GetCoreMask();
|
||||
R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, svc::ResultInvalidCoreId());
|
||||
R_UNLESS(affinity_mask != 0, svc::ResultInvalidCombination());
|
||||
|
||||
/* Validate the core id. */
|
||||
if (IsValidVirtualCoreId(core_id)) {
|
||||
R_UNLESS(((1ul << core_id) & affinity_mask) != 0, svc::ResultInvalidCombination());
|
||||
} else {
|
||||
R_UNLESS(core_id == ams::svc::IdealCoreNoUpdate || core_id == ams::svc::IdealCoreDontCare, svc::ResultInvalidCoreId());
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the core mask. */
|
||||
R_TRY(thread->SetCoreMask(core_id, affinity_mask));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetThreadId(uint64_t *out_thread_id, ams::svc::Handle thread_handle) {
|
||||
/* Get the thread from its handle. */
|
||||
KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject<KThread>(thread_handle);
|
||||
R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the thread's id. */
|
||||
*out_thread_id = thread->GetId();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetThreadContext3(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle thread_handle) {
|
||||
/* Get the thread from its handle. */
|
||||
KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject<KThread>(thread_handle);
|
||||
R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Require the handle be to a non-current thread in the current process. */
|
||||
R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(), svc::ResultInvalidHandle());
|
||||
R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(), svc::ResultBusy());
|
||||
|
||||
/* Get the thread context. */
|
||||
ams::svc::ThreadContext context = {};
|
||||
R_TRY(thread->GetThreadContext3(std::addressof(context)));
|
||||
|
||||
/* Copy the thread context to user space. */
|
||||
R_TRY(out_context.CopyFrom(std::addressof(context)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetThreadList(int32_t *out_num_threads, KUserPointer<uint64_t *> out_thread_ids, int32_t max_out_count, ams::svc::Handle debug_handle) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||
|
||||
/* Validate that the out count is valid. */
|
||||
R_UNLESS((0 <= max_out_count && max_out_count <= static_cast<int32_t>(std::numeric_limits<int32_t>::max() / sizeof(u64))), svc::ResultOutOfRange());
|
||||
|
||||
/* Validate that the pointer is in range. */
|
||||
if (max_out_count > 0) {
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(KProcessAddress(out_thread_ids.GetUnsafePointer()), max_out_count * sizeof(u64)), svc::ResultInvalidCurrentMemory());
|
||||
}
|
||||
|
||||
/* Get the handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Try to get as a debug object. */
|
||||
KScopedAutoObject debug = handle_table.GetObject<KDebug>(debug_handle);
|
||||
if (debug.IsNotNull()) {
|
||||
/* Check that the debug object has a process. */
|
||||
R_UNLESS(debug->IsAttached(), svc::ResultProcessTerminated());
|
||||
R_UNLESS(debug->OpenProcess(), svc::ResultProcessTerminated());
|
||||
ON_SCOPE_EXIT { debug->CloseProcess(); };
|
||||
|
||||
/* Get the thread list. */
|
||||
R_TRY(debug->GetProcessUnsafe()->GetThreadList(out_num_threads, out_thread_ids, max_out_count));
|
||||
} else {
|
||||
/* Only allow getting as a process (or global) if the caller does not have ForceDebugProd. */
|
||||
R_UNLESS(!GetCurrentProcess().CanForceDebugProd(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Try to get as a process. */
|
||||
KScopedAutoObject process = handle_table.GetObjectWithoutPseudoHandle<KProcess>(debug_handle);
|
||||
if (process.IsNotNull()) {
|
||||
/* Get the thread list. */
|
||||
R_TRY(process->GetThreadList(out_num_threads, out_thread_ids, max_out_count));
|
||||
} else {
|
||||
/* If the object is not a process, the caller may want the global thread list. */
|
||||
R_UNLESS(debug_handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||
|
||||
/* If passed invalid handle, we should return the global thread list. */
|
||||
R_TRY(KThread::GetThreadList(out_num_threads, out_thread_ids, max_out_count));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result CreateThread64(ams::svc::Handle *out_handle, ams::svc::ThreadFunc func, ams::svc::Address arg, ams::svc::Address stack_bottom, int32_t priority, int32_t core_id) {
|
||||
R_RETURN(CreateThread(out_handle, func, arg, stack_bottom, priority, core_id));
|
||||
}
|
||||
|
||||
Result StartThread64(ams::svc::Handle thread_handle) {
|
||||
R_RETURN(StartThread(thread_handle));
|
||||
}
|
||||
|
||||
void ExitThread64() {
|
||||
return ExitThread();
|
||||
}
|
||||
|
||||
void SleepThread64(int64_t ns) {
|
||||
return SleepThread(ns);
|
||||
}
|
||||
|
||||
Result GetThreadPriority64(int32_t *out_priority, ams::svc::Handle thread_handle) {
|
||||
R_RETURN(GetThreadPriority(out_priority, thread_handle));
|
||||
}
|
||||
|
||||
Result SetThreadPriority64(ams::svc::Handle thread_handle, int32_t priority) {
|
||||
R_RETURN(SetThreadPriority(thread_handle, priority));
|
||||
}
|
||||
|
||||
Result GetThreadCoreMask64(int32_t *out_core_id, uint64_t *out_affinity_mask, ams::svc::Handle thread_handle) {
|
||||
R_RETURN(GetThreadCoreMask(out_core_id, out_affinity_mask, thread_handle));
|
||||
}
|
||||
|
||||
Result SetThreadCoreMask64(ams::svc::Handle thread_handle, int32_t core_id, uint64_t affinity_mask) {
|
||||
R_RETURN(SetThreadCoreMask(thread_handle, core_id, affinity_mask));
|
||||
}
|
||||
|
||||
Result GetThreadId64(uint64_t *out_thread_id, ams::svc::Handle thread_handle) {
|
||||
R_RETURN(GetThreadId(out_thread_id, thread_handle));
|
||||
}
|
||||
|
||||
Result GetThreadContext364(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle thread_handle) {
|
||||
R_RETURN(GetThreadContext3(out_context, thread_handle));
|
||||
}
|
||||
|
||||
Result GetThreadList64(int32_t *out_num_threads, KUserPointer<uint64_t *> out_thread_ids, int32_t max_out_count, ams::svc::Handle debug_handle) {
|
||||
R_RETURN(GetThreadList(out_num_threads, out_thread_ids, max_out_count, debug_handle));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result CreateThread64From32(ams::svc::Handle *out_handle, ams::svc::ThreadFunc func, ams::svc::Address arg, ams::svc::Address stack_bottom, int32_t priority, int32_t core_id) {
|
||||
R_RETURN(CreateThread(out_handle, func, arg, stack_bottom, priority, core_id));
|
||||
}
|
||||
|
||||
Result StartThread64From32(ams::svc::Handle thread_handle) {
|
||||
R_RETURN(StartThread(thread_handle));
|
||||
}
|
||||
|
||||
void ExitThread64From32() {
|
||||
return ExitThread();
|
||||
}
|
||||
|
||||
void SleepThread64From32(int64_t ns) {
|
||||
return SleepThread(ns);
|
||||
}
|
||||
|
||||
Result GetThreadPriority64From32(int32_t *out_priority, ams::svc::Handle thread_handle) {
|
||||
R_RETURN(GetThreadPriority(out_priority, thread_handle));
|
||||
}
|
||||
|
||||
Result SetThreadPriority64From32(ams::svc::Handle thread_handle, int32_t priority) {
|
||||
R_RETURN(SetThreadPriority(thread_handle, priority));
|
||||
}
|
||||
|
||||
Result GetThreadCoreMask64From32(int32_t *out_core_id, uint64_t *out_affinity_mask, ams::svc::Handle thread_handle) {
|
||||
R_RETURN(GetThreadCoreMask(out_core_id, out_affinity_mask, thread_handle));
|
||||
}
|
||||
|
||||
Result SetThreadCoreMask64From32(ams::svc::Handle thread_handle, int32_t core_id, uint64_t affinity_mask) {
|
||||
R_RETURN(SetThreadCoreMask(thread_handle, core_id, affinity_mask));
|
||||
}
|
||||
|
||||
Result GetThreadId64From32(uint64_t *out_thread_id, ams::svc::Handle thread_handle) {
|
||||
R_RETURN(GetThreadId(out_thread_id, thread_handle));
|
||||
}
|
||||
|
||||
Result GetThreadContext364From32(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle thread_handle) {
|
||||
R_RETURN(GetThreadContext3(out_context, thread_handle));
|
||||
}
|
||||
|
||||
Result GetThreadList64From32(int32_t *out_num_threads, KUserPointer<uint64_t *> out_thread_ids, int32_t max_out_count, ams::svc::Handle debug_handle) {
|
||||
R_RETURN(GetThreadList(out_num_threads, out_thread_ids, max_out_count, debug_handle));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
Result GetLastThreadInfoImpl(ams::svc::LastThreadContext *out_context, uintptr_t *out_tls_address, uint32_t *out_flags) {
|
||||
/* Disable interrupts. */
|
||||
KScopedInterruptDisable di;
|
||||
|
||||
/* Get the previous thread. */
|
||||
KThread *prev_thread = Kernel::GetScheduler().GetPreviousThread();
|
||||
R_UNLESS(prev_thread != nullptr, svc::ResultNoThread());
|
||||
|
||||
/* Verify the last thread was owned by the current process. */
|
||||
R_UNLESS(prev_thread->GetOwnerProcess() == GetCurrentProcessPointer(), svc::ResultUnknownThread());
|
||||
|
||||
/* Clear the output flags. */
|
||||
*out_flags = 0;
|
||||
|
||||
/* Get the thread's exception context. */
|
||||
GetExceptionContext(prev_thread)->GetSvcThreadContext(out_context);
|
||||
|
||||
/* Get the tls address. */
|
||||
*out_tls_address = GetInteger(prev_thread->GetThreadLocalRegionAddress());
|
||||
|
||||
/* Set the syscall flag if appropriate. */
|
||||
if (prev_thread->IsCallingSvc()) {
|
||||
*out_flags |= ams::svc::LastThreadInfoFlag_ThreadInSystemCall;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SynchronizeCurrentProcessToFutureTime(int64_t ns) {
|
||||
/* Get the wait object. */
|
||||
KWaitObject *wait_object = GetCurrentProcess().GetWaitObjectPointer();
|
||||
|
||||
/* Convert the timeout from nanoseconds to ticks. */
|
||||
s64 timeout;
|
||||
if (ns > 0) {
|
||||
u64 ticks = KHardwareTimer::GetTick();
|
||||
ticks += ams::svc::Tick(TimeSpan::FromNanoSeconds(ns));
|
||||
ticks += 2;
|
||||
|
||||
timeout = ticks;
|
||||
} else {
|
||||
timeout = ns;
|
||||
}
|
||||
|
||||
/* Synchronize to the desired time. */
|
||||
R_TRY(wait_object->Synchronize(timeout));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetDebugFutureThreadInfo(ams::svc::LastThreadContext *out_context, uint64_t *out_thread_id, ams::svc::Handle debug_handle, int64_t ns) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNoThread());
|
||||
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Synchronize the current process to the desired time. */
|
||||
R_TRY(SynchronizeCurrentProcessToFutureTime(ns));
|
||||
|
||||
/* Get the running thread info. */
|
||||
R_TRY(debug->GetRunningThreadInfo(out_context, out_thread_id));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LegacyGetFutureThreadInfo(ams::svc::LastThreadContext *out_context, uintptr_t *out_tls_address, uint32_t *out_flags, int64_t ns) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNoThread());
|
||||
|
||||
/* Synchronize the current process to the desired time. */
|
||||
R_TRY(SynchronizeCurrentProcessToFutureTime(ns));
|
||||
|
||||
/* Get the thread info. */
|
||||
R_TRY(GetLastThreadInfoImpl(out_context, out_tls_address, out_flags));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetLastThreadInfo(ams::svc::LastThreadContext *out_context, uintptr_t *out_tls_address, uint32_t *out_flags) {
|
||||
/* Only allow invoking the svc on development hardware. */
|
||||
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNoThread());
|
||||
|
||||
/* Get the thread info. */
|
||||
R_TRY(GetLastThreadInfoImpl(out_context, out_tls_address, out_flags));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result GetDebugFutureThreadInfo64(ams::svc::lp64::LastThreadContext *out_context, uint64_t *out_thread_id, ams::svc::Handle debug_handle, int64_t ns) {
|
||||
R_RETURN(GetDebugFutureThreadInfo(out_context, out_thread_id, debug_handle, ns));
|
||||
}
|
||||
|
||||
Result LegacyGetFutureThreadInfo64(ams::svc::lp64::LastThreadContext *out_context, ams::svc::Address *out_tls_address, uint32_t *out_flags, int64_t ns) {
|
||||
R_RETURN(LegacyGetFutureThreadInfo(out_context, reinterpret_cast<uintptr_t *>(out_tls_address), out_flags, ns));
|
||||
}
|
||||
|
||||
Result GetLastThreadInfo64(ams::svc::lp64::LastThreadContext *out_context, ams::svc::Address *out_tls_address, uint32_t *out_flags) {
|
||||
static_assert(sizeof(*out_tls_address) == sizeof(uintptr_t));
|
||||
R_RETURN(GetLastThreadInfo(out_context, reinterpret_cast<uintptr_t *>(out_tls_address), out_flags));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result GetDebugFutureThreadInfo64From32(ams::svc::ilp32::LastThreadContext *out_context, uint64_t *out_thread_id, ams::svc::Handle debug_handle, int64_t ns) {
|
||||
ams::svc::LastThreadContext context = {};
|
||||
R_TRY(GetDebugFutureThreadInfo(std::addressof(context), out_thread_id, debug_handle, ns));
|
||||
|
||||
*out_context = {
|
||||
.fp = static_cast<u32>(context.fp),
|
||||
.sp = static_cast<u32>(context.sp),
|
||||
.lr = static_cast<u32>(context.lr),
|
||||
.pc = static_cast<u32>(context.pc),
|
||||
};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LegacyGetFutureThreadInfo64From32(ams::svc::ilp32::LastThreadContext *out_context, ams::svc::Address *out_tls_address, uint32_t *out_flags, int64_t ns) {
|
||||
static_assert(sizeof(*out_tls_address) == sizeof(uintptr_t));
|
||||
|
||||
ams::svc::LastThreadContext context = {};
|
||||
R_TRY(LegacyGetFutureThreadInfo(std::addressof(context), reinterpret_cast<uintptr_t *>(out_tls_address), out_flags, ns));
|
||||
|
||||
*out_context = {
|
||||
.fp = static_cast<u32>(context.fp),
|
||||
.sp = static_cast<u32>(context.sp),
|
||||
.lr = static_cast<u32>(context.lr),
|
||||
.pc = static_cast<u32>(context.pc),
|
||||
};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetLastThreadInfo64From32(ams::svc::ilp32::LastThreadContext *out_context, ams::svc::Address *out_tls_address, uint32_t *out_flags) {
|
||||
static_assert(sizeof(*out_tls_address) == sizeof(uintptr_t));
|
||||
|
||||
ams::svc::LastThreadContext context = {};
|
||||
R_TRY(GetLastThreadInfo(std::addressof(context), reinterpret_cast<uintptr_t *>(out_tls_address), out_flags));
|
||||
|
||||
*out_context = {
|
||||
.fp = static_cast<u32>(context.fp),
|
||||
.sp = static_cast<u32>(context.sp),
|
||||
.lr = static_cast<u32>(context.lr),
|
||||
.pc = static_cast<u32>(context.pc),
|
||||
};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
int64_t GetSystemTick() {
|
||||
return KHardwareTimer::GetTick();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
int64_t GetSystemTick64() {
|
||||
return GetSystemTick();
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
int64_t GetSystemTick64From32() {
|
||||
return GetSystemTick();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
/* ============================= Common ============================= */
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidTransferMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case ams::svc::MemoryPermission_None:
|
||||
case ams::svc::MemoryPermission_Read:
|
||||
case ams::svc::MemoryPermission_ReadWrite:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result MapTransferMemory(ams::svc::Handle trmem_handle, uintptr_t address, size_t size, ams::svc::MemoryPermission map_perm) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Validate the permission. */
|
||||
R_UNLESS(IsValidTransferMemoryPermission(map_perm), svc::ResultInvalidState());
|
||||
|
||||
/* Get the transfer memory. */
|
||||
KScopedAutoObject trmem = GetCurrentProcess().GetHandleTable().GetObject<KTransferMemory>(trmem_handle);
|
||||
R_UNLESS(trmem.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify that the mapping is in range. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_Transfered), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Map the transfer memory. */
|
||||
R_TRY(trmem->Map(address, size, map_perm));
|
||||
|
||||
/* We succeeded. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result UnmapTransferMemory(ams::svc::Handle trmem_handle, uintptr_t address, size_t size) {
|
||||
/* Validate the address/size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Get the transfer memory. */
|
||||
KScopedAutoObject trmem = GetCurrentProcess().GetHandleTable().GetObject<KTransferMemory>(trmem_handle);
|
||||
R_UNLESS(trmem.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Verify that the mapping is in range. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_Transfered), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Unmap the transfer memory. */
|
||||
R_TRY(trmem->Unmap(address, size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateTransferMemory(ams::svc::Handle *out, uintptr_t address, size_t size, ams::svc::MemoryPermission map_perm) {
|
||||
/* Validate the size. */
|
||||
R_UNLESS(util::IsAligned(address, PageSize), svc::ResultInvalidAddress());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Validate the permissions. */
|
||||
R_UNLESS(IsValidTransferMemoryPermission(map_perm), svc::ResultInvalidNewMemoryPermission());
|
||||
|
||||
/* Get the current process and handle table. */
|
||||
auto &process = GetCurrentProcess();
|
||||
auto &handle_table = process.GetHandleTable();
|
||||
|
||||
/* Reserve a new transfer memory from the process resource limit. */
|
||||
KScopedResourceReservation trmem_reservation(std::addressof(process), ams::svc::LimitableResource_TransferMemoryCountMax);
|
||||
R_UNLESS(trmem_reservation.Succeeded(), svc::ResultLimitReached());
|
||||
|
||||
/* Create the transfer memory. */
|
||||
KTransferMemory *trmem = KTransferMemory::Create();
|
||||
R_UNLESS(trmem != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
/* Ensure the only reference is in the handle table when we're done. */
|
||||
ON_SCOPE_EXIT { trmem->Close(); };
|
||||
|
||||
/* Ensure that the region is in range. */
|
||||
R_UNLESS(process.GetPageTable().Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Initialize the transfer memory. */
|
||||
R_TRY(trmem->Initialize(address, size, map_perm));
|
||||
|
||||
/* Commit the reservation. */
|
||||
trmem_reservation.Commit();
|
||||
|
||||
/* Register the transfer memory. */
|
||||
KTransferMemory::Register(trmem);
|
||||
|
||||
/* Add the transfer memory to the handle table. */
|
||||
R_TRY(handle_table.Add(out, trmem));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result MapTransferMemory64(ams::svc::Handle trmem_handle, ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission owner_perm) {
|
||||
R_RETURN(MapTransferMemory(trmem_handle, address, size, owner_perm));
|
||||
}
|
||||
|
||||
Result UnmapTransferMemory64(ams::svc::Handle trmem_handle, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapTransferMemory(trmem_handle, address, size));
|
||||
}
|
||||
|
||||
Result CreateTransferMemory64(ams::svc::Handle *out_handle, ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission map_perm) {
|
||||
R_RETURN(CreateTransferMemory(out_handle, address, size, map_perm));
|
||||
}
|
||||
|
||||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result MapTransferMemory64From32(ams::svc::Handle trmem_handle, ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission owner_perm) {
|
||||
R_RETURN(MapTransferMemory(trmem_handle, address, size, owner_perm));
|
||||
}
|
||||
|
||||
Result UnmapTransferMemory64From32(ams::svc::Handle trmem_handle, ams::svc::Address address, ams::svc::Size size) {
|
||||
R_RETURN(UnmapTransferMemory(trmem_handle, address, size));
|
||||
}
|
||||
|
||||
Result CreateTransferMemory64From32(ams::svc::Handle *out_handle, ams::svc::Address address, ams::svc::Size size, ams::svc::MemoryPermission map_perm) {
|
||||
R_RETURN(CreateTransferMemory(out_handle, address, size, map_perm));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user