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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f1fae414 was 55b77d9, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Separate list_t typedef from link_t (kernel part).

  • list_t represents lists
  • Use list_first(), list_last(), list_empty() where appropriate
  • Use list_foreach() where possible
  • Replace improper uses of list_prepend() with list_insert_after()
  • Replace improper uses of list_append() with list_insert_before()
  • Property mode set to 100644
File size: 52.5 KB
Line 
1/*
2 * Copyright (c) 2010 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup genericmm
30 * @{
31 */
32
33/**
34 * @file
35 * @brief Address space related functions.
36 *
37 * This file contains address space manipulation functions.
38 * Roughly speaking, this is a higher-level client of
39 * Virtual Address Translation (VAT) subsystem.
40 *
41 * Functionality provided by this file allows one to
42 * create address spaces and create, resize and share
43 * address space areas.
44 *
45 * @see page.c
46 *
47 */
48
49#include <mm/as.h>
50#include <arch/mm/as.h>
51#include <mm/page.h>
52#include <mm/frame.h>
53#include <mm/slab.h>
54#include <mm/tlb.h>
55#include <arch/mm/page.h>
56#include <genarch/mm/page_pt.h>
57#include <genarch/mm/page_ht.h>
58#include <mm/asid.h>
59#include <arch/mm/asid.h>
60#include <preemption.h>
61#include <synch/spinlock.h>
62#include <synch/mutex.h>
63#include <adt/list.h>
64#include <adt/btree.h>
65#include <proc/task.h>
66#include <proc/thread.h>
67#include <arch/asm.h>
68#include <panic.h>
69#include <debug.h>
70#include <print.h>
71#include <memstr.h>
72#include <macros.h>
73#include <bitops.h>
74#include <arch.h>
75#include <errno.h>
76#include <config.h>
77#include <align.h>
78#include <typedefs.h>
79#include <syscall/copy.h>
80#include <arch/interrupt.h>
81
82/**
83 * Each architecture decides what functions will be used to carry out
84 * address space operations such as creating or locking page tables.
85 */
86as_operations_t *as_operations = NULL;
87
88/** Slab for as_t objects.
89 *
90 */
91static slab_cache_t *as_slab;
92
93/** ASID subsystem lock.
94 *
95 * This lock protects:
96 * - inactive_as_with_asid_list
97 * - as->asid for each as of the as_t type
98 * - asids_allocated counter
99 *
100 */
101SPINLOCK_INITIALIZE(asidlock);
102
103/**
104 * Inactive address spaces (on all processors)
105 * that have valid ASID.
106 */
107LIST_INITIALIZE(inactive_as_with_asid_list);
108
109/** Kernel address space. */
110as_t *AS_KERNEL = NULL;
111
112NO_TRACE static int as_constructor(void *obj, unsigned int flags)
113{
114 as_t *as = (as_t *) obj;
115
116 link_initialize(&as->inactive_as_with_asid_link);
117 mutex_initialize(&as->lock, MUTEX_PASSIVE);
118
119 return as_constructor_arch(as, flags);
120}
121
122NO_TRACE static size_t as_destructor(void *obj)
123{
124 return as_destructor_arch((as_t *) obj);
125}
126
127/** Initialize address space subsystem. */
128void as_init(void)
129{
130 as_arch_init();
131
132 as_slab = slab_cache_create("as_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_list));
238
239 btree_node_t *node =
240 list_get_instance(list_first(&as->as_area_btree.leaf_list),
241 btree_node_t, leaf_link);
242
243 if ((cond = node->keys))
244 as_area_destroy(as, node->key[0]);
245 }
246
247 btree_destroy(&as->as_area_btree);
248
249#ifdef AS_PAGE_TABLE
250 page_table_destroy(as->genarch.page_table);
251#else
252 page_table_destroy(NULL);
253#endif
254
255 slab_free(as_slab, as);
256}
257
258/** Hold a reference to an address space.
259 *
260 * Holding a reference to an address space prevents destruction
261 * of that address space.
262 *
263 * @param as Address space to be held.
264 *
265 */
266NO_TRACE void as_hold(as_t *as)
267{
268 atomic_inc(&as->refcount);
269}
270
271/** Release a reference to an address space.
272 *
273 * The last one to release a reference to an address space
274 * destroys the address space.
275 *
276 * @param asAddress space to be released.
277 *
278 */
279NO_TRACE void as_release(as_t *as)
280{
281 if (atomic_predec(&as->refcount) == 0)
282 as_destroy(as);
283}
284
285/** Check area conflicts with other areas.
286 *
287 * @param as Address space.
288 * @param addr Starting virtual address of the area being tested.
289 * @param count Number of pages in the area being tested.
290 * @param 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_list));
605
606 btree_node_t *node =
607 list_get_instance(list_last(&area->used_space.leaf_list),
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
678 * (e.g. TSB on sparc64, PHT on ppc32).
679 */
680 as_invalidate_translation_cache(as, area->base + P2SZ(pages),
681 area->pages - pages);
682 tlb_shootdown_finalize(ipl);
683
684 page_table_unlock(as, false);
685 } else {
686 /*
687 * Growing the area.
688 * Check for overlaps with other address space areas.
689 */
690 if (!check_area_conflicts(as, address, pages, area)) {
691 mutex_unlock(&area->lock);
692 mutex_unlock(&as->lock);
693 return EADDRNOTAVAIL;
694 }
695 }
696
697 if (area->backend && area->backend->resize) {
698 if (!area->backend->resize(area, pages)) {
699 mutex_unlock(&area->lock);
700 mutex_unlock(&as->lock);
701 return ENOMEM;
702 }
703 }
704
705 area->pages = pages;
706
707 mutex_unlock(&area->lock);
708 mutex_unlock(&as->lock);
709
710 return 0;
711}
712
713/** Remove reference to address space area share info.
714 *
715 * If the reference count drops to 0, the sh_info is deallocated.
716 *
717 * @param sh_info Pointer to address space area share info.
718 *
719 */
720NO_TRACE static void sh_info_remove_reference(share_info_t *sh_info)
721{
722 bool dealloc = false;
723
724 mutex_lock(&sh_info->lock);
725 ASSERT(sh_info->refcount);
726
727 if (--sh_info->refcount == 0) {
728 dealloc = true;
729
730 /*
731 * Now walk carefully the pagemap B+tree and free/remove
732 * reference from all frames found there.
733 */
734 list_foreach(sh_info->pagemap.leaf_list, cur) {
735 btree_node_t *node
736 = list_get_instance(cur, btree_node_t, leaf_link);
737 btree_key_t i;
738
739 for (i = 0; i < node->keys; i++)
740 frame_free((uintptr_t) node->value[i]);
741 }
742
743 }
744 mutex_unlock(&sh_info->lock);
745
746 if (dealloc) {
747 btree_destroy(&sh_info->pagemap);
748 free(sh_info);
749 }
750}
751
752/** Destroy address space area.
753 *
754 * @param as Address space.
755 * @param address Address within the area to be deleted.
756 *
757 * @return Zero on success or a value from @ref errno.h on failure.
758 *
759 */
760int as_area_destroy(as_t *as, uintptr_t address)
761{
762 mutex_lock(&as->lock);
763
764 as_area_t *area = find_area_and_lock(as, address);
765 if (!area) {
766 mutex_unlock(&as->lock);
767 return ENOENT;
768 }
769
770 if (area->backend && area->backend->destroy)
771 area->backend->destroy(area);
772
773 uintptr_t base = area->base;
774
775 page_table_lock(as, false);
776
777 /*
778 * Start TLB shootdown sequence.
779 */
780 ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES, as->asid, area->base,
781 area->pages);
782
783 /*
784 * Visit only the pages mapped by used_space B+tree.
785 */
786 list_foreach(area->used_space.leaf_list, cur) {
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,
797 ptr + P2SZ(size), false);
798
799 ASSERT(pte);
800 ASSERT(PTE_VALID(pte));
801 ASSERT(PTE_PRESENT(pte));
802
803 if ((area->backend) &&
804 (area->backend->frame_free)) {
805 area->backend->frame_free(area,
806 ptr + P2SZ(size),
807 PTE_GET_FRAME(pte));
808 }
809
810 page_mapping_remove(as, ptr + P2SZ(size));
811 }
812 }
813 }
814
815 /*
816 * Finish TLB shootdown sequence.
817 */
818
819 tlb_invalidate_pages(as->asid, area->base, area->pages);
820
821 /*
822 * Invalidate potential software translation caches
823 * (e.g. TSB on sparc64, PHT on ppc32).
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 btree_remove(&as->as_area_btree, base, NULL);
843
844 free(area);
845
846 mutex_unlock(&as->lock);
847 return 0;
848}
849
850/** Share address space area with another or the same address space.
851 *
852 * Address space area mapping is shared with a new address space area.
853 * If the source address space area has not been shared so far,
854 * a new sh_info is created. The new address space area simply gets the
855 * sh_info of the source area. The process of duplicating the
856 * mapping is done through the backend share function.
857 *
858 * @param src_as Pointer to source address space.
859 * @param src_base Base address of the source address space area.
860 * @param acc_size Expected size of the source area.
861 * @param dst_as Pointer to destination address space.
862 * @param dst_base Target base address.
863 * @param dst_flags_mask Destination address space area flags mask.
864 *
865 * @return Zero on success.
866 * @return ENOENT if there is no such task or such address space.
867 * @return EPERM if there was a problem in accepting the area.
868 * @return ENOMEM if there was a problem in allocating destination
869 * address space area.
870 * @return ENOTSUP if the address space area backend does not support
871 * sharing.
872 *
873 */
874int as_area_share(as_t *src_as, uintptr_t src_base, size_t acc_size,
875 as_t *dst_as, uintptr_t dst_base, unsigned int dst_flags_mask)
876{
877 mutex_lock(&src_as->lock);
878 as_area_t *src_area = find_area_and_lock(src_as, src_base);
879 if (!src_area) {
880 /*
881 * Could not find the source address space area.
882 */
883 mutex_unlock(&src_as->lock);
884 return ENOENT;
885 }
886
887 if ((!src_area->backend) || (!src_area->backend->share)) {
888 /*
889 * There is no backend or the backend does not
890 * know how to share the area.
891 */
892 mutex_unlock(&src_area->lock);
893 mutex_unlock(&src_as->lock);
894 return ENOTSUP;
895 }
896
897 size_t src_size = P2SZ(src_area->pages);
898 unsigned int src_flags = src_area->flags;
899 mem_backend_t *src_backend = src_area->backend;
900 mem_backend_data_t src_backend_data = src_area->backend_data;
901
902 /* Share the cacheable flag from the original mapping */
903 if (src_flags & AS_AREA_CACHEABLE)
904 dst_flags_mask |= AS_AREA_CACHEABLE;
905
906 if ((src_size != acc_size) ||
907 ((src_flags & dst_flags_mask) != dst_flags_mask)) {
908 mutex_unlock(&src_area->lock);
909 mutex_unlock(&src_as->lock);
910 return EPERM;
911 }
912
913 /*
914 * Now we are committed to sharing the area.
915 * First, prepare the area for sharing.
916 * Then it will be safe to unlock it.
917 */
918 share_info_t *sh_info = src_area->sh_info;
919 if (!sh_info) {
920 sh_info = (share_info_t *) malloc(sizeof(share_info_t), 0);
921 mutex_initialize(&sh_info->lock, MUTEX_PASSIVE);
922 sh_info->refcount = 2;
923 btree_create(&sh_info->pagemap);
924 src_area->sh_info = sh_info;
925
926 /*
927 * Call the backend to setup sharing.
928 */
929 src_area->backend->share(src_area);
930 } else {
931 mutex_lock(&sh_info->lock);
932 sh_info->refcount++;
933 mutex_unlock(&sh_info->lock);
934 }
935
936 mutex_unlock(&src_area->lock);
937 mutex_unlock(&src_as->lock);
938
939 /*
940 * Create copy of the source address space area.
941 * The destination area is created with AS_AREA_ATTR_PARTIAL
942 * attribute set which prevents race condition with
943 * preliminary as_page_fault() calls.
944 * The flags of the source area are masked against dst_flags_mask
945 * to support sharing in less privileged mode.
946 */
947 as_area_t *dst_area = as_area_create(dst_as, dst_flags_mask, src_size,
948 dst_base, AS_AREA_ATTR_PARTIAL, src_backend, &src_backend_data);
949 if (!dst_area) {
950 /*
951 * Destination address space area could not be created.
952 */
953 sh_info_remove_reference(sh_info);
954
955 return ENOMEM;
956 }
957
958 /*
959 * Now the destination address space area has been
960 * fully initialized. Clear the AS_AREA_ATTR_PARTIAL
961 * attribute and set the sh_info.
962 */
963 mutex_lock(&dst_as->lock);
964 mutex_lock(&dst_area->lock);
965 dst_area->attributes &= ~AS_AREA_ATTR_PARTIAL;
966 dst_area->sh_info = sh_info;
967 mutex_unlock(&dst_area->lock);
968 mutex_unlock(&dst_as->lock);
969
970 return 0;
971}
972
973/** Check access mode for address space area.
974 *
975 * @param area Address space area.
976 * @param access Access mode.
977 *
978 * @return False if access violates area's permissions, true
979 * otherwise.
980 *
981 */
982NO_TRACE bool as_area_check_access(as_area_t *area, pf_access_t access)
983{
984 ASSERT(mutex_locked(&area->lock));
985
986 int flagmap[] = {
987 [PF_ACCESS_READ] = AS_AREA_READ,
988 [PF_ACCESS_WRITE] = AS_AREA_WRITE,
989 [PF_ACCESS_EXEC] = AS_AREA_EXEC
990 };
991
992 if (!(area->flags & flagmap[access]))
993 return false;
994
995 return true;
996}
997
998/** Convert address space area flags to page flags.
999 *
1000 * @param aflags Flags of some address space area.
1001 *
1002 * @return Flags to be passed to page_mapping_insert().
1003 *
1004 */
1005NO_TRACE static unsigned int area_flags_to_page_flags(unsigned int aflags)
1006{
1007 unsigned int flags = PAGE_USER | PAGE_PRESENT;
1008
1009 if (aflags & AS_AREA_READ)
1010 flags |= PAGE_READ;
1011
1012 if (aflags & AS_AREA_WRITE)
1013 flags |= PAGE_WRITE;
1014
1015 if (aflags & AS_AREA_EXEC)
1016 flags |= PAGE_EXEC;
1017
1018 if (aflags & AS_AREA_CACHEABLE)
1019 flags |= PAGE_CACHEABLE;
1020
1021 return flags;
1022}
1023
1024/** Change adress space area flags.
1025 *
1026 * The idea is to have the same data, but with a different access mode.
1027 * This is needed e.g. for writing code into memory and then executing it.
1028 * In order for this to work properly, this may copy the data
1029 * into private anonymous memory (unless it's already there).
1030 *
1031 * @param as Address space.
1032 * @param flags Flags of the area memory.
1033 * @param address Address within the area to be changed.
1034 *
1035 * @return Zero on success or a value from @ref errno.h on failure.
1036 *
1037 */
1038int as_area_change_flags(as_t *as, unsigned int flags, uintptr_t address)
1039{
1040 /* Flags for the new memory mapping */
1041 unsigned int page_flags = area_flags_to_page_flags(flags);
1042
1043 mutex_lock(&as->lock);
1044
1045 as_area_t *area = find_area_and_lock(as, address);
1046 if (!area) {
1047 mutex_unlock(&as->lock);
1048 return ENOENT;
1049 }
1050
1051 if ((area->sh_info) || (area->backend != &anon_backend)) {
1052 /* Copying shared areas not supported yet */
1053 /* Copying non-anonymous memory not supported yet */
1054 mutex_unlock(&area->lock);
1055 mutex_unlock(&as->lock);
1056 return ENOTSUP;
1057 }
1058
1059 /*
1060 * Compute total number of used pages in the used_space B+tree
1061 */
1062 size_t used_pages = 0;
1063
1064 list_foreach(area->used_space.leaf_list, cur) {
1065 btree_node_t *node
1066 = list_get_instance(cur, btree_node_t, leaf_link);
1067 btree_key_t i;
1068
1069 for (i = 0; i < node->keys; i++)
1070 used_pages += (size_t) node->value[i];
1071 }
1072
1073 /* An array for storing frame numbers */
1074 uintptr_t *old_frame = malloc(used_pages * sizeof(uintptr_t), 0);
1075
1076 page_table_lock(as, false);
1077
1078 /*
1079 * Start TLB shootdown sequence.
1080 */
1081 ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES, as->asid, area->base,
1082 area->pages);
1083
1084 /*
1085 * Remove used pages from page tables and remember their frame
1086 * numbers.
1087 */
1088 size_t frame_idx = 0;
1089
1090 list_foreach(area->used_space.leaf_list, cur) {
1091 btree_node_t *node = list_get_instance(cur, btree_node_t,
1092 leaf_link);
1093 btree_key_t i;
1094
1095 for (i = 0; i < node->keys; i++) {
1096 uintptr_t ptr = node->key[i];
1097 size_t size;
1098
1099 for (size = 0; size < (size_t) node->value[i]; size++) {
1100 pte_t *pte = page_mapping_find(as,
1101 ptr + P2SZ(size), false);
1102
1103 ASSERT(pte);
1104 ASSERT(PTE_VALID(pte));
1105 ASSERT(PTE_PRESENT(pte));
1106
1107 old_frame[frame_idx++] = PTE_GET_FRAME(pte);
1108
1109 /* Remove old mapping */
1110 page_mapping_remove(as, ptr + P2SZ(size));
1111 }
1112 }
1113 }
1114
1115 /*
1116 * Finish TLB shootdown sequence.
1117 */
1118
1119 tlb_invalidate_pages(as->asid, area->base, area->pages);
1120
1121 /*
1122 * Invalidate potential software translation caches
1123 * (e.g. TSB on sparc64, PHT on ppc32).
1124 */
1125 as_invalidate_translation_cache(as, area->base, area->pages);
1126 tlb_shootdown_finalize(ipl);
1127
1128 page_table_unlock(as, false);
1129
1130 /*
1131 * Set the new flags.
1132 */
1133 area->flags = flags;
1134
1135 /*
1136 * Map pages back in with new flags. This step is kept separate
1137 * so that the memory area could not be accesed with both the old and
1138 * the new flags at once.
1139 */
1140 frame_idx = 0;
1141
1142 list_foreach(area->used_space.leaf_list, cur) {
1143 btree_node_t *node
1144 = list_get_instance(cur, btree_node_t, leaf_link);
1145 btree_key_t i;
1146
1147 for (i = 0; i < node->keys; i++) {
1148 uintptr_t ptr = node->key[i];
1149 size_t size;
1150
1151 for (size = 0; size < (size_t) node->value[i]; size++) {
1152 page_table_lock(as, false);
1153
1154 /* Insert the new mapping */
1155 page_mapping_insert(as, ptr + P2SZ(size),
1156 old_frame[frame_idx++], page_flags);
1157
1158 page_table_unlock(as, false);
1159 }
1160 }
1161 }
1162
1163 free(old_frame);
1164
1165 mutex_unlock(&area->lock);
1166 mutex_unlock(&as->lock);
1167
1168 return 0;
1169}
1170
1171/** Handle page fault within the current address space.
1172 *
1173 * This is the high-level page fault handler. It decides whether the page fault
1174 * can be resolved by any backend and if so, it invokes the backend to resolve
1175 * the page fault.
1176 *
1177 * Interrupts are assumed disabled.
1178 *
1179 * @param page Faulting page.
1180 * @param access Access mode that caused the page fault (i.e.
1181 * read/write/exec).
1182 * @param istate Pointer to the interrupted state.
1183 *
1184 * @return AS_PF_FAULT on page fault.
1185 * @return AS_PF_OK on success.
1186 * @return AS_PF_DEFER if the fault was caused by copy_to_uspace()
1187 * or copy_from_uspace().
1188 *
1189 */
1190int as_page_fault(uintptr_t page, pf_access_t access, istate_t *istate)
1191{
1192 if (!THREAD)
1193 return AS_PF_FAULT;
1194
1195 if (!AS)
1196 return AS_PF_FAULT;
1197
1198 mutex_lock(&AS->lock);
1199 as_area_t *area = find_area_and_lock(AS, page);
1200 if (!area) {
1201 /*
1202 * No area contained mapping for 'page'.
1203 * Signal page fault to low-level handler.
1204 */
1205 mutex_unlock(&AS->lock);
1206 goto page_fault;
1207 }
1208
1209 if (area->attributes & AS_AREA_ATTR_PARTIAL) {
1210 /*
1211 * The address space area is not fully initialized.
1212 * Avoid possible race by returning error.
1213 */
1214 mutex_unlock(&area->lock);
1215 mutex_unlock(&AS->lock);
1216 goto page_fault;
1217 }
1218
1219 if ((!area->backend) || (!area->backend->page_fault)) {
1220 /*
1221 * The address space area is not backed by any backend
1222 * or the backend cannot handle page faults.
1223 */
1224 mutex_unlock(&area->lock);
1225 mutex_unlock(&AS->lock);
1226 goto page_fault;
1227 }
1228
1229 page_table_lock(AS, false);
1230
1231 /*
1232 * To avoid race condition between two page faults on the same address,
1233 * we need to make sure the mapping has not been already inserted.
1234 */
1235 pte_t *pte;
1236 if ((pte = page_mapping_find(AS, page, false))) {
1237 if (PTE_PRESENT(pte)) {
1238 if (((access == PF_ACCESS_READ) && PTE_READABLE(pte)) ||
1239 (access == PF_ACCESS_WRITE && PTE_WRITABLE(pte)) ||
1240 (access == PF_ACCESS_EXEC && PTE_EXECUTABLE(pte))) {
1241 page_table_unlock(AS, false);
1242 mutex_unlock(&area->lock);
1243 mutex_unlock(&AS->lock);
1244 return AS_PF_OK;
1245 }
1246 }
1247 }
1248
1249 /*
1250 * Resort to the backend page fault handler.
1251 */
1252 if (area->backend->page_fault(area, page, access) != AS_PF_OK) {
1253 page_table_unlock(AS, false);
1254 mutex_unlock(&area->lock);
1255 mutex_unlock(&AS->lock);
1256 goto page_fault;
1257 }
1258
1259 page_table_unlock(AS, false);
1260 mutex_unlock(&area->lock);
1261 mutex_unlock(&AS->lock);
1262 return AS_PF_OK;
1263
1264page_fault:
1265 if (THREAD->in_copy_from_uspace) {
1266 THREAD->in_copy_from_uspace = false;
1267 istate_set_retaddr(istate,
1268 (uintptr_t) &memcpy_from_uspace_failover_address);
1269 } else if (THREAD->in_copy_to_uspace) {
1270 THREAD->in_copy_to_uspace = false;
1271 istate_set_retaddr(istate,
1272 (uintptr_t) &memcpy_to_uspace_failover_address);
1273 } else {
1274 return AS_PF_FAULT;
1275 }
1276
1277 return AS_PF_DEFER;
1278}
1279
1280/** Switch address spaces.
1281 *
1282 * Note that this function cannot sleep as it is essentially a part of
1283 * scheduling. Sleeping here would lead to deadlock on wakeup. Another
1284 * thing which is forbidden in this context is locking the address space.
1285 *
1286 * When this function is enetered, no spinlocks may be held.
1287 *
1288 * @param old Old address space or NULL.
1289 * @param new New address space.
1290 *
1291 */
1292void as_switch(as_t *old_as, as_t *new_as)
1293{
1294 DEADLOCK_PROBE_INIT(p_asidlock);
1295 preemption_disable();
1296
1297retry:
1298 (void) interrupts_disable();
1299 if (!spinlock_trylock(&asidlock)) {
1300 /*
1301 * Avoid deadlock with TLB shootdown.
1302 * We can enable interrupts here because
1303 * preemption is disabled. We should not be
1304 * holding any other lock.
1305 */
1306 (void) interrupts_enable();
1307 DEADLOCK_PROBE(p_asidlock, DEADLOCK_THRESHOLD);
1308 goto retry;
1309 }
1310 preemption_enable();
1311
1312 /*
1313 * First, take care of the old address space.
1314 */
1315 if (old_as) {
1316 ASSERT(old_as->cpu_refcount);
1317
1318 if ((--old_as->cpu_refcount == 0) && (old_as != AS_KERNEL)) {
1319 /*
1320 * The old address space is no longer active on
1321 * any processor. It can be appended to the
1322 * list of inactive address spaces with assigned
1323 * ASID.
1324 */
1325 ASSERT(old_as->asid != ASID_INVALID);
1326
1327 list_append(&old_as->inactive_as_with_asid_link,
1328 &inactive_as_with_asid_list);
1329 }
1330
1331 /*
1332 * Perform architecture-specific tasks when the address space
1333 * is being removed from the CPU.
1334 */
1335 as_deinstall_arch(old_as);
1336 }
1337
1338 /*
1339 * Second, prepare the new address space.
1340 */
1341 if ((new_as->cpu_refcount++ == 0) && (new_as != AS_KERNEL)) {
1342 if (new_as->asid != ASID_INVALID)
1343 list_remove(&new_as->inactive_as_with_asid_link);
1344 else
1345 new_as->asid = asid_get();
1346 }
1347
1348#ifdef AS_PAGE_TABLE
1349 SET_PTL0_ADDRESS(new_as->genarch.page_table);
1350#endif
1351
1352 /*
1353 * Perform architecture-specific steps.
1354 * (e.g. write ASID to hardware register etc.)
1355 */
1356 as_install_arch(new_as);
1357
1358 spinlock_unlock(&asidlock);
1359
1360 AS = new_as;
1361}
1362
1363/** Compute flags for virtual address translation subsytem.
1364 *
1365 * @param area Address space area.
1366 *
1367 * @return Flags to be used in page_mapping_insert().
1368 *
1369 */
1370NO_TRACE unsigned int as_area_get_flags(as_area_t *area)
1371{
1372 ASSERT(mutex_locked(&area->lock));
1373
1374 return area_flags_to_page_flags(area->flags);
1375}
1376
1377/** Create page table.
1378 *
1379 * Depending on architecture, create either address space private or global page
1380 * table.
1381 *
1382 * @param flags Flags saying whether the page table is for the kernel
1383 * address space.
1384 *
1385 * @return First entry of the page table.
1386 *
1387 */
1388NO_TRACE pte_t *page_table_create(unsigned int flags)
1389{
1390 ASSERT(as_operations);
1391 ASSERT(as_operations->page_table_create);
1392
1393 return as_operations->page_table_create(flags);
1394}
1395
1396/** Destroy page table.
1397 *
1398 * Destroy page table in architecture specific way.
1399 *
1400 * @param page_table Physical address of PTL0.
1401 *
1402 */
1403NO_TRACE void page_table_destroy(pte_t *page_table)
1404{
1405 ASSERT(as_operations);
1406 ASSERT(as_operations->page_table_destroy);
1407
1408 as_operations->page_table_destroy(page_table);
1409}
1410
1411/** Lock page table.
1412 *
1413 * This function should be called before any page_mapping_insert(),
1414 * page_mapping_remove() and page_mapping_find().
1415 *
1416 * Locking order is such that address space areas must be locked
1417 * prior to this call. Address space can be locked prior to this
1418 * call in which case the lock argument is false.
1419 *
1420 * @param as Address space.
1421 * @param lock If false, do not attempt to lock as->lock.
1422 *
1423 */
1424NO_TRACE void page_table_lock(as_t *as, bool lock)
1425{
1426 ASSERT(as_operations);
1427 ASSERT(as_operations->page_table_lock);
1428
1429 as_operations->page_table_lock(as, lock);
1430}
1431
1432/** Unlock page table.
1433 *
1434 * @param as Address space.
1435 * @param unlock If false, do not attempt to unlock as->lock.
1436 *
1437 */
1438NO_TRACE void page_table_unlock(as_t *as, bool unlock)
1439{
1440 ASSERT(as_operations);
1441 ASSERT(as_operations->page_table_unlock);
1442
1443 as_operations->page_table_unlock(as, unlock);
1444}
1445
1446/** Test whether page tables are locked.
1447 *
1448 * @param as Address space where the page tables belong.
1449 *
1450 * @return True if the page tables belonging to the address soace
1451 * are locked, otherwise false.
1452 */
1453NO_TRACE bool page_table_locked(as_t *as)
1454{
1455 ASSERT(as_operations);
1456 ASSERT(as_operations->page_table_locked);
1457
1458 return as_operations->page_table_locked(as);
1459}
1460
1461/** Return size of the address space area with given base.
1462 *
1463 * @param base Arbitrary address inside the address space area.
1464 *
1465 * @return Size of the address space area in bytes or zero if it
1466 * does not exist.
1467 *
1468 */
1469size_t as_area_get_size(uintptr_t base)
1470{
1471 size_t size;
1472
1473 page_table_lock(AS, true);
1474 as_area_t *src_area = find_area_and_lock(AS, base);
1475
1476 if (src_area) {
1477 size = P2SZ(src_area->pages);
1478 mutex_unlock(&src_area->lock);
1479 } else
1480 size = 0;
1481
1482 page_table_unlock(AS, true);
1483 return size;
1484}
1485
1486/** Mark portion of address space area as used.
1487 *
1488 * The address space area must be already locked.
1489 *
1490 * @param area Address space area.
1491 * @param page First page to be marked.
1492 * @param count Number of page to be marked.
1493 *
1494 * @return False on failure or true on success.
1495 *
1496 */
1497bool used_space_insert(as_area_t *area, uintptr_t page, size_t count)
1498{
1499 ASSERT(mutex_locked(&area->lock));
1500 ASSERT(page == ALIGN_DOWN(page, PAGE_SIZE));
1501 ASSERT(count);
1502
1503 btree_node_t *leaf;
1504 size_t pages = (size_t) btree_search(&area->used_space, page, &leaf);
1505 if (pages) {
1506 /*
1507 * We hit the beginning of some used space.
1508 */
1509 return false;
1510 }
1511
1512 if (!leaf->keys) {
1513 btree_insert(&area->used_space, page, (void *) count, leaf);
1514 goto success;
1515 }
1516
1517 btree_node_t *node = btree_leaf_node_left_neighbour(&area->used_space, leaf);
1518 if (node) {
1519 uintptr_t left_pg = node->key[node->keys - 1];
1520 uintptr_t right_pg = leaf->key[0];
1521 size_t left_cnt = (size_t) node->value[node->keys - 1];
1522 size_t right_cnt = (size_t) leaf->value[0];
1523
1524 /*
1525 * Examine the possibility that the interval fits
1526 * somewhere between the rightmost interval of
1527 * the left neigbour and the first interval of the leaf.
1528 */
1529
1530 if (page >= right_pg) {
1531 /* Do nothing. */
1532 } else if (overlaps(page, P2SZ(count), left_pg,
1533 P2SZ(left_cnt))) {
1534 /* The interval intersects with the left interval. */
1535 return false;
1536 } else if (overlaps(page, P2SZ(count), right_pg,
1537 P2SZ(right_cnt))) {
1538 /* The interval intersects with the right interval. */
1539 return false;
1540 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1541 (page + P2SZ(count) == right_pg)) {
1542 /*
1543 * The interval can be added by merging the two already
1544 * present intervals.
1545 */
1546 node->value[node->keys - 1] += count + right_cnt;
1547 btree_remove(&area->used_space, right_pg, leaf);
1548 goto success;
1549 } else if (page == left_pg + P2SZ(left_cnt)) {
1550 /*
1551 * The interval can be added by simply growing the left
1552 * interval.
1553 */
1554 node->value[node->keys - 1] += count;
1555 goto success;
1556 } else if (page + P2SZ(count) == right_pg) {
1557 /*
1558 * The interval can be addded by simply moving base of
1559 * the right interval down and increasing its size
1560 * accordingly.
1561 */
1562 leaf->value[0] += count;
1563 leaf->key[0] = page;
1564 goto success;
1565 } else {
1566 /*
1567 * The interval is between both neigbouring intervals,
1568 * but cannot be merged with any of them.
1569 */
1570 btree_insert(&area->used_space, page, (void *) count,
1571 leaf);
1572 goto success;
1573 }
1574 } else if (page < leaf->key[0]) {
1575 uintptr_t right_pg = leaf->key[0];
1576 size_t right_cnt = (size_t) leaf->value[0];
1577
1578 /*
1579 * Investigate the border case in which the left neighbour does
1580 * not exist but the interval fits from the left.
1581 */
1582
1583 if (overlaps(page, P2SZ(count), right_pg, P2SZ(right_cnt))) {
1584 /* The interval intersects with the right interval. */
1585 return false;
1586 } else if (page + P2SZ(count) == right_pg) {
1587 /*
1588 * The interval can be added by moving the base of the
1589 * right interval down and increasing its size
1590 * accordingly.
1591 */
1592 leaf->key[0] = page;
1593 leaf->value[0] += count;
1594 goto success;
1595 } else {
1596 /*
1597 * The interval doesn't adjoin with the right interval.
1598 * It must be added individually.
1599 */
1600 btree_insert(&area->used_space, page, (void *) count,
1601 leaf);
1602 goto success;
1603 }
1604 }
1605
1606 node = btree_leaf_node_right_neighbour(&area->used_space, leaf);
1607 if (node) {
1608 uintptr_t left_pg = leaf->key[leaf->keys - 1];
1609 uintptr_t right_pg = node->key[0];
1610 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
1611 size_t right_cnt = (size_t) node->value[0];
1612
1613 /*
1614 * Examine the possibility that the interval fits
1615 * somewhere between the leftmost interval of
1616 * the right neigbour and the last interval of the leaf.
1617 */
1618
1619 if (page < left_pg) {
1620 /* Do nothing. */
1621 } else if (overlaps(page, P2SZ(count), left_pg,
1622 P2SZ(left_cnt))) {
1623 /* The interval intersects with the left interval. */
1624 return false;
1625 } else if (overlaps(page, P2SZ(count), right_pg,
1626 P2SZ(right_cnt))) {
1627 /* The interval intersects with the right interval. */
1628 return false;
1629 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1630 (page + P2SZ(count) == right_pg)) {
1631 /*
1632 * The interval can be added by merging the two already
1633 * present intervals.
1634 */
1635 leaf->value[leaf->keys - 1] += count + right_cnt;
1636 btree_remove(&area->used_space, right_pg, node);
1637 goto success;
1638 } else if (page == left_pg + P2SZ(left_cnt)) {
1639 /*
1640 * The interval can be added by simply growing the left
1641 * interval.
1642 */
1643 leaf->value[leaf->keys - 1] += count;
1644 goto success;
1645 } else if (page + P2SZ(count) == right_pg) {
1646 /*
1647 * The interval can be addded by simply moving base of
1648 * the right interval down and increasing its size
1649 * accordingly.
1650 */
1651 node->value[0] += count;
1652 node->key[0] = page;
1653 goto success;
1654 } else {
1655 /*
1656 * The interval is between both neigbouring intervals,
1657 * but cannot be merged with any of them.
1658 */
1659 btree_insert(&area->used_space, page, (void *) count,
1660 leaf);
1661 goto success;
1662 }
1663 } else if (page >= leaf->key[leaf->keys - 1]) {
1664 uintptr_t left_pg = leaf->key[leaf->keys - 1];
1665 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
1666
1667 /*
1668 * Investigate the border case in which the right neighbour
1669 * does not exist but the interval fits from the right.
1670 */
1671
1672 if (overlaps(page, P2SZ(count), left_pg, P2SZ(left_cnt))) {
1673 /* The interval intersects with the left interval. */
1674 return false;
1675 } else if (left_pg + P2SZ(left_cnt) == page) {
1676 /*
1677 * The interval can be added by growing the left
1678 * interval.
1679 */
1680 leaf->value[leaf->keys - 1] += count;
1681 goto success;
1682 } else {
1683 /*
1684 * The interval doesn't adjoin with the left interval.
1685 * It must be added individually.
1686 */
1687 btree_insert(&area->used_space, page, (void *) count,
1688 leaf);
1689 goto success;
1690 }
1691 }
1692
1693 /*
1694 * Note that if the algorithm made it thus far, the interval can fit
1695 * only between two other intervals of the leaf. The two border cases
1696 * were already resolved.
1697 */
1698 btree_key_t i;
1699 for (i = 1; i < leaf->keys; i++) {
1700 if (page < leaf->key[i]) {
1701 uintptr_t left_pg = leaf->key[i - 1];
1702 uintptr_t right_pg = leaf->key[i];
1703 size_t left_cnt = (size_t) leaf->value[i - 1];
1704 size_t right_cnt = (size_t) leaf->value[i];
1705
1706 /*
1707 * The interval fits between left_pg and right_pg.
1708 */
1709
1710 if (overlaps(page, P2SZ(count), left_pg,
1711 P2SZ(left_cnt))) {
1712 /*
1713 * The interval intersects with the left
1714 * interval.
1715 */
1716 return false;
1717 } else if (overlaps(page, P2SZ(count), right_pg,
1718 P2SZ(right_cnt))) {
1719 /*
1720 * The interval intersects with the right
1721 * interval.
1722 */
1723 return false;
1724 } else if ((page == left_pg + P2SZ(left_cnt)) &&
1725 (page + P2SZ(count) == right_pg)) {
1726 /*
1727 * The interval can be added by merging the two
1728 * already present intervals.
1729 */
1730 leaf->value[i - 1] += count + right_cnt;
1731 btree_remove(&area->used_space, right_pg, leaf);
1732 goto success;
1733 } else if (page == left_pg + P2SZ(left_cnt)) {
1734 /*
1735 * The interval can be added by simply growing
1736 * the left interval.
1737 */
1738 leaf->value[i - 1] += count;
1739 goto success;
1740 } else if (page + P2SZ(count) == right_pg) {
1741 /*
1742 * The interval can be addded by simply moving
1743 * base of the right interval down and
1744 * increasing its size accordingly.
1745 */
1746 leaf->value[i] += count;
1747 leaf->key[i] = page;
1748 goto success;
1749 } else {
1750 /*
1751 * The interval is between both neigbouring
1752 * intervals, but cannot be merged with any of
1753 * them.
1754 */
1755 btree_insert(&area->used_space, page,
1756 (void *) count, leaf);
1757 goto success;
1758 }
1759 }
1760 }
1761
1762 panic("Inconsistency detected while adding %zu pages of used "
1763 "space at %p.", count, (void *) page);
1764
1765success:
1766 area->resident += count;
1767 return true;
1768}
1769
1770/** Mark portion of address space area as unused.
1771 *
1772 * The address space area must be already locked.
1773 *
1774 * @param area Address space area.
1775 * @param page First page to be marked.
1776 * @param count Number of page to be marked.
1777 *
1778 * @return False on failure or true on success.
1779 *
1780 */
1781bool used_space_remove(as_area_t *area, uintptr_t page, size_t count)
1782{
1783 ASSERT(mutex_locked(&area->lock));
1784 ASSERT(page == ALIGN_DOWN(page, PAGE_SIZE));
1785 ASSERT(count);
1786
1787 btree_node_t *leaf;
1788 size_t pages = (size_t) btree_search(&area->used_space, page, &leaf);
1789 if (pages) {
1790 /*
1791 * We are lucky, page is the beginning of some interval.
1792 */
1793 if (count > pages) {
1794 return false;
1795 } else if (count == pages) {
1796 btree_remove(&area->used_space, page, leaf);
1797 goto success;
1798 } else {
1799 /*
1800 * Find the respective interval.
1801 * Decrease its size and relocate its start address.
1802 */
1803 btree_key_t i;
1804 for (i = 0; i < leaf->keys; i++) {
1805 if (leaf->key[i] == page) {
1806 leaf->key[i] += P2SZ(count);
1807 leaf->value[i] -= count;
1808 goto success;
1809 }
1810 }
1811
1812 goto error;
1813 }
1814 }
1815
1816 btree_node_t *node = btree_leaf_node_left_neighbour(&area->used_space,
1817 leaf);
1818 if ((node) && (page < leaf->key[0])) {
1819 uintptr_t left_pg = node->key[node->keys - 1];
1820 size_t left_cnt = (size_t) node->value[node->keys - 1];
1821
1822 if (overlaps(left_pg, P2SZ(left_cnt), page, P2SZ(count))) {
1823 if (page + P2SZ(count) == left_pg + P2SZ(left_cnt)) {
1824 /*
1825 * The interval is contained in the rightmost
1826 * interval of the left neighbour and can be
1827 * removed by updating the size of the bigger
1828 * interval.
1829 */
1830 node->value[node->keys - 1] -= count;
1831 goto success;
1832 } else if (page + P2SZ(count) <
1833 left_pg + P2SZ(left_cnt)) {
1834 size_t new_cnt;
1835
1836 /*
1837 * The interval is contained in the rightmost
1838 * interval of the left neighbour but its
1839 * removal requires both updating the size of
1840 * the original interval and also inserting a
1841 * new interval.
1842 */
1843 new_cnt = ((left_pg + P2SZ(left_cnt)) -
1844 (page + P2SZ(count))) >> PAGE_WIDTH;
1845 node->value[node->keys - 1] -= count + new_cnt;
1846 btree_insert(&area->used_space, page +
1847 P2SZ(count), (void *) new_cnt, leaf);
1848 goto success;
1849 }
1850 }
1851
1852 return false;
1853 } else if (page < leaf->key[0])
1854 return false;
1855
1856 if (page > leaf->key[leaf->keys - 1]) {
1857 uintptr_t left_pg = leaf->key[leaf->keys - 1];
1858 size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
1859
1860 if (overlaps(left_pg, P2SZ(left_cnt), page, P2SZ(count))) {
1861 if (page + P2SZ(count) == left_pg + P2SZ(left_cnt)) {
1862 /*
1863 * The interval is contained in the rightmost
1864 * interval of the leaf and can be removed by
1865 * updating the size of the bigger interval.
1866 */
1867 leaf->value[leaf->keys - 1] -= count;
1868 goto success;
1869 } else if (page + P2SZ(count) < left_pg +
1870 P2SZ(left_cnt)) {
1871 size_t new_cnt;
1872
1873 /*
1874 * The interval is contained in the rightmost
1875 * interval of the leaf but its removal
1876 * requires both updating the size of the
1877 * original interval and also inserting a new
1878 * interval.
1879 */
1880 new_cnt = ((left_pg + P2SZ(left_cnt)) -
1881 (page + P2SZ(count))) >> PAGE_WIDTH;
1882 leaf->value[leaf->keys - 1] -= count + new_cnt;
1883 btree_insert(&area->used_space, page +
1884 P2SZ(count), (void *) new_cnt, leaf);
1885 goto success;
1886 }
1887 }
1888
1889 return false;
1890 }
1891
1892 /*
1893 * The border cases have been already resolved.
1894 * Now the interval can be only between intervals of the leaf.
1895 */
1896 btree_key_t i;
1897 for (i = 1; i < leaf->keys - 1; i++) {
1898 if (page < leaf->key[i]) {
1899 uintptr_t left_pg = leaf->key[i - 1];
1900 size_t left_cnt = (size_t) leaf->value[i - 1];
1901
1902 /*
1903 * Now the interval is between intervals corresponding
1904 * to (i - 1) and i.
1905 */
1906 if (overlaps(left_pg, P2SZ(left_cnt), page,
1907 P2SZ(count))) {
1908 if (page + P2SZ(count) ==
1909 left_pg + P2SZ(left_cnt)) {
1910 /*
1911 * The interval is contained in the
1912 * interval (i - 1) of the leaf and can
1913 * be removed by updating the size of
1914 * the bigger interval.
1915 */
1916 leaf->value[i - 1] -= count;
1917 goto success;
1918 } else if (page + P2SZ(count) <
1919 left_pg + P2SZ(left_cnt)) {
1920 size_t new_cnt;
1921
1922 /*
1923 * The interval is contained in the
1924 * interval (i - 1) of the leaf but its
1925 * removal requires both updating the
1926 * size of the original interval and
1927 * also inserting a new interval.
1928 */
1929 new_cnt = ((left_pg + P2SZ(left_cnt)) -
1930 (page + P2SZ(count))) >>
1931 PAGE_WIDTH;
1932 leaf->value[i - 1] -= count + new_cnt;
1933 btree_insert(&area->used_space, page +
1934 P2SZ(count), (void *) new_cnt,
1935 leaf);
1936 goto success;
1937 }
1938 }
1939
1940 return false;
1941 }
1942 }
1943
1944error:
1945 panic("Inconsistency detected while removing %zu pages of used "
1946 "space from %p.", count, (void *) page);
1947
1948success:
1949 area->resident -= count;
1950 return true;
1951}
1952
1953/*
1954 * Address space related syscalls.
1955 */
1956
1957/** Wrapper for as_area_create(). */
1958sysarg_t sys_as_area_create(uintptr_t address, size_t size, unsigned int flags)
1959{
1960 if (as_area_create(AS, flags | AS_AREA_CACHEABLE, size, address,
1961 AS_AREA_ATTR_NONE, &anon_backend, NULL))
1962 return (sysarg_t) address;
1963 else
1964 return (sysarg_t) -1;
1965}
1966
1967/** Wrapper for as_area_resize(). */
1968sysarg_t sys_as_area_resize(uintptr_t address, size_t size, unsigned int flags)
1969{
1970 return (sysarg_t) as_area_resize(AS, address, size, 0);
1971}
1972
1973/** Wrapper for as_area_change_flags(). */
1974sysarg_t sys_as_area_change_flags(uintptr_t address, unsigned int flags)
1975{
1976 return (sysarg_t) as_area_change_flags(AS, flags, address);
1977}
1978
1979/** Wrapper for as_area_destroy(). */
1980sysarg_t sys_as_area_destroy(uintptr_t address)
1981{
1982 return (sysarg_t) as_area_destroy(AS, address);
1983}
1984
1985/** Return pointer to unmapped address space area
1986 *
1987 * @param base Lowest address bound.
1988 * @param size Requested size of the allocation.
1989 *
1990 * @return Pointer to the beginning of unmapped address space area.
1991 *
1992 */
1993sysarg_t sys_as_get_unmapped_area(uintptr_t base, size_t size)
1994{
1995 if (size == 0)
1996 return 0;
1997
1998 /*
1999 * Make sure we allocate from page-aligned
2000 * address. Check for possible overflow in
2001 * each step.
2002 */
2003
2004 size_t pages = SIZE2FRAMES(size);
2005 uintptr_t ret = 0;
2006
2007 /*
2008 * Find the lowest unmapped address aligned on the sz
2009 * boundary, not smaller than base and of the required size.
2010 */
2011
2012 mutex_lock(&AS->lock);
2013
2014 /* First check the base address itself */
2015 uintptr_t addr = ALIGN_UP(base, PAGE_SIZE);
2016 if ((addr >= base) &&
2017 (check_area_conflicts(AS, addr, pages, NULL)))
2018 ret = addr;
2019
2020 /* Eventually check the addresses behind each area */
2021 list_foreach(AS->as_area_btree.leaf_list, cur) {
2022 if (ret != 0)
2023 break;
2024
2025 btree_node_t *node =
2026 list_get_instance(cur, btree_node_t, leaf_link);
2027
2028 btree_key_t i;
2029 for (i = 0; (ret == 0) && (i < node->keys); i++) {
2030 uintptr_t addr;
2031
2032 as_area_t *area = (as_area_t *) node->value[i];
2033
2034 mutex_lock(&area->lock);
2035
2036 addr = ALIGN_UP(area->base + P2SZ(area->pages),
2037 PAGE_SIZE);
2038
2039 if ((addr >= base) && (addr >= area->base) &&
2040 (check_area_conflicts(AS, addr, pages, area)))
2041 ret = addr;
2042
2043 mutex_unlock(&area->lock);
2044 }
2045 }
2046
2047 mutex_unlock(&AS->lock);
2048
2049 return (sysarg_t) ret;
2050}
2051
2052/** Get list of adress space areas.
2053 *
2054 * @param as Address space.
2055 * @param obuf Place to save pointer to returned buffer.
2056 * @param osize Place to save size of returned buffer.
2057 *
2058 */
2059void as_get_area_info(as_t *as, as_area_info_t **obuf, size_t *osize)
2060{
2061 mutex_lock(&as->lock);
2062
2063 /* First pass, count number of areas. */
2064
2065 size_t area_cnt = 0;
2066
2067 list_foreach(as->as_area_btree.leaf_list, cur) {
2068 btree_node_t *node =
2069 list_get_instance(cur, btree_node_t, leaf_link);
2070 area_cnt += node->keys;
2071 }
2072
2073 size_t isize = area_cnt * sizeof(as_area_info_t);
2074 as_area_info_t *info = malloc(isize, 0);
2075
2076 /* Second pass, record data. */
2077
2078 size_t area_idx = 0;
2079
2080 list_foreach(as->as_area_btree.leaf_list, cur) {
2081 btree_node_t *node =
2082 list_get_instance(cur, btree_node_t, leaf_link);
2083 btree_key_t i;
2084
2085 for (i = 0; i < node->keys; i++) {
2086 as_area_t *area = node->value[i];
2087
2088 ASSERT(area_idx < area_cnt);
2089 mutex_lock(&area->lock);
2090
2091 info[area_idx].start_addr = area->base;
2092 info[area_idx].size = P2SZ(area->pages);
2093 info[area_idx].flags = area->flags;
2094 ++area_idx;
2095
2096 mutex_unlock(&area->lock);
2097 }
2098 }
2099
2100 mutex_unlock(&as->lock);
2101
2102 *obuf = info;
2103 *osize = isize;
2104}
2105
2106/** Print out information about address space.
2107 *
2108 * @param as Address space.
2109 *
2110 */
2111void as_print(as_t *as)
2112{
2113 mutex_lock(&as->lock);
2114
2115 /* Print out info about address space areas */
2116 list_foreach(as->as_area_btree.leaf_list, cur) {
2117 btree_node_t *node
2118 = list_get_instance(cur, btree_node_t, leaf_link);
2119 btree_key_t i;
2120
2121 for (i = 0; i < node->keys; i++) {
2122 as_area_t *area = node->value[i];
2123
2124 mutex_lock(&area->lock);
2125 printf("as_area: %p, base=%p, pages=%zu"
2126 " (%p - %p)\n", area, (void *) area->base,
2127 area->pages, (void *) area->base,
2128 (void *) (area->base + P2SZ(area->pages)));
2129 mutex_unlock(&area->lock);
2130 }
2131 }
2132
2133 mutex_unlock(&as->lock);
2134}
2135
2136/** @}
2137 */
Note: See TracBrowser for help on using the repository browser.