|
|
|
@ -70,58 +70,6 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority |
|
|
|
} |
|
|
|
} // Anonymous namespace
|
|
|
|
|
|
|
|
// Represents a page used for thread-local storage.
|
|
|
|
//
|
|
|
|
// Each TLS page contains slots that may be used by processes and threads.
|
|
|
|
// Every process and thread is created with a slot in some arbitrary page
|
|
|
|
// (whichever page happens to have an available slot).
|
|
|
|
class TLSPage { |
|
|
|
public: |
|
|
|
static constexpr std::size_t num_slot_entries = |
|
|
|
Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE; |
|
|
|
|
|
|
|
explicit TLSPage(VAddr address) : base_address{address} {} |
|
|
|
|
|
|
|
bool HasAvailableSlots() const { |
|
|
|
return !is_slot_used.all(); |
|
|
|
} |
|
|
|
|
|
|
|
VAddr GetBaseAddress() const { |
|
|
|
return base_address; |
|
|
|
} |
|
|
|
|
|
|
|
std::optional<VAddr> ReserveSlot() { |
|
|
|
for (std::size_t i = 0; i < is_slot_used.size(); i++) { |
|
|
|
if (is_slot_used[i]) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
is_slot_used[i] = true; |
|
|
|
return base_address + (i * Core::Memory::TLS_ENTRY_SIZE); |
|
|
|
} |
|
|
|
|
|
|
|
return std::nullopt; |
|
|
|
} |
|
|
|
|
|
|
|
void ReleaseSlot(VAddr address) { |
|
|
|
// Ensure that all given addresses are consistent with how TLS pages
|
|
|
|
// are intended to be used when releasing slots.
|
|
|
|
ASSERT(IsWithinPage(address)); |
|
|
|
ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0); |
|
|
|
|
|
|
|
const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE; |
|
|
|
is_slot_used[index] = false; |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
bool IsWithinPage(VAddr address) const { |
|
|
|
return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE; |
|
|
|
} |
|
|
|
|
|
|
|
VAddr base_address; |
|
|
|
std::bitset<num_slot_entries> is_slot_used; |
|
|
|
}; |
|
|
|
|
|
|
|
ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, |
|
|
|
ProcessType type, KResourceLimit* res_limit) { |
|
|
|
auto& kernel = system.Kernel(); |
|
|
|
@ -404,7 +352,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, |
|
|
|
} |
|
|
|
|
|
|
|
// Create TLS region
|
|
|
|
tls_region_address = CreateTLSRegion(); |
|
|
|
R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address))); |
|
|
|
memory_reservation.Commit(); |
|
|
|
|
|
|
|
return handle_table.Initialize(capabilities.GetHandleTableSize()); |
|
|
|
@ -444,7 +392,7 @@ void KProcess::PrepareForTermination() { |
|
|
|
|
|
|
|
stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); |
|
|
|
|
|
|
|
FreeTLSRegion(tls_region_address); |
|
|
|
this->DeleteThreadLocalRegion(tls_region_address); |
|
|
|
tls_region_address = 0; |
|
|
|
|
|
|
|
if (resource_limit) { |
|
|
|
@ -487,63 +435,103 @@ void KProcess::Finalize() { |
|
|
|
KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempts to find a TLS page that contains a free slot for |
|
|
|
* use by a thread. |
|
|
|
* |
|
|
|
* @returns If a page with an available slot is found, then an iterator |
|
|
|
* pointing to the page is returned. Otherwise the end iterator |
|
|
|
* is returned instead. |
|
|
|
*/ |
|
|
|
static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) { |
|
|
|
return std::find_if(tls_pages.begin(), tls_pages.end(), |
|
|
|
[](const auto& page) { return page.HasAvailableSlots(); }); |
|
|
|
} |
|
|
|
ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) { |
|
|
|
KThreadLocalPage* tlp = nullptr; |
|
|
|
VAddr tlr = 0; |
|
|
|
|
|
|
|
VAddr KProcess::CreateTLSRegion() { |
|
|
|
KScopedSchedulerLock lock(kernel); |
|
|
|
if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; |
|
|
|
tls_page_iter != tls_pages.cend()) { |
|
|
|
return *tls_page_iter->ReserveSlot(); |
|
|
|
} |
|
|
|
// See if we can get a region from a partially used TLP.
|
|
|
|
{ |
|
|
|
KScopedSchedulerLock sl{kernel}; |
|
|
|
|
|
|
|
if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) { |
|
|
|
tlr = it->Reserve(); |
|
|
|
ASSERT(tlr != 0); |
|
|
|
|
|
|
|
if (it->IsAllUsed()) { |
|
|
|
tlp = std::addressof(*it); |
|
|
|
partially_used_tlp_tree.erase(it); |
|
|
|
fully_used_tlp_tree.insert(*tlp); |
|
|
|
} |
|
|
|
|
|
|
|
Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()}; |
|
|
|
ASSERT(tls_page_ptr); |
|
|
|
*out = tlr; |
|
|
|
return ResultSuccess; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const VAddr start{page_table->GetKernelMapRegionStart()}; |
|
|
|
const VAddr size{page_table->GetKernelMapRegionEnd() - start}; |
|
|
|
const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)}; |
|
|
|
const VAddr tls_page_addr{page_table |
|
|
|
->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize, |
|
|
|
KMemoryState::ThreadLocal, |
|
|
|
KMemoryPermission::UserReadWrite, |
|
|
|
tls_map_addr) |
|
|
|
.ValueOr(0)}; |
|
|
|
// Allocate a new page.
|
|
|
|
tlp = KThreadLocalPage::Allocate(kernel); |
|
|
|
R_UNLESS(tlp != nullptr, ResultOutOfMemory); |
|
|
|
auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); }); |
|
|
|
|
|
|
|
ASSERT(tls_page_addr); |
|
|
|
// Initialize the new page.
|
|
|
|
R_TRY(tlp->Initialize(kernel, this)); |
|
|
|
|
|
|
|
std::memset(tls_page_ptr, 0, PageSize); |
|
|
|
tls_pages.emplace_back(tls_page_addr); |
|
|
|
// Reserve a TLR.
|
|
|
|
tlr = tlp->Reserve(); |
|
|
|
ASSERT(tlr != 0); |
|
|
|
|
|
|
|
const auto reserve_result{tls_pages.back().ReserveSlot()}; |
|
|
|
ASSERT(reserve_result.has_value()); |
|
|
|
// Insert into our tree.
|
|
|
|
{ |
|
|
|
KScopedSchedulerLock sl{kernel}; |
|
|
|
if (tlp->IsAllUsed()) { |
|
|
|
fully_used_tlp_tree.insert(*tlp); |
|
|
|
} else { |
|
|
|
partially_used_tlp_tree.insert(*tlp); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return *reserve_result; |
|
|
|
// We succeeded!
|
|
|
|
tlp_guard.Cancel(); |
|
|
|
*out = tlr; |
|
|
|
return ResultSuccess; |
|
|
|
} |
|
|
|
|
|
|
|
void KProcess::FreeTLSRegion(VAddr tls_address) { |
|
|
|
KScopedSchedulerLock lock(kernel); |
|
|
|
const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); |
|
|
|
auto iter = |
|
|
|
std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { |
|
|
|
return page.GetBaseAddress() == aligned_address; |
|
|
|
}); |
|
|
|
ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) { |
|
|
|
KThreadLocalPage* page_to_free = nullptr; |
|
|
|
|
|
|
|
// Release the region.
|
|
|
|
{ |
|
|
|
KScopedSchedulerLock sl{kernel}; |
|
|
|
|
|
|
|
// Try to find the page in the partially used list.
|
|
|
|
auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize)); |
|
|
|
if (it == partially_used_tlp_tree.end()) { |
|
|
|
// If we don't find it, it has to be in the fully used list.
|
|
|
|
it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize)); |
|
|
|
R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress); |
|
|
|
|
|
|
|
// Release the region.
|
|
|
|
it->Release(addr); |
|
|
|
|
|
|
|
// Move the page out of the fully used list.
|
|
|
|
KThreadLocalPage* tlp = std::addressof(*it); |
|
|
|
fully_used_tlp_tree.erase(it); |
|
|
|
if (tlp->IsAllFree()) { |
|
|
|
page_to_free = tlp; |
|
|
|
} else { |
|
|
|
partially_used_tlp_tree.insert(*tlp); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// Release the region.
|
|
|
|
it->Release(addr); |
|
|
|
|
|
|
|
// Handle the all-free case.
|
|
|
|
KThreadLocalPage* tlp = std::addressof(*it); |
|
|
|
if (tlp->IsAllFree()) { |
|
|
|
partially_used_tlp_tree.erase(it); |
|
|
|
page_to_free = tlp; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// If we should free the page it was in, do so.
|
|
|
|
if (page_to_free != nullptr) { |
|
|
|
page_to_free->Finalize(); |
|
|
|
|
|
|
|
// Something has gone very wrong if we're freeing a region
|
|
|
|
// with no actual page available.
|
|
|
|
ASSERT(iter != tls_pages.cend()); |
|
|
|
KThreadLocalPage::Free(kernel, page_to_free); |
|
|
|
} |
|
|
|
|
|
|
|
iter->ReleaseSlot(tls_address); |
|
|
|
return ResultSuccess; |
|
|
|
} |
|
|
|
|
|
|
|
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { |
|
|
|
|