source: mainline/kernel/generic/src/mm/as.c@ 3b8a990

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

Define two new as area backend callbacks.

  • Add is_resizable(as_area_t *)
  • Add is_shareable(as_area_t *)
  • Remove special knowledge of the phys backend from as_area_resize()
  • 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.
428 * Check if it doesn't conflict with kernel address space.
429 */
430 if (!KERNEL_ADDRESS_SPACE_SHADOWED) {
[b6f3e7e]431 return !overlaps(addr, P2SZ(count), KERNEL_ADDRESS_SPACE_START,
[e3ee9b9]432 KERNEL_ADDRESS_SPACE_END - KERNEL_ADDRESS_SPACE_START);
433 }
434
435 return true;
436}
437
[fbcdeb8]438/** Return pointer to unmapped address space area
439 *
440 * The address space must be already locked when calling
441 * this function.
442 *
[35a3d950]443 * @param as Address space.
444 * @param bound Lowest address bound.
445 * @param size Requested size of the allocation.
446 * @param guarded True if the allocation must be protected by guard pages.
[fbcdeb8]447 *
448 * @return Address of the beginning of unmapped address space area.
449 * @return -1 if no suitable address space area was found.
450 *
451 */
452NO_TRACE static uintptr_t as_get_unmapped_area(as_t *as, uintptr_t bound,
[35a3d950]453 size_t size, bool guarded)
[fbcdeb8]454{
455 ASSERT(mutex_locked(&as->lock));
456
457 if (size == 0)
458 return (uintptr_t) -1;
459
460 /*
461 * Make sure we allocate from page-aligned
462 * address. Check for possible overflow in
463 * each step.
464 */
465
466 size_t pages = SIZE2FRAMES(size);
467
468 /*
469 * Find the lowest unmapped address aligned on the size
470 * boundary, not smaller than bound and of the required size.
471 */
472
473 /* First check the bound address itself */
474 uintptr_t addr = ALIGN_UP(bound, PAGE_SIZE);
[35a3d950]475 if (addr >= bound) {
476 if (guarded) {
477 /* Leave an unmapped page between the lower
478 * bound and the area's start address.
479 */
480 addr += P2SZ(1);
481 }
482
483 if (check_area_conflicts(as, addr, pages, guarded, NULL))
484 return addr;
485 }
[fbcdeb8]486
487 /* Eventually check the addresses behind each area */
488 list_foreach(as->as_area_btree.leaf_list, cur) {
489 btree_node_t *node =
490 list_get_instance(cur, btree_node_t, leaf_link);
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{
[fbcdeb8]544 if ((*base != (uintptr_t) -1) && ((*base % PAGE_SIZE) != 0))
[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{
[1068f6a]688 mutex_lock(&as->lock);
[df0103f7]689
690 /*
691 * Locate the area.
692 */
[da1bafb]693 as_area_t *area = find_area_and_lock(as, address);
[df0103f7]694 if (!area) {
[1068f6a]695 mutex_unlock(&as->lock);
[7242a78e]696 return ENOENT;
[df0103f7]697 }
[01029fc]698
699 if (!area->backend->is_resizable(area)) {
[df0103f7]700 /*
[01029fc]701 * The backend does not support resizing for this area.
[df0103f7]702 */
[1068f6a]703 mutex_unlock(&area->lock);
704 mutex_unlock(&as->lock);
[7242a78e]705 return ENOTSUP;
[df0103f7]706 }
[da1bafb]707
[8182031]708 if (area->sh_info) {
709 /*
[da1bafb]710 * Remapping of shared address space areas
[8182031]711 * is not supported.
712 */
713 mutex_unlock(&area->lock);
714 mutex_unlock(&as->lock);
715 return ENOTSUP;
716 }
[da1bafb]717
718 size_t pages = SIZE2FRAMES((address - area->base) + size);
[df0103f7]719 if (!pages) {
720 /*
721 * Zero size address space areas are not allowed.
722 */
[1068f6a]723 mutex_unlock(&area->lock);
724 mutex_unlock(&as->lock);
[7242a78e]725 return EPERM;
[df0103f7]726 }
727
728 if (pages < area->pages) {
[b6f3e7e]729 uintptr_t start_free = area->base + P2SZ(pages);
[da1bafb]730
[df0103f7]731 /*
732 * Shrinking the area.
733 * No need to check for overlaps.
734 */
[da1bafb]735
[c964521]736 page_table_lock(as, false);
[da1bafb]737
[56789125]738 /*
739 * Remove frames belonging to used space starting from
740 * the highest addresses downwards until an overlap with
741 * the resized address space area is found. Note that this
742 * is also the right way to remove part of the used_space
743 * B+tree leaf list.
[da1bafb]744 */
745 bool cond = true;
746 while (cond) {
[55b77d9]747 ASSERT(!list_empty(&area->used_space.leaf_list));
[da1bafb]748
749 btree_node_t *node =
[55b77d9]750 list_get_instance(list_last(&area->used_space.leaf_list),
[6f4495f5]751 btree_node_t, leaf_link);
[da1bafb]752
[56789125]753 if ((cond = (bool) node->keys)) {
[da1bafb]754 uintptr_t ptr = node->key[node->keys - 1];
755 size_t size =
[98000fb]756 (size_t) node->value[node->keys - 1];
[da1bafb]757 size_t i = 0;
758
[b6f3e7e]759 if (overlaps(ptr, P2SZ(size), area->base,
760 P2SZ(pages))) {
[56789125]761
[b6f3e7e]762 if (ptr + P2SZ(size) <= start_free) {
[56789125]763 /*
[6f4495f5]764 * The whole interval fits
765 * completely in the resized
766 * address space area.
[56789125]767 */
768 break;
769 }
[da1bafb]770
[56789125]771 /*
[6f4495f5]772 * Part of the interval corresponding
773 * to b and c overlaps with the resized
774 * address space area.
[56789125]775 */
[da1bafb]776
777 /* We are almost done */
778 cond = false;
779 i = (start_free - ptr) >> PAGE_WIDTH;
[6745592]780 if (!used_space_remove(area, start_free,
[da1bafb]781 size - i))
782 panic("Cannot remove used space.");
[56789125]783 } else {
784 /*
[6f4495f5]785 * The interval of used space can be
786 * completely removed.
[56789125]787 */
[da1bafb]788 if (!used_space_remove(area, ptr, size))
789 panic("Cannot remove used space.");
[56789125]790 }
[da1bafb]791
[d67dfdc]792 /*
793 * Start TLB shootdown sequence.
794 *
795 * The sequence is rather short and can be
796 * repeated multiple times. The reason is that
797 * we don't want to have used_space_remove()
798 * inside the sequence as it may use a blocking
799 * memory allocation for its B+tree. Blocking
800 * while holding the tlblock spinlock is
801 * forbidden and would hit a kernel assertion.
802 */
803
804 ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES,
805 as->asid, area->base + P2SZ(pages),
806 area->pages - pages);
807
[da1bafb]808 for (; i < size; i++) {
[b6f3e7e]809 pte_t *pte = page_mapping_find(as,
[0ff03f3]810 ptr + P2SZ(i), false);
[da1bafb]811
812 ASSERT(pte);
813 ASSERT(PTE_VALID(pte));
814 ASSERT(PTE_PRESENT(pte));
815
816 if ((area->backend) &&
817 (area->backend->frame_free)) {
[0ee077ee]818 area->backend->frame_free(area,
[b6f3e7e]819 ptr + P2SZ(i),
[6f4495f5]820 PTE_GET_FRAME(pte));
[8182031]821 }
[da1bafb]822
[b6f3e7e]823 page_mapping_remove(as, ptr + P2SZ(i));
[56789125]824 }
[da1bafb]825
[d67dfdc]826 /*
827 * Finish TLB shootdown sequence.
828 */
[da1bafb]829
[d67dfdc]830 tlb_invalidate_pages(as->asid,
831 area->base + P2SZ(pages),
832 area->pages - pages);
[31d8e10]833
[d67dfdc]834 /*
835 * Invalidate software translation caches
836 * (e.g. TSB on sparc64, PHT on ppc32).
837 */
838 as_invalidate_translation_cache(as,
839 area->base + P2SZ(pages),
840 area->pages - pages);
841 tlb_shootdown_finalize(ipl);
842 }
843 }
[da1bafb]844 page_table_unlock(as, false);
[df0103f7]845 } else {
846 /*
847 * Growing the area.
[0941e9ae]848 */
849
[94795812]850 if (overflows_into_positive(address, P2SZ(pages)))
[0941e9ae]851 return EINVAL;
852
853 /*
[df0103f7]854 * Check for overlaps with other address space areas.
855 */
[35a3d950]856 bool const guarded = area->flags & AS_AREA_GUARD;
857 if (!check_area_conflicts(as, address, pages, guarded, area)) {
[1068f6a]858 mutex_unlock(&area->lock);
[da1bafb]859 mutex_unlock(&as->lock);
[7242a78e]860 return EADDRNOTAVAIL;
[df0103f7]861 }
[da1bafb]862 }
863
[e394b736]864 if (area->backend && area->backend->resize) {
865 if (!area->backend->resize(area, pages)) {
866 mutex_unlock(&area->lock);
867 mutex_unlock(&as->lock);
868 return ENOMEM;
869 }
870 }
871
[df0103f7]872 area->pages = pages;
873
[1068f6a]874 mutex_unlock(&area->lock);
875 mutex_unlock(&as->lock);
[da1bafb]876
[7242a78e]877 return 0;
878}
879
[e3ee9b9]880/** Remove reference to address space area share info.
881 *
882 * If the reference count drops to 0, the sh_info is deallocated.
883 *
884 * @param sh_info Pointer to address space area share info.
885 *
886 */
[7a0359b]887NO_TRACE static void sh_info_remove_reference(share_info_t *sh_info)
[e3ee9b9]888{
889 bool dealloc = false;
890
891 mutex_lock(&sh_info->lock);
892 ASSERT(sh_info->refcount);
893
894 if (--sh_info->refcount == 0) {
895 dealloc = true;
896
897 /*
898 * Now walk carefully the pagemap B+tree and free/remove
899 * reference from all frames found there.
900 */
[55b77d9]901 list_foreach(sh_info->pagemap.leaf_list, cur) {
[e3ee9b9]902 btree_node_t *node
903 = list_get_instance(cur, btree_node_t, leaf_link);
904 btree_key_t i;
905
906 for (i = 0; i < node->keys; i++)
907 frame_free((uintptr_t) node->value[i]);
908 }
909
910 }
911 mutex_unlock(&sh_info->lock);
912
913 if (dealloc) {
914 btree_destroy(&sh_info->pagemap);
915 free(sh_info);
916 }
917}
918
[7242a78e]919/** Destroy address space area.
920 *
[da1bafb]921 * @param as Address space.
922 * @param address Address within the area to be deleted.
923 *
924 * @return Zero on success or a value from @ref errno.h on failure.
[7242a78e]925 *
926 */
[7f1c620]927int as_area_destroy(as_t *as, uintptr_t address)
[7242a78e]928{
[1068f6a]929 mutex_lock(&as->lock);
[da1bafb]930
931 as_area_t *area = find_area_and_lock(as, address);
[7242a78e]932 if (!area) {
[1068f6a]933 mutex_unlock(&as->lock);
[7242a78e]934 return ENOENT;
935 }
[e394b736]936
937 if (area->backend && area->backend->destroy)
938 area->backend->destroy(area);
[da1bafb]939
940 uintptr_t base = area->base;
941
[c964521]942 page_table_lock(as, false);
[da1bafb]943
[5552d60]944 /*
945 * Start TLB shootdown sequence.
946 */
[402eda5]947 ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES, as->asid, area->base,
948 area->pages);
[da1bafb]949
[567807b1]950 /*
951 * Visit only the pages mapped by used_space B+tree.
952 */
[55b77d9]953 list_foreach(area->used_space.leaf_list, cur) {
[567807b1]954 btree_node_t *node;
[da1bafb]955 btree_key_t i;
[56789125]956
[f8d069e8]957 node = list_get_instance(cur, btree_node_t, leaf_link);
958 for (i = 0; i < node->keys; i++) {
[da1bafb]959 uintptr_t ptr = node->key[i];
960 size_t size;
[56789125]961
[da1bafb]962 for (size = 0; size < (size_t) node->value[i]; size++) {
[b6f3e7e]963 pte_t *pte = page_mapping_find(as,
[0ff03f3]964 ptr + P2SZ(size), false);
[da1bafb]965
966 ASSERT(pte);
967 ASSERT(PTE_VALID(pte));
968 ASSERT(PTE_PRESENT(pte));
969
970 if ((area->backend) &&
971 (area->backend->frame_free)) {
972 area->backend->frame_free(area,
[b6f3e7e]973 ptr + P2SZ(size),
974 PTE_GET_FRAME(pte));
[56789125]975 }
[da1bafb]976
[b6f3e7e]977 page_mapping_remove(as, ptr + P2SZ(size));
[7242a78e]978 }
979 }
980 }
[da1bafb]981
[7242a78e]982 /*
[5552d60]983 * Finish TLB shootdown sequence.
[7242a78e]984 */
[da1bafb]985
[f1d1f5d3]986 tlb_invalidate_pages(as->asid, area->base, area->pages);
[da1bafb]987
[f1d1f5d3]988 /*
[eef1b031]989 * Invalidate potential software translation caches
990 * (e.g. TSB on sparc64, PHT on ppc32).
[f1d1f5d3]991 */
992 as_invalidate_translation_cache(as, area->base, area->pages);
[402eda5]993 tlb_shootdown_finalize(ipl);
[da1bafb]994
[c964521]995 page_table_unlock(as, false);
[f1d1f5d3]996
[5552d60]997 btree_destroy(&area->used_space);
[da1bafb]998
[8d4f2ae]999 area->attributes |= AS_AREA_ATTR_PARTIAL;
[8182031]1000
1001 if (area->sh_info)
1002 sh_info_remove_reference(area->sh_info);
[da1bafb]1003
[1068f6a]1004 mutex_unlock(&area->lock);
[da1bafb]1005
[7242a78e]1006 /*
1007 * Remove the empty area from address space.
1008 */
[f1d1f5d3]1009 btree_remove(&as->as_area_btree, base, NULL);
[7242a78e]1010
[8d4f2ae]1011 free(area);
1012
[f1d1f5d3]1013 mutex_unlock(&as->lock);
[7242a78e]1014 return 0;
[df0103f7]1015}
1016
[8d6bc2d5]1017/** Share address space area with another or the same address space.
[df0103f7]1018 *
[0ee077ee]1019 * Address space area mapping is shared with a new address space area.
1020 * If the source address space area has not been shared so far,
1021 * a new sh_info is created. The new address space area simply gets the
1022 * sh_info of the source area. The process of duplicating the
1023 * mapping is done through the backend share function.
[da1bafb]1024 *
1025 * @param src_as Pointer to source address space.
1026 * @param src_base Base address of the source address space area.
1027 * @param acc_size Expected size of the source area.
1028 * @param dst_as Pointer to destination address space.
[fd4d8c0]1029 * @param dst_flags_mask Destination address space area flags mask.
[fbcdeb8]1030 * @param dst_base Target base address. If set to -1,
1031 * a suitable mappable area is found.
1032 * @param bound Lowest address bound if dst_base is set to -1.
1033 * Otherwise ignored.
[df0103f7]1034 *
[da1bafb]1035 * @return Zero on success.
1036 * @return ENOENT if there is no such task or such address space.
1037 * @return EPERM if there was a problem in accepting the area.
1038 * @return ENOMEM if there was a problem in allocating destination
1039 * address space area.
1040 * @return ENOTSUP if the address space area backend does not support
1041 * sharing.
1042 *
[df0103f7]1043 */
[7f1c620]1044int as_area_share(as_t *src_as, uintptr_t src_base, size_t acc_size,
[fbcdeb8]1045 as_t *dst_as, unsigned int dst_flags_mask, uintptr_t *dst_base,
1046 uintptr_t bound)
[df0103f7]1047{
[1068f6a]1048 mutex_lock(&src_as->lock);
[da1bafb]1049 as_area_t *src_area = find_area_and_lock(src_as, src_base);
[a9e8b39]1050 if (!src_area) {
[6fa476f7]1051 /*
1052 * Could not find the source address space area.
1053 */
[1068f6a]1054 mutex_unlock(&src_as->lock);
[6fa476f7]1055 return ENOENT;
1056 }
[da1bafb]1057
[01029fc]1058 if (!src_area->backend->is_shareable(src_area)) {
[8d6bc2d5]1059 /*
[01029fc]1060 * The backend does not permit sharing of this area.
[8d6bc2d5]1061 */
1062 mutex_unlock(&src_area->lock);
1063 mutex_unlock(&src_as->lock);
1064 return ENOTSUP;
1065 }
1066
[b6f3e7e]1067 size_t src_size = P2SZ(src_area->pages);
[da1bafb]1068 unsigned int src_flags = src_area->flags;
1069 mem_backend_t *src_backend = src_area->backend;
1070 mem_backend_data_t src_backend_data = src_area->backend_data;
1071
[1ec1fd8]1072 /* Share the cacheable flag from the original mapping */
1073 if (src_flags & AS_AREA_CACHEABLE)
1074 dst_flags_mask |= AS_AREA_CACHEABLE;
[da1bafb]1075
1076 if ((src_size != acc_size) ||
1077 ((src_flags & dst_flags_mask) != dst_flags_mask)) {
[8d6bc2d5]1078 mutex_unlock(&src_area->lock);
1079 mutex_unlock(&src_as->lock);
[df0103f7]1080 return EPERM;
1081 }
[da1bafb]1082
[8d6bc2d5]1083 /*
1084 * Now we are committed to sharing the area.
[8440473]1085 * First, prepare the area for sharing.
[8d6bc2d5]1086 * Then it will be safe to unlock it.
1087 */
[da1bafb]1088 share_info_t *sh_info = src_area->sh_info;
[8d6bc2d5]1089 if (!sh_info) {
1090 sh_info = (share_info_t *) malloc(sizeof(share_info_t), 0);
[08a19ba]1091 mutex_initialize(&sh_info->lock, MUTEX_PASSIVE);
[8d6bc2d5]1092 sh_info->refcount = 2;
1093 btree_create(&sh_info->pagemap);
1094 src_area->sh_info = sh_info;
[da1bafb]1095
[c0697c4c]1096 /*
1097 * Call the backend to setup sharing.
1098 */
1099 src_area->backend->share(src_area);
[8d6bc2d5]1100 } else {
1101 mutex_lock(&sh_info->lock);
1102 sh_info->refcount++;
1103 mutex_unlock(&sh_info->lock);
1104 }
[da1bafb]1105
[8d6bc2d5]1106 mutex_unlock(&src_area->lock);
1107 mutex_unlock(&src_as->lock);
[da1bafb]1108
[df0103f7]1109 /*
[a9e8b39]1110 * Create copy of the source address space area.
1111 * The destination area is created with AS_AREA_ATTR_PARTIAL
1112 * attribute set which prevents race condition with
1113 * preliminary as_page_fault() calls.
[fd4d8c0]1114 * The flags of the source area are masked against dst_flags_mask
1115 * to support sharing in less privileged mode.
[df0103f7]1116 */
[fbcdeb8]1117 as_area_t *dst_area = as_area_create(dst_as, dst_flags_mask,
1118 src_size, AS_AREA_ATTR_PARTIAL, src_backend,
1119 &src_backend_data, dst_base, bound);
[a9e8b39]1120 if (!dst_area) {
[df0103f7]1121 /*
1122 * Destination address space area could not be created.
1123 */
[8d6bc2d5]1124 sh_info_remove_reference(sh_info);
1125
[df0103f7]1126 return ENOMEM;
1127 }
[da1bafb]1128
[a9e8b39]1129 /*
1130 * Now the destination address space area has been
1131 * fully initialized. Clear the AS_AREA_ATTR_PARTIAL
[8d6bc2d5]1132 * attribute and set the sh_info.
[da1bafb]1133 */
1134 mutex_lock(&dst_as->lock);
[1068f6a]1135 mutex_lock(&dst_area->lock);
[a9e8b39]1136 dst_area->attributes &= ~AS_AREA_ATTR_PARTIAL;
[8d6bc2d5]1137 dst_area->sh_info = sh_info;
[1068f6a]1138 mutex_unlock(&dst_area->lock);
[da1bafb]1139 mutex_unlock(&dst_as->lock);
1140
[df0103f7]1141 return 0;
1142}
1143
[fb84455]1144/** Check access mode for address space area.
1145 *
[da1bafb]1146 * @param area Address space area.
1147 * @param access Access mode.
1148 *
1149 * @return False if access violates area's permissions, true
1150 * otherwise.
[fb84455]1151 *
1152 */
[97bdb4a]1153NO_TRACE bool as_area_check_access(as_area_t *area, pf_access_t access)
[fb84455]1154{
[fc47885]1155 ASSERT(mutex_locked(&area->lock));
1156
[fb84455]1157 int flagmap[] = {
1158 [PF_ACCESS_READ] = AS_AREA_READ,
1159 [PF_ACCESS_WRITE] = AS_AREA_WRITE,
1160 [PF_ACCESS_EXEC] = AS_AREA_EXEC
1161 };
[da1bafb]1162
[fb84455]1163 if (!(area->flags & flagmap[access]))
1164 return false;
1165
1166 return true;
1167}
1168
[e3ee9b9]1169/** Convert address space area flags to page flags.
1170 *
1171 * @param aflags Flags of some address space area.
1172 *
1173 * @return Flags to be passed to page_mapping_insert().
1174 *
1175 */
[7a0359b]1176NO_TRACE static unsigned int area_flags_to_page_flags(unsigned int aflags)
[e3ee9b9]1177{
1178 unsigned int flags = PAGE_USER | PAGE_PRESENT;
1179
1180 if (aflags & AS_AREA_READ)
1181 flags |= PAGE_READ;
1182
1183 if (aflags & AS_AREA_WRITE)
1184 flags |= PAGE_WRITE;
1185
1186 if (aflags & AS_AREA_EXEC)
1187 flags |= PAGE_EXEC;
1188
1189 if (aflags & AS_AREA_CACHEABLE)
1190 flags |= PAGE_CACHEABLE;
1191
1192 return flags;
1193}
1194
[6745592]1195/** Change adress space area flags.
[c98e6ee]1196 *
1197 * The idea is to have the same data, but with a different access mode.
1198 * This is needed e.g. for writing code into memory and then executing it.
1199 * In order for this to work properly, this may copy the data
1200 * into private anonymous memory (unless it's already there).
1201 *
[76fca31]1202 * @param as Address space.
1203 * @param flags Flags of the area memory.
1204 * @param address Address within the area to be changed.
1205 *
1206 * @return Zero on success or a value from @ref errno.h on failure.
[c98e6ee]1207 *
1208 */
[da1bafb]1209int as_area_change_flags(as_t *as, unsigned int flags, uintptr_t address)
[c98e6ee]1210{
1211 /* Flags for the new memory mapping */
[da1bafb]1212 unsigned int page_flags = area_flags_to_page_flags(flags);
1213
[c98e6ee]1214 mutex_lock(&as->lock);
[da1bafb]1215
1216 as_area_t *area = find_area_and_lock(as, address);
[c98e6ee]1217 if (!area) {
1218 mutex_unlock(&as->lock);
1219 return ENOENT;
1220 }
[da1bafb]1221
[76fca31]1222 if ((area->sh_info) || (area->backend != &anon_backend)) {
[c98e6ee]1223 /* Copying shared areas not supported yet */
1224 /* Copying non-anonymous memory not supported yet */
1225 mutex_unlock(&area->lock);
1226 mutex_unlock(&as->lock);
1227 return ENOTSUP;
1228 }
[da1bafb]1229
[c98e6ee]1230 /*
1231 * Compute total number of used pages in the used_space B+tree
1232 */
[da1bafb]1233 size_t used_pages = 0;
1234
[55b77d9]1235 list_foreach(area->used_space.leaf_list, cur) {
[da1bafb]1236 btree_node_t *node
1237 = list_get_instance(cur, btree_node_t, leaf_link);
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
[55b77d9]1261 list_foreach(area->used_space.leaf_list, cur) {
[b6f3e7e]1262 btree_node_t *node = list_get_instance(cur, btree_node_t,
1263 leaf_link);
[da1bafb]1264 btree_key_t i;
[c98e6ee]1265
1266 for (i = 0; i < node->keys; i++) {
[da1bafb]1267 uintptr_t ptr = node->key[i];
1268 size_t size;
[c98e6ee]1269
[da1bafb]1270 for (size = 0; size < (size_t) node->value[i]; size++) {
[b6f3e7e]1271 pte_t *pte = page_mapping_find(as,
[0ff03f3]1272 ptr + P2SZ(size), false);
[da1bafb]1273
1274 ASSERT(pte);
1275 ASSERT(PTE_VALID(pte));
1276 ASSERT(PTE_PRESENT(pte));
1277
[c98e6ee]1278 old_frame[frame_idx++] = PTE_GET_FRAME(pte);
[da1bafb]1279
[c98e6ee]1280 /* Remove old mapping */
[b6f3e7e]1281 page_mapping_remove(as, ptr + P2SZ(size));
[c98e6ee]1282 }
1283 }
1284 }
[da1bafb]1285
[c98e6ee]1286 /*
1287 * Finish TLB shootdown sequence.
1288 */
[da1bafb]1289
[c98e6ee]1290 tlb_invalidate_pages(as->asid, area->base, area->pages);
[76fca31]1291
[c98e6ee]1292 /*
[eef1b031]1293 * Invalidate potential software translation caches
1294 * (e.g. TSB on sparc64, PHT on ppc32).
[c98e6ee]1295 */
1296 as_invalidate_translation_cache(as, area->base, area->pages);
[402eda5]1297 tlb_shootdown_finalize(ipl);
[da1bafb]1298
[c964521]1299 page_table_unlock(as, false);
[da1bafb]1300
[ae7f6fb]1301 /*
1302 * Set the new flags.
1303 */
1304 area->flags = flags;
[da1bafb]1305
[c98e6ee]1306 /*
1307 * Map pages back in with new flags. This step is kept separate
[6745592]1308 * so that the memory area could not be accesed with both the old and
1309 * the new flags at once.
[c98e6ee]1310 */
1311 frame_idx = 0;
[da1bafb]1312
[55b77d9]1313 list_foreach(area->used_space.leaf_list, cur) {
[da1bafb]1314 btree_node_t *node
1315 = list_get_instance(cur, btree_node_t, leaf_link);
1316 btree_key_t i;
[c98e6ee]1317
1318 for (i = 0; i < node->keys; i++) {
[da1bafb]1319 uintptr_t ptr = node->key[i];
1320 size_t size;
[c98e6ee]1321
[da1bafb]1322 for (size = 0; size < (size_t) node->value[i]; size++) {
[c98e6ee]1323 page_table_lock(as, false);
[da1bafb]1324
[c98e6ee]1325 /* Insert the new mapping */
[b6f3e7e]1326 page_mapping_insert(as, ptr + P2SZ(size),
[c98e6ee]1327 old_frame[frame_idx++], page_flags);
[da1bafb]1328
[c98e6ee]1329 page_table_unlock(as, false);
1330 }
1331 }
1332 }
[da1bafb]1333
[c98e6ee]1334 free(old_frame);
[da1bafb]1335
[c98e6ee]1336 mutex_unlock(&area->lock);
1337 mutex_unlock(&as->lock);
[da1bafb]1338
[c98e6ee]1339 return 0;
1340}
1341
[20d50a1]1342/** Handle page fault within the current address space.
1343 *
[6745592]1344 * This is the high-level page fault handler. It decides whether the page fault
1345 * can be resolved by any backend and if so, it invokes the backend to resolve
1346 * the page fault.
[8182031]1347 *
[20d50a1]1348 * Interrupts are assumed disabled.
1349 *
[da1bafb]1350 * @param page Faulting page.
1351 * @param access Access mode that caused the page fault (i.e.
1352 * read/write/exec).
1353 * @param istate Pointer to the interrupted state.
1354 *
1355 * @return AS_PF_FAULT on page fault.
1356 * @return AS_PF_OK on success.
1357 * @return AS_PF_DEFER if the fault was caused by copy_to_uspace()
1358 * or copy_from_uspace().
[20d50a1]1359 *
1360 */
[7f1c620]1361int as_page_fault(uintptr_t page, pf_access_t access, istate_t *istate)
[20d50a1]1362{
[1068f6a]1363 if (!THREAD)
[8182031]1364 return AS_PF_FAULT;
[7af8c0e]1365
1366 if (!AS)
1367 return AS_PF_FAULT;
1368
[1068f6a]1369 mutex_lock(&AS->lock);
[da1bafb]1370 as_area_t *area = find_area_and_lock(AS, page);
[20d50a1]1371 if (!area) {
1372 /*
1373 * No area contained mapping for 'page'.
1374 * Signal page fault to low-level handler.
1375 */
[1068f6a]1376 mutex_unlock(&AS->lock);
[e3c762cd]1377 goto page_fault;
[20d50a1]1378 }
[da1bafb]1379
[a9e8b39]1380 if (area->attributes & AS_AREA_ATTR_PARTIAL) {
1381 /*
1382 * The address space area is not fully initialized.
1383 * Avoid possible race by returning error.
1384 */
[1068f6a]1385 mutex_unlock(&area->lock);
1386 mutex_unlock(&AS->lock);
[da1bafb]1387 goto page_fault;
[a9e8b39]1388 }
[da1bafb]1389
1390 if ((!area->backend) || (!area->backend->page_fault)) {
[8182031]1391 /*
1392 * The address space area is not backed by any backend
1393 * or the backend cannot handle page faults.
1394 */
1395 mutex_unlock(&area->lock);
1396 mutex_unlock(&AS->lock);
[da1bafb]1397 goto page_fault;
[8182031]1398 }
[da1bafb]1399
[2299914]1400 page_table_lock(AS, false);
1401
1402 /*
[6745592]1403 * To avoid race condition between two page faults on the same address,
1404 * we need to make sure the mapping has not been already inserted.
[2299914]1405 */
[da1bafb]1406 pte_t *pte;
[0ff03f3]1407 if ((pte = page_mapping_find(AS, page, false))) {
[2299914]1408 if (PTE_PRESENT(pte)) {
[fb84455]1409 if (((access == PF_ACCESS_READ) && PTE_READABLE(pte)) ||
[6f4495f5]1410 (access == PF_ACCESS_WRITE && PTE_WRITABLE(pte)) ||
1411 (access == PF_ACCESS_EXEC && PTE_EXECUTABLE(pte))) {
[fb84455]1412 page_table_unlock(AS, false);
1413 mutex_unlock(&area->lock);
1414 mutex_unlock(&AS->lock);
1415 return AS_PF_OK;
1416 }
[2299914]1417 }
1418 }
[20d50a1]1419
1420 /*
[8182031]1421 * Resort to the backend page fault handler.
[20d50a1]1422 */
[0ee077ee]1423 if (area->backend->page_fault(area, page, access) != AS_PF_OK) {
[8182031]1424 page_table_unlock(AS, false);
1425 mutex_unlock(&area->lock);
1426 mutex_unlock(&AS->lock);
1427 goto page_fault;
1428 }
[20d50a1]1429
[8182031]1430 page_table_unlock(AS, false);
[1068f6a]1431 mutex_unlock(&area->lock);
1432 mutex_unlock(&AS->lock);
[e3c762cd]1433 return AS_PF_OK;
[da1bafb]1434
[e3c762cd]1435page_fault:
1436 if (THREAD->in_copy_from_uspace) {
1437 THREAD->in_copy_from_uspace = false;
[6f4495f5]1438 istate_set_retaddr(istate,
1439 (uintptr_t) &memcpy_from_uspace_failover_address);
[e3c762cd]1440 } else if (THREAD->in_copy_to_uspace) {
1441 THREAD->in_copy_to_uspace = false;
[6f4495f5]1442 istate_set_retaddr(istate,
1443 (uintptr_t) &memcpy_to_uspace_failover_address);
[e3c762cd]1444 } else {
1445 return AS_PF_FAULT;
1446 }
[da1bafb]1447
[e3c762cd]1448 return AS_PF_DEFER;
[20d50a1]1449}
1450
[7e4e532]1451/** Switch address spaces.
[1068f6a]1452 *
1453 * Note that this function cannot sleep as it is essentially a part of
[879585a3]1454 * scheduling. Sleeping here would lead to deadlock on wakeup. Another
1455 * thing which is forbidden in this context is locking the address space.
[20d50a1]1456 *
[7250d2c]1457 * When this function is entered, no spinlocks may be held.
[31d8e10]1458 *
[da1bafb]1459 * @param old Old address space or NULL.
1460 * @param new New address space.
1461 *
[20d50a1]1462 */
[80bcaed]1463void as_switch(as_t *old_as, as_t *new_as)
[20d50a1]1464{
[31d8e10]1465 DEADLOCK_PROBE_INIT(p_asidlock);
1466 preemption_disable();
[da1bafb]1467
[31d8e10]1468retry:
1469 (void) interrupts_disable();
1470 if (!spinlock_trylock(&asidlock)) {
[da1bafb]1471 /*
[31d8e10]1472 * Avoid deadlock with TLB shootdown.
1473 * We can enable interrupts here because
1474 * preemption is disabled. We should not be
1475 * holding any other lock.
1476 */
1477 (void) interrupts_enable();
1478 DEADLOCK_PROBE(p_asidlock, DEADLOCK_THRESHOLD);
1479 goto retry;
1480 }
1481 preemption_enable();
[da1bafb]1482
[7e4e532]1483 /*
1484 * First, take care of the old address space.
[da1bafb]1485 */
[80bcaed]1486 if (old_as) {
1487 ASSERT(old_as->cpu_refcount);
[da1bafb]1488
1489 if ((--old_as->cpu_refcount == 0) && (old_as != AS_KERNEL)) {
[7e4e532]1490 /*
1491 * The old address space is no longer active on
1492 * any processor. It can be appended to the
1493 * list of inactive address spaces with assigned
1494 * ASID.
1495 */
[2057572]1496 ASSERT(old_as->asid != ASID_INVALID);
[da1bafb]1497
[2057572]1498 list_append(&old_as->inactive_as_with_asid_link,
[55b77d9]1499 &inactive_as_with_asid_list);
[7e4e532]1500 }
[da1bafb]1501
[57da95c]1502 /*
1503 * Perform architecture-specific tasks when the address space
1504 * is being removed from the CPU.
1505 */
[80bcaed]1506 as_deinstall_arch(old_as);
[7e4e532]1507 }
[da1bafb]1508
[7e4e532]1509 /*
1510 * Second, prepare the new address space.
1511 */
[80bcaed]1512 if ((new_as->cpu_refcount++ == 0) && (new_as != AS_KERNEL)) {
[879585a3]1513 if (new_as->asid != ASID_INVALID)
[80bcaed]1514 list_remove(&new_as->inactive_as_with_asid_link);
[879585a3]1515 else
1516 new_as->asid = asid_get();
[7e4e532]1517 }
[da1bafb]1518
[80bcaed]1519#ifdef AS_PAGE_TABLE
1520 SET_PTL0_ADDRESS(new_as->genarch.page_table);
1521#endif
[7e4e532]1522
[20d50a1]1523 /*
1524 * Perform architecture-specific steps.
[4512d7e]1525 * (e.g. write ASID to hardware register etc.)
[20d50a1]1526 */
[80bcaed]1527 as_install_arch(new_as);
[da1bafb]1528
[879585a3]1529 spinlock_unlock(&asidlock);
[20d50a1]1530
[80bcaed]1531 AS = new_as;
[20d50a1]1532}
[6a3c9a7]1533
[df0103f7]1534/** Compute flags for virtual address translation subsytem.
1535 *
[da1bafb]1536 * @param area Address space area.
1537 *
1538 * @return Flags to be used in page_mapping_insert().
[df0103f7]1539 *
1540 */
[97bdb4a]1541NO_TRACE unsigned int as_area_get_flags(as_area_t *area)
[df0103f7]1542{
[1d432f9]1543 ASSERT(mutex_locked(&area->lock));
[fc47885]1544
[da1bafb]1545 return area_flags_to_page_flags(area->flags);
[df0103f7]1546}
1547
[ef67bab]1548/** Create page table.
1549 *
[6745592]1550 * Depending on architecture, create either address space private or global page
1551 * table.
[ef67bab]1552 *
[da1bafb]1553 * @param flags Flags saying whether the page table is for the kernel
1554 * address space.
1555 *
1556 * @return First entry of the page table.
[ef67bab]1557 *
1558 */
[97bdb4a]1559NO_TRACE pte_t *page_table_create(unsigned int flags)
[ef67bab]1560{
[bd1deed]1561 ASSERT(as_operations);
1562 ASSERT(as_operations->page_table_create);
1563
1564 return as_operations->page_table_create(flags);
[ef67bab]1565}
[d3e7ff4]1566
[482826d]1567/** Destroy page table.
1568 *
1569 * Destroy page table in architecture specific way.
1570 *
[da1bafb]1571 * @param page_table Physical address of PTL0.
1572 *
[482826d]1573 */
[97bdb4a]1574NO_TRACE void page_table_destroy(pte_t *page_table)
[482826d]1575{
[bd1deed]1576 ASSERT(as_operations);
1577 ASSERT(as_operations->page_table_destroy);
1578
1579 as_operations->page_table_destroy(page_table);
[482826d]1580}
1581
[2299914]1582/** Lock page table.
1583 *
1584 * This function should be called before any page_mapping_insert(),
1585 * page_mapping_remove() and page_mapping_find().
[da1bafb]1586 *
[2299914]1587 * Locking order is such that address space areas must be locked
1588 * prior to this call. Address space can be locked prior to this
1589 * call in which case the lock argument is false.
1590 *
[da1bafb]1591 * @param as Address space.
1592 * @param lock If false, do not attempt to lock as->lock.
1593 *
[2299914]1594 */
[97bdb4a]1595NO_TRACE void page_table_lock(as_t *as, bool lock)
[2299914]1596{
1597 ASSERT(as_operations);
1598 ASSERT(as_operations->page_table_lock);
[bd1deed]1599
[2299914]1600 as_operations->page_table_lock(as, lock);
1601}
1602
1603/** Unlock page table.
1604 *
[da1bafb]1605 * @param as Address space.
1606 * @param unlock If false, do not attempt to unlock as->lock.
1607 *
[2299914]1608 */
[97bdb4a]1609NO_TRACE void page_table_unlock(as_t *as, bool unlock)
[2299914]1610{
1611 ASSERT(as_operations);
1612 ASSERT(as_operations->page_table_unlock);
[bd1deed]1613
[2299914]1614 as_operations->page_table_unlock(as, unlock);
1615}
1616
[ada559c]1617/** Test whether page tables are locked.
1618 *
[e3ee9b9]1619 * @param as Address space where the page tables belong.
[ada559c]1620 *
[e3ee9b9]1621 * @return True if the page tables belonging to the address soace
1622 * are locked, otherwise false.
[ada559c]1623 */
[97bdb4a]1624NO_TRACE bool page_table_locked(as_t *as)
[ada559c]1625{
1626 ASSERT(as_operations);
1627 ASSERT(as_operations->page_table_locked);
1628
1629 return as_operations->page_table_locked(as);
1630}
1631
[b878df3]1632/** Return size of the address space area with given base.
1633 *
[1d432f9]1634 * @param base Arbitrary address inside the address space area.
[da1bafb]1635 *
1636 * @return Size of the address space area in bytes or zero if it
1637 * does not exist.
[b878df3]1638 *
1639 */
1640size_t as_area_get_size(uintptr_t base)
[7c23af9]1641{
1642 size_t size;
[da1bafb]1643
[1d432f9]1644 page_table_lock(AS, true);
[da1bafb]1645 as_area_t *src_area = find_area_and_lock(AS, base);
1646
[6745592]1647 if (src_area) {
[b6f3e7e]1648 size = P2SZ(src_area->pages);
[1068f6a]1649 mutex_unlock(&src_area->lock);
[da1bafb]1650 } else
[7c23af9]1651 size = 0;
[da1bafb]1652
[1d432f9]1653 page_table_unlock(AS, true);
[7c23af9]1654 return size;
1655}
1656
[25bf215]1657/** Mark portion of address space area as used.
1658 *
1659 * The address space area must be already locked.
1660 *
[da1bafb]1661 * @param area Address space area.
1662 * @param page First page to be marked.
1663 * @param count Number of page to be marked.
1664 *
[fc47885]1665 * @return False on failure or true on success.
[25bf215]1666 *
1667 */
[fc47885]1668bool used_space_insert(as_area_t *area, uintptr_t page, size_t count)
[25bf215]1669{
[1d432f9]1670 ASSERT(mutex_locked(&area->lock));
[25bf215]1671 ASSERT(page == ALIGN_DOWN(page, PAGE_SIZE));
1672 ASSERT(count);
[da1bafb]1673
1674 btree_node_t *leaf;
1675 size_t pages = (size_t) btree_search(&area->used_space, page, &leaf);
[25bf215]1676 if (pages) {
1677 /*
1678 * We hit the beginning of some used space.
1679 */
[fc47885]1680 return false;
[25bf215]1681 }
[da1bafb]1682
[a6cb8cb]1683 if (!leaf->keys) {
[da1bafb]1684 btree_insert(&area->used_space, page, (void *) count, leaf);
[fc47885]1685 goto success;
[a6cb8cb]1686 }
[da1bafb]1687
1688 btree_node_t *node = btree_leaf_node_left_neighbour(&area->used_space, leaf);
[25bf215]1689 if (node) {
[6f4495f5]1690 uintptr_t left_pg = node->key[node->keys - 1];
1691 uintptr_t right_pg = leaf->key[0];
[98000fb]1692 size_t left_cnt = (size_t) node->value[node->keys - 1];
1693 size_t right_cnt = (size_t) leaf->value[0];
[25bf215]1694
1695 /*
1696 * Examine the possibility that the interval fits
1697 * somewhere between the rightmost interval of
1698 * the left neigbour and the first interval of the leaf.
1699 */
[da1bafb]1700
[25bf215]1701 if (page >= right_pg) {
1702 /* Do nothing. */
[b6f3e7e]1703 } else if (overlaps(page, P2SZ(count), left_pg,
1704 P2SZ(left_cnt))) {
[25bf215]1705 /* The interval intersects with the left interval. */
[fc47885]1706 return false;
[b6f3e7e]1707 } else if (overlaps(page, P2SZ(count), right_pg,
1708 P2SZ(right_cnt))) {
[25bf215]1709 /* The interval intersects with the right interval. */
[fc47885]1710 return false;
[b6f3e7e]1711 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1712 (page + P2SZ(count) == right_pg)) {
[6f4495f5]1713 /*
1714 * The interval can be added by merging the two already
1715 * present intervals.
1716 */
[56789125]1717 node->value[node->keys - 1] += count + right_cnt;
[da1bafb]1718 btree_remove(&area->used_space, right_pg, leaf);
[fc47885]1719 goto success;
[b6f3e7e]1720 } else if (page == left_pg + P2SZ(left_cnt)) {
[da1bafb]1721 /*
[6f4495f5]1722 * The interval can be added by simply growing the left
1723 * interval.
1724 */
[56789125]1725 node->value[node->keys - 1] += count;
[fc47885]1726 goto success;
[b6f3e7e]1727 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1728 /*
[6f4495f5]1729 * The interval can be addded by simply moving base of
1730 * the right interval down and increasing its size
1731 * accordingly.
[25bf215]1732 */
[56789125]1733 leaf->value[0] += count;
[25bf215]1734 leaf->key[0] = page;
[fc47885]1735 goto success;
[25bf215]1736 } else {
1737 /*
1738 * The interval is between both neigbouring intervals,
1739 * but cannot be merged with any of them.
1740 */
[da1bafb]1741 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1742 leaf);
[fc47885]1743 goto success;
[25bf215]1744 }
1745 } else if (page < leaf->key[0]) {
[7f1c620]1746 uintptr_t right_pg = leaf->key[0];
[98000fb]1747 size_t right_cnt = (size_t) leaf->value[0];
[da1bafb]1748
[25bf215]1749 /*
[6f4495f5]1750 * Investigate the border case in which the left neighbour does
1751 * not exist but the interval fits from the left.
[25bf215]1752 */
[da1bafb]1753
[b6f3e7e]1754 if (overlaps(page, P2SZ(count), right_pg, P2SZ(right_cnt))) {
[25bf215]1755 /* The interval intersects with the right interval. */
[fc47885]1756 return false;
[b6f3e7e]1757 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1758 /*
[6f4495f5]1759 * The interval can be added by moving the base of the
1760 * right interval down and increasing its size
1761 * accordingly.
[25bf215]1762 */
1763 leaf->key[0] = page;
[56789125]1764 leaf->value[0] += count;
[fc47885]1765 goto success;
[25bf215]1766 } else {
1767 /*
1768 * The interval doesn't adjoin with the right interval.
1769 * It must be added individually.
1770 */
[da1bafb]1771 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1772 leaf);
[fc47885]1773 goto success;
[25bf215]1774 }
1775 }
[da1bafb]1776
1777 node = btree_leaf_node_right_neighbour(&area->used_space, leaf);
[25bf215]1778 if (node) {
[6f4495f5]1779 uintptr_t left_pg = leaf->key[leaf->keys - 1];
1780 uintptr_t right_pg = node->key[0];
[98000fb]1781 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
1782 size_t right_cnt = (size_t) node->value[0];
[25bf215]1783
1784 /*
1785 * Examine the possibility that the interval fits
1786 * somewhere between the leftmost interval of
1787 * the right neigbour and the last interval of the leaf.
1788 */
[da1bafb]1789
[25bf215]1790 if (page < left_pg) {
1791 /* Do nothing. */
[b6f3e7e]1792 } else if (overlaps(page, P2SZ(count), left_pg,
1793 P2SZ(left_cnt))) {
[25bf215]1794 /* The interval intersects with the left interval. */
[fc47885]1795 return false;
[b6f3e7e]1796 } else if (overlaps(page, P2SZ(count), right_pg,
1797 P2SZ(right_cnt))) {
[25bf215]1798 /* The interval intersects with the right interval. */
[fc47885]1799 return false;
[b6f3e7e]1800 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1801 (page + P2SZ(count) == right_pg)) {
[6f4495f5]1802 /*
1803 * The interval can be added by merging the two already
1804 * present intervals.
[da1bafb]1805 */
[56789125]1806 leaf->value[leaf->keys - 1] += count + right_cnt;
[da1bafb]1807 btree_remove(&area->used_space, right_pg, node);
[fc47885]1808 goto success;
[b6f3e7e]1809 } else if (page == left_pg + P2SZ(left_cnt)) {
[6f4495f5]1810 /*
1811 * The interval can be added by simply growing the left
1812 * interval.
[da1bafb]1813 */
[fc47885]1814 leaf->value[leaf->keys - 1] += count;
1815 goto success;
[b6f3e7e]1816 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1817 /*
[6f4495f5]1818 * The interval can be addded by simply moving base of
1819 * the right interval down and increasing its size
1820 * accordingly.
[25bf215]1821 */
[56789125]1822 node->value[0] += count;
[25bf215]1823 node->key[0] = page;
[fc47885]1824 goto success;
[25bf215]1825 } else {
1826 /*
1827 * The interval is between both neigbouring intervals,
1828 * but cannot be merged with any of them.
1829 */
[da1bafb]1830 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1831 leaf);
[fc47885]1832 goto success;
[25bf215]1833 }
1834 } else if (page >= leaf->key[leaf->keys - 1]) {
[7f1c620]1835 uintptr_t left_pg = leaf->key[leaf->keys - 1];
[98000fb]1836 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
[da1bafb]1837
[25bf215]1838 /*
[6f4495f5]1839 * Investigate the border case in which the right neighbour
1840 * does not exist but the interval fits from the right.
[25bf215]1841 */
[da1bafb]1842
[b6f3e7e]1843 if (overlaps(page, P2SZ(count), left_pg, P2SZ(left_cnt))) {
[56789125]1844 /* The interval intersects with the left interval. */
[fc47885]1845 return false;
[b6f3e7e]1846 } else if (left_pg + P2SZ(left_cnt) == page) {
[6f4495f5]1847 /*
1848 * The interval can be added by growing the left
1849 * interval.
1850 */
[56789125]1851 leaf->value[leaf->keys - 1] += count;
[fc47885]1852 goto success;
[25bf215]1853 } else {
1854 /*
1855 * The interval doesn't adjoin with the left interval.
1856 * It must be added individually.
1857 */
[da1bafb]1858 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1859 leaf);
[fc47885]1860 goto success;
[25bf215]1861 }
1862 }
1863
1864 /*
[6f4495f5]1865 * Note that if the algorithm made it thus far, the interval can fit
1866 * only between two other intervals of the leaf. The two border cases
1867 * were already resolved.
[25bf215]1868 */
[da1bafb]1869 btree_key_t i;
[25bf215]1870 for (i = 1; i < leaf->keys; i++) {
1871 if (page < leaf->key[i]) {
[6f4495f5]1872 uintptr_t left_pg = leaf->key[i - 1];
1873 uintptr_t right_pg = leaf->key[i];
[98000fb]1874 size_t left_cnt = (size_t) leaf->value[i - 1];
1875 size_t right_cnt = (size_t) leaf->value[i];
[da1bafb]1876
[25bf215]1877 /*
1878 * The interval fits between left_pg and right_pg.
1879 */
[da1bafb]1880
[b6f3e7e]1881 if (overlaps(page, P2SZ(count), left_pg,
1882 P2SZ(left_cnt))) {
[6f4495f5]1883 /*
1884 * The interval intersects with the left
1885 * interval.
1886 */
[fc47885]1887 return false;
[b6f3e7e]1888 } else if (overlaps(page, P2SZ(count), right_pg,
1889 P2SZ(right_cnt))) {
[6f4495f5]1890 /*
1891 * The interval intersects with the right
1892 * interval.
1893 */
[fc47885]1894 return false;
[b6f3e7e]1895 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1896 (page + P2SZ(count) == right_pg)) {
[6f4495f5]1897 /*
1898 * The interval can be added by merging the two
1899 * already present intervals.
1900 */
[56789125]1901 leaf->value[i - 1] += count + right_cnt;
[da1bafb]1902 btree_remove(&area->used_space, right_pg, leaf);
[fc47885]1903 goto success;
[b6f3e7e]1904 } else if (page == left_pg + P2SZ(left_cnt)) {
[6f4495f5]1905 /*
1906 * The interval can be added by simply growing
1907 * the left interval.
1908 */
[56789125]1909 leaf->value[i - 1] += count;
[fc47885]1910 goto success;
[b6f3e7e]1911 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1912 /*
[da1bafb]1913 * The interval can be addded by simply moving
[6f4495f5]1914 * base of the right interval down and
1915 * increasing its size accordingly.
[da1bafb]1916 */
[56789125]1917 leaf->value[i] += count;
[25bf215]1918 leaf->key[i] = page;
[fc47885]1919 goto success;
[25bf215]1920 } else {
1921 /*
[6f4495f5]1922 * The interval is between both neigbouring
1923 * intervals, but cannot be merged with any of
1924 * them.
[25bf215]1925 */
[da1bafb]1926 btree_insert(&area->used_space, page,
[6f4495f5]1927 (void *) count, leaf);
[fc47885]1928 goto success;
[25bf215]1929 }
1930 }
1931 }
[da1bafb]1932
[7e752b2]1933 panic("Inconsistency detected while adding %zu pages of used "
1934 "space at %p.", count, (void *) page);
[fc47885]1935
1936success:
1937 area->resident += count;
1938 return true;
[25bf215]1939}
1940
1941/** Mark portion of address space area as unused.
1942 *
1943 * The address space area must be already locked.
1944 *
[da1bafb]1945 * @param area Address space area.
1946 * @param page First page to be marked.
1947 * @param count Number of page to be marked.
1948 *
[fc47885]1949 * @return False on failure or true on success.
[25bf215]1950 *
1951 */
[fc47885]1952bool used_space_remove(as_area_t *area, uintptr_t page, size_t count)
[25bf215]1953{
[1d432f9]1954 ASSERT(mutex_locked(&area->lock));
[25bf215]1955 ASSERT(page == ALIGN_DOWN(page, PAGE_SIZE));
1956 ASSERT(count);
[da1bafb]1957
1958 btree_node_t *leaf;
1959 size_t pages = (size_t) btree_search(&area->used_space, page, &leaf);
[25bf215]1960 if (pages) {
1961 /*
1962 * We are lucky, page is the beginning of some interval.
1963 */
1964 if (count > pages) {
[fc47885]1965 return false;
[25bf215]1966 } else if (count == pages) {
[da1bafb]1967 btree_remove(&area->used_space, page, leaf);
[fc47885]1968 goto success;
[25bf215]1969 } else {
1970 /*
1971 * Find the respective interval.
1972 * Decrease its size and relocate its start address.
1973 */
[da1bafb]1974 btree_key_t i;
[25bf215]1975 for (i = 0; i < leaf->keys; i++) {
1976 if (leaf->key[i] == page) {
[b6f3e7e]1977 leaf->key[i] += P2SZ(count);
[56789125]1978 leaf->value[i] -= count;
[fc47885]1979 goto success;
[25bf215]1980 }
1981 }
[fc47885]1982
[25bf215]1983 goto error;
1984 }
1985 }
[da1bafb]1986
[b6f3e7e]1987 btree_node_t *node = btree_leaf_node_left_neighbour(&area->used_space,
1988 leaf);
[da1bafb]1989 if ((node) && (page < leaf->key[0])) {
[7f1c620]1990 uintptr_t left_pg = node->key[node->keys - 1];
[98000fb]1991 size_t left_cnt = (size_t) node->value[node->keys - 1];
[da1bafb]1992
[b6f3e7e]1993 if (overlaps(left_pg, P2SZ(left_cnt), page, P2SZ(count))) {
1994 if (page + P2SZ(count) == left_pg + P2SZ(left_cnt)) {
[25bf215]1995 /*
[6f4495f5]1996 * The interval is contained in the rightmost
1997 * interval of the left neighbour and can be
1998 * removed by updating the size of the bigger
1999 * interval.
[25bf215]2000 */
[56789125]2001 node->value[node->keys - 1] -= count;
[fc47885]2002 goto success;
[b6f3e7e]2003 } else if (page + P2SZ(count) <
2004 left_pg + P2SZ(left_cnt)) {
2005 size_t new_cnt;
2006
[25bf215]2007 /*
[6f4495f5]2008 * The interval is contained in the rightmost
2009 * interval of the left neighbour but its
2010 * removal requires both updating the size of
2011 * the original interval and also inserting a
2012 * new interval.
[25bf215]2013 */
[b6f3e7e]2014 new_cnt = ((left_pg + P2SZ(left_cnt)) -
2015 (page + P2SZ(count))) >> PAGE_WIDTH;
[56789125]2016 node->value[node->keys - 1] -= count + new_cnt;
[da1bafb]2017 btree_insert(&area->used_space, page +
[b6f3e7e]2018 P2SZ(count), (void *) new_cnt, leaf);
[fc47885]2019 goto success;
[25bf215]2020 }
2021 }
[fc47885]2022
2023 return false;
[da1bafb]2024 } else if (page < leaf->key[0])
[fc47885]2025 return false;
[25bf215]2026
2027 if (page > leaf->key[leaf->keys - 1]) {
[7f1c620]2028 uintptr_t left_pg = leaf->key[leaf->keys - 1];
[98000fb]2029 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
[da1bafb]2030
[b6f3e7e]2031 if (overlaps(left_pg, P2SZ(left_cnt), page, P2SZ(count))) {
2032 if (page + P2SZ(count) == left_pg + P2SZ(left_cnt)) {
[25bf215]2033 /*
[6f4495f5]2034 * The interval is contained in the rightmost
2035 * interval of the leaf and can be removed by
2036 * updating the size of the bigger interval.
[25bf215]2037 */
[56789125]2038 leaf->value[leaf->keys - 1] -= count;
[fc47885]2039 goto success;
[b6f3e7e]2040 } else if (page + P2SZ(count) < left_pg +
2041 P2SZ(left_cnt)) {
2042 size_t new_cnt;
2043
[25bf215]2044 /*
[6f4495f5]2045 * The interval is contained in the rightmost
2046 * interval of the leaf but its removal
2047 * requires both updating the size of the
2048 * original interval and also inserting a new
2049 * interval.
[25bf215]2050 */
[b6f3e7e]2051 new_cnt = ((left_pg + P2SZ(left_cnt)) -
2052 (page + P2SZ(count))) >> PAGE_WIDTH;
[56789125]2053 leaf->value[leaf->keys - 1] -= count + new_cnt;
[da1bafb]2054 btree_insert(&area->used_space, page +
[b6f3e7e]2055 P2SZ(count), (void *) new_cnt, leaf);
[fc47885]2056 goto success;
[25bf215]2057 }
2058 }
[fc47885]2059
2060 return false;
[da1bafb]2061 }
[25bf215]2062
2063 /*
2064 * The border cases have been already resolved.
[fc47885]2065 * Now the interval can be only between intervals of the leaf.
[25bf215]2066 */
[da1bafb]2067 btree_key_t i;
[25bf215]2068 for (i = 1; i < leaf->keys - 1; i++) {
2069 if (page < leaf->key[i]) {
[7f1c620]2070 uintptr_t left_pg = leaf->key[i - 1];
[98000fb]2071 size_t left_cnt = (size_t) leaf->value[i - 1];
[da1bafb]2072
[25bf215]2073 /*
[6f4495f5]2074 * Now the interval is between intervals corresponding
2075 * to (i - 1) and i.
[25bf215]2076 */
[b6f3e7e]2077 if (overlaps(left_pg, P2SZ(left_cnt), page,
2078 P2SZ(count))) {
2079 if (page + P2SZ(count) ==
2080 left_pg + P2SZ(left_cnt)) {
[25bf215]2081 /*
[6f4495f5]2082 * The interval is contained in the
2083 * interval (i - 1) of the leaf and can
2084 * be removed by updating the size of
2085 * the bigger interval.
[25bf215]2086 */
[56789125]2087 leaf->value[i - 1] -= count;
[fc47885]2088 goto success;
[b6f3e7e]2089 } else if (page + P2SZ(count) <
2090 left_pg + P2SZ(left_cnt)) {
2091 size_t new_cnt;
2092
[25bf215]2093 /*
[6f4495f5]2094 * The interval is contained in the
2095 * interval (i - 1) of the leaf but its
2096 * removal requires both updating the
2097 * size of the original interval and
[25bf215]2098 * also inserting a new interval.
2099 */
[b6f3e7e]2100 new_cnt = ((left_pg + P2SZ(left_cnt)) -
2101 (page + P2SZ(count))) >>
[6f4495f5]2102 PAGE_WIDTH;
[56789125]2103 leaf->value[i - 1] -= count + new_cnt;
[da1bafb]2104 btree_insert(&area->used_space, page +
[b6f3e7e]2105 P2SZ(count), (void *) new_cnt,
[6f4495f5]2106 leaf);
[fc47885]2107 goto success;
[25bf215]2108 }
2109 }
[fc47885]2110
2111 return false;
[25bf215]2112 }
2113 }
[da1bafb]2114
[25bf215]2115error:
[7e752b2]2116 panic("Inconsistency detected while removing %zu pages of used "
2117 "space from %p.", count, (void *) page);
[fc47885]2118
2119success:
2120 area->resident -= count;
2121 return true;
[25bf215]2122}
2123
[df0103f7]2124/*
2125 * Address space related syscalls.
2126 */
2127
[fbcdeb8]2128sysarg_t sys_as_area_create(uintptr_t base, size_t size, unsigned int flags,
2129 uintptr_t bound)
[df0103f7]2130{
[fbcdeb8]2131 uintptr_t virt = base;
2132 as_area_t *area = as_area_create(AS, flags | AS_AREA_CACHEABLE, size,
2133 AS_AREA_ATTR_NONE, &anon_backend, NULL, &virt, bound);
2134 if (area == NULL)
[96b02eb9]2135 return (sysarg_t) -1;
[fbcdeb8]2136
2137 return (sysarg_t) virt;
[df0103f7]2138}
2139
[96b02eb9]2140sysarg_t sys_as_area_resize(uintptr_t address, size_t size, unsigned int flags)
[df0103f7]2141{
[96b02eb9]2142 return (sysarg_t) as_area_resize(AS, address, size, 0);
[7242a78e]2143}
2144
[96b02eb9]2145sysarg_t sys_as_area_change_flags(uintptr_t address, unsigned int flags)
[c98e6ee]2146{
[96b02eb9]2147 return (sysarg_t) as_area_change_flags(AS, flags, address);
[c98e6ee]2148}
2149
[96b02eb9]2150sysarg_t sys_as_area_destroy(uintptr_t address)
[7242a78e]2151{
[96b02eb9]2152 return (sysarg_t) as_area_destroy(AS, address);
[df0103f7]2153}
[b45c443]2154
[336db295]2155/** Get list of adress space areas.
2156 *
[da1bafb]2157 * @param as Address space.
2158 * @param obuf Place to save pointer to returned buffer.
2159 * @param osize Place to save size of returned buffer.
2160 *
[336db295]2161 */
2162void as_get_area_info(as_t *as, as_area_info_t **obuf, size_t *osize)
2163{
2164 mutex_lock(&as->lock);
[da1bafb]2165
[336db295]2166 /* First pass, count number of areas. */
[da1bafb]2167
2168 size_t area_cnt = 0;
2169
[55b77d9]2170 list_foreach(as->as_area_btree.leaf_list, cur) {
[da1bafb]2171 btree_node_t *node =
2172 list_get_instance(cur, btree_node_t, leaf_link);
[336db295]2173 area_cnt += node->keys;
2174 }
[da1bafb]2175
2176 size_t isize = area_cnt * sizeof(as_area_info_t);
2177 as_area_info_t *info = malloc(isize, 0);
2178
[336db295]2179 /* Second pass, record data. */
[da1bafb]2180
2181 size_t area_idx = 0;
2182
[55b77d9]2183 list_foreach(as->as_area_btree.leaf_list, cur) {
[da1bafb]2184 btree_node_t *node =
2185 list_get_instance(cur, btree_node_t, leaf_link);
2186 btree_key_t i;
2187
[336db295]2188 for (i = 0; i < node->keys; i++) {
2189 as_area_t *area = node->value[i];
[da1bafb]2190
[336db295]2191 ASSERT(area_idx < area_cnt);
2192 mutex_lock(&area->lock);
[da1bafb]2193
[336db295]2194 info[area_idx].start_addr = area->base;
[b6f3e7e]2195 info[area_idx].size = P2SZ(area->pages);
[336db295]2196 info[area_idx].flags = area->flags;
2197 ++area_idx;
[da1bafb]2198
[336db295]2199 mutex_unlock(&area->lock);
2200 }
2201 }
[da1bafb]2202
[336db295]2203 mutex_unlock(&as->lock);
[da1bafb]2204
[336db295]2205 *obuf = info;
2206 *osize = isize;
2207}
2208
[64c2ad5]2209/** Print out information about address space.
2210 *
[da1bafb]2211 * @param as Address space.
2212 *
[64c2ad5]2213 */
2214void as_print(as_t *as)
2215{
2216 mutex_lock(&as->lock);
2217
[0b37882]2218 /* Print out info about address space areas */
[55b77d9]2219 list_foreach(as->as_area_btree.leaf_list, cur) {
[da1bafb]2220 btree_node_t *node
2221 = list_get_instance(cur, btree_node_t, leaf_link);
2222 btree_key_t i;
[64c2ad5]2223
2224 for (i = 0; i < node->keys; i++) {
[7ba7c6d]2225 as_area_t *area = node->value[i];
[da1bafb]2226
[64c2ad5]2227 mutex_lock(&area->lock);
[7e752b2]2228 printf("as_area: %p, base=%p, pages=%zu"
2229 " (%p - %p)\n", area, (void *) area->base,
2230 area->pages, (void *) area->base,
[b6f3e7e]2231 (void *) (area->base + P2SZ(area->pages)));
[64c2ad5]2232 mutex_unlock(&area->lock);
2233 }
2234 }
2235
2236 mutex_unlock(&as->lock);
2237}
2238
[cc73a8a1]2239/** @}
[b45c443]2240 */
Note: See TracBrowser for help on using the repository browser.