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

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

remove forward static function declarations and reorder functions
(if not needed for recursion, forward static function declaration only increases source code size and makes it much harder to instantly tell whether a function is actually static or not)

coding style changes
(no change in functionality)

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