1/*
2 * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2002-2009, Axel D��rfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 *
6 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
7 * Distributed under the terms of the NewOS License.
8 */
9
10
11#include <vm/VMAddressSpace.h>
12
13#include <stdlib.h>
14
15#include <new>
16
17#include <KernelExport.h>
18
19#include <util/OpenHashTable.h>
20
21#include <heap.h>
22#include <thread.h>
23#include <vm/vm.h>
24#include <vm/VMArea.h>
25#include <vm/VMCache.h>
26
27#include "VMKernelAddressSpace.h"
28#include "VMUserAddressSpace.h"
29
30
31//#define TRACE_VM
32#ifdef TRACE_VM
33#	define TRACE(x) dprintf x
34#else
35#	define TRACE(x) ;
36#endif
37
38
39#define ASPACE_HASH_TABLE_SIZE 1024
40
41
42// #pragma mark - AddressSpaceHashDefinition
43
44
45namespace {
46
47struct AddressSpaceHashDefinition {
48	typedef team_id			KeyType;
49	typedef VMAddressSpace	ValueType;
50
51	size_t HashKey(team_id key) const
52	{
53		return key;
54	}
55
56	size_t Hash(const VMAddressSpace* value) const
57	{
58		return HashKey(value->ID());
59	}
60
61	bool Compare(team_id key, const VMAddressSpace* value) const
62	{
63		return value->ID() == key;
64	}
65
66	VMAddressSpace*& GetLink(VMAddressSpace* value) const
67	{
68		return value->HashTableLink();
69	}
70};
71
72typedef BOpenHashTable<AddressSpaceHashDefinition> AddressSpaceTable;
73
74} // namespace
75
76
77static AddressSpaceTable	sAddressSpaceTable;
78static rw_lock				sAddressSpaceTableLock;
79
80VMAddressSpace* VMAddressSpace::sKernelAddressSpace;
81
82
83// #pragma mark - VMAddressSpace
84
85
86VMAddressSpace::VMAddressSpace(team_id id, addr_t base, size_t size,
87	const char* name)
88	:
89	fBase(base),
90	fEndAddress(base + (size - 1)),
91	fFreeSpace(size),
92	fID(id),
93	fRefCount(1),
94	fFaultCount(0),
95	fChangeCount(0),
96	fTranslationMap(NULL),
97	fRandomizingEnabled(true),
98	fDeleting(false)
99{
100	rw_lock_init(&fLock, name);
101//	rw_lock_init(&fLock, kernel ? "kernel address space" : "address space");
102}
103
104
105VMAddressSpace::~VMAddressSpace()
106{
107	TRACE(("VMAddressSpace::~VMAddressSpace: called on aspace %" B_PRId32 "\n",
108		ID()));
109
110	WriteLock();
111
112	delete fTranslationMap;
113
114	rw_lock_destroy(&fLock);
115}
116
117
118/*static*/ status_t
119VMAddressSpace::Init()
120{
121	rw_lock_init(&sAddressSpaceTableLock, "address spaces table");
122
123	// create the area and address space hash tables
124	{
125		new(&sAddressSpaceTable) AddressSpaceTable;
126		status_t error = sAddressSpaceTable.Init(ASPACE_HASH_TABLE_SIZE);
127		if (error != B_OK)
128			panic("vm_init: error creating aspace hash table\n");
129	}
130
131	// create the initial kernel address space
132	if (Create(B_SYSTEM_TEAM, KERNEL_BASE, KERNEL_SIZE, true,
133			&sKernelAddressSpace) != B_OK) {
134		panic("vm_init: error creating kernel address space!\n");
135	}
136
137	add_debugger_command("aspaces", &_DumpListCommand,
138		"Dump a list of all address spaces");
139	add_debugger_command("aspace", &_DumpCommand,
140		"Dump info about a particular address space");
141
142	return B_OK;
143}
144
145
146/*! Deletes all areas in the specified address space, and the address
147	space by decreasing all reference counters. It also marks the
148	address space of being in deletion state, so that no more areas
149	can be created in it.
150	After this, the address space is not operational anymore, but might
151	still be in memory until the last reference has been released.
152*/
153void
154VMAddressSpace::RemoveAndPut()
155{
156	WriteLock();
157	fDeleting = true;
158	WriteUnlock();
159
160	vm_delete_areas(this, true);
161	Put();
162}
163
164
165status_t
166VMAddressSpace::InitObject()
167{
168	return B_OK;
169}
170
171
172void
173VMAddressSpace::Dump() const
174{
175	kprintf("dump of address space at %p:\n", this);
176	kprintf("id: %" B_PRId32 "\n", fID);
177	kprintf("ref_count: %" B_PRId32 "\n", fRefCount);
178	kprintf("fault_count: %" B_PRId32 "\n", fFaultCount);
179	kprintf("translation_map: %p\n", fTranslationMap);
180	kprintf("base: %#" B_PRIxADDR "\n", fBase);
181	kprintf("end: %#" B_PRIxADDR "\n", fEndAddress);
182	kprintf("change_count: %" B_PRId32 "\n", fChangeCount);
183}
184
185
186/*static*/ status_t
187VMAddressSpace::Create(team_id teamID, addr_t base, size_t size, bool kernel,
188	VMAddressSpace** _addressSpace)
189{
190	VMAddressSpace* addressSpace = kernel
191		? (VMAddressSpace*)new(std::nothrow) VMKernelAddressSpace(teamID, base,
192			size)
193		: (VMAddressSpace*)new(std::nothrow) VMUserAddressSpace(teamID, base,
194			size);
195	if (addressSpace == NULL)
196		return B_NO_MEMORY;
197
198	status_t status = addressSpace->InitObject();
199	if (status != B_OK) {
200		delete addressSpace;
201		return status;
202	}
203
204	TRACE(("VMAddressSpace::Create(): team %" B_PRId32 " (%skernel): %#lx "
205		"bytes starting at %#lx => %p\n", teamID, kernel ? "" : "!", size,
206		base, addressSpace));
207
208	// create the corresponding translation map
209	status = arch_vm_translation_map_create_map(kernel,
210		&addressSpace->fTranslationMap);
211	if (status != B_OK) {
212		delete addressSpace;
213		return status;
214	}
215
216	// add the aspace to the global hash table
217	rw_lock_write_lock(&sAddressSpaceTableLock);
218	sAddressSpaceTable.InsertUnchecked(addressSpace);
219	rw_lock_write_unlock(&sAddressSpaceTableLock);
220
221	*_addressSpace = addressSpace;
222	return B_OK;
223}
224
225
226/*static*/ VMAddressSpace*
227VMAddressSpace::GetKernel()
228{
229	// we can treat this one a little differently since it can't be deleted
230	sKernelAddressSpace->Get();
231	return sKernelAddressSpace;
232}
233
234
235/*static*/ team_id
236VMAddressSpace::CurrentID()
237{
238	Thread* thread = thread_get_current_thread();
239
240	if (thread != NULL && thread->team->address_space != NULL)
241		return thread->team->id;
242
243	return B_ERROR;
244}
245
246
247/*static*/ VMAddressSpace*
248VMAddressSpace::GetCurrent()
249{
250	Thread* thread = thread_get_current_thread();
251
252	if (thread != NULL) {
253		VMAddressSpace* addressSpace = thread->team->address_space;
254		if (addressSpace != NULL) {
255			addressSpace->Get();
256			return addressSpace;
257		}
258	}
259
260	return NULL;
261}
262
263
264/*static*/ VMAddressSpace*
265VMAddressSpace::Get(team_id teamID)
266{
267	rw_lock_read_lock(&sAddressSpaceTableLock);
268	VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(teamID);
269	if (addressSpace)
270		addressSpace->Get();
271	rw_lock_read_unlock(&sAddressSpaceTableLock);
272
273	return addressSpace;
274}
275
276
277/*static*/ VMAddressSpace*
278VMAddressSpace::DebugFirst()
279{
280	return sAddressSpaceTable.GetIterator().Next();
281}
282
283
284/*static*/ VMAddressSpace*
285VMAddressSpace::DebugNext(VMAddressSpace* addressSpace)
286{
287	if (addressSpace == NULL)
288		return NULL;
289
290	AddressSpaceTable::Iterator it
291		= sAddressSpaceTable.GetIterator(addressSpace->ID());
292	it.Next();
293	return it.Next();
294}
295
296
297/*static*/ VMAddressSpace*
298VMAddressSpace::DebugGet(team_id teamID)
299{
300	return sAddressSpaceTable.Lookup(teamID);
301}
302
303
304/*static*/ void
305VMAddressSpace::_DeleteIfUnreferenced(team_id id)
306{
307	rw_lock_write_lock(&sAddressSpaceTableLock);
308
309	bool remove = false;
310	VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(id);
311	if (addressSpace != NULL && addressSpace->fRefCount == 0) {
312		sAddressSpaceTable.RemoveUnchecked(addressSpace);
313		remove = true;
314	}
315
316	rw_lock_write_unlock(&sAddressSpaceTableLock);
317
318	if (remove)
319		delete addressSpace;
320}
321
322
323/*static*/ int
324VMAddressSpace::_DumpCommand(int argc, char** argv)
325{
326	VMAddressSpace* aspace;
327
328	if (argc < 2) {
329		kprintf("aspace: not enough arguments\n");
330		return 0;
331	}
332
333	// if the argument looks like a number, treat it as such
334
335	{
336		team_id id = strtoul(argv[1], NULL, 0);
337
338		aspace = sAddressSpaceTable.Lookup(id);
339		if (aspace == NULL) {
340			kprintf("invalid aspace id\n");
341		} else {
342			aspace->Dump();
343		}
344	}
345
346	return 0;
347}
348
349
350/*static*/ int
351VMAddressSpace::_DumpListCommand(int argc, char** argv)
352{
353	kprintf("  %*s      id     %*s     %*s   area count    area size\n",
354		B_PRINTF_POINTER_WIDTH, "address", B_PRINTF_POINTER_WIDTH, "base",
355		B_PRINTF_POINTER_WIDTH, "end");
356
357	AddressSpaceTable::Iterator it = sAddressSpaceTable.GetIterator();
358	while (VMAddressSpace* space = it.Next()) {
359		int32 areaCount = 0;
360		off_t areaSize = 0;
361		for (VMAddressSpace::AreaIterator areaIt = space->GetAreaIterator();
362				VMArea* area = areaIt.Next();) {
363			areaCount++;
364			areaSize += area->Size();
365		}
366		kprintf("%p  %6" B_PRId32 "   %#010" B_PRIxADDR "   %#10" B_PRIxADDR
367			"   %10" B_PRId32 "   %10" B_PRIdOFF "\n", space, space->ID(),
368			space->Base(), space->EndAddress(), areaCount, areaSize);
369	}
370
371	return 0;
372}
373