* Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <KernelExport.h>
#include <kernel.h>
#include <kimage.h>
#include <kscheduler.h>
#include <lock.h>
#include <Notifications.h>
#include <team.h>
#include <thread.h>
#include <thread_types.h>
#include <user_debugger.h>
#include <util/AutoLock.h>
#include <util/ThreadAutoLock.h>
#include <stdlib.h>
#include <string.h>
#ifdef TRACE_IMAGE
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#define ADD_DEBUGGER_COMMANDS
namespace {
struct ImageTableDefinition {
typedef image_id KeyType;
typedef struct image ValueType;
size_t HashKey(image_id key) const { return key; }
size_t Hash(struct image* value) const { return value->info.basic_info.id; }
bool Compare(image_id key, struct image* value) const
{ return value->info.basic_info.id == key; }
struct image*& GetLink(struct image* value) const
{ return value->hash_link; }
};
typedef BOpenHashTable<ImageTableDefinition> ImageTable;
class ImageNotificationService : public DefaultNotificationService {
public:
ImageNotificationService()
: DefaultNotificationService("images")
{
}
void Notify(uint32 eventCode, struct image* image)
{
char eventBuffer[128];
KMessage event;
event.SetTo(eventBuffer, sizeof(eventBuffer), IMAGE_MONITOR);
event.AddInt32("event", eventCode);
event.AddInt32("image", image->info.basic_info.id);
event.AddPointer("imageStruct", image);
DefaultNotificationService::Notify(event, eventCode);
}
};
}
static image_id sNextImageID = 1;
static mutex sImageMutex = MUTEX_INITIALIZER("image");
static ImageTable* sImageTable;
static ImageNotificationService sNotificationService;
*/
static image_id
register_image(Team *team, extended_image_info *info, size_t size, bool locked)
{
image_id id = atomic_add(&sNextImageID, 1);
struct image *image;
image = (struct image*)malloc(sizeof(struct image));
if (image == NULL)
return B_NO_MEMORY;
memcpy(&image->info, info, sizeof(extended_image_info));
image->team = team->id;
if (!locked)
mutex_lock(&sImageMutex);
image->info.basic_info.id = id;
if (image->info.basic_info.type == B_APP_IMAGE)
team->image_list.Add(image, false);
else
team->image_list.Add(image);
sImageTable->Insert(image);
sNotificationService.Notify(IMAGE_ADDED, image);
if (!locked)
mutex_unlock(&sImageMutex);
TRACE(("register_image(team = %p, image id = %ld, image = %p\n", team, id, image));
return id;
}
*/
image_id
register_image(Team *team, extended_image_info *info, size_t size)
{
return register_image(team, info, size, false);
}
*/
status_t
unregister_image(Team *team, image_id id)
{
status_t status = B_ENTRY_NOT_FOUND;
mutex_lock(&sImageMutex);
struct image *image = sImageTable->Lookup(id);
if (image != NULL && image->team == team->id) {
team->image_list.Remove(image);
sImageTable->Remove(image);
status = B_OK;
}
mutex_unlock(&sImageMutex);
if (status == B_OK) {
user_debug_image_deleted(&image->info.basic_info);
sNotificationService.Notify(IMAGE_REMOVED, image);
free(image);
}
return status;
}
status_t
copy_images(team_id fromTeamId, Team *toTeam)
{
Team* fromTeam = Team::Get(fromTeamId);
if (fromTeam == NULL)
return B_BAD_TEAM_ID;
BReference<Team> teamReference(fromTeam, true);
MutexLocker locker(sImageMutex);
for (struct image* image = fromTeam->image_list.First();
image != NULL; image = fromTeam->image_list.GetNext(image)) {
image_id id = register_image(toTeam, &image->info, sizeof(image->info),
true);
if (id < 0)
return id;
}
return B_OK;
}
Interrupts must be enabled.
*/
int32
count_images(Team *team)
{
MutexLocker locker(sImageMutex);
int32 count = 0;
for (struct image* image = team->image_list.First();
image != NULL; image = team->image_list.GetNext(image)) {
count++;
}
return count;
}
with a team that has already been removed from the list (in thread_exit()).
*/
status_t
remove_images(Team *team)
{
ASSERT(team != NULL);
mutex_lock(&sImageMutex);
DoublyLinkedList<struct image> images;
images.TakeFrom(&team->image_list);
for (struct image* image = images.First();
image != NULL; image = images.GetNext(image)) {
sImageTable->Remove(image);
}
mutex_unlock(&sImageMutex);
while (struct image* image = images.RemoveHead())
free(image);
return B_OK;
}
status_t
_get_image_info(image_id id, image_info *info, size_t size)
{
if (size > sizeof(image_info))
return B_BAD_VALUE;
status_t status = B_ENTRY_NOT_FOUND;
mutex_lock(&sImageMutex);
struct image *image = sImageTable->Lookup(id);
if (image != NULL) {
memcpy(info, &image->info.basic_info, size);
status = B_OK;
}
mutex_unlock(&sImageMutex);
return status;
}
status_t
_get_next_image_info(team_id teamID, int32 *cookie, image_info *info,
size_t size)
{
if (size > sizeof(image_info))
return B_BAD_VALUE;
Team* team = Team::Get(teamID);
if (team == NULL)
return B_BAD_TEAM_ID;
BReference<Team> teamReference(team, true);
MutexLocker imageLocker(sImageMutex);
int32 count = 0;
for (struct image* image = team->image_list.First();
image != NULL; image = team->image_list.GetNext(image)) {
if (count == *cookie) {
memcpy(info, &image->info.basic_info, size);
(*cookie)++;
return B_OK;
}
count++;
}
return B_ENTRY_NOT_FOUND;
}
#ifdef ADD_DEBUGGER_COMMANDS
static int
dump_images_list(int argc, char **argv)
{
Team *team;
if (argc > 1) {
team_id id = strtol(argv[1], NULL, 0);
team = team_get_team_struct_locked(id);
if (team == NULL) {
kprintf("No team with ID %" B_PRId32 " found\n", id);
return 1;
}
} else
team = thread_get_current_thread()->team;
kprintf("Registered images of team %" B_PRId32 "\n", team->id);
kprintf(" ID %-*s size %-*s size name\n",
B_PRINTF_POINTER_WIDTH, "text", B_PRINTF_POINTER_WIDTH, "data");
for (struct image* image = team->image_list.First();
image != NULL; image = team->image_list.GetNext(image)) {
image_info *info = &image->info.basic_info;
kprintf("%6" B_PRId32 " %p %-7" B_PRId32 " %p %-7" B_PRId32 " %s\n",
info->id, info->text, info->text_size, info->data, info->data_size,
info->name);
}
return 0;
}
#endif
struct image*
image_iterate_through_images(image_iterator_callback callback, void* cookie)
{
MutexLocker locker(sImageMutex);
ImageTable::Iterator it = sImageTable->GetIterator();
struct image* image = NULL;
while ((image = it.Next()) != NULL) {
if (callback(image, cookie))
break;
}
return image;
}
struct image*
image_iterate_through_team_images(team_id teamID,
image_iterator_callback callback, void* cookie)
{
Team* team = Team::Get(teamID);
if (team == NULL)
return NULL;
BReference<Team> teamReference(team, true);
MutexLocker imageLocker(sImageMutex);
struct image *image = NULL;
for (image = team->image_list.First();
image != NULL; image = team->image_list.GetNext(image)) {
if (callback(image, cookie))
break;
}
return image;
}
status_t
image_init(void)
{
sImageTable = new(std::nothrow) ImageTable;
if (sImageTable == NULL) {
panic("image_init(): Failed to allocate image table!");
return B_NO_MEMORY;
}
status_t error = sImageTable->Init();
if (error != B_OK) {
panic("image_init(): Failed to init image table: %s", strerror(error));
return error;
}
new(&sNotificationService) ImageNotificationService();
sNotificationService.Register();
#ifdef ADD_DEBUGGER_COMMANDS
add_debugger_command("team_images", &dump_images_list, "Dump all registered images from the current team");
#endif
return B_OK;
}
static void
notify_loading_app(status_t result, bool suspend)
{
Team* team = thread_get_current_thread()->team;
TeamLocker teamLocker(team);
if (team->loading_info != NULL) {
thread_prepare_suspend();
team->loading_info->result = result;
team->loading_info->condition.NotifyAll();
team->loading_info = NULL;
teamLocker.Unlock();
if (suspend)
thread_suspend(true);
}
}
status_t
_user_unregister_image(image_id id)
{
return unregister_image(thread_get_current_thread()->team, id);
}
image_id
_user_register_image(extended_image_info *userInfo, size_t size)
{
extended_image_info info;
if (size != sizeof(info))
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userInfo)
|| user_memcpy(&info, userInfo, size) < B_OK)
return B_BAD_ADDRESS;
return register_image(thread_get_current_thread()->team, &info, size);
}
void
_user_image_relocated(image_id id)
{
image_info info;
status_t error;
error = _get_image_info(id, &info, sizeof(image_info));
if (error != B_OK) {
dprintf("_user_image_relocated(%" B_PRId32 "): Failed to get image "
"info: %" B_PRIx32 "\n", id, error);
return;
}
user_debug_image_created(&info);
if (info.type == B_APP_IMAGE)
notify_loading_app(B_OK, true);
}
void
_user_loading_app_failed(status_t error)
{
if (error >= B_OK)
error = B_ERROR;
notify_loading_app(error, false);
_user_exit_team(error);
}
status_t
_user_get_image_info(image_id id, image_info *userInfo, size_t size)
{
image_info info;
status_t status;
if (size > sizeof(image_info))
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userInfo))
return B_BAD_ADDRESS;
status = _get_image_info(id, &info, sizeof(image_info));
if (user_memcpy(userInfo, &info, size) < B_OK)
return B_BAD_ADDRESS;
return status;
}
status_t
_user_get_next_image_info(team_id team, int32 *_cookie, image_info *userInfo,
size_t size)
{
image_info info;
status_t status;
int32 cookie;
if (size > sizeof(image_info))
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userInfo) || !IS_USER_ADDRESS(_cookie)
|| user_memcpy(&cookie, _cookie, sizeof(int32)) < B_OK) {
return B_BAD_ADDRESS;
}
status = _get_next_image_info(team, &cookie, &info, sizeof(image_info));
if (user_memcpy(userInfo, &info, size) < B_OK
|| user_memcpy(_cookie, &cookie, sizeof(int32)) < B_OK) {
return B_BAD_ADDRESS;
}
return status;
}