Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"

This reverts commit 15b7df8ef1.
This commit is contained in:
souldbminersmwc
2025-11-09 16:14:52 -05:00
parent 22ec140738
commit 21a3f953d7
3804 changed files with 435 additions and 570162 deletions

View File

@@ -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 &region = 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>;
}

View File

@@ -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

View File

@@ -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;
};
}

View File

@@ -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;
}
};
}

View File

@@ -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;
}
};
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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>;
}

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
};
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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));
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -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;
};
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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 */
}

View File

@@ -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 */
}

View File

@@ -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 */
}

View File

@@ -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();
}
};
}

View File

@@ -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));
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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);
};
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
};
}

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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];
}
};
}

View File

@@ -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 {
/* ... */
}
};
}

View File

@@ -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));
}

View File

@@ -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);
}
};
}

View File

@@ -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;
}
};
}

View File

@@ -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;
}
}

View File

@@ -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;
};
}

View File

@@ -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);
}
};
}

View File

@@ -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;
}
};
}

View File

@@ -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);
}
};
}

View File

@@ -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