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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bd81386 was fc47885, checked in by Martin Decky <martin@…>, 15 years ago

keep track of the number of resident pages on-the-fly (do not traverse the B+ tree while reading sysinfo)
cstyle updates (mostly update of comments)

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