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

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

Define two new as area backend callbacks.

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