1 /* 2 * Copyright 2016, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(D61_BSD) 11 */ 12 13#include <sel4utils/vspace.h> 14 15#include "window.h" 16#include "../../badge.h" 17#include "../../state.h" 18 19#include "../addrspace/vspace.h" 20#include "../process/pid.h" 21#include "../process/process.h" 22 23/*! @file 24 @brief Manages and keeps track of memory windows. */ 25 26#define W_INITIAL_SIZE 4 27 28/*! @brief Internal helper function to switch a window between modes. 29 30 It will first release all the previous stored mode related objects, and if the window wasn't 31 empty to start with, unmap the vspace range (so new window data isn't munged with old mapped 32 pages). Then it sets the mode variable of the window. This means that using this helper function 33 to set the mode to W_MODE_EMPTY will effectively delete all the mode-related objects and unmap 34 old stuff in the window. 35 36 @param window The window to switch mode for. (No ownership) 37 @param mode The new mode to switch to. 38*/ 39static void 40window_switch_mode(struct w_window* window, enum w_window_mode mode) 41{ 42 /* Clean up all windoe mode states. */ 43 assert(window && window->magic == W_MAGIC); 44 45 /* Free the pager capability. */ 46 if (window->pager.capPtr) { 47 vka_cnode_revoke(&window->pager); 48 vka_cnode_delete(&window->pager); 49 vka_cspace_free(&procServ.vka, window->pager.capPtr); 50 window->pager.capPtr = 0; 51 window->pagerPID = PID_NULL; 52 } 53 54 /* Unreference the associated dataspace. */ 55 if (window->ramDataspace) { 56 ram_dspace_unref(window->ramDataspace->parentList, window->ramDataspace->ID); 57 window->ramDataspace = NULL; 58 window->ramDataspaceOffset = (vaddr_t) 0; 59 } 60 61 if (window->mode != W_MODE_EMPTY) { 62 /* There was something previous here. So we must first unmap this window. */ 63 if (window->parentList == &procServ.windowList && window->clientOwnerPID != PID_NULL) { 64 /* Get owner client PCB to access its vspace. */ 65 struct proc_pcb* clientPCB = pid_get_pcb(&procServ.PIDList, 66 window->clientOwnerPID); 67 if (clientPCB) { 68 vs_unmap_window(&clientPCB->vspace, window->wID); 69 } 70 } 71 } 72 window->mode = mode; 73} 74 75/*! @brief Window OAT creation callback function. 76 77 This callback function is called by the OAT allocation helper library in <data_struct/coat.h>, 78 in order to create window objects. 79*/ 80static cvector_item_t 81window_oat_create(coat_t *oat, int id, uint32_t arg[COAT_ARGS]) 82{ 83 struct w_window *nw = kmalloc(sizeof(struct w_window)); 84 if (!nw) { 85 ROS_ERROR("window_oat_create out of memory!"); 86 return NULL; 87 } 88 memset(nw, 0, sizeof(struct w_window)); 89 nw->magic = W_MAGIC; 90 nw->wID = id; 91 nw->parentList = (struct w_list*) oat; 92 nw->pagerPID = PID_NULL; 93 assert(nw->parentList->magic == W_LIST_MAGIC); 94 95 /* Mint the badged capability representing this window. */ 96 nw->capability = procserv_mint_badge(W_BADGE_BASE + id); 97 if (!nw->capability.capPtr) { 98 ROS_ERROR("window_oat_create could not mint cap!"); 99 free(nw); 100 return NULL; 101 } 102 return (cvector_item_t) nw; 103} 104 105/*! @brief Window OAT deletion callback function. 106 107 This callback function is called by the OAT allocation helper library in <data_struct/coat.h>, 108 in order to delete window objects previously created by window_oat_create(). 109*/ 110static void 111window_oat_delete(coat_t *oat, cvector_item_t *obj) 112{ 113 struct w_window *window = (struct w_window *) obj; 114 assert(window); 115 assert(window->magic == W_MAGIC); 116 117 /* Clean up window mode state. */ 118 window_switch_mode(window, W_MODE_EMPTY); 119 120 /* Free the reservation. */ 121 if (window->reservation.res != NULL) { 122 assert(window->vspace); 123 vspace_free_reservation(window->vspace, window->reservation); 124 window->reservation.res = NULL; 125 } 126 127 /* Free the capability. */ 128 if (window->capability.capPtr) { 129 vka_cnode_revoke(&window->capability); 130 vka_cnode_delete(&window->capability); 131 vka_cspace_free(&procServ.vka, window->capability.capPtr); 132 } 133 134 /* Free the actual window structure. */ 135 memset(window, 0, sizeof(struct w_window)); 136 kfree(window); 137} 138 139/* --------------------------------------- Window functions ------------------------------------- */ 140 141void 142w_init(struct w_list *wlist) 143{ 144 assert(wlist); 145 dprintf("Initialising window allocation table (max %d windows).\n", W_MAX_WINDOWS); 146 147 /* Configure the object allocation table creation / deletion callback func pointers. */ 148 wlist->windows.oat_expand = NULL; 149 wlist->windows.oat_create = window_oat_create; 150 wlist->windows.oat_delete = window_oat_delete; 151 wlist->magic = W_LIST_MAGIC; 152 153 /* Initialise the allocation table. */ 154 coat_init(&wlist->windows, 1, W_MAX_WINDOWS); 155} 156 157void 158w_deinit(struct w_list *wlist) 159{ 160 assert(wlist); 161 coat_release(&wlist->windows); 162} 163 164struct w_window* 165w_create_window(struct w_list *wlist, vaddr_t size, int ownerPID, seL4_Word permissions, 166 vspace_t *vspace, reservation_t reservation, bool cacheable) 167{ 168 assert(wlist); 169 uint32_t arg[COAT_ARGS]; 170 struct w_window* w = NULL; 171 172 /* Allocate the window ID. */ 173 int ID = coat_alloc(&wlist->windows, arg, (cvector_item_t *) &w); 174 if (ID == W_INVALID_WINID) { 175 ROS_ERROR("Could not allocate window."); 176 return NULL; 177 } 178 179 /* Fill in the structure. */ 180 assert(w != NULL && w->magic == W_MAGIC); 181 w->clientOwnerPID = ownerPID; 182 w->size = size; 183 w->mode = W_MODE_EMPTY; 184 w->permissions = permissions; 185 w->vspace = vspace; 186 w->reservation = reservation; 187 w->cacheable = cacheable; 188 return w; 189} 190 191int 192w_delete_window(struct w_list *wlist, int windowID) 193{ 194 coat_free(&wlist->windows, windowID); 195 return ESUCCESS; 196} 197 198struct w_window* 199w_get_window(struct w_list *wlist, int windowID) 200{ 201 if (windowID <= W_INVALID_WINID || windowID >= W_MAX_WINDOWS) { 202 /* Invalid ID. */ 203 return NULL; 204 } 205 struct w_window* window = (struct w_window*) coat_get(&wlist->windows, windowID); 206 if (!window) { 207 /* No such window ID exists. */ 208 return NULL; 209 } 210 assert(window->magic == W_MAGIC); 211 return window; 212} 213 214void 215w_set_pager_endpoint(struct w_window *window, cspacepath_t endpoint, uint32_t pid) 216{ 217 assert(window && window->magic == W_MAGIC); 218 if (!endpoint.capPtr) { 219 window_switch_mode(window, W_MODE_EMPTY); 220 return; 221 } 222 window_switch_mode(window, W_MODE_PAGER); 223 window->pager = endpoint; 224 window->pagerPID = pid; 225} 226 227void 228w_set_anon_dspace(struct w_window *window, struct ram_dspace *dspace, vaddr_t offset) 229{ 230 assert(window && window->magic == W_MAGIC); 231 if (!dspace) { 232 window_switch_mode(window, W_MODE_EMPTY); 233 return; 234 } 235 assert(dspace->magic = RAM_DATASPACE_MAGIC); 236 window_switch_mode(window, W_MODE_ANONYMOUS); 237 window->ramDataspace = dspace; 238 window->ramDataspaceOffset = offset; 239 ram_dspace_ref(dspace->parentList, dspace->ID); 240} 241 242void 243w_purge_dspace(struct w_list *wlist, struct ram_dspace *dspace) 244{ 245 assert(wlist); 246 for (int i = 1; i < W_MAX_WINDOWS; i++) { 247 struct w_window *window = w_get_window(wlist, i); 248 249 if (window && window->ramDataspace) { 250 if (window->ramDataspace == dspace || (window->ramDataspace->parentList == 251 dspace->parentList && window->ramDataspace->ID == dspace->ID)) { 252 /* Set it back to empty. Not that this will unmap the window. */ 253 window_switch_mode(window, W_MODE_EMPTY); 254 } 255 } 256 } 257} 258 259int 260w_resize_window(struct w_window *window, vaddr_t vaddr, vaddr_t size) 261{ 262 assert(window && window->magic == W_MAGIC); 263 if (size == window->size) { 264 /* Nothing to do here. */ 265 return ESUCCESS; 266 } 267 268 int error = sel4utils_move_resize_reservation(window->vspace, window->reservation, 269 (void*) vaddr, size); 270 if (error) { 271 return EINVALIDWINDOW; 272 } 273 274 dvprintf("window ID %d resized from size 0x%x to 0x%x\n", window->wID, window->size, size); 275 window->size = size; 276 return ESUCCESS; 277} 278 279/* -------------------------------- Window Association functions -------------------------------- */ 280 281static int 282w_associate_compare(const void * a, const void * b) 283{ 284 return ( ((struct w_associated_window*) a)->offset < 285 ((struct w_associated_window*) b)->offset ? 286 -1 : 1 287 ); 288} 289 290/*! @brief Updates the window association list by sorting the base address of the window. 291 @param aw The window association list of a process. 292 */ 293static void 294w_associate_update(struct w_associated_windowlist *aw) 295{ 296 assert(aw); 297 qsort(aw->associated, aw->numIndex, sizeof(struct w_associated_window), w_associate_compare); 298 aw->updated = true; 299} 300 301static void 302w_associate_reserve(struct w_associated_windowlist *aw, int num) { 303 if (num < aw->associatedVectorSize) { 304 /* Nothing to do. */ 305 return; 306 } 307 if (aw->associatedVectorSize == 0 || aw->associated == NULL) { 308 aw->associatedVectorSize = num; 309 if (aw->associatedVectorSize < W_INITIAL_SIZE) { 310 aw->associatedVectorSize = W_INITIAL_SIZE; 311 } 312 aw->associated = kmalloc(sizeof(struct w_associated_window) * aw->associatedVectorSize); 313 assert(aw->associated); 314 return; 315 } 316 aw->associatedVectorSize = (aw->associatedVectorSize * 2) + 1; 317 aw->associated = krealloc(aw->associated, 318 sizeof(struct w_associated_window) * aw->associatedVectorSize); 319 assert(aw->associated); 320} 321 322void 323w_associate_init(struct w_associated_windowlist *aw) 324{ 325 aw->associated = NULL; 326 w_associate_clear(aw); 327} 328 329int 330w_associate(struct w_associated_windowlist *aw, int winID, vaddr_t offset, vaddr_t size) 331{ 332 assert(aw); 333 assert(winID != W_INVALID_WINID); 334 assert(winID > 0 && winID < W_MAX_WINDOWS); 335 if (aw->numIndex >= W_MAX_ASSOCIATED_WINDOWS) { 336 return ENOMEM; 337 } 338 w_associate_reserve(aw, aw->numIndex + 1 ); 339 aw->associated[aw->numIndex].winID = winID; 340 aw->associated[aw->numIndex].offset = offset; 341 aw->associated[aw->numIndex].size = size; 342 aw->numIndex++; 343 aw->updated = 0; 344 return ESUCCESS; 345} 346 347void 348w_associate_print(struct w_associated_windowlist *aw) 349{ 350 assert(aw); 351 for (int i = 0; i < aw->numIndex; i++) { 352 dprintf(" ��� Associated window %d: winID %d addr 0x%x ��������� 0x%x, size 0x%x\n", i, 353 aw->associated[i].winID, aw->associated[i].offset, 354 aw->associated[i].offset + aw->associated[i].size, 355 aw->associated[i].size); 356 } 357} 358 359void 360w_unassociate(struct w_associated_windowlist *aw, int winID) 361{ 362 assert(aw); 363 for (int i = 0; i < aw->numIndex; i++) { 364 if (aw->associated[i].winID == winID) { 365 aw->associated[i] = aw->associated[--aw->numIndex]; 366 } 367 } 368 /* Removing something from a sorted list doesn't make it unsorted. */ 369} 370 371void 372w_associate_clear(struct w_associated_windowlist *aw) 373{ 374 assert(aw); 375 aw->numIndex = 0; 376 if (aw->associated != NULL) { 377 kfree(aw->associated); 378 aw->associated = NULL; 379 } 380 aw->associatedVectorSize = 0; 381 /* An empty list is sorted. */ 382 aw->updated = true; 383 /* Reserve an initial few window spots. */ 384 w_associate_reserve(aw, W_INITIAL_SIZE); 385} 386 387void 388w_associate_release_associated_all_windows(struct w_list *wlist, struct w_associated_windowlist *aw) 389{ 390 assert(aw && wlist); 391 for (int i = 0; i < aw->numIndex; i++) { 392 /* Delete this window from the window list. */ 393 w_delete_window(wlist, aw->associated[i].winID); 394 } 395 w_associate_clear(aw); 396} 397 398static inline char 399w_associate_window_contains(struct w_associated_window *w, vaddr_t addr) 400{ 401 return (w->offset <= addr && 402 w->offset + w->size > addr); 403} 404 405static int 406w_associate_find_index(struct w_associated_windowlist *aw, vaddr_t addr) 407{ 408 assert(aw); 409 410 /* Re-sort the list if it's out of date. */ 411 if (!aw->updated) { 412 w_associate_update(aw); 413 assert(aw->updated); 414 } 415 416 /* Binary search for the associated window. */ 417 418 int startIndex = 0; 419 int endIndex = aw->numIndex; 420 int currentIndex = -1; 421 char found = false; 422 423 while (1) { 424 currentIndex = (startIndex + endIndex) / 2; 425 426 if (startIndex >= endIndex) { 427 break; 428 } 429 430 assert(currentIndex >= 0); 431 assert(currentIndex < aw->numIndex); 432 433 if (w_associate_window_contains(&aw->associated[currentIndex], addr)) { 434 /* We have found the correct window. */ 435 found = true; 436 break; 437 } else if (aw->associated[currentIndex].offset <= addr) { 438 /* Guess is too small, search down the bigger half. */ 439 startIndex = currentIndex + 1; 440 } else { 441 /* Guess is too large, search down the smaller half. */ 442 endIndex = currentIndex; 443 } 444 } 445 446 return found ? currentIndex : (-1 - currentIndex); 447} 448 449struct w_associated_window * 450w_associate_find(struct w_associated_windowlist *aw, vaddr_t addr) 451{ 452 int findResult = w_associate_find_index(aw, addr); 453 return findResult < 0 ? NULL : &aw->associated[findResult]; 454} 455 456struct w_associated_window * 457w_associate_find_winID(struct w_associated_windowlist *aw, int winID) 458{ 459 /* Re-sort the list if it's out of date. */ 460 if (!aw->updated) { 461 w_associate_update(aw); 462 assert(aw->updated); 463 } 464 465 for (int i = 0; i < aw->numIndex; i++) { 466 if (aw->associated[i].winID == winID) { 467 return &aw->associated[i]; 468 } 469 } 470 return NULL; 471} 472 473bool 474w_associate_check(struct w_associated_windowlist *aw, vaddr_t offset, vaddr_t size) 475{ 476 /* Search for both the start vaddress and end vaddress. If either of them are valid, 477 then this window is invalid. Otherwise if the searched index are the same, then this 478 means there was not a window in between offset and size. 479 */ 480 int findResult1 = w_associate_find_index(aw, offset); 481 if (findResult1 >= 0) { 482 return false; 483 } 484 int findResult2 = w_associate_find_index(aw, (offset + size - 1)); 485 if (findResult2 >= 0) { 486 return false; 487 } 488 return (findResult1 == findResult2) ? true : false; 489} 490 491struct w_associated_window * 492w_associate_find_range(struct w_associated_windowlist *aw, vaddr_t offset, vaddr_t size) 493{ 494 /* Search for both the start vaddress and end vaddress. If they both point to the same window, 495 then that window must contain the entire range. */ 496 int findResult1 = w_associate_find_index(aw, offset); 497 if (findResult1 < 0) { 498 return NULL; 499 } 500 int findResult2 = w_associate_find_index(aw, (offset + size - 1)); 501 if (findResult2 < 0) { 502 return NULL; 503 } 504 if (findResult1 != findResult2) { 505 /* A window ended and another window started in between our given range. This means 506 the given range is NOT contained by a single window. */ 507 return NULL; 508 } 509 return &aw->associated[findResult1]; 510} 511