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

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

Allow calling page_mapping_find() with unlocked page tables.

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