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