|
|
|
@ -3,6 +3,7 @@ |
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <bitset>
|
|
|
|
#include <memory>
|
|
|
|
#include <random>
|
|
|
|
#include "common/alignment.h"
|
|
|
|
@ -48,8 +49,58 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) { |
|
|
|
} |
|
|
|
} // Anonymous namespace
|
|
|
|
|
|
|
|
SharedPtr<Process> Process::Create(Core::System& system, std::string name, |
|
|
|
Process::ProcessType type) { |
|
|
|
// 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 = Memory::PAGE_SIZE / 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 * 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 % Memory::TLS_ENTRY_SIZE) == 0); |
|
|
|
|
|
|
|
const std::size_t index = (address - base_address) / Memory::TLS_ENTRY_SIZE; |
|
|
|
is_slot_used[index] = false; |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
bool IsWithinPage(VAddr address) const { |
|
|
|
return base_address <= address && address < base_address + Memory::PAGE_SIZE; |
|
|
|
} |
|
|
|
|
|
|
|
VAddr base_address; |
|
|
|
std::bitset<num_slot_entries> is_slot_used; |
|
|
|
}; |
|
|
|
|
|
|
|
SharedPtr<Process> Process::Create(Core::System& system, std::string name, ProcessType type) { |
|
|
|
auto& kernel = system.Kernel(); |
|
|
|
|
|
|
|
SharedPtr<Process> process(new Process(system)); |
|
|
|
@ -181,61 +232,55 @@ void Process::PrepareForTermination() { |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* Finds a free location for the TLS section of a thread. |
|
|
|
* @param tls_slots The TLS page array of the thread's owner process. |
|
|
|
* Returns a tuple of (page, slot, alloc_needed) where: |
|
|
|
* page: The index of the first allocated TLS page that has free slots. |
|
|
|
* slot: The index of the first free slot in the indicated page. |
|
|
|
* alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full). |
|
|
|
* 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 std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot( |
|
|
|
const std::vector<std::bitset<8>>& tls_slots) { |
|
|
|
// Iterate over all the allocated pages, and try to find one where not all slots are used.
|
|
|
|
for (std::size_t page = 0; page < tls_slots.size(); ++page) { |
|
|
|
const auto& page_tls_slots = tls_slots[page]; |
|
|
|
if (!page_tls_slots.all()) { |
|
|
|
// We found a page with at least one free slot, find which slot it is
|
|
|
|
for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) { |
|
|
|
if (!page_tls_slots.test(slot)) { |
|
|
|
return std::make_tuple(page, slot, false); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return std::make_tuple(0, 0, true); |
|
|
|
static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) { |
|
|
|
return std::find_if(tls_pages.begin(), tls_pages.end(), |
|
|
|
[](const auto& page) { return page.HasAvailableSlots(); }); |
|
|
|
} |
|
|
|
|
|
|
|
VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) { |
|
|
|
auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots); |
|
|
|
const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress(); |
|
|
|
VAddr Process::CreateTLSRegion() { |
|
|
|
auto tls_page_iter = FindTLSPageWithAvailableSlots(tls_pages); |
|
|
|
|
|
|
|
if (needs_allocation) { |
|
|
|
tls_slots.emplace_back(0); // The page is completely available at the start
|
|
|
|
available_page = tls_slots.size() - 1; |
|
|
|
available_slot = 0; // Use the first slot in the new page
|
|
|
|
if (tls_page_iter == tls_pages.cend()) { |
|
|
|
const auto region_address = |
|
|
|
vm_manager.FindFreeRegion(vm_manager.GetTLSIORegionBaseAddress(), |
|
|
|
vm_manager.GetTLSIORegionEndAddress(), Memory::PAGE_SIZE); |
|
|
|
ASSERT(region_address.Succeeded()); |
|
|
|
|
|
|
|
// Allocate some memory from the end of the linear heap for this region.
|
|
|
|
auto& tls_memory = thread.GetTLSMemory(); |
|
|
|
tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0); |
|
|
|
const auto map_result = vm_manager.MapMemoryBlock( |
|
|
|
*region_address, std::make_shared<std::vector<u8>>(Memory::PAGE_SIZE), 0, |
|
|
|
Memory::PAGE_SIZE, MemoryState::ThreadLocal); |
|
|
|
ASSERT(map_result.Succeeded()); |
|
|
|
|
|
|
|
vm_manager.RefreshMemoryBlockMappings(tls_memory.get()); |
|
|
|
tls_pages.emplace_back(*region_address); |
|
|
|
|
|
|
|
vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0, |
|
|
|
Memory::PAGE_SIZE, MemoryState::ThreadLocal); |
|
|
|
} |
|
|
|
const auto reserve_result = tls_pages.back().ReserveSlot(); |
|
|
|
ASSERT(reserve_result.has_value()); |
|
|
|
|
|
|
|
tls_slots[available_page].set(available_slot); |
|
|
|
return *reserve_result; |
|
|
|
} |
|
|
|
|
|
|
|
return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE; |
|
|
|
return *tls_page_iter->ReserveSlot(); |
|
|
|
} |
|
|
|
|
|
|
|
void Process::FreeTLSSlot(VAddr tls_address) { |
|
|
|
const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress(); |
|
|
|
const VAddr tls_page = tls_base / Memory::PAGE_SIZE; |
|
|
|
const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; |
|
|
|
void Process::FreeTLSRegion(VAddr tls_address) { |
|
|
|
const VAddr aligned_address = Common::AlignDown(tls_address, Memory::PAGE_SIZE); |
|
|
|
auto iter = |
|
|
|
std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { |
|
|
|
return page.GetBaseAddress() == aligned_address; |
|
|
|
}); |
|
|
|
|
|
|
|
// Something has gone very wrong if we're freeing a region
|
|
|
|
// with no actual page available.
|
|
|
|
ASSERT(iter != tls_pages.cend()); |
|
|
|
|
|
|
|
tls_slots[tls_page].reset(tls_slot); |
|
|
|
iter->ReleaseSlot(tls_address); |
|
|
|
} |
|
|
|
|
|
|
|
void Process::LoadModule(CodeSet module_, VAddr base_addr) { |
|
|
|
|