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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 314f4b59 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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