1/*
2 * Copyright 2003-2007, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6/*! User Runtime Loader support in the kernel */
7
8
9#include <KernelExport.h>
10
11#include <kernel.h>
12#include <kimage.h>
13#include <kscheduler.h>
14#include <lock.h>
15#include <Notifications.h>
16#include <team.h>
17#include <thread.h>
18#include <thread_types.h>
19#include <user_debugger.h>
20#include <util/AutoLock.h>
21#include <util/ThreadAutoLock.h>
22
23#include <stdlib.h>
24#include <string.h>
25
26
27//#define TRACE_IMAGE
28#ifdef TRACE_IMAGE
29#	define TRACE(x) dprintf x
30#else
31#	define TRACE(x) ;
32#endif
33
34#define ADD_DEBUGGER_COMMANDS
35
36
37namespace {
38
39struct ImageTableDefinition {
40	typedef image_id		KeyType;
41	typedef struct image	ValueType;
42
43	size_t HashKey(image_id key) const { return key; }
44	size_t Hash(struct image* value) const { return value->info.basic_info.id; }
45	bool Compare(image_id key, struct image* value) const
46		{ return value->info.basic_info.id == key; }
47	struct image*& GetLink(struct image* value) const
48		{ return value->hash_link; }
49};
50
51typedef BOpenHashTable<ImageTableDefinition> ImageTable;
52
53
54class ImageNotificationService : public DefaultNotificationService {
55public:
56	ImageNotificationService()
57		: DefaultNotificationService("images")
58	{
59	}
60
61	void Notify(uint32 eventCode, struct image* image)
62	{
63		char eventBuffer[128];
64		KMessage event;
65		event.SetTo(eventBuffer, sizeof(eventBuffer), IMAGE_MONITOR);
66		event.AddInt32("event", eventCode);
67		event.AddInt32("image", image->info.basic_info.id);
68		event.AddPointer("imageStruct", image);
69
70		DefaultNotificationService::Notify(event, eventCode);
71	}
72};
73
74} // namespace
75
76
77static image_id sNextImageID = 1;
78static mutex sImageMutex = MUTEX_INITIALIZER("image");
79static ImageTable* sImageTable;
80static ImageNotificationService sNotificationService;
81
82
83/*!	Registers an image with the specified team.
84*/
85static image_id
86register_image(Team *team, extended_image_info *info, size_t size, bool locked)
87{
88	image_id id = atomic_add(&sNextImageID, 1);
89	struct image *image;
90
91	image = (struct image*)malloc(sizeof(struct image));
92	if (image == NULL)
93		return B_NO_MEMORY;
94
95	memcpy(&image->info, info, sizeof(extended_image_info));
96	image->team = team->id;
97
98	if (!locked)
99		mutex_lock(&sImageMutex);
100
101	image->info.basic_info.id = id;
102
103	// Add the app image to the head of the list. Some code relies on it being
104	// the first image to be returned by get_next_image_info().
105	if (image->info.basic_info.type == B_APP_IMAGE)
106		list_add_link_to_head(&team->image_list, image);
107	else
108		list_add_item(&team->image_list, image);
109	sImageTable->Insert(image);
110
111	// notify listeners
112	sNotificationService.Notify(IMAGE_ADDED, image);
113
114	if (!locked)
115		mutex_unlock(&sImageMutex);
116
117	TRACE(("register_image(team = %p, image id = %ld, image = %p\n", team, id, image));
118	return id;
119}
120
121
122/*!	Registers an image with the specified team.
123*/
124image_id
125register_image(Team *team, extended_image_info *info, size_t size)
126{
127	return register_image(team, info, size, false);
128}
129
130
131/*!	Unregisters an image from the specified team.
132*/
133status_t
134unregister_image(Team *team, image_id id)
135{
136	status_t status = B_ENTRY_NOT_FOUND;
137
138	mutex_lock(&sImageMutex);
139
140	struct image *image = sImageTable->Lookup(id);
141	if (image != NULL && image->team == team->id) {
142		list_remove_link(image);
143		sImageTable->Remove(image);
144		status = B_OK;
145	}
146
147	mutex_unlock(&sImageMutex);
148
149	if (status == B_OK) {
150		// notify the debugger
151		user_debug_image_deleted(&image->info.basic_info);
152
153		// notify listeners
154		sNotificationService.Notify(IMAGE_REMOVED, image);
155
156		free(image);
157	}
158
159	return status;
160}
161
162
163status_t
164copy_images(team_id fromTeamId, Team *toTeam)
165{
166	// get the team
167	Team* fromTeam = Team::Get(fromTeamId);
168	if (fromTeam == NULL)
169		return B_BAD_TEAM_ID;
170	BReference<Team> teamReference(fromTeam, true);
171
172	MutexLocker locker(sImageMutex);
173
174	struct image *image = NULL;
175	while ((image = (struct image*)list_get_next_item(&fromTeam->image_list,
176			image)) != NULL) {
177		image_id id = register_image(toTeam, &image->info, sizeof(image->info),
178			true);
179		if (id < 0)
180			return id;
181	}
182
183	return B_OK;
184}
185
186
187/*!	Counts the registered images from the specified team.
188	Interrupts must be enabled.
189*/
190int32
191count_images(Team *team)
192{
193	struct image *image = NULL;
194	int32 count = 0;
195
196	MutexLocker locker(sImageMutex);
197
198	while ((image = (struct image*)list_get_next_item(&team->image_list, image))
199			!= NULL) {
200		count++;
201	}
202
203	return count;
204}
205
206
207/*!	Removes all images from the specified team. Must only be called
208	with a team that has already been removed from the list (in thread_exit()).
209*/
210status_t
211remove_images(Team *team)
212{
213	struct image *image;
214
215	ASSERT(team != NULL);
216
217	mutex_lock(&sImageMutex);
218
219	while ((image = (struct image*)list_remove_head_item(&team->image_list))
220			!= NULL) {
221		sImageTable->Remove(image);
222		free(image);
223	}
224
225	mutex_unlock(&sImageMutex);
226
227	return B_OK;
228}
229
230
231status_t
232_get_image_info(image_id id, image_info *info, size_t size)
233{
234	if (size > sizeof(image_info))
235		return B_BAD_VALUE;
236
237	status_t status = B_ENTRY_NOT_FOUND;
238
239	mutex_lock(&sImageMutex);
240
241	struct image *image = sImageTable->Lookup(id);
242	if (image != NULL) {
243		memcpy(info, &image->info.basic_info, size);
244		status = B_OK;
245	}
246
247	mutex_unlock(&sImageMutex);
248
249	return status;
250}
251
252
253status_t
254_get_next_image_info(team_id teamID, int32 *cookie, image_info *info,
255	size_t size)
256{
257	if (size > sizeof(image_info))
258		return B_BAD_VALUE;
259
260	// get the team
261	Team* team = Team::Get(teamID);
262	if (team == NULL)
263		return B_BAD_TEAM_ID;
264	BReference<Team> teamReference(team, true);
265
266	// iterate through the team's images
267	MutexLocker imageLocker(sImageMutex);
268
269	struct image* image = NULL;
270	int32 count = 0;
271
272	while ((image = (struct image*)list_get_next_item(&team->image_list,
273			image)) != NULL) {
274		if (count == *cookie) {
275			memcpy(info, &image->info.basic_info, size);
276			(*cookie)++;
277			return B_OK;
278		}
279		count++;
280	}
281
282	return B_ENTRY_NOT_FOUND;
283}
284
285
286#ifdef ADD_DEBUGGER_COMMANDS
287static int
288dump_images_list(int argc, char **argv)
289{
290	struct image *image = NULL;
291	Team *team;
292
293	if (argc > 1) {
294		team_id id = strtol(argv[1], NULL, 0);
295		team = team_get_team_struct_locked(id);
296		if (team == NULL) {
297			kprintf("No team with ID %" B_PRId32 " found\n", id);
298			return 1;
299		}
300	} else
301		team = thread_get_current_thread()->team;
302
303	kprintf("Registered images of team %" B_PRId32 "\n", team->id);
304	kprintf("    ID %-*s   size    %-*s   size    name\n",
305		B_PRINTF_POINTER_WIDTH, "text", B_PRINTF_POINTER_WIDTH, "data");
306
307	while ((image = (struct image*)list_get_next_item(&team->image_list, image))
308			!= NULL) {
309		image_info *info = &image->info.basic_info;
310
311		kprintf("%6" B_PRId32 " %p %-7" B_PRId32 " %p %-7" B_PRId32 " %s\n",
312			info->id, info->text, info->text_size, info->data, info->data_size,
313			info->name);
314	}
315
316	return 0;
317}
318#endif
319
320
321struct image*
322image_iterate_through_images(image_iterator_callback callback, void* cookie)
323{
324	MutexLocker locker(sImageMutex);
325
326	ImageTable::Iterator it = sImageTable->GetIterator();
327	struct image* image = NULL;
328	while ((image = it.Next()) != NULL) {
329		if (callback(image, cookie))
330			break;
331	}
332
333	return image;
334}
335
336
337struct image*
338image_iterate_through_team_images(team_id teamID,
339	image_iterator_callback callback, void* cookie)
340{
341	// get the team
342	Team* team = Team::Get(teamID);
343	if (team == NULL)
344		return NULL;
345	BReference<Team> teamReference(team, true);
346
347	// iterate through the team's images
348	MutexLocker imageLocker(sImageMutex);
349
350	struct image* image = NULL;
351
352	while ((image = (struct image*)list_get_next_item(&team->image_list,
353			image)) != NULL) {
354		if (callback(image, cookie))
355			break;
356	}
357
358	return image;
359}
360
361
362status_t
363image_debug_lookup_user_symbol_address(Team *team, addr_t address,
364	addr_t *_baseAddress, const char **_symbolName, const char **_imageName,
365	bool *_exactMatch)
366{
367	// TODO: Work together with ELF reader and runtime_loader. For regular user
368	// images we have the symbol and string table addresses.
369
370	struct image *image = NULL;
371
372	while ((image = (struct image*)list_get_next_item(&team->image_list, image))
373			!= NULL) {
374		image_info *info = &image->info.basic_info;
375
376		if ((address < (addr_t)info->text
377				|| address >= (addr_t)info->text + info->text_size)
378			&& (address < (addr_t)info->data
379				|| address >= (addr_t)info->data + info->data_size))
380			continue;
381
382		// found image
383		*_symbolName = NULL;
384		*_imageName = info->name;
385		*_baseAddress = (addr_t)info->text;
386		*_exactMatch = false;
387
388		return B_OK;
389	}
390
391	return B_ENTRY_NOT_FOUND;
392}
393
394
395status_t
396image_init(void)
397{
398	sImageTable = new(std::nothrow) ImageTable;
399	if (sImageTable == NULL) {
400		panic("image_init(): Failed to allocate image table!");
401		return B_NO_MEMORY;
402	}
403
404	status_t error = sImageTable->Init();
405	if (error != B_OK) {
406		panic("image_init(): Failed to init image table: %s", strerror(error));
407		return error;
408	}
409
410	new(&sNotificationService) ImageNotificationService();
411
412	sNotificationService.Register();
413
414#ifdef ADD_DEBUGGER_COMMANDS
415	add_debugger_command("team_images", &dump_images_list, "Dump all registered images from the current team");
416#endif
417
418	return B_OK;
419}
420
421
422static void
423notify_loading_app(status_t result, bool suspend)
424{
425	Team* team = thread_get_current_thread()->team;
426
427	TeamLocker teamLocker(team);
428
429	if (team->loading_info != NULL) {
430		// there's indeed someone waiting
431
432		thread_prepare_suspend();
433
434		// wake up the waiting thread
435		team->loading_info->result = result;
436		team->loading_info->condition.NotifyAll();
437		team->loading_info = NULL;
438
439		// we're done with the team stuff
440		teamLocker.Unlock();
441
442		// suspend ourselves, if desired
443		if (suspend)
444			thread_suspend(true);
445	}
446}
447
448
449//	#pragma mark -
450//	Functions exported for the user space
451
452
453status_t
454_user_unregister_image(image_id id)
455{
456	return unregister_image(thread_get_current_thread()->team, id);
457}
458
459
460image_id
461_user_register_image(extended_image_info *userInfo, size_t size)
462{
463	extended_image_info info;
464
465	if (size != sizeof(info))
466		return B_BAD_VALUE;
467
468	if (!IS_USER_ADDRESS(userInfo)
469		|| user_memcpy(&info, userInfo, size) < B_OK)
470		return B_BAD_ADDRESS;
471
472	return register_image(thread_get_current_thread()->team, &info, size);
473}
474
475
476void
477_user_image_relocated(image_id id)
478{
479	image_info info;
480	status_t error;
481
482	// get an image info
483	error = _get_image_info(id, &info, sizeof(image_info));
484	if (error != B_OK) {
485		dprintf("_user_image_relocated(%" B_PRId32 "): Failed to get image "
486			"info: %" B_PRIx32 "\n", id, error);
487		return;
488	}
489
490	// notify the debugger
491	user_debug_image_created(&info);
492
493	// If the image is the app image, loading is done. We need to notify the
494	// thread who initiated the process and is now waiting for us to be done.
495	if (info.type == B_APP_IMAGE)
496		notify_loading_app(B_OK, true);
497}
498
499
500void
501_user_loading_app_failed(status_t error)
502{
503	if (error >= B_OK)
504		error = B_ERROR;
505
506	notify_loading_app(error, false);
507
508	_user_exit_team(error);
509}
510
511
512status_t
513_user_get_image_info(image_id id, image_info *userInfo, size_t size)
514{
515	image_info info;
516	status_t status;
517
518	if (size > sizeof(image_info))
519		return B_BAD_VALUE;
520
521	if (!IS_USER_ADDRESS(userInfo))
522		return B_BAD_ADDRESS;
523
524	status = _get_image_info(id, &info, sizeof(image_info));
525
526	if (user_memcpy(userInfo, &info, size) < B_OK)
527		return B_BAD_ADDRESS;
528
529	return status;
530}
531
532
533status_t
534_user_get_next_image_info(team_id team, int32 *_cookie, image_info *userInfo,
535	size_t size)
536{
537	image_info info;
538	status_t status;
539	int32 cookie;
540
541	if (size > sizeof(image_info))
542		return B_BAD_VALUE;
543
544	if (!IS_USER_ADDRESS(userInfo) || !IS_USER_ADDRESS(_cookie)
545		|| user_memcpy(&cookie, _cookie, sizeof(int32)) < B_OK) {
546		return B_BAD_ADDRESS;
547	}
548
549	status = _get_next_image_info(team, &cookie, &info, sizeof(image_info));
550
551	if (user_memcpy(userInfo, &info, size) < B_OK
552		|| user_memcpy(_cookie, &cookie, sizeof(int32)) < B_OK) {
553		return B_BAD_ADDRESS;
554	}
555
556	return status;
557}
558
559