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

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

Declare malloc() etc in standard <stdlib.h> rather than <mm/slab.h>

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