1/*
2 * Copyright 2014, Pawe�� Dziepak, pdziepak@quarnos.org.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "elf_tls.h"
7
8#include <stdlib.h>
9#include <string.h>
10
11#include <support/TLS.h>
12
13#include <tls.h>
14
15#include <util/kernel_cpp.h>
16
17
18class TLSBlock {
19public:
20	inline				TLSBlock();
21	inline				TLSBlock(void* pointer);
22
23	inline	status_t	Initialize(unsigned dso);
24
25			void		Destroy();
26
27			bool		IsInvalid() const	{ return fPointer == NULL; }
28
29			void*		operator+(addr_t offset) const
30							{ return (void*)((addr_t)fPointer + offset); }
31
32private:
33			void*		fPointer;
34};
35
36class Generation {
37public:
38	inline				Generation();
39
40			unsigned	Counter() const	{ return fCounter; }
41			unsigned	Size() const	{ return fSize; }
42
43			void		SetCounter(unsigned counter)	{ fCounter = counter; }
44			void		SetSize(unsigned size)	{ fSize = size; }
45
46private:
47			unsigned	fCounter;
48			unsigned	fSize;
49};
50
51class DynamicThreadVector {
52public:
53	inline				DynamicThreadVector();
54
55			void		DestroyAll();
56
57	inline	TLSBlock&	operator[](unsigned dso);
58
59private:
60			bool		_DoesExist() const	{ return *fVector != NULL; }
61			unsigned	_Size() const
62							{ return _DoesExist()
63									? fGeneration->Size() : 0; }
64
65			unsigned	_Generation() const;
66
67			status_t	_ResizeVector(unsigned minimumSize);
68
69			TLSBlock**	fVector;
70			Generation*	fGeneration;
71			TLSBlock	fNullBlock;
72};
73
74
75TLSBlockTemplates*	TLSBlockTemplates::fInstance;
76
77
78void
79TLSBlockTemplate::SetBaseAddress(addr_t baseAddress)
80{
81	fAddress = (void*)((addr_t)fAddress + baseAddress);
82}
83
84
85TLSBlock
86TLSBlockTemplate::CreateBlock()
87{
88	void* pointer = malloc(fMemorySize);
89	if (pointer == NULL)
90		return TLSBlock();
91	memcpy(pointer, fAddress, fFileSize);
92	if (fMemorySize > fFileSize)
93		memset((char*)pointer + fFileSize, 0, fMemorySize - fFileSize);
94	return TLSBlock(pointer);
95}
96
97
98TLSBlockTemplates&
99TLSBlockTemplates::Get()
100{
101	if (fInstance == NULL)
102		fInstance = new TLSBlockTemplates;
103	return *fInstance;
104}
105
106
107unsigned
108TLSBlockTemplates::Register(const TLSBlockTemplate& block)
109{
110	unsigned dso;
111
112	if (!fFreeDSOs.empty()) {
113		dso = fFreeDSOs.back();
114		fFreeDSOs.pop_back();
115		fTemplates[dso] = block;
116	} else {
117		dso = fTemplates.size();
118		fTemplates.push_back(block);
119	}
120
121	fTemplates[dso].SetGeneration(fGeneration);
122	return dso;
123}
124
125
126void
127TLSBlockTemplates::Unregister(unsigned dso)
128{
129	if (dso == unsigned(-1))
130		return;
131
132	fGeneration++;
133	fFreeDSOs.push_back(dso);
134}
135
136
137void
138TLSBlockTemplates::SetBaseAddress(unsigned dso, addr_t baseAddress)
139{
140	if (dso != unsigned(-1))
141		fTemplates[dso].SetBaseAddress(baseAddress);
142}
143
144
145unsigned
146TLSBlockTemplates::GetGeneration(unsigned dso) const
147{
148	if (dso == unsigned(-1))
149		return fGeneration;
150	return fTemplates[dso].Generation();
151}
152
153
154TLSBlock
155TLSBlockTemplates::CreateBlock(unsigned dso)
156{
157	return fTemplates[dso].CreateBlock();
158}
159
160
161TLSBlockTemplates::TLSBlockTemplates()
162	:
163	fGeneration(0)
164{
165}
166
167
168TLSBlock::TLSBlock()
169	:
170	fPointer(NULL)
171{
172}
173
174
175TLSBlock::TLSBlock(void* pointer)
176	:
177	fPointer(pointer)
178{
179}
180
181
182status_t
183TLSBlock::Initialize(unsigned dso)
184{
185	fPointer = TLSBlockTemplates::Get().CreateBlock(dso).fPointer;
186	return fPointer != NULL ? B_OK : B_NO_MEMORY;
187}
188
189
190void
191TLSBlock::Destroy()
192{
193	free(fPointer);
194	fPointer = NULL;
195}
196
197
198Generation::Generation()
199	:
200	fCounter(0),
201	fSize(0)
202{
203}
204
205
206DynamicThreadVector::DynamicThreadVector()
207	:
208	fVector((TLSBlock**)tls_address(TLS_DYNAMIC_THREAD_VECTOR)),
209	fGeneration(NULL)
210{
211	if (*fVector != NULL)
212		fGeneration = (Generation*)*(void**)*fVector;
213}
214
215
216void
217DynamicThreadVector::DestroyAll()
218{
219	for (unsigned i = 0; i < _Size(); i++) {
220		TLSBlock& block = (*fVector)[i + 1];
221		if (!block.IsInvalid())
222			block.Destroy();
223	}
224
225	free(*fVector);
226	*fVector = NULL;
227
228	delete fGeneration;
229}
230
231
232TLSBlock&
233DynamicThreadVector::operator[](unsigned dso)
234{
235	unsigned generation = TLSBlockTemplates::Get().GetGeneration(-1);
236	if (_Generation() < generation) {
237		for (unsigned i = 0; i < _Size(); i++) {
238			TLSBlock& block = (*fVector)[i + 1];
239			unsigned dsoGeneration
240				= TLSBlockTemplates::Get().GetGeneration(dso);
241			if (_Generation() < dsoGeneration && dsoGeneration <= generation)
242				block.Destroy();
243		}
244
245		fGeneration->SetCounter(generation);
246	}
247
248	if (_Size() <= dso) {
249		status_t result = _ResizeVector(dso + 1);
250		if (result != B_OK)
251			return fNullBlock;
252	}
253
254	TLSBlock& block = (*fVector)[dso + 1];
255	if (block.IsInvalid()) {
256		status_t result = block.Initialize(dso);
257		if (result != B_OK)
258			return fNullBlock;
259	};
260
261	return block;
262}
263
264
265unsigned
266DynamicThreadVector::_Generation() const
267{
268	if (fGeneration != NULL)
269		return fGeneration->Counter();
270	return unsigned(-1);
271}
272
273
274status_t
275DynamicThreadVector::_ResizeVector(unsigned minimumSize)
276{
277	static const unsigned kInitialSize = 4;
278	unsigned size = std::max(minimumSize, kInitialSize);
279	unsigned oldSize = _Size();
280	if (size <= oldSize)
281		return B_OK;
282
283	void* newVector = realloc(*fVector, (size + 1) * sizeof(TLSBlock));
284	if (newVector == NULL)
285		return B_NO_MEMORY;
286
287	*fVector = (TLSBlock*)newVector;
288	memset(*fVector + oldSize + 1, 0, (size - oldSize) * sizeof(TLSBlock));
289	if (fGeneration == NULL) {
290		fGeneration = new Generation;
291		if (fGeneration == NULL)
292			return B_NO_MEMORY;
293	}
294
295	*(Generation**)*fVector = fGeneration;
296	fGeneration->SetSize(size);
297
298	return B_OK;
299}
300
301
302void*
303get_tls_address(unsigned dso, addr_t offset)
304{
305	DynamicThreadVector dynamicThreadVector;
306	TLSBlock& block = dynamicThreadVector[dso];
307	if (block.IsInvalid())
308		return NULL;
309	return block + offset;
310}
311
312
313void
314destroy_thread_tls()
315{
316	DynamicThreadVector().DestroyAll();
317}
318
319