Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_address_space_allocator.hpp"
|
||||
#include "os_rng_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u64 MaxProbabilityVariationInverseShift = 4;
|
||||
|
||||
}
|
||||
|
||||
u64 AddressSpaceAllocatorDefaultGenerateRandom(u64 max) {
|
||||
/* Check that max is in range. */
|
||||
AMS_ASSERT(max + 1 <= (UINT64_C(1) << (BITSIZEOF(u64) - MaxProbabilityVariationInverseShift)));
|
||||
|
||||
/* Generate random u64. */
|
||||
const u64 rand = GetRngManager().GenerateRandomU64();
|
||||
|
||||
/* Coerce into range. */
|
||||
AMS_ASSERT(max < std::numeric_limits<u64>::max());
|
||||
return rand % (max + 1);
|
||||
}
|
||||
|
||||
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
||||
AddressSpaceAllocatorBase<AddressType, SizeType>::AddressSpaceAllocatorBase(u64 start_address, u64 end_address, SizeType guard_size, const AddressSpaceAllocatorForbiddenRegion *forbidden_regions, size_t num_forbidden_regions) : m_critical_section(), m_forbidden_region_count(0) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(start_address >= guard_size);
|
||||
AMS_ASSERT(end_address + guard_size >= end_address);
|
||||
|
||||
/* Set member variables. */
|
||||
m_guard_page_count = util::DivideUp(guard_size, MemoryPageSize);
|
||||
m_start_page = start_address / MemoryPageSize;
|
||||
m_end_page = (end_address / MemoryPageSize) + m_guard_page_count;
|
||||
|
||||
/* Check forbidden region count. */
|
||||
AMS_ASSERT(num_forbidden_regions <= MaxForbiddenRegions);
|
||||
|
||||
/* Set forbidden regions. */
|
||||
for (size_t i = 0; i < num_forbidden_regions; ++i) {
|
||||
if (const auto ®ion = forbidden_regions[i]; region.size > 0) {
|
||||
/* Check region is valid. */
|
||||
AMS_ASSERT(util::IsAligned(region.address, MemoryPageSize));
|
||||
AMS_ASSERT(util::IsAligned(region.size, MemoryPageSize));
|
||||
AMS_ASSERT((region.address / MemoryPageSize) >= m_guard_page_count);
|
||||
AMS_ASSERT(region.address < region.address + region.size);
|
||||
|
||||
/* Set region. */
|
||||
const auto idx = m_forbidden_region_count++;
|
||||
m_forbidden_region_start_pages[idx] = (region.address / MemoryPageSize) - m_guard_page_count;
|
||||
m_forbidden_region_end_pages[idx] = ((region.address + region.size) / MemoryPageSize) + m_guard_page_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
||||
bool AddressSpaceAllocatorBase<AddressType, SizeType>::CheckGuardSpace(AddressType address, SizeType size, SizeType guard_size) {
|
||||
return this->CheckFreeSpace(address - guard_size, guard_size) && this->CheckFreeSpace(address + size, guard_size);
|
||||
}
|
||||
|
||||
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
||||
bool AddressSpaceAllocatorBase<AddressType, SizeType>::GetNextNonOverlappedNodeUnsafe(AddressType page, SizeType page_count) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(page < page + page_count);
|
||||
|
||||
return this->CheckFreeSpace(page * MemoryPageSize, page_count * MemoryPageSize);
|
||||
}
|
||||
|
||||
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
||||
AddressType AddressSpaceAllocatorBase<AddressType, SizeType>::AllocateSpace(SizeType size, SizeType align, SizeType align_offset, AddressSpaceGenerateRandomFunction generate_random) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(align > 0);
|
||||
AMS_ASSERT((align_offset & ~(align - 1)) == 0);
|
||||
AMS_ASSERT(util::IsAligned(align_offset, MemoryPageSize));
|
||||
AMS_ASSERT(util::IsAligned(align, MemoryPageSize));
|
||||
|
||||
/* Determine the page count. */
|
||||
const SizeType page_count = util::DivideUp(size, MemoryPageSize);
|
||||
|
||||
/* Determine alignment page counts. */
|
||||
const SizeType align_offset_page_count = align_offset / MemoryPageSize;
|
||||
const SizeType align_page_count = align / MemoryPageSize;
|
||||
|
||||
/* Check page counts. */
|
||||
if (page_count + align_offset_page_count > m_end_page - m_guard_page_count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Determine the range to look in. */
|
||||
const AddressType rand_start = (align_offset_page_count <= m_start_page + m_guard_page_count) ? util::DivideUp(m_start_page + m_guard_page_count - align_offset_page_count, align_page_count) : 0;
|
||||
const AddressType rand_end = (m_end_page - page_count - align_offset_page_count - m_guard_page_count) / align_page_count;
|
||||
|
||||
/* Check that we can find a space. */
|
||||
if (rand_start > rand_end) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to find a space up to 512 times. */
|
||||
for (size_t i = 0; i < 512; ++i) {
|
||||
/* Acquire exclusive access before doing calculations. */
|
||||
std::scoped_lock lk(m_critical_section);
|
||||
|
||||
/* Determine a random page. */
|
||||
const u64 random = generate_random(rand_end - rand_start);
|
||||
|
||||
const AddressType target = ((random + rand_start) * align_page_count) + align_offset_page_count;
|
||||
AMS_ASSERT(m_start_page <= target - m_guard_page_count && target + page_count + m_guard_page_count <= m_end_page);
|
||||
|
||||
/* Check that the page is not forbidden. */
|
||||
bool forbidden = false;
|
||||
for (size_t j = 0; j < m_forbidden_region_count; ++j) {
|
||||
if (m_forbidden_region_start_pages[j] < target + page_count && target < m_forbidden_region_end_pages[j]) {
|
||||
forbidden = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the page is valid, use it. */
|
||||
if (!forbidden && this->GetNextNonOverlappedNodeUnsafe(target - m_guard_page_count, page_count + 2 * m_guard_page_count)) {
|
||||
return target * MemoryPageSize;
|
||||
}
|
||||
}
|
||||
|
||||
/* We failed to find space. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Instantiate template. */
|
||||
/* TODO: instantiate <u64, u64> on 32-bit? */
|
||||
template class AddressSpaceAllocatorBase<uintptr_t, size_t>;
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_address_space_allocator_forbidden_region.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
enum AddressAllocationResult {
|
||||
AddressAllocationResult_Success,
|
||||
AddressAllocationResult_OutOfMemory,
|
||||
AddressAllocationResult_OutOfSpace,
|
||||
};
|
||||
|
||||
template<std::unsigned_integral AddressType_, std::unsigned_integral SizeType_>
|
||||
class AddressSpaceAllocatorBase {
|
||||
NON_COPYABLE(AddressSpaceAllocatorBase);
|
||||
NON_MOVEABLE(AddressSpaceAllocatorBase);
|
||||
public:
|
||||
using AddressType = AddressType_;
|
||||
using SizeType = SizeType_;
|
||||
private:
|
||||
static constexpr size_t MaxForbiddenRegions = 2;
|
||||
private:
|
||||
InternalCriticalSection m_critical_section;
|
||||
AddressType m_start_page;
|
||||
AddressType m_end_page;
|
||||
SizeType m_guard_page_count;
|
||||
AddressType m_forbidden_region_start_pages[MaxForbiddenRegions];
|
||||
AddressType m_forbidden_region_end_pages[MaxForbiddenRegions];
|
||||
size_t m_forbidden_region_count;
|
||||
public:
|
||||
AddressSpaceAllocatorBase(u64 start_address, u64 end_address, SizeType guard_size, const AddressSpaceAllocatorForbiddenRegion *forbidden_regions, size_t num_forbidden_regions);
|
||||
|
||||
AddressType AllocateSpace(SizeType size, SizeType align, SizeType align_offset, AddressSpaceGenerateRandomFunction generate_random);
|
||||
|
||||
bool CheckGuardSpace(AddressType address, SizeType size, SizeType guard_size);
|
||||
private:
|
||||
bool GetNextNonOverlappedNodeUnsafe(AddressType page, SizeType page_count);
|
||||
public:
|
||||
virtual bool CheckFreeSpace(AddressType address, SizeType size) = 0;
|
||||
};
|
||||
|
||||
u64 AddressSpaceAllocatorDefaultGenerateRandom(u64 max);
|
||||
|
||||
}
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_address_space_allocator_impl.os.horizon.hpp"
|
||||
#else
|
||||
#include "os_address_space_allocator_impl.generic.hpp"
|
||||
#endif
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
struct AddressSpaceAllocatorForbiddenRegion {
|
||||
u64 address;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class AddressSpaceAllocator final : public AddressSpaceAllocatorBase<uintptr_t, size_t> {
|
||||
private:
|
||||
using Base = AddressSpaceAllocatorBase<uintptr_t, size_t>;
|
||||
public:
|
||||
using Base::Base;
|
||||
public:
|
||||
virtual bool CheckFreeSpace(uintptr_t address, size_t size) override {
|
||||
AMS_UNUSED(address, size);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class AddressSpaceAllocator final : public AddressSpaceAllocatorBase<uintptr_t, size_t> {
|
||||
private:
|
||||
using Base = AddressSpaceAllocatorBase<uintptr_t, size_t>;
|
||||
public:
|
||||
using Base::Base;
|
||||
public:
|
||||
virtual bool CheckFreeSpace(AddressType address, SizeType size) override {
|
||||
/* Query the memory. */
|
||||
svc::MemoryInfo memory_info;
|
||||
svc::PageInfo page_info;
|
||||
const auto result = svc::QueryMemory(std::addressof(memory_info), std::addressof(page_info), address);
|
||||
R_ASSERT(result);
|
||||
AMS_UNUSED(result);
|
||||
|
||||
return memory_info.state == svc::MemoryState_Free && address + size <= memory_info.base_address + memory_info.size;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_aslr_space_manager_types.hpp"
|
||||
#include "os_resource_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
ALWAYS_INLINE AslrSpaceManager &GetAslrSpaceManager() {
|
||||
return GetResourceManager().GetAslrSpaceManager();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_address_space_allocator_forbidden_region.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class AslrSpaceManagerHorizonImpl {
|
||||
NON_COPYABLE(AslrSpaceManagerHorizonImpl);
|
||||
NON_MOVEABLE(AslrSpaceManagerHorizonImpl);
|
||||
private:
|
||||
static constexpr u64 AslrBase32Bit = 0x0000200000ul;
|
||||
static constexpr u64 AslrSize32Bit = 0x003FE00000ul;
|
||||
static constexpr u64 AslrBase64BitDeprecated = 0x0008000000ul;
|
||||
static constexpr u64 AslrSize64BitDeprecated = 0x0078000000ul;
|
||||
static constexpr u64 AslrBase64Bit = 0x0008000000ul;
|
||||
static constexpr u64 AslrSize64Bit = 0x7FF8000000ul;
|
||||
|
||||
static constexpr size_t ForbiddenRegionCount = 2;
|
||||
private:
|
||||
static u64 GetAslrInfo(os::NativeHandle process_handle, svc::InfoType type) {
|
||||
u64 value;
|
||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), type, process_handle, 0));
|
||||
|
||||
static_assert(std::same_as<size_t, uintptr_t>);
|
||||
AMS_ASSERT(value <= std::numeric_limits<size_t>::max());
|
||||
|
||||
return static_cast<u64>(value & std::numeric_limits<size_t>::max());
|
||||
}
|
||||
private:
|
||||
AddressSpaceAllocatorForbiddenRegion m_forbidden_regions[ForbiddenRegionCount];
|
||||
public:
|
||||
AslrSpaceManagerHorizonImpl() {
|
||||
this->InitializeForbiddenRegions(svc::PseudoHandle::CurrentProcess);
|
||||
}
|
||||
|
||||
AslrSpaceManagerHorizonImpl(os::NativeHandle process_handle) {
|
||||
this->InitializeForbiddenRegions(process_handle);
|
||||
}
|
||||
|
||||
const AddressSpaceAllocatorForbiddenRegion *GetForbiddenRegions() const {
|
||||
return m_forbidden_regions;
|
||||
}
|
||||
|
||||
static size_t GetForbiddenRegionCount() {
|
||||
return ForbiddenRegionCount;
|
||||
}
|
||||
|
||||
static u64 GetHeapSpaceBeginAddress(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_HeapRegionAddress);
|
||||
}
|
||||
|
||||
static u64 GetHeapSpaceSize(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_HeapRegionSize);
|
||||
}
|
||||
|
||||
static u64 GetAliasSpaceBeginAddress(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_AliasRegionAddress);
|
||||
}
|
||||
|
||||
static u64 GetAliasSpaceSize(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_AliasRegionSize);
|
||||
}
|
||||
|
||||
static u64 GetAslrSpaceBeginAddress(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_AslrRegionAddress);
|
||||
}
|
||||
|
||||
static u64 GetAslrSpaceEndAddress(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_AslrRegionAddress) + GetAslrInfo(process_handle, svc::InfoType_AslrRegionSize);
|
||||
}
|
||||
private:
|
||||
void InitializeForbiddenRegions(os::NativeHandle process_handle) {
|
||||
m_forbidden_regions[0] = { .address = GetHeapSpaceBeginAddress(process_handle), .size = GetHeapSpaceSize(process_handle) };
|
||||
m_forbidden_regions[1] = { .address = GetAliasSpaceBeginAddress(process_handle), .size = GetAliasSpaceSize(process_handle) };
|
||||
}
|
||||
};
|
||||
|
||||
using AslrSpaceManagerImpl = AslrSpaceManagerHorizonImpl;
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_address_space_allocator_forbidden_region.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class AslrSpaceManagerLinuxImpl {
|
||||
NON_COPYABLE(AslrSpaceManagerLinuxImpl);
|
||||
NON_MOVEABLE(AslrSpaceManagerLinuxImpl);
|
||||
public:
|
||||
constexpr AslrSpaceManagerLinuxImpl() = default;
|
||||
|
||||
static constexpr ALWAYS_INLINE const AddressSpaceAllocatorForbiddenRegion *GetForbiddenRegions() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE size_t GetForbiddenRegionCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetHeapSpaceBeginAddress() {
|
||||
return 8_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetHeapSpaceSize() {
|
||||
return 4_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAliasSpaceBeginAddress() {
|
||||
return 60_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAliasSpaceSize() {
|
||||
return 4_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAslrSpaceBeginAddress() {
|
||||
return 2_MB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAslrSpaceEndAddress() {
|
||||
return 64_GB;
|
||||
}
|
||||
};
|
||||
|
||||
using AslrSpaceManagerImpl = AslrSpaceManagerLinuxImpl;
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_address_space_allocator_forbidden_region.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class AslrSpaceManagerLinuxImpl {
|
||||
NON_COPYABLE(AslrSpaceManagerLinuxImpl);
|
||||
NON_MOVEABLE(AslrSpaceManagerLinuxImpl);
|
||||
public:
|
||||
constexpr AslrSpaceManagerLinuxImpl() = default;
|
||||
|
||||
static constexpr ALWAYS_INLINE const AddressSpaceAllocatorForbiddenRegion *GetForbiddenRegions() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE size_t GetForbiddenRegionCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetHeapSpaceBeginAddress() {
|
||||
return 8_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetHeapSpaceSize() {
|
||||
return 4_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAliasSpaceBeginAddress() {
|
||||
return 60_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAliasSpaceSize() {
|
||||
return 4_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAslrSpaceBeginAddress() {
|
||||
return 2_MB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAslrSpaceEndAddress() {
|
||||
return 64_GB;
|
||||
}
|
||||
};
|
||||
|
||||
using AslrSpaceManagerImpl = AslrSpaceManagerLinuxImpl;
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_address_space_allocator_forbidden_region.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class AslrSpaceManagerWindowsImpl {
|
||||
NON_COPYABLE(AslrSpaceManagerWindowsImpl);
|
||||
NON_MOVEABLE(AslrSpaceManagerWindowsImpl);
|
||||
public:
|
||||
constexpr AslrSpaceManagerWindowsImpl() = default;
|
||||
|
||||
static constexpr ALWAYS_INLINE const AddressSpaceAllocatorForbiddenRegion *GetForbiddenRegions() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE size_t GetForbiddenRegionCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetHeapSpaceBeginAddress() {
|
||||
return 8_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetHeapSpaceSize() {
|
||||
return 4_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAliasSpaceBeginAddress() {
|
||||
return 60_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAliasSpaceSize() {
|
||||
return 4_GB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAslrSpaceBeginAddress() {
|
||||
return 2_MB;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE u64 GetAslrSpaceEndAddress() {
|
||||
return 64_GB;
|
||||
}
|
||||
};
|
||||
|
||||
using AslrSpaceManagerImpl = AslrSpaceManagerWindowsImpl;
|
||||
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_address_space_allocator.hpp"
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_aslr_space_manager_impl.os.horizon.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_WINDOWS)
|
||||
#include "os_aslr_space_manager_impl.os.windows.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_LINUX)
|
||||
#include "os_aslr_space_manager_impl.os.linux.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_MACOS)
|
||||
#include "os_aslr_space_manager_impl.os.macos.hpp"
|
||||
#else
|
||||
#error "Unknown OS for AslrSpaceManagerImpl"
|
||||
#endif
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
constexpr inline size_t AslrSpaceLargeAlign = 2_MB;
|
||||
constexpr inline size_t AslrSpaceGuardSize = 4 * MemoryPageSize;
|
||||
|
||||
template<typename Allocator, typename Impl>
|
||||
class AslrSpaceManagerTemplate {
|
||||
NON_COPYABLE(AslrSpaceManagerTemplate);
|
||||
NON_MOVEABLE(AslrSpaceManagerTemplate);
|
||||
private:
|
||||
using AddressType = typename Allocator::AddressType;
|
||||
using SizeType = typename Allocator::SizeType;
|
||||
private:
|
||||
Impl m_impl;
|
||||
Allocator m_allocator;
|
||||
public:
|
||||
AslrSpaceManagerTemplate() : m_impl(), m_allocator(m_impl.GetAslrSpaceBeginAddress(), m_impl.GetAslrSpaceEndAddress(), AslrSpaceGuardSize, m_impl.GetForbiddenRegions(), m_impl.GetForbiddenRegionCount()) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
AslrSpaceManagerTemplate(os::NativeHandle process_handle) : m_impl(process_handle), m_allocator(m_impl.GetAslrSpaceBeginAddress(process_handle), m_impl.GetAslrSpaceEndAddress(process_handle), AslrSpaceGuardSize, m_impl.GetForbiddenRegions(), m_impl.GetForbiddenRegionCount(), process_handle) {
|
||||
/* ... */
|
||||
}
|
||||
#endif
|
||||
|
||||
AddressType AllocateSpace(SizeType size, SizeType align_offset, AddressSpaceGenerateRandomFunction generate_random) {
|
||||
/* Try to allocate a large-aligned space, if we can. */
|
||||
if (align_offset || (size / AslrSpaceLargeAlign) != 0) {
|
||||
if (auto large_align = m_allocator.AllocateSpace(size, AslrSpaceLargeAlign, align_offset & (AslrSpaceLargeAlign - 1), generate_random); large_align != 0) {
|
||||
return large_align;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate a page-aligned space. */
|
||||
return m_allocator.AllocateSpace(size, MemoryPageSize, 0, generate_random);
|
||||
}
|
||||
|
||||
bool CheckGuardSpace(AddressType address, SizeType size) {
|
||||
return m_allocator.CheckGuardSpace(address, size, AslrSpaceGuardSize);
|
||||
}
|
||||
|
||||
template<typename MapFunction, typename UnmapFunction>
|
||||
Result MapAtRandomAddressWithCustomRandomGenerator(AddressType *out, MapFunction map_function, UnmapFunction unmap_function, SizeType size, SizeType align_offset, AddressSpaceGenerateRandomFunction generate_random) {
|
||||
/* Try to map up to 64 times. */
|
||||
for (auto i = 0; i < 64; ++i) {
|
||||
/* Reserve space to map the memory. */
|
||||
const uintptr_t map_address = this->AllocateSpace(size, align_offset, generate_random);
|
||||
if (map_address == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Try to map. */
|
||||
R_TRY_CATCH(map_function(map_address, size)) {
|
||||
/* If we failed to map at the address, retry. */
|
||||
R_CATCH(os::ResultInvalidCurrentMemoryState) { continue; }
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Check guard space. */
|
||||
if (!this->CheckGuardSpace(map_address, size)) {
|
||||
/* We don't have guard space, so unmap. */
|
||||
unmap_function(map_address, size);
|
||||
|
||||
/* NOTE: Nintendo is missing this continue; this is almost certainly a bug. */
|
||||
/* This will cause them to incorrectly return success after unmapping if guard space is not present. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We mapped successfully. */
|
||||
*out = map_address;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* We failed to map. */
|
||||
R_THROW(os::ResultOutOfAddressSpace());
|
||||
}
|
||||
|
||||
template<typename MapFunction, typename UnmapFunction>
|
||||
Result MapAtRandomAddress(AddressType *out, MapFunction map_function, UnmapFunction unmap_function, SizeType size, SizeType align_offset) {
|
||||
R_RETURN(this->MapAtRandomAddressWithCustomRandomGenerator(out, map_function, unmap_function, size, align_offset, os::impl::AddressSpaceAllocatorDefaultGenerateRandom));
|
||||
}
|
||||
};
|
||||
|
||||
using AslrSpaceManager = AslrSpaceManagerTemplate<AddressSpaceAllocator, AslrSpaceManagerImpl>;
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_cache_impl.os.horizon.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_WINDOWS)
|
||||
#include "os_cache_impl.os.windows.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_LINUX)
|
||||
#include "os_cache_impl.os.linux.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_MACOS)
|
||||
#include "os_cache_impl.os.macos.hpp"
|
||||
#else
|
||||
#error "Unknown OS for CacheImpl"
|
||||
#endif
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
inline void FlushDataCacheImpl(const void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
{
|
||||
/* Declare helper variables. */
|
||||
uintptr_t cache_type_register = 0;
|
||||
uintptr_t cache_line_size = 0;
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
|
||||
/* Get the cache type register. */
|
||||
__asm__ __volatile__("mrs %[cache_type_register], ctr_el0" : [cache_type_register]"=r"(cache_type_register));
|
||||
|
||||
/* Calculate cache line size. */
|
||||
cache_line_size = 4 << ((cache_type_register >> 16) & 0xF);
|
||||
|
||||
/* Get the thread local region. */
|
||||
auto * const tlr = svc::GetThreadLocalRegion();
|
||||
|
||||
/* Note to the kernel that we're performing cache maintenance, in case we get interrupted while touching cache lines. */
|
||||
tlr->cache_maintenance_flag = 1;
|
||||
ON_SCOPE_EXIT { tlr->cache_maintenance_flag = 0; };
|
||||
|
||||
/* Iterate, flushing cache lines. */
|
||||
for (uintptr_t cur = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1); cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__ ("dc civac, %[cur]" :: [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Insert a memory barrier, now that memory has been flushed. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
}
|
||||
#else
|
||||
const auto result = svc::FlushProcessDataCache(svc::PseudoHandle::CurrentProcess, reinterpret_cast<uintptr_t>(addr), size);
|
||||
R_ASSERT(result);
|
||||
AMS_UNUSED(result);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void FlushEntireDataCacheImpl() {
|
||||
svc::FlushEntireDataCache();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
inline void FlushDataCacheImpl(const void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
{
|
||||
/* Declare helper variables. */
|
||||
uintptr_t cache_type_register = 0;
|
||||
uintptr_t cache_line_size = 0;
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
|
||||
/* Get the cache type register. */
|
||||
__asm__ __volatile__("mrs %[cache_type_register], ctr_el0" : [cache_type_register]"=r"(cache_type_register));
|
||||
|
||||
/* Calculate cache line size. */
|
||||
cache_line_size = 4 << ((cache_type_register >> 16) & 0xF);
|
||||
|
||||
/* Iterate, flushing cache lines. */
|
||||
for (uintptr_t cur = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1); cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__ ("dc civac, %[cur]" :: [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Insert a memory barrier, now that memory has been flushed. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
}
|
||||
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Theoretically, do nothing here? */
|
||||
AMS_UNUSED(addr, size);
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
#else
|
||||
#error "Unknown architecture for linux DataCache"
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void FlushEntireDataCacheImpl() {
|
||||
#if defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Theoretically, do nothing here? */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM64)
|
||||
AMS_ABORT("FlushEntireDataCacheImpl called on arm64 linux");
|
||||
#else
|
||||
#error "Unknown architecture for linux DataCache"
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
inline void FlushDataCacheImpl(const void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
{
|
||||
/* Declare helper variables. */
|
||||
uintptr_t cache_type_register = 0;
|
||||
uintptr_t cache_line_size = 0;
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
|
||||
/* Get the cache type register. */
|
||||
__asm__ __volatile__("mrs %[cache_type_register], ctr_el0" : [cache_type_register]"=r"(cache_type_register));
|
||||
|
||||
/* Calculate cache line size. */
|
||||
cache_line_size = 4 << ((cache_type_register >> 16) & 0xF);
|
||||
|
||||
/* Iterate, flushing cache lines. */
|
||||
for (uintptr_t cur = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1); cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__ ("dc civac, %[cur]" :: [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Insert a memory barrier, now that memory has been flushed. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
}
|
||||
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Theoretically, do nothing here? */
|
||||
AMS_UNUSED(addr, size);
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
#else
|
||||
#error "Unknown architecture for macOS DataCache"
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void FlushEntireDataCacheImpl() {
|
||||
#if defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Theoretically, do nothing here? */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM64)
|
||||
AMS_ABORT("FlushEntireDataCacheImpl called on arm64 macOS");
|
||||
#else
|
||||
#error "Unknown architecture for macOS DataCache"
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
inline void FlushDataCacheImpl(const void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
{
|
||||
/* Declare helper variables. */
|
||||
uintptr_t cache_type_register = 0;
|
||||
uintptr_t cache_line_size = 0;
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
|
||||
/* Get the cache type register. */
|
||||
__asm__ __volatile__("mrs %[cache_type_register], ctr_el0" : [cache_type_register]"=r"(cache_type_register));
|
||||
|
||||
/* Calculate cache line size. */
|
||||
cache_line_size = 4 << ((cache_type_register >> 16) & 0xF);
|
||||
|
||||
/* Iterate, flushing cache lines. */
|
||||
for (uintptr_t cur = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1); cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__ ("dc civac, %[cur]" :: [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Insert a memory barrier, now that memory has been flushed. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
}
|
||||
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Theoretically, do nothing here? */
|
||||
AMS_UNUSED(addr, size);
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
#else
|
||||
#error "Unknown architecture for windows DataCache"
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void FlushEntireDataCacheImpl() {
|
||||
#if defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Theoretically, do nothing here? */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
#else
|
||||
#error "Unknown architecture for windows DataCache"
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_debug_impl.os.horizon.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_WINDOWS)
|
||||
#include "os_debug_impl.os.windows.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_LINUX)
|
||||
#include "os_debug_impl.os.linux.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_MACOS)
|
||||
#include "os_debug_impl.os.macos.hpp"
|
||||
#else
|
||||
#error "Unknown OS for DebugImpl"
|
||||
#endif
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class DebugHorizonImpl {
|
||||
public:
|
||||
static uintptr_t GetCurrentStackPointer() {
|
||||
uintptr_t v;
|
||||
__asm__ __volatile__("mov %[v], sp" : [v]"=&r"(v) :: "memory");
|
||||
return v;
|
||||
}
|
||||
|
||||
static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(out_stack != nullptr);
|
||||
AMS_ASSERT(out_size != nullptr);
|
||||
|
||||
/* Get the current thread. */
|
||||
auto *cur_thread = os::impl::GetCurrentThread();
|
||||
auto *cur_fiber = cur_thread->current_fiber;
|
||||
|
||||
/* Get the current stack pointer. */
|
||||
uintptr_t cur_sp = GetCurrentStackPointer();
|
||||
|
||||
/* Determine current stack extents, TODO Fiber */
|
||||
uintptr_t stack_top = reinterpret_cast<uintptr_t>(cur_fiber == nullptr ? cur_thread->stack : /* TODO: cur_fiber->stack */ nullptr);
|
||||
size_t stack_size = reinterpret_cast<size_t>(cur_fiber == nullptr ? cur_thread->stack_size : /* TODO: cur_fiber->stack_size */ 0);
|
||||
|
||||
uintptr_t stack_bottom = stack_top + stack_size;
|
||||
|
||||
/* TODO: User exception handler, check if stack is out of range and use exception stack. */
|
||||
|
||||
/* Check that the stack pointer is in bounds. */
|
||||
AMS_ABORT_UNLESS((stack_top <= cur_sp) && (cur_sp < stack_bottom));
|
||||
|
||||
/* Set the output. */
|
||||
*out_stack = stack_top;
|
||||
*out_size = stack_size;
|
||||
}
|
||||
|
||||
static void QueryMemoryInfo(os::MemoryInfo *out) {
|
||||
AMS_UNUSED(out);
|
||||
AMS_ABORT("TODO: Horizon QueryMemoryInfo");
|
||||
}
|
||||
|
||||
static Tick GetIdleTickCount() {
|
||||
u64 value;
|
||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_IdleTickCount, svc::InvalidHandle, static_cast<u64>(-1)));
|
||||
|
||||
return os::Tick(value);
|
||||
}
|
||||
|
||||
static Tick GetThreadTickCount() {
|
||||
u64 value;
|
||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_ThreadTickCount, svc::PseudoHandle::CurrentThread, static_cast<u64>(-1)));
|
||||
|
||||
return os::Tick(value);
|
||||
}
|
||||
|
||||
static int GetFreeThreadCount() {
|
||||
u64 value;
|
||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_FreeThreadCount, svc::PseudoHandle::CurrentProcess, 0));
|
||||
|
||||
AMS_ASSERT(value <= static_cast<u64>(std::numeric_limits<int>::max()));
|
||||
|
||||
return static_cast<int>(value);
|
||||
}
|
||||
};
|
||||
|
||||
using DebugImpl = DebugHorizonImpl;
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class DebugLinuxImpl {
|
||||
public:
|
||||
static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(out_stack != nullptr);
|
||||
AMS_ASSERT(out_size != nullptr);
|
||||
|
||||
/* Get the current stack by pthread */
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(std::addressof(attr));
|
||||
ON_SCOPE_EXIT { pthread_attr_destroy(std::addressof(attr)); };
|
||||
|
||||
const auto getattr_res = pthread_getattr_np(pthread_self(), std::addressof(attr));
|
||||
AMS_ABORT_UNLESS(getattr_res == 0);
|
||||
|
||||
/* Get the thread satck. */
|
||||
void *base = nullptr;
|
||||
size_t size = 0;
|
||||
const auto getstack_res = pthread_attr_getstack(std::addressof(attr), std::addressof(base), std::addressof(size));
|
||||
AMS_ABORT_UNLESS(getstack_res == 0);
|
||||
|
||||
*out_stack = reinterpret_cast<uintptr_t>(base);
|
||||
*out_size = size;
|
||||
}
|
||||
|
||||
static void QueryMemoryInfo(os::MemoryInfo *out) {
|
||||
AMS_UNUSED(out);
|
||||
AMS_ABORT("TODO: Linux QueryMemoryInfo");
|
||||
}
|
||||
|
||||
static Tick GetIdleTickCount() {
|
||||
return os::Tick(0);
|
||||
}
|
||||
|
||||
static Tick GetThreadTickCount() {
|
||||
return os::Tick(0);
|
||||
}
|
||||
|
||||
static int GetFreeThreadCount() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
using DebugImpl = DebugLinuxImpl;
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class DebugMacosImpl {
|
||||
public:
|
||||
static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(out_stack != nullptr);
|
||||
AMS_ASSERT(out_size != nullptr);
|
||||
|
||||
/* Get the current pthread. */
|
||||
const auto self = pthread_self();
|
||||
|
||||
/* Get the thread stack. */
|
||||
uintptr_t stack_bottom = reinterpret_cast<uintptr_t>(pthread_get_stackaddr_np(self));
|
||||
size_t stack_size = pthread_get_stacksize_np(self);
|
||||
|
||||
uintptr_t stack_top = stack_bottom - stack_size;
|
||||
|
||||
*out_stack = stack_top;
|
||||
*out_size = stack_size;
|
||||
}
|
||||
|
||||
static void QueryMemoryInfo(os::MemoryInfo *out) {
|
||||
AMS_UNUSED(out);
|
||||
AMS_ABORT("TODO: macOS QueryMemoryInfo");
|
||||
}
|
||||
|
||||
static Tick GetIdleTickCount() {
|
||||
return os::Tick(0);
|
||||
}
|
||||
|
||||
static Tick GetThreadTickCount() {
|
||||
return os::Tick(0);
|
||||
}
|
||||
|
||||
static int GetFreeThreadCount() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
using DebugImpl = DebugMacosImpl;
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/windows.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class DebugWindowsImpl {
|
||||
public:
|
||||
static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(out_stack != nullptr);
|
||||
AMS_ASSERT(out_size != nullptr);
|
||||
|
||||
/* Get the current stack by NT_TIB */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
auto *tib = reinterpret_cast<NT_TIB *>(::NtCurrentTeb());
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
*out_stack = reinterpret_cast<uintptr_t>(tib->StackLimit);
|
||||
*out_size = reinterpret_cast<uintptr_t>(tib->StackBase) - reinterpret_cast<uintptr_t>(tib->StackLimit);
|
||||
}
|
||||
|
||||
static void QueryMemoryInfo(os::MemoryInfo *out) {
|
||||
AMS_UNUSED(out);
|
||||
AMS_ABORT("TODO: Windows QueryMemoryInfo");
|
||||
}
|
||||
|
||||
static Tick GetIdleTickCount() {
|
||||
return os::Tick(0);
|
||||
}
|
||||
|
||||
static Tick GetThreadTickCount() {
|
||||
return os::Tick(0);
|
||||
}
|
||||
|
||||
static int GetFreeThreadCount() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
using DebugImpl = DebugWindowsImpl;
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class CheckBusyMutexPermission {
|
||||
public:
|
||||
CheckBusyMutexPermission() {
|
||||
/* In order to use the disable counter, we must support SynchronizePreemptionState. */
|
||||
u64 value;
|
||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_IsSvcPermitted, 0, svc::SvcId_SynchronizePreemptionState));
|
||||
|
||||
/* Verify that it's supported. */
|
||||
AMS_ABORT_UNLESS(value != 0);
|
||||
}
|
||||
};
|
||||
|
||||
ALWAYS_INLINE void CallCheckBusyMutexPermission() {
|
||||
AMS_FUNCTION_LOCAL_STATIC(CheckBusyMutexPermission, s_check);
|
||||
AMS_UNUSED(s_check);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_giant_lock_types.hpp"
|
||||
#include "os_resource_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
ALWAYS_INLINE GiantLock &GetGiantLock() {
|
||||
return GetResourceManager().GetGiantLock();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class GiantLockHorizonImpl{};
|
||||
|
||||
using GiantLock = GiantLockHorizonImpl;
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_giant_lock.os.linux.hpp"
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
#define AMS_OS_IMPL_GIANT_LOCK_FILE_PATH "/tmp/.ams_os_giant_lock"
|
||||
|
||||
void GiantLockLinuxImpl::Lock() {
|
||||
m_fd = ::open(AMS_OS_IMPL_GIANT_LOCK_FILE_PATH, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
|
||||
AMS_ABORT_UNLESS(m_fd >= 0);
|
||||
|
||||
while (::lockf(m_fd, F_LOCK, 0) < 0) {
|
||||
AMS_ABORT_UNLESS(errno == EINTR);
|
||||
}
|
||||
}
|
||||
|
||||
void GiantLockLinuxImpl::Unlock() {
|
||||
AMS_ASSERT(m_fd >= 0);
|
||||
|
||||
while (::lockf(m_fd, F_ULOCK, 0) < 0) {
|
||||
AMS_ABORT_UNLESS(errno == EINTR);
|
||||
}
|
||||
|
||||
while (::close(m_fd) < 0) {
|
||||
AMS_ABORT_UNLESS(errno == EINTR);
|
||||
}
|
||||
|
||||
m_fd = os::InvalidNativeHandle;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class GiantLockLinuxImpl {
|
||||
private:
|
||||
s32 m_fd = -1;
|
||||
public:
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
ALWAYS_INLINE void lock() {
|
||||
this->Lock();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void unlock() {
|
||||
this->Unlock();
|
||||
}
|
||||
};
|
||||
|
||||
using GiantLock = GiantLockLinuxImpl;
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_giant_lock.os.macos.hpp"
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
#define AMS_OS_IMPL_GIANT_LOCK_FILE_PATH "/tmp/.ams_os_giant_lock"
|
||||
|
||||
void GiantLockMacosImpl::Lock() {
|
||||
m_fd = ::open(AMS_OS_IMPL_GIANT_LOCK_FILE_PATH, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
|
||||
AMS_ABORT_UNLESS(m_fd >= 0);
|
||||
|
||||
while (::lockf(m_fd, F_LOCK, 0) < 0) {
|
||||
AMS_ABORT_UNLESS(errno == EINTR);
|
||||
}
|
||||
}
|
||||
|
||||
void GiantLockMacosImpl::Unlock() {
|
||||
AMS_ASSERT(m_fd >= 0);
|
||||
|
||||
while (::lockf(m_fd, F_ULOCK, 0) < 0) {
|
||||
AMS_ABORT_UNLESS(errno == EINTR);
|
||||
}
|
||||
|
||||
while (::close(m_fd) < 0) {
|
||||
AMS_ABORT_UNLESS(errno == EINTR);
|
||||
}
|
||||
|
||||
m_fd = os::InvalidNativeHandle;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class GiantLockMacosImpl {
|
||||
private:
|
||||
s32 m_fd = -1;
|
||||
public:
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
ALWAYS_INLINE void lock() {
|
||||
this->Lock();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void unlock() {
|
||||
this->Unlock();
|
||||
}
|
||||
};
|
||||
|
||||
using GiantLock = GiantLockMacosImpl;
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/windows.hpp>
|
||||
#include "os_giant_lock.os.windows.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
GiantLockWindowsImpl::GiantLockWindowsImpl() {
|
||||
const NativeHandle handle = ::CreateMutexA(nullptr, false, "AMS_OS_STRATOSPHERE_GIANT_LOCK");
|
||||
AMS_ASSERT(handle != InvalidNativeHandle);
|
||||
|
||||
m_handle = handle;
|
||||
}
|
||||
|
||||
GiantLockWindowsImpl::~GiantLockWindowsImpl() {
|
||||
const auto ret = ::CloseHandle(m_handle);
|
||||
AMS_ASSERT(ret != 0);
|
||||
AMS_UNUSED(ret);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class GiantLockWindowsImpl {
|
||||
private:
|
||||
NativeHandle m_handle;
|
||||
public:
|
||||
GiantLockWindowsImpl();
|
||||
~GiantLockWindowsImpl();
|
||||
|
||||
ALWAYS_INLINE void lock() {
|
||||
::WaitForSingleObject(m_handle, INFINITE);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void unlock() {
|
||||
::ReleaseMutex(m_handle);
|
||||
}
|
||||
};
|
||||
|
||||
using GiantLock = GiantLockWindowsImpl;
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_giant_lock.os.horizon.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_WINDOWS)
|
||||
#include "os_giant_lock.os.windows.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_LINUX)
|
||||
#include "os_giant_lock.os.linux.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_MACOS)
|
||||
#include "os_giant_lock.os.macos.hpp"
|
||||
#else
|
||||
#error "Unknown OS for GiantLock"
|
||||
#endif
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_resource_manager.hpp"
|
||||
|
||||
extern "C" { extern u8 __argdata__[]; }
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
namespace {
|
||||
|
||||
class MemoryArranger {
|
||||
private:
|
||||
uintptr_t m_address;
|
||||
public:
|
||||
constexpr MemoryArranger(uintptr_t address) : m_address(address) { /* ... */ }
|
||||
|
||||
template<typename T>
|
||||
T *Arrange() {
|
||||
this->Align(alignof(T));
|
||||
return static_cast<T *>(this->Arrange(sizeof(T)));
|
||||
}
|
||||
|
||||
void *Arrange(size_t size) {
|
||||
const auto address = m_address;
|
||||
m_address += size;
|
||||
return reinterpret_cast<void *>(address);
|
||||
}
|
||||
|
||||
void Align(size_t align) {
|
||||
m_address = util::AlignUp(m_address, align);
|
||||
}
|
||||
|
||||
char *ArrangeCharArray(size_t size) {
|
||||
return reinterpret_cast<char *>(Arrange(size));
|
||||
}
|
||||
};
|
||||
|
||||
bool HasArguments(uintptr_t args_region) {
|
||||
/* Check that the arguments region is read-write. */
|
||||
svc::MemoryInfo mi;
|
||||
svc::PageInfo pi;
|
||||
|
||||
if (R_FAILED(svc::QueryMemory(std::addressof(mi), std::addressof(pi), args_region))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mi.permission == svc::MemoryPermission_ReadWrite;
|
||||
}
|
||||
|
||||
const char *SkipSpace(const char *p, const char *end) {
|
||||
while (p < end && std::isspace(*p)) { ++p; }
|
||||
return p;
|
||||
}
|
||||
|
||||
const char *GetTokenEnd(const char *p, const char *end) {
|
||||
while (p < end && !std::isspace(*p)) { ++p; }
|
||||
return p;
|
||||
}
|
||||
|
||||
const char *GetQuotedTokenEnd(const char *p, const char *end) {
|
||||
while (p < end && *p != '"') { ++p; }
|
||||
return p;
|
||||
}
|
||||
|
||||
int MakeArgv(char **out_argv_buf, char *arg_buf, const char *cmd_line, size_t cmd_line_size, int arg_max) {
|
||||
/* Prepare to parse arguments. */
|
||||
auto idx = 0;
|
||||
auto src = cmd_line;
|
||||
auto dst = arg_buf;
|
||||
const auto end = src + cmd_line_size;
|
||||
|
||||
/* Parse all tokens. */
|
||||
while (true) {
|
||||
/* Advance past any spaces. */
|
||||
src = SkipSpace(src, end);
|
||||
if (src >= end) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check that we don't have too many arguments. */
|
||||
if (idx >= arg_max) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Find the start/end of the current argument token. */
|
||||
const char *arg_end;
|
||||
const char *src_next;
|
||||
if (*src == '"') {
|
||||
++src;
|
||||
arg_end = GetQuotedTokenEnd(src, end);
|
||||
src_next = arg_end + 1;
|
||||
} else {
|
||||
arg_end = GetTokenEnd(src, end);
|
||||
src_next = arg_end;
|
||||
}
|
||||
|
||||
/* Determine token size. */
|
||||
const auto arg_size = arg_end - src;
|
||||
|
||||
/* Set the argv pointer. */
|
||||
out_argv_buf[idx++] = dst;
|
||||
|
||||
/* Copy the argument. */
|
||||
std::memcpy(dst, src, arg_size);
|
||||
dst += arg_size;
|
||||
|
||||
/* Null-terminate the argument token. */
|
||||
*(dst++) = '\x00';
|
||||
|
||||
/* Advance to next token. */
|
||||
src = src_next;
|
||||
}
|
||||
|
||||
/* Null terminate the final token. */
|
||||
*(dst++) = '\x00';
|
||||
|
||||
/* Null terminate argv. */
|
||||
out_argv_buf[idx] = nullptr;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetHostArgc(int argc);
|
||||
void SetHostArgv(char **argv);
|
||||
|
||||
void Initialize() {
|
||||
/* Only allow os::Initialize to be called once. */
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized, false);
|
||||
if (s_initialized) {
|
||||
return;
|
||||
}
|
||||
s_initialized = true;
|
||||
|
||||
/* Initialize the global os resource manager. */
|
||||
os::impl::ResourceManagerHolder::InitializeResourceManagerInstance();
|
||||
|
||||
/* Setup host argc/argv as needed. */
|
||||
const uintptr_t args_region = reinterpret_cast<uintptr_t>(__argdata__);
|
||||
if (HasArguments(args_region)) {
|
||||
/* Create arguments memory arranger. */
|
||||
MemoryArranger arranger(args_region);
|
||||
|
||||
/* Arrange. */
|
||||
const auto &header = *arranger.Arrange<ldr::ProgramArguments>();
|
||||
const char *cmd_line = arranger.ArrangeCharArray(header.arguments_size);
|
||||
char *arg_buf = arranger.ArrangeCharArray(header.arguments_size + 2);
|
||||
char **argv_buf = arranger.Arrange<char *>();
|
||||
|
||||
/* Determine extents. */
|
||||
const auto arg_buf_size = reinterpret_cast<uintptr_t>(argv_buf) - args_region;
|
||||
const auto arg_max = (header.allocated_size - arg_buf_size) / sizeof(char *);
|
||||
|
||||
/* Make argv. */
|
||||
const auto arg_count = MakeArgv(argv_buf, arg_buf, cmd_line, header.arguments_size, arg_max);
|
||||
|
||||
/* Set host argc/argv. */
|
||||
os::SetHostArgc(arg_count);
|
||||
os::SetHostArgv(argv_buf);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_resource_manager.hpp"
|
||||
|
||||
namespace ams {
|
||||
|
||||
void Main();
|
||||
|
||||
namespace init {
|
||||
|
||||
void InitializeDefaultAllocator();
|
||||
|
||||
}
|
||||
|
||||
/* TODO: This should probably instead be a custom init::Initialize*()? */
|
||||
namespace fs {
|
||||
|
||||
void InitializeForHostTool();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
void SetHostArgc(int argc);
|
||||
void SetHostArgv(char **argv);
|
||||
|
||||
[[gnu::constructor(101)]] void Initialize() {
|
||||
/* Only allow os::Initialize to be called once. */
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized, false);
|
||||
if (s_initialized) {
|
||||
return;
|
||||
}
|
||||
s_initialized = true;
|
||||
|
||||
/* Initialize the global os resource manager. */
|
||||
os::impl::ResourceManagerHolder::InitializeResourceManagerInstance();
|
||||
|
||||
/* Initialize virtual address memory. */
|
||||
os::InitializeVirtualAddressMemory();
|
||||
|
||||
/* Ensure that the init library's allocator has been setup. */
|
||||
init::InitializeDefaultAllocator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" int main(int argc, char **argv) {
|
||||
/* Ensure os library is initialized. */
|
||||
::ams::os::Initialize();
|
||||
|
||||
/* Set argc/argv. */
|
||||
::ams::os::SetHostArgc(argc);
|
||||
::ams::os::SetHostArgv(argv);
|
||||
|
||||
/* Ensure filesystem library is initialized. */
|
||||
::ams::fs::InitializeForHostTool();
|
||||
|
||||
/* Call main. */
|
||||
::ams::Main();
|
||||
|
||||
/* TODO: Should we try to implement a custom exit here? */
|
||||
return 0;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_resource_manager.hpp"
|
||||
|
||||
namespace ams {
|
||||
|
||||
void Main();
|
||||
|
||||
namespace init {
|
||||
|
||||
void InitializeDefaultAllocator();
|
||||
|
||||
}
|
||||
|
||||
/* TODO: This should probably instead be a custom init::Initialize*()? */
|
||||
namespace fs {
|
||||
|
||||
void InitializeForHostTool();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
void SetHostArgc(int argc);
|
||||
void SetHostArgv(char **argv);
|
||||
|
||||
[[gnu::constructor(101)]] void Initialize() {
|
||||
/* Only allow os::Initialize to be called once. */
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized, false);
|
||||
if (s_initialized) {
|
||||
return;
|
||||
}
|
||||
s_initialized = true;
|
||||
|
||||
/* Initialize the global os resource manager. */
|
||||
os::impl::ResourceManagerHolder::InitializeResourceManagerInstance();
|
||||
|
||||
/* Initialize virtual address memory. */
|
||||
os::InitializeVirtualAddressMemory();
|
||||
|
||||
/* Ensure that the init library's allocator has been setup. */
|
||||
init::InitializeDefaultAllocator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" int main(int argc, char **argv) {
|
||||
/* Ensure os library is initialized. */
|
||||
::ams::os::Initialize();
|
||||
|
||||
/* Set argc/argv. */
|
||||
::ams::os::SetHostArgc(argc);
|
||||
::ams::os::SetHostArgv(argv);
|
||||
|
||||
/* Ensure filesystem library is initialized. */
|
||||
::ams::fs::InitializeForHostTool();
|
||||
|
||||
/* Call main. */
|
||||
::ams::Main();
|
||||
|
||||
/* TODO: Should we try to implement a custom exit here? */
|
||||
return 0;
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_resource_manager.hpp"
|
||||
|
||||
namespace ams {
|
||||
|
||||
void Main();
|
||||
|
||||
namespace init {
|
||||
|
||||
void InitializeDefaultAllocator();
|
||||
|
||||
}
|
||||
|
||||
/* TODO: This should probably instead be a custom init::Initialize*()? */
|
||||
namespace fs {
|
||||
|
||||
void InitializeForHostTool();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
void SetHostArgc(int argc);
|
||||
void SetHostArgv(char **argv);
|
||||
|
||||
namespace {
|
||||
|
||||
void SetupWindowsConsole(DWORD which) {
|
||||
/* Get handle to standard device. */
|
||||
const auto handle = ::GetStdHandle(which);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the console mode. */
|
||||
DWORD mode;
|
||||
if (!::GetConsoleMode(handle, std::addressof(mode))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enable printing with ANSI escape codes. */
|
||||
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
|
||||
/* Set console mode. */
|
||||
::SetConsoleMode(handle, mode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
/* Only allow os::Initialize to be called once. */
|
||||
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized, false);
|
||||
if (s_initialized) {
|
||||
return;
|
||||
}
|
||||
s_initialized = true;
|
||||
|
||||
/* Initialize the global os resource manager. */
|
||||
os::impl::ResourceManagerHolder::InitializeResourceManagerInstance();
|
||||
|
||||
/* Initialize virtual address memory. */
|
||||
os::InitializeVirtualAddressMemory();
|
||||
|
||||
/* Ensure that the init library's allocator has been setup. */
|
||||
init::InitializeDefaultAllocator();
|
||||
|
||||
/* Try to set up the windows console. */
|
||||
SetupWindowsConsole(STD_OUTPUT_HANDLE);
|
||||
SetupWindowsConsole(STD_ERROR_HANDLE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
|
||||
/* Ensure os library is initialized. */
|
||||
::ams::os::Initialize();
|
||||
|
||||
/* Set argc/argv. */
|
||||
::ams::os::SetHostArgc(__argc);
|
||||
::ams::os::SetHostArgv(__argv);
|
||||
|
||||
/* Ensure filesystem library is initialized. */
|
||||
::ams::fs::InitializeForHostTool();
|
||||
|
||||
/* Call main. */
|
||||
::ams::Main();
|
||||
|
||||
/* TODO: Should we try to implement a custom exit here? */
|
||||
return 0;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InsecureMemoryImpl {
|
||||
public:
|
||||
static Result AllocateInsecureMemoryImpl(uintptr_t *out_address, size_t size);
|
||||
static void FreeInsecureMemoryImpl(uintptr_t address, size_t size);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_insecure_memory_impl.hpp"
|
||||
#include "os_aslr_space_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
Result InsecureMemoryImpl::AllocateInsecureMemoryImpl(uintptr_t *out_address, size_t size) {
|
||||
/* Map at a random address. */
|
||||
R_RETURN(impl::GetAslrSpaceManager().MapAtRandomAddress(out_address,
|
||||
[](uintptr_t map_address, size_t map_size) -> Result {
|
||||
R_TRY_CATCH(svc::MapInsecurePhysicalMemory(map_address, map_size)) {
|
||||
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfMemory())
|
||||
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
R_SUCCEED();
|
||||
},
|
||||
[](uintptr_t map_address, size_t map_size) -> void {
|
||||
return InsecureMemoryImpl::FreeInsecureMemoryImpl(map_address, map_size);
|
||||
},
|
||||
size,
|
||||
0
|
||||
));
|
||||
}
|
||||
|
||||
void InsecureMemoryImpl::FreeInsecureMemoryImpl(uintptr_t address, size_t size) {
|
||||
R_ABORT_UNLESS(svc::UnmapInsecurePhysicalMemory(address, size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_inter_process_event.hpp"
|
||||
#include "os_inter_process_event_impl.hpp"
|
||||
#include "os_multiple_wait_object_list.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
inline void SetupInterProcessEventType(InterProcessEventType *event, NativeHandle read_handle, bool read_handle_managed, NativeHandle write_handle, bool write_handle_managed, EventClearMode clear_mode) {
|
||||
/* Set handles. */
|
||||
event->readable_handle = read_handle;
|
||||
event->is_readable_handle_managed = read_handle_managed;
|
||||
event->writable_handle = write_handle;
|
||||
event->is_writable_handle_managed = write_handle_managed;
|
||||
|
||||
/* Set auto clear. */
|
||||
event->auto_clear = (clear_mode == EventClearMode_AutoClear);
|
||||
|
||||
/* Create the waitlist node. */
|
||||
util::ConstructAt(event->multi_wait_object_list_storage);
|
||||
|
||||
/* Set state. */
|
||||
event->state = InterProcessEventType::State_Initialized;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result CreateInterProcessEvent(InterProcessEventType *event, EventClearMode clear_mode) {
|
||||
NativeHandle rh, wh;
|
||||
R_TRY(impl::InterProcessEventImpl::Create(std::addressof(wh), std::addressof(rh)));
|
||||
|
||||
SetupInterProcessEventType(event, rh, true, wh, true, clear_mode);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void DestroyInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
|
||||
/* Clear the state. */
|
||||
event->state = InterProcessEventType::State_NotInitialized;
|
||||
|
||||
/* Close handles if required. */
|
||||
if (event->is_readable_handle_managed) {
|
||||
if (event->readable_handle != os::InvalidNativeHandle) {
|
||||
impl::InterProcessEventImpl::Close(event->readable_handle);
|
||||
}
|
||||
event->is_readable_handle_managed = false;
|
||||
}
|
||||
|
||||
if (event->is_writable_handle_managed) {
|
||||
if (event->writable_handle != os::InvalidNativeHandle) {
|
||||
impl::InterProcessEventImpl::Close(event->writable_handle);
|
||||
}
|
||||
event->is_writable_handle_managed = false;
|
||||
}
|
||||
|
||||
/* Destroy the waitlist. */
|
||||
util::DestroyAt(event->multi_wait_object_list_storage);
|
||||
}
|
||||
|
||||
void AttachInterProcessEvent(InterProcessEventType *event, NativeHandle read_handle, bool read_handle_managed, NativeHandle write_handle, bool write_handle_managed, EventClearMode clear_mode) {
|
||||
AMS_ASSERT(read_handle != os::InvalidNativeHandle || write_handle != os::InvalidNativeHandle);
|
||||
|
||||
return SetupInterProcessEventType(event, read_handle, read_handle_managed, write_handle, write_handle_managed, clear_mode);
|
||||
}
|
||||
|
||||
NativeHandle DetachReadableHandleOfInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
|
||||
const NativeHandle handle = event->readable_handle;
|
||||
|
||||
event->readable_handle = os::InvalidNativeHandle;
|
||||
event->is_readable_handle_managed = false;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
NativeHandle DetachWritableHandleOfInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
|
||||
const NativeHandle handle = event->writable_handle;
|
||||
|
||||
event->writable_handle = os::InvalidNativeHandle;
|
||||
event->is_writable_handle_managed = false;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void WaitInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
|
||||
return impl::InterProcessEventImpl::Wait(event->readable_handle, event->auto_clear);
|
||||
}
|
||||
|
||||
bool TryWaitInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
|
||||
return impl::InterProcessEventImpl::TryWait(event->readable_handle, event->auto_clear);
|
||||
}
|
||||
|
||||
bool TimedWaitInterProcessEvent(InterProcessEventType *event, TimeSpan timeout) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
|
||||
|
||||
return impl::InterProcessEventImpl::TimedWait(event->readable_handle, event->auto_clear, timeout);
|
||||
}
|
||||
|
||||
void SignalInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
|
||||
|
||||
return impl::InterProcessEventImpl::Signal(event->writable_handle);
|
||||
}
|
||||
|
||||
void ClearInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
|
||||
|
||||
auto handle = event->readable_handle;
|
||||
if (handle == os::InvalidNativeHandle) {
|
||||
handle = event->writable_handle;
|
||||
}
|
||||
return impl::InterProcessEventImpl::Clear(handle);
|
||||
}
|
||||
|
||||
NativeHandle GetReadableHandleOfInterProcessEvent(const InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
|
||||
|
||||
return event->readable_handle;
|
||||
}
|
||||
|
||||
NativeHandle GetWritableHandleOfInterProcessEvent(const InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
|
||||
|
||||
return event->writable_handle;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
Result CreateInterProcessEvent(InterProcessEventType *event, EventClearMode clear_mode);
|
||||
void DestroyInterProcessEvent(InterProcessEventType *event);
|
||||
|
||||
void AttachInterProcessEvent(InterProcessEventType *event, NativeHandle read_handle, bool read_handle_managed, NativeHandle write_handle, bool write_handle_managed, EventClearMode clear_mode);
|
||||
|
||||
NativeHandle DetachReadableHandleOfInterProcessEvent(InterProcessEventType *event);
|
||||
NativeHandle DetachWritableHandleOfInterProcessEvent(InterProcessEventType *event);
|
||||
|
||||
void WaitInterProcessEvent(InterProcessEventType *event);
|
||||
bool TryWaitInterProcessEvent(InterProcessEventType *event);
|
||||
bool TimedWaitInterProcessEvent(InterProcessEventType *event, TimeSpan timeout);
|
||||
|
||||
void SignalInterProcessEvent(InterProcessEventType *event);
|
||||
void ClearInterProcessEvent(InterProcessEventType *event);
|
||||
|
||||
NativeHandle GetReadableHandleOfInterProcessEvent(const InterProcessEventType *event);
|
||||
NativeHandle GetWritableHandleOfInterProcessEvent(const InterProcessEventType *event);
|
||||
|
||||
void InitializeMultiWaitHolder(MultiWaitHolderType *multi_wait_holder, InterProcessEventType *event);
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_inter_process_event_impl.os.horizon.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_WINDOWS)
|
||||
#include "os_inter_process_event_impl.os.windows.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_LINUX)
|
||||
#include "os_inter_process_event_impl.os.linux.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_MACOS)
|
||||
#include "os_inter_process_event_impl.os.macos.hpp"
|
||||
#else
|
||||
#error "Unknown OS for ams::os::InterProcessEventImpl"
|
||||
#endif
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_inter_process_event.hpp"
|
||||
#include "os_inter_process_event_impl.os.horizon.hpp"
|
||||
#include "os_timeout_helper.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
Result InterProcessEventHorizonImpl::Create(NativeHandle *out_write, NativeHandle *out_read) {
|
||||
/* Create the event handles. */
|
||||
svc::Handle wh, rh;
|
||||
R_TRY_CATCH(svc::CreateEvent(std::addressof(wh), std::addressof(rh))) {
|
||||
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
*out_write = wh;
|
||||
*out_read = rh;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void InterProcessEventHorizonImpl::Close(NativeHandle handle) {
|
||||
if (handle != os::InvalidNativeHandle) {
|
||||
R_ABORT_UNLESS(svc::CloseHandle(handle));
|
||||
}
|
||||
}
|
||||
|
||||
void InterProcessEventHorizonImpl::Signal(NativeHandle handle) {
|
||||
R_ABORT_UNLESS(svc::SignalEvent(handle));
|
||||
}
|
||||
|
||||
void InterProcessEventHorizonImpl::Clear(NativeHandle handle) {
|
||||
R_ABORT_UNLESS(svc::ClearEvent(handle));
|
||||
}
|
||||
|
||||
void InterProcessEventHorizonImpl::Wait(NativeHandle handle, bool auto_clear) {
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), static_cast<svc::Handle *>(std::addressof(handle)), 1, svc::WaitInfinite);
|
||||
if (R_SUCCEEDED(res)) {
|
||||
/* Clear, if we must. */
|
||||
if (auto_clear) {
|
||||
R_TRY_CATCH(svc::ResetSignal(handle)) {
|
||||
/* Some other thread might have caught this before we did. */
|
||||
R_CATCH(svc::ResultInvalidState) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
bool InterProcessEventHorizonImpl::TryWait(NativeHandle handle, bool auto_clear) {
|
||||
/* If we're auto clear, just try to reset. */
|
||||
if (auto_clear) {
|
||||
return R_SUCCEEDED(svc::ResetSignal(handle));
|
||||
}
|
||||
|
||||
/* Not auto-clear. */
|
||||
while (true) {
|
||||
/* Continuously wait, until success or timeout. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), static_cast<svc::Handle *>(std::addressof(handle)), 1, 0);
|
||||
|
||||
/* If we succeeded, we're signaled. */
|
||||
if (R_SUCCEEDED(res)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If we timed out, we're not signaled. */
|
||||
if (svc::ResultTimedOut::Includes(res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
bool InterProcessEventHorizonImpl::TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout) {
|
||||
TimeoutHelper timeout_helper(timeout);
|
||||
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), static_cast<svc::Handle *>(std::addressof(handle)), 1, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds());
|
||||
if (R_SUCCEEDED(res)) {
|
||||
/* Clear, if we must. */
|
||||
if (auto_clear) {
|
||||
R_TRY_CATCH(svc::ResetSignal(handle)) {
|
||||
/* Some other thread might have caught this before we did. */
|
||||
R_CATCH(svc::ResultInvalidState) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (svc::ResultTimedOut::Includes(res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterProcessEventHorizonImpl {
|
||||
public:
|
||||
static Result Create(NativeHandle *out_write, NativeHandle *out_read);
|
||||
static void Close(NativeHandle handle);
|
||||
static void Signal(NativeHandle handle);
|
||||
static void Clear(NativeHandle handle);
|
||||
static void Wait(NativeHandle handle, bool auto_clear);
|
||||
static bool TryWait(NativeHandle handle, bool auto_clear);
|
||||
static bool TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout);
|
||||
};
|
||||
|
||||
using InterProcessEventImpl = InterProcessEventHorizonImpl;
|
||||
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_inter_process_event.hpp"
|
||||
#include "os_inter_process_event_impl.os.linux.hpp"
|
||||
#include "os_timeout_helper.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <poll.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
bool PollEvent(NativeHandle handle, s64 ns) {
|
||||
struct pollfd pfd;
|
||||
pfd.fd = handle;
|
||||
pfd.events = POLLIN;
|
||||
pfd.revents = 0;
|
||||
|
||||
/* Determine timeout. */
|
||||
constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds();
|
||||
struct timespec ts = { .tv_sec = (ns / NanoSecondsPerSecond), .tv_nsec = ns % NanoSecondsPerSecond };
|
||||
|
||||
s32 res;
|
||||
do {
|
||||
res = ::ppoll(std::addressof(pfd), 1, ns >= 0 ? std::addressof(ts) : nullptr, nullptr);
|
||||
} while (res < 0 && errno == EINTR);
|
||||
|
||||
AMS_ASSERT(res == 0 || res == 1);
|
||||
|
||||
const bool signaled = pfd.revents & POLLIN;
|
||||
AMS_ASSERT(signaled == (res == 1));
|
||||
return signaled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool InterProcessEventLinuxImpl::ResetEventSignal(NativeHandle handle) {
|
||||
u64 dummy;
|
||||
s32 res;
|
||||
do {
|
||||
res = ::read(handle, std::addressof(dummy), sizeof(dummy));
|
||||
} while (res < 0 && errno == EINTR);
|
||||
|
||||
AMS_ASSERT(res == sizeof(u64) || (res < 0 && errno == EAGAIN));
|
||||
if (res == sizeof(u64)) {
|
||||
AMS_ASSERT(dummy > 0);
|
||||
}
|
||||
|
||||
return res == sizeof(u64);
|
||||
}
|
||||
|
||||
Result InterProcessEventLinuxImpl::CreateSingle(NativeHandle *out_event) {
|
||||
/* Create the event handle. */
|
||||
os::NativeHandle event;
|
||||
do {
|
||||
event = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
||||
} while (event < 0 && errno == EINTR);
|
||||
R_UNLESS(event != os::InvalidNativeHandle, os::ResultOutOfResource());
|
||||
|
||||
/* Set the output. */
|
||||
*out_event = event;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result InterProcessEventLinuxImpl::Create(NativeHandle *out_write, NativeHandle *out_read) {
|
||||
/* Create the writable handle. */
|
||||
os::NativeHandle write;
|
||||
R_TRY(CreateSingle(std::addressof(write)));
|
||||
ON_RESULT_FAILURE { Close(write); };
|
||||
|
||||
/* Create the read handle. */
|
||||
os::NativeHandle read;
|
||||
do {
|
||||
read = ::dup(write);
|
||||
} while (read < 0 && errno == EINTR);
|
||||
R_UNLESS(read != os::InvalidNativeHandle, os::ResultOutOfResource());
|
||||
|
||||
/* Set the output. */
|
||||
*out_write = write;
|
||||
*out_read = read;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void InterProcessEventLinuxImpl::Close(NativeHandle handle) {
|
||||
if (handle != os::InvalidNativeHandle) {
|
||||
s32 ret;
|
||||
do {
|
||||
ret = ::close(handle);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
AMS_ASSERT(ret == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void InterProcessEventLinuxImpl::Signal(NativeHandle handle) {
|
||||
const u64 counter_add = 1;
|
||||
|
||||
s32 ret;
|
||||
do {
|
||||
ret = ::write(handle, std::addressof(counter_add), sizeof(counter_add));
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
AMS_ASSERT(ret == sizeof(counter_add));
|
||||
}
|
||||
|
||||
void InterProcessEventLinuxImpl::Clear(NativeHandle handle) {
|
||||
ResetEventSignal(handle);
|
||||
}
|
||||
|
||||
void InterProcessEventLinuxImpl::Wait(NativeHandle handle, bool auto_clear) {
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
auto ret = PollEvent(handle, -1);
|
||||
AMS_ASSERT(ret);
|
||||
AMS_UNUSED(ret);
|
||||
|
||||
/* If we're not obligated to clear, we're done. */
|
||||
if (!auto_clear) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to reset. */
|
||||
if (ResetEventSignal(handle)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InterProcessEventLinuxImpl::TryWait(NativeHandle handle, bool auto_clear) {
|
||||
/* If we're auto clear, just try to reset. */
|
||||
if (auto_clear) {
|
||||
return ResetEventSignal(handle);
|
||||
}
|
||||
|
||||
return PollEvent(handle, 0);
|
||||
}
|
||||
|
||||
bool InterProcessEventLinuxImpl::TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout) {
|
||||
TimeoutHelper timeout_helper(timeout);
|
||||
|
||||
do {
|
||||
if (const auto res = PollEvent(handle, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds()); res) {
|
||||
/* If we're not obligated to clear, we're done. */
|
||||
if (!auto_clear) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Try to reset. */
|
||||
if (ResetEventSignal(handle)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while (!timeout_helper.TimedOut());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterProcessEventLinuxImpl {
|
||||
public:
|
||||
static Result CreateSingle(NativeHandle *out_event);
|
||||
static Result Create(NativeHandle *out_write, NativeHandle *out_read);
|
||||
static void Close(NativeHandle handle);
|
||||
static void Signal(NativeHandle handle);
|
||||
static void Clear(NativeHandle handle);
|
||||
static void Wait(NativeHandle handle, bool auto_clear);
|
||||
static bool TryWait(NativeHandle handle, bool auto_clear);
|
||||
static bool TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout);
|
||||
private:
|
||||
static bool ResetEventSignal(NativeHandle handle);
|
||||
};
|
||||
|
||||
using InterProcessEventImpl = InterProcessEventLinuxImpl;
|
||||
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_inter_process_event.hpp"
|
||||
#include "os_inter_process_event_impl.os.macos.hpp"
|
||||
#include "os_timeout_helper.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
/* On macOS, the maximum size of a pipe buffer is 64_KB. */
|
||||
static constexpr size_t PipeBufferSizeMax = 64_KB;
|
||||
|
||||
constinit char g_shared_pipe_read_buffer[PipeBufferSizeMax];
|
||||
|
||||
bool PollEvent(NativeHandle handle, s64 ns) {
|
||||
struct pollfd pfd;
|
||||
pfd.fd = handle;
|
||||
pfd.events = POLLIN;
|
||||
pfd.revents = 0;
|
||||
|
||||
/* Determine timeout. */
|
||||
constexpr s64 NanoSecondsPerMilliSecond = TimeSpan::FromMilliSeconds(1).GetNanoSeconds();
|
||||
|
||||
/* TODO: Will macos ever support ppoll? */
|
||||
const int timeout = static_cast<int>(ns >= 0 ? (ns / NanoSecondsPerMilliSecond) : -1);
|
||||
|
||||
s32 res;
|
||||
do {
|
||||
res = ::poll(std::addressof(pfd), 1, timeout);
|
||||
} while (res < 0 && errno == EINTR);
|
||||
|
||||
AMS_ASSERT(res == 0 || res == 1);
|
||||
|
||||
const bool signaled = pfd.revents & POLLIN;
|
||||
AMS_ASSERT(signaled == (res == 1));
|
||||
return signaled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool InterProcessEventMacosImpl::ResetEventSignal(NativeHandle handle) {
|
||||
s32 res;
|
||||
do {
|
||||
res = ::read(handle, g_shared_pipe_read_buffer, sizeof(g_shared_pipe_read_buffer));
|
||||
} while (res < 0 && errno == EINTR);
|
||||
|
||||
if (res > 0) {
|
||||
AMS_ASSERT(res <= static_cast<s32>(sizeof(g_shared_pipe_read_buffer)));
|
||||
} else {
|
||||
AMS_ASSERT(res < 0);
|
||||
AMS_ASSERT(errno == EAGAIN);
|
||||
}
|
||||
|
||||
return res > 0;
|
||||
}
|
||||
|
||||
Result InterProcessEventMacosImpl::Create(NativeHandle *out_write, NativeHandle *out_read) {
|
||||
/* Create the handles. */
|
||||
os::NativeHandle handles[2];
|
||||
s32 res;
|
||||
do {
|
||||
res = ::pipe(handles);
|
||||
} while (res < 0 && errno == EINTR);
|
||||
R_UNLESS(res == 0, os::ResultOutOfResource());
|
||||
ON_RESULT_FAILURE { Close(handles[0]); Close(handles[1]); };
|
||||
|
||||
/* Set as non-blocking. */
|
||||
do {
|
||||
res = ::fcntl(handles[0], F_SETFL, O_NONBLOCK);
|
||||
} while (res < 0 && errno == EINTR);
|
||||
R_UNLESS(res == 0, os::ResultOutOfResource());
|
||||
|
||||
do {
|
||||
res = ::fcntl(handles[1], F_SETFL, O_NONBLOCK);
|
||||
} while (res < 0 && errno == EINTR);
|
||||
R_UNLESS(res == 0, os::ResultOutOfResource());
|
||||
|
||||
/* Set the output. */
|
||||
*out_read = handles[0];
|
||||
*out_write = handles[1];
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void InterProcessEventMacosImpl::Close(NativeHandle handle) {
|
||||
if (handle != os::InvalidNativeHandle) {
|
||||
s32 ret;
|
||||
do {
|
||||
ret = ::close(handle);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
AMS_ASSERT(ret == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void InterProcessEventMacosImpl::Signal(NativeHandle handle) {
|
||||
const u8 data = 0xCC;
|
||||
|
||||
s32 ret;
|
||||
do {
|
||||
ret = ::write(handle, std::addressof(data), sizeof(data));
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
AMS_ASSERT(ret == sizeof(data) || (ret < 0 && errno == EAGAIN));
|
||||
}
|
||||
|
||||
void InterProcessEventMacosImpl::Clear(NativeHandle handle) {
|
||||
ResetEventSignal(handle);
|
||||
}
|
||||
|
||||
void InterProcessEventMacosImpl::Wait(NativeHandle handle, bool auto_clear) {
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
auto ret = PollEvent(handle, -1);
|
||||
AMS_ASSERT(ret);
|
||||
AMS_UNUSED(ret);
|
||||
|
||||
/* If we're not obligated to clear, we're done. */
|
||||
if (!auto_clear) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to reset. */
|
||||
if (ResetEventSignal(handle)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InterProcessEventMacosImpl::TryWait(NativeHandle handle, bool auto_clear) {
|
||||
/* If we're auto clear, just try to reset. */
|
||||
if (auto_clear) {
|
||||
return ResetEventSignal(handle);
|
||||
}
|
||||
|
||||
return PollEvent(handle, 0);
|
||||
}
|
||||
|
||||
bool InterProcessEventMacosImpl::TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout) {
|
||||
TimeoutHelper timeout_helper(timeout);
|
||||
|
||||
do {
|
||||
if (const auto res = PollEvent(handle, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds()); res) {
|
||||
/* If we're not obligated to clear, we're done. */
|
||||
if (!auto_clear) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Try to reset. */
|
||||
if (ResetEventSignal(handle)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while (!timeout_helper.TimedOut());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterProcessEventMacosImpl {
|
||||
public:
|
||||
static Result Create(NativeHandle *out_write, NativeHandle *out_read);
|
||||
static void Close(NativeHandle handle);
|
||||
static void Signal(NativeHandle handle);
|
||||
static void Clear(NativeHandle handle);
|
||||
static void Wait(NativeHandle handle, bool auto_clear);
|
||||
static bool TryWait(NativeHandle handle, bool auto_clear);
|
||||
static bool TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout);
|
||||
private:
|
||||
static bool ResetEventSignal(NativeHandle handle);
|
||||
};
|
||||
|
||||
using InterProcessEventImpl = InterProcessEventMacosImpl;
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/windows.hpp>
|
||||
#include "os_inter_process_event.hpp"
|
||||
#include "os_inter_process_event_impl.os.windows.hpp"
|
||||
#include "os_giant_lock.hpp"
|
||||
#include "os_timeout_helper.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
bool InterProcessEventWindowsImpl::ResetEventSignal(NativeHandle handle) {
|
||||
std::scoped_lock lk(GetGiantLock());
|
||||
|
||||
if (auto ret = ::WaitForSingleObject(handle, 0); ret == WAIT_OBJECT_0) {
|
||||
::ResetEvent(handle);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result InterProcessEventWindowsImpl::Create(NativeHandle *out_write, NativeHandle *out_read) {
|
||||
/* Create the writable handle. */
|
||||
auto write = ::CreateEvent(nullptr, true, false, nullptr);
|
||||
AMS_ASSERT(write != os::InvalidNativeHandle);
|
||||
|
||||
/* Create the read handle. */
|
||||
os::NativeHandle read;
|
||||
const auto cur_proc = ::GetCurrentProcess();
|
||||
const auto ret = ::DuplicateHandle(cur_proc, write, cur_proc, std::addressof(read), 0, false, DUPLICATE_SAME_ACCESS);
|
||||
AMS_ASSERT(ret != 0);
|
||||
AMS_UNUSED(ret);
|
||||
|
||||
/* Set the output. */
|
||||
*out_write = write;
|
||||
*out_read = read;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void InterProcessEventWindowsImpl::Close(NativeHandle handle) {
|
||||
if (handle != os::InvalidNativeHandle) {
|
||||
const auto ret = ::CloseHandle(handle);
|
||||
AMS_ASSERT(ret != 0);
|
||||
AMS_UNUSED(ret);
|
||||
}
|
||||
}
|
||||
|
||||
void InterProcessEventWindowsImpl::Signal(NativeHandle handle) {
|
||||
const auto ret = ::SetEvent(handle);
|
||||
AMS_ASSERT(ret != 0);
|
||||
AMS_UNUSED(ret);
|
||||
}
|
||||
|
||||
void InterProcessEventWindowsImpl::Clear(NativeHandle handle) {
|
||||
const auto ret = ::ResetEvent(handle);
|
||||
AMS_ASSERT(ret != 0);
|
||||
AMS_UNUSED(ret);
|
||||
}
|
||||
|
||||
void InterProcessEventWindowsImpl::Wait(NativeHandle handle, bool auto_clear) {
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
auto ret = ::WaitForSingleObject(handle, INFINITE);
|
||||
AMS_ASSERT(ret == WAIT_OBJECT_0);
|
||||
AMS_UNUSED(ret);
|
||||
|
||||
/* If we're not obligated to clear, we're done. */
|
||||
if (!auto_clear) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to reset. */
|
||||
if (ResetEventSignal(handle)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InterProcessEventWindowsImpl::TryWait(NativeHandle handle, bool auto_clear) {
|
||||
/* If we're auto clear, just try to reset. */
|
||||
if (auto_clear) {
|
||||
return ResetEventSignal(handle);
|
||||
}
|
||||
|
||||
const auto ret = ::WaitForSingleObject(handle, 0);
|
||||
AMS_ASSERT((ret == WAIT_OBJECT_0) || (ret == WAIT_TIMEOUT));
|
||||
|
||||
return ret == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
bool InterProcessEventWindowsImpl::TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout) {
|
||||
TimeoutHelper timeout_helper(timeout);
|
||||
|
||||
do {
|
||||
if (const auto res = ::WaitForSingleObject(handle, timeout_helper.GetTimeLeftOnTarget()); res == WAIT_OBJECT_0) {
|
||||
/* If we're not obligated to clear, we're done. */
|
||||
if (!auto_clear) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Try to reset. */
|
||||
if (ResetEventSignal(handle)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while (!timeout_helper.TimedOut());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterProcessEventWindowsImpl {
|
||||
public:
|
||||
static Result Create(NativeHandle *out_write, NativeHandle *out_read);
|
||||
static void Close(NativeHandle handle);
|
||||
static void Signal(NativeHandle handle);
|
||||
static void Clear(NativeHandle handle);
|
||||
static void Wait(NativeHandle handle, bool auto_clear);
|
||||
static bool TryWait(NativeHandle handle, bool auto_clear);
|
||||
static bool TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout);
|
||||
private:
|
||||
static bool ResetEventSignal(NativeHandle handle);
|
||||
};
|
||||
|
||||
using InterProcessEventImpl = InterProcessEventWindowsImpl;
|
||||
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_disable_counter.os.horizon.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE void PrefetchForBusyMutex(u32 *p) {
|
||||
/* Nintendo does PRFM pstl1keep. */
|
||||
__builtin_prefetch(p, 1);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void WaitForEventsForBusyMutex() {
|
||||
__asm__ __volatile__("wfe" ::: "memory");
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 LoadExclusiveForBusyMutex(u32 *p) {
|
||||
u32 v;
|
||||
__asm__ __volatile__("ldaxr %w[v], %[p]" : [v]"=&r"(v) : [p]"Q"(*p) : "memory");
|
||||
return v;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool StoreExclusiveForBusyMutex(u32 *p, u32 v) {
|
||||
int result;
|
||||
__asm__ __volatile__("stxr %w[result], %w[v], %[p]" : [result]"=&r"(result) : [v]"r"(v), [p]"Q"(*p) : "memory");
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void ClearExclusiveForBusyMutex() {
|
||||
__asm__ __volatile__("clrex" ::: "memory");
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void StoreUnlockValueForBusyMutex(u32 *p) {
|
||||
__asm__ __volatile__("stlr wzr, %[p]" :: [p]"Q"(*p) : "memory");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InternalBusyMutexImpl::Lock() {
|
||||
/* Get the thread local region. */
|
||||
auto * const tlr = svc::GetThreadLocalRegion();
|
||||
|
||||
/* Determine disable counters. */
|
||||
const auto cur_dc = tlr->disable_count;
|
||||
AMS_ASSERT(cur_dc < std::numeric_limits<decltype(cur_dc)>::max());
|
||||
const auto next_dc = cur_dc + 1;
|
||||
|
||||
/* Check that we're allowed to use busy mutexes. */
|
||||
CallCheckBusyMutexPermission();
|
||||
|
||||
/* Get pointer to our value. */
|
||||
u32 * const p = std::addressof(m_value);
|
||||
|
||||
/* Pre-fetch the busy mutex. */
|
||||
PrefetchForBusyMutex(p);
|
||||
|
||||
/* Acquire the busy mutex. */
|
||||
while (true) {
|
||||
/* Set the updated disable counter. */
|
||||
tlr->disable_count = next_dc;
|
||||
|
||||
/* Try to acquire. */
|
||||
const u32 v = LoadExclusiveForBusyMutex(p);
|
||||
if (AMS_LIKELY(v == 0) && AMS_LIKELY(StoreExclusiveForBusyMutex(p, 1))) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Reset the disable counter, since we failed to acquire. */
|
||||
tlr->disable_count = cur_dc;
|
||||
|
||||
/* If we don't hold any other busy mutexes, acknowledge any interrupts that occurred while we tried to acquire the lock. */
|
||||
if (cur_dc == 0 && tlr->interrupt_flag) {
|
||||
svc::SynchronizePreemptionState();
|
||||
}
|
||||
|
||||
/* If the lock is held by another core, wait for it to be released. */
|
||||
if (v != 0) {
|
||||
WaitForEventsForBusyMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InternalBusyMutexImpl::TryLock() {
|
||||
/* Get the thread local region. */
|
||||
auto * const tlr = svc::GetThreadLocalRegion();
|
||||
|
||||
/* Determine disable counters. */
|
||||
const auto cur_dc = tlr->disable_count;
|
||||
AMS_ASSERT(cur_dc < std::numeric_limits<decltype(cur_dc)>::max());
|
||||
const auto next_dc = cur_dc + 1;
|
||||
|
||||
/* Check that we're allowed to use busy mutexes. */
|
||||
CallCheckBusyMutexPermission();
|
||||
|
||||
/* Get pointer to our value. */
|
||||
u32 * const p = std::addressof(m_value);
|
||||
|
||||
/* Pre-fetch the busy mutex. */
|
||||
PrefetchForBusyMutex(p);
|
||||
|
||||
/* Try to acquire the busy mutex. */
|
||||
while (true) {
|
||||
/* Set the updated disable counter. */
|
||||
tlr->disable_count = next_dc;
|
||||
|
||||
/* Ensure we do whatever cleanup we need to. */
|
||||
auto release_guard = SCOPE_GUARD {
|
||||
/* Reset disable counter. */
|
||||
tlr->disable_count = cur_dc;
|
||||
|
||||
/* If we don't hold any other busy mutexes, acknowledge any interrupts that occurred while we tried to acquire the lock. */
|
||||
if (cur_dc == 0 && tlr->interrupt_flag) {
|
||||
svc::SynchronizePreemptionState();
|
||||
}
|
||||
};
|
||||
|
||||
/* Try to acquire. */
|
||||
const u32 v = LoadExclusiveForBusyMutex(p);
|
||||
if (AMS_UNLIKELY(v != 0)) {
|
||||
ClearExclusiveForBusyMutex();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AMS_LIKELY(StoreExclusiveForBusyMutex(p, 1))) {
|
||||
/* We successfully acquired the busy mutex. */
|
||||
release_guard.Cancel();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InternalBusyMutexImpl::Unlock() {
|
||||
/* Get pointer to our value. */
|
||||
u32 * const p = std::addressof(m_value);
|
||||
|
||||
/* Unlock the mutex. */
|
||||
StoreUnlockValueForBusyMutex(p);
|
||||
|
||||
/* Get the thread local region. */
|
||||
auto * const tlr = svc::GetThreadLocalRegion();
|
||||
|
||||
/* Determine disable counters. */
|
||||
const auto cur_dc = tlr->disable_count;
|
||||
AMS_ASSERT(cur_dc != 0);
|
||||
const auto next_dc = cur_dc - 1;
|
||||
|
||||
/* Decrement disable count. */
|
||||
tlr->disable_count = next_dc;
|
||||
|
||||
/* If we don't hold any other busy mutexes, acknowledge any interrupts that occurred while we held the lock. */
|
||||
if (next_dc == 0 && tlr->interrupt_flag) {
|
||||
svc::SynchronizePreemptionState();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_timeout_helper.hpp"
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void InternalConditionVariableImpl::Signal() {
|
||||
ams::svc::SignalProcessWideKey(reinterpret_cast<uintptr_t>(std::addressof(m_value)), 1);
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Broadcast() {
|
||||
ams::svc::SignalProcessWideKey(reinterpret_cast<uintptr_t>(std::addressof(m_value)), -1);
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Wait(InternalCriticalSection *cs) {
|
||||
const auto cur_handle = GetCurrentThreadHandle();
|
||||
AMS_ASSERT((cs->Get()->m_thread_handle & ~ams::svc::HandleWaitMask) == cur_handle);
|
||||
|
||||
R_ABORT_UNLESS(ams::svc::WaitProcessWideKeyAtomic(reinterpret_cast<uintptr_t>(std::addressof(cs->Get()->m_thread_handle)), reinterpret_cast<uintptr_t>(std::addressof(m_value)), cur_handle, -1));
|
||||
}
|
||||
|
||||
ConditionVariableStatus InternalConditionVariableImpl::TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper) {
|
||||
const auto cur_handle = GetCurrentThreadHandle();
|
||||
AMS_ASSERT((cs->Get()->m_thread_handle & ~ams::svc::HandleWaitMask) == cur_handle);
|
||||
|
||||
const TimeSpan left = timeout_helper.GetTimeLeftOnTarget();
|
||||
if (left > 0) {
|
||||
R_TRY_CATCH(ams::svc::WaitProcessWideKeyAtomic(reinterpret_cast<uintptr_t>(std::addressof(cs->Get()->m_thread_handle)), reinterpret_cast<uintptr_t>(std::addressof(m_value)), cur_handle, left.GetNanoSeconds())) {
|
||||
R_CATCH(svc::ResultTimedOut) {
|
||||
cs->Enter();
|
||||
return ConditionVariableStatus::TimedOut;
|
||||
}
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
return ConditionVariableStatus::Success;
|
||||
} else {
|
||||
return ConditionVariableStatus::TimedOut;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/windows.hpp>
|
||||
#include "os_internal_critical_section_impl.os.windows.hpp"
|
||||
#include "os_timeout_helper.hpp"
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
struct WindowsConditionVariable {
|
||||
CONDITION_VARIABLE cv;
|
||||
};
|
||||
|
||||
void InternalConditionVariableImpl::Initialize() {
|
||||
::InitializeConditionVariable(std::addressof(util::GetReference(m_windows_cv_storage).cv));
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Finalize() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Signal() {
|
||||
::WakeConditionVariable(std::addressof(util::GetReference(m_windows_cv_storage).cv));
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Broadcast() {
|
||||
::WakeAllConditionVariable(std::addressof(util::GetReference(m_windows_cv_storage).cv));
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Wait(InternalCriticalSection *cs) {
|
||||
::SleepConditionVariableCS(std::addressof(util::GetReference(m_windows_cv_storage).cv), std::addressof(util::GetReference(cs->Get()->m_windows_critical_section_storage).cs), INFINITE);
|
||||
}
|
||||
|
||||
ConditionVariableStatus InternalConditionVariableImpl::TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper) {
|
||||
const auto ret = ::SleepConditionVariableCS(std::addressof(util::GetReference(m_windows_cv_storage).cv), std::addressof(util::GetReference(cs->Get()->m_windows_critical_section_storage).cs), timeout_helper.GetTimeLeftOnTarget());
|
||||
if (ret == 0) {
|
||||
if (::GetLastError() == ERROR_TIMEOUT) {
|
||||
return ConditionVariableStatus::TimedOut;
|
||||
}
|
||||
}
|
||||
|
||||
return ConditionVariableStatus::Success;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_timeout_helper.hpp"
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
#if defined(AMS_OS_IMPL_USE_PTHREADS)
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Initialize() {
|
||||
/* TODO: What should be done here? */
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Finalize() {
|
||||
/* TODO: What should be done here? */
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Signal() {
|
||||
AMS_ABORT_UNLESS(pthread_cond_signal(std::addressof(m_pthread_cond)) == 0);
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Broadcast() {
|
||||
AMS_ABORT_UNLESS(pthread_cond_broadcast(std::addressof(m_pthread_cond)) == 0);
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Wait(InternalCriticalSection *cs) {
|
||||
AMS_ABORT_UNLESS(pthread_cond_wait(std::addressof(m_pthread_cond), std::addressof(cs->Get()->m_pthread_mutex)) == 0);
|
||||
}
|
||||
|
||||
ConditionVariableStatus InternalConditionVariableImpl::TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper) {
|
||||
struct timespec ts;
|
||||
const auto gettime_res = clock_gettime(CLOCK_REALTIME, std::addressof(ts));
|
||||
AMS_ASSERT(gettime_res == 0);
|
||||
AMS_UNUSED(gettime_res);
|
||||
|
||||
constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds();
|
||||
const s64 ns = timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds();
|
||||
ts.tv_sec += (ns / NanoSecondsPerSecond);
|
||||
ts.tv_nsec += (ns % NanoSecondsPerSecond);
|
||||
if (ts.tv_nsec >= NanoSecondsPerSecond) {
|
||||
ts.tv_sec += ts.tv_nsec / NanoSecondsPerSecond;
|
||||
ts.tv_nsec %= NanoSecondsPerSecond;
|
||||
}
|
||||
|
||||
const auto res = pthread_cond_timedwait(std::addressof(m_pthread_cond), std::addressof(cs->Get()->m_pthread_mutex), std::addressof(ts));
|
||||
|
||||
if (res != 0) {
|
||||
AMS_ABORT_UNLESS(res == ETIMEDOUT);
|
||||
return ConditionVariableStatus::TimedOut;
|
||||
}
|
||||
|
||||
return ConditionVariableStatus::Success;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_timeout_helper.hpp"
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
|
||||
void InternalCriticalSectionImpl::Enter() {
|
||||
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
|
||||
|
||||
/* Use the libnx impl. */
|
||||
static_assert(std::is_same<decltype(m_thread_handle), ::Mutex>::value);
|
||||
return ::mutexLock(std::addressof(m_thread_handle));
|
||||
}
|
||||
|
||||
bool InternalCriticalSectionImpl::TryEnter() {
|
||||
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
|
||||
|
||||
/* Use the libnx impl. */
|
||||
static_assert(std::is_same<decltype(m_thread_handle), ::Mutex>::value);
|
||||
return ::mutexTryLock(std::addressof(m_thread_handle));
|
||||
}
|
||||
|
||||
void InternalCriticalSectionImpl::Leave() {
|
||||
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
|
||||
|
||||
/* Use the libnx impl. */
|
||||
static_assert(std::is_same<decltype(m_thread_handle), ::Mutex>::value);
|
||||
return ::mutexUnlock(std::addressof(m_thread_handle));
|
||||
}
|
||||
|
||||
bool InternalCriticalSectionImpl::IsLockedByCurrentThread() const {
|
||||
/* Use the libnx impl. */
|
||||
static_assert(std::is_same<decltype(m_thread_handle), ::Mutex>::value);
|
||||
return ::mutexIsLockedByCurrentThread(std::addressof(m_thread_handle));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error "Architecture not yet supported for os::InternalCriticalSectionImpl"
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/windows.hpp>
|
||||
#include "os_internal_critical_section_impl.os.windows.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void InternalCriticalSectionImpl::Initialize() {
|
||||
::InitializeCriticalSection(std::addressof(util::GetReference(m_windows_critical_section_storage).cs));
|
||||
}
|
||||
|
||||
void InternalCriticalSectionImpl::Finalize() {
|
||||
::DeleteCriticalSection(std::addressof(util::GetReference(m_windows_critical_section_storage).cs));
|
||||
}
|
||||
|
||||
void InternalCriticalSectionImpl::Enter() {
|
||||
::EnterCriticalSection(std::addressof(util::GetReference(m_windows_critical_section_storage).cs));
|
||||
}
|
||||
|
||||
bool InternalCriticalSectionImpl::TryEnter() {
|
||||
return ::TryEnterCriticalSection(std::addressof(util::GetReference(m_windows_critical_section_storage).cs)) != 0;
|
||||
}
|
||||
|
||||
void InternalCriticalSectionImpl::Leave() {
|
||||
return ::LeaveCriticalSection(std::addressof(util::GetReference(m_windows_critical_section_storage).cs));
|
||||
}
|
||||
|
||||
bool InternalCriticalSectionImpl::IsLockedByCurrentThread() const {
|
||||
/* Get the cs. */
|
||||
CRITICAL_SECTION * const cs = std::addressof(util::GetReference(m_windows_critical_section_storage).cs);
|
||||
|
||||
/* If the critical section has no owning thread, it's not locked. */
|
||||
if (cs->OwningThread == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If it has an owner, TryLock() will succeed if and only if we own the critical section. */
|
||||
if (::TryEnterCriticalSection(cs) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We now hold the critical section. To avoid a race, check that we didn't just acquire it by chance. */
|
||||
const auto holds_lock = cs->RecursionCount > 1;
|
||||
|
||||
/* Leave, since we just successfully entered. */
|
||||
::LeaveCriticalSection(cs);
|
||||
|
||||
return holds_lock;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
struct WindowsCriticalSection {
|
||||
mutable CRITICAL_SECTION cs;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#if defined(ATMOSPHERE_OS_LINUX)
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(AMS_OS_IMPL_USE_PTHREADS)
|
||||
namespace ams::os::impl {
|
||||
|
||||
#if defined(AMS_OS_INTERNAL_CRITICAL_SECTION_IMPL_CAN_CHECK_LOCKED_BY_CURRENT_THREAD)
|
||||
namespace {
|
||||
|
||||
#if defined(ATMOSPHERE_OS_LINUX)
|
||||
|
||||
template<typename T>
|
||||
concept IsGlibcPthreadMutexImplementationWithOwnerField = requires (T &t) {
|
||||
{ t.__data.__owner } -> std::convertible_to<int>;
|
||||
};
|
||||
|
||||
static_assert(IsGlibcPthreadMutexImplementationWithOwnerField<pthread_mutex_t>);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void InternalCriticalSectionImpl::Initialize() {
|
||||
/* TODO: What should be done here? */
|
||||
}
|
||||
|
||||
void InternalCriticalSectionImpl::Finalize() {
|
||||
/* TODO: What should be done here? */
|
||||
}
|
||||
|
||||
void InternalCriticalSectionImpl::Enter() {
|
||||
AMS_ABORT_UNLESS(pthread_mutex_lock(std::addressof(m_pthread_mutex)) == 0);
|
||||
}
|
||||
|
||||
bool InternalCriticalSectionImpl::TryEnter() {
|
||||
return pthread_mutex_trylock(std::addressof(m_pthread_mutex)) == 0;
|
||||
}
|
||||
|
||||
void InternalCriticalSectionImpl::Leave() {
|
||||
AMS_ABORT_UNLESS(pthread_mutex_unlock(std::addressof(m_pthread_mutex)) == 0);
|
||||
}
|
||||
|
||||
#if defined(AMS_OS_INTERNAL_CRITICAL_SECTION_IMPL_CAN_CHECK_LOCKED_BY_CURRENT_THREAD)
|
||||
bool InternalCriticalSectionImpl::IsLockedByCurrentThread() const {
|
||||
/* TODO: This should be less of a terrible hack. */
|
||||
#if defined(ATMOSPHERE_OS_LINUX)
|
||||
return m_pthread_mutex.__data.__owner == ::syscall(SYS_gettid);
|
||||
#else
|
||||
#error "Unknown OS/underlying implementation for pthread InternalCriticalSectionImpl::IsLockedByCurrentThread()"
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u8 LightEventState_NotSignaled = -1;
|
||||
constexpr inline u8 LightEventState_Invalid = 0;
|
||||
constexpr inline u8 LightEventState_Signaled = 1;
|
||||
|
||||
static_assert(LightEventState_NotSignaled == 0xFF);
|
||||
|
||||
ALWAYS_INLINE bool AtomicAutoClearLightEvent(std::atomic<u8> &signal_state) {
|
||||
u8 expected = LightEventState_Signaled;
|
||||
return signal_state.compare_exchange_strong(expected, LightEventState_NotSignaled);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 GetBroadcastCounterUnsafe(u16 &low, u8 &high) {
|
||||
const u32 upper = high;
|
||||
return (upper << BITSIZEOF(low)) | low;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void IncrementBroadcastCounterUnsafe(u16 &low, u8 &high) {
|
||||
if ((++low) == 0) {
|
||||
++high;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::Initialize(bool signaled) {
|
||||
/* Set broadcast counter. */
|
||||
m_counter_low = 0;
|
||||
m_counter_high = 0;
|
||||
|
||||
/* Set initial state. */
|
||||
m_signal_state = signaled ? LightEventState_Signaled : LightEventState_NotSignaled;
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::Finalize() {
|
||||
/* Set final state. */
|
||||
m_signal_state = LightEventState_Invalid;
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::SignalWithAutoClear() {
|
||||
/* Signal only if we need to. */
|
||||
if (m_signal_state.load(std::memory_order_acquire) == LightEventState_NotSignaled) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_cs);
|
||||
|
||||
/* Set our state to signaled. */
|
||||
m_signal_state = LightEventState_Signaled;
|
||||
|
||||
/* Signal to any waiters. */
|
||||
m_cv.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::SignalWithManualClear() {
|
||||
/* Signal only if we need to. */
|
||||
if (m_signal_state.load(std::memory_order_acquire) == LightEventState_NotSignaled) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_cs);
|
||||
|
||||
/* Set our state to signaled. */
|
||||
m_signal_state = LightEventState_Signaled;
|
||||
|
||||
/* Increment our broadcast counter. */
|
||||
IncrementBroadcastCounterUnsafe(m_counter_low, m_counter_high);
|
||||
|
||||
/* Broadcast to any waiters. */
|
||||
m_cv.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::Clear() {
|
||||
/* Clear only if we need to. */
|
||||
if (m_signal_state.load(std::memory_order_acquire) == LightEventState_Signaled) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_cs);
|
||||
|
||||
/* Set our state to signaled. */
|
||||
m_signal_state = LightEventState_NotSignaled;
|
||||
}
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::WaitWithAutoClear() {
|
||||
/* Loop waiting. */
|
||||
while (true) {
|
||||
/* Get the current state. */
|
||||
const auto state = m_signal_state.load(std::memory_order_acquire);
|
||||
|
||||
/* If we're not signaled, stop looping so we can properly wait to be signaled. */
|
||||
if (state == LightEventState_NotSignaled) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we're signaled, try to reset. */
|
||||
if (state == LightEventState_Signaled && AtomicAutoClearLightEvent(m_signal_state)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait explicitly on the not-signaled event. */
|
||||
std::scoped_lock lk(m_cs);
|
||||
|
||||
while (m_signal_state == LightEventState_NotSignaled) {
|
||||
m_cv.Wait(std::addressof(m_cs));
|
||||
}
|
||||
|
||||
/* Set our state to not signaled. */
|
||||
m_signal_state = LightEventState_NotSignaled;
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::WaitWithManualClear() {
|
||||
/* Loop waiting. */
|
||||
while (true) {
|
||||
/* Get the current m_signal_state. */
|
||||
const auto state = m_signal_state.load(std::memory_order_acquire);
|
||||
|
||||
/* If we're not signaled, stop looping so we can properly wait to be signaled. */
|
||||
if (state == LightEventState_NotSignaled) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we're signaled, we're done. */
|
||||
if (state == LightEventState_Signaled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait explicitly on the not-signaled event. */
|
||||
std::scoped_lock lk(m_cs);
|
||||
|
||||
/* Get the broadcast counter. */
|
||||
const auto bc = GetBroadcastCounterUnsafe(m_counter_low, m_counter_high);
|
||||
|
||||
while (m_signal_state == LightEventState_NotSignaled) {
|
||||
/* Check if a broadcast has occurred. */
|
||||
if (bc != GetBroadcastCounterUnsafe(m_counter_low, m_counter_high)) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_cv.Wait(std::addressof(m_cs));
|
||||
}
|
||||
}
|
||||
|
||||
bool InternalLightEventImpl::TryWaitWithAutoClear() {
|
||||
return m_signal_state.load(std::memory_order_acquire) == LightEventState_Signaled && AtomicAutoClearLightEvent(m_signal_state);
|
||||
}
|
||||
|
||||
bool InternalLightEventImpl::TryWaitWithManualClear() {
|
||||
return m_signal_state.load(std::memory_order_acquire) == LightEventState_Signaled;
|
||||
}
|
||||
|
||||
bool InternalLightEventImpl::TimedWaitWithAutoClear(const TimeoutHelper &timeout_helper) {
|
||||
/* Loop waiting. */
|
||||
while (true) {
|
||||
/* Get the current state. */
|
||||
const auto state = m_signal_state.load(std::memory_order_acquire);
|
||||
|
||||
/* If we're not signaled, stop looping so we can properly wait to be signaled. */
|
||||
if (state == LightEventState_NotSignaled) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we're signaled, try to reset. */
|
||||
if (state == LightEventState_Signaled && AtomicAutoClearLightEvent(m_signal_state)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait explicitly on the not-signaled event. */
|
||||
std::scoped_lock lk(m_cs);
|
||||
|
||||
while (m_signal_state == LightEventState_NotSignaled) {
|
||||
if (m_cv.TimedWait(std::addressof(m_cs), timeout_helper) == ConditionVariableStatus::TimedOut) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set our state to not signaled. */
|
||||
m_signal_state = LightEventState_NotSignaled;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalLightEventImpl::TimedWaitWithManualClear(const TimeoutHelper &timeout_helper) {
|
||||
/* Loop waiting. */
|
||||
while (true) {
|
||||
/* Get the current m_signal_state. */
|
||||
const auto state = m_signal_state.load(std::memory_order_acquire);
|
||||
|
||||
/* If we're not signaled, stop looping so we can properly wait to be signaled. */
|
||||
if (state == LightEventState_NotSignaled) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we're signaled, we're done. */
|
||||
if (state == LightEventState_Signaled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait explicitly on the not-signaled event. */
|
||||
std::scoped_lock lk(m_cs);
|
||||
|
||||
/* Get the broadcast counter. */
|
||||
const auto bc = GetBroadcastCounterUnsafe(m_counter_low, m_counter_high);
|
||||
|
||||
while (m_signal_state == LightEventState_NotSignaled) {
|
||||
/* Check if a broadcast has occurred. */
|
||||
if (bc != GetBroadcastCounterUnsafe(m_counter_low, m_counter_high)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_cv.TimedWait(std::addressof(m_cs), timeout_helper) == ConditionVariableStatus::TimedOut) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline s32 LightEventState_NotSignaledAndNoWaiter = 0;
|
||||
constexpr inline s32 LightEventState_NotSignaledAndWaiter = 1;
|
||||
constexpr inline s32 LightEventState_Signaled = 2;
|
||||
|
||||
ALWAYS_INLINE uintptr_t GetAddressOfAtomicInteger(std::atomic<s32> *ptr) {
|
||||
static_assert(sizeof(*ptr) == sizeof(s32));
|
||||
static_assert(alignof(*ptr) == alignof(s32));
|
||||
static_assert(std::atomic<s32>::is_always_lock_free);
|
||||
|
||||
return reinterpret_cast<uintptr_t>(ptr);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool SvcSignalToAddressForAutoClear(std::atomic<s32> *state, s32 expected) {
|
||||
/* Signal. */
|
||||
R_TRY_CATCH(svc::SignalToAddress(GetAddressOfAtomicInteger(state), svc::SignalType_SignalAndModifyByWaitingCountIfEqual, expected, 1)) {
|
||||
R_CATCH(svc::ResultInvalidState) { return false; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool SvcSignalToAddressForManualClear(std::atomic<s32> *state, s32 expected) {
|
||||
/* Signal. */
|
||||
R_TRY_CATCH(svc::SignalToAddress(GetAddressOfAtomicInteger(state), svc::SignalType_SignalAndIncrementIfEqual, expected, -1)) {
|
||||
R_CATCH(svc::ResultInvalidState) { return false; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool SvcWaitForAddressIfEqual(std::atomic<s32> *state) {
|
||||
/* Wait. */
|
||||
R_TRY_CATCH(svc::WaitForAddress(GetAddressOfAtomicInteger(state), svc::ArbitrationType_WaitIfEqual, LightEventState_NotSignaledAndWaiter, -1ll)) {
|
||||
R_CATCH(svc::ResultInvalidState) { return false; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool SvcWaitForAddressIfEqual(bool *out_timed_out, std::atomic<s32> *state, s64 timeout) {
|
||||
/* Default to not timed out. */
|
||||
*out_timed_out = false;
|
||||
|
||||
/* Wait. */
|
||||
R_TRY_CATCH(svc::WaitForAddress(GetAddressOfAtomicInteger(state), svc::ArbitrationType_WaitIfEqual, LightEventState_NotSignaledAndWaiter, timeout)) {
|
||||
R_CATCH(svc::ResultInvalidState) { return false; }
|
||||
R_CATCH(svc::ResultTimedOut) { *out_timed_out = true; return false; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE s32 LoadAcquireExclusiveForLightEvent(s32 *p) {
|
||||
s32 v;
|
||||
__asm__ __volatile__("ldaxr %w[v], %[p]" : [v]"=&r"(v) : [p]"Q"(*p) : "memory");
|
||||
return v;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool StoreReleaseExclusiveForLightEvent(s32 *p, s32 v) {
|
||||
int result;
|
||||
__asm__ __volatile__("stlxr %w[result], %w[v], %[p]" : [result]"=&r"(result) : [v]"r"(v), [p]"Q"(*p) : "memory");
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void ClearExclusiveForLightEvent() {
|
||||
__asm__ __volatile__("clrex" ::: "memory");
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool CompareAndSwap(std::atomic<s32> *state, s32 expected, s32 desired) {
|
||||
/* NOTE: This works around gcc not emitting clrex on ldaxr fail. */
|
||||
s32 * const state_ptr = reinterpret_cast<s32 *>(GetAddressOfAtomicInteger(state));
|
||||
|
||||
while (true) {
|
||||
if (AMS_UNLIKELY(LoadAcquireExclusiveForLightEvent(state_ptr) != expected)) {
|
||||
ClearExclusiveForLightEvent();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AMS_LIKELY(StoreReleaseExclusiveForLightEvent(state_ptr, desired))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::Initialize(bool signaled) {
|
||||
/* Set initial state. */
|
||||
m_state.store(signaled ? LightEventState_Signaled : LightEventState_NotSignaledAndNoWaiter, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::Finalize() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::SignalWithAutoClear() {
|
||||
/* Loop until signaled */
|
||||
while (true) {
|
||||
/* Fence memory. */
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
/* Get the current state. */
|
||||
const auto state = m_state.load(std::memory_order_relaxed);
|
||||
switch (state) {
|
||||
case LightEventState_NotSignaledAndNoWaiter:
|
||||
/* Try to signal. */
|
||||
if (CompareAndSwap(std::addressof(m_state), state, LightEventState_Signaled)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case LightEventState_NotSignaledAndWaiter:
|
||||
/* Try to signal. */
|
||||
if (SvcSignalToAddressForAutoClear(std::addressof(m_state), state)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case LightEventState_Signaled:
|
||||
/* If we're already signaled, we're done. */
|
||||
return;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::SignalWithManualClear() {
|
||||
/* Loop until signaled */
|
||||
while (true) {
|
||||
/* Fence memory. */
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
/* Get the current state. */
|
||||
const auto state = m_state.load(std::memory_order_relaxed);
|
||||
switch (state) {
|
||||
case LightEventState_NotSignaledAndNoWaiter:
|
||||
/* Try to signal. */
|
||||
if (CompareAndSwap(std::addressof(m_state), state, LightEventState_Signaled)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case LightEventState_NotSignaledAndWaiter:
|
||||
/* Try to signal. */
|
||||
if (SvcSignalToAddressForManualClear(std::addressof(m_state), state)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case LightEventState_Signaled:
|
||||
/* If we're already signaled, we're done. */
|
||||
return;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::Clear() {
|
||||
/* Fence memory. */
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
ON_SCOPE_EXIT { std::atomic_thread_fence(std::memory_order_release); };
|
||||
|
||||
/* Change state from signaled to not-signaled. */
|
||||
CompareAndSwap(std::addressof(m_state), LightEventState_Signaled, LightEventState_NotSignaledAndNoWaiter);
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::WaitWithAutoClear() {
|
||||
/* When we're done, fence memory. */
|
||||
ON_SCOPE_EXIT { std::atomic_thread_fence(std::memory_order_seq_cst); };
|
||||
|
||||
/* Loop waiting. */
|
||||
while (true) {
|
||||
/* Get the current state. */
|
||||
const auto state = m_state.load(std::memory_order_acquire);
|
||||
switch (state) {
|
||||
case LightEventState_NotSignaledAndNoWaiter:
|
||||
/* Change state to have waiter. */
|
||||
if (!CompareAndSwap(std::addressof(m_state), state, LightEventState_NotSignaledAndWaiter)) {
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case LightEventState_NotSignaledAndWaiter:
|
||||
/* Wait for the address. */
|
||||
if (SvcWaitForAddressIfEqual(std::addressof(m_state))) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case LightEventState_Signaled:
|
||||
/* Change state to no-waiters. */
|
||||
if (CompareAndSwap(std::addressof(m_state), state, LightEventState_NotSignaledAndNoWaiter)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InternalLightEventImpl::WaitWithManualClear() {
|
||||
/* When we're done, fence memory. */
|
||||
ON_SCOPE_EXIT { std::atomic_thread_fence(std::memory_order_seq_cst); };
|
||||
|
||||
/* Loop waiting. */
|
||||
while (true) {
|
||||
/* Get the current state. */
|
||||
const auto state = m_state.load(std::memory_order_acquire);
|
||||
switch (state) {
|
||||
case LightEventState_NotSignaledAndNoWaiter:
|
||||
/* Change state to have waiter. */
|
||||
if (!CompareAndSwap(std::addressof(m_state), state, LightEventState_NotSignaledAndWaiter)) {
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case LightEventState_NotSignaledAndWaiter:
|
||||
/* Wait for the address. */
|
||||
SvcWaitForAddressIfEqual(std::addressof(m_state));
|
||||
return;
|
||||
case LightEventState_Signaled:
|
||||
/* We're signaled, so return. */
|
||||
return;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InternalLightEventImpl::TryWaitWithAutoClear() {
|
||||
/* When we're done, fence memory. */
|
||||
ON_SCOPE_EXIT { std::atomic_thread_fence(std::memory_order_seq_cst); };
|
||||
|
||||
return CompareAndSwap(std::addressof(m_state), LightEventState_Signaled, LightEventState_NotSignaledAndNoWaiter);
|
||||
}
|
||||
|
||||
bool InternalLightEventImpl::TryWaitWithManualClear() {
|
||||
/* When we're done, fence memory. */
|
||||
ON_SCOPE_EXIT { std::atomic_thread_fence(std::memory_order_seq_cst); };
|
||||
|
||||
return m_state.load(std::memory_order_acquire) == LightEventState_Signaled;
|
||||
}
|
||||
|
||||
bool InternalLightEventImpl::TimedWaitWithAutoClear(const TimeoutHelper &timeout_helper) {
|
||||
/* When we're done, fence memory. */
|
||||
ON_SCOPE_EXIT { std::atomic_thread_fence(std::memory_order_seq_cst); };
|
||||
|
||||
/* Loop waiting. */
|
||||
while (true) {
|
||||
/* Get the current state. */
|
||||
const auto state = m_state.load(std::memory_order_acquire);
|
||||
switch (state) {
|
||||
case LightEventState_NotSignaledAndNoWaiter:
|
||||
/* Change state to have waiter. */
|
||||
if (!CompareAndSwap(std::addressof(m_state), state, LightEventState_NotSignaledAndWaiter)) {
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case LightEventState_NotSignaledAndWaiter:
|
||||
/* Wait for the address, checking timeout. */
|
||||
{
|
||||
bool timed_out;
|
||||
if (SvcWaitForAddressIfEqual(std::addressof(timed_out), std::addressof(m_state), timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds())) {
|
||||
return true;
|
||||
}
|
||||
if (timed_out) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LightEventState_Signaled:
|
||||
/* Try to clear. */
|
||||
if (CompareAndSwap(std::addressof(m_state), LightEventState_Signaled, LightEventState_NotSignaledAndNoWaiter)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InternalLightEventImpl::TimedWaitWithManualClear(const TimeoutHelper &timeout_helper) {
|
||||
/* When we're done, fence memory. */
|
||||
ON_SCOPE_EXIT { std::atomic_thread_fence(std::memory_order_seq_cst); };
|
||||
|
||||
/* Loop waiting. */
|
||||
while (true) {
|
||||
/* Get the current state. */
|
||||
const auto state = m_state.load(std::memory_order_acquire);
|
||||
switch (state) {
|
||||
case LightEventState_NotSignaledAndNoWaiter:
|
||||
/* Change state to have waiter. */
|
||||
if (!CompareAndSwap(std::addressof(m_state), state, LightEventState_NotSignaledAndWaiter)) {
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case LightEventState_NotSignaledAndWaiter:
|
||||
/* Wait and check for timeout. */
|
||||
{
|
||||
bool timed_out;
|
||||
SvcWaitForAddressIfEqual(std::addressof(timed_out), std::addressof(m_state), timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds());
|
||||
return !timed_out;
|
||||
}
|
||||
break;
|
||||
case LightEventState_Signaled:
|
||||
/* We're signaled, so return. */
|
||||
return true;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_disable_counter.os.horizon.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE void PrefetchForBusyMutex(u32 *p) {
|
||||
/* Nintendo does PRFM pstl1keep. */
|
||||
__builtin_prefetch(p, 1);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void SendEventLocalForBusyMutex() {
|
||||
__asm__ __volatile__("sevl" ::: "memory");
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void WaitForEventsForBusyMutex() {
|
||||
__asm__ __volatile__("wfe" ::: "memory");
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 LoadAcquireExclusiveForBusyMutex(u32 *p) {
|
||||
u32 v;
|
||||
__asm__ __volatile__("ldaxr %w[v], %[p]" : [v]"=&r"(v) : [p]"Q"(*p) : "memory");
|
||||
return v;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 LoadExclusiveForBusyMutex(u32 *p) {
|
||||
u32 v;
|
||||
__asm__ __volatile__("ldxr %w[v], %[p]" : [v]"=&r"(v) : [p]"Q"(*p) : "memory");
|
||||
return v;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool StoreReleaseExclusiveForBusyMutex(u32 *p, u32 v) {
|
||||
int result;
|
||||
__asm__ __volatile__("stlxr %w[result], %w[v], %[p]" : [result]"=&r"(result) : [v]"r"(v), [p]"Q"(*p) : "memory");
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool StoreExclusiveForBusyMutex(u32 *p, u32 v) {
|
||||
int result;
|
||||
__asm__ __volatile__("stxr %w[result], %w[v], %[p]" : [result]"=&r"(result) : [v]"r"(v), [p]"Q"(*p) : "memory");
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void StoreReleaseWriteLockValueForBusyMutex(u32 *p) {
|
||||
u8 * const p8 = InternalReaderWriterBusyMutexValue::GetWriterCurrentPointer(p);
|
||||
|
||||
u8 v;
|
||||
__asm__ __volatile__("ldrb %w[v], %[p8]\n"
|
||||
"add %w[v], %w[v], #1\n"
|
||||
"stlrb %w[v], %[p8]\n"
|
||||
: [v]"=&r"(v)
|
||||
: [p8]"Q"(*p8)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InternalReaderWriterBusyMutexImpl::AcquireReadLock() {
|
||||
/* Get the thread local region. */
|
||||
auto * const tlr = svc::GetThreadLocalRegion();
|
||||
|
||||
/* Determine disable counters. */
|
||||
const auto cur_dc = tlr->disable_count;
|
||||
AMS_ABORT_UNLESS(cur_dc < std::numeric_limits<decltype(cur_dc)>::max());
|
||||
const auto next_dc = cur_dc + 1;
|
||||
|
||||
/* Check that we're allowed to use busy mutexes. */
|
||||
CallCheckBusyMutexPermission();
|
||||
|
||||
/* Get pointer to our value. */
|
||||
u32 * const p = std::addressof(m_value);
|
||||
|
||||
/* Pre-fetch the busy mutex. */
|
||||
PrefetchForBusyMutex(p);
|
||||
|
||||
/* Acquire the read-lock for the mutex. */
|
||||
while (true) {
|
||||
/* Set the updated disable counter. */
|
||||
tlr->disable_count = next_dc;
|
||||
|
||||
/* Try to acquire. */
|
||||
const u32 v = LoadAcquireExclusiveForBusyMutex(p);
|
||||
|
||||
/* We can only acquire read lock if not write-locked. */
|
||||
const bool write_locked = InternalReaderWriterBusyMutexValue::IsWriteLocked(v);
|
||||
if (AMS_LIKELY(!write_locked)) {
|
||||
/* Check that we don't overflow the reader count. */
|
||||
const u32 new_v = v + 1;
|
||||
AMS_ABORT_UNLESS(InternalReaderWriterBusyMutexValue::GetReaderCount(new_v) != 0);
|
||||
|
||||
/* Try to store our updated lock value. */
|
||||
if (AMS_LIKELY(StoreExclusiveForBusyMutex(p, new_v))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the disable counter, since we failed to acquire. */
|
||||
tlr->disable_count = cur_dc;
|
||||
|
||||
/* If we don't hold any other busy mutexes, acknowledge any interrupts that occurred while we tried to acquire the lock. */
|
||||
if (cur_dc == 0 && tlr->interrupt_flag) {
|
||||
svc::SynchronizePreemptionState();
|
||||
}
|
||||
|
||||
/* If the lock is held by another core, wait for it to be released. */
|
||||
if (write_locked) {
|
||||
WaitForEventsForBusyMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InternalReaderWriterBusyMutexImpl::ReleaseReadLock() {
|
||||
/* Release the read lock. */
|
||||
{
|
||||
/* Get pointer to our value. */
|
||||
u32 * const p = std::addressof(m_value);
|
||||
|
||||
u32 v;
|
||||
do {
|
||||
/* Get and validate the current value. */
|
||||
v = LoadExclusiveForBusyMutex(p);
|
||||
AMS_ABORT_UNLESS(InternalReaderWriterBusyMutexValue::GetReaderCount(v) != 0);
|
||||
} while (!StoreReleaseExclusiveForBusyMutex(p, v - 1));
|
||||
}
|
||||
|
||||
/* Get the thread local region. */
|
||||
auto * const tlr = svc::GetThreadLocalRegion();
|
||||
|
||||
/* Determine disable counters. */
|
||||
const auto cur_dc = tlr->disable_count;
|
||||
AMS_ASSERT(cur_dc != 0);
|
||||
const auto next_dc = cur_dc - 1;
|
||||
|
||||
/* Decrement disable count. */
|
||||
tlr->disable_count = next_dc;
|
||||
|
||||
/* If we don't hold any other busy mutexes, acknowledge any interrupts that occurred while we held the lock. */
|
||||
if (next_dc == 0 && tlr->interrupt_flag) {
|
||||
svc::SynchronizePreemptionState();
|
||||
}
|
||||
}
|
||||
|
||||
void InternalReaderWriterBusyMutexImpl::AcquireWriteLock() {
|
||||
/* Get the thread local region. */
|
||||
auto * const tlr = svc::GetThreadLocalRegion();
|
||||
|
||||
/* Determine disable counters. */
|
||||
const auto cur_dc = tlr->disable_count;
|
||||
AMS_ABORT_UNLESS(cur_dc < std::numeric_limits<decltype(cur_dc)>::max());
|
||||
const auto next_dc = cur_dc + 1;
|
||||
|
||||
/* Check that we're allowed to use busy mutexes. */
|
||||
CallCheckBusyMutexPermission();
|
||||
|
||||
/* Get pointer to our value. */
|
||||
u32 * const p = std::addressof(m_value);
|
||||
|
||||
/* Pre-fetch the busy mutex. */
|
||||
PrefetchForBusyMutex(p);
|
||||
|
||||
/* Acquire the read-lock for the mutex. */
|
||||
while (true) {
|
||||
/* Set the updated disable counter. */
|
||||
tlr->disable_count = next_dc;
|
||||
|
||||
/* Try to acquire. */
|
||||
const u32 v = LoadAcquireExclusiveForBusyMutex(p);
|
||||
|
||||
/* Check that we can write lock. */
|
||||
AMS_ABORT_UNLESS(static_cast<u8>(InternalReaderWriterBusyMutexValue::GetWriterNext(v) - InternalReaderWriterBusyMutexValue::GetWriterCurrent(v)) < InternalReaderWriterBusyMutexValue::WriterCountMax);
|
||||
|
||||
/* Determine our write-lock number. */
|
||||
const u32 new_v = InternalReaderWriterBusyMutexValue::IncrementWriterNext(v);
|
||||
|
||||
/* Try to store our updated lock value. */
|
||||
if (AMS_UNLIKELY(!StoreExclusiveForBusyMutex(p, new_v))) {
|
||||
/* Reset the disable counter, since we failed to acquire. */
|
||||
tlr->disable_count = cur_dc;
|
||||
|
||||
/* If we don't hold any other busy mutexes, acknowledge any interrupts that occurred while we tried to acquire the lock. */
|
||||
if (cur_dc == 0 && tlr->interrupt_flag) {
|
||||
svc::SynchronizePreemptionState();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Wait until the lock is truly acquired. */
|
||||
if (InternalReaderWriterBusyMutexValue::GetReaderCount(new_v) != 0 || InternalReaderWriterBusyMutexValue::GetWriterNext(v) != InternalReaderWriterBusyMutexValue::GetWriterCurrent(new_v)) {
|
||||
/* Send an event, so that we can immediately wait without fail. */
|
||||
SendEventLocalForBusyMutex();
|
||||
|
||||
while (true) {
|
||||
/* Wait for a lock update. */
|
||||
WaitForEventsForBusyMutex();
|
||||
|
||||
/* Get the updated value. */
|
||||
const u32 cur_v = LoadAcquireExclusiveForBusyMutex(p);
|
||||
if (InternalReaderWriterBusyMutexValue::GetReaderCount(cur_v) == 0 && InternalReaderWriterBusyMutexValue::GetWriterNext(v) == InternalReaderWriterBusyMutexValue::GetWriterCurrent(cur_v)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We've acquired the write lock. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InternalReaderWriterBusyMutexImpl::ReleaseWriteLock() {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ABORT_UNLESS(InternalReaderWriterBusyMutexValue::IsWriteLocked(m_value));
|
||||
|
||||
/* Get pointer to our value. */
|
||||
u32 * const p = std::addressof(m_value);
|
||||
|
||||
/* Release the write lock. */
|
||||
StoreReleaseWriteLockValueForBusyMutex(p);
|
||||
|
||||
/* Get the thread local region. */
|
||||
auto * const tlr = svc::GetThreadLocalRegion();
|
||||
|
||||
/* Determine disable counters. */
|
||||
const auto cur_dc = tlr->disable_count;
|
||||
AMS_ASSERT(cur_dc != 0);
|
||||
const auto next_dc = cur_dc - 1;
|
||||
|
||||
/* Decrement disable count. */
|
||||
tlr->disable_count = next_dc;
|
||||
|
||||
/* If we don't hold any other busy mutexes, acknowledge any interrupts that occurred while we held the lock. */
|
||||
if (next_dc == 0 && tlr->interrupt_flag) {
|
||||
svc::SynchronizePreemptionState();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
/* TODO */
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
/* TODO */
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
/* TODO */
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_interrupt_event_target_impl.os.horizon.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_WINDOWS)
|
||||
#include "os_interrupt_event_target_impl.os.windows.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_LINUX)
|
||||
#include "os_interrupt_event_target_impl.os.linux.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_MACOS)
|
||||
#include "os_interrupt_event_target_impl.os.macos.hpp"
|
||||
#else
|
||||
#error "Unknown OS for ams::os::InterruptEventImpl"
|
||||
#endif
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterruptEventImpl {
|
||||
private:
|
||||
InterruptEventTargetImpl m_impl;
|
||||
public:
|
||||
explicit InterruptEventImpl(InterruptName name, EventClearMode clear_mode) : m_impl(name, clear_mode) { /* ... */ }
|
||||
|
||||
void Clear() {
|
||||
return m_impl.Clear();
|
||||
}
|
||||
|
||||
void Wait() {
|
||||
return m_impl.Wait();
|
||||
}
|
||||
|
||||
bool TryWait() {
|
||||
return m_impl.TryWait();
|
||||
}
|
||||
|
||||
bool TimedWait(TimeSpan timeout) {
|
||||
return m_impl.TimedWait(timeout);
|
||||
}
|
||||
|
||||
TriBool IsSignaled() {
|
||||
return m_impl.IsSignaled();
|
||||
}
|
||||
|
||||
NativeHandle GetHandle() const {
|
||||
return m_impl.GetHandle();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_interrupt_event_target_impl.os.horizon.hpp"
|
||||
#include "os_timeout_helper.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
InterruptEventHorizonImpl::InterruptEventHorizonImpl(InterruptName name, EventClearMode clear_mode) {
|
||||
m_manual_clear = (clear_mode == EventClearMode_ManualClear);
|
||||
|
||||
auto interrupt_type = m_manual_clear ? svc::InterruptType_Level : svc::InterruptType_Edge;
|
||||
svc::Handle handle;
|
||||
R_ABORT_UNLESS(svc::CreateInterruptEvent(std::addressof(handle), static_cast<s32>(name), interrupt_type));
|
||||
|
||||
m_handle = handle;
|
||||
}
|
||||
|
||||
InterruptEventHorizonImpl::~InterruptEventHorizonImpl() {
|
||||
R_ABORT_UNLESS(svc::CloseHandle(m_handle));
|
||||
}
|
||||
|
||||
void InterruptEventHorizonImpl::Clear() {
|
||||
R_ABORT_UNLESS(svc::ClearEvent(m_handle));
|
||||
}
|
||||
|
||||
void InterruptEventHorizonImpl::Wait() {
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(m_handle), 1, svc::WaitInfinite);
|
||||
if (R_SUCCEEDED(res)) {
|
||||
/* Clear, if we must. */
|
||||
if (!m_manual_clear) {
|
||||
R_TRY_CATCH(svc::ResetSignal(m_handle)) {
|
||||
/* Some other thread might have caught this before we did. */
|
||||
R_CATCH(svc::ResultInvalidState) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
bool InterruptEventHorizonImpl::TryWait() {
|
||||
/* If we're auto clear, just try to reset. */
|
||||
if (!m_manual_clear) {
|
||||
return R_SUCCEEDED(svc::ResetSignal(m_handle));
|
||||
}
|
||||
|
||||
/* Not auto-clear. */
|
||||
while (true) {
|
||||
/* Continuously wait, until success or timeout. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(m_handle), 1, 0);
|
||||
|
||||
/* If we succeeded, we're signaled. */
|
||||
if (R_SUCCEEDED(res)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If we timed out, we're not signaled. */
|
||||
if (svc::ResultTimedOut::Includes(res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
bool InterruptEventHorizonImpl::TimedWait(TimeSpan timeout) {
|
||||
TimeoutHelper timeout_helper(timeout);
|
||||
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(m_handle), 1, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds());
|
||||
if (R_SUCCEEDED(res)) {
|
||||
/* Clear, if we must. */
|
||||
if (!m_manual_clear) {
|
||||
R_TRY_CATCH(svc::ResetSignal(m_handle)) {
|
||||
/* Some other thread might have caught this before we did. */
|
||||
R_CATCH(svc::ResultInvalidState) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (svc::ResultTimedOut::Includes(res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterruptEventHorizonImpl {
|
||||
private:
|
||||
svc::Handle m_handle;
|
||||
bool m_manual_clear;
|
||||
public:
|
||||
explicit InterruptEventHorizonImpl(InterruptName name, EventClearMode mode);
|
||||
~InterruptEventHorizonImpl();
|
||||
|
||||
void Clear();
|
||||
void Wait();
|
||||
bool TryWait();
|
||||
bool TimedWait(TimeSpan timeout);
|
||||
|
||||
TriBool IsSignaled() {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
NativeHandle GetHandle() const {
|
||||
return m_handle;
|
||||
}
|
||||
};
|
||||
|
||||
using InterruptEventTargetImpl = InterruptEventHorizonImpl;
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterruptEventLinuxImpl {
|
||||
private:
|
||||
NativeHandle m_handle;
|
||||
bool m_manual_clear;
|
||||
public:
|
||||
explicit InterruptEventLinuxImpl(InterruptName name, EventClearMode mode) {
|
||||
AMS_UNUSED(name, mode);
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
~InterruptEventLinuxImpl() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
void Clear() { AMS_ABORT("TODO"); }
|
||||
void Wait() { AMS_ABORT("TODO"); }
|
||||
bool TryWait() { AMS_ABORT("TODO"); }
|
||||
bool TimedWait(TimeSpan) { AMS_ABORT("TODO"); }
|
||||
|
||||
TriBool IsSignaled() {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
NativeHandle GetHandle() const {
|
||||
return m_handle;
|
||||
}
|
||||
};
|
||||
|
||||
using InterruptEventTargetImpl = InterruptEventLinuxImpl;
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterruptEventMacosImpl {
|
||||
private:
|
||||
NativeHandle m_handle;
|
||||
bool m_manual_clear;
|
||||
public:
|
||||
explicit InterruptEventMacosImpl(InterruptName name, EventClearMode mode) {
|
||||
AMS_UNUSED(name, mode);
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
~InterruptEventMacosImpl() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
void Clear() { AMS_ABORT("TODO"); }
|
||||
void Wait() { AMS_ABORT("TODO"); }
|
||||
bool TryWait() { AMS_ABORT("TODO"); }
|
||||
bool TimedWait(TimeSpan) { AMS_ABORT("TODO"); }
|
||||
|
||||
TriBool IsSignaled() {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
NativeHandle GetHandle() const {
|
||||
return m_handle;
|
||||
}
|
||||
};
|
||||
|
||||
using InterruptEventTargetImpl = InterruptEventMacosImpl;
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_interrupt_event_target_impl.os.windows.hpp"
|
||||
#include "os_timeout_helper.hpp"
|
||||
#include "os_giant_lock.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
InterruptEventWindowsImpl::InterruptEventWindowsImpl(InterruptName name, EventClearMode clear_mode) {
|
||||
const NativeHandle handle = ::OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, reinterpret_cast<LPCTSTR>(name));
|
||||
AMS_ASSERT(handle != InvalidNativeHandle);
|
||||
|
||||
m_handle = handle;
|
||||
m_manual_clear = (clear_mode == EventClearMode_ManualClear);
|
||||
}
|
||||
|
||||
InterruptEventWindowsImpl::~InterruptEventWindowsImpl() {
|
||||
const auto ret = ::CloseHandle(m_handle);
|
||||
AMS_ASSERT(ret != 0);
|
||||
AMS_UNUSED(ret);
|
||||
}
|
||||
|
||||
bool InterruptEventWindowsImpl::ResetEventSignal() {
|
||||
std::scoped_lock lk(GetGiantLock());
|
||||
|
||||
if (auto ret = ::WaitForSingleObject(m_handle, 0); ret == WAIT_OBJECT_0) {
|
||||
::ResetEvent(m_handle);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void InterruptEventWindowsImpl::Clear() {
|
||||
const auto ret = ::ResetEvent(m_handle);
|
||||
AMS_ASSERT(ret != 0);
|
||||
AMS_UNUSED(ret);
|
||||
}
|
||||
|
||||
void InterruptEventWindowsImpl::Wait() {
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
auto ret = ::WaitForSingleObject(m_handle, INFINITE);
|
||||
AMS_ASSERT(ret == WAIT_OBJECT_0);
|
||||
AMS_UNUSED(ret);
|
||||
|
||||
/* If we're not obligated to clear, we're done. */
|
||||
if (m_manual_clear) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to reset. */
|
||||
if (this->ResetEventSignal()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InterruptEventWindowsImpl::TryWait() {
|
||||
/* If we're auto clear, just try to reset. */
|
||||
if (!m_manual_clear) {
|
||||
return this->ResetEventSignal();
|
||||
}
|
||||
|
||||
const auto ret = ::WaitForSingleObject(m_handle, 0);
|
||||
AMS_ASSERT((ret == WAIT_OBJECT_0) || (ret == WAIT_TIMEOUT));
|
||||
|
||||
return ret == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
bool InterruptEventWindowsImpl::TimedWait(TimeSpan timeout) {
|
||||
TimeoutHelper timeout_helper(timeout);
|
||||
|
||||
do {
|
||||
if (const auto res = ::WaitForSingleObject(m_handle, timeout_helper.GetTimeLeftOnTarget()); res == WAIT_OBJECT_0) {
|
||||
/* If we're not obligated to clear, we're done. */
|
||||
if (m_manual_clear) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Try to reset. */
|
||||
if (this->ResetEventSignal()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while (!timeout_helper.TimedOut());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterruptEventWindowsImpl {
|
||||
private:
|
||||
NativeHandle m_handle;
|
||||
bool m_manual_clear;
|
||||
public:
|
||||
explicit InterruptEventWindowsImpl(InterruptName name, EventClearMode mode);
|
||||
~InterruptEventWindowsImpl();
|
||||
|
||||
void Clear();
|
||||
void Wait();
|
||||
bool TryWait();
|
||||
bool TimedWait(TimeSpan timeout);
|
||||
|
||||
TriBool IsSignaled() {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
NativeHandle GetHandle() const {
|
||||
return m_handle;
|
||||
}
|
||||
private:
|
||||
bool ResetEventSignal();
|
||||
};
|
||||
|
||||
using InterruptEventTargetImpl = InterruptEventWindowsImpl;
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class IoRegionImpl {
|
||||
public:
|
||||
static Result CreateIoRegion(NativeHandle *out, NativeHandle io_pool_handle, uintptr_t address, size_t size, MemoryMapping mapping, MemoryPermission permission);
|
||||
|
||||
static Result MapIoRegion(void **out, NativeHandle handle, size_t size, MemoryPermission perm);
|
||||
static void UnmapIoRegion(NativeHandle handle, void *address, size_t size);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_io_region_impl.hpp"
|
||||
#include "os_aslr_space_manager_types.hpp"
|
||||
#include "os_aslr_space_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr svc::MemoryPermission ConvertToSvcMemoryPermission(os::MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case os::MemoryPermission_None: return svc::MemoryPermission_None;
|
||||
case os::MemoryPermission_ReadOnly: return svc::MemoryPermission_Read;
|
||||
case os::MemoryPermission_WriteOnly: return svc::MemoryPermission_Write;
|
||||
case os::MemoryPermission_ReadWrite: return svc::MemoryPermission_ReadWrite;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr svc::MemoryMapping ConvertToSvcMemoryMapping(os::MemoryMapping mapping) {
|
||||
static_assert(std::same_as<svc::MemoryMapping, os::MemoryMapping>);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result IoRegionImpl::CreateIoRegion(NativeHandle *out, NativeHandle io_pool_handle, uintptr_t address, size_t size, MemoryMapping mapping, MemoryPermission permission) {
|
||||
/* Convert mapping/permission. */
|
||||
const auto svc_mapping = ConvertToSvcMemoryMapping(mapping);
|
||||
const auto svc_perm = ConvertToSvcMemoryPermission(permission);
|
||||
|
||||
/* Create the io region. */
|
||||
/* TODO: Result conversion/abort on unexpected result? */
|
||||
svc::Handle handle;
|
||||
R_TRY(svc::CreateIoRegion(std::addressof(handle), io_pool_handle, address, size, svc_mapping, svc_perm));
|
||||
|
||||
*out = handle;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IoRegionImpl::MapIoRegion(void **out, NativeHandle handle, size_t size, MemoryPermission perm) {
|
||||
/* Convert permission. */
|
||||
const auto svc_perm = ConvertToSvcMemoryPermission(perm);
|
||||
|
||||
/* Map at a random address. */
|
||||
uintptr_t mapped_address;
|
||||
R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address),
|
||||
[handle, svc_perm](uintptr_t map_address, size_t map_size) -> Result {
|
||||
R_TRY_CATCH(svc::MapIoRegion(handle, map_address, map_size, svc_perm)) {
|
||||
/* TODO: What's the correct result for these? */
|
||||
// R_CONVERT(svc::ResultInvalidHandle, os::ResultInvalidHandle())
|
||||
// R_CONVERT(svc::ResultInvalidSize, os::Result???())
|
||||
// R_CONVERT(svc::ResultInvalidState, os::Result???())
|
||||
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
R_SUCCEED();
|
||||
},
|
||||
[handle](uintptr_t map_address, size_t map_size) -> void {
|
||||
return IoRegionImpl::UnmapIoRegion(handle, reinterpret_cast<void *>(map_address), map_size);
|
||||
},
|
||||
size,
|
||||
0
|
||||
));
|
||||
|
||||
/* Return the address we mapped at. */
|
||||
*out = reinterpret_cast<void *>(mapped_address);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IoRegionImpl::UnmapIoRegion(NativeHandle handle, void *address, size_t size) {
|
||||
R_ABORT_UNLESS(svc::UnmapIoRegion(handle, reinterpret_cast<uintptr_t>(address), size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_io_region_impl.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
Result IoRegionImpl::CreateIoRegion(NativeHandle *out, NativeHandle io_pool_handle, uintptr_t address, size_t size, MemoryMapping mapping, MemoryPermission permission) {
|
||||
AMS_UNUSED(out, io_pool_handle, address, size, mapping, permission);
|
||||
R_THROW(os::ResultNotSupported());
|
||||
}
|
||||
|
||||
Result IoRegionImpl::MapIoRegion(void **out, NativeHandle handle, size_t size, MemoryPermission perm) {
|
||||
AMS_UNUSED(out, handle, size, perm);
|
||||
R_THROW(os::ResultNotSupported());
|
||||
}
|
||||
|
||||
void IoRegionImpl::UnmapIoRegion(NativeHandle handle, void *address, size_t size) {
|
||||
AMS_UNUSED(handle, address, size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr);
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
|
||||
/* Determine svc arguments. */
|
||||
u32 svc_mask = svc::MemoryAttribute_Uncached;
|
||||
u32 svc_attr = 0;
|
||||
|
||||
switch (attr) {
|
||||
case os::MemoryAttribute_Normal: svc_attr = 0; break;
|
||||
case os::MemoryAttribute_Uncached: svc_attr = svc::MemoryAttribute_Uncached; break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Loop, setting attribute. */
|
||||
auto cur_address = address;
|
||||
auto remaining = size;
|
||||
while (remaining > 0) {
|
||||
/* Query the memory. */
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_address));
|
||||
|
||||
/* Determine the current size. */
|
||||
const size_t cur_size = std::min<size_t>(mem_info.base_address + mem_info.size - cur_address, remaining);
|
||||
|
||||
/* Set the attribute, if necessary. */
|
||||
if (mem_info.attribute != svc_attr) {
|
||||
if (const auto res = svc::SetMemoryAttribute(address, size, svc_mask, svc_attr); R_FAILED(res)) {
|
||||
/* NOTE: Nintendo logs here. */
|
||||
R_ABORT_UNLESS(res);
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
cur_address += cur_size;
|
||||
remaining -= cur_size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
|
||||
/* TODO: Should this do anything? */
|
||||
AMS_UNUSED(address, size, attr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
|
||||
/* TODO: Should this do anything? */
|
||||
AMS_UNUSED(address, size, attr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
|
||||
/* TODO: Should this do anything? */
|
||||
AMS_UNUSED(address, size, attr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_memory_heap_manager.hpp"
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
Result MemoryHeapManager::SetHeapSize(size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(util::IsAligned(size, MemoryHeapUnitSize));
|
||||
|
||||
/* Acquire locks. */
|
||||
std::scoped_lock lk1(util::GetReference(::ams::os::impl::GetCurrentThread()->cs_thread));
|
||||
std::scoped_lock lk2(m_cs);
|
||||
|
||||
/* If we need to, expand the heap. */
|
||||
if (size > m_heap_size) {
|
||||
/* Set the new heap size. */
|
||||
uintptr_t address = 0;
|
||||
R_TRY(m_impl.SetHeapSize(std::addressof(address), size));
|
||||
R_UNLESS(address != 0, os::ResultOutOfMemory());
|
||||
|
||||
/* Check that the new heap address is consistent. */
|
||||
if (m_heap_size == 0) {
|
||||
AMS_ASSERT(util::IsAligned(address, MemoryHeapUnitSize));
|
||||
} else {
|
||||
AMS_ASSERT(address == m_heap_address);
|
||||
}
|
||||
|
||||
/* Set up the new heap address. */
|
||||
this->AddToFreeSpaceUnsafe(address + m_heap_size, size - m_heap_size);
|
||||
|
||||
/* Set our heap address. */
|
||||
m_heap_address = address;
|
||||
m_heap_size = size;
|
||||
} else if (size < m_heap_size) {
|
||||
/* We're shrinking the heap, so we need to remove memory blocks. */
|
||||
const uintptr_t end_address = m_heap_address + size;
|
||||
const size_t remove_size = m_heap_size - size;
|
||||
|
||||
/* Get the end of the heap. */
|
||||
auto it = m_free_memory_list.end();
|
||||
--it;
|
||||
R_UNLESS(it != m_free_memory_list.end(), os::ResultBusy());
|
||||
|
||||
/* Check that the block can be decommitted. */
|
||||
R_UNLESS(it->GetAddress() <= end_address, os::ResultBusy());
|
||||
R_UNLESS(it->GetSize() >= remove_size, os::ResultBusy());
|
||||
|
||||
/* Adjust the last node. */
|
||||
if (const size_t node_size = it->GetSize() - remove_size; node_size == 0) {
|
||||
m_free_memory_list.erase(it);
|
||||
it->Clean();
|
||||
} else {
|
||||
it->SetSize(node_size);
|
||||
}
|
||||
|
||||
/* Set the reduced heap size. */
|
||||
uintptr_t address = 0;
|
||||
R_ABORT_UNLESS(m_impl.SetHeapSize(std::addressof(address), size));
|
||||
|
||||
/* Set our heap address. */
|
||||
m_heap_size = size;
|
||||
if (size == 0) {
|
||||
m_heap_address = 0;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MemoryHeapManager::AllocateFromHeap(uintptr_t *out_address, size_t size) {
|
||||
/* Acquire locks. */
|
||||
std::scoped_lock lk1(util::GetReference(::ams::os::impl::GetCurrentThread()->cs_thread));
|
||||
std::scoped_lock lk2(m_cs);
|
||||
|
||||
/* Find free space. */
|
||||
auto it = this->FindFreeSpaceUnsafe(size);
|
||||
R_UNLESS(it != m_free_memory_list.end(), os::ResultOutOfMemory());
|
||||
|
||||
/* If necessary, split the memory block. */
|
||||
if (it->GetSize() > size) {
|
||||
this->SplitFreeMemoryNodeUnsafe(it, size);
|
||||
}
|
||||
|
||||
/* Remove the block. */
|
||||
m_free_memory_list.erase(it);
|
||||
it->Clean();
|
||||
|
||||
/* Increment the used heap size. */
|
||||
m_used_heap_size += size;
|
||||
|
||||
/* Set the output address. */
|
||||
*out_address = it->GetAddress();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void MemoryHeapManager::ReleaseToHeap(uintptr_t address, size_t size) {
|
||||
/* Acquire locks. */
|
||||
std::scoped_lock lk1(util::GetReference(::ams::os::impl::GetCurrentThread()->cs_thread));
|
||||
std::scoped_lock lk2(m_cs);
|
||||
|
||||
/* Check pre-condition. */
|
||||
AMS_ABORT_UNLESS(this->IsRegionAllocatedMemoryUnsafe(address, size));
|
||||
|
||||
/* Restore the permissions on the memory. */
|
||||
os::SetMemoryPermission(address, size, MemoryPermission_ReadWrite);
|
||||
os::SetMemoryAttribute(address, size, MemoryAttribute_Normal);
|
||||
|
||||
/* Add the memory back to our free list. */
|
||||
this->AddToFreeSpaceUnsafe(address, size);
|
||||
|
||||
/* Decrement the used heap size. */
|
||||
m_used_heap_size -= size;
|
||||
}
|
||||
|
||||
MemoryHeapManager::FreeMemoryList::iterator MemoryHeapManager::FindFreeSpaceUnsafe(size_t size) {
|
||||
/* Find the best fit candidate. */
|
||||
auto best = m_free_memory_list.end();
|
||||
|
||||
for (auto it = m_free_memory_list.begin(); it != m_free_memory_list.end(); ++it) {
|
||||
if (const size_t node_size = it->GetSize(); node_size >= size) {
|
||||
if (best == m_free_memory_list.end() || node_size < best->GetSize()) {
|
||||
best = it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
MemoryHeapManager::FreeMemoryList::iterator MemoryHeapManager::ConcatenatePreviousFreeMemoryNodeUnsafe(FreeMemoryList::iterator node) {
|
||||
/* Get the previous node. */
|
||||
auto prev = node;
|
||||
--prev;
|
||||
|
||||
/* If there's no previous, we're done. */
|
||||
if (prev == m_free_memory_list.end() || node == m_free_memory_list.end()) {
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Otherwise, if the previous isn't contiguous, we can't merge. */
|
||||
if (prev->GetAddress() + prev->GetSize() != node->GetAddress()) {
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Otherwise, increase the size of the previous node, and remove the current node. */
|
||||
prev->SetSize(prev->GetSize() + node->GetSize());
|
||||
m_free_memory_list.erase(node);
|
||||
node->Clean();
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
void MemoryHeapManager::SplitFreeMemoryNodeUnsafe(FreeMemoryList::iterator it, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(it->GetSize() > size);
|
||||
AMS_ASSERT(util::IsAligned(size, MemoryBlockUnitSize));
|
||||
|
||||
/* Create new node. */
|
||||
auto *new_node = std::construct_at(reinterpret_cast<FreeMemoryNode *>(it->GetAddress() + size));
|
||||
new_node->SetSize(it->GetSize() - size);
|
||||
|
||||
/* Set the old node's size. */
|
||||
it->SetSize(size);
|
||||
|
||||
/* Insert the new node. */
|
||||
m_free_memory_list.insert(++it, *new_node);
|
||||
}
|
||||
|
||||
void MemoryHeapManager::AddToFreeSpaceUnsafe(uintptr_t address, size_t size) {
|
||||
/* Create new node. */
|
||||
auto *new_node = std::construct_at(reinterpret_cast<FreeMemoryNode *>(address));
|
||||
new_node->SetSize(size);
|
||||
|
||||
/* Find the appropriate place to insert the node. */
|
||||
auto it = m_free_memory_list.begin();
|
||||
for (/* ... */; it != m_free_memory_list.end(); ++it) {
|
||||
if (address < it->GetAddress()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert the new node. */
|
||||
it = m_free_memory_list.insert(it, *new_node);
|
||||
|
||||
/* Perform coalescing as relevant. */
|
||||
it = this->ConcatenatePreviousFreeMemoryNodeUnsafe(it);
|
||||
this->ConcatenatePreviousFreeMemoryNodeUnsafe(++it);
|
||||
}
|
||||
|
||||
bool MemoryHeapManager::IsRegionAllocatedMemoryUnsafe(uintptr_t address, size_t size) {
|
||||
/* Look for a node containing the region. */
|
||||
for (auto it = m_free_memory_list.begin(); it != m_free_memory_list.end(); ++it) {
|
||||
const uintptr_t node_address = it->GetAddress();
|
||||
const size_t node_size = it->GetSize();
|
||||
|
||||
if (node_address < address + size && address < node_address + node_size) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_memory_heap_manager_types.hpp"
|
||||
#include "os_resource_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
ALWAYS_INLINE MemoryHeapManager &GetMemoryHeapManager() {
|
||||
return GetResourceManager().GetMemoryHeapManager();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MemoryHeapManagerHorizonImpl {
|
||||
public:
|
||||
Result SetHeapSize(uintptr_t *out, size_t size) {
|
||||
R_TRY_CATCH(svc::SetHeapSize(out, size)) {
|
||||
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfMemory())
|
||||
R_CONVERT(svc::ResultLimitReached, os::ResultOutOfMemory())
|
||||
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfMemory())
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
using MemoryHeapManagerImpl = MemoryHeapManagerHorizonImpl;
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MemoryHeapManagerLinuxImpl {
|
||||
NON_COPYABLE(MemoryHeapManagerLinuxImpl);
|
||||
NON_MOVEABLE(MemoryHeapManagerLinuxImpl);
|
||||
private:
|
||||
uintptr_t m_real_reserved_address;
|
||||
size_t m_real_reserved_size;
|
||||
uintptr_t m_aligned_reserved_heap_address;
|
||||
size_t m_aligned_reserved_heap_size;
|
||||
size_t m_committed_size;
|
||||
public:
|
||||
MemoryHeapManagerLinuxImpl() : m_real_reserved_address(0), m_real_reserved_size(0), m_aligned_reserved_heap_address(0), m_aligned_reserved_heap_size(0), m_committed_size(0) {
|
||||
/* Reserve a 32 GB region of virtual address space. */
|
||||
constexpr size_t TargetReservedSize = 32_GB;
|
||||
const auto reserved = ::mmap(nullptr, TargetReservedSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
|
||||
|
||||
m_real_reserved_address = reinterpret_cast<uintptr_t>(reserved);
|
||||
m_real_reserved_size = TargetReservedSize;
|
||||
|
||||
m_aligned_reserved_heap_address = util::AlignUp(m_real_reserved_address, MemoryHeapUnitSize);
|
||||
m_aligned_reserved_heap_size = m_real_reserved_size - MemoryHeapUnitSize;
|
||||
}
|
||||
|
||||
Result SetHeapSize(uintptr_t *out, size_t size) {
|
||||
/* Check that we have a reserved address. */
|
||||
R_UNLESS(m_real_reserved_address != 0, os::ResultOutOfMemory());
|
||||
|
||||
/* If necessary, commit the new memory. */
|
||||
if (size > m_committed_size) {
|
||||
R_UNLESS(this->CommitMemory(size), os::ResultOutOfMemory());
|
||||
} else if (size < m_committed_size) {
|
||||
/* Otherwise, decommit. */
|
||||
this->DecommitMemory(m_aligned_reserved_heap_address + size, m_committed_size - size);
|
||||
}
|
||||
|
||||
/* Set the committed size. */
|
||||
m_committed_size = size;
|
||||
|
||||
/* Set the out address. */
|
||||
*out = m_aligned_reserved_heap_address;
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
bool CommitMemory(size_t size) {
|
||||
const auto res = ::mprotect(reinterpret_cast<void *>(m_aligned_reserved_heap_address), size, PROT_READ | PROT_WRITE);
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
void DecommitMemory(uintptr_t address, size_t size) {
|
||||
const auto reserved = ::mmap(reinterpret_cast<void *>(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
|
||||
}
|
||||
};
|
||||
|
||||
using MemoryHeapManagerImpl = MemoryHeapManagerLinuxImpl;
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MemoryHeapManagerMacosImpl {
|
||||
NON_COPYABLE(MemoryHeapManagerMacosImpl);
|
||||
NON_MOVEABLE(MemoryHeapManagerMacosImpl);
|
||||
private:
|
||||
uintptr_t m_real_reserved_address;
|
||||
size_t m_real_reserved_size;
|
||||
uintptr_t m_aligned_reserved_heap_address;
|
||||
size_t m_aligned_reserved_heap_size;
|
||||
size_t m_committed_size;
|
||||
public:
|
||||
MemoryHeapManagerMacosImpl() : m_real_reserved_address(0), m_real_reserved_size(0), m_aligned_reserved_heap_address(0), m_aligned_reserved_heap_size(0), m_committed_size(0) {
|
||||
/* Reserve a 32 GB region of virtual address space. */
|
||||
constexpr size_t TargetReservedSize = 32_GB;
|
||||
const auto reserved = ::mmap(nullptr, TargetReservedSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
|
||||
|
||||
m_real_reserved_address = reinterpret_cast<uintptr_t>(reserved);
|
||||
m_real_reserved_size = TargetReservedSize;
|
||||
|
||||
m_aligned_reserved_heap_address = util::AlignUp(m_real_reserved_address, MemoryHeapUnitSize);
|
||||
m_aligned_reserved_heap_size = m_real_reserved_size - MemoryHeapUnitSize;
|
||||
}
|
||||
|
||||
Result SetHeapSize(uintptr_t *out, size_t size) {
|
||||
/* Check that we have a reserved address. */
|
||||
R_UNLESS(m_real_reserved_address != 0, os::ResultOutOfMemory());
|
||||
|
||||
/* If necessary, commit the new memory. */
|
||||
if (size > m_committed_size) {
|
||||
R_UNLESS(this->CommitMemory(size), os::ResultOutOfMemory());
|
||||
} else if (size < m_committed_size) {
|
||||
/* Otherwise, decommit. */
|
||||
this->DecommitMemory(m_aligned_reserved_heap_address + size, m_committed_size - size);
|
||||
}
|
||||
|
||||
/* Set the committed size. */
|
||||
m_committed_size = size;
|
||||
|
||||
/* Set the out address. */
|
||||
*out = m_aligned_reserved_heap_address;
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
bool CommitMemory(size_t size) {
|
||||
const auto res = ::mprotect(reinterpret_cast<void *>(m_aligned_reserved_heap_address), size, PROT_READ | PROT_WRITE);
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
void DecommitMemory(uintptr_t address, size_t size) {
|
||||
const auto reserved = ::mmap(reinterpret_cast<void *>(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
|
||||
}
|
||||
};
|
||||
|
||||
using MemoryHeapManagerImpl = MemoryHeapManagerMacosImpl;
|
||||
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/windows.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MemoryHeapManagerWindowsImpl {
|
||||
NON_COPYABLE(MemoryHeapManagerWindowsImpl);
|
||||
NON_MOVEABLE(MemoryHeapManagerWindowsImpl);
|
||||
private:
|
||||
LPVOID m_real_reserved_address;
|
||||
size_t m_real_reserved_size;
|
||||
LPVOID m_aligned_reserved_heap_address;
|
||||
size_t m_aligned_reserved_heap_size;
|
||||
size_t m_committed_size;
|
||||
public:
|
||||
MemoryHeapManagerWindowsImpl() : m_real_reserved_address(nullptr), m_real_reserved_size(0), m_aligned_reserved_heap_address(nullptr), m_aligned_reserved_heap_size(0), m_committed_size(0) {
|
||||
/* Define target size. */
|
||||
constexpr size_t TargetReservedSize = 32_GB;
|
||||
|
||||
/* Allocate appropriate amount of virtual space. */
|
||||
size_t reserved_size = 0;
|
||||
size_t reserved_addend = TargetReservedSize;
|
||||
while (reserved_addend >= MemoryHeapUnitSize) {
|
||||
if (this->ReserveVirtualSpace(0, reserved_size + reserved_addend)) {
|
||||
this->ReleaseVirtualSpace();
|
||||
|
||||
reserved_size += reserved_addend;
|
||||
if (reserved_size >= TargetReservedSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reserved_addend /= 2;
|
||||
}
|
||||
|
||||
/* Reserve virtual space. */
|
||||
AMS_ABORT_UNLESS(this->ReserveVirtualSpace(0, reserved_size));
|
||||
}
|
||||
|
||||
Result SetHeapSize(uintptr_t *out, size_t size) {
|
||||
/* Check that we have a reserved address. */
|
||||
R_UNLESS(m_real_reserved_address != nullptr, os::ResultOutOfMemory());
|
||||
|
||||
/* If necessary, commit the new memory. */
|
||||
if (size > m_committed_size) {
|
||||
R_UNLESS(this->CommitMemory(size), os::ResultOutOfMemory());
|
||||
} else if (size < m_committed_size) {
|
||||
/* Otherwise, decommit. */
|
||||
this->DecommitMemory(reinterpret_cast<uintptr_t>(m_aligned_reserved_heap_address) + size, m_committed_size - size);
|
||||
}
|
||||
|
||||
/* Set the committed size. */
|
||||
m_committed_size = size;
|
||||
|
||||
/* Set the out address. */
|
||||
*out = reinterpret_cast<uintptr_t>(m_aligned_reserved_heap_address);
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
bool ReserveVirtualSpace(uintptr_t address, size_t size) {
|
||||
AMS_ABORT_UNLESS(m_real_reserved_address == nullptr);
|
||||
AMS_ABORT_UNLESS(m_real_reserved_size == 0);
|
||||
|
||||
size_t reserve_size = util::AlignUp(size, MemoryHeapUnitSize);
|
||||
if constexpr (constexpr size_t VirtualAllocUnitSize = 64_KB; MemoryHeapUnitSize > VirtualAllocUnitSize) {
|
||||
reserve_size += MemoryHeapUnitSize - VirtualAllocUnitSize;
|
||||
}
|
||||
|
||||
LPVOID res = ::VirtualAlloc(reinterpret_cast<LPVOID>(address), reserve_size, MEM_RESERVE, PAGE_READWRITE);
|
||||
if (res == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_real_reserved_address = res;
|
||||
m_real_reserved_size = reserve_size;
|
||||
|
||||
m_aligned_reserved_heap_address = reinterpret_cast<LPVOID>(util::AlignUp(reinterpret_cast<uintptr_t>(m_real_reserved_address), MemoryHeapUnitSize));
|
||||
m_aligned_reserved_heap_size = size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReleaseVirtualSpace() {
|
||||
if (m_real_reserved_address != nullptr) {
|
||||
auto res = ::VirtualFree(m_real_reserved_address, 0, MEM_RELEASE);
|
||||
AMS_ASSERT(res);
|
||||
AMS_UNUSED(res);
|
||||
|
||||
m_real_reserved_address = nullptr;
|
||||
m_real_reserved_size = 0;
|
||||
|
||||
m_aligned_reserved_heap_address = nullptr;
|
||||
m_aligned_reserved_heap_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool CommitMemory(size_t size) {
|
||||
LPVOID address = ::VirtualAlloc(m_aligned_reserved_heap_address, static_cast<SIZE_T>(size), MEM_COMMIT, PAGE_READWRITE);
|
||||
if (address == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AMS_ABORT_UNLESS(address == m_aligned_reserved_heap_address);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DecommitMemory(uintptr_t address, size_t size) {
|
||||
auto res = ::VirtualFree(reinterpret_cast<LPVOID>(address), static_cast<SIZE_T>(size), MEM_DECOMMIT);
|
||||
AMS_ASSERT(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
};
|
||||
|
||||
using MemoryHeapManagerImpl = MemoryHeapManagerWindowsImpl;
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_memory_heap_manager_impl.os.horizon.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_WINDOWS)
|
||||
#include "os_memory_heap_manager_impl.os.windows.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_LINUX)
|
||||
#include "os_memory_heap_manager_impl.os.linux.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_MACOS)
|
||||
#include "os_memory_heap_manager_impl.os.macos.hpp"
|
||||
#else
|
||||
#error "Unknown OS for MemoryHeapManagerImpl"
|
||||
#endif
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MemoryHeapManager;
|
||||
|
||||
class FreeMemoryNode {
|
||||
private:
|
||||
friend class MemoryHeapManager;
|
||||
private:
|
||||
util::IntrusiveListNode m_node;
|
||||
size_t m_size;
|
||||
public:
|
||||
ALWAYS_INLINE uintptr_t GetAddress() const { return reinterpret_cast<uintptr_t>(this); }
|
||||
ALWAYS_INLINE size_t GetSize() const { return m_size; }
|
||||
ALWAYS_INLINE void SetSize(size_t size) { m_size = size; }
|
||||
ALWAYS_INLINE void Clean() { std::memset(reinterpret_cast<void *>(this), 0, sizeof(FreeMemoryNode)); }
|
||||
};
|
||||
static_assert(sizeof(FreeMemoryNode) == sizeof(util::IntrusiveListNode) + sizeof(size_t));
|
||||
|
||||
class MemoryHeapManager {
|
||||
NON_COPYABLE(MemoryHeapManager);
|
||||
NON_MOVEABLE(MemoryHeapManager);
|
||||
private:
|
||||
using FreeMemoryList = typename util::IntrusiveListMemberTraits<&FreeMemoryNode::m_node>::ListType;
|
||||
private:
|
||||
uintptr_t m_heap_address;
|
||||
size_t m_heap_size;
|
||||
size_t m_used_heap_size;
|
||||
FreeMemoryList m_free_memory_list;
|
||||
InternalCriticalSection m_cs;
|
||||
MemoryHeapManagerImpl m_impl;
|
||||
public:
|
||||
MemoryHeapManager() : m_heap_address(0), m_heap_size(0), m_used_heap_size(0) { /* ... */ }
|
||||
|
||||
Result SetHeapSize(size_t size);
|
||||
|
||||
Result AllocateFromHeap(uintptr_t *out_address, size_t size);
|
||||
void ReleaseToHeap(uintptr_t address, size_t size);
|
||||
|
||||
bool IsRegionInMemoryHeap(uintptr_t address, size_t size) const {
|
||||
return m_heap_address <= address && (address + size) <= (m_heap_address + m_heap_size);
|
||||
}
|
||||
|
||||
uintptr_t GetHeapAddress() const { return m_heap_address; }
|
||||
size_t GetHeapSize() const { return m_heap_size; }
|
||||
size_t GetUsedHeapSize() const { return m_used_heap_size; }
|
||||
private:
|
||||
FreeMemoryList::iterator FindFreeSpaceUnsafe(size_t size);
|
||||
FreeMemoryList::iterator ConcatenatePreviousFreeMemoryNodeUnsafe(FreeMemoryList::iterator node);
|
||||
void SplitFreeMemoryNodeUnsafe(FreeMemoryList::iterator it, size_t size);
|
||||
void AddToFreeSpaceUnsafe(uintptr_t address, size_t size);
|
||||
bool IsRegionAllocatedMemoryUnsafe(uintptr_t address, size_t size);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm);
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
void SetMemoryPermissionBySvc(uintptr_t start, size_t size, svc::MemoryPermission perm) {
|
||||
uintptr_t cur_address = start;
|
||||
size_t remaining_size = size;
|
||||
while (remaining_size > 0) {
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_address));
|
||||
|
||||
size_t cur_size = std::min(mem_info.base_address + mem_info.size - cur_address, remaining_size);
|
||||
|
||||
if (mem_info.permission != perm) {
|
||||
R_ABORT_UNLESS(svc::SetMemoryPermission(cur_address, cur_size, perm));
|
||||
}
|
||||
|
||||
cur_address += cur_size;
|
||||
remaining_size -= cur_size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case MemoryPermission_None:
|
||||
return SetMemoryPermissionBySvc(address, size, svc::MemoryPermission_None);
|
||||
case MemoryPermission_ReadOnly:
|
||||
return SetMemoryPermissionBySvc(address, size, svc::MemoryPermission_Read);
|
||||
case MemoryPermission_ReadWrite:
|
||||
return SetMemoryPermissionBySvc(address, size, svc::MemoryPermission_ReadWrite);
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case MemoryPermission_None:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_NONE);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadOnly:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadWrite:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ | PROT_WRITE);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case MemoryPermission_None:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_NONE);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadOnly:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadWrite:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ | PROT_WRITE);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/windows.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
|
||||
DWORD old;
|
||||
|
||||
uintptr_t cur_address = address;
|
||||
size_t remaining = size;
|
||||
while (remaining > 0) {
|
||||
const size_t cur_size = std::min<size_t>(remaining, 2_GB);
|
||||
switch (perm) {
|
||||
case MemoryPermission_None:
|
||||
{
|
||||
auto res = ::VirtualProtect(reinterpret_cast<LPVOID>(cur_address), static_cast<DWORD>(cur_size), PAGE_NOACCESS, std::addressof(old));
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadOnly:
|
||||
{
|
||||
auto res = ::VirtualProtect(reinterpret_cast<LPVOID>(cur_address), static_cast<DWORD>(cur_size), PAGE_READONLY, std::addressof(old));
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadWrite:
|
||||
{
|
||||
auto res = ::VirtualProtect(reinterpret_cast<LPVOID>(cur_address), static_cast<DWORD>(cur_size), PAGE_READWRITE, std::addressof(old));
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
cur_address += cur_size;
|
||||
remaining -= cur_size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
template<typename T>
|
||||
concept IsMessageQueueType = requires(T &t) {
|
||||
{ t.buffer } -> std::convertible_to<uintptr_t *>;
|
||||
{ t.offset } -> std::convertible_to<s32>;
|
||||
{ t.count } -> std::same_as<decltype(t.offset) &>;
|
||||
{ t.capacity } -> std::same_as<decltype(t.count) &>;
|
||||
};
|
||||
|
||||
template<typename T> requires IsMessageQueueType<T>
|
||||
class MessageQueueHelper {
|
||||
public:
|
||||
static ALWAYS_INLINE bool IsMessageQueueFull(const T *mq) {
|
||||
return mq->count >= mq->capacity;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE bool IsMessageQueueEmpty(const T *mq) {
|
||||
return mq->count == 0;
|
||||
}
|
||||
|
||||
static void EnqueueUnsafe(T *mq, uintptr_t data) {
|
||||
/* Ensure our limits are correct. */
|
||||
auto count = mq->count;
|
||||
const auto capacity = mq->capacity;
|
||||
AMS_ASSERT(count < capacity);
|
||||
|
||||
/* Determine where we're writing. */
|
||||
auto ind = mq->offset + count;
|
||||
if (ind >= capacity) {
|
||||
ind -= capacity;
|
||||
}
|
||||
AMS_ASSERT(0 <= ind && ind < capacity);
|
||||
|
||||
/* Write the data. */
|
||||
mq->buffer[ind] = data;
|
||||
++count;
|
||||
|
||||
/* Update tracking. */
|
||||
mq->count = count;
|
||||
}
|
||||
|
||||
static uintptr_t DequeueUnsafe(T *mq) {
|
||||
/* Ensure our limits are correct. */
|
||||
auto count = mq->count;
|
||||
auto offset = mq->offset;
|
||||
const auto capacity = mq->capacity;
|
||||
AMS_ASSERT(count > 0);
|
||||
AMS_ASSERT(offset >= 0 && offset < capacity);
|
||||
|
||||
/* Get the data. */
|
||||
auto data = mq->buffer[offset++];
|
||||
|
||||
/* Calculate new tracking variables. */
|
||||
if (offset >= capacity) {
|
||||
offset -= capacity;
|
||||
}
|
||||
--count;
|
||||
|
||||
/* Update tracking. */
|
||||
mq->offset = offset;
|
||||
mq->count = count;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void JamUnsafe(T *mq, uintptr_t data) {
|
||||
/* Ensure our limits are correct. */
|
||||
auto count = mq->count;
|
||||
const auto capacity = mq->capacity;
|
||||
AMS_ASSERT(count < capacity);
|
||||
|
||||
/* Determine where we're writing. */
|
||||
auto offset = mq->offset - 1;
|
||||
if (offset < 0) {
|
||||
offset += capacity;
|
||||
}
|
||||
AMS_ASSERT(0 <= offset && offset < capacity);
|
||||
|
||||
/* Write the data. */
|
||||
mq->buffer[offset] = data;
|
||||
++count;
|
||||
|
||||
/* Update tracking. */
|
||||
mq->offset = offset;
|
||||
mq->count = count;
|
||||
}
|
||||
|
||||
static uintptr_t PeekUnsafe(const T *mq) {
|
||||
/* Ensure our limits are correct. */
|
||||
const auto count = mq->count;
|
||||
const auto offset = mq->offset;
|
||||
AMS_ASSERT(count > 0);
|
||||
AMS_UNUSED(count);
|
||||
|
||||
return mq->buffer[offset];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitObjectList;
|
||||
class MultiWaitImpl;
|
||||
|
||||
class MultiWaitHolderBase {
|
||||
private:
|
||||
MultiWaitImpl *m_multi_wait = nullptr;
|
||||
public:
|
||||
util::IntrusiveListNode m_multi_wait_node;
|
||||
util::IntrusiveListNode m_object_list_node;
|
||||
public:
|
||||
/* Gets whether the held object is currently signaled. */
|
||||
virtual TriBool IsSignaled() const = 0;
|
||||
|
||||
/* Adds to multi wait's object list, returns is signaled. */
|
||||
virtual TriBool AddToObjectList() = 0;
|
||||
|
||||
/* Removes from the multi wait's object list. */
|
||||
virtual void RemoveFromObjectList() = 0;
|
||||
|
||||
/* Gets whether waitable has a native handle, writes to output if it does. */
|
||||
virtual bool GetNativeHandle(os::NativeHandle *) const = 0;
|
||||
|
||||
/* Gets the amount of time remaining until this wakes up. */
|
||||
virtual TimeSpan GetAbsoluteTimeToWakeup() const {
|
||||
return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max());
|
||||
}
|
||||
|
||||
/* Interface with multi wait. */
|
||||
ALWAYS_INLINE void SetMultiWait(MultiWaitImpl *m) {
|
||||
m_multi_wait = m;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE MultiWaitImpl *GetMultiWait() const {
|
||||
return m_multi_wait;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool IsLinked() const { return m_multi_wait != nullptr; }
|
||||
ALWAYS_INLINE bool IsNotLinked() const { return m_multi_wait == nullptr; }
|
||||
};
|
||||
|
||||
class MultiWaitHolderOfUserWaitObject : public MultiWaitHolderBase {
|
||||
public:
|
||||
/* All user objects have no handle to wait on. */
|
||||
virtual bool GetNativeHandle(os::NativeHandle *) const override final {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class MultiWaitHolderOfNativeWaitObject : public MultiWaitHolderBase {
|
||||
public:
|
||||
/* All native objects have native handles, and thus don't have object list semantics. */
|
||||
virtual TriBool AddToObjectList() override final {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
virtual void RemoveFromObjectList() override final {
|
||||
/* ... */
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "os_multiple_wait_holder_of_native_handle.hpp"
|
||||
#include "os_multiple_wait_holder_of_event.hpp"
|
||||
#include "os_multiple_wait_holder_of_inter_process_event.hpp"
|
||||
#include "os_multiple_wait_holder_of_interrupt_event.hpp"
|
||||
#include "os_multiple_wait_holder_of_timer_event.hpp"
|
||||
#include "os_multiple_wait_holder_of_thread.hpp"
|
||||
#include "os_multiple_wait_holder_of_semaphore.hpp"
|
||||
#include "os_multiple_wait_holder_of_message_queue.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
struct MultiWaitHolderImpl {
|
||||
union {
|
||||
util::TypedStorage<MultiWaitHolderOfNativeHandle> holder_of_native_handle_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfEvent> holder_of_event_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfInterProcessEvent> holder_of_inter_process_event_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfInterruptEvent> holder_of_interrupt_event_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfTimerEvent> holder_of_timer_event_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfThread> holder_of_thread_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfSemaphore> holder_of_semaphore_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfMessageQueueNotFull> holder_of_mq_not_full_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfMessageQueueNotEmpty> holder_of_mq_not_empty_storage;
|
||||
};
|
||||
};
|
||||
|
||||
#define CHECK_HOLDER(T) \
|
||||
static_assert(std::is_base_of<::ams::os::impl::MultiWaitHolderBase, T>::value && std::is_trivially_destructible<T>::value, #T)
|
||||
|
||||
CHECK_HOLDER(MultiWaitHolderOfNativeHandle);
|
||||
CHECK_HOLDER(MultiWaitHolderOfEvent);
|
||||
CHECK_HOLDER(MultiWaitHolderOfInterProcessEvent);
|
||||
CHECK_HOLDER(MultiWaitHolderOfInterruptEvent);
|
||||
CHECK_HOLDER(MultiWaitHolderOfTimerEvent);
|
||||
CHECK_HOLDER(MultiWaitHolderOfThread);
|
||||
CHECK_HOLDER(MultiWaitHolderOfSemaphore);
|
||||
CHECK_HOLDER(MultiWaitHolderOfMessageQueueNotFull);
|
||||
CHECK_HOLDER(MultiWaitHolderOfMessageQueueNotEmpty);
|
||||
|
||||
#undef CHECK_HOLDER
|
||||
|
||||
static_assert(std::is_trivial<MultiWaitHolderImpl>::value && std::is_trivially_destructible<MultiWaitHolderImpl>::value);
|
||||
static_assert(sizeof(MultiWaitHolderImpl) == sizeof(os::MultiWaitHolderType::impl_storage));
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "os_multiple_wait_holder_base.hpp"
|
||||
#include "os_multiple_wait_object_list.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfEvent : public MultiWaitHolderOfUserWaitObject {
|
||||
private:
|
||||
EventType *m_event;
|
||||
private:
|
||||
ALWAYS_INLINE TriBool IsSignaledUnsafe() const {
|
||||
return m_event->signaled ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
explicit MultiWaitHolderOfEvent(EventType *e) : m_event(e) { /* ... */ }
|
||||
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(GetReference(m_event->cs_event));
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual TriBool AddToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_event->cs_event));
|
||||
|
||||
GetReference(m_event->multi_wait_object_list_storage).PushBackToList(*this);
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual void RemoveFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_event->cs_event));
|
||||
|
||||
GetReference(m_event->multi_wait_object_list_storage).EraseFromList(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "os_multiple_wait_holder_base.hpp"
|
||||
#include "os_inter_process_event.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfInterProcessEvent : public MultiWaitHolderOfNativeWaitObject {
|
||||
private:
|
||||
InterProcessEventType *m_event;
|
||||
public:
|
||||
explicit MultiWaitHolderOfInterProcessEvent(InterProcessEventType *e) : m_event(e) { /* ... */ }
|
||||
|
||||
/* IsSignaled, GetHandle both implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
virtual bool GetNativeHandle(os::NativeHandle *out) const override {
|
||||
*out = m_event->readable_handle;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_multiple_wait_holder_of_interrupt_event.hpp"
|
||||
#include "os_interrupt_event_impl.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
bool MultiWaitHolderOfInterruptEvent::GetNativeHandle(os::NativeHandle *out) const {
|
||||
*out = GetReference(m_event->impl).GetHandle();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "os_multiple_wait_holder_base.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfInterruptEvent : public MultiWaitHolderOfNativeWaitObject {
|
||||
private:
|
||||
InterruptEventType *m_event;
|
||||
public:
|
||||
explicit MultiWaitHolderOfInterruptEvent(InterruptEventType *e) : m_event(e) { /* ... */ }
|
||||
|
||||
/* IsSignaled, GetHandle both implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
virtual bool GetNativeHandle(os::NativeHandle *out) const override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "os_multiple_wait_holder_base.hpp"
|
||||
#include "os_multiple_wait_object_list.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfMessageQueueNotEmpty : public MultiWaitHolderOfUserWaitObject {
|
||||
private:
|
||||
MessageQueueType *m_mq;
|
||||
private:
|
||||
ALWAYS_INLINE TriBool IsSignaledUnsafe() const {
|
||||
return m_mq->count > 0 ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
explicit MultiWaitHolderOfMessageQueueNotEmpty(MessageQueueType *mq) : m_mq(mq) { /* ... */ }
|
||||
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual TriBool AddToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
|
||||
GetReference(m_mq->waitlist_not_empty).PushBackToList(*this);
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual void RemoveFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
|
||||
GetReference(m_mq->waitlist_not_empty).EraseFromList(*this);
|
||||
}
|
||||
};
|
||||
|
||||
class MultiWaitHolderOfMessageQueueNotFull : public MultiWaitHolderOfUserWaitObject {
|
||||
private:
|
||||
MessageQueueType *m_mq;
|
||||
private:
|
||||
ALWAYS_INLINE TriBool IsSignaledUnsafe() const {
|
||||
return m_mq->count < m_mq->capacity ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
explicit MultiWaitHolderOfMessageQueueNotFull(MessageQueueType *mq) : m_mq(mq) { /* ... */ }
|
||||
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual TriBool AddToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
|
||||
GetReference(m_mq->waitlist_not_full).PushBackToList(*this);
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual void RemoveFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
|
||||
GetReference(m_mq->waitlist_not_full).EraseFromList(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "os_multiple_wait_holder_base.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfNativeHandle : public MultiWaitHolderOfNativeWaitObject {
|
||||
private:
|
||||
NativeHandle m_handle;
|
||||
public:
|
||||
explicit MultiWaitHolderOfNativeHandle(NativeHandle h) : m_handle(h) { /* ... */ }
|
||||
|
||||
/* IsSignaled, GetHandle both implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
virtual bool GetNativeHandle(os::NativeHandle *out) const override {
|
||||
*out = m_handle;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "os_multiple_wait_holder_base.hpp"
|
||||
#include "os_multiple_wait_object_list.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfSemaphore : public MultiWaitHolderOfUserWaitObject {
|
||||
private:
|
||||
SemaphoreType *m_semaphore;
|
||||
private:
|
||||
TriBool IsSignaledImpl() const {
|
||||
return m_semaphore->count > 0 ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
explicit MultiWaitHolderOfSemaphore(SemaphoreType *s) : m_semaphore(s) { /* ... */ }
|
||||
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(GetReference(m_semaphore->cs_sema));
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual TriBool AddToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_semaphore->cs_sema));
|
||||
|
||||
GetReference(m_semaphore->waitlist).PushBackToList(*this);
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual void RemoveFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_semaphore->cs_sema));
|
||||
|
||||
GetReference(m_semaphore->waitlist).EraseFromList(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "os_multiple_wait_holder_base.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfThread : public MultiWaitHolderOfUserWaitObject {
|
||||
private:
|
||||
ThreadType *m_thread;
|
||||
private:
|
||||
TriBool IsSignaledImpl() const {
|
||||
return m_thread->state == ThreadType::State_Terminated ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
explicit MultiWaitHolderOfThread(ThreadType *t) : m_thread(t) { /* ... */ }
|
||||
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(GetReference(m_thread->cs_thread));
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual TriBool AddToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_thread->cs_thread));
|
||||
|
||||
GetReference(m_thread->waitlist).PushBackToList(*this);
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual void RemoveFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_thread->cs_thread));
|
||||
|
||||
GetReference(m_thread->waitlist).EraseFromList(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user