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

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

Support for active mutexes. Active mutexes implement busy waiting, pretty much
in the same way as spinlocks, but can be passed to condition variables, which is
the motivation for this enhancement.

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