source: mainline/kernel/generic/src/mm/as.c@ 39b13f8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 39b13f8 was feeac0d, checked in by Jiri Svoboda <jiri@…>, 12 years ago

Simplify use of list_foreach.

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