mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
kern: implement KThreadLocalPage
This commit is contained in:
parent
484f132651
commit
059c706f19
15 changed files with 295 additions and 30 deletions
|
@ -52,6 +52,7 @@
|
|||
/* Auto Objects. */
|
||||
#include <mesosphere/kern_k_auto_object.hpp>
|
||||
#include <mesosphere/kern_k_handle_table.hpp>
|
||||
#include <mesosphere/kern_k_process.hpp>
|
||||
|
||||
/* Supervisor Calls. */
|
||||
#include <mesosphere/kern_svc.hpp>
|
||||
|
|
|
@ -21,11 +21,10 @@
|
|||
|
||||
namespace ams::kern::init {
|
||||
|
||||
constexpr size_t PageSize = 0x1000;
|
||||
constexpr size_t L1BlockSize = 0x40000000;
|
||||
constexpr size_t L2BlockSize = 0x200000;
|
||||
constexpr size_t L1BlockSize = 1_GB;
|
||||
constexpr size_t L2BlockSize = 2_MB;
|
||||
constexpr size_t L2ContiguousBlockSize = 0x10 * L2BlockSize;
|
||||
constexpr size_t L3BlockSize = 0x1000;
|
||||
constexpr size_t L3BlockSize = PageSize;
|
||||
constexpr size_t L3ContiguousBlockSize = 0x10 * L3BlockSize;
|
||||
|
||||
class PageTableEntry {
|
||||
|
|
|
@ -17,3 +17,9 @@
|
|||
#include <vapours.hpp>
|
||||
#include <mesosphere/kern_panic.hpp>
|
||||
#include <mesosphere/svc/kern_svc_results.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
constexpr size_t PageSize = 4_KB;
|
||||
|
||||
}
|
||||
|
|
|
@ -44,18 +44,18 @@ namespace ams::kern {
|
|||
std::atomic<u64> num_specific_svc[0x80];
|
||||
u32 perf_counters[6];
|
||||
};
|
||||
static_assert(sizeof(KCoreLocalContext) < KMemoryManager::PageSize);
|
||||
static_assert(sizeof(KCoreLocalContext) < PageSize);
|
||||
|
||||
struct KCoreLocalPage {
|
||||
KCoreLocalContext context;
|
||||
u8 padding[KMemoryManager::PageSize - sizeof(KCoreLocalContext)];
|
||||
u8 padding[PageSize - sizeof(KCoreLocalContext)];
|
||||
};
|
||||
static_assert(sizeof(KCoreLocalPage) == KMemoryManager::PageSize);
|
||||
static_assert(sizeof(KCoreLocalPage) == PageSize);
|
||||
|
||||
struct KCoreLocalRegion {
|
||||
KCoreLocalPage current;
|
||||
KCoreLocalPage absolute[cpu::NumCores];
|
||||
};
|
||||
static_assert(sizeof(KCoreLocalRegion) == KMemoryManager::PageSize * (1 + cpu::NumCores));
|
||||
static_assert(sizeof(KCoreLocalRegion) == PageSize * (1 + cpu::NumCores));
|
||||
|
||||
}
|
||||
|
|
|
@ -397,6 +397,14 @@ namespace ams::kern {
|
|||
static ALWAYS_INLINE KMemoryBlockTree &GetVirtualLinearMemoryBlockTree() { return s_virtual_linear_tree; }
|
||||
static ALWAYS_INLINE KMemoryBlockTree &GetPhysicalLinearMemoryBlockTree() { return s_physical_linear_tree; }
|
||||
|
||||
static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress address) {
|
||||
return GetInteger(address) + s_linear_phys_to_virt_diff;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KPhysicalAddress GetLinearPhysicalAddress(KVirtualAddress address) {
|
||||
return GetInteger(address) + s_linear_virt_to_phys_diff;
|
||||
}
|
||||
|
||||
static NOINLINE KVirtualAddress GetMainStackTopAddress(s32 core_id) {
|
||||
return GetVirtualMemoryBlockTree().FindFirstBlockByTypeAttr(KMemoryRegionType_KernelMiscMainStack, static_cast<u32>(core_id))->GetEndAddress();
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
namespace ams::kern {
|
||||
|
||||
class KMemoryManager {
|
||||
public:
|
||||
static constexpr size_t PageSize = 0x1000; /* TODO: Elsewhere? */
|
||||
private:
|
||||
class Impl {
|
||||
public:
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <mesosphere/kern_slab_helpers.hpp>
|
||||
#include <mesosphere/kern_k_memory_layout.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KPageBuffer : public KSlabAllocated<KPageBuffer> {
|
||||
private:
|
||||
alignas(PageSize) u8 buffer[PageSize];
|
||||
public:
|
||||
KPageBuffer() {
|
||||
std::memset(buffer, 0, sizeof(buffer));
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KPageBuffer *FromPhysicalAddress(KPhysicalAddress phys_addr) {
|
||||
const KVirtualAddress virt_addr = KMemoryLayout::GetLinearVirtualAddress(phys_addr);
|
||||
|
||||
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(phys_addr), PageSize));
|
||||
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(virt_addr), PageSize));
|
||||
|
||||
return GetPointer<KPageBuffer>(virt_addr);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(KPageBuffer) == PageSize);
|
||||
static_assert(alignof(KPageBuffer) == PageSize);
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <mesosphere/kern_select_cpu.hpp>
|
||||
#include <mesosphere/kern_slab_helpers.hpp>
|
||||
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||
#include <mesosphere/kern_k_handle_table.hpp>
|
||||
#include <mesosphere/kern_k_thread.hpp>
|
||||
#include <mesosphere/kern_k_thread_local_page.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
|
||||
/* TODO: This is a placeholder definition. */
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/kern_select_cpu.hpp>
|
||||
#include <mesosphere/kern_slab_helpers.hpp>
|
||||
#include <mesosphere/kern_k_page_buffer.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KThread;
|
||||
class KProcess;
|
||||
|
||||
class KThreadLocalPage : public util::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>, public KSlabAllocated<KThreadLocalPage> {
|
||||
public:
|
||||
static constexpr size_t RegionsPerPage = PageSize / ams::svc::ThreadLocalRegionSize;
|
||||
static_assert(RegionsPerPage > 0);
|
||||
private:
|
||||
KProcessAddress virt_addr;
|
||||
KProcess *owner;
|
||||
bool is_region_free[RegionsPerPage];
|
||||
public:
|
||||
constexpr explicit KThreadLocalPage(KProcessAddress addr) : virt_addr(addr), owner(nullptr), is_region_free() {
|
||||
for (size_t i = 0; i < RegionsPerPage; i++) {
|
||||
this->is_region_free[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr explicit KThreadLocalPage() : KThreadLocalPage(Null<KProcessAddress>) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE KProcessAddress GetAddress() const { return this->virt_addr; }
|
||||
private:
|
||||
constexpr ALWAYS_INLINE KProcessAddress GetRegionAddress(size_t i) {
|
||||
return this->GetAddress() + i * ams::svc::ThreadLocalRegionSize;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool Contains(KProcessAddress addr) {
|
||||
return this->GetAddress() <= addr && addr < this->GetAddress() + PageSize;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_t GetRegionIndex(KProcessAddress addr) {
|
||||
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(addr), ams::svc::ThreadLocalRegionSize));
|
||||
MESOSPHERE_ASSERT(this->Contains(addr));
|
||||
return (addr - this->GetAddress()) / ams::svc::ThreadLocalRegionSize;
|
||||
}
|
||||
public:
|
||||
Result Initialize(KProcess *process);
|
||||
Result Finalize();
|
||||
|
||||
KProcessAddress Reserve();
|
||||
void Release(KProcessAddress addr);
|
||||
|
||||
bool IsAllUsed() const {
|
||||
for (size_t i = 0; i < RegionsPerPage; i++) {
|
||||
if (this->is_region_free[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsAllFree() const {
|
||||
for (size_t i = 0; i < RegionsPerPage; i++) {
|
||||
if (!this->is_region_free[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsAnyUsed() const {
|
||||
return !this->IsAllFree();
|
||||
}
|
||||
|
||||
bool IsAnyFree() const {
|
||||
return !this->IsAllUsed();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -50,6 +50,10 @@ namespace ams::kern {
|
|||
return this->address - rhs;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE ptrdiff_t operator-(KTypedAddress rhs) const {
|
||||
return this->address - rhs.address;
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
constexpr ALWAYS_INLINE KTypedAddress operator+=(I rhs) {
|
||||
static_assert(std::is_integral<I>::value);
|
||||
|
|
|
@ -65,3 +65,11 @@ namespace ams::kern {
|
|||
MESOSPHERE_INIT_ABORT(); \
|
||||
} \
|
||||
})
|
||||
|
||||
#define MESOSPHERE_R_ABORT_UNLESS(expr) \
|
||||
({ \
|
||||
const ::ams::Result _tmp_meso_r_abort_res = static_cast<::ams::Result>(expr); \
|
||||
if (AMS_UNLIKELY((R_FAILED(_tmp_meso_r_abort_res))) { \
|
||||
MESOSPHERE_PANIC("Result Abort(): %s 2%03d-%04d", #expr, _tmp_meso_r_abort_res.GetModule(), _tmp_meso_r_abort_res.GetDescription()); \
|
||||
} \
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace ams::kern {
|
|||
for (size_t i = 0; i < num_block_shifts; i++) {
|
||||
overhead_size += KPageHeap::Block::CalculateMetadataOverheadSize(region_size, block_shifts[i], (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0);
|
||||
}
|
||||
return util::AlignUp(overhead_size, KMemoryManager::PageSize);
|
||||
return util::AlignUp(overhead_size, PageSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
73
libraries/libmesosphere/source/kern_k_thread_local_page.cpp
Normal file
73
libraries/libmesosphere/source/kern_k_thread_local_page.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
Result KThreadLocalPage::Initialize(KProcess *process) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Set that this process owns us. */
|
||||
this->owner = process;
|
||||
|
||||
/* Allocate a new page. */
|
||||
KPageBuffer *page_buf = KPageBuffer::Allocate();
|
||||
R_UNLESS(page_buf != nullptr, svc::ResultOutOfMemory());
|
||||
auto page_buf_guard = SCOPE_GUARD { KPageBuffer::Free(page_buf); };
|
||||
|
||||
/* Map the address in. */
|
||||
/* TODO: R_TRY(this->owner->GetPageTable().Map(...)); */
|
||||
|
||||
/* We succeeded. */
|
||||
page_buf_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KThreadLocalPage::Finalize() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Get the physical address of the page. */
|
||||
KPhysicalAddress phys_addr = Null<KPhysicalAddress>;
|
||||
/* TODO: MESOSPHERE_ABORT_UNLESS(this->owner->GetPageTable().GetPhysicalAddress(&phys_addr, this->GetAddress())); */
|
||||
|
||||
/* Unmap the page. */
|
||||
/* TODO: R_TRY(this->owner->GetPageTable().Unmap(...); */
|
||||
|
||||
/* Free the page. */
|
||||
KPageBuffer::Free(KPageBuffer::FromPhysicalAddress(phys_addr));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
KProcessAddress KThreadLocalPage::Reserve() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
for (size_t i = 0; i < util::size(this->is_region_free); i++) {
|
||||
if (this->is_region_free[i]) {
|
||||
this->is_region_free[i] = false;
|
||||
return this->GetRegionAddress(i);
|
||||
}
|
||||
}
|
||||
|
||||
return Null<KProcessAddress>;
|
||||
}
|
||||
|
||||
void KThreadLocalPage::Release(KProcessAddress addr) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
this->is_region_free[this->GetRegionIndex(addr)] = true;
|
||||
}
|
||||
|
||||
}
|
|
@ -57,8 +57,8 @@ namespace ams {
|
|||
using BaseType = typename ResultTraits::BaseType;
|
||||
static constexpr BaseType SuccessValue = ResultTraits::SuccessValue;
|
||||
public:
|
||||
constexpr inline BaseType GetModule() const { return ResultTraits::GetModuleFromValue(static_cast<const Self *>(this)->GetValue()); }
|
||||
constexpr inline BaseType GetDescription() const { return ResultTraits::GetDescriptionFromValue(static_cast<const Self *>(this)->GetValue()); }
|
||||
constexpr ALWAYS_INLINE BaseType GetModule() const { return ResultTraits::GetModuleFromValue(static_cast<const Self *>(this)->GetValue()); }
|
||||
constexpr ALWAYS_INLINE BaseType GetDescription() const { return ResultTraits::GetDescriptionFromValue(static_cast<const Self *>(this)->GetValue()); }
|
||||
};
|
||||
|
||||
class ResultConstructor;
|
||||
|
@ -81,15 +81,15 @@ namespace ams {
|
|||
/* TODO: It sure would be nice to make this private. */
|
||||
constexpr Result(typename Base::BaseType v) : value(v) { static_assert(std::is_same<typename Base::BaseType, ::Result>::value); }
|
||||
|
||||
constexpr inline operator ResultSuccess() const;
|
||||
constexpr ALWAYS_INLINE operator ResultSuccess() const;
|
||||
NX_CONSTEXPR bool CanAccept(Result result) { return true; }
|
||||
|
||||
constexpr inline bool IsSuccess() const { return this->GetValue() == Base::SuccessValue; }
|
||||
constexpr inline bool IsFailure() const { return !this->IsSuccess(); }
|
||||
constexpr inline typename Base::BaseType GetModule() const { return Base::GetModule(); }
|
||||
constexpr inline typename Base::BaseType GetDescription() const { return Base::GetDescription(); }
|
||||
constexpr ALWAYS_INLINE bool IsSuccess() const { return this->GetValue() == Base::SuccessValue; }
|
||||
constexpr ALWAYS_INLINE bool IsFailure() const { return !this->IsSuccess(); }
|
||||
constexpr ALWAYS_INLINE typename Base::BaseType GetModule() const { return Base::GetModule(); }
|
||||
constexpr ALWAYS_INLINE typename Base::BaseType GetDescription() const { return Base::GetDescription(); }
|
||||
|
||||
constexpr inline typename Base::BaseType GetValue() const { return this->value; }
|
||||
constexpr ALWAYS_INLINE typename Base::BaseType GetValue() const { return this->value; }
|
||||
};
|
||||
static_assert(sizeof(Result) == sizeof(Result::Base::BaseType), "sizeof(Result) == sizeof(Result::Base::BaseType)");
|
||||
static_assert(std::is_trivially_destructible<Result>::value, "std::is_trivially_destructible<Result>::value");
|
||||
|
@ -98,12 +98,12 @@ namespace ams {
|
|||
|
||||
class ResultConstructor {
|
||||
public:
|
||||
static constexpr inline Result MakeResult(ResultTraits::BaseType value) {
|
||||
static constexpr ALWAYS_INLINE Result MakeResult(ResultTraits::BaseType value) {
|
||||
return Result(value);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline Result MakeResult(ResultTraits::BaseType value) {
|
||||
constexpr ALWAYS_INLINE Result MakeResult(ResultTraits::BaseType value) {
|
||||
return ResultConstructor::MakeResult(value);
|
||||
}
|
||||
|
||||
|
@ -116,12 +116,12 @@ namespace ams {
|
|||
constexpr operator Result() const { return result::impl::MakeResult(Base::SuccessValue); }
|
||||
NX_CONSTEXPR bool CanAccept(Result result) { return result.IsSuccess(); }
|
||||
|
||||
constexpr inline bool IsSuccess() const { return true; }
|
||||
constexpr inline bool IsFailure() const { return !this->IsSuccess(); }
|
||||
constexpr inline typename Base::BaseType GetModule() const { return Base::GetModule(); }
|
||||
constexpr inline typename Base::BaseType GetDescription() const { return Base::GetDescription(); }
|
||||
constexpr ALWAYS_INLINE bool IsSuccess() const { return true; }
|
||||
constexpr ALWAYS_INLINE bool IsFailure() const { return !this->IsSuccess(); }
|
||||
constexpr ALWAYS_INLINE typename Base::BaseType GetModule() const { return Base::GetModule(); }
|
||||
constexpr ALWAYS_INLINE typename Base::BaseType GetDescription() const { return Base::GetDescription(); }
|
||||
|
||||
constexpr inline typename Base::BaseType GetValue() const { return Base::SuccessValue; }
|
||||
constexpr ALWAYS_INLINE typename Base::BaseType GetValue() const { return Base::SuccessValue; }
|
||||
};
|
||||
|
||||
namespace result::impl {
|
||||
|
@ -130,7 +130,7 @@ namespace ams {
|
|||
|
||||
}
|
||||
|
||||
constexpr inline Result::operator ResultSuccess() const {
|
||||
constexpr ALWAYS_INLINE Result::operator ResultSuccess() const {
|
||||
if (!ResultSuccess::CanAccept(*this)) {
|
||||
result::impl::OnResultAssertion(*this);
|
||||
}
|
||||
|
@ -151,10 +151,10 @@ namespace ams {
|
|||
constexpr operator Result() const { return MakeResult(Value); }
|
||||
constexpr operator ResultSuccess() const { OnResultAssertion(Value); }
|
||||
|
||||
constexpr inline bool IsSuccess() const { return false; }
|
||||
constexpr inline bool IsFailure() const { return !this->IsSuccess(); }
|
||||
constexpr ALWAYS_INLINE bool IsSuccess() const { return false; }
|
||||
constexpr ALWAYS_INLINE bool IsFailure() const { return !this->IsSuccess(); }
|
||||
|
||||
constexpr inline typename Base::BaseType GetValue() const { return Value; }
|
||||
constexpr ALWAYS_INLINE typename Base::BaseType GetValue() const { return Value; }
|
||||
};
|
||||
|
||||
template<ResultTraits::BaseType _Module, ResultTraits::BaseType DescStart, ResultTraits::BaseType DescEnd>
|
||||
|
|
|
@ -275,6 +275,8 @@ namespace ams::svc {
|
|||
ThreadActivity_Paused = 1,
|
||||
};
|
||||
|
||||
constexpr size_t ThreadLocalRegionSize = 0x200;
|
||||
|
||||
/* Process types. */
|
||||
enum ProcessInfoType : u32 {
|
||||
ProcessInfoType_ProcessState = 0,
|
||||
|
|
Loading…
Reference in a new issue