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