source: mainline/kernel/generic/src/mm/as.c@ 57355a40

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 57355a40 was 57355a40, checked in by Jakub Jermar <jakub@…>, 13 years ago

Rather than testing for address overlap with kernel address space,
test whether the address range is contained in the user address space.

  • Property mode set to 100644
File size: 55.6 KB
RevLine 
[20d50a1]1/*
[0321109]2 * Copyright (c) 2010 Jakub Jermar
[20d50a1]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
[cc73a8a1]29/** @addtogroup genericmm
[b45c443]30 * @{
31 */
32
[9179d0a]33/**
[b45c443]34 * @file
[da1bafb]35 * @brief Address space related functions.
[9179d0a]36 *
[20d50a1]37 * This file contains address space manipulation functions.
38 * Roughly speaking, this is a higher-level client of
39 * Virtual Address Translation (VAT) subsystem.
[9179d0a]40 *
41 * Functionality provided by this file allows one to
[cc73a8a1]42 * create address spaces and create, resize and share
[9179d0a]43 * address space areas.
44 *
45 * @see page.c
46 *
[20d50a1]47 */
48
49#include <mm/as.h>
[ef67bab]50#include <arch/mm/as.h>
[20d50a1]51#include <mm/page.h>
52#include <mm/frame.h>
[085d973]53#include <mm/slab.h>
[20d50a1]54#include <mm/tlb.h>
55#include <arch/mm/page.h>
56#include <genarch/mm/page_pt.h>
[2802767]57#include <genarch/mm/page_ht.h>
[4512d7e]58#include <mm/asid.h>
[20d50a1]59#include <arch/mm/asid.h>
[31d8e10]60#include <preemption.h>
[20d50a1]61#include <synch/spinlock.h>
[1068f6a]62#include <synch/mutex.h>
[5c9a08b]63#include <adt/list.h>
[252127e]64#include <adt/btree.h>
[df0103f7]65#include <proc/task.h>
[e3c762cd]66#include <proc/thread.h>
[20d50a1]67#include <arch/asm.h>
[df0103f7]68#include <panic.h>
[20d50a1]69#include <debug.h>
[df0103f7]70#include <print.h>
[20d50a1]71#include <memstr.h>
[5a7d9d1]72#include <macros.h>
[0b37882]73#include <bitops.h>
[20d50a1]74#include <arch.h>
[df0103f7]75#include <errno.h>
76#include <config.h>
[25bf215]77#include <align.h>
[d99c1d2]78#include <typedefs.h>
[e3c762cd]79#include <syscall/copy.h>
80#include <arch/interrupt.h>
[20d50a1]81
[cc73a8a1]82/**
83 * Each architecture decides what functions will be used to carry out
84 * address space operations such as creating or locking page tables.
85 */
[ef67bab]86as_operations_t *as_operations = NULL;
[20d50a1]87
[fc47885]88/** Slab for as_t objects.
[da1bafb]89 *
[57da95c]90 */
91static slab_cache_t *as_slab;
92
[fc47885]93/** ASID subsystem lock.
94 *
95 * This lock protects:
[55b77d9]96 * - inactive_as_with_asid_list
[879585a3]97 * - as->asid for each as of the as_t type
98 * - asids_allocated counter
[da1bafb]99 *
[6f4495f5]100 */
[879585a3]101SPINLOCK_INITIALIZE(asidlock);
[7e4e532]102
103/**
[fc47885]104 * Inactive address spaces (on all processors)
105 * that have valid ASID.
[7e4e532]106 */
[55b77d9]107LIST_INITIALIZE(inactive_as_with_asid_list);
[7e4e532]108
[071a8ae6]109/** Kernel address space. */
110as_t *AS_KERNEL = NULL;
111
[7a0359b]112NO_TRACE static int as_constructor(void *obj, unsigned int flags)
[29b2bbf]113{
114 as_t *as = (as_t *) obj;
[da1bafb]115
[29b2bbf]116 link_initialize(&as->inactive_as_with_asid_link);
[7f341820]117 mutex_initialize(&as->lock, MUTEX_PASSIVE);
[29b2bbf]118
[fc47885]119 return as_constructor_arch(as, flags);
[29b2bbf]120}
121
[7a0359b]122NO_TRACE static size_t as_destructor(void *obj)
[29b2bbf]123{
[fc47885]124 return as_destructor_arch((as_t *) obj);
[29b2bbf]125}
126
[ef67bab]127/** Initialize address space subsystem. */
128void as_init(void)
129{
130 as_arch_init();
[da1bafb]131
[f97f1e51]132 as_slab = slab_cache_create("as_t", sizeof(as_t), 0,
[6f4495f5]133 as_constructor, as_destructor, SLAB_CACHE_MAGDEFERRED);
[57da95c]134
[8e1ea655]135 AS_KERNEL = as_create(FLAG_AS_KERNEL);
[125e944]136 if (!AS_KERNEL)
[f651e80]137 panic("Cannot create kernel address space.");
[125e944]138
[fc47885]139 /*
140 * Make sure the kernel address space
[76fca31]141 * reference count never drops to zero.
142 */
[6193351]143 as_hold(AS_KERNEL);
[ef67bab]144}
145
[071a8ae6]146/** Create address space.
147 *
[da1bafb]148 * @param flags Flags that influence the way in wich the address
149 * space is created.
150 *
[071a8ae6]151 */
[da1bafb]152as_t *as_create(unsigned int flags)
[20d50a1]153{
[da1bafb]154 as_t *as = (as_t *) slab_alloc(as_slab, 0);
[29b2bbf]155 (void) as_create_arch(as, 0);
156
[252127e]157 btree_create(&as->as_area_btree);
[bb68433]158
159 if (flags & FLAG_AS_KERNEL)
160 as->asid = ASID_KERNEL;
161 else
162 as->asid = ASID_INVALID;
163
[31d8e10]164 atomic_set(&as->refcount, 0);
[47800e0]165 as->cpu_refcount = 0;
[da1bafb]166
[b3f8fb7]167#ifdef AS_PAGE_TABLE
[80bcaed]168 as->genarch.page_table = page_table_create(flags);
[b3f8fb7]169#else
170 page_table_create(flags);
171#endif
[76fca31]172
[20d50a1]173 return as;
174}
175
[482826d]176/** Destroy adress space.
177 *
[6f4495f5]178 * When there are no tasks referencing this address space (i.e. its refcount is
179 * zero), the address space can be destroyed.
[31d8e10]180 *
181 * We know that we don't hold any spinlock.
[6745592]182 *
[da1bafb]183 * @param as Address space to be destroyed.
184 *
[482826d]185 */
186void as_destroy(as_t *as)
[5be1923]187{
[31d8e10]188 DEADLOCK_PROBE_INIT(p_asidlock);
[fc47885]189
[1624aae]190 ASSERT(as != AS);
[31d8e10]191 ASSERT(atomic_get(&as->refcount) == 0);
[482826d]192
193 /*
[663bb537]194 * Since there is no reference to this address space, it is safe not to
195 * lock its mutex.
[482826d]196 */
[fc47885]197
[31d8e10]198 /*
199 * We need to avoid deadlock between TLB shootdown and asidlock.
200 * We therefore try to take asid conditionally and if we don't succeed,
201 * we enable interrupts and try again. This is done while preemption is
202 * disabled to prevent nested context switches. We also depend on the
203 * fact that so far no spinlocks are held.
204 */
205 preemption_disable();
[da1bafb]206 ipl_t ipl = interrupts_read();
207
[31d8e10]208retry:
209 interrupts_disable();
210 if (!spinlock_trylock(&asidlock)) {
211 interrupts_enable();
212 DEADLOCK_PROBE(p_asidlock, DEADLOCK_THRESHOLD);
213 goto retry;
214 }
[da1bafb]215
216 /* Interrupts disabled, enable preemption */
217 preemption_enable();
218
219 if ((as->asid != ASID_INVALID) && (as != AS_KERNEL)) {
[1624aae]220 if (as->cpu_refcount == 0)
[31e8ddd]221 list_remove(&as->inactive_as_with_asid_link);
[da1bafb]222
[482826d]223 asid_put(as->asid);
224 }
[da1bafb]225
[879585a3]226 spinlock_unlock(&asidlock);
[fdaad75d]227 interrupts_restore(ipl);
[fc47885]228
[da1bafb]229
[482826d]230 /*
231 * Destroy address space areas of the address space.
[8440473]232 * The B+tree must be walked carefully because it is
[6f9a9bc]233 * also being destroyed.
[da1bafb]234 */
235 bool cond = true;
236 while (cond) {
[55b77d9]237 ASSERT(!list_empty(&as->as_area_btree.leaf_list));
[da1bafb]238
239 btree_node_t *node =
[55b77d9]240 list_get_instance(list_first(&as->as_area_btree.leaf_list),
[6f4495f5]241 btree_node_t, leaf_link);
[da1bafb]242
243 if ((cond = node->keys))
[6f9a9bc]244 as_area_destroy(as, node->key[0]);
[482826d]245 }
[da1bafb]246
[152b2b0]247 btree_destroy(&as->as_area_btree);
[da1bafb]248
[b3f8fb7]249#ifdef AS_PAGE_TABLE
[80bcaed]250 page_table_destroy(as->genarch.page_table);
[b3f8fb7]251#else
252 page_table_destroy(NULL);
253#endif
[da1bafb]254
[57da95c]255 slab_free(as_slab, as);
[5be1923]256}
257
[0321109]258/** Hold a reference to an address space.
259 *
[fc47885]260 * Holding a reference to an address space prevents destruction
261 * of that address space.
[0321109]262 *
[da1bafb]263 * @param as Address space to be held.
264 *
[0321109]265 */
[7a0359b]266NO_TRACE void as_hold(as_t *as)
[0321109]267{
268 atomic_inc(&as->refcount);
269}
270
271/** Release a reference to an address space.
272 *
[fc47885]273 * The last one to release a reference to an address space
274 * destroys the address space.
[0321109]275 *
[da1bafb]276 * @param asAddress space to be released.
277 *
[0321109]278 */
[7a0359b]279NO_TRACE void as_release(as_t *as)
[0321109]280{
281 if (atomic_predec(&as->refcount) == 0)
282 as_destroy(as);
283}
284
[e3ee9b9]285/** Check area conflicts with other areas.
286 *
[35a3d950]287 * @param as Address space.
288 * @param addr Starting virtual address of the area being tested.
289 * @param count Number of pages in the area being tested.
290 * @param guarded True if the area being tested is protected by guard pages.
291 * @param avoid Do not touch this area.
[e3ee9b9]292 *
293 * @return True if there is no conflict, false otherwise.
294 *
295 */
[0b37882]296NO_TRACE static bool check_area_conflicts(as_t *as, uintptr_t addr,
[35a3d950]297 size_t count, bool guarded, as_area_t *avoid)
[e3ee9b9]298{
[0b37882]299 ASSERT((addr % PAGE_SIZE) == 0);
[e3ee9b9]300 ASSERT(mutex_locked(&as->lock));
[94795812]301
302 /*
303 * If the addition of the supposed area address and size overflows,
304 * report conflict.
305 */
306 if (overflows_into_positive(addr, P2SZ(count)))
307 return false;
[e3ee9b9]308
309 /*
310 * We don't want any area to have conflicts with NULL page.
311 */
[b6f3e7e]312 if (overlaps(addr, P2SZ(count), (uintptr_t) NULL, PAGE_SIZE))
[e3ee9b9]313 return false;
[35a3d950]314
[e3ee9b9]315 /*
316 * The leaf node is found in O(log n), where n is proportional to
317 * the number of address space areas belonging to as.
318 * The check for conflicts is then attempted on the rightmost
319 * record in the left neighbour, the leftmost record in the right
320 * neighbour and all records in the leaf node itself.
321 */
322 btree_node_t *leaf;
323 as_area_t *area =
[0b37882]324 (as_area_t *) btree_search(&as->as_area_btree, addr, &leaf);
[e3ee9b9]325 if (area) {
[0b37882]326 if (area != avoid)
[e3ee9b9]327 return false;
328 }
329
330 /* First, check the two border cases. */
331 btree_node_t *node =
332 btree_leaf_node_left_neighbour(&as->as_area_btree, leaf);
333 if (node) {
334 area = (as_area_t *) node->value[node->keys - 1];
335
[0b37882]336 if (area != avoid) {
337 mutex_lock(&area->lock);
[35a3d950]338
[94795812]339 /*
340 * If at least one of the two areas are protected
[35a3d950]341 * by the AS_AREA_GUARD flag then we must be sure
342 * that they are separated by at least one unmapped
343 * page.
344 */
345 int const gp = (guarded ||
346 (area->flags & AS_AREA_GUARD)) ? 1 : 0;
[0b37882]347
[94795812]348 /*
349 * The area comes from the left neighbour node, which
350 * means that there already are some areas in the leaf
351 * node, which in turn means that adding gp is safe and
352 * will not cause an integer overflow.
353 */
[b6f3e7e]354 if (overlaps(addr, P2SZ(count), area->base,
[35a3d950]355 P2SZ(area->pages + gp))) {
[0b37882]356 mutex_unlock(&area->lock);
357 return false;
358 }
359
[e3ee9b9]360 mutex_unlock(&area->lock);
361 }
362 }
363
364 node = btree_leaf_node_right_neighbour(&as->as_area_btree, leaf);
365 if (node) {
366 area = (as_area_t *) node->value[0];
367
[0b37882]368 if (area != avoid) {
[94795812]369 int gp;
370
[0b37882]371 mutex_lock(&area->lock);
[35a3d950]372
[94795812]373 gp = (guarded || (area->flags & AS_AREA_GUARD)) ? 1 : 0;
374 if (gp && overflows(addr, P2SZ(count))) {
375 /*
376 * Guard page not needed if the supposed area
377 * is adjacent to the end of the address space.
378 * We already know that the following test is
379 * going to fail...
380 */
381 gp--;
382 }
[0b37882]383
[35a3d950]384 if (overlaps(addr, P2SZ(count + gp), area->base,
[b6f3e7e]385 P2SZ(area->pages))) {
[0b37882]386 mutex_unlock(&area->lock);
387 return false;
388 }
389
[e3ee9b9]390 mutex_unlock(&area->lock);
391 }
392 }
393
394 /* Second, check the leaf node. */
395 btree_key_t i;
396 for (i = 0; i < leaf->keys; i++) {
397 area = (as_area_t *) leaf->value[i];
[94795812]398 int agp;
399 int gp;
[e3ee9b9]400
[0b37882]401 if (area == avoid)
[e3ee9b9]402 continue;
403
404 mutex_lock(&area->lock);
[35a3d950]405
[94795812]406 gp = (guarded || (area->flags & AS_AREA_GUARD)) ? 1 : 0;
407 agp = gp;
408
409 /*
410 * Sanitize the two possible unsigned integer overflows.
411 */
412 if (gp && overflows(addr, P2SZ(count)))
413 gp--;
414 if (agp && overflows(area->base, P2SZ(area->pages)))
415 agp--;
[35a3d950]416
417 if (overlaps(addr, P2SZ(count + gp), area->base,
[94795812]418 P2SZ(area->pages + agp))) {
[e3ee9b9]419 mutex_unlock(&area->lock);
420 return false;
421 }
422
423 mutex_unlock(&area->lock);
424 }
425
426 /*
427 * So far, the area does not conflict with other areas.
[57355a40]428 * Check if it is contained in the user address space.
[e3ee9b9]429 */
430 if (!KERNEL_ADDRESS_SPACE_SHADOWED) {
[57355a40]431 return iswithin(USER_ADDRESS_SPACE_START,
432 (USER_ADDRESS_SPACE_END - USER_ADDRESS_SPACE_START) + 1,
433 addr, P2SZ(count));
[e3ee9b9]434 }
435
436 return true;
437}
438
[fbcdeb8]439/** Return pointer to unmapped address space area
440 *
441 * The address space must be already locked when calling
442 * this function.
443 *
[35a3d950]444 * @param as Address space.
445 * @param bound Lowest address bound.
446 * @param size Requested size of the allocation.
447 * @param guarded True if the allocation must be protected by guard pages.
[fbcdeb8]448 *
449 * @return Address of the beginning of unmapped address space area.
450 * @return -1 if no suitable address space area was found.
451 *
452 */
453NO_TRACE static uintptr_t as_get_unmapped_area(as_t *as, uintptr_t bound,
[35a3d950]454 size_t size, bool guarded)
[fbcdeb8]455{
456 ASSERT(mutex_locked(&as->lock));
457
458 if (size == 0)
459 return (uintptr_t) -1;
460
461 /*
462 * Make sure we allocate from page-aligned
463 * address. Check for possible overflow in
464 * each step.
465 */
466
467 size_t pages = SIZE2FRAMES(size);
468
469 /*
470 * Find the lowest unmapped address aligned on the size
471 * boundary, not smaller than bound and of the required size.
472 */
473
474 /* First check the bound address itself */
475 uintptr_t addr = ALIGN_UP(bound, PAGE_SIZE);
[35a3d950]476 if (addr >= bound) {
477 if (guarded) {
478 /* Leave an unmapped page between the lower
479 * bound and the area's start address.
480 */
481 addr += P2SZ(1);
482 }
483
484 if (check_area_conflicts(as, addr, pages, guarded, NULL))
485 return addr;
486 }
[fbcdeb8]487
488 /* Eventually check the addresses behind each area */
489 list_foreach(as->as_area_btree.leaf_list, cur) {
490 btree_node_t *node =
491 list_get_instance(cur, btree_node_t, leaf_link);
492
493 for (btree_key_t i = 0; i < node->keys; i++) {
494 as_area_t *area = (as_area_t *) node->value[i];
495
496 mutex_lock(&area->lock);
497
498 addr =
499 ALIGN_UP(area->base + P2SZ(area->pages), PAGE_SIZE);
[35a3d950]500
501 if (guarded || area->flags & AS_AREA_GUARD) {
502 /* We must leave an unmapped page
503 * between the two areas.
504 */
505 addr += P2SZ(1);
506 }
507
[fbcdeb8]508 bool avail =
509 ((addr >= bound) && (addr >= area->base) &&
[35a3d950]510 (check_area_conflicts(as, addr, pages, guarded, area)));
[fbcdeb8]511
512 mutex_unlock(&area->lock);
513
514 if (avail)
515 return addr;
516 }
517 }
518
519 /* No suitable address space area found */
520 return (uintptr_t) -1;
521}
522
[20d50a1]523/** Create address space area of common attributes.
524 *
525 * The created address space area is added to the target address space.
526 *
[da1bafb]527 * @param as Target address space.
528 * @param flags Flags of the area memory.
529 * @param size Size of area.
530 * @param attrs Attributes of the area.
531 * @param backend Address space area backend. NULL if no backend is used.
532 * @param backend_data NULL or a pointer to an array holding two void *.
[fbcdeb8]533 * @param base Starting virtual address of the area.
534 * If set to -1, a suitable mappable area is found.
535 * @param bound Lowest address bound if base is set to -1.
536 * Otherwise ignored.
[da1bafb]537 *
538 * @return Address space area on success or NULL on failure.
[20d50a1]539 *
540 */
[da1bafb]541as_area_t *as_area_create(as_t *as, unsigned int flags, size_t size,
[fbcdeb8]542 unsigned int attrs, mem_backend_t *backend,
543 mem_backend_data_t *backend_data, uintptr_t *base, uintptr_t bound)
[20d50a1]544{
[fbcdeb8]545 if ((*base != (uintptr_t) -1) && ((*base % PAGE_SIZE) != 0))
[37e7d2b9]546 return NULL;
[da1bafb]547
[0b37882]548 if (size == 0)
[dbbeb26]549 return NULL;
[0941e9ae]550
[0b37882]551 size_t pages = SIZE2FRAMES(size);
552
[37e7d2b9]553 /* Writeable executable areas are not supported. */
554 if ((flags & AS_AREA_EXEC) && (flags & AS_AREA_WRITE))
555 return NULL;
[35a3d950]556
557 bool const guarded = flags & AS_AREA_GUARD;
[20d50a1]558
[1068f6a]559 mutex_lock(&as->lock);
[20d50a1]560
[fbcdeb8]561 if (*base == (uintptr_t) -1) {
[35a3d950]562 *base = as_get_unmapped_area(as, bound, size, guarded);
[fbcdeb8]563 if (*base == (uintptr_t) -1) {
564 mutex_unlock(&as->lock);
565 return NULL;
566 }
567 }
[35a3d950]568
[94795812]569 if (overflows_into_positive(*base, size))
[0941e9ae]570 return NULL;
571
[35a3d950]572 if (!check_area_conflicts(as, *base, pages, guarded, NULL)) {
[1068f6a]573 mutex_unlock(&as->lock);
[37e7d2b9]574 return NULL;
575 }
[20d50a1]576
[da1bafb]577 as_area_t *area = (as_area_t *) malloc(sizeof(as_area_t), 0);
578
579 mutex_initialize(&area->lock, MUTEX_PASSIVE);
580
581 area->as = as;
582 area->flags = flags;
583 area->attributes = attrs;
[0b37882]584 area->pages = pages;
[fc47885]585 area->resident = 0;
[fbcdeb8]586 area->base = *base;
[da1bafb]587 area->sh_info = NULL;
588 area->backend = backend;
589
[0ee077ee]590 if (backend_data)
[da1bafb]591 area->backend_data = *backend_data;
[0ee077ee]592 else
[da1bafb]593 memsetb(&area->backend_data, sizeof(area->backend_data), 0);
594
[e394b736]595 if (area->backend && area->backend->create) {
596 if (!area->backend->create(area)) {
597 free(area);
598 mutex_unlock(&as->lock);
599 return NULL;
600 }
601 }
602
[da1bafb]603 btree_create(&area->used_space);
[fbcdeb8]604 btree_insert(&as->as_area_btree, *base, (void *) area,
605 NULL);
[bb68433]606
[1068f6a]607 mutex_unlock(&as->lock);
[da1bafb]608
609 return area;
[20d50a1]610}
611
[e3ee9b9]612/** Find address space area and lock it.
613 *
614 * @param as Address space.
615 * @param va Virtual address.
616 *
617 * @return Locked address space area containing va on success or
618 * NULL on failure.
619 *
620 */
[7a0359b]621NO_TRACE static as_area_t *find_area_and_lock(as_t *as, uintptr_t va)
[e3ee9b9]622{
623 ASSERT(mutex_locked(&as->lock));
624
625 btree_node_t *leaf;
[b6f3e7e]626 as_area_t *area = (as_area_t *) btree_search(&as->as_area_btree, va,
627 &leaf);
[e3ee9b9]628 if (area) {
629 /* va is the base address of an address space area */
630 mutex_lock(&area->lock);
631 return area;
632 }
633
634 /*
[326bf65]635 * Search the leaf node and the rightmost record of its left neighbour
[e3ee9b9]636 * to find out whether this is a miss or va belongs to an address
637 * space area found there.
638 */
639
640 /* First, search the leaf node itself. */
641 btree_key_t i;
642
643 for (i = 0; i < leaf->keys; i++) {
644 area = (as_area_t *) leaf->value[i];
645
646 mutex_lock(&area->lock);
[326bf65]647
[b6f3e7e]648 if ((area->base <= va) &&
649 (va <= area->base + (P2SZ(area->pages) - 1)))
[e3ee9b9]650 return area;
651
652 mutex_unlock(&area->lock);
653 }
654
655 /*
656 * Second, locate the left neighbour and test its last record.
657 * Because of its position in the B+tree, it must have base < va.
658 */
[b6f3e7e]659 btree_node_t *lnode = btree_leaf_node_left_neighbour(&as->as_area_btree,
660 leaf);
[e3ee9b9]661 if (lnode) {
662 area = (as_area_t *) lnode->value[lnode->keys - 1];
663
664 mutex_lock(&area->lock);
665
[b6f3e7e]666 if (va <= area->base + (P2SZ(area->pages) - 1))
[e3ee9b9]667 return area;
668
669 mutex_unlock(&area->lock);
670 }
671
672 return NULL;
673}
674
[df0103f7]675/** Find address space area and change it.
676 *
[da1bafb]677 * @param as Address space.
678 * @param address Virtual address belonging to the area to be changed.
679 * Must be page-aligned.
680 * @param size New size of the virtual memory block starting at
681 * address.
682 * @param flags Flags influencing the remap operation. Currently unused.
683 *
684 * @return Zero on success or a value from @ref errno.h otherwise.
[df0103f7]685 *
[da1bafb]686 */
687int as_area_resize(as_t *as, uintptr_t address, size_t size, unsigned int flags)
[df0103f7]688{
[1068f6a]689 mutex_lock(&as->lock);
[df0103f7]690
691 /*
692 * Locate the area.
693 */
[da1bafb]694 as_area_t *area = find_area_and_lock(as, address);
[df0103f7]695 if (!area) {
[1068f6a]696 mutex_unlock(&as->lock);
[7242a78e]697 return ENOENT;
[df0103f7]698 }
[01029fc]699
700 if (!area->backend->is_resizable(area)) {
[df0103f7]701 /*
[01029fc]702 * The backend does not support resizing for this area.
[df0103f7]703 */
[1068f6a]704 mutex_unlock(&area->lock);
705 mutex_unlock(&as->lock);
[7242a78e]706 return ENOTSUP;
[df0103f7]707 }
[da1bafb]708
[8182031]709 if (area->sh_info) {
710 /*
[da1bafb]711 * Remapping of shared address space areas
[8182031]712 * is not supported.
713 */
714 mutex_unlock(&area->lock);
715 mutex_unlock(&as->lock);
716 return ENOTSUP;
717 }
[da1bafb]718
719 size_t pages = SIZE2FRAMES((address - area->base) + size);
[df0103f7]720 if (!pages) {
721 /*
722 * Zero size address space areas are not allowed.
723 */
[1068f6a]724 mutex_unlock(&area->lock);
725 mutex_unlock(&as->lock);
[7242a78e]726 return EPERM;
[df0103f7]727 }
728
729 if (pages < area->pages) {
[b6f3e7e]730 uintptr_t start_free = area->base + P2SZ(pages);
[da1bafb]731
[df0103f7]732 /*
733 * Shrinking the area.
734 * No need to check for overlaps.
735 */
[da1bafb]736
[c964521]737 page_table_lock(as, false);
[da1bafb]738
[56789125]739 /*
740 * Remove frames belonging to used space starting from
741 * the highest addresses downwards until an overlap with
742 * the resized address space area is found. Note that this
743 * is also the right way to remove part of the used_space
744 * B+tree leaf list.
[da1bafb]745 */
746 bool cond = true;
747 while (cond) {
[55b77d9]748 ASSERT(!list_empty(&area->used_space.leaf_list));
[da1bafb]749
750 btree_node_t *node =
[55b77d9]751 list_get_instance(list_last(&area->used_space.leaf_list),
[6f4495f5]752 btree_node_t, leaf_link);
[da1bafb]753
[56789125]754 if ((cond = (bool) node->keys)) {
[da1bafb]755 uintptr_t ptr = node->key[node->keys - 1];
756 size_t size =
[98000fb]757 (size_t) node->value[node->keys - 1];
[da1bafb]758 size_t i = 0;
759
[b6f3e7e]760 if (overlaps(ptr, P2SZ(size), area->base,
761 P2SZ(pages))) {
[56789125]762
[b6f3e7e]763 if (ptr + P2SZ(size) <= start_free) {
[56789125]764 /*
[6f4495f5]765 * The whole interval fits
766 * completely in the resized
767 * address space area.
[56789125]768 */
769 break;
770 }
[da1bafb]771
[56789125]772 /*
[6f4495f5]773 * Part of the interval corresponding
774 * to b and c overlaps with the resized
775 * address space area.
[56789125]776 */
[da1bafb]777
778 /* We are almost done */
779 cond = false;
780 i = (start_free - ptr) >> PAGE_WIDTH;
[6745592]781 if (!used_space_remove(area, start_free,
[da1bafb]782 size - i))
783 panic("Cannot remove used space.");
[56789125]784 } else {
785 /*
[6f4495f5]786 * The interval of used space can be
787 * completely removed.
[56789125]788 */
[da1bafb]789 if (!used_space_remove(area, ptr, size))
790 panic("Cannot remove used space.");
[56789125]791 }
[da1bafb]792
[d67dfdc]793 /*
794 * Start TLB shootdown sequence.
795 *
796 * The sequence is rather short and can be
797 * repeated multiple times. The reason is that
798 * we don't want to have used_space_remove()
799 * inside the sequence as it may use a blocking
800 * memory allocation for its B+tree. Blocking
801 * while holding the tlblock spinlock is
802 * forbidden and would hit a kernel assertion.
803 */
804
805 ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES,
806 as->asid, area->base + P2SZ(pages),
807 area->pages - pages);
808
[da1bafb]809 for (; i < size; i++) {
[b6f3e7e]810 pte_t *pte = page_mapping_find(as,
[0ff03f3]811 ptr + P2SZ(i), false);
[da1bafb]812
813 ASSERT(pte);
814 ASSERT(PTE_VALID(pte));
815 ASSERT(PTE_PRESENT(pte));
816
817 if ((area->backend) &&
818 (area->backend->frame_free)) {
[0ee077ee]819 area->backend->frame_free(area,
[b6f3e7e]820 ptr + P2SZ(i),
[6f4495f5]821 PTE_GET_FRAME(pte));
[8182031]822 }
[da1bafb]823
[b6f3e7e]824 page_mapping_remove(as, ptr + P2SZ(i));
[56789125]825 }
[da1bafb]826
[d67dfdc]827 /*
828 * Finish TLB shootdown sequence.
829 */
[da1bafb]830
[d67dfdc]831 tlb_invalidate_pages(as->asid,
832 area->base + P2SZ(pages),
833 area->pages - pages);
[31d8e10]834
[d67dfdc]835 /*
836 * Invalidate software translation caches
837 * (e.g. TSB on sparc64, PHT on ppc32).
838 */
839 as_invalidate_translation_cache(as,
840 area->base + P2SZ(pages),
841 area->pages - pages);
842 tlb_shootdown_finalize(ipl);
843 }
844 }
[da1bafb]845 page_table_unlock(as, false);
[df0103f7]846 } else {
847 /*
848 * Growing the area.
[0941e9ae]849 */
850
[94795812]851 if (overflows_into_positive(address, P2SZ(pages)))
[0941e9ae]852 return EINVAL;
853
854 /*
[df0103f7]855 * Check for overlaps with other address space areas.
856 */
[35a3d950]857 bool const guarded = area->flags & AS_AREA_GUARD;
858 if (!check_area_conflicts(as, address, pages, guarded, area)) {
[1068f6a]859 mutex_unlock(&area->lock);
[da1bafb]860 mutex_unlock(&as->lock);
[7242a78e]861 return EADDRNOTAVAIL;
[df0103f7]862 }
[da1bafb]863 }
864
[e394b736]865 if (area->backend && area->backend->resize) {
866 if (!area->backend->resize(area, pages)) {
867 mutex_unlock(&area->lock);
868 mutex_unlock(&as->lock);
869 return ENOMEM;
870 }
871 }
872
[df0103f7]873 area->pages = pages;
874
[1068f6a]875 mutex_unlock(&area->lock);
876 mutex_unlock(&as->lock);
[da1bafb]877
[7242a78e]878 return 0;
879}
880
[e3ee9b9]881/** Remove reference to address space area share info.
882 *
883 * If the reference count drops to 0, the sh_info is deallocated.
884 *
885 * @param sh_info Pointer to address space area share info.
886 *
887 */
[7a0359b]888NO_TRACE static void sh_info_remove_reference(share_info_t *sh_info)
[e3ee9b9]889{
890 bool dealloc = false;
891
892 mutex_lock(&sh_info->lock);
893 ASSERT(sh_info->refcount);
894
895 if (--sh_info->refcount == 0) {
896 dealloc = true;
897
898 /*
899 * Now walk carefully the pagemap B+tree and free/remove
900 * reference from all frames found there.
901 */
[55b77d9]902 list_foreach(sh_info->pagemap.leaf_list, cur) {
[e3ee9b9]903 btree_node_t *node
904 = list_get_instance(cur, btree_node_t, leaf_link);
905 btree_key_t i;
906
907 for (i = 0; i < node->keys; i++)
908 frame_free((uintptr_t) node->value[i]);
909 }
910
911 }
912 mutex_unlock(&sh_info->lock);
913
914 if (dealloc) {
915 btree_destroy(&sh_info->pagemap);
916 free(sh_info);
917 }
918}
919
[7242a78e]920/** Destroy address space area.
921 *
[da1bafb]922 * @param as Address space.
923 * @param address Address within the area to be deleted.
924 *
925 * @return Zero on success or a value from @ref errno.h on failure.
[7242a78e]926 *
927 */
[7f1c620]928int as_area_destroy(as_t *as, uintptr_t address)
[7242a78e]929{
[1068f6a]930 mutex_lock(&as->lock);
[da1bafb]931
932 as_area_t *area = find_area_and_lock(as, address);
[7242a78e]933 if (!area) {
[1068f6a]934 mutex_unlock(&as->lock);
[7242a78e]935 return ENOENT;
936 }
[e394b736]937
938 if (area->backend && area->backend->destroy)
939 area->backend->destroy(area);
[da1bafb]940
941 uintptr_t base = area->base;
942
[c964521]943 page_table_lock(as, false);
[da1bafb]944
[5552d60]945 /*
946 * Start TLB shootdown sequence.
947 */
[402eda5]948 ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES, as->asid, area->base,
949 area->pages);
[da1bafb]950
[567807b1]951 /*
952 * Visit only the pages mapped by used_space B+tree.
953 */
[55b77d9]954 list_foreach(area->used_space.leaf_list, cur) {
[567807b1]955 btree_node_t *node;
[da1bafb]956 btree_key_t i;
[56789125]957
[f8d069e8]958 node = list_get_instance(cur, btree_node_t, leaf_link);
959 for (i = 0; i < node->keys; i++) {
[da1bafb]960 uintptr_t ptr = node->key[i];
961 size_t size;
[56789125]962
[da1bafb]963 for (size = 0; size < (size_t) node->value[i]; size++) {
[b6f3e7e]964 pte_t *pte = page_mapping_find(as,
[0ff03f3]965 ptr + P2SZ(size), false);
[da1bafb]966
967 ASSERT(pte);
968 ASSERT(PTE_VALID(pte));
969 ASSERT(PTE_PRESENT(pte));
970
971 if ((area->backend) &&
972 (area->backend->frame_free)) {
973 area->backend->frame_free(area,
[b6f3e7e]974 ptr + P2SZ(size),
975 PTE_GET_FRAME(pte));
[56789125]976 }
[da1bafb]977
[b6f3e7e]978 page_mapping_remove(as, ptr + P2SZ(size));
[7242a78e]979 }
980 }
981 }
[da1bafb]982
[7242a78e]983 /*
[5552d60]984 * Finish TLB shootdown sequence.
[7242a78e]985 */
[da1bafb]986
[f1d1f5d3]987 tlb_invalidate_pages(as->asid, area->base, area->pages);
[da1bafb]988
[f1d1f5d3]989 /*
[eef1b031]990 * Invalidate potential software translation caches
991 * (e.g. TSB on sparc64, PHT on ppc32).
[f1d1f5d3]992 */
993 as_invalidate_translation_cache(as, area->base, area->pages);
[402eda5]994 tlb_shootdown_finalize(ipl);
[da1bafb]995
[c964521]996 page_table_unlock(as, false);
[f1d1f5d3]997
[5552d60]998 btree_destroy(&area->used_space);
[da1bafb]999
[8d4f2ae]1000 area->attributes |= AS_AREA_ATTR_PARTIAL;
[8182031]1001
1002 if (area->sh_info)
1003 sh_info_remove_reference(area->sh_info);
[da1bafb]1004
[1068f6a]1005 mutex_unlock(&area->lock);
[da1bafb]1006
[7242a78e]1007 /*
1008 * Remove the empty area from address space.
1009 */
[f1d1f5d3]1010 btree_remove(&as->as_area_btree, base, NULL);
[7242a78e]1011
[8d4f2ae]1012 free(area);
1013
[f1d1f5d3]1014 mutex_unlock(&as->lock);
[7242a78e]1015 return 0;
[df0103f7]1016}
1017
[8d6bc2d5]1018/** Share address space area with another or the same address space.
[df0103f7]1019 *
[0ee077ee]1020 * Address space area mapping is shared with a new address space area.
1021 * If the source address space area has not been shared so far,
1022 * a new sh_info is created. The new address space area simply gets the
1023 * sh_info of the source area. The process of duplicating the
1024 * mapping is done through the backend share function.
[da1bafb]1025 *
1026 * @param src_as Pointer to source address space.
1027 * @param src_base Base address of the source address space area.
1028 * @param acc_size Expected size of the source area.
1029 * @param dst_as Pointer to destination address space.
[fd4d8c0]1030 * @param dst_flags_mask Destination address space area flags mask.
[fbcdeb8]1031 * @param dst_base Target base address. If set to -1,
1032 * a suitable mappable area is found.
1033 * @param bound Lowest address bound if dst_base is set to -1.
1034 * Otherwise ignored.
[df0103f7]1035 *
[da1bafb]1036 * @return Zero on success.
1037 * @return ENOENT if there is no such task or such address space.
1038 * @return EPERM if there was a problem in accepting the area.
1039 * @return ENOMEM if there was a problem in allocating destination
1040 * address space area.
1041 * @return ENOTSUP if the address space area backend does not support
1042 * sharing.
1043 *
[df0103f7]1044 */
[7f1c620]1045int as_area_share(as_t *src_as, uintptr_t src_base, size_t acc_size,
[fbcdeb8]1046 as_t *dst_as, unsigned int dst_flags_mask, uintptr_t *dst_base,
1047 uintptr_t bound)
[df0103f7]1048{
[1068f6a]1049 mutex_lock(&src_as->lock);
[da1bafb]1050 as_area_t *src_area = find_area_and_lock(src_as, src_base);
[a9e8b39]1051 if (!src_area) {
[6fa476f7]1052 /*
1053 * Could not find the source address space area.
1054 */
[1068f6a]1055 mutex_unlock(&src_as->lock);
[6fa476f7]1056 return ENOENT;
1057 }
[da1bafb]1058
[01029fc]1059 if (!src_area->backend->is_shareable(src_area)) {
[8d6bc2d5]1060 /*
[01029fc]1061 * The backend does not permit sharing of this area.
[8d6bc2d5]1062 */
1063 mutex_unlock(&src_area->lock);
1064 mutex_unlock(&src_as->lock);
1065 return ENOTSUP;
1066 }
1067
[b6f3e7e]1068 size_t src_size = P2SZ(src_area->pages);
[da1bafb]1069 unsigned int src_flags = src_area->flags;
1070 mem_backend_t *src_backend = src_area->backend;
1071 mem_backend_data_t src_backend_data = src_area->backend_data;
1072
[1ec1fd8]1073 /* Share the cacheable flag from the original mapping */
1074 if (src_flags & AS_AREA_CACHEABLE)
1075 dst_flags_mask |= AS_AREA_CACHEABLE;
[da1bafb]1076
1077 if ((src_size != acc_size) ||
1078 ((src_flags & dst_flags_mask) != dst_flags_mask)) {
[8d6bc2d5]1079 mutex_unlock(&src_area->lock);
1080 mutex_unlock(&src_as->lock);
[df0103f7]1081 return EPERM;
1082 }
[da1bafb]1083
[8d6bc2d5]1084 /*
1085 * Now we are committed to sharing the area.
[8440473]1086 * First, prepare the area for sharing.
[8d6bc2d5]1087 * Then it will be safe to unlock it.
1088 */
[da1bafb]1089 share_info_t *sh_info = src_area->sh_info;
[8d6bc2d5]1090 if (!sh_info) {
1091 sh_info = (share_info_t *) malloc(sizeof(share_info_t), 0);
[08a19ba]1092 mutex_initialize(&sh_info->lock, MUTEX_PASSIVE);
[8d6bc2d5]1093 sh_info->refcount = 2;
1094 btree_create(&sh_info->pagemap);
1095 src_area->sh_info = sh_info;
[da1bafb]1096
[c0697c4c]1097 /*
1098 * Call the backend to setup sharing.
1099 */
1100 src_area->backend->share(src_area);
[8d6bc2d5]1101 } else {
1102 mutex_lock(&sh_info->lock);
1103 sh_info->refcount++;
1104 mutex_unlock(&sh_info->lock);
1105 }
[da1bafb]1106
[8d6bc2d5]1107 mutex_unlock(&src_area->lock);
1108 mutex_unlock(&src_as->lock);
[da1bafb]1109
[df0103f7]1110 /*
[a9e8b39]1111 * Create copy of the source address space area.
1112 * The destination area is created with AS_AREA_ATTR_PARTIAL
1113 * attribute set which prevents race condition with
1114 * preliminary as_page_fault() calls.
[fd4d8c0]1115 * The flags of the source area are masked against dst_flags_mask
1116 * to support sharing in less privileged mode.
[df0103f7]1117 */
[fbcdeb8]1118 as_area_t *dst_area = as_area_create(dst_as, dst_flags_mask,
1119 src_size, AS_AREA_ATTR_PARTIAL, src_backend,
1120 &src_backend_data, dst_base, bound);
[a9e8b39]1121 if (!dst_area) {
[df0103f7]1122 /*
1123 * Destination address space area could not be created.
1124 */
[8d6bc2d5]1125 sh_info_remove_reference(sh_info);
1126
[df0103f7]1127 return ENOMEM;
1128 }
[da1bafb]1129
[a9e8b39]1130 /*
1131 * Now the destination address space area has been
1132 * fully initialized. Clear the AS_AREA_ATTR_PARTIAL
[8d6bc2d5]1133 * attribute and set the sh_info.
[da1bafb]1134 */
1135 mutex_lock(&dst_as->lock);
[1068f6a]1136 mutex_lock(&dst_area->lock);
[a9e8b39]1137 dst_area->attributes &= ~AS_AREA_ATTR_PARTIAL;
[8d6bc2d5]1138 dst_area->sh_info = sh_info;
[1068f6a]1139 mutex_unlock(&dst_area->lock);
[da1bafb]1140 mutex_unlock(&dst_as->lock);
1141
[df0103f7]1142 return 0;
1143}
1144
[fb84455]1145/** Check access mode for address space area.
1146 *
[da1bafb]1147 * @param area Address space area.
1148 * @param access Access mode.
1149 *
1150 * @return False if access violates area's permissions, true
1151 * otherwise.
[fb84455]1152 *
1153 */
[97bdb4a]1154NO_TRACE bool as_area_check_access(as_area_t *area, pf_access_t access)
[fb84455]1155{
[fc47885]1156 ASSERT(mutex_locked(&area->lock));
1157
[fb84455]1158 int flagmap[] = {
1159 [PF_ACCESS_READ] = AS_AREA_READ,
1160 [PF_ACCESS_WRITE] = AS_AREA_WRITE,
1161 [PF_ACCESS_EXEC] = AS_AREA_EXEC
1162 };
[da1bafb]1163
[fb84455]1164 if (!(area->flags & flagmap[access]))
1165 return false;
1166
1167 return true;
1168}
1169
[e3ee9b9]1170/** Convert address space area flags to page flags.
1171 *
1172 * @param aflags Flags of some address space area.
1173 *
1174 * @return Flags to be passed to page_mapping_insert().
1175 *
1176 */
[7a0359b]1177NO_TRACE static unsigned int area_flags_to_page_flags(unsigned int aflags)
[e3ee9b9]1178{
1179 unsigned int flags = PAGE_USER | PAGE_PRESENT;
1180
1181 if (aflags & AS_AREA_READ)
1182 flags |= PAGE_READ;
1183
1184 if (aflags & AS_AREA_WRITE)
1185 flags |= PAGE_WRITE;
1186
1187 if (aflags & AS_AREA_EXEC)
1188 flags |= PAGE_EXEC;
1189
1190 if (aflags & AS_AREA_CACHEABLE)
1191 flags |= PAGE_CACHEABLE;
1192
1193 return flags;
1194}
1195
[6745592]1196/** Change adress space area flags.
[c98e6ee]1197 *
1198 * The idea is to have the same data, but with a different access mode.
1199 * This is needed e.g. for writing code into memory and then executing it.
1200 * In order for this to work properly, this may copy the data
1201 * into private anonymous memory (unless it's already there).
1202 *
[76fca31]1203 * @param as Address space.
1204 * @param flags Flags of the area memory.
1205 * @param address Address within the area to be changed.
1206 *
1207 * @return Zero on success or a value from @ref errno.h on failure.
[c98e6ee]1208 *
1209 */
[da1bafb]1210int as_area_change_flags(as_t *as, unsigned int flags, uintptr_t address)
[c98e6ee]1211{
1212 /* Flags for the new memory mapping */
[da1bafb]1213 unsigned int page_flags = area_flags_to_page_flags(flags);
1214
[c98e6ee]1215 mutex_lock(&as->lock);
[da1bafb]1216
1217 as_area_t *area = find_area_and_lock(as, address);
[c98e6ee]1218 if (!area) {
1219 mutex_unlock(&as->lock);
1220 return ENOENT;
1221 }
[da1bafb]1222
[76fca31]1223 if ((area->sh_info) || (area->backend != &anon_backend)) {
[c98e6ee]1224 /* Copying shared areas not supported yet */
1225 /* Copying non-anonymous memory not supported yet */
1226 mutex_unlock(&area->lock);
1227 mutex_unlock(&as->lock);
1228 return ENOTSUP;
1229 }
[da1bafb]1230
[c98e6ee]1231 /*
1232 * Compute total number of used pages in the used_space B+tree
1233 */
[da1bafb]1234 size_t used_pages = 0;
1235
[55b77d9]1236 list_foreach(area->used_space.leaf_list, cur) {
[da1bafb]1237 btree_node_t *node
1238 = list_get_instance(cur, btree_node_t, leaf_link);
1239 btree_key_t i;
[c98e6ee]1240
[da1bafb]1241 for (i = 0; i < node->keys; i++)
[98000fb]1242 used_pages += (size_t) node->value[i];
[c98e6ee]1243 }
[da1bafb]1244
[c98e6ee]1245 /* An array for storing frame numbers */
[da1bafb]1246 uintptr_t *old_frame = malloc(used_pages * sizeof(uintptr_t), 0);
1247
[c964521]1248 page_table_lock(as, false);
[da1bafb]1249
[c98e6ee]1250 /*
1251 * Start TLB shootdown sequence.
1252 */
[402eda5]1253 ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES, as->asid, area->base,
1254 area->pages);
[da1bafb]1255
[c98e6ee]1256 /*
1257 * Remove used pages from page tables and remember their frame
1258 * numbers.
1259 */
[da1bafb]1260 size_t frame_idx = 0;
1261
[55b77d9]1262 list_foreach(area->used_space.leaf_list, cur) {
[b6f3e7e]1263 btree_node_t *node = list_get_instance(cur, btree_node_t,
1264 leaf_link);
[da1bafb]1265 btree_key_t i;
[c98e6ee]1266
1267 for (i = 0; i < node->keys; i++) {
[da1bafb]1268 uintptr_t ptr = node->key[i];
1269 size_t size;
[c98e6ee]1270
[da1bafb]1271 for (size = 0; size < (size_t) node->value[i]; size++) {
[b6f3e7e]1272 pte_t *pte = page_mapping_find(as,
[0ff03f3]1273 ptr + P2SZ(size), false);
[da1bafb]1274
1275 ASSERT(pte);
1276 ASSERT(PTE_VALID(pte));
1277 ASSERT(PTE_PRESENT(pte));
1278
[c98e6ee]1279 old_frame[frame_idx++] = PTE_GET_FRAME(pte);
[da1bafb]1280
[c98e6ee]1281 /* Remove old mapping */
[b6f3e7e]1282 page_mapping_remove(as, ptr + P2SZ(size));
[c98e6ee]1283 }
1284 }
1285 }
[da1bafb]1286
[c98e6ee]1287 /*
1288 * Finish TLB shootdown sequence.
1289 */
[da1bafb]1290
[c98e6ee]1291 tlb_invalidate_pages(as->asid, area->base, area->pages);
[76fca31]1292
[c98e6ee]1293 /*
[eef1b031]1294 * Invalidate potential software translation caches
1295 * (e.g. TSB on sparc64, PHT on ppc32).
[c98e6ee]1296 */
1297 as_invalidate_translation_cache(as, area->base, area->pages);
[402eda5]1298 tlb_shootdown_finalize(ipl);
[da1bafb]1299
[c964521]1300 page_table_unlock(as, false);
[da1bafb]1301
[ae7f6fb]1302 /*
1303 * Set the new flags.
1304 */
1305 area->flags = flags;
[da1bafb]1306
[c98e6ee]1307 /*
1308 * Map pages back in with new flags. This step is kept separate
[6745592]1309 * so that the memory area could not be accesed with both the old and
1310 * the new flags at once.
[c98e6ee]1311 */
1312 frame_idx = 0;
[da1bafb]1313
[55b77d9]1314 list_foreach(area->used_space.leaf_list, cur) {
[da1bafb]1315 btree_node_t *node
1316 = list_get_instance(cur, btree_node_t, leaf_link);
1317 btree_key_t i;
[c98e6ee]1318
1319 for (i = 0; i < node->keys; i++) {
[da1bafb]1320 uintptr_t ptr = node->key[i];
1321 size_t size;
[c98e6ee]1322
[da1bafb]1323 for (size = 0; size < (size_t) node->value[i]; size++) {
[c98e6ee]1324 page_table_lock(as, false);
[da1bafb]1325
[c98e6ee]1326 /* Insert the new mapping */
[b6f3e7e]1327 page_mapping_insert(as, ptr + P2SZ(size),
[c98e6ee]1328 old_frame[frame_idx++], page_flags);
[da1bafb]1329
[c98e6ee]1330 page_table_unlock(as, false);
1331 }
1332 }
1333 }
[da1bafb]1334
[c98e6ee]1335 free(old_frame);
[da1bafb]1336
[c98e6ee]1337 mutex_unlock(&area->lock);
1338 mutex_unlock(&as->lock);
[da1bafb]1339
[c98e6ee]1340 return 0;
1341}
1342
[20d50a1]1343/** Handle page fault within the current address space.
1344 *
[6745592]1345 * This is the high-level page fault handler. It decides whether the page fault
1346 * can be resolved by any backend and if so, it invokes the backend to resolve
1347 * the page fault.
[8182031]1348 *
[20d50a1]1349 * Interrupts are assumed disabled.
1350 *
[da1bafb]1351 * @param page Faulting page.
1352 * @param access Access mode that caused the page fault (i.e.
1353 * read/write/exec).
1354 * @param istate Pointer to the interrupted state.
1355 *
1356 * @return AS_PF_FAULT on page fault.
1357 * @return AS_PF_OK on success.
1358 * @return AS_PF_DEFER if the fault was caused by copy_to_uspace()
1359 * or copy_from_uspace().
[20d50a1]1360 *
1361 */
[7f1c620]1362int as_page_fault(uintptr_t page, pf_access_t access, istate_t *istate)
[20d50a1]1363{
[1068f6a]1364 if (!THREAD)
[8182031]1365 return AS_PF_FAULT;
[7af8c0e]1366
1367 if (!AS)
1368 return AS_PF_FAULT;
1369
[1068f6a]1370 mutex_lock(&AS->lock);
[da1bafb]1371 as_area_t *area = find_area_and_lock(AS, page);
[20d50a1]1372 if (!area) {
1373 /*
1374 * No area contained mapping for 'page'.
1375 * Signal page fault to low-level handler.
1376 */
[1068f6a]1377 mutex_unlock(&AS->lock);
[e3c762cd]1378 goto page_fault;
[20d50a1]1379 }
[da1bafb]1380
[a9e8b39]1381 if (area->attributes & AS_AREA_ATTR_PARTIAL) {
1382 /*
1383 * The address space area is not fully initialized.
1384 * Avoid possible race by returning error.
1385 */
[1068f6a]1386 mutex_unlock(&area->lock);
1387 mutex_unlock(&AS->lock);
[da1bafb]1388 goto page_fault;
[a9e8b39]1389 }
[da1bafb]1390
1391 if ((!area->backend) || (!area->backend->page_fault)) {
[8182031]1392 /*
1393 * The address space area is not backed by any backend
1394 * or the backend cannot handle page faults.
1395 */
1396 mutex_unlock(&area->lock);
1397 mutex_unlock(&AS->lock);
[da1bafb]1398 goto page_fault;
[8182031]1399 }
[da1bafb]1400
[2299914]1401 page_table_lock(AS, false);
1402
1403 /*
[6745592]1404 * To avoid race condition between two page faults on the same address,
1405 * we need to make sure the mapping has not been already inserted.
[2299914]1406 */
[da1bafb]1407 pte_t *pte;
[0ff03f3]1408 if ((pte = page_mapping_find(AS, page, false))) {
[2299914]1409 if (PTE_PRESENT(pte)) {
[fb84455]1410 if (((access == PF_ACCESS_READ) && PTE_READABLE(pte)) ||
[6f4495f5]1411 (access == PF_ACCESS_WRITE && PTE_WRITABLE(pte)) ||
1412 (access == PF_ACCESS_EXEC && PTE_EXECUTABLE(pte))) {
[fb84455]1413 page_table_unlock(AS, false);
1414 mutex_unlock(&area->lock);
1415 mutex_unlock(&AS->lock);
1416 return AS_PF_OK;
1417 }
[2299914]1418 }
1419 }
[20d50a1]1420
1421 /*
[8182031]1422 * Resort to the backend page fault handler.
[20d50a1]1423 */
[0ee077ee]1424 if (area->backend->page_fault(area, page, access) != AS_PF_OK) {
[8182031]1425 page_table_unlock(AS, false);
1426 mutex_unlock(&area->lock);
1427 mutex_unlock(&AS->lock);
1428 goto page_fault;
1429 }
[20d50a1]1430
[8182031]1431 page_table_unlock(AS, false);
[1068f6a]1432 mutex_unlock(&area->lock);
1433 mutex_unlock(&AS->lock);
[e3c762cd]1434 return AS_PF_OK;
[da1bafb]1435
[e3c762cd]1436page_fault:
1437 if (THREAD->in_copy_from_uspace) {
1438 THREAD->in_copy_from_uspace = false;
[6f4495f5]1439 istate_set_retaddr(istate,
1440 (uintptr_t) &memcpy_from_uspace_failover_address);
[e3c762cd]1441 } else if (THREAD->in_copy_to_uspace) {
1442 THREAD->in_copy_to_uspace = false;
[6f4495f5]1443 istate_set_retaddr(istate,
1444 (uintptr_t) &memcpy_to_uspace_failover_address);
[e3c762cd]1445 } else {
1446 return AS_PF_FAULT;
1447 }
[da1bafb]1448
[e3c762cd]1449 return AS_PF_DEFER;
[20d50a1]1450}
1451
[7e4e532]1452/** Switch address spaces.
[1068f6a]1453 *
1454 * Note that this function cannot sleep as it is essentially a part of
[879585a3]1455 * scheduling. Sleeping here would lead to deadlock on wakeup. Another
1456 * thing which is forbidden in this context is locking the address space.
[20d50a1]1457 *
[7250d2c]1458 * When this function is entered, no spinlocks may be held.
[31d8e10]1459 *
[da1bafb]1460 * @param old Old address space or NULL.
1461 * @param new New address space.
1462 *
[20d50a1]1463 */
[80bcaed]1464void as_switch(as_t *old_as, as_t *new_as)
[20d50a1]1465{
[31d8e10]1466 DEADLOCK_PROBE_INIT(p_asidlock);
1467 preemption_disable();
[da1bafb]1468
[31d8e10]1469retry:
1470 (void) interrupts_disable();
1471 if (!spinlock_trylock(&asidlock)) {
[da1bafb]1472 /*
[31d8e10]1473 * Avoid deadlock with TLB shootdown.
1474 * We can enable interrupts here because
1475 * preemption is disabled. We should not be
1476 * holding any other lock.
1477 */
1478 (void) interrupts_enable();
1479 DEADLOCK_PROBE(p_asidlock, DEADLOCK_THRESHOLD);
1480 goto retry;
1481 }
1482 preemption_enable();
[da1bafb]1483
[7e4e532]1484 /*
1485 * First, take care of the old address space.
[da1bafb]1486 */
[80bcaed]1487 if (old_as) {
1488 ASSERT(old_as->cpu_refcount);
[da1bafb]1489
1490 if ((--old_as->cpu_refcount == 0) && (old_as != AS_KERNEL)) {
[7e4e532]1491 /*
1492 * The old address space is no longer active on
1493 * any processor. It can be appended to the
1494 * list of inactive address spaces with assigned
1495 * ASID.
1496 */
[2057572]1497 ASSERT(old_as->asid != ASID_INVALID);
[da1bafb]1498
[2057572]1499 list_append(&old_as->inactive_as_with_asid_link,
[55b77d9]1500 &inactive_as_with_asid_list);
[7e4e532]1501 }
[da1bafb]1502
[57da95c]1503 /*
1504 * Perform architecture-specific tasks when the address space
1505 * is being removed from the CPU.
1506 */
[80bcaed]1507 as_deinstall_arch(old_as);
[7e4e532]1508 }
[da1bafb]1509
[7e4e532]1510 /*
1511 * Second, prepare the new address space.
1512 */
[80bcaed]1513 if ((new_as->cpu_refcount++ == 0) && (new_as != AS_KERNEL)) {
[879585a3]1514 if (new_as->asid != ASID_INVALID)
[80bcaed]1515 list_remove(&new_as->inactive_as_with_asid_link);
[879585a3]1516 else
1517 new_as->asid = asid_get();
[7e4e532]1518 }
[da1bafb]1519
[80bcaed]1520#ifdef AS_PAGE_TABLE
1521 SET_PTL0_ADDRESS(new_as->genarch.page_table);
1522#endif
[7e4e532]1523
[20d50a1]1524 /*
1525 * Perform architecture-specific steps.
[4512d7e]1526 * (e.g. write ASID to hardware register etc.)
[20d50a1]1527 */
[80bcaed]1528 as_install_arch(new_as);
[da1bafb]1529
[879585a3]1530 spinlock_unlock(&asidlock);
[20d50a1]1531
[80bcaed]1532 AS = new_as;
[20d50a1]1533}
[6a3c9a7]1534
[df0103f7]1535/** Compute flags for virtual address translation subsytem.
1536 *
[da1bafb]1537 * @param area Address space area.
1538 *
1539 * @return Flags to be used in page_mapping_insert().
[df0103f7]1540 *
1541 */
[97bdb4a]1542NO_TRACE unsigned int as_area_get_flags(as_area_t *area)
[df0103f7]1543{
[1d432f9]1544 ASSERT(mutex_locked(&area->lock));
[fc47885]1545
[da1bafb]1546 return area_flags_to_page_flags(area->flags);
[df0103f7]1547}
1548
[ef67bab]1549/** Create page table.
1550 *
[6745592]1551 * Depending on architecture, create either address space private or global page
1552 * table.
[ef67bab]1553 *
[da1bafb]1554 * @param flags Flags saying whether the page table is for the kernel
1555 * address space.
1556 *
1557 * @return First entry of the page table.
[ef67bab]1558 *
1559 */
[97bdb4a]1560NO_TRACE pte_t *page_table_create(unsigned int flags)
[ef67bab]1561{
[bd1deed]1562 ASSERT(as_operations);
1563 ASSERT(as_operations->page_table_create);
1564
1565 return as_operations->page_table_create(flags);
[ef67bab]1566}
[d3e7ff4]1567
[482826d]1568/** Destroy page table.
1569 *
1570 * Destroy page table in architecture specific way.
1571 *
[da1bafb]1572 * @param page_table Physical address of PTL0.
1573 *
[482826d]1574 */
[97bdb4a]1575NO_TRACE void page_table_destroy(pte_t *page_table)
[482826d]1576{
[bd1deed]1577 ASSERT(as_operations);
1578 ASSERT(as_operations->page_table_destroy);
1579
1580 as_operations->page_table_destroy(page_table);
[482826d]1581}
1582
[2299914]1583/** Lock page table.
1584 *
1585 * This function should be called before any page_mapping_insert(),
1586 * page_mapping_remove() and page_mapping_find().
[da1bafb]1587 *
[2299914]1588 * Locking order is such that address space areas must be locked
1589 * prior to this call. Address space can be locked prior to this
1590 * call in which case the lock argument is false.
1591 *
[da1bafb]1592 * @param as Address space.
1593 * @param lock If false, do not attempt to lock as->lock.
1594 *
[2299914]1595 */
[97bdb4a]1596NO_TRACE void page_table_lock(as_t *as, bool lock)
[2299914]1597{
1598 ASSERT(as_operations);
1599 ASSERT(as_operations->page_table_lock);
[bd1deed]1600
[2299914]1601 as_operations->page_table_lock(as, lock);
1602}
1603
1604/** Unlock page table.
1605 *
[da1bafb]1606 * @param as Address space.
1607 * @param unlock If false, do not attempt to unlock as->lock.
1608 *
[2299914]1609 */
[97bdb4a]1610NO_TRACE void page_table_unlock(as_t *as, bool unlock)
[2299914]1611{
1612 ASSERT(as_operations);
1613 ASSERT(as_operations->page_table_unlock);
[bd1deed]1614
[2299914]1615 as_operations->page_table_unlock(as, unlock);
1616}
1617
[ada559c]1618/** Test whether page tables are locked.
1619 *
[e3ee9b9]1620 * @param as Address space where the page tables belong.
[ada559c]1621 *
[e3ee9b9]1622 * @return True if the page tables belonging to the address soace
1623 * are locked, otherwise false.
[ada559c]1624 */
[97bdb4a]1625NO_TRACE bool page_table_locked(as_t *as)
[ada559c]1626{
1627 ASSERT(as_operations);
1628 ASSERT(as_operations->page_table_locked);
1629
1630 return as_operations->page_table_locked(as);
1631}
1632
[b878df3]1633/** Return size of the address space area with given base.
1634 *
[1d432f9]1635 * @param base Arbitrary address inside the address space area.
[da1bafb]1636 *
1637 * @return Size of the address space area in bytes or zero if it
1638 * does not exist.
[b878df3]1639 *
1640 */
1641size_t as_area_get_size(uintptr_t base)
[7c23af9]1642{
1643 size_t size;
[da1bafb]1644
[1d432f9]1645 page_table_lock(AS, true);
[da1bafb]1646 as_area_t *src_area = find_area_and_lock(AS, base);
1647
[6745592]1648 if (src_area) {
[b6f3e7e]1649 size = P2SZ(src_area->pages);
[1068f6a]1650 mutex_unlock(&src_area->lock);
[da1bafb]1651 } else
[7c23af9]1652 size = 0;
[da1bafb]1653
[1d432f9]1654 page_table_unlock(AS, true);
[7c23af9]1655 return size;
1656}
1657
[25bf215]1658/** Mark portion of address space area as used.
1659 *
1660 * The address space area must be already locked.
1661 *
[da1bafb]1662 * @param area Address space area.
1663 * @param page First page to be marked.
1664 * @param count Number of page to be marked.
1665 *
[fc47885]1666 * @return False on failure or true on success.
[25bf215]1667 *
1668 */
[fc47885]1669bool used_space_insert(as_area_t *area, uintptr_t page, size_t count)
[25bf215]1670{
[1d432f9]1671 ASSERT(mutex_locked(&area->lock));
[25bf215]1672 ASSERT(page == ALIGN_DOWN(page, PAGE_SIZE));
1673 ASSERT(count);
[da1bafb]1674
1675 btree_node_t *leaf;
1676 size_t pages = (size_t) btree_search(&area->used_space, page, &leaf);
[25bf215]1677 if (pages) {
1678 /*
1679 * We hit the beginning of some used space.
1680 */
[fc47885]1681 return false;
[25bf215]1682 }
[da1bafb]1683
[a6cb8cb]1684 if (!leaf->keys) {
[da1bafb]1685 btree_insert(&area->used_space, page, (void *) count, leaf);
[fc47885]1686 goto success;
[a6cb8cb]1687 }
[da1bafb]1688
1689 btree_node_t *node = btree_leaf_node_left_neighbour(&area->used_space, leaf);
[25bf215]1690 if (node) {
[6f4495f5]1691 uintptr_t left_pg = node->key[node->keys - 1];
1692 uintptr_t right_pg = leaf->key[0];
[98000fb]1693 size_t left_cnt = (size_t) node->value[node->keys - 1];
1694 size_t right_cnt = (size_t) leaf->value[0];
[25bf215]1695
1696 /*
1697 * Examine the possibility that the interval fits
1698 * somewhere between the rightmost interval of
1699 * the left neigbour and the first interval of the leaf.
1700 */
[da1bafb]1701
[25bf215]1702 if (page >= right_pg) {
1703 /* Do nothing. */
[b6f3e7e]1704 } else if (overlaps(page, P2SZ(count), left_pg,
1705 P2SZ(left_cnt))) {
[25bf215]1706 /* The interval intersects with the left interval. */
[fc47885]1707 return false;
[b6f3e7e]1708 } else if (overlaps(page, P2SZ(count), right_pg,
1709 P2SZ(right_cnt))) {
[25bf215]1710 /* The interval intersects with the right interval. */
[fc47885]1711 return false;
[b6f3e7e]1712 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1713 (page + P2SZ(count) == right_pg)) {
[6f4495f5]1714 /*
1715 * The interval can be added by merging the two already
1716 * present intervals.
1717 */
[56789125]1718 node->value[node->keys - 1] += count + right_cnt;
[da1bafb]1719 btree_remove(&area->used_space, right_pg, leaf);
[fc47885]1720 goto success;
[b6f3e7e]1721 } else if (page == left_pg + P2SZ(left_cnt)) {
[da1bafb]1722 /*
[6f4495f5]1723 * The interval can be added by simply growing the left
1724 * interval.
1725 */
[56789125]1726 node->value[node->keys - 1] += count;
[fc47885]1727 goto success;
[b6f3e7e]1728 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1729 /*
[6f4495f5]1730 * The interval can be addded by simply moving base of
1731 * the right interval down and increasing its size
1732 * accordingly.
[25bf215]1733 */
[56789125]1734 leaf->value[0] += count;
[25bf215]1735 leaf->key[0] = page;
[fc47885]1736 goto success;
[25bf215]1737 } else {
1738 /*
1739 * The interval is between both neigbouring intervals,
1740 * but cannot be merged with any of them.
1741 */
[da1bafb]1742 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1743 leaf);
[fc47885]1744 goto success;
[25bf215]1745 }
1746 } else if (page < leaf->key[0]) {
[7f1c620]1747 uintptr_t right_pg = leaf->key[0];
[98000fb]1748 size_t right_cnt = (size_t) leaf->value[0];
[da1bafb]1749
[25bf215]1750 /*
[6f4495f5]1751 * Investigate the border case in which the left neighbour does
1752 * not exist but the interval fits from the left.
[25bf215]1753 */
[da1bafb]1754
[b6f3e7e]1755 if (overlaps(page, P2SZ(count), right_pg, P2SZ(right_cnt))) {
[25bf215]1756 /* The interval intersects with the right interval. */
[fc47885]1757 return false;
[b6f3e7e]1758 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1759 /*
[6f4495f5]1760 * The interval can be added by moving the base of the
1761 * right interval down and increasing its size
1762 * accordingly.
[25bf215]1763 */
1764 leaf->key[0] = page;
[56789125]1765 leaf->value[0] += count;
[fc47885]1766 goto success;
[25bf215]1767 } else {
1768 /*
1769 * The interval doesn't adjoin with the right interval.
1770 * It must be added individually.
1771 */
[da1bafb]1772 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1773 leaf);
[fc47885]1774 goto success;
[25bf215]1775 }
1776 }
[da1bafb]1777
1778 node = btree_leaf_node_right_neighbour(&area->used_space, leaf);
[25bf215]1779 if (node) {
[6f4495f5]1780 uintptr_t left_pg = leaf->key[leaf->keys - 1];
1781 uintptr_t right_pg = node->key[0];
[98000fb]1782 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
1783 size_t right_cnt = (size_t) node->value[0];
[25bf215]1784
1785 /*
1786 * Examine the possibility that the interval fits
1787 * somewhere between the leftmost interval of
1788 * the right neigbour and the last interval of the leaf.
1789 */
[da1bafb]1790
[25bf215]1791 if (page < left_pg) {
1792 /* Do nothing. */
[b6f3e7e]1793 } else if (overlaps(page, P2SZ(count), left_pg,
1794 P2SZ(left_cnt))) {
[25bf215]1795 /* The interval intersects with the left interval. */
[fc47885]1796 return false;
[b6f3e7e]1797 } else if (overlaps(page, P2SZ(count), right_pg,
1798 P2SZ(right_cnt))) {
[25bf215]1799 /* The interval intersects with the right interval. */
[fc47885]1800 return false;
[b6f3e7e]1801 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1802 (page + P2SZ(count) == right_pg)) {
[6f4495f5]1803 /*
1804 * The interval can be added by merging the two already
1805 * present intervals.
[da1bafb]1806 */
[56789125]1807 leaf->value[leaf->keys - 1] += count + right_cnt;
[da1bafb]1808 btree_remove(&area->used_space, right_pg, node);
[fc47885]1809 goto success;
[b6f3e7e]1810 } else if (page == left_pg + P2SZ(left_cnt)) {
[6f4495f5]1811 /*
1812 * The interval can be added by simply growing the left
1813 * interval.
[da1bafb]1814 */
[fc47885]1815 leaf->value[leaf->keys - 1] += count;
1816 goto success;
[b6f3e7e]1817 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1818 /*
[6f4495f5]1819 * The interval can be addded by simply moving base of
1820 * the right interval down and increasing its size
1821 * accordingly.
[25bf215]1822 */
[56789125]1823 node->value[0] += count;
[25bf215]1824 node->key[0] = page;
[fc47885]1825 goto success;
[25bf215]1826 } else {
1827 /*
1828 * The interval is between both neigbouring intervals,
1829 * but cannot be merged with any of them.
1830 */
[da1bafb]1831 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1832 leaf);
[fc47885]1833 goto success;
[25bf215]1834 }
1835 } else if (page >= leaf->key[leaf->keys - 1]) {
[7f1c620]1836 uintptr_t left_pg = leaf->key[leaf->keys - 1];
[98000fb]1837 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
[da1bafb]1838
[25bf215]1839 /*
[6f4495f5]1840 * Investigate the border case in which the right neighbour
1841 * does not exist but the interval fits from the right.
[25bf215]1842 */
[da1bafb]1843
[b6f3e7e]1844 if (overlaps(page, P2SZ(count), left_pg, P2SZ(left_cnt))) {
[56789125]1845 /* The interval intersects with the left interval. */
[fc47885]1846 return false;
[b6f3e7e]1847 } else if (left_pg + P2SZ(left_cnt) == page) {
[6f4495f5]1848 /*
1849 * The interval can be added by growing the left
1850 * interval.
1851 */
[56789125]1852 leaf->value[leaf->keys - 1] += count;
[fc47885]1853 goto success;
[25bf215]1854 } else {
1855 /*
1856 * The interval doesn't adjoin with the left interval.
1857 * It must be added individually.
1858 */
[da1bafb]1859 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1860 leaf);
[fc47885]1861 goto success;
[25bf215]1862 }
1863 }
1864
1865 /*
[6f4495f5]1866 * Note that if the algorithm made it thus far, the interval can fit
1867 * only between two other intervals of the leaf. The two border cases
1868 * were already resolved.
[25bf215]1869 */
[da1bafb]1870 btree_key_t i;
[25bf215]1871 for (i = 1; i < leaf->keys; i++) {
1872 if (page < leaf->key[i]) {
[6f4495f5]1873 uintptr_t left_pg = leaf->key[i - 1];
1874 uintptr_t right_pg = leaf->key[i];
[98000fb]1875 size_t left_cnt = (size_t) leaf->value[i - 1];
1876 size_t right_cnt = (size_t) leaf->value[i];
[da1bafb]1877
[25bf215]1878 /*
1879 * The interval fits between left_pg and right_pg.
1880 */
[da1bafb]1881
[b6f3e7e]1882 if (overlaps(page, P2SZ(count), left_pg,
1883 P2SZ(left_cnt))) {
[6f4495f5]1884 /*
1885 * The interval intersects with the left
1886 * interval.
1887 */
[fc47885]1888 return false;
[b6f3e7e]1889 } else if (overlaps(page, P2SZ(count), right_pg,
1890 P2SZ(right_cnt))) {
[6f4495f5]1891 /*
1892 * The interval intersects with the right
1893 * interval.
1894 */
[fc47885]1895 return false;
[b6f3e7e]1896 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1897 (page + P2SZ(count) == right_pg)) {
[6f4495f5]1898 /*
1899 * The interval can be added by merging the two
1900 * already present intervals.
1901 */
[56789125]1902 leaf->value[i - 1] += count + right_cnt;
[da1bafb]1903 btree_remove(&area->used_space, right_pg, leaf);
[fc47885]1904 goto success;
[b6f3e7e]1905 } else if (page == left_pg + P2SZ(left_cnt)) {
[6f4495f5]1906 /*
1907 * The interval can be added by simply growing
1908 * the left interval.
1909 */
[56789125]1910 leaf->value[i - 1] += count;
[fc47885]1911 goto success;
[b6f3e7e]1912 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1913 /*
[da1bafb]1914 * The interval can be addded by simply moving
[6f4495f5]1915 * base of the right interval down and
1916 * increasing its size accordingly.
[da1bafb]1917 */
[56789125]1918 leaf->value[i] += count;
[25bf215]1919 leaf->key[i] = page;
[fc47885]1920 goto success;
[25bf215]1921 } else {
1922 /*
[6f4495f5]1923 * The interval is between both neigbouring
1924 * intervals, but cannot be merged with any of
1925 * them.
[25bf215]1926 */
[da1bafb]1927 btree_insert(&area->used_space, page,
[6f4495f5]1928 (void *) count, leaf);
[fc47885]1929 goto success;
[25bf215]1930 }
1931 }
1932 }
[da1bafb]1933
[7e752b2]1934 panic("Inconsistency detected while adding %zu pages of used "
1935 "space at %p.", count, (void *) page);
[fc47885]1936
1937success:
1938 area->resident += count;
1939 return true;
[25bf215]1940}
1941
1942/** Mark portion of address space area as unused.
1943 *
1944 * The address space area must be already locked.
1945 *
[da1bafb]1946 * @param area Address space area.
1947 * @param page First page to be marked.
1948 * @param count Number of page to be marked.
1949 *
[fc47885]1950 * @return False on failure or true on success.
[25bf215]1951 *
1952 */
[fc47885]1953bool used_space_remove(as_area_t *area, uintptr_t page, size_t count)
[25bf215]1954{
[1d432f9]1955 ASSERT(mutex_locked(&area->lock));
[25bf215]1956 ASSERT(page == ALIGN_DOWN(page, PAGE_SIZE));
1957 ASSERT(count);
[da1bafb]1958
1959 btree_node_t *leaf;
1960 size_t pages = (size_t) btree_search(&area->used_space, page, &leaf);
[25bf215]1961 if (pages) {
1962 /*
1963 * We are lucky, page is the beginning of some interval.
1964 */
1965 if (count > pages) {
[fc47885]1966 return false;
[25bf215]1967 } else if (count == pages) {
[da1bafb]1968 btree_remove(&area->used_space, page, leaf);
[fc47885]1969 goto success;
[25bf215]1970 } else {
1971 /*
1972 * Find the respective interval.
1973 * Decrease its size and relocate its start address.
1974 */
[da1bafb]1975 btree_key_t i;
[25bf215]1976 for (i = 0; i < leaf->keys; i++) {
1977 if (leaf->key[i] == page) {
[b6f3e7e]1978 leaf->key[i] += P2SZ(count);
[56789125]1979 leaf->value[i] -= count;
[fc47885]1980 goto success;
[25bf215]1981 }
1982 }
[fc47885]1983
[25bf215]1984 goto error;
1985 }
1986 }
[da1bafb]1987
[b6f3e7e]1988 btree_node_t *node = btree_leaf_node_left_neighbour(&area->used_space,
1989 leaf);
[da1bafb]1990 if ((node) && (page < leaf->key[0])) {
[7f1c620]1991 uintptr_t left_pg = node->key[node->keys - 1];
[98000fb]1992 size_t left_cnt = (size_t) node->value[node->keys - 1];
[da1bafb]1993
[b6f3e7e]1994 if (overlaps(left_pg, P2SZ(left_cnt), page, P2SZ(count))) {
1995 if (page + P2SZ(count) == left_pg + P2SZ(left_cnt)) {
[25bf215]1996 /*
[6f4495f5]1997 * The interval is contained in the rightmost
1998 * interval of the left neighbour and can be
1999 * removed by updating the size of the bigger
2000 * interval.
[25bf215]2001 */
[56789125]2002 node->value[node->keys - 1] -= count;
[fc47885]2003 goto success;
[b6f3e7e]2004 } else if (page + P2SZ(count) <
2005 left_pg + P2SZ(left_cnt)) {
2006 size_t new_cnt;
2007
[25bf215]2008 /*
[6f4495f5]2009 * The interval is contained in the rightmost
2010 * interval of the left neighbour but its
2011 * removal requires both updating the size of
2012 * the original interval and also inserting a
2013 * new interval.
[25bf215]2014 */
[b6f3e7e]2015 new_cnt = ((left_pg + P2SZ(left_cnt)) -
2016 (page + P2SZ(count))) >> PAGE_WIDTH;
[56789125]2017 node->value[node->keys - 1] -= count + new_cnt;
[da1bafb]2018 btree_insert(&area->used_space, page +
[b6f3e7e]2019 P2SZ(count), (void *) new_cnt, leaf);
[fc47885]2020 goto success;
[25bf215]2021 }
2022 }
[fc47885]2023
2024 return false;
[da1bafb]2025 } else if (page < leaf->key[0])
[fc47885]2026 return false;
[25bf215]2027
2028 if (page > leaf->key[leaf->keys - 1]) {
[7f1c620]2029 uintptr_t left_pg = leaf->key[leaf->keys - 1];
[98000fb]2030 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
[da1bafb]2031
[b6f3e7e]2032 if (overlaps(left_pg, P2SZ(left_cnt), page, P2SZ(count))) {
2033 if (page + P2SZ(count) == left_pg + P2SZ(left_cnt)) {
[25bf215]2034 /*
[6f4495f5]2035 * The interval is contained in the rightmost
2036 * interval of the leaf and can be removed by
2037 * updating the size of the bigger interval.
[25bf215]2038 */
[56789125]2039 leaf->value[leaf->keys - 1] -= count;
[fc47885]2040 goto success;
[b6f3e7e]2041 } else if (page + P2SZ(count) < left_pg +
2042 P2SZ(left_cnt)) {
2043 size_t new_cnt;
2044
[25bf215]2045 /*
[6f4495f5]2046 * The interval is contained in the rightmost
2047 * interval of the leaf but its removal
2048 * requires both updating the size of the
2049 * original interval and also inserting a new
2050 * interval.
[25bf215]2051 */
[b6f3e7e]2052 new_cnt = ((left_pg + P2SZ(left_cnt)) -
2053 (page + P2SZ(count))) >> PAGE_WIDTH;
[56789125]2054 leaf->value[leaf->keys - 1] -= count + new_cnt;
[da1bafb]2055 btree_insert(&area->used_space, page +
[b6f3e7e]2056 P2SZ(count), (void *) new_cnt, leaf);
[fc47885]2057 goto success;
[25bf215]2058 }
2059 }
[fc47885]2060
2061 return false;
[da1bafb]2062 }
[25bf215]2063
2064 /*
2065 * The border cases have been already resolved.
[fc47885]2066 * Now the interval can be only between intervals of the leaf.
[25bf215]2067 */
[da1bafb]2068 btree_key_t i;
[25bf215]2069 for (i = 1; i < leaf->keys - 1; i++) {
2070 if (page < leaf->key[i]) {
[7f1c620]2071 uintptr_t left_pg = leaf->key[i - 1];
[98000fb]2072 size_t left_cnt = (size_t) leaf->value[i - 1];
[da1bafb]2073
[25bf215]2074 /*
[6f4495f5]2075 * Now the interval is between intervals corresponding
2076 * to (i - 1) and i.
[25bf215]2077 */
[b6f3e7e]2078 if (overlaps(left_pg, P2SZ(left_cnt), page,
2079 P2SZ(count))) {
2080 if (page + P2SZ(count) ==
2081 left_pg + P2SZ(left_cnt)) {
[25bf215]2082 /*
[6f4495f5]2083 * The interval is contained in the
2084 * interval (i - 1) of the leaf and can
2085 * be removed by updating the size of
2086 * the bigger interval.
[25bf215]2087 */
[56789125]2088 leaf->value[i - 1] -= count;
[fc47885]2089 goto success;
[b6f3e7e]2090 } else if (page + P2SZ(count) <
2091 left_pg + P2SZ(left_cnt)) {
2092 size_t new_cnt;
2093
[25bf215]2094 /*
[6f4495f5]2095 * The interval is contained in the
2096 * interval (i - 1) of the leaf but its
2097 * removal requires both updating the
2098 * size of the original interval and
[25bf215]2099 * also inserting a new interval.
2100 */
[b6f3e7e]2101 new_cnt = ((left_pg + P2SZ(left_cnt)) -
2102 (page + P2SZ(count))) >>
[6f4495f5]2103 PAGE_WIDTH;
[56789125]2104 leaf->value[i - 1] -= count + new_cnt;
[da1bafb]2105 btree_insert(&area->used_space, page +
[b6f3e7e]2106 P2SZ(count), (void *) new_cnt,
[6f4495f5]2107 leaf);
[fc47885]2108 goto success;
[25bf215]2109 }
2110 }
[fc47885]2111
2112 return false;
[25bf215]2113 }
2114 }
[da1bafb]2115
[25bf215]2116error:
[7e752b2]2117 panic("Inconsistency detected while removing %zu pages of used "
2118 "space from %p.", count, (void *) page);
[fc47885]2119
2120success:
2121 area->resident -= count;
2122 return true;
[25bf215]2123}
2124
[df0103f7]2125/*
2126 * Address space related syscalls.
2127 */
2128
[fbcdeb8]2129sysarg_t sys_as_area_create(uintptr_t base, size_t size, unsigned int flags,
2130 uintptr_t bound)
[df0103f7]2131{
[fbcdeb8]2132 uintptr_t virt = base;
2133 as_area_t *area = as_area_create(AS, flags | AS_AREA_CACHEABLE, size,
2134 AS_AREA_ATTR_NONE, &anon_backend, NULL, &virt, bound);
2135 if (area == NULL)
[96b02eb9]2136 return (sysarg_t) -1;
[fbcdeb8]2137
2138 return (sysarg_t) virt;
[df0103f7]2139}
2140
[96b02eb9]2141sysarg_t sys_as_area_resize(uintptr_t address, size_t size, unsigned int flags)
[df0103f7]2142{
[96b02eb9]2143 return (sysarg_t) as_area_resize(AS, address, size, 0);
[7242a78e]2144}
2145
[96b02eb9]2146sysarg_t sys_as_area_change_flags(uintptr_t address, unsigned int flags)
[c98e6ee]2147{
[96b02eb9]2148 return (sysarg_t) as_area_change_flags(AS, flags, address);
[c98e6ee]2149}
2150
[96b02eb9]2151sysarg_t sys_as_area_destroy(uintptr_t address)
[7242a78e]2152{
[96b02eb9]2153 return (sysarg_t) as_area_destroy(AS, address);
[df0103f7]2154}
[b45c443]2155
[336db295]2156/** Get list of adress space areas.
2157 *
[da1bafb]2158 * @param as Address space.
2159 * @param obuf Place to save pointer to returned buffer.
2160 * @param osize Place to save size of returned buffer.
2161 *
[336db295]2162 */
2163void as_get_area_info(as_t *as, as_area_info_t **obuf, size_t *osize)
2164{
2165 mutex_lock(&as->lock);
[da1bafb]2166
[336db295]2167 /* First pass, count number of areas. */
[da1bafb]2168
2169 size_t area_cnt = 0;
2170
[55b77d9]2171 list_foreach(as->as_area_btree.leaf_list, cur) {
[da1bafb]2172 btree_node_t *node =
2173 list_get_instance(cur, btree_node_t, leaf_link);
[336db295]2174 area_cnt += node->keys;
2175 }
[da1bafb]2176
2177 size_t isize = area_cnt * sizeof(as_area_info_t);
2178 as_area_info_t *info = malloc(isize, 0);
2179
[336db295]2180 /* Second pass, record data. */
[da1bafb]2181
2182 size_t area_idx = 0;
2183
[55b77d9]2184 list_foreach(as->as_area_btree.leaf_list, cur) {
[da1bafb]2185 btree_node_t *node =
2186 list_get_instance(cur, btree_node_t, leaf_link);
2187 btree_key_t i;
2188
[336db295]2189 for (i = 0; i < node->keys; i++) {
2190 as_area_t *area = node->value[i];
[da1bafb]2191
[336db295]2192 ASSERT(area_idx < area_cnt);
2193 mutex_lock(&area->lock);
[da1bafb]2194
[336db295]2195 info[area_idx].start_addr = area->base;
[b6f3e7e]2196 info[area_idx].size = P2SZ(area->pages);
[336db295]2197 info[area_idx].flags = area->flags;
2198 ++area_idx;
[da1bafb]2199
[336db295]2200 mutex_unlock(&area->lock);
2201 }
2202 }
[da1bafb]2203
[336db295]2204 mutex_unlock(&as->lock);
[da1bafb]2205
[336db295]2206 *obuf = info;
2207 *osize = isize;
2208}
2209
[64c2ad5]2210/** Print out information about address space.
2211 *
[da1bafb]2212 * @param as Address space.
2213 *
[64c2ad5]2214 */
2215void as_print(as_t *as)
2216{
2217 mutex_lock(&as->lock);
2218
[0b37882]2219 /* Print out info about address space areas */
[55b77d9]2220 list_foreach(as->as_area_btree.leaf_list, cur) {
[da1bafb]2221 btree_node_t *node
2222 = list_get_instance(cur, btree_node_t, leaf_link);
2223 btree_key_t i;
[64c2ad5]2224
2225 for (i = 0; i < node->keys; i++) {
[7ba7c6d]2226 as_area_t *area = node->value[i];
[da1bafb]2227
[64c2ad5]2228 mutex_lock(&area->lock);
[7e752b2]2229 printf("as_area: %p, base=%p, pages=%zu"
2230 " (%p - %p)\n", area, (void *) area->base,
2231 area->pages, (void *) area->base,
[b6f3e7e]2232 (void *) (area->base + P2SZ(area->pages)));
[64c2ad5]2233 mutex_unlock(&area->lock);
2234 }
2235 }
2236
2237 mutex_unlock(&as->lock);
2238}
2239
[cc73a8a1]2240/** @}
[b45c443]2241 */
Note: See TracBrowser for help on using the repository browser.