summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Zach Hilman2019-07-04 15:32:32 -0400
committerGravatar GitHub2019-07-04 15:32:32 -0400
commit54a02d14fd38282e446a56892a1c45f558ee0e42 (patch)
tree79a86a51973712ee8307071a6c5dc471289746eb /src
parentMerge pull request #2670 from DarkLordZach/fix-merge-discrep-1 (diff)
parentkernel/process: Default initialize all member variables (diff)
downloadyuzu-54a02d14fd38282e446a56892a1c45f558ee0e42.tar.gz
yuzu-54a02d14fd38282e446a56892a1c45f558ee0e42.tar.xz
yuzu-54a02d14fd38282e446a56892a1c45f558ee0e42.zip
Merge pull request #2555 from lioncash/tls
kernel/process: Decouple TLS handling from threads
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/kernel/process.cpp135
-rw-r--r--src/core/hle/kernel/process.h12
-rw-r--r--src/core/hle/kernel/thread.cpp4
-rw-r--r--src/core/hle/kernel/thread.h16
-rw-r--r--src/core/hle/kernel/vm_manager.cpp31
-rw-r--r--src/core/hle/kernel/vm_manager.h31
6 files changed, 148 insertions, 81 deletions
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 7cfc513a1..f45ef05f6 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <bitset>
6#include <memory> 7#include <memory>
7#include <random> 8#include <random>
8#include "common/alignment.h" 9#include "common/alignment.h"
@@ -48,8 +49,58 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
48} 49}
49} // Anonymous namespace 50} // Anonymous namespace
50 51
51SharedPtr<Process> Process::Create(Core::System& system, std::string name, 52// Represents a page used for thread-local storage.
52 Process::ProcessType type) { 53//
54// Each TLS page contains slots that may be used by processes and threads.
55// Every process and thread is created with a slot in some arbitrary page
56// (whichever page happens to have an available slot).
57class TLSPage {
58public:
59 static constexpr std::size_t num_slot_entries = Memory::PAGE_SIZE / Memory::TLS_ENTRY_SIZE;
60
61 explicit TLSPage(VAddr address) : base_address{address} {}
62
63 bool HasAvailableSlots() const {
64 return !is_slot_used.all();
65 }
66
67 VAddr GetBaseAddress() const {
68 return base_address;
69 }
70
71 std::optional<VAddr> ReserveSlot() {
72 for (std::size_t i = 0; i < is_slot_used.size(); i++) {
73 if (is_slot_used[i]) {
74 continue;
75 }
76
77 is_slot_used[i] = true;
78 return base_address + (i * Memory::TLS_ENTRY_SIZE);
79 }
80
81 return std::nullopt;
82 }
83
84 void ReleaseSlot(VAddr address) {
85 // Ensure that all given addresses are consistent with how TLS pages
86 // are intended to be used when releasing slots.
87 ASSERT(IsWithinPage(address));
88 ASSERT((address % Memory::TLS_ENTRY_SIZE) == 0);
89
90 const std::size_t index = (address - base_address) / Memory::TLS_ENTRY_SIZE;
91 is_slot_used[index] = false;
92 }
93
94private:
95 bool IsWithinPage(VAddr address) const {
96 return base_address <= address && address < base_address + Memory::PAGE_SIZE;
97 }
98
99 VAddr base_address;
100 std::bitset<num_slot_entries> is_slot_used;
101};
102
103SharedPtr<Process> Process::Create(Core::System& system, std::string name, ProcessType type) {
53 auto& kernel = system.Kernel(); 104 auto& kernel = system.Kernel();
54 105
55 SharedPtr<Process> process(new Process(system)); 106 SharedPtr<Process> process(new Process(system));
@@ -181,61 +232,55 @@ void Process::PrepareForTermination() {
181} 232}
182 233
183/** 234/**
184 * Finds a free location for the TLS section of a thread. 235 * Attempts to find a TLS page that contains a free slot for
185 * @param tls_slots The TLS page array of the thread's owner process. 236 * use by a thread.
186 * Returns a tuple of (page, slot, alloc_needed) where: 237 *
187 * page: The index of the first allocated TLS page that has free slots. 238 * @returns If a page with an available slot is found, then an iterator
188 * slot: The index of the first free slot in the indicated page. 239 * pointing to the page is returned. Otherwise the end iterator
189 * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full). 240 * is returned instead.
190 */ 241 */
191static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot( 242static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
192 const std::vector<std::bitset<8>>& tls_slots) { 243 return std::find_if(tls_pages.begin(), tls_pages.end(),
193 // Iterate over all the allocated pages, and try to find one where not all slots are used. 244 [](const auto& page) { return page.HasAvailableSlots(); });
194 for (std::size_t page = 0; page < tls_slots.size(); ++page) {
195 const auto& page_tls_slots = tls_slots[page];
196 if (!page_tls_slots.all()) {
197 // We found a page with at least one free slot, find which slot it is
198 for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
199 if (!page_tls_slots.test(slot)) {
200 return std::make_tuple(page, slot, false);
201 }
202 }
203 }
204 }
205
206 return std::make_tuple(0, 0, true);
207} 245}
208 246
209VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) { 247VAddr Process::CreateTLSRegion() {
210 auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots); 248 auto tls_page_iter = FindTLSPageWithAvailableSlots(tls_pages);
211 const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress();
212 249
213 if (needs_allocation) { 250 if (tls_page_iter == tls_pages.cend()) {
214 tls_slots.emplace_back(0); // The page is completely available at the start 251 const auto region_address =
215 available_page = tls_slots.size() - 1; 252 vm_manager.FindFreeRegion(vm_manager.GetTLSIORegionBaseAddress(),
216 available_slot = 0; // Use the first slot in the new page 253 vm_manager.GetTLSIORegionEndAddress(), Memory::PAGE_SIZE);
254 ASSERT(region_address.Succeeded());
217 255
218 // Allocate some memory from the end of the linear heap for this region. 256 const auto map_result = vm_manager.MapMemoryBlock(
219 auto& tls_memory = thread.GetTLSMemory(); 257 *region_address, std::make_shared<std::vector<u8>>(Memory::PAGE_SIZE), 0,
220 tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0); 258 Memory::PAGE_SIZE, MemoryState::ThreadLocal);
259 ASSERT(map_result.Succeeded());
221 260
222 vm_manager.RefreshMemoryBlockMappings(tls_memory.get()); 261 tls_pages.emplace_back(*region_address);
223 262
224 vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0, 263 const auto reserve_result = tls_pages.back().ReserveSlot();
225 Memory::PAGE_SIZE, MemoryState::ThreadLocal); 264 ASSERT(reserve_result.has_value());
226 }
227 265
228 tls_slots[available_page].set(available_slot); 266 return *reserve_result;
267 }
229 268
230 return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE; 269 return *tls_page_iter->ReserveSlot();
231} 270}
232 271
233void Process::FreeTLSSlot(VAddr tls_address) { 272void Process::FreeTLSRegion(VAddr tls_address) {
234 const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress(); 273 const VAddr aligned_address = Common::AlignDown(tls_address, Memory::PAGE_SIZE);
235 const VAddr tls_page = tls_base / Memory::PAGE_SIZE; 274 auto iter =
236 const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; 275 std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
276 return page.GetBaseAddress() == aligned_address;
277 });
278
279 // Something has gone very wrong if we're freeing a region
280 // with no actual page available.
281 ASSERT(iter != tls_pages.cend());
237 282
238 tls_slots[tls_page].reset(tls_slot); 283 iter->ReleaseSlot(tls_address);
239} 284}
240 285
241void Process::LoadModule(CodeSet module_, VAddr base_addr) { 286void Process::LoadModule(CodeSet module_, VAddr base_addr) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 248fd3840..83ea02bee 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <bitset>
9#include <cstddef> 8#include <cstddef>
10#include <list> 9#include <list>
11#include <string> 10#include <string>
@@ -32,6 +31,7 @@ namespace Kernel {
32class KernelCore; 31class KernelCore;
33class ResourceLimit; 32class ResourceLimit;
34class Thread; 33class Thread;
34class TLSPage;
35 35
36struct CodeSet; 36struct CodeSet;
37 37
@@ -260,10 +260,10 @@ public:
260 // Thread-local storage management 260 // Thread-local storage management
261 261
262 // Marks the next available region as used and returns the address of the slot. 262 // Marks the next available region as used and returns the address of the slot.
263 VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread); 263 [[nodiscard]] VAddr CreateTLSRegion();
264 264
265 // Frees a used TLS slot identified by the given address 265 // Frees a used TLS slot identified by the given address
266 void FreeTLSSlot(VAddr tls_address); 266 void FreeTLSRegion(VAddr tls_address);
267 267
268private: 268private:
269 explicit Process(Core::System& system); 269 explicit Process(Core::System& system);
@@ -290,7 +290,7 @@ private:
290 u64 code_memory_size = 0; 290 u64 code_memory_size = 0;
291 291
292 /// Current status of the process 292 /// Current status of the process
293 ProcessStatus status; 293 ProcessStatus status{};
294 294
295 /// The ID of this process 295 /// The ID of this process
296 u64 process_id = 0; 296 u64 process_id = 0;
@@ -310,7 +310,7 @@ private:
310 /// holds the TLS for a specific thread. This vector contains which parts are in use for each 310 /// holds the TLS for a specific thread. This vector contains which parts are in use for each
311 /// page as a bitmask. 311 /// page as a bitmask.
312 /// This vector will grow as more pages are allocated for new threads. 312 /// This vector will grow as more pages are allocated for new threads.
313 std::vector<std::bitset<8>> tls_slots; 313 std::vector<TLSPage> tls_pages;
314 314
315 /// Contains the parsed process capability descriptors. 315 /// Contains the parsed process capability descriptors.
316 ProcessCapabilities capabilities; 316 ProcessCapabilities capabilities;
@@ -339,7 +339,7 @@ private:
339 Mutex mutex; 339 Mutex mutex;
340 340
341 /// Random values for svcGetInfo RandomEntropy 341 /// Random values for svcGetInfo RandomEntropy
342 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; 342 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
343 343
344 /// List of threads that are running with this process as their owner. 344 /// List of threads that are running with this process as their owner.
345 std::list<const Thread*> thread_list; 345 std::list<const Thread*> thread_list;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index a055a5002..ec529e7f2 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -65,7 +65,7 @@ void Thread::Stop() {
65 owner_process->UnregisterThread(this); 65 owner_process->UnregisterThread(this);
66 66
67 // Mark the TLS slot in the thread's page as free. 67 // Mark the TLS slot in the thread's page as free.
68 owner_process->FreeTLSSlot(tls_address); 68 owner_process->FreeTLSRegion(tls_address);
69} 69}
70 70
71void Thread::WakeAfterDelay(s64 nanoseconds) { 71void Thread::WakeAfterDelay(s64 nanoseconds) {
@@ -205,9 +205,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
205 thread->name = std::move(name); 205 thread->name = std::move(name);
206 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); 206 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
207 thread->owner_process = &owner_process; 207 thread->owner_process = &owner_process;
208 thread->tls_address = thread->owner_process->CreateTLSRegion();
208 thread->scheduler = &system.Scheduler(processor_id); 209 thread->scheduler = &system.Scheduler(processor_id);
209 thread->scheduler->AddThread(thread); 210 thread->scheduler->AddThread(thread);
210 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
211 211
212 thread->owner_process->RegisterThread(thread.get()); 212 thread->owner_process->RegisterThread(thread.get());
213 213
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index b4b9cda7c..07e989637 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <functional> 7#include <functional>
8#include <memory>
9#include <string> 8#include <string>
10#include <vector> 9#include <vector>
11 10
@@ -78,9 +77,6 @@ enum class ThreadActivity : u32 {
78 77
79class Thread final : public WaitObject { 78class Thread final : public WaitObject {
80public: 79public:
81 using TLSMemory = std::vector<u8>;
82 using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
83
84 using MutexWaitingThreads = std::vector<SharedPtr<Thread>>; 80 using MutexWaitingThreads = std::vector<SharedPtr<Thread>>;
85 81
86 using ThreadContext = Core::ARM_Interface::ThreadContext; 82 using ThreadContext = Core::ARM_Interface::ThreadContext;
@@ -169,14 +165,6 @@ public:
169 return thread_id; 165 return thread_id;
170 } 166 }
171 167
172 TLSMemoryPtr& GetTLSMemory() {
173 return tls_memory;
174 }
175
176 const TLSMemoryPtr& GetTLSMemory() const {
177 return tls_memory;
178 }
179
180 /// Resumes a thread from waiting 168 /// Resumes a thread from waiting
181 void ResumeFromWait(); 169 void ResumeFromWait();
182 170
@@ -463,11 +451,9 @@ private:
463 u32 ideal_core{0xFFFFFFFF}; 451 u32 ideal_core{0xFFFFFFFF};
464 u64 affinity_mask{0x1}; 452 u64 affinity_mask{0x1};
465 453
466 TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>(); 454 ThreadActivity activity = ThreadActivity::Normal;
467 455
468 std::string name; 456 std::string name;
469
470 ThreadActivity activity = ThreadActivity::Normal;
471}; 457};
472 458
473/** 459/**
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index c929c2a52..3df5ccb7f 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -152,22 +152,33 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
152} 152}
153 153
154ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const { 154ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
155 // Find the first Free VMA. 155 return FindFreeRegion(GetASLRRegionBaseAddress(), GetASLRRegionEndAddress(), size);
156 const VAddr base = GetASLRRegionBaseAddress(); 156}
157 const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
158 if (vma.second.type != VMAType::Free)
159 return false;
160 157
161 const VAddr vma_end = vma.second.base + vma.second.size; 158ResultVal<VAddr> VMManager::FindFreeRegion(VAddr begin, VAddr end, u64 size) const {
162 return vma_end > base && vma_end >= base + size; 159 ASSERT(begin < end);
163 }); 160 ASSERT(size <= end - begin);
164 161
165 if (vma_handle == vma_map.end()) { 162 const VMAHandle vma_handle =
163 std::find_if(vma_map.begin(), vma_map.end(), [begin, end, size](const auto& vma) {
164 if (vma.second.type != VMAType::Free) {
165 return false;
166 }
167 const VAddr vma_base = vma.second.base;
168 const VAddr vma_end = vma_base + vma.second.size;
169 const VAddr assumed_base = (begin < vma_base) ? vma_base : begin;
170 const VAddr used_range = assumed_base + size;
171
172 return vma_base <= assumed_base && assumed_base < used_range && used_range < end &&
173 used_range <= vma_end;
174 });
175
176 if (vma_handle == vma_map.cend()) {
166 // TODO(Subv): Find the correct error code here. 177 // TODO(Subv): Find the correct error code here.
167 return ResultCode(-1); 178 return ResultCode(-1);
168 } 179 }
169 180
170 const VAddr target = std::max(base, vma_handle->second.base); 181 const VAddr target = std::max(begin, vma_handle->second.base);
171 return MakeResult<VAddr>(target); 182 return MakeResult<VAddr>(target);
172} 183}
173 184
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index dfbf7a894..752ae62f9 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -362,14 +362,39 @@ public:
362 ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); 362 ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
363 363
364 /** 364 /**
365 * Finds the first free address that can hold a region of the desired size. 365 * Finds the first free memory region of the given size within
366 * the user-addressable ASLR memory region.
366 * 367 *
367 * @param size Size of the desired region. 368 * @param size The size of the desired region in bytes.
368 * @return The found free address. 369 *
370 * @returns If successful, the base address of the free region with
371 * the given size.
369 */ 372 */
370 ResultVal<VAddr> FindFreeRegion(u64 size) const; 373 ResultVal<VAddr> FindFreeRegion(u64 size) const;
371 374
372 /** 375 /**
376 * Finds the first free address range that can hold a region of the desired size
377 *
378 * @param begin The starting address of the range.
379 * This is treated as an inclusive beginning address.
380 *
381 * @param end The ending address of the range.
382 * This is treated as an exclusive ending address.
383 *
384 * @param size The size of the free region to attempt to locate,
385 * in bytes.
386 *
387 * @returns If successful, the base address of the free region with
388 * the given size.
389 *
390 * @returns If unsuccessful, a result containing an error code.
391 *
392 * @pre The starting address must be less than the ending address.
393 * @pre The size must not exceed the address range itself.
394 */
395 ResultVal<VAddr> FindFreeRegion(VAddr begin, VAddr end, u64 size) const;
396
397 /**
373 * Maps a memory-mapped IO region at a given address. 398 * Maps a memory-mapped IO region at a given address.
374 * 399 *
375 * @param target The guest address to start the mapping at. 400 * @param target The guest address to start the mapping at.