kern: implement KThreadLocalPage

This commit is contained in:
Michael Scire 2020-01-30 16:51:35 -08:00
parent 484f132651
commit 059c706f19
15 changed files with 295 additions and 30 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -19,8 +19,6 @@
namespace ams::kern {
class KMemoryManager {
public:
static constexpr size_t PageSize = 0x1000; /* TODO: Elsewhere? */
private:
class Impl {
public:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

@ -275,6 +275,8 @@ namespace ams::svc {
ThreadActivity_Paused = 1,
};
constexpr size_t ThreadLocalRegionSize = 0x200;
/* Process types. */
enum ProcessInfoType : u32 {
ProcessInfoType_ProcessState = 0,