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