Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Merge branch 'devel'
[palacios.git] / kitten / mm / aspace.c
1 /* Copyright (c) 2007,2008 Sandia National Laboratories */
2
3 #include <lwk/kernel.h>
4 #include <lwk/task.h>
5 #include <lwk/spinlock.h>
6 #include <lwk/string.h>
7 #include <lwk/aspace.h>
8 #include <lwk/idspace.h>
9 #include <lwk/htable.h>
10 #include <lwk/log2.h>
11 #include <lwk/cpuinfo.h>
12 #include <lwk/pmem.h>
13 #include <arch/uaccess.h>
14
15 /**
16  * ID space used to allocate address space IDs.
17  */
18 static idspace_t idspace;
19
20 /**
21  * Hash table used to lookup address space structures by ID.
22  */
23 static htable_t htable;
24
25 /**
26  * Lock for serializing access to the htable.
27  */
28 static DEFINE_SPINLOCK(htable_lock);
29
30 /**
31  * Memory region structure. A memory region represents a contiguous region 
32  * [start, end) of valid memory addresses in an address space.
33  */
34 struct region {
35         struct aspace *  aspace;   /* Address space this region belongs to */
36         struct list_head link;     /* Linkage in the aspace->region_list */
37
38         vaddr_t          start;    /* Starting address of the region */
39         vaddr_t          end;      /* 1st byte after end of the region */
40         vmflags_t        flags;    /* Permissions, caching, etc. */
41         vmpagesize_t     pagesz;   /* Allowed page sizes... 2^bit */
42         id_t             smartmap; /* If (flags & VM_SMARTMAP), ID of the
43                                       aspace this region is mapped to */
44         char             name[16]; /* Human-readable name of the region */
45 };
46
47 /**
48  * This calculates a region's end address. Normally end is the address of the
49  * first byte after the region. However if the region extends to the end of
50  * memory, that is not possible so set end to the last valid address,
51  * ULONG_MAX.
52  */
53 static vaddr_t
54 calc_end(vaddr_t start, size_t extent)
55 {
56         vaddr_t end = start + extent;
57         if (end == 0)
58                 end = ULONG_MAX;
59         return end;
60 }
61
62 /**
63  * Locates the region covering the specified address.
64  */
65 static struct region *
66 find_region(struct aspace *aspace, vaddr_t addr)
67 {
68         struct region *rgn;
69         
70         list_for_each_entry(rgn, &aspace->region_list, link) {
71                 if ((rgn->start <= addr) && (rgn->end > addr))
72                         return rgn;
73         }
74         return NULL;
75 }
76
77 /**
78  * Finds a region that overlaps the specified interval.
79  */
80 static struct region *
81 find_overlapping_region(struct aspace *aspace, vaddr_t start, vaddr_t end)
82 {
83         struct region *rgn;
84
85         list_for_each_entry(rgn, &aspace->region_list, link) {
86                 if ((start < rgn->end) && (end > rgn->start))
87                         return rgn;
88         }
89         return NULL;
90 }
91
92 /**
93  * Locates the region that is SMARTMAP'ed to the specified aspace ID.
94  */
95 static struct region *
96 find_smartmap_region(struct aspace *aspace, id_t src_aspace)
97 {
98         struct region *rgn;
99         
100         list_for_each_entry(rgn, &aspace->region_list, link) {
101                 if ((rgn->flags & VM_SMARTMAP) && (rgn->smartmap == src_aspace))
102                         return rgn;
103         }
104         return NULL;
105 }
106
107 /**
108  * Looks up an aspace object by ID and returns it with its spinlock locked.
109  */
110 static struct aspace *
111 lookup_and_lock(id_t id)
112 {
113         struct aspace *aspace;
114
115         /* Lock the hash table, lookup aspace object by ID */
116         spin_lock(&htable_lock);
117         if ((aspace = htable_lookup(htable, id)) == NULL) {
118                 spin_unlock(&htable_lock);
119                 return NULL;
120         }
121
122         /* Lock the identified aspace */
123         spin_lock(&aspace->lock);
124
125         /* Unlock the hash table, others may now use it */
126         spin_unlock(&htable_lock);
127
128         return aspace;
129 }
130
131 /**
132  * Like lookup_and_lock(), but looks up two address spaces instead of one.
133  */
134 static int
135 lookup_and_lock_two(id_t a, id_t b,
136                     struct aspace **aspace_a, struct aspace **aspace_b)
137 {
138         /* Lock the hash table, lookup aspace objects by ID */
139         spin_lock(&htable_lock);
140         if ((*aspace_a = htable_lookup(htable, a)) == NULL) {
141                 spin_unlock(&htable_lock);
142                 return -ENOENT;
143         }
144
145         if ((*aspace_b = htable_lookup(htable, b)) == NULL) {
146                 spin_unlock(&htable_lock);
147                 return -ENOENT;
148         }
149
150         /* Lock the identified aspaces */
151         spin_lock(&(*aspace_a)->lock);
152         spin_lock(&(*aspace_b)->lock);
153
154         /* Unlock the hash table, others may now use it */
155         spin_unlock(&htable_lock);
156
157         return 0;
158 }
159
160 static bool
161 id_ok(id_t id)
162 {
163         return ((id >= ASPACE_MIN_ID) && (id <= ASPACE_MAX_ID));
164 }
165
166 int __init
167 aspace_subsys_init(void)
168 {
169         int status;
170
171         /* Create an ID space for allocating address space IDs */
172         if ((status = idspace_create(__ASPACE_MIN_ID, __ASPACE_MAX_ID, &idspace)))
173                 panic("Failed to create aspace ID space (status=%d).", status);
174
175         /* Create a hash table that will be used for quick ID->aspace lookups */
176         if ((status = htable_create(7 /* 2^7 bins */,
177                                     offsetof(struct aspace, id),
178                                     offsetof(struct aspace, ht_link),
179                                     &htable)))
180                 panic("Failed to create aspace hash table (status=%d).", status);
181
182         /* Create an aspace for use by kernel threads */
183         if ((status = aspace_create(KERNEL_ASPACE_ID, "kernel", NULL)))
184                 panic("Failed to create kernel aspace (status=%d).", status);
185
186         /* Switch to the newly created kernel address space */
187         if ((current->aspace = aspace_acquire(KERNEL_ASPACE_ID)) == NULL)
188                 panic("Failed to acquire kernel aspace.");
189         arch_aspace_activate(current->aspace);
190
191         return 0;
192 }
193
194 int
195 aspace_get_myid(id_t *id)
196 {
197         *id = current->aspace->id;
198         return 0;
199 }
200
201 int
202 sys_aspace_get_myid(id_t __user *id)
203 {
204         int status;
205         id_t _id;
206
207         if ((status = aspace_get_myid(&_id)) != 0)
208                 return status;
209
210         if (id && copy_to_user(id, &_id, sizeof(*id)))
211                 return -EINVAL;
212
213         return 0;
214 }
215
216 int
217 aspace_create(id_t id_request, const char *name, id_t *id)
218 {
219         int status;
220         id_t new_id;
221         struct aspace *aspace;
222         unsigned long flags;
223
224         if ((status = idspace_alloc_id(idspace, id_request, &new_id)) != 0)
225                 return status;
226
227         if ((aspace = kmem_alloc(sizeof(*aspace))) == NULL) {
228                 idspace_free_id(idspace, new_id);
229                 return -ENOMEM;
230         }
231
232         /*
233          * Initialize the address space. kmem_alloc() allocates zeroed memory
234          * so fields with an initial state of zero do not need to be explicitly
235          * initialized.
236          */
237         aspace->id = new_id;
238         spin_lock_init(&aspace->lock);
239         list_head_init(&aspace->region_list);
240         hlist_node_init(&aspace->ht_link);
241         if (name)
242                 strlcpy(aspace->name, name, sizeof(aspace->name));
243
244         /* Create a region for the kernel portion of the address space */
245         status =
246         __aspace_add_region(
247                 aspace,
248                 PAGE_OFFSET,
249                 ULONG_MAX-PAGE_OFFSET+1, /* # bytes to end of memory */
250                 VM_KERNEL,
251                 PAGE_SIZE,
252                 "kernel"
253         );
254         if (status)
255                 goto error1;
256
257         /* Do architecture-specific initialization */
258         if ((status = arch_aspace_create(aspace)) != 0)
259                 goto error2;
260
261         /* Add new address space to a hash table, for quick lookups by ID */
262         spin_lock_irqsave(&htable_lock, flags);
263         BUG_ON(htable_add(htable, aspace));
264         spin_unlock_irqrestore(&htable_lock, flags);
265
266         if (id)
267                 *id = new_id;
268         return 0;
269
270 error2:
271         BUG_ON(__aspace_del_region(aspace,PAGE_OFFSET,ULONG_MAX-PAGE_OFFSET+1));
272 error1:
273         idspace_free_id(idspace, aspace->id);
274         kmem_free(aspace);
275         return status;
276 }
277
278 int
279 sys_aspace_create(id_t id_request, const char __user *name, id_t __user *id)
280 {
281         int status;
282         char _name[16];
283         id_t _id;
284
285         if (current->uid != 0)
286                 return -EPERM;
287
288         if ((id_request != ANY_ID) && !id_ok(id_request))
289                 return -EINVAL;
290
291         if (strncpy_from_user(_name, name, sizeof(_name)) < 0)
292                 return -EFAULT;
293         _name[sizeof(_name) - 1] = '\0';
294
295         if ((status = aspace_create(id_request, _name, &_id)) != 0)
296                 return status;
297
298         BUG_ON(!id_ok(_id));
299
300         if (id && copy_to_user(id, &_id, sizeof(*id)))
301                 return -EFAULT;
302
303         return 0;
304 }
305
306 int
307 aspace_destroy(id_t id)
308 {
309         struct aspace *aspace;
310         struct list_head *pos, *tmp;
311         struct region *rgn;
312         unsigned long irqstate;
313
314         /* Lock the hash table, lookup aspace object by ID */
315         spin_lock_irqsave(&htable_lock, irqstate);
316         if ((aspace = htable_lookup(htable, id)) == NULL) {
317                 spin_unlock_irqrestore(&htable_lock, irqstate);
318                 return -EINVAL;
319         }
320
321         /* Lock the identified aspace */
322         spin_lock(&aspace->lock);
323
324         if (aspace->refcnt) {
325                 spin_unlock(&aspace->lock);
326                 spin_unlock_irqrestore(&htable_lock, irqstate);
327                 return -EBUSY;
328         }
329
330         /* Remove aspace from hash table, preventing others from finding it */
331         BUG_ON(htable_del(htable, aspace));
332
333         /* Unlock the hash table, others may now use it */
334         spin_unlock_irqrestore(&htable_lock, irqstate);
335         spin_unlock(&aspace->lock);
336  
337         /* Finish up destroying the aspace, we have the only reference */
338         list_for_each_safe(pos, tmp, &aspace->region_list) {
339                 rgn = list_entry(pos, struct region, link);
340                 /* Must drop our reference on all SMARTMAP'ed aspaces */
341                 if (rgn->flags & VM_SMARTMAP) {
342                         struct aspace *src;
343                         spin_lock_irqsave(&htable_lock, irqstate);
344                         src = htable_lookup(htable, rgn->smartmap);
345                         BUG_ON(src == NULL);
346                         spin_lock(&src->lock);
347                         --src->refcnt;
348                         spin_unlock(&src->lock);
349                         spin_unlock_irqrestore(&htable_lock, irqstate);
350                 }
351                 list_del(&rgn->link);
352                 kmem_free(rgn);
353         }
354         arch_aspace_destroy(aspace);
355         BUG_ON(idspace_free_id(idspace, aspace->id));
356         kmem_free(aspace);
357         return 0;
358 }
359
360 int
361 sys_aspace_destroy(id_t id)
362 {
363         if (current->uid != 0)
364                 return -EPERM;
365         if (!id_ok(id))
366                 return -EINVAL;
367         return aspace_destroy(id);
368 }
369
370 /**
371  * Acquires an address space object. The object is guaranteed not to be
372  * deleted until it is released via aspace_release().
373  */
374 struct aspace *
375 aspace_acquire(id_t id)
376 {
377         struct aspace *aspace;
378         unsigned long irqstate;
379
380         local_irq_save(irqstate);
381         if ((aspace = lookup_and_lock(id)) != NULL) {
382                 ++aspace->refcnt;
383                 spin_unlock(&aspace->lock);
384         }
385         local_irq_restore(irqstate);
386         return aspace;
387 }
388
389 /**
390  * Releases an aspace object that was previously acquired via aspace_acquire().
391  * The aspace object passed in must be unlocked.
392  */
393 void
394 aspace_release(struct aspace *aspace)
395 {
396         unsigned long irqstate;
397         spin_lock_irqsave(&aspace->lock, irqstate);
398         --aspace->refcnt;
399         spin_unlock_irqrestore(&aspace->lock, irqstate);
400 }
401
402 int
403 __aspace_find_hole(struct aspace *aspace,
404                    vaddr_t start_hint, size_t extent, size_t alignment,
405                    vaddr_t *start)
406 {
407         struct region *rgn;
408         vaddr_t hole;
409
410         if (!aspace || !extent || !is_power_of_2(alignment))
411                 return -EINVAL;
412
413         if (start_hint == 0)
414                 start_hint = 1;
415
416         hole = round_up(start_hint, alignment);
417         while ((rgn = find_overlapping_region(aspace, hole, hole + extent))) {
418                 if (rgn->end == ULONG_MAX)
419                         return -ENOENT;
420                 hole = round_up(rgn->end, alignment);
421         }
422         
423         if (start)
424                 *start = hole;
425         return 0;
426 }
427
428 int
429 aspace_find_hole(id_t id,
430                  vaddr_t start_hint, size_t extent, size_t alignment,
431                  vaddr_t *start)
432 {
433         int status;
434         struct aspace *aspace;
435         unsigned long irqstate;
436
437         local_irq_save(irqstate);
438         aspace = lookup_and_lock(id);
439         status = __aspace_find_hole(aspace, start_hint, extent, alignment,
440                                     start);
441         if (aspace) spin_unlock(&aspace->lock);
442         local_irq_restore(irqstate);
443         return status;
444 }
445
446 int
447 sys_aspace_find_hole(id_t id,
448                      vaddr_t start_hint, size_t extent, size_t alignment,
449                      vaddr_t __user *start)
450 {
451         vaddr_t _start;
452         int status;
453
454         if (current->uid != 0)
455                 return -EPERM;
456
457         if (!id_ok(id))
458                 return -EINVAL;
459
460         status = aspace_find_hole(id, start_hint, extent, alignment, &_start);
461         if (status)
462                 return status;
463         
464         if (start && copy_to_user(start, &_start, sizeof(_start)))
465                 return -EFAULT;
466
467         return 0;
468 }
469
470 int
471 __aspace_add_region(struct aspace *aspace,
472                     vaddr_t start, size_t extent,
473                     vmflags_t flags, vmpagesize_t pagesz,
474                     const char *name)
475 {
476         struct region *rgn;
477         struct region *cur;
478         struct list_head *pos;
479         vaddr_t end = calc_end(start, extent);
480
481         if (!aspace || !start)
482                 return -EINVAL;
483
484         /* Region must have non-zero size */
485         if (extent == 0) {
486                 printk(KERN_WARNING "Extent must be non-zero.\n");
487                 return -EINVAL;
488         }
489
490         /* Region must have a positive size */
491         if (start >= end) {
492                 printk(KERN_WARNING
493                        "Invalid region size (start=0x%lx, extent=0x%lx).\n",
494                        start, extent);
495                 return -EINVAL;
496         }
497
498         /* Architecture must support the page size specified */
499         if ((pagesz & cpu_info[0].pagesz_mask) == 0) {
500                 printk(KERN_WARNING
501                         "Invalid page size specified (pagesz=0x%lx).\n",
502                         pagesz);
503                 return -EINVAL;
504         }
505         pagesz &= cpu_info[0].pagesz_mask;
506
507         /* Only one page size may be specified */
508         if (!is_power_of_2(pagesz)) {
509                 printk(KERN_WARNING
510                        "More than one page size specified (pagesz=0x%lx).\n",
511                        pagesz);
512                 return -EINVAL;
513         }
514
515         /* Region must be aligned to at least the specified page size */
516         if ((start & (pagesz-1)) || ((end!=ULONG_MAX) && (end & (pagesz-1)))) {
517                 printk(KERN_WARNING
518                        "Region is misaligned (start=0x%lx, end=0x%lx).\n",
519                        start, end);
520                 return -EINVAL;
521         }
522
523         /* Region must not overlap with any existing regions */
524         list_for_each_entry(cur, &aspace->region_list, link) {
525                 if ((start < cur->end) && (end > cur->start)) {
526                         printk(KERN_WARNING
527                                "Region overlaps with existing region.\n");
528                         return -ENOTUNIQ;
529                 }
530         }
531
532         /* Allocate and initialize a new region object */
533         if ((rgn = kmem_alloc(sizeof(struct region))) == NULL)
534                 return -ENOMEM;
535
536         rgn->aspace = aspace;
537         rgn->start  = start;
538         rgn->end    = end;
539         rgn->flags  = flags;
540         rgn->pagesz = pagesz;
541         if (name)
542                 strlcpy(rgn->name, name, sizeof(rgn->name));
543
544         /* The heap region is special, remember its bounds */
545         if (flags & VM_HEAP) {
546                 aspace->heap_start = start;
547                 aspace->heap_end   = end;
548                 aspace->brk        = aspace->heap_start;
549                 aspace->mmap_brk   = aspace->heap_end;
550         }
551
552         /* Insert region into address space's sorted region list */
553         list_for_each(pos, &aspace->region_list) {
554                 cur = list_entry(pos, struct region, link);
555                 if (cur->start > rgn->start)
556                         break;
557         }
558         list_add_tail(&rgn->link, pos);
559         return 0;
560 }
561
562 int
563 aspace_add_region(id_t id,
564                   vaddr_t start, size_t extent,
565                   vmflags_t flags, vmpagesize_t pagesz,
566                   const char *name)
567 {
568         int status;
569         struct aspace *aspace;
570         unsigned long irqstate;
571
572         local_irq_save(irqstate);
573         aspace = lookup_and_lock(id);
574         status = __aspace_add_region(aspace, start, extent, flags, pagesz, name);
575         if (aspace) spin_unlock(&aspace->lock);
576         local_irq_restore(irqstate);
577         return status;
578 }
579
580 int
581 sys_aspace_add_region(id_t id,
582                       vaddr_t start, size_t extent,
583                       vmflags_t flags, vmpagesize_t pagesz,
584                       const char __user *name)
585 {
586         char _name[16];
587
588         if (current->uid != 0)
589                 return -EPERM;
590
591         if (!id_ok(id))
592                 return -EINVAL;
593
594         if (strncpy_from_user(_name, name, sizeof(_name)) < 0)
595                 return -EFAULT;
596         _name[sizeof(_name) - 1] = '\0';
597
598         return aspace_add_region(id, start, extent, flags, pagesz, _name);
599 }
600
601
602 int
603 __aspace_del_region(struct aspace *aspace, vaddr_t start, size_t extent)
604 {
605         int status;
606         struct region *rgn;
607         vaddr_t end = calc_end(start, extent);
608
609         if (!aspace)
610                 return -EINVAL;
611
612         /* Locate the region to delete */
613         rgn = find_region(aspace, start);
614         if (!rgn || (rgn->start != start) || (rgn->end != end)
615              || (rgn->flags & VM_KERNEL))
616                 return -EINVAL;
617
618         if (!(rgn->flags & VM_SMARTMAP)) {
619                 /* Unmap all of the memory that was mapped to the region */
620                 status = __aspace_unmap_pmem(aspace, start, extent);
621                 if (status)
622                         return status;
623         }
624
625         /* Remove the region from the address space */
626         list_del(&rgn->link);
627         kmem_free(rgn);
628         return 0;
629 }
630
631 int
632 aspace_del_region(id_t id, vaddr_t start, size_t extent)
633 {
634         int status;
635         struct aspace *aspace;
636         unsigned long irqstate;
637
638         local_irq_save(irqstate);
639         aspace = lookup_and_lock(id);
640         status = __aspace_del_region(aspace, start, extent);
641         if (aspace) spin_unlock(&aspace->lock);
642         local_irq_restore(irqstate);
643         return status;
644 }
645
646 int
647 sys_aspace_del_region(id_t id, vaddr_t start, size_t extent)
648 {
649         if (current->uid != 0)
650                 return -EPERM;
651         if (!id_ok(id))
652                 return -EINVAL;
653         return aspace_del_region(id, start, extent);
654 }
655
656 static int
657 map_pmem(struct aspace *aspace,
658          paddr_t pmem, vaddr_t start, size_t extent,
659          bool umem_only)
660 {
661         int status;
662         struct region *rgn;
663
664         if (!aspace)
665                 return -EINVAL;
666
667         if (umem_only && !pmem_is_umem(pmem, extent)) {
668                 printk(KERN_WARNING
669                        "User-space tried to map non-UMEM "
670                        "(pmem=0x%lx, extent=0x%lx).\n",
671                        pmem, extent);
672                 return -EPERM;
673         }
674
675         while (extent) {
676                 /* Find region covering the address */
677                 rgn = find_region(aspace, start);
678                 if (!rgn) {
679                         printk(KERN_WARNING
680                                 "Failed to find region covering addr=0x%lx.\n",
681                                 start);
682                         return -EINVAL;
683                 }
684
685                 /* Can't map anything to kernel or SMARTMAP regions */
686                 if ((rgn->flags & VM_KERNEL) || (rgn->flags & VM_SMARTMAP)) {
687                         printk(KERN_WARNING
688                                 "Trying to map memory to protected region.\n");
689                         return -EINVAL;
690                 }
691
692                 /* addresses must be aligned to region's page size */
693                 if ((start & (rgn->pagesz-1)) || (pmem & (rgn->pagesz-1))) {
694                         printk(KERN_WARNING
695                                 "Misalignment "
696                                 "(start=0x%lx, pmem=0x%lx, pagesz=0x%lx).\n",
697                                 start, pmem, rgn->pagesz);
698                         return -EINVAL;
699                 }
700
701                 /* Map until full extent mapped or end of region is reached */
702                 while (extent && (start < rgn->end)) {
703
704                         status = 
705                         arch_aspace_map_page(
706                                 aspace,
707                                 start,
708                                 pmem,
709                                 rgn->flags,
710                                 rgn->pagesz
711                         );
712                         if (status)
713                                 return status;
714
715                         extent -= rgn->pagesz;
716                         start  += rgn->pagesz;
717                         pmem   += rgn->pagesz;
718                 }
719         }
720
721         return 0;
722 }
723
724 static int
725 map_pmem_locked(id_t id,
726                 paddr_t pmem, vaddr_t start, size_t extent,
727                 bool umem_only)
728 {
729         int status;
730         struct aspace *aspace;
731         unsigned long irqstate;
732
733         local_irq_save(irqstate);
734         aspace = lookup_and_lock(id);
735         status = map_pmem(aspace, pmem, start, extent, umem_only);
736         if (aspace) spin_unlock(&aspace->lock);
737         local_irq_restore(irqstate);
738         return status;
739 }
740
741 int
742 __aspace_map_pmem(struct aspace *aspace,
743                   paddr_t pmem, vaddr_t start, size_t extent)
744 {
745         return map_pmem(aspace, pmem, start, extent, false);
746 }
747
748 int
749 aspace_map_pmem(id_t id, paddr_t pmem, vaddr_t start, size_t extent)
750 {
751         return map_pmem_locked(id, pmem, start, extent, false);
752 }
753
754 int
755 sys_aspace_map_pmem(id_t id, paddr_t pmem, vaddr_t start, size_t extent)
756 {
757         if (current->uid != 0)
758                 return -EPERM;
759         if (!id_ok(id))
760                 return -EINVAL;
761         return map_pmem_locked(id, pmem, start, extent, true);
762 }
763
764 int
765 __aspace_unmap_pmem(struct aspace *aspace, vaddr_t start, size_t extent)
766 {
767         struct region *rgn;
768
769         if (!aspace)
770                 return -EINVAL;
771
772         while (extent) {
773                 /* Find region covering the address */
774                 rgn = find_region(aspace, start);
775                 if (!rgn) {
776                         printk(KERN_WARNING
777                                 "Failed to find region covering addr=0x%lx.\n",
778                                 start);
779                         return -EINVAL;
780                 }
781
782                 /* Can't unmap anything from kernel or SMARTMAP regions */
783                 if ((rgn->flags & VM_KERNEL) || (rgn->flags & VM_SMARTMAP)) {
784                         printk(KERN_WARNING
785                                 "Trying to map memory to protected region.\n");
786                         return -EINVAL;
787                 }
788
789                 /* address must be aligned to region's page size */
790                 if (start & (rgn->pagesz-1)) {
791                         printk(KERN_WARNING
792                                 "Misalignment (start=0x%lx, pagesz=0x%lx).\n",
793                                 start, rgn->pagesz);
794                         return -EINVAL;
795                 }
796
797                 /* Unmap until full extent unmapped or end of region is reached */
798                 while (extent && (start < rgn->end)) {
799
800                         arch_aspace_unmap_page(
801                                 aspace,
802                                 start,
803                                 rgn->pagesz
804                         );
805
806                         extent -= rgn->pagesz;
807                         start  += rgn->pagesz;
808                 }
809         }
810
811         return 0;
812 }
813
814 int
815 aspace_unmap_pmem(id_t id, vaddr_t start, size_t extent)
816 {
817         int status;
818         struct aspace *aspace;
819         unsigned long irqstate;
820
821         local_irq_save(irqstate);
822         aspace = lookup_and_lock(id);
823         status = __aspace_unmap_pmem(aspace, start, extent);
824         if (aspace) spin_unlock(&aspace->lock);
825         local_irq_restore(irqstate);
826         return status;
827 }
828
829 int
830 sys_aspace_unmap_pmem(id_t id, vaddr_t start, size_t extent)
831 {
832         if (current->uid != 0)
833                 return -EPERM;
834         if (!id_ok(id))
835                 return -EINVAL;
836         return aspace_unmap_pmem(id, start, extent);
837 }
838
839 int
840 __aspace_smartmap(struct aspace *src, struct aspace *dst,
841                   vaddr_t start, size_t extent)
842 {
843         int status;
844         vaddr_t end = start + extent;
845         char name[16];
846         struct region *rgn;
847
848         /* Can only SMARTMAP a given aspace in once */
849         if (find_smartmap_region(dst, src->id))
850                 return -EINVAL;
851
852         if (start >= end)
853                 return -EINVAL;
854
855         if ((start & (SMARTMAP_ALIGN-1)) || (end & (SMARTMAP_ALIGN-1)))
856                 return -EINVAL;
857
858         snprintf(name, sizeof(name), "SMARTMAP-%u", (unsigned int)src->id);
859         if ((status = __aspace_add_region(dst, start, extent,
860                                           VM_SMARTMAP, PAGE_SIZE, name)))
861                 return status;
862
863         /* Do architecture-specific SMARTMAP initialization */
864         if ((status = arch_aspace_smartmap(src, dst, start, extent))) {
865                 BUG_ON(__aspace_del_region(dst, start, extent));
866                 return status;
867         }
868
869         /* Remember the source aspace that the SMARTMAP region is mapped to */
870         rgn = find_region(dst, start);
871         BUG_ON(!rgn);
872         rgn->smartmap = src->id;
873
874         /* Ensure source aspace doesn't go away while we have it SMARTMAP'ed */
875         ++src->refcnt;
876
877         return 0;
878 }
879
880 int
881 aspace_smartmap(id_t src, id_t dst, vaddr_t start, size_t extent)
882 {
883         int status;
884         struct aspace *src_spc, *dst_spc;
885         unsigned long irqstate;
886
887         /* Don't allow self SMARTMAP'ing */
888         if (src == dst)
889                 return -EINVAL;
890
891         local_irq_save(irqstate);
892         if ((status = lookup_and_lock_two(src, dst, &src_spc, &dst_spc))) {
893                 local_irq_restore(irqstate);
894                 return status;
895         }
896         status = __aspace_smartmap(src_spc, dst_spc, start, extent);
897         spin_unlock(&src_spc->lock);
898         spin_unlock(&dst_spc->lock);
899         local_irq_restore(irqstate);
900         return status;
901 }
902
903 int
904 sys_aspace_smartmap(id_t src, id_t dst, vaddr_t start, size_t extent)
905 {
906         if (current->uid != 0)
907                 return -EPERM;
908         if (!id_ok(src) || !id_ok(dst))
909                 return -EINVAL;
910         return aspace_smartmap(src, dst, start, extent);
911 }
912
913 int
914 __aspace_unsmartmap(struct aspace *src, struct aspace *dst)
915 {
916         struct region *rgn;
917         size_t extent;
918
919         if ((rgn = find_smartmap_region(dst, src->id)) == NULL)
920                 return -EINVAL;
921         extent = rgn->end - rgn->start;
922
923         /* Do architecture-specific SMARTMAP unmapping */
924         BUG_ON(arch_aspace_unsmartmap(src, dst, rgn->start, extent));
925
926         /* Delete the SMARTMAP region and release our reference on the source */
927         BUG_ON(__aspace_del_region(dst, rgn->start, extent));
928         --src->refcnt;
929
930         return 0;
931 }
932
933 int
934 aspace_unsmartmap(id_t src, id_t dst)
935 {
936         int status;
937         struct aspace *src_spc, *dst_spc;
938         unsigned long irqstate;
939
940         /* Don't allow self SMARTMAP'ing */
941         if (src == dst)
942                 return -EINVAL;
943
944         local_irq_save(irqstate);
945         if ((status = lookup_and_lock_two(src, dst, &src_spc, &dst_spc))) {
946                 local_irq_restore(irqstate);
947                 return status;
948         }
949         status = __aspace_unsmartmap(src_spc, dst_spc);
950         spin_unlock(&src_spc->lock);
951         spin_unlock(&dst_spc->lock);
952         local_irq_restore(irqstate);
953         return status;
954 }
955
956 int
957 sys_aspace_unsmartmap(id_t src, id_t dst)
958 {
959         if (current->uid != 0)
960                 return -EPERM;
961         if (!id_ok(src) || !id_ok(dst))
962                 return -EINVAL;
963         return aspace_unsmartmap(src, dst);
964 }
965
966 int
967 aspace_dump2console(id_t id)
968 {
969         struct aspace *aspace;
970         struct region *rgn;
971         unsigned long irqstate;
972
973         local_irq_save(irqstate);
974
975         if ((aspace = lookup_and_lock(id)) == NULL) {
976                 local_irq_restore(irqstate);
977                 return -EINVAL;
978         }
979
980         printk(KERN_DEBUG "DUMP OF ADDRESS SPACE %u:\n", aspace->id);
981         printk(KERN_DEBUG "  name:    %s\n", aspace->name);
982         printk(KERN_DEBUG "  refcnt:  %d\n", aspace->refcnt);
983         printk(KERN_DEBUG "  regions:\n");
984         list_for_each_entry(rgn, &aspace->region_list, link) {
985                 printk(KERN_DEBUG
986                         "    [0x%016lx, 0x%016lx%c %s\n",
987                         rgn->start,
988                         rgn->end,
989                         (rgn->end == ULONG_MAX) ? ']' : ')',
990                         rgn->name
991                 );
992         }
993
994         spin_unlock(&aspace->lock);
995         local_irq_restore(irqstate);
996         return 0;
997 }
998
999 int
1000 sys_aspace_dump2console(id_t id)
1001 {
1002         return aspace_dump2console(id);
1003 }