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

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

Separate list_t typedef from link_t (kernel part).

  • list_t represents lists
  • Use list_first(), list_last(), list_empty() where appropriate
  • Use list_foreach() where possible
  • Replace improper uses of list_prepend() with list_insert_after()
  • Replace improper uses of list_append() with list_insert_before()
  • Property mode set to 100644
File size: 52.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>
[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
[29b2bbf]132 as_slab = slab_cache_create("as_slab", 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 *
[0b37882]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 avoid Do not touch this area.
[e3ee9b9]291 *
292 * @return True if there is no conflict, false otherwise.
293 *
294 */
[0b37882]295NO_TRACE static bool check_area_conflicts(as_t *as, uintptr_t addr,
296 size_t count, as_area_t *avoid)
[e3ee9b9]297{
[0b37882]298 ASSERT((addr % PAGE_SIZE) == 0);
[e3ee9b9]299 ASSERT(mutex_locked(&as->lock));
300
301 /*
302 * We don't want any area to have conflicts with NULL page.
303 */
[b6f3e7e]304 if (overlaps(addr, P2SZ(count), (uintptr_t) NULL, PAGE_SIZE))
[e3ee9b9]305 return false;
306
307 /*
308 * The leaf node is found in O(log n), where n is proportional to
309 * the number of address space areas belonging to as.
310 * The check for conflicts is then attempted on the rightmost
311 * record in the left neighbour, the leftmost record in the right
312 * neighbour and all records in the leaf node itself.
313 */
314 btree_node_t *leaf;
315 as_area_t *area =
[0b37882]316 (as_area_t *) btree_search(&as->as_area_btree, addr, &leaf);
[e3ee9b9]317 if (area) {
[0b37882]318 if (area != avoid)
[e3ee9b9]319 return false;
320 }
321
322 /* First, check the two border cases. */
323 btree_node_t *node =
324 btree_leaf_node_left_neighbour(&as->as_area_btree, leaf);
325 if (node) {
326 area = (as_area_t *) node->value[node->keys - 1];
327
[0b37882]328 if (area != avoid) {
329 mutex_lock(&area->lock);
330
[b6f3e7e]331 if (overlaps(addr, P2SZ(count), area->base,
332 P2SZ(area->pages))) {
[0b37882]333 mutex_unlock(&area->lock);
334 return false;
335 }
336
[e3ee9b9]337 mutex_unlock(&area->lock);
338 }
339 }
340
341 node = btree_leaf_node_right_neighbour(&as->as_area_btree, leaf);
342 if (node) {
343 area = (as_area_t *) node->value[0];
344
[0b37882]345 if (area != avoid) {
346 mutex_lock(&area->lock);
347
[b6f3e7e]348 if (overlaps(addr, P2SZ(count), area->base,
349 P2SZ(area->pages))) {
[0b37882]350 mutex_unlock(&area->lock);
351 return false;
352 }
353
[e3ee9b9]354 mutex_unlock(&area->lock);
355 }
356 }
357
358 /* Second, check the leaf node. */
359 btree_key_t i;
360 for (i = 0; i < leaf->keys; i++) {
361 area = (as_area_t *) leaf->value[i];
362
[0b37882]363 if (area == avoid)
[e3ee9b9]364 continue;
365
366 mutex_lock(&area->lock);
367
[b6f3e7e]368 if (overlaps(addr, P2SZ(count), area->base,
369 P2SZ(area->pages))) {
[e3ee9b9]370 mutex_unlock(&area->lock);
371 return false;
372 }
373
374 mutex_unlock(&area->lock);
375 }
376
377 /*
378 * So far, the area does not conflict with other areas.
379 * Check if it doesn't conflict with kernel address space.
380 */
381 if (!KERNEL_ADDRESS_SPACE_SHADOWED) {
[b6f3e7e]382 return !overlaps(addr, P2SZ(count), KERNEL_ADDRESS_SPACE_START,
[e3ee9b9]383 KERNEL_ADDRESS_SPACE_END - KERNEL_ADDRESS_SPACE_START);
384 }
385
386 return true;
387}
388
[20d50a1]389/** Create address space area of common attributes.
390 *
391 * The created address space area is added to the target address space.
392 *
[da1bafb]393 * @param as Target address space.
394 * @param flags Flags of the area memory.
395 * @param size Size of area.
396 * @param base Base address of area.
397 * @param attrs Attributes of the area.
398 * @param backend Address space area backend. NULL if no backend is used.
399 * @param backend_data NULL or a pointer to an array holding two void *.
400 *
401 * @return Address space area on success or NULL on failure.
[20d50a1]402 *
403 */
[da1bafb]404as_area_t *as_area_create(as_t *as, unsigned int flags, size_t size,
405 uintptr_t base, unsigned int attrs, mem_backend_t *backend,
406 mem_backend_data_t *backend_data)
[20d50a1]407{
[0b37882]408 if ((base % PAGE_SIZE) != 0)
[37e7d2b9]409 return NULL;
[da1bafb]410
[0b37882]411 if (size == 0)
[dbbeb26]412 return NULL;
[da1bafb]413
[0b37882]414 size_t pages = SIZE2FRAMES(size);
415
[37e7d2b9]416 /* Writeable executable areas are not supported. */
417 if ((flags & AS_AREA_EXEC) && (flags & AS_AREA_WRITE))
418 return NULL;
[20d50a1]419
[1068f6a]420 mutex_lock(&as->lock);
[20d50a1]421
[0b37882]422 if (!check_area_conflicts(as, base, pages, NULL)) {
[1068f6a]423 mutex_unlock(&as->lock);
[37e7d2b9]424 return NULL;
425 }
[20d50a1]426
[da1bafb]427 as_area_t *area = (as_area_t *) malloc(sizeof(as_area_t), 0);
428
429 mutex_initialize(&area->lock, MUTEX_PASSIVE);
430
431 area->as = as;
432 area->flags = flags;
433 area->attributes = attrs;
[0b37882]434 area->pages = pages;
[fc47885]435 area->resident = 0;
[da1bafb]436 area->base = base;
437 area->sh_info = NULL;
438 area->backend = backend;
439
[0ee077ee]440 if (backend_data)
[da1bafb]441 area->backend_data = *backend_data;
[0ee077ee]442 else
[da1bafb]443 memsetb(&area->backend_data, sizeof(area->backend_data), 0);
444
[e394b736]445 if (area->backend && area->backend->create) {
446 if (!area->backend->create(area)) {
447 free(area);
448 mutex_unlock(&as->lock);
449 return NULL;
450 }
451 }
452
[da1bafb]453 btree_create(&area->used_space);
454 btree_insert(&as->as_area_btree, base, (void *) area, NULL);
[bb68433]455
[1068f6a]456 mutex_unlock(&as->lock);
[da1bafb]457
458 return area;
[20d50a1]459}
460
[e3ee9b9]461/** Find address space area and lock it.
462 *
463 * @param as Address space.
464 * @param va Virtual address.
465 *
466 * @return Locked address space area containing va on success or
467 * NULL on failure.
468 *
469 */
[7a0359b]470NO_TRACE static as_area_t *find_area_and_lock(as_t *as, uintptr_t va)
[e3ee9b9]471{
472 ASSERT(mutex_locked(&as->lock));
473
474 btree_node_t *leaf;
[b6f3e7e]475 as_area_t *area = (as_area_t *) btree_search(&as->as_area_btree, va,
476 &leaf);
[e3ee9b9]477 if (area) {
478 /* va is the base address of an address space area */
479 mutex_lock(&area->lock);
480 return area;
481 }
482
483 /*
[326bf65]484 * Search the leaf node and the rightmost record of its left neighbour
[e3ee9b9]485 * to find out whether this is a miss or va belongs to an address
486 * space area found there.
487 */
488
489 /* First, search the leaf node itself. */
490 btree_key_t i;
491
492 for (i = 0; i < leaf->keys; i++) {
493 area = (as_area_t *) leaf->value[i];
494
495 mutex_lock(&area->lock);
[326bf65]496
[b6f3e7e]497 if ((area->base <= va) &&
498 (va <= area->base + (P2SZ(area->pages) - 1)))
[e3ee9b9]499 return area;
500
501 mutex_unlock(&area->lock);
502 }
503
504 /*
505 * Second, locate the left neighbour and test its last record.
506 * Because of its position in the B+tree, it must have base < va.
507 */
[b6f3e7e]508 btree_node_t *lnode = btree_leaf_node_left_neighbour(&as->as_area_btree,
509 leaf);
[e3ee9b9]510 if (lnode) {
511 area = (as_area_t *) lnode->value[lnode->keys - 1];
512
513 mutex_lock(&area->lock);
514
[b6f3e7e]515 if (va <= area->base + (P2SZ(area->pages) - 1))
[e3ee9b9]516 return area;
517
518 mutex_unlock(&area->lock);
519 }
520
521 return NULL;
522}
523
[df0103f7]524/** Find address space area and change it.
525 *
[da1bafb]526 * @param as Address space.
527 * @param address Virtual address belonging to the area to be changed.
528 * Must be page-aligned.
529 * @param size New size of the virtual memory block starting at
530 * address.
531 * @param flags Flags influencing the remap operation. Currently unused.
532 *
533 * @return Zero on success or a value from @ref errno.h otherwise.
[df0103f7]534 *
[da1bafb]535 */
536int as_area_resize(as_t *as, uintptr_t address, size_t size, unsigned int flags)
[df0103f7]537{
[1068f6a]538 mutex_lock(&as->lock);
[df0103f7]539
540 /*
541 * Locate the area.
542 */
[da1bafb]543 as_area_t *area = find_area_and_lock(as, address);
[df0103f7]544 if (!area) {
[1068f6a]545 mutex_unlock(&as->lock);
[7242a78e]546 return ENOENT;
[df0103f7]547 }
[da1bafb]548
[0ee077ee]549 if (area->backend == &phys_backend) {
[df0103f7]550 /*
551 * Remapping of address space areas associated
552 * with memory mapped devices is not supported.
553 */
[1068f6a]554 mutex_unlock(&area->lock);
555 mutex_unlock(&as->lock);
[7242a78e]556 return ENOTSUP;
[df0103f7]557 }
[da1bafb]558
[8182031]559 if (area->sh_info) {
560 /*
[da1bafb]561 * Remapping of shared address space areas
[8182031]562 * is not supported.
563 */
564 mutex_unlock(&area->lock);
565 mutex_unlock(&as->lock);
566 return ENOTSUP;
567 }
[da1bafb]568
569 size_t pages = SIZE2FRAMES((address - area->base) + size);
[df0103f7]570 if (!pages) {
571 /*
572 * Zero size address space areas are not allowed.
573 */
[1068f6a]574 mutex_unlock(&area->lock);
575 mutex_unlock(&as->lock);
[7242a78e]576 return EPERM;
[df0103f7]577 }
578
579 if (pages < area->pages) {
[b6f3e7e]580 uintptr_t start_free = area->base + P2SZ(pages);
[da1bafb]581
[df0103f7]582 /*
583 * Shrinking the area.
584 * No need to check for overlaps.
585 */
[da1bafb]586
[c964521]587 page_table_lock(as, false);
[da1bafb]588
[5552d60]589 /*
590 * Start TLB shootdown sequence.
591 */
[402eda5]592 ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES, as->asid,
[b6f3e7e]593 area->base + P2SZ(pages), area->pages - pages);
[da1bafb]594
[56789125]595 /*
596 * Remove frames belonging to used space starting from
597 * the highest addresses downwards until an overlap with
598 * the resized address space area is found. Note that this
599 * is also the right way to remove part of the used_space
600 * B+tree leaf list.
[da1bafb]601 */
602 bool cond = true;
603 while (cond) {
[55b77d9]604 ASSERT(!list_empty(&area->used_space.leaf_list));
[da1bafb]605
606 btree_node_t *node =
[55b77d9]607 list_get_instance(list_last(&area->used_space.leaf_list),
[6f4495f5]608 btree_node_t, leaf_link);
[da1bafb]609
[56789125]610 if ((cond = (bool) node->keys)) {
[da1bafb]611 uintptr_t ptr = node->key[node->keys - 1];
612 size_t size =
[98000fb]613 (size_t) node->value[node->keys - 1];
[da1bafb]614 size_t i = 0;
615
[b6f3e7e]616 if (overlaps(ptr, P2SZ(size), area->base,
617 P2SZ(pages))) {
[56789125]618
[b6f3e7e]619 if (ptr + P2SZ(size) <= start_free) {
[56789125]620 /*
[6f4495f5]621 * The whole interval fits
622 * completely in the resized
623 * address space area.
[56789125]624 */
625 break;
626 }
[da1bafb]627
[56789125]628 /*
[6f4495f5]629 * Part of the interval corresponding
630 * to b and c overlaps with the resized
631 * address space area.
[56789125]632 */
[da1bafb]633
634 /* We are almost done */
635 cond = false;
636 i = (start_free - ptr) >> PAGE_WIDTH;
[6745592]637 if (!used_space_remove(area, start_free,
[da1bafb]638 size - i))
639 panic("Cannot remove used space.");
[56789125]640 } else {
641 /*
[6f4495f5]642 * The interval of used space can be
643 * completely removed.
[56789125]644 */
[da1bafb]645 if (!used_space_remove(area, ptr, size))
646 panic("Cannot remove used space.");
[56789125]647 }
[da1bafb]648
649 for (; i < size; i++) {
[b6f3e7e]650 pte_t *pte = page_mapping_find(as,
[0ff03f3]651 ptr + P2SZ(i), false);
[da1bafb]652
653 ASSERT(pte);
654 ASSERT(PTE_VALID(pte));
655 ASSERT(PTE_PRESENT(pte));
656
657 if ((area->backend) &&
658 (area->backend->frame_free)) {
[0ee077ee]659 area->backend->frame_free(area,
[b6f3e7e]660 ptr + P2SZ(i),
[6f4495f5]661 PTE_GET_FRAME(pte));
[8182031]662 }
[da1bafb]663
[b6f3e7e]664 page_mapping_remove(as, ptr + P2SZ(i));
[56789125]665 }
[df0103f7]666 }
667 }
[da1bafb]668
[df0103f7]669 /*
[5552d60]670 * Finish TLB shootdown sequence.
[df0103f7]671 */
[da1bafb]672
[b6f3e7e]673 tlb_invalidate_pages(as->asid, area->base + P2SZ(pages),
[6f4495f5]674 area->pages - pages);
[da1bafb]675
[f1d1f5d3]676 /*
[eef1b031]677 * Invalidate software translation caches
678 * (e.g. TSB on sparc64, PHT on ppc32).
[f1d1f5d3]679 */
[b6f3e7e]680 as_invalidate_translation_cache(as, area->base + P2SZ(pages),
681 area->pages - pages);
[402eda5]682 tlb_shootdown_finalize(ipl);
[31d8e10]683
[da1bafb]684 page_table_unlock(as, false);
[df0103f7]685 } else {
686 /*
687 * Growing the area.
688 * Check for overlaps with other address space areas.
689 */
[0b37882]690 if (!check_area_conflicts(as, address, pages, area)) {
[1068f6a]691 mutex_unlock(&area->lock);
[da1bafb]692 mutex_unlock(&as->lock);
[7242a78e]693 return EADDRNOTAVAIL;
[df0103f7]694 }
[da1bafb]695 }
696
[e394b736]697 if (area->backend && area->backend->resize) {
698 if (!area->backend->resize(area, pages)) {
699 mutex_unlock(&area->lock);
700 mutex_unlock(&as->lock);
701 return ENOMEM;
702 }
703 }
704
[df0103f7]705 area->pages = pages;
706
[1068f6a]707 mutex_unlock(&area->lock);
708 mutex_unlock(&as->lock);
[da1bafb]709
[7242a78e]710 return 0;
711}
712
[e3ee9b9]713/** Remove reference to address space area share info.
714 *
715 * If the reference count drops to 0, the sh_info is deallocated.
716 *
717 * @param sh_info Pointer to address space area share info.
718 *
719 */
[7a0359b]720NO_TRACE static void sh_info_remove_reference(share_info_t *sh_info)
[e3ee9b9]721{
722 bool dealloc = false;
723
724 mutex_lock(&sh_info->lock);
725 ASSERT(sh_info->refcount);
726
727 if (--sh_info->refcount == 0) {
728 dealloc = true;
729
730 /*
731 * Now walk carefully the pagemap B+tree and free/remove
732 * reference from all frames found there.
733 */
[55b77d9]734 list_foreach(sh_info->pagemap.leaf_list, cur) {
[e3ee9b9]735 btree_node_t *node
736 = list_get_instance(cur, btree_node_t, leaf_link);
737 btree_key_t i;
738
739 for (i = 0; i < node->keys; i++)
740 frame_free((uintptr_t) node->value[i]);
741 }
742
743 }
744 mutex_unlock(&sh_info->lock);
745
746 if (dealloc) {
747 btree_destroy(&sh_info->pagemap);
748 free(sh_info);
749 }
750}
751
[7242a78e]752/** Destroy address space area.
753 *
[da1bafb]754 * @param as Address space.
755 * @param address Address within the area to be deleted.
756 *
757 * @return Zero on success or a value from @ref errno.h on failure.
[7242a78e]758 *
759 */
[7f1c620]760int as_area_destroy(as_t *as, uintptr_t address)
[7242a78e]761{
[1068f6a]762 mutex_lock(&as->lock);
[da1bafb]763
764 as_area_t *area = find_area_and_lock(as, address);
[7242a78e]765 if (!area) {
[1068f6a]766 mutex_unlock(&as->lock);
[7242a78e]767 return ENOENT;
768 }
[e394b736]769
770 if (area->backend && area->backend->destroy)
771 area->backend->destroy(area);
[da1bafb]772
773 uintptr_t base = area->base;
774
[c964521]775 page_table_lock(as, false);
[da1bafb]776
[5552d60]777 /*
778 * Start TLB shootdown sequence.
779 */
[402eda5]780 ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES, as->asid, area->base,
781 area->pages);
[da1bafb]782
[567807b1]783 /*
784 * Visit only the pages mapped by used_space B+tree.
785 */
[55b77d9]786 list_foreach(area->used_space.leaf_list, cur) {
[567807b1]787 btree_node_t *node;
[da1bafb]788 btree_key_t i;
[56789125]789
[f8d069e8]790 node = list_get_instance(cur, btree_node_t, leaf_link);
791 for (i = 0; i < node->keys; i++) {
[da1bafb]792 uintptr_t ptr = node->key[i];
793 size_t size;
[56789125]794
[da1bafb]795 for (size = 0; size < (size_t) node->value[i]; size++) {
[b6f3e7e]796 pte_t *pte = page_mapping_find(as,
[0ff03f3]797 ptr + P2SZ(size), false);
[da1bafb]798
799 ASSERT(pte);
800 ASSERT(PTE_VALID(pte));
801 ASSERT(PTE_PRESENT(pte));
802
803 if ((area->backend) &&
804 (area->backend->frame_free)) {
805 area->backend->frame_free(area,
[b6f3e7e]806 ptr + P2SZ(size),
807 PTE_GET_FRAME(pte));
[56789125]808 }
[da1bafb]809
[b6f3e7e]810 page_mapping_remove(as, ptr + P2SZ(size));
[7242a78e]811 }
812 }
813 }
[da1bafb]814
[7242a78e]815 /*
[5552d60]816 * Finish TLB shootdown sequence.
[7242a78e]817 */
[da1bafb]818
[f1d1f5d3]819 tlb_invalidate_pages(as->asid, area->base, area->pages);
[da1bafb]820
[f1d1f5d3]821 /*
[eef1b031]822 * Invalidate potential software translation caches
823 * (e.g. TSB on sparc64, PHT on ppc32).
[f1d1f5d3]824 */
825 as_invalidate_translation_cache(as, area->base, area->pages);
[402eda5]826 tlb_shootdown_finalize(ipl);
[da1bafb]827
[c964521]828 page_table_unlock(as, false);
[f1d1f5d3]829
[5552d60]830 btree_destroy(&area->used_space);
[da1bafb]831
[8d4f2ae]832 area->attributes |= AS_AREA_ATTR_PARTIAL;
[8182031]833
834 if (area->sh_info)
835 sh_info_remove_reference(area->sh_info);
[da1bafb]836
[1068f6a]837 mutex_unlock(&area->lock);
[da1bafb]838
[7242a78e]839 /*
840 * Remove the empty area from address space.
841 */
[f1d1f5d3]842 btree_remove(&as->as_area_btree, base, NULL);
[7242a78e]843
[8d4f2ae]844 free(area);
845
[f1d1f5d3]846 mutex_unlock(&as->lock);
[7242a78e]847 return 0;
[df0103f7]848}
849
[8d6bc2d5]850/** Share address space area with another or the same address space.
[df0103f7]851 *
[0ee077ee]852 * Address space area mapping is shared with a new address space area.
853 * If the source address space area has not been shared so far,
854 * a new sh_info is created. The new address space area simply gets the
855 * sh_info of the source area. The process of duplicating the
856 * mapping is done through the backend share function.
[da1bafb]857 *
858 * @param src_as Pointer to source address space.
859 * @param src_base Base address of the source address space area.
860 * @param acc_size Expected size of the source area.
861 * @param dst_as Pointer to destination address space.
862 * @param dst_base Target base address.
[fd4d8c0]863 * @param dst_flags_mask Destination address space area flags mask.
[df0103f7]864 *
[da1bafb]865 * @return Zero on success.
866 * @return ENOENT if there is no such task or such address space.
867 * @return EPERM if there was a problem in accepting the area.
868 * @return ENOMEM if there was a problem in allocating destination
869 * address space area.
870 * @return ENOTSUP if the address space area backend does not support
871 * sharing.
872 *
[df0103f7]873 */
[7f1c620]874int as_area_share(as_t *src_as, uintptr_t src_base, size_t acc_size,
[da1bafb]875 as_t *dst_as, uintptr_t dst_base, unsigned int dst_flags_mask)
[df0103f7]876{
[1068f6a]877 mutex_lock(&src_as->lock);
[da1bafb]878 as_area_t *src_area = find_area_and_lock(src_as, src_base);
[a9e8b39]879 if (!src_area) {
[6fa476f7]880 /*
881 * Could not find the source address space area.
882 */
[1068f6a]883 mutex_unlock(&src_as->lock);
[6fa476f7]884 return ENOENT;
885 }
[da1bafb]886
887 if ((!src_area->backend) || (!src_area->backend->share)) {
[8d6bc2d5]888 /*
[f47fd19]889 * There is no backend or the backend does not
[0ee077ee]890 * know how to share the area.
[8d6bc2d5]891 */
892 mutex_unlock(&src_area->lock);
893 mutex_unlock(&src_as->lock);
894 return ENOTSUP;
895 }
896
[b6f3e7e]897 size_t src_size = P2SZ(src_area->pages);
[da1bafb]898 unsigned int src_flags = src_area->flags;
899 mem_backend_t *src_backend = src_area->backend;
900 mem_backend_data_t src_backend_data = src_area->backend_data;
901
[1ec1fd8]902 /* Share the cacheable flag from the original mapping */
903 if (src_flags & AS_AREA_CACHEABLE)
904 dst_flags_mask |= AS_AREA_CACHEABLE;
[da1bafb]905
906 if ((src_size != acc_size) ||
907 ((src_flags & dst_flags_mask) != dst_flags_mask)) {
[8d6bc2d5]908 mutex_unlock(&src_area->lock);
909 mutex_unlock(&src_as->lock);
[df0103f7]910 return EPERM;
911 }
[da1bafb]912
[8d6bc2d5]913 /*
914 * Now we are committed to sharing the area.
[8440473]915 * First, prepare the area for sharing.
[8d6bc2d5]916 * Then it will be safe to unlock it.
917 */
[da1bafb]918 share_info_t *sh_info = src_area->sh_info;
[8d6bc2d5]919 if (!sh_info) {
920 sh_info = (share_info_t *) malloc(sizeof(share_info_t), 0);
[08a19ba]921 mutex_initialize(&sh_info->lock, MUTEX_PASSIVE);
[8d6bc2d5]922 sh_info->refcount = 2;
923 btree_create(&sh_info->pagemap);
924 src_area->sh_info = sh_info;
[da1bafb]925
[c0697c4c]926 /*
927 * Call the backend to setup sharing.
928 */
929 src_area->backend->share(src_area);
[8d6bc2d5]930 } else {
931 mutex_lock(&sh_info->lock);
932 sh_info->refcount++;
933 mutex_unlock(&sh_info->lock);
934 }
[da1bafb]935
[8d6bc2d5]936 mutex_unlock(&src_area->lock);
937 mutex_unlock(&src_as->lock);
[da1bafb]938
[df0103f7]939 /*
[a9e8b39]940 * Create copy of the source address space area.
941 * The destination area is created with AS_AREA_ATTR_PARTIAL
942 * attribute set which prevents race condition with
943 * preliminary as_page_fault() calls.
[fd4d8c0]944 * The flags of the source area are masked against dst_flags_mask
945 * to support sharing in less privileged mode.
[df0103f7]946 */
[da1bafb]947 as_area_t *dst_area = as_area_create(dst_as, dst_flags_mask, src_size,
948 dst_base, AS_AREA_ATTR_PARTIAL, src_backend, &src_backend_data);
[a9e8b39]949 if (!dst_area) {
[df0103f7]950 /*
951 * Destination address space area could not be created.
952 */
[8d6bc2d5]953 sh_info_remove_reference(sh_info);
954
[df0103f7]955 return ENOMEM;
956 }
[da1bafb]957
[a9e8b39]958 /*
959 * Now the destination address space area has been
960 * fully initialized. Clear the AS_AREA_ATTR_PARTIAL
[8d6bc2d5]961 * attribute and set the sh_info.
[da1bafb]962 */
963 mutex_lock(&dst_as->lock);
[1068f6a]964 mutex_lock(&dst_area->lock);
[a9e8b39]965 dst_area->attributes &= ~AS_AREA_ATTR_PARTIAL;
[8d6bc2d5]966 dst_area->sh_info = sh_info;
[1068f6a]967 mutex_unlock(&dst_area->lock);
[da1bafb]968 mutex_unlock(&dst_as->lock);
969
[df0103f7]970 return 0;
971}
972
[fb84455]973/** Check access mode for address space area.
974 *
[da1bafb]975 * @param area Address space area.
976 * @param access Access mode.
977 *
978 * @return False if access violates area's permissions, true
979 * otherwise.
[fb84455]980 *
981 */
[97bdb4a]982NO_TRACE bool as_area_check_access(as_area_t *area, pf_access_t access)
[fb84455]983{
[fc47885]984 ASSERT(mutex_locked(&area->lock));
985
[fb84455]986 int flagmap[] = {
987 [PF_ACCESS_READ] = AS_AREA_READ,
988 [PF_ACCESS_WRITE] = AS_AREA_WRITE,
989 [PF_ACCESS_EXEC] = AS_AREA_EXEC
990 };
[da1bafb]991
[fb84455]992 if (!(area->flags & flagmap[access]))
993 return false;
994
995 return true;
996}
997
[e3ee9b9]998/** Convert address space area flags to page flags.
999 *
1000 * @param aflags Flags of some address space area.
1001 *
1002 * @return Flags to be passed to page_mapping_insert().
1003 *
1004 */
[7a0359b]1005NO_TRACE static unsigned int area_flags_to_page_flags(unsigned int aflags)
[e3ee9b9]1006{
1007 unsigned int flags = PAGE_USER | PAGE_PRESENT;
1008
1009 if (aflags & AS_AREA_READ)
1010 flags |= PAGE_READ;
1011
1012 if (aflags & AS_AREA_WRITE)
1013 flags |= PAGE_WRITE;
1014
1015 if (aflags & AS_AREA_EXEC)
1016 flags |= PAGE_EXEC;
1017
1018 if (aflags & AS_AREA_CACHEABLE)
1019 flags |= PAGE_CACHEABLE;
1020
1021 return flags;
1022}
1023
[6745592]1024/** Change adress space area flags.
[c98e6ee]1025 *
1026 * The idea is to have the same data, but with a different access mode.
1027 * This is needed e.g. for writing code into memory and then executing it.
1028 * In order for this to work properly, this may copy the data
1029 * into private anonymous memory (unless it's already there).
1030 *
[76fca31]1031 * @param as Address space.
1032 * @param flags Flags of the area memory.
1033 * @param address Address within the area to be changed.
1034 *
1035 * @return Zero on success or a value from @ref errno.h on failure.
[c98e6ee]1036 *
1037 */
[da1bafb]1038int as_area_change_flags(as_t *as, unsigned int flags, uintptr_t address)
[c98e6ee]1039{
1040 /* Flags for the new memory mapping */
[da1bafb]1041 unsigned int page_flags = area_flags_to_page_flags(flags);
1042
[c98e6ee]1043 mutex_lock(&as->lock);
[da1bafb]1044
1045 as_area_t *area = find_area_and_lock(as, address);
[c98e6ee]1046 if (!area) {
1047 mutex_unlock(&as->lock);
1048 return ENOENT;
1049 }
[da1bafb]1050
[76fca31]1051 if ((area->sh_info) || (area->backend != &anon_backend)) {
[c98e6ee]1052 /* Copying shared areas not supported yet */
1053 /* Copying non-anonymous memory not supported yet */
1054 mutex_unlock(&area->lock);
1055 mutex_unlock(&as->lock);
1056 return ENOTSUP;
1057 }
[da1bafb]1058
[c98e6ee]1059 /*
1060 * Compute total number of used pages in the used_space B+tree
1061 */
[da1bafb]1062 size_t used_pages = 0;
1063
[55b77d9]1064 list_foreach(area->used_space.leaf_list, cur) {
[da1bafb]1065 btree_node_t *node
1066 = list_get_instance(cur, btree_node_t, leaf_link);
1067 btree_key_t i;
[c98e6ee]1068
[da1bafb]1069 for (i = 0; i < node->keys; i++)
[98000fb]1070 used_pages += (size_t) node->value[i];
[c98e6ee]1071 }
[da1bafb]1072
[c98e6ee]1073 /* An array for storing frame numbers */
[da1bafb]1074 uintptr_t *old_frame = malloc(used_pages * sizeof(uintptr_t), 0);
1075
[c964521]1076 page_table_lock(as, false);
[da1bafb]1077
[c98e6ee]1078 /*
1079 * Start TLB shootdown sequence.
1080 */
[402eda5]1081 ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES, as->asid, area->base,
1082 area->pages);
[da1bafb]1083
[c98e6ee]1084 /*
1085 * Remove used pages from page tables and remember their frame
1086 * numbers.
1087 */
[da1bafb]1088 size_t frame_idx = 0;
1089
[55b77d9]1090 list_foreach(area->used_space.leaf_list, cur) {
[b6f3e7e]1091 btree_node_t *node = list_get_instance(cur, btree_node_t,
1092 leaf_link);
[da1bafb]1093 btree_key_t i;
[c98e6ee]1094
1095 for (i = 0; i < node->keys; i++) {
[da1bafb]1096 uintptr_t ptr = node->key[i];
1097 size_t size;
[c98e6ee]1098
[da1bafb]1099 for (size = 0; size < (size_t) node->value[i]; size++) {
[b6f3e7e]1100 pte_t *pte = page_mapping_find(as,
[0ff03f3]1101 ptr + P2SZ(size), false);
[da1bafb]1102
1103 ASSERT(pte);
1104 ASSERT(PTE_VALID(pte));
1105 ASSERT(PTE_PRESENT(pte));
1106
[c98e6ee]1107 old_frame[frame_idx++] = PTE_GET_FRAME(pte);
[da1bafb]1108
[c98e6ee]1109 /* Remove old mapping */
[b6f3e7e]1110 page_mapping_remove(as, ptr + P2SZ(size));
[c98e6ee]1111 }
1112 }
1113 }
[da1bafb]1114
[c98e6ee]1115 /*
1116 * Finish TLB shootdown sequence.
1117 */
[da1bafb]1118
[c98e6ee]1119 tlb_invalidate_pages(as->asid, area->base, area->pages);
[76fca31]1120
[c98e6ee]1121 /*
[eef1b031]1122 * Invalidate potential software translation caches
1123 * (e.g. TSB on sparc64, PHT on ppc32).
[c98e6ee]1124 */
1125 as_invalidate_translation_cache(as, area->base, area->pages);
[402eda5]1126 tlb_shootdown_finalize(ipl);
[da1bafb]1127
[c964521]1128 page_table_unlock(as, false);
[da1bafb]1129
[ae7f6fb]1130 /*
1131 * Set the new flags.
1132 */
1133 area->flags = flags;
[da1bafb]1134
[c98e6ee]1135 /*
1136 * Map pages back in with new flags. This step is kept separate
[6745592]1137 * so that the memory area could not be accesed with both the old and
1138 * the new flags at once.
[c98e6ee]1139 */
1140 frame_idx = 0;
[da1bafb]1141
[55b77d9]1142 list_foreach(area->used_space.leaf_list, cur) {
[da1bafb]1143 btree_node_t *node
1144 = list_get_instance(cur, btree_node_t, leaf_link);
1145 btree_key_t i;
[c98e6ee]1146
1147 for (i = 0; i < node->keys; i++) {
[da1bafb]1148 uintptr_t ptr = node->key[i];
1149 size_t size;
[c98e6ee]1150
[da1bafb]1151 for (size = 0; size < (size_t) node->value[i]; size++) {
[c98e6ee]1152 page_table_lock(as, false);
[da1bafb]1153
[c98e6ee]1154 /* Insert the new mapping */
[b6f3e7e]1155 page_mapping_insert(as, ptr + P2SZ(size),
[c98e6ee]1156 old_frame[frame_idx++], page_flags);
[da1bafb]1157
[c98e6ee]1158 page_table_unlock(as, false);
1159 }
1160 }
1161 }
[da1bafb]1162
[c98e6ee]1163 free(old_frame);
[da1bafb]1164
[c98e6ee]1165 mutex_unlock(&area->lock);
1166 mutex_unlock(&as->lock);
[da1bafb]1167
[c98e6ee]1168 return 0;
1169}
1170
[20d50a1]1171/** Handle page fault within the current address space.
1172 *
[6745592]1173 * This is the high-level page fault handler. It decides whether the page fault
1174 * can be resolved by any backend and if so, it invokes the backend to resolve
1175 * the page fault.
[8182031]1176 *
[20d50a1]1177 * Interrupts are assumed disabled.
1178 *
[da1bafb]1179 * @param page Faulting page.
1180 * @param access Access mode that caused the page fault (i.e.
1181 * read/write/exec).
1182 * @param istate Pointer to the interrupted state.
1183 *
1184 * @return AS_PF_FAULT on page fault.
1185 * @return AS_PF_OK on success.
1186 * @return AS_PF_DEFER if the fault was caused by copy_to_uspace()
1187 * or copy_from_uspace().
[20d50a1]1188 *
1189 */
[7f1c620]1190int as_page_fault(uintptr_t page, pf_access_t access, istate_t *istate)
[20d50a1]1191{
[1068f6a]1192 if (!THREAD)
[8182031]1193 return AS_PF_FAULT;
[7af8c0e]1194
1195 if (!AS)
1196 return AS_PF_FAULT;
1197
[1068f6a]1198 mutex_lock(&AS->lock);
[da1bafb]1199 as_area_t *area = find_area_and_lock(AS, page);
[20d50a1]1200 if (!area) {
1201 /*
1202 * No area contained mapping for 'page'.
1203 * Signal page fault to low-level handler.
1204 */
[1068f6a]1205 mutex_unlock(&AS->lock);
[e3c762cd]1206 goto page_fault;
[20d50a1]1207 }
[da1bafb]1208
[a9e8b39]1209 if (area->attributes & AS_AREA_ATTR_PARTIAL) {
1210 /*
1211 * The address space area is not fully initialized.
1212 * Avoid possible race by returning error.
1213 */
[1068f6a]1214 mutex_unlock(&area->lock);
1215 mutex_unlock(&AS->lock);
[da1bafb]1216 goto page_fault;
[a9e8b39]1217 }
[da1bafb]1218
1219 if ((!area->backend) || (!area->backend->page_fault)) {
[8182031]1220 /*
1221 * The address space area is not backed by any backend
1222 * or the backend cannot handle page faults.
1223 */
1224 mutex_unlock(&area->lock);
1225 mutex_unlock(&AS->lock);
[da1bafb]1226 goto page_fault;
[8182031]1227 }
[da1bafb]1228
[2299914]1229 page_table_lock(AS, false);
1230
1231 /*
[6745592]1232 * To avoid race condition between two page faults on the same address,
1233 * we need to make sure the mapping has not been already inserted.
[2299914]1234 */
[da1bafb]1235 pte_t *pte;
[0ff03f3]1236 if ((pte = page_mapping_find(AS, page, false))) {
[2299914]1237 if (PTE_PRESENT(pte)) {
[fb84455]1238 if (((access == PF_ACCESS_READ) && PTE_READABLE(pte)) ||
[6f4495f5]1239 (access == PF_ACCESS_WRITE && PTE_WRITABLE(pte)) ||
1240 (access == PF_ACCESS_EXEC && PTE_EXECUTABLE(pte))) {
[fb84455]1241 page_table_unlock(AS, false);
1242 mutex_unlock(&area->lock);
1243 mutex_unlock(&AS->lock);
1244 return AS_PF_OK;
1245 }
[2299914]1246 }
1247 }
[20d50a1]1248
1249 /*
[8182031]1250 * Resort to the backend page fault handler.
[20d50a1]1251 */
[0ee077ee]1252 if (area->backend->page_fault(area, page, access) != AS_PF_OK) {
[8182031]1253 page_table_unlock(AS, false);
1254 mutex_unlock(&area->lock);
1255 mutex_unlock(&AS->lock);
1256 goto page_fault;
1257 }
[20d50a1]1258
[8182031]1259 page_table_unlock(AS, false);
[1068f6a]1260 mutex_unlock(&area->lock);
1261 mutex_unlock(&AS->lock);
[e3c762cd]1262 return AS_PF_OK;
[da1bafb]1263
[e3c762cd]1264page_fault:
1265 if (THREAD->in_copy_from_uspace) {
1266 THREAD->in_copy_from_uspace = false;
[6f4495f5]1267 istate_set_retaddr(istate,
1268 (uintptr_t) &memcpy_from_uspace_failover_address);
[e3c762cd]1269 } else if (THREAD->in_copy_to_uspace) {
1270 THREAD->in_copy_to_uspace = false;
[6f4495f5]1271 istate_set_retaddr(istate,
1272 (uintptr_t) &memcpy_to_uspace_failover_address);
[e3c762cd]1273 } else {
1274 return AS_PF_FAULT;
1275 }
[da1bafb]1276
[e3c762cd]1277 return AS_PF_DEFER;
[20d50a1]1278}
1279
[7e4e532]1280/** Switch address spaces.
[1068f6a]1281 *
1282 * Note that this function cannot sleep as it is essentially a part of
[879585a3]1283 * scheduling. Sleeping here would lead to deadlock on wakeup. Another
1284 * thing which is forbidden in this context is locking the address space.
[20d50a1]1285 *
[31d8e10]1286 * When this function is enetered, no spinlocks may be held.
1287 *
[da1bafb]1288 * @param old Old address space or NULL.
1289 * @param new New address space.
1290 *
[20d50a1]1291 */
[80bcaed]1292void as_switch(as_t *old_as, as_t *new_as)
[20d50a1]1293{
[31d8e10]1294 DEADLOCK_PROBE_INIT(p_asidlock);
1295 preemption_disable();
[da1bafb]1296
[31d8e10]1297retry:
1298 (void) interrupts_disable();
1299 if (!spinlock_trylock(&asidlock)) {
[da1bafb]1300 /*
[31d8e10]1301 * Avoid deadlock with TLB shootdown.
1302 * We can enable interrupts here because
1303 * preemption is disabled. We should not be
1304 * holding any other lock.
1305 */
1306 (void) interrupts_enable();
1307 DEADLOCK_PROBE(p_asidlock, DEADLOCK_THRESHOLD);
1308 goto retry;
1309 }
1310 preemption_enable();
[da1bafb]1311
[7e4e532]1312 /*
1313 * First, take care of the old address space.
[da1bafb]1314 */
[80bcaed]1315 if (old_as) {
1316 ASSERT(old_as->cpu_refcount);
[da1bafb]1317
1318 if ((--old_as->cpu_refcount == 0) && (old_as != AS_KERNEL)) {
[7e4e532]1319 /*
1320 * The old address space is no longer active on
1321 * any processor. It can be appended to the
1322 * list of inactive address spaces with assigned
1323 * ASID.
1324 */
[2057572]1325 ASSERT(old_as->asid != ASID_INVALID);
[da1bafb]1326
[2057572]1327 list_append(&old_as->inactive_as_with_asid_link,
[55b77d9]1328 &inactive_as_with_asid_list);
[7e4e532]1329 }
[da1bafb]1330
[57da95c]1331 /*
1332 * Perform architecture-specific tasks when the address space
1333 * is being removed from the CPU.
1334 */
[80bcaed]1335 as_deinstall_arch(old_as);
[7e4e532]1336 }
[da1bafb]1337
[7e4e532]1338 /*
1339 * Second, prepare the new address space.
1340 */
[80bcaed]1341 if ((new_as->cpu_refcount++ == 0) && (new_as != AS_KERNEL)) {
[879585a3]1342 if (new_as->asid != ASID_INVALID)
[80bcaed]1343 list_remove(&new_as->inactive_as_with_asid_link);
[879585a3]1344 else
1345 new_as->asid = asid_get();
[7e4e532]1346 }
[da1bafb]1347
[80bcaed]1348#ifdef AS_PAGE_TABLE
1349 SET_PTL0_ADDRESS(new_as->genarch.page_table);
1350#endif
[7e4e532]1351
[20d50a1]1352 /*
1353 * Perform architecture-specific steps.
[4512d7e]1354 * (e.g. write ASID to hardware register etc.)
[20d50a1]1355 */
[80bcaed]1356 as_install_arch(new_as);
[da1bafb]1357
[879585a3]1358 spinlock_unlock(&asidlock);
[20d50a1]1359
[80bcaed]1360 AS = new_as;
[20d50a1]1361}
[6a3c9a7]1362
[df0103f7]1363/** Compute flags for virtual address translation subsytem.
1364 *
[da1bafb]1365 * @param area Address space area.
1366 *
1367 * @return Flags to be used in page_mapping_insert().
[df0103f7]1368 *
1369 */
[97bdb4a]1370NO_TRACE unsigned int as_area_get_flags(as_area_t *area)
[df0103f7]1371{
[1d432f9]1372 ASSERT(mutex_locked(&area->lock));
[fc47885]1373
[da1bafb]1374 return area_flags_to_page_flags(area->flags);
[df0103f7]1375}
1376
[ef67bab]1377/** Create page table.
1378 *
[6745592]1379 * Depending on architecture, create either address space private or global page
1380 * table.
[ef67bab]1381 *
[da1bafb]1382 * @param flags Flags saying whether the page table is for the kernel
1383 * address space.
1384 *
1385 * @return First entry of the page table.
[ef67bab]1386 *
1387 */
[97bdb4a]1388NO_TRACE pte_t *page_table_create(unsigned int flags)
[ef67bab]1389{
[bd1deed]1390 ASSERT(as_operations);
1391 ASSERT(as_operations->page_table_create);
1392
1393 return as_operations->page_table_create(flags);
[ef67bab]1394}
[d3e7ff4]1395
[482826d]1396/** Destroy page table.
1397 *
1398 * Destroy page table in architecture specific way.
1399 *
[da1bafb]1400 * @param page_table Physical address of PTL0.
1401 *
[482826d]1402 */
[97bdb4a]1403NO_TRACE void page_table_destroy(pte_t *page_table)
[482826d]1404{
[bd1deed]1405 ASSERT(as_operations);
1406 ASSERT(as_operations->page_table_destroy);
1407
1408 as_operations->page_table_destroy(page_table);
[482826d]1409}
1410
[2299914]1411/** Lock page table.
1412 *
1413 * This function should be called before any page_mapping_insert(),
1414 * page_mapping_remove() and page_mapping_find().
[da1bafb]1415 *
[2299914]1416 * Locking order is such that address space areas must be locked
1417 * prior to this call. Address space can be locked prior to this
1418 * call in which case the lock argument is false.
1419 *
[da1bafb]1420 * @param as Address space.
1421 * @param lock If false, do not attempt to lock as->lock.
1422 *
[2299914]1423 */
[97bdb4a]1424NO_TRACE void page_table_lock(as_t *as, bool lock)
[2299914]1425{
1426 ASSERT(as_operations);
1427 ASSERT(as_operations->page_table_lock);
[bd1deed]1428
[2299914]1429 as_operations->page_table_lock(as, lock);
1430}
1431
1432/** Unlock page table.
1433 *
[da1bafb]1434 * @param as Address space.
1435 * @param unlock If false, do not attempt to unlock as->lock.
1436 *
[2299914]1437 */
[97bdb4a]1438NO_TRACE void page_table_unlock(as_t *as, bool unlock)
[2299914]1439{
1440 ASSERT(as_operations);
1441 ASSERT(as_operations->page_table_unlock);
[bd1deed]1442
[2299914]1443 as_operations->page_table_unlock(as, unlock);
1444}
1445
[ada559c]1446/** Test whether page tables are locked.
1447 *
[e3ee9b9]1448 * @param as Address space where the page tables belong.
[ada559c]1449 *
[e3ee9b9]1450 * @return True if the page tables belonging to the address soace
1451 * are locked, otherwise false.
[ada559c]1452 */
[97bdb4a]1453NO_TRACE bool page_table_locked(as_t *as)
[ada559c]1454{
1455 ASSERT(as_operations);
1456 ASSERT(as_operations->page_table_locked);
1457
1458 return as_operations->page_table_locked(as);
1459}
1460
[b878df3]1461/** Return size of the address space area with given base.
1462 *
[1d432f9]1463 * @param base Arbitrary address inside the address space area.
[da1bafb]1464 *
1465 * @return Size of the address space area in bytes or zero if it
1466 * does not exist.
[b878df3]1467 *
1468 */
1469size_t as_area_get_size(uintptr_t base)
[7c23af9]1470{
1471 size_t size;
[da1bafb]1472
[1d432f9]1473 page_table_lock(AS, true);
[da1bafb]1474 as_area_t *src_area = find_area_and_lock(AS, base);
1475
[6745592]1476 if (src_area) {
[b6f3e7e]1477 size = P2SZ(src_area->pages);
[1068f6a]1478 mutex_unlock(&src_area->lock);
[da1bafb]1479 } else
[7c23af9]1480 size = 0;
[da1bafb]1481
[1d432f9]1482 page_table_unlock(AS, true);
[7c23af9]1483 return size;
1484}
1485
[25bf215]1486/** Mark portion of address space area as used.
1487 *
1488 * The address space area must be already locked.
1489 *
[da1bafb]1490 * @param area Address space area.
1491 * @param page First page to be marked.
1492 * @param count Number of page to be marked.
1493 *
[fc47885]1494 * @return False on failure or true on success.
[25bf215]1495 *
1496 */
[fc47885]1497bool used_space_insert(as_area_t *area, uintptr_t page, size_t count)
[25bf215]1498{
[1d432f9]1499 ASSERT(mutex_locked(&area->lock));
[25bf215]1500 ASSERT(page == ALIGN_DOWN(page, PAGE_SIZE));
1501 ASSERT(count);
[da1bafb]1502
1503 btree_node_t *leaf;
1504 size_t pages = (size_t) btree_search(&area->used_space, page, &leaf);
[25bf215]1505 if (pages) {
1506 /*
1507 * We hit the beginning of some used space.
1508 */
[fc47885]1509 return false;
[25bf215]1510 }
[da1bafb]1511
[a6cb8cb]1512 if (!leaf->keys) {
[da1bafb]1513 btree_insert(&area->used_space, page, (void *) count, leaf);
[fc47885]1514 goto success;
[a6cb8cb]1515 }
[da1bafb]1516
1517 btree_node_t *node = btree_leaf_node_left_neighbour(&area->used_space, leaf);
[25bf215]1518 if (node) {
[6f4495f5]1519 uintptr_t left_pg = node->key[node->keys - 1];
1520 uintptr_t right_pg = leaf->key[0];
[98000fb]1521 size_t left_cnt = (size_t) node->value[node->keys - 1];
1522 size_t right_cnt = (size_t) leaf->value[0];
[25bf215]1523
1524 /*
1525 * Examine the possibility that the interval fits
1526 * somewhere between the rightmost interval of
1527 * the left neigbour and the first interval of the leaf.
1528 */
[da1bafb]1529
[25bf215]1530 if (page >= right_pg) {
1531 /* Do nothing. */
[b6f3e7e]1532 } else if (overlaps(page, P2SZ(count), left_pg,
1533 P2SZ(left_cnt))) {
[25bf215]1534 /* The interval intersects with the left interval. */
[fc47885]1535 return false;
[b6f3e7e]1536 } else if (overlaps(page, P2SZ(count), right_pg,
1537 P2SZ(right_cnt))) {
[25bf215]1538 /* The interval intersects with the right interval. */
[fc47885]1539 return false;
[b6f3e7e]1540 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1541 (page + P2SZ(count) == right_pg)) {
[6f4495f5]1542 /*
1543 * The interval can be added by merging the two already
1544 * present intervals.
1545 */
[56789125]1546 node->value[node->keys - 1] += count + right_cnt;
[da1bafb]1547 btree_remove(&area->used_space, right_pg, leaf);
[fc47885]1548 goto success;
[b6f3e7e]1549 } else if (page == left_pg + P2SZ(left_cnt)) {
[da1bafb]1550 /*
[6f4495f5]1551 * The interval can be added by simply growing the left
1552 * interval.
1553 */
[56789125]1554 node->value[node->keys - 1] += count;
[fc47885]1555 goto success;
[b6f3e7e]1556 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1557 /*
[6f4495f5]1558 * The interval can be addded by simply moving base of
1559 * the right interval down and increasing its size
1560 * accordingly.
[25bf215]1561 */
[56789125]1562 leaf->value[0] += count;
[25bf215]1563 leaf->key[0] = page;
[fc47885]1564 goto success;
[25bf215]1565 } else {
1566 /*
1567 * The interval is between both neigbouring intervals,
1568 * but cannot be merged with any of them.
1569 */
[da1bafb]1570 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1571 leaf);
[fc47885]1572 goto success;
[25bf215]1573 }
1574 } else if (page < leaf->key[0]) {
[7f1c620]1575 uintptr_t right_pg = leaf->key[0];
[98000fb]1576 size_t right_cnt = (size_t) leaf->value[0];
[da1bafb]1577
[25bf215]1578 /*
[6f4495f5]1579 * Investigate the border case in which the left neighbour does
1580 * not exist but the interval fits from the left.
[25bf215]1581 */
[da1bafb]1582
[b6f3e7e]1583 if (overlaps(page, P2SZ(count), right_pg, P2SZ(right_cnt))) {
[25bf215]1584 /* The interval intersects with the right interval. */
[fc47885]1585 return false;
[b6f3e7e]1586 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1587 /*
[6f4495f5]1588 * The interval can be added by moving the base of the
1589 * right interval down and increasing its size
1590 * accordingly.
[25bf215]1591 */
1592 leaf->key[0] = page;
[56789125]1593 leaf->value[0] += count;
[fc47885]1594 goto success;
[25bf215]1595 } else {
1596 /*
1597 * The interval doesn't adjoin with the right interval.
1598 * It must be added individually.
1599 */
[da1bafb]1600 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1601 leaf);
[fc47885]1602 goto success;
[25bf215]1603 }
1604 }
[da1bafb]1605
1606 node = btree_leaf_node_right_neighbour(&area->used_space, leaf);
[25bf215]1607 if (node) {
[6f4495f5]1608 uintptr_t left_pg = leaf->key[leaf->keys - 1];
1609 uintptr_t right_pg = node->key[0];
[98000fb]1610 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
1611 size_t right_cnt = (size_t) node->value[0];
[25bf215]1612
1613 /*
1614 * Examine the possibility that the interval fits
1615 * somewhere between the leftmost interval of
1616 * the right neigbour and the last interval of the leaf.
1617 */
[da1bafb]1618
[25bf215]1619 if (page < left_pg) {
1620 /* Do nothing. */
[b6f3e7e]1621 } else if (overlaps(page, P2SZ(count), left_pg,
1622 P2SZ(left_cnt))) {
[25bf215]1623 /* The interval intersects with the left interval. */
[fc47885]1624 return false;
[b6f3e7e]1625 } else if (overlaps(page, P2SZ(count), right_pg,
1626 P2SZ(right_cnt))) {
[25bf215]1627 /* The interval intersects with the right interval. */
[fc47885]1628 return false;
[b6f3e7e]1629 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1630 (page + P2SZ(count) == right_pg)) {
[6f4495f5]1631 /*
1632 * The interval can be added by merging the two already
1633 * present intervals.
[da1bafb]1634 */
[56789125]1635 leaf->value[leaf->keys - 1] += count + right_cnt;
[da1bafb]1636 btree_remove(&area->used_space, right_pg, node);
[fc47885]1637 goto success;
[b6f3e7e]1638 } else if (page == left_pg + P2SZ(left_cnt)) {
[6f4495f5]1639 /*
1640 * The interval can be added by simply growing the left
1641 * interval.
[da1bafb]1642 */
[fc47885]1643 leaf->value[leaf->keys - 1] += count;
1644 goto success;
[b6f3e7e]1645 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1646 /*
[6f4495f5]1647 * The interval can be addded by simply moving base of
1648 * the right interval down and increasing its size
1649 * accordingly.
[25bf215]1650 */
[56789125]1651 node->value[0] += count;
[25bf215]1652 node->key[0] = page;
[fc47885]1653 goto success;
[25bf215]1654 } else {
1655 /*
1656 * The interval is between both neigbouring intervals,
1657 * but cannot be merged with any of them.
1658 */
[da1bafb]1659 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1660 leaf);
[fc47885]1661 goto success;
[25bf215]1662 }
1663 } else if (page >= leaf->key[leaf->keys - 1]) {
[7f1c620]1664 uintptr_t left_pg = leaf->key[leaf->keys - 1];
[98000fb]1665 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
[da1bafb]1666
[25bf215]1667 /*
[6f4495f5]1668 * Investigate the border case in which the right neighbour
1669 * does not exist but the interval fits from the right.
[25bf215]1670 */
[da1bafb]1671
[b6f3e7e]1672 if (overlaps(page, P2SZ(count), left_pg, P2SZ(left_cnt))) {
[56789125]1673 /* The interval intersects with the left interval. */
[fc47885]1674 return false;
[b6f3e7e]1675 } else if (left_pg + P2SZ(left_cnt) == page) {
[6f4495f5]1676 /*
1677 * The interval can be added by growing the left
1678 * interval.
1679 */
[56789125]1680 leaf->value[leaf->keys - 1] += count;
[fc47885]1681 goto success;
[25bf215]1682 } else {
1683 /*
1684 * The interval doesn't adjoin with the left interval.
1685 * It must be added individually.
1686 */
[da1bafb]1687 btree_insert(&area->used_space, page, (void *) count,
[6f4495f5]1688 leaf);
[fc47885]1689 goto success;
[25bf215]1690 }
1691 }
1692
1693 /*
[6f4495f5]1694 * Note that if the algorithm made it thus far, the interval can fit
1695 * only between two other intervals of the leaf. The two border cases
1696 * were already resolved.
[25bf215]1697 */
[da1bafb]1698 btree_key_t i;
[25bf215]1699 for (i = 1; i < leaf->keys; i++) {
1700 if (page < leaf->key[i]) {
[6f4495f5]1701 uintptr_t left_pg = leaf->key[i - 1];
1702 uintptr_t right_pg = leaf->key[i];
[98000fb]1703 size_t left_cnt = (size_t) leaf->value[i - 1];
1704 size_t right_cnt = (size_t) leaf->value[i];
[da1bafb]1705
[25bf215]1706 /*
1707 * The interval fits between left_pg and right_pg.
1708 */
[da1bafb]1709
[b6f3e7e]1710 if (overlaps(page, P2SZ(count), left_pg,
1711 P2SZ(left_cnt))) {
[6f4495f5]1712 /*
1713 * The interval intersects with the left
1714 * interval.
1715 */
[fc47885]1716 return false;
[b6f3e7e]1717 } else if (overlaps(page, P2SZ(count), right_pg,
1718 P2SZ(right_cnt))) {
[6f4495f5]1719 /*
1720 * The interval intersects with the right
1721 * interval.
1722 */
[fc47885]1723 return false;
[b6f3e7e]1724 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1725 (page + P2SZ(count) == right_pg)) {
[6f4495f5]1726 /*
1727 * The interval can be added by merging the two
1728 * already present intervals.
1729 */
[56789125]1730 leaf->value[i - 1] += count + right_cnt;
[da1bafb]1731 btree_remove(&area->used_space, right_pg, leaf);
[fc47885]1732 goto success;
[b6f3e7e]1733 } else if (page == left_pg + P2SZ(left_cnt)) {
[6f4495f5]1734 /*
1735 * The interval can be added by simply growing
1736 * the left interval.
1737 */
[56789125]1738 leaf->value[i - 1] += count;
[fc47885]1739 goto success;
[b6f3e7e]1740 } else if (page + P2SZ(count) == right_pg) {
[25bf215]1741 /*
[da1bafb]1742 * The interval can be addded by simply moving
[6f4495f5]1743 * base of the right interval down and
1744 * increasing its size accordingly.
[da1bafb]1745 */
[56789125]1746 leaf->value[i] += count;
[25bf215]1747 leaf->key[i] = page;
[fc47885]1748 goto success;
[25bf215]1749 } else {
1750 /*
[6f4495f5]1751 * The interval is between both neigbouring
1752 * intervals, but cannot be merged with any of
1753 * them.
[25bf215]1754 */
[da1bafb]1755 btree_insert(&area->used_space, page,
[6f4495f5]1756 (void *) count, leaf);
[fc47885]1757 goto success;
[25bf215]1758 }
1759 }
1760 }
[da1bafb]1761
[7e752b2]1762 panic("Inconsistency detected while adding %zu pages of used "
1763 "space at %p.", count, (void *) page);
[fc47885]1764
1765success:
1766 area->resident += count;
1767 return true;
[25bf215]1768}
1769
1770/** Mark portion of address space area as unused.
1771 *
1772 * The address space area must be already locked.
1773 *
[da1bafb]1774 * @param area Address space area.
1775 * @param page First page to be marked.
1776 * @param count Number of page to be marked.
1777 *
[fc47885]1778 * @return False on failure or true on success.
[25bf215]1779 *
1780 */
[fc47885]1781bool used_space_remove(as_area_t *area, uintptr_t page, size_t count)
[25bf215]1782{
[1d432f9]1783 ASSERT(mutex_locked(&area->lock));
[25bf215]1784 ASSERT(page == ALIGN_DOWN(page, PAGE_SIZE));
1785 ASSERT(count);
[da1bafb]1786
1787 btree_node_t *leaf;
1788 size_t pages = (size_t) btree_search(&area->used_space, page, &leaf);
[25bf215]1789 if (pages) {
1790 /*
1791 * We are lucky, page is the beginning of some interval.
1792 */
1793 if (count > pages) {
[fc47885]1794 return false;
[25bf215]1795 } else if (count == pages) {
[da1bafb]1796 btree_remove(&area->used_space, page, leaf);
[fc47885]1797 goto success;
[25bf215]1798 } else {
1799 /*
1800 * Find the respective interval.
1801 * Decrease its size and relocate its start address.
1802 */
[da1bafb]1803 btree_key_t i;
[25bf215]1804 for (i = 0; i < leaf->keys; i++) {
1805 if (leaf->key[i] == page) {
[b6f3e7e]1806 leaf->key[i] += P2SZ(count);
[56789125]1807 leaf->value[i] -= count;
[fc47885]1808 goto success;
[25bf215]1809 }
1810 }
[fc47885]1811
[25bf215]1812 goto error;
1813 }
1814 }
[da1bafb]1815
[b6f3e7e]1816 btree_node_t *node = btree_leaf_node_left_neighbour(&area->used_space,
1817 leaf);
[da1bafb]1818 if ((node) && (page < leaf->key[0])) {
[7f1c620]1819 uintptr_t left_pg = node->key[node->keys - 1];
[98000fb]1820 size_t left_cnt = (size_t) node->value[node->keys - 1];
[da1bafb]1821
[b6f3e7e]1822 if (overlaps(left_pg, P2SZ(left_cnt), page, P2SZ(count))) {
1823 if (page + P2SZ(count) == left_pg + P2SZ(left_cnt)) {
[25bf215]1824 /*
[6f4495f5]1825 * The interval is contained in the rightmost
1826 * interval of the left neighbour and can be
1827 * removed by updating the size of the bigger
1828 * interval.
[25bf215]1829 */
[56789125]1830 node->value[node->keys - 1] -= count;
[fc47885]1831 goto success;
[b6f3e7e]1832 } else if (page + P2SZ(count) <
1833 left_pg + P2SZ(left_cnt)) {
1834 size_t new_cnt;
1835
[25bf215]1836 /*
[6f4495f5]1837 * The interval is contained in the rightmost
1838 * interval of the left neighbour but its
1839 * removal requires both updating the size of
1840 * the original interval and also inserting a
1841 * new interval.
[25bf215]1842 */
[b6f3e7e]1843 new_cnt = ((left_pg + P2SZ(left_cnt)) -
1844 (page + P2SZ(count))) >> PAGE_WIDTH;
[56789125]1845 node->value[node->keys - 1] -= count + new_cnt;
[da1bafb]1846 btree_insert(&area->used_space, page +
[b6f3e7e]1847 P2SZ(count), (void *) new_cnt, leaf);
[fc47885]1848 goto success;
[25bf215]1849 }
1850 }
[fc47885]1851
1852 return false;
[da1bafb]1853 } else if (page < leaf->key[0])
[fc47885]1854 return false;
[25bf215]1855
1856 if (page > leaf->key[leaf->keys - 1]) {
[7f1c620]1857 uintptr_t left_pg = leaf->key[leaf->keys - 1];
[98000fb]1858 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
[da1bafb]1859
[b6f3e7e]1860 if (overlaps(left_pg, P2SZ(left_cnt), page, P2SZ(count))) {
1861 if (page + P2SZ(count) == left_pg + P2SZ(left_cnt)) {
[25bf215]1862 /*
[6f4495f5]1863 * The interval is contained in the rightmost
1864 * interval of the leaf and can be removed by
1865 * updating the size of the bigger interval.
[25bf215]1866 */
[56789125]1867 leaf->value[leaf->keys - 1] -= count;
[fc47885]1868 goto success;
[b6f3e7e]1869 } else if (page + P2SZ(count) < left_pg +
1870 P2SZ(left_cnt)) {
1871 size_t new_cnt;
1872
[25bf215]1873 /*
[6f4495f5]1874 * The interval is contained in the rightmost
1875 * interval of the leaf but its removal
1876 * requires both updating the size of the
1877 * original interval and also inserting a new
1878 * interval.
[25bf215]1879 */
[b6f3e7e]1880 new_cnt = ((left_pg + P2SZ(left_cnt)) -
1881 (page + P2SZ(count))) >> PAGE_WIDTH;
[56789125]1882 leaf->value[leaf->keys - 1] -= count + new_cnt;
[da1bafb]1883 btree_insert(&area->used_space, page +
[b6f3e7e]1884 P2SZ(count), (void *) new_cnt, leaf);
[fc47885]1885 goto success;
[25bf215]1886 }
1887 }
[fc47885]1888
1889 return false;
[da1bafb]1890 }
[25bf215]1891
1892 /*
1893 * The border cases have been already resolved.
[fc47885]1894 * Now the interval can be only between intervals of the leaf.
[25bf215]1895 */
[da1bafb]1896 btree_key_t i;
[25bf215]1897 for (i = 1; i < leaf->keys - 1; i++) {
1898 if (page < leaf->key[i]) {
[7f1c620]1899 uintptr_t left_pg = leaf->key[i - 1];
[98000fb]1900 size_t left_cnt = (size_t) leaf->value[i - 1];
[da1bafb]1901
[25bf215]1902 /*
[6f4495f5]1903 * Now the interval is between intervals corresponding
1904 * to (i - 1) and i.
[25bf215]1905 */
[b6f3e7e]1906 if (overlaps(left_pg, P2SZ(left_cnt), page,
1907 P2SZ(count))) {
1908 if (page + P2SZ(count) ==
1909 left_pg + P2SZ(left_cnt)) {
[25bf215]1910 /*
[6f4495f5]1911 * The interval is contained in the
1912 * interval (i - 1) of the leaf and can
1913 * be removed by updating the size of
1914 * the bigger interval.
[25bf215]1915 */
[56789125]1916 leaf->value[i - 1] -= count;
[fc47885]1917 goto success;
[b6f3e7e]1918 } else if (page + P2SZ(count) <
1919 left_pg + P2SZ(left_cnt)) {
1920 size_t new_cnt;
1921
[25bf215]1922 /*
[6f4495f5]1923 * The interval is contained in the
1924 * interval (i - 1) of the leaf but its
1925 * removal requires both updating the
1926 * size of the original interval and
[25bf215]1927 * also inserting a new interval.
1928 */
[b6f3e7e]1929 new_cnt = ((left_pg + P2SZ(left_cnt)) -
1930 (page + P2SZ(count))) >>
[6f4495f5]1931 PAGE_WIDTH;
[56789125]1932 leaf->value[i - 1] -= count + new_cnt;
[da1bafb]1933 btree_insert(&area->used_space, page +
[b6f3e7e]1934 P2SZ(count), (void *) new_cnt,
[6f4495f5]1935 leaf);
[fc47885]1936 goto success;
[25bf215]1937 }
1938 }
[fc47885]1939
1940 return false;
[25bf215]1941 }
1942 }
[da1bafb]1943
[25bf215]1944error:
[7e752b2]1945 panic("Inconsistency detected while removing %zu pages of used "
1946 "space from %p.", count, (void *) page);
[fc47885]1947
1948success:
1949 area->resident -= count;
1950 return true;
[25bf215]1951}
1952
[df0103f7]1953/*
1954 * Address space related syscalls.
1955 */
1956
1957/** Wrapper for as_area_create(). */
[96b02eb9]1958sysarg_t sys_as_area_create(uintptr_t address, size_t size, unsigned int flags)
[df0103f7]1959{
[6f4495f5]1960 if (as_area_create(AS, flags | AS_AREA_CACHEABLE, size, address,
1961 AS_AREA_ATTR_NONE, &anon_backend, NULL))
[96b02eb9]1962 return (sysarg_t) address;
[df0103f7]1963 else
[96b02eb9]1964 return (sysarg_t) -1;
[df0103f7]1965}
1966
[c6e314a]1967/** Wrapper for as_area_resize(). */
[96b02eb9]1968sysarg_t sys_as_area_resize(uintptr_t address, size_t size, unsigned int flags)
[df0103f7]1969{
[96b02eb9]1970 return (sysarg_t) as_area_resize(AS, address, size, 0);
[7242a78e]1971}
1972
[c98e6ee]1973/** Wrapper for as_area_change_flags(). */
[96b02eb9]1974sysarg_t sys_as_area_change_flags(uintptr_t address, unsigned int flags)
[c98e6ee]1975{
[96b02eb9]1976 return (sysarg_t) as_area_change_flags(AS, flags, address);
[c98e6ee]1977}
1978
[c6e314a]1979/** Wrapper for as_area_destroy(). */
[96b02eb9]1980sysarg_t sys_as_area_destroy(uintptr_t address)
[7242a78e]1981{
[96b02eb9]1982 return (sysarg_t) as_area_destroy(AS, address);
[df0103f7]1983}
[b45c443]1984
[0b37882]1985/** Return pointer to unmapped address space area
1986 *
1987 * @param base Lowest address bound.
1988 * @param size Requested size of the allocation.
1989 *
1990 * @return Pointer to the beginning of unmapped address space area.
1991 *
1992 */
1993sysarg_t sys_as_get_unmapped_area(uintptr_t base, size_t size)
1994{
1995 if (size == 0)
1996 return 0;
1997
1998 /*
1999 * Make sure we allocate from page-aligned
2000 * address. Check for possible overflow in
2001 * each step.
2002 */
2003
2004 size_t pages = SIZE2FRAMES(size);
2005 uintptr_t ret = 0;
2006
2007 /*
2008 * Find the lowest unmapped address aligned on the sz
2009 * boundary, not smaller than base and of the required size.
2010 */
2011
2012 mutex_lock(&AS->lock);
2013
2014 /* First check the base address itself */
2015 uintptr_t addr = ALIGN_UP(base, PAGE_SIZE);
2016 if ((addr >= base) &&
2017 (check_area_conflicts(AS, addr, pages, NULL)))
2018 ret = addr;
2019
2020 /* Eventually check the addresses behind each area */
[55b77d9]2021 list_foreach(AS->as_area_btree.leaf_list, cur) {
2022 if (ret != 0)
2023 break;
2024
[0b37882]2025 btree_node_t *node =
2026 list_get_instance(cur, btree_node_t, leaf_link);
2027
2028 btree_key_t i;
2029 for (i = 0; (ret == 0) && (i < node->keys); i++) {
[b6f3e7e]2030 uintptr_t addr;
2031
[0b37882]2032 as_area_t *area = (as_area_t *) node->value[i];
2033
2034 mutex_lock(&area->lock);
2035
[b6f3e7e]2036 addr = ALIGN_UP(area->base + P2SZ(area->pages),
[0b37882]2037 PAGE_SIZE);
2038
2039 if ((addr >= base) && (addr >= area->base) &&
2040 (check_area_conflicts(AS, addr, pages, area)))
2041 ret = addr;
2042
2043 mutex_unlock(&area->lock);
2044 }
2045 }
2046
2047 mutex_unlock(&AS->lock);
2048
2049 return (sysarg_t) ret;
2050}
2051
[336db295]2052/** Get list of adress space areas.
2053 *
[da1bafb]2054 * @param as Address space.
2055 * @param obuf Place to save pointer to returned buffer.
2056 * @param osize Place to save size of returned buffer.
2057 *
[336db295]2058 */
2059void as_get_area_info(as_t *as, as_area_info_t **obuf, size_t *osize)
2060{
2061 mutex_lock(&as->lock);
[da1bafb]2062
[336db295]2063 /* First pass, count number of areas. */
[da1bafb]2064
2065 size_t area_cnt = 0;
2066
[55b77d9]2067 list_foreach(as->as_area_btree.leaf_list, cur) {
[da1bafb]2068 btree_node_t *node =
2069 list_get_instance(cur, btree_node_t, leaf_link);
[336db295]2070 area_cnt += node->keys;
2071 }
[da1bafb]2072
2073 size_t isize = area_cnt * sizeof(as_area_info_t);
2074 as_area_info_t *info = malloc(isize, 0);
2075
[336db295]2076 /* Second pass, record data. */
[da1bafb]2077
2078 size_t area_idx = 0;
2079
[55b77d9]2080 list_foreach(as->as_area_btree.leaf_list, cur) {
[da1bafb]2081 btree_node_t *node =
2082 list_get_instance(cur, btree_node_t, leaf_link);
2083 btree_key_t i;
2084
[336db295]2085 for (i = 0; i < node->keys; i++) {
2086 as_area_t *area = node->value[i];
[da1bafb]2087
[336db295]2088 ASSERT(area_idx < area_cnt);
2089 mutex_lock(&area->lock);
[da1bafb]2090
[336db295]2091 info[area_idx].start_addr = area->base;
[b6f3e7e]2092 info[area_idx].size = P2SZ(area->pages);
[336db295]2093 info[area_idx].flags = area->flags;
2094 ++area_idx;
[da1bafb]2095
[336db295]2096 mutex_unlock(&area->lock);
2097 }
2098 }
[da1bafb]2099
[336db295]2100 mutex_unlock(&as->lock);
[da1bafb]2101
[336db295]2102 *obuf = info;
2103 *osize = isize;
2104}
2105
[64c2ad5]2106/** Print out information about address space.
2107 *
[da1bafb]2108 * @param as Address space.
2109 *
[64c2ad5]2110 */
2111void as_print(as_t *as)
2112{
2113 mutex_lock(&as->lock);
2114
[0b37882]2115 /* Print out info about address space areas */
[55b77d9]2116 list_foreach(as->as_area_btree.leaf_list, cur) {
[da1bafb]2117 btree_node_t *node
2118 = list_get_instance(cur, btree_node_t, leaf_link);
2119 btree_key_t i;
[64c2ad5]2120
2121 for (i = 0; i < node->keys; i++) {
[7ba7c6d]2122 as_area_t *area = node->value[i];
[da1bafb]2123
[64c2ad5]2124 mutex_lock(&area->lock);
[7e752b2]2125 printf("as_area: %p, base=%p, pages=%zu"
2126 " (%p - %p)\n", area, (void *) area->base,
2127 area->pages, (void *) area->base,
[b6f3e7e]2128 (void *) (area->base + P2SZ(area->pages)));
[64c2ad5]2129 mutex_unlock(&area->lock);
2130 }
2131 }
2132
2133 mutex_unlock(&as->lock);
2134}
2135
[cc73a8a1]2136/** @}
[b45c443]2137 */
Note: See TracBrowser for help on using the repository browser.