]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/vm/vm_kern.c
xnu-2422.100.13.tar.gz
[apple/xnu.git] / osfmk / vm / vm_kern.c
index acd7d2a82bcd0f45035ac1248015d3fe0e4feeec..65e48ae7d437ae798eb127317f3cbd468bc0db0c 100644 (file)
@@ -126,15 +126,17 @@ kmem_alloc_contig(
 
        if (map == VM_MAP_NULL || (flags & ~(KMA_KOBJECT | KMA_LOMEM | KMA_NOPAGEWAIT))) 
                return KERN_INVALID_ARGUMENT;
+
+       map_size = vm_map_round_page(size,
+                                    VM_MAP_PAGE_MASK(map));
+       map_mask = (vm_map_offset_t)mask;
        
-       if (size == 0) {
+       /* Check for zero allocation size (either directly or via overflow) */
+       if (map_size == 0) {
                *addrp = 0;
                return KERN_INVALID_ARGUMENT;
        }
 
-       map_size = vm_map_round_page(size);
-       map_mask = (vm_map_offset_t)mask;
-
        /*
         *      Allocate a new object (if necessary) and the reference we
         *      will be donating to the map entry.  We must do this before
@@ -164,8 +166,12 @@ kmem_alloc_contig(
        kr = cpm_allocate(CAST_DOWN(vm_size_t, map_size), &pages, max_pnum, pnum_mask, FALSE, flags);
 
        if (kr != KERN_SUCCESS) {
-               vm_map_remove(map, vm_map_trunc_page(map_addr),
-                             vm_map_round_page(map_addr + map_size), 0);
+               vm_map_remove(map,
+                             vm_map_trunc_page(map_addr,
+                                               VM_MAP_PAGE_MASK(map)),
+                             vm_map_round_page(map_addr + map_size,
+                                               VM_MAP_PAGE_MASK(map)),
+                             0);
                vm_object_deallocate(object);
                *addrp = 0;
                return kr;
@@ -181,16 +187,25 @@ kmem_alloc_contig(
        }
        vm_object_unlock(object);
 
-       if ((kr = vm_map_wire(map, vm_map_trunc_page(map_addr),
-                             vm_map_round_page(map_addr + map_size), VM_PROT_DEFAULT, FALSE)) 
-               != KERN_SUCCESS) {
+       kr = vm_map_wire(map,
+                        vm_map_trunc_page(map_addr,
+                                          VM_MAP_PAGE_MASK(map)),
+                        vm_map_round_page(map_addr + map_size,
+                                          VM_MAP_PAGE_MASK(map)),
+                        VM_PROT_DEFAULT,
+                        FALSE);
+       if (kr != KERN_SUCCESS) {
                if (object == kernel_object) {
                        vm_object_lock(object);
                        vm_object_page_remove(object, offset, offset + map_size);
                        vm_object_unlock(object);
                }
-               vm_map_remove(map, vm_map_trunc_page(map_addr), 
-                             vm_map_round_page(map_addr + map_size), 0);
+               vm_map_remove(map,
+                             vm_map_trunc_page(map_addr,
+                                               VM_MAP_PAGE_MASK(map)), 
+                             vm_map_round_page(map_addr + map_size,
+                                               VM_MAP_PAGE_MASK(map)),
+                             0);
                vm_object_deallocate(object);
                return kr;
        }
@@ -232,11 +247,11 @@ kernel_memory_allocate(
        vm_object_t             object;
        vm_object_offset_t      offset;
        vm_object_offset_t      pg_offset;
-       vm_map_entry_t          entry;
+       vm_map_entry_t          entry = NULL;
        vm_map_offset_t         map_addr, fill_start;
        vm_map_offset_t         map_mask;
        vm_map_size_t           map_size, fill_size;
-       kern_return_t           kr;
+       kern_return_t           kr, pe_result;
        vm_page_t               mem;
        vm_page_t               guard_page_list = NULL;
        vm_page_t               wired_page_list = NULL;
@@ -244,26 +259,30 @@ kernel_memory_allocate(
        int                     wired_page_count = 0;
        int                     i;
        int                     vm_alloc_flags;
+       vm_prot_t               kma_prot;
 
        if (! vm_kernel_ready) {
                panic("kernel_memory_allocate: VM is not ready");
        }
 
-       if (size == 0) {
-               *addrp = 0;
-               return KERN_INVALID_ARGUMENT;
-       }
-       map_size = vm_map_round_page(size);
+       map_size = vm_map_round_page(size,
+                                    VM_MAP_PAGE_MASK(map));
        map_mask = (vm_map_offset_t) mask;
        vm_alloc_flags = 0;
 
+       /* Check for zero allocation size (either directly or via overflow) */
+       if (map_size == 0) {
+               *addrp = 0;
+               return KERN_INVALID_ARGUMENT;
+       }
 
        /*
         * limit the size of a single extent of wired memory
         * to try and limit the damage to the system if
         * too many pages get wired down
+        * limit raised to 2GB with 128GB max physical limit
         */
-        if (map_size > (1 << 30)) {
+        if (map_size > (1ULL << 31)) {
                 return KERN_RESOURCE_SHORTAGE;
         }
 
@@ -325,6 +344,7 @@ kernel_memory_allocate(
                guard_page_list = mem;
        }
 
+       if (! (flags & KMA_VAONLY)) {
        for (i = 0; i < wired_page_count; i++) {
                uint64_t        unavailable;
                
@@ -356,6 +376,7 @@ kernel_memory_allocate(
                mem->pageq.next = (queue_entry_t)wired_page_list;
                wired_page_list = mem;
        }
+       }
 
        /*
         *      Allocate a new object (if necessary).  We must do this before
@@ -364,6 +385,9 @@ kernel_memory_allocate(
        if ((flags & KMA_KOBJECT) != 0) {
                object = kernel_object;
                vm_object_reference(object);
+       } else if ((flags & KMA_COMPRESSOR) != 0) {
+               object = compressor_object;
+               vm_object_reference(object);
        } else {
                object = vm_object_allocate(map_size);
        }
@@ -377,15 +401,16 @@ kernel_memory_allocate(
        }
 
        entry->object.vm_object = object;
-       entry->offset = offset = (object == kernel_object) ? 
+       entry->offset = offset = (object == kernel_object || object == compressor_object) ? 
                        map_addr : 0;
-
-       entry->wired_count++;
+       
+       if (object != compressor_object)
+               entry->wired_count++;
 
        if (flags & KMA_PERMANENT)
                entry->permanent = TRUE;
 
-       if (object != kernel_object)
+       if (object != kernel_object && object != compressor_object)
                vm_object_reference(object);
 
        vm_object_lock(object);
@@ -406,6 +431,12 @@ kernel_memory_allocate(
                mem->busy = FALSE;
                pg_offset += PAGE_SIZE_64;
        }
+
+       kma_prot = VM_PROT_READ | VM_PROT_WRITE;
+
+       if (flags & KMA_VAONLY) {
+               pg_offset = fill_start + fill_size;
+       } else {
        for (pg_offset = fill_start; pg_offset < fill_start + fill_size; pg_offset += PAGE_SIZE_64) {
                if (wired_page_list == NULL)
                        panic("kernel_memory_allocate: wired_page_list == NULL");
@@ -421,15 +452,25 @@ kernel_memory_allocate(
                mem->pmapped = TRUE;
                mem->wpmapped = TRUE;
 
-               PMAP_ENTER(kernel_pmap, map_addr + pg_offset, mem, 
-                          VM_PROT_READ | VM_PROT_WRITE, 0, TRUE);
+               PMAP_ENTER_OPTIONS(kernel_pmap, map_addr + pg_offset, mem,
+                                  kma_prot, VM_PROT_NONE, ((flags & KMA_KSTACK) ? VM_MEM_STACK : 0), TRUE,
+                                  PMAP_OPTIONS_NOWAIT, pe_result);
 
+               if (pe_result == KERN_RESOURCE_SHORTAGE) {
+                       vm_object_unlock(object);
+
+                       PMAP_ENTER(kernel_pmap, map_addr + pg_offset, mem, 
+                                  kma_prot, VM_PROT_NONE, ((flags & KMA_KSTACK) ? VM_MEM_STACK : 0), TRUE);
+
+                       vm_object_lock(object);
+               }
                if (flags & KMA_NOENCRYPT) {
                        bzero(CAST_DOWN(void *, (map_addr + pg_offset)), PAGE_SIZE);
 
                        pmap_set_noencrypt(mem->phys_page);
                }
        }
+       }
        if ((fill_start + fill_size) < map_size) {
                if (guard_page_list == NULL)
                        panic("kernel_memory_allocate: guard_page_list == NULL");
@@ -445,16 +486,18 @@ kernel_memory_allocate(
        if (guard_page_list || wired_page_list)
                panic("kernel_memory_allocate: non empty list\n");
 
+       if (! (flags & KMA_VAONLY)) {
        vm_page_lockspin_queues();
        vm_page_wire_count += wired_page_count;
        vm_page_unlock_queues();
+       }
 
        vm_object_unlock(object);
 
        /*
         * now that the pages are wired, we no longer have to fear coalesce
         */
-       if (object == kernel_object)
+       if (object == kernel_object || object == compressor_object)
                vm_map_simplify(map, map_addr);
        else
                vm_object_deallocate(object);
@@ -475,6 +518,243 @@ out:
        return kr;
 }
 
+kern_return_t
+kernel_memory_populate(
+       vm_map_t        map,
+       vm_offset_t     addr,
+       vm_size_t       size,
+       int             flags)
+{
+       vm_object_t             object;
+       vm_object_offset_t      offset, pg_offset;
+       kern_return_t           kr, pe_result;
+       vm_page_t               mem;
+       vm_page_t               page_list = NULL;
+       int                     page_count = 0;
+       int                     i;
+
+       page_count = (int) (size / PAGE_SIZE_64);
+
+       assert((flags & (KMA_COMPRESSOR|KMA_KOBJECT)) != (KMA_COMPRESSOR|KMA_KOBJECT));
+
+       if (flags & KMA_COMPRESSOR) {
+
+               for (i = 0; i < page_count; i++) {
+                       for (;;) {
+                               mem = vm_page_grab();
+
+                               if (mem != VM_PAGE_NULL)
+                                       break;
+                               
+                               VM_PAGE_WAIT();
+                       }
+                       mem->pageq.next = (queue_entry_t) page_list;
+                       page_list = mem;
+               }
+               offset = addr;
+               object = compressor_object;
+
+               vm_object_lock(object);
+
+               for (pg_offset = 0;
+                    pg_offset < size;
+                    pg_offset += PAGE_SIZE_64) {
+
+                       mem = page_list;
+                       page_list = (vm_page_t) mem->pageq.next;
+                       mem->pageq.next = NULL;
+
+                       vm_page_insert(mem, object, offset + pg_offset);
+                       assert(mem->busy);
+
+                       PMAP_ENTER_OPTIONS(kernel_pmap, addr + pg_offset, mem,
+                                          VM_PROT_READ | VM_PROT_WRITE, VM_PROT_NONE,
+                                          0, TRUE, PMAP_OPTIONS_NOWAIT, pe_result);
+
+                       if (pe_result == KERN_RESOURCE_SHORTAGE) {
+
+                               vm_object_unlock(object);
+
+                               PMAP_ENTER(kernel_pmap, addr + pg_offset, mem,
+                                          VM_PROT_READ | VM_PROT_WRITE, VM_PROT_NONE, 0, TRUE);
+
+                               vm_object_lock(object);
+                       }
+                       mem->busy = FALSE;
+                       mem->pmapped = TRUE;
+                       mem->wpmapped = TRUE;
+                       mem->compressor = TRUE;
+               }
+               vm_object_unlock(object);
+
+               return KERN_SUCCESS;
+       }
+
+       for (i = 0; i < page_count; i++) {
+               for (;;) {
+                       if (flags & KMA_LOMEM)
+                               mem = vm_page_grablo();
+                       else
+                               mem = vm_page_grab();
+                       
+                       if (mem != VM_PAGE_NULL)
+                               break;
+
+                       if (flags & KMA_NOPAGEWAIT) {
+                               kr = KERN_RESOURCE_SHORTAGE;
+                               goto out;
+                       }
+                       if ((flags & KMA_LOMEM) &&
+                           (vm_lopage_needed == TRUE)) {
+                               kr = KERN_RESOURCE_SHORTAGE;
+                               goto out;
+                       }
+                       VM_PAGE_WAIT();
+               }
+               mem->pageq.next = (queue_entry_t) page_list;
+               page_list = mem;
+       }
+       if (flags & KMA_KOBJECT) {
+               offset = addr;
+               object = kernel_object;
+
+               vm_object_lock(object);
+       } else {
+               /*
+                * If it's not the kernel object, we need to:
+                *      lock map;
+                *      lookup entry;
+                *      lock object;
+                *      take reference on object;
+                *      unlock map;
+                */
+               panic("kernel_memory_populate(%p,0x%llx,0x%llx,0x%x): "
+                     "!KMA_KOBJECT",
+                     map, (uint64_t) addr, (uint64_t) size, flags);
+       }
+
+       for (pg_offset = 0;
+            pg_offset < size;
+            pg_offset += PAGE_SIZE_64) {
+
+               if (page_list == NULL)
+                       panic("kernel_memory_populate: page_list == NULL");
+
+               mem = page_list;
+               page_list = (vm_page_t) mem->pageq.next;
+               mem->pageq.next = NULL;
+
+               mem->wire_count++;
+
+               vm_page_insert(mem, object, offset + pg_offset);
+
+               mem->busy = FALSE;
+               mem->pmapped = TRUE;
+               mem->wpmapped = TRUE;
+
+               PMAP_ENTER_OPTIONS(kernel_pmap, addr + pg_offset, mem,
+                                  VM_PROT_READ | VM_PROT_WRITE, VM_PROT_NONE,
+                                  ((flags & KMA_KSTACK) ? VM_MEM_STACK : 0), TRUE,
+                                  PMAP_OPTIONS_NOWAIT, pe_result);
+
+               if (pe_result == KERN_RESOURCE_SHORTAGE) {
+
+                       vm_object_unlock(object);
+
+                       PMAP_ENTER(kernel_pmap, addr + pg_offset, mem,
+                                  VM_PROT_READ | VM_PROT_WRITE, VM_PROT_NONE,
+                                  ((flags & KMA_KSTACK) ? VM_MEM_STACK : 0), TRUE);
+
+                       vm_object_lock(object);
+               }
+               if (flags & KMA_NOENCRYPT) {
+                       bzero(CAST_DOWN(void *, (addr + pg_offset)), PAGE_SIZE);
+                       pmap_set_noencrypt(mem->phys_page);
+               }
+       }
+       vm_page_lock_queues();
+       vm_page_wire_count += page_count;
+       vm_page_unlock_queues();
+
+       vm_object_unlock(object);
+
+       return KERN_SUCCESS;
+
+out:
+       if (page_list)
+               vm_page_free_list(page_list, FALSE);
+
+       return kr;
+}
+
+
+void
+kernel_memory_depopulate(
+       vm_map_t        map,
+       vm_offset_t     addr,
+       vm_size_t       size,
+       int             flags)
+{
+       vm_object_t             object;
+       vm_object_offset_t      offset, pg_offset;
+       vm_page_t               mem;
+       vm_page_t               local_freeq = NULL;
+
+       assert((flags & (KMA_COMPRESSOR|KMA_KOBJECT)) != (KMA_COMPRESSOR|KMA_KOBJECT));
+
+       if (flags & KMA_COMPRESSOR) {
+               offset = addr;
+               object = compressor_object;
+
+               vm_object_lock(object);
+       } else if (flags & KMA_KOBJECT) {
+               offset = addr;
+               object = kernel_object;
+
+               vm_object_lock(object);
+       } else {
+               offset = 0;
+               object = NULL;
+                /*
+                 * If it's not the kernel object, we need to:
+                 *      lock map;
+                 *      lookup entry;
+                 *      lock object;
+                 *      unlock map;
+                 */
+               panic("kernel_memory_depopulate(%p,0x%llx,0x%llx,0x%x): "
+                     "!KMA_KOBJECT",
+                     map, (uint64_t) addr, (uint64_t) size, flags);
+       }
+       pmap_protect(kernel_map->pmap, offset, offset + size, VM_PROT_NONE);
+
+       for (pg_offset = 0;
+            pg_offset < size;
+            pg_offset += PAGE_SIZE_64) {
+
+               mem = vm_page_lookup(object, offset + pg_offset);
+
+               assert(mem);
+
+               pmap_disconnect(mem->phys_page);
+
+               mem->busy = TRUE;
+
+               assert(mem->tabled);
+               vm_page_remove(mem, TRUE);
+               assert(mem->busy);
+
+               assert(mem->pageq.next == NULL &&
+                      mem->pageq.prev == NULL);
+               mem->pageq.next = (queue_entry_t)local_freeq;
+               local_freeq = mem;
+       }
+       vm_object_unlock(object);
+
+       if (local_freeq)
+               vm_page_free_list(local_freeq, TRUE);
+}
+
 /*
  *     kmem_alloc:
  *
@@ -523,10 +803,13 @@ kmem_realloc(
        vm_page_t               mem;
        kern_return_t           kr;
 
-       oldmapmin = vm_map_trunc_page(oldaddr);
-       oldmapmax = vm_map_round_page(oldaddr + oldsize);
+       oldmapmin = vm_map_trunc_page(oldaddr,
+                                     VM_MAP_PAGE_MASK(map));
+       oldmapmax = vm_map_round_page(oldaddr + oldsize,
+                                     VM_MAP_PAGE_MASK(map));
        oldmapsize = oldmapmax - oldmapmin;
-       newmapsize = vm_map_round_page(newsize);
+       newmapsize = vm_map_round_page(newsize,
+                                      VM_MAP_PAGE_MASK(map));
 
 
        /*
@@ -668,7 +951,8 @@ kmem_alloc_pageable(
 #else
        map_addr = vm_map_min(map);
 #endif
-       map_size = vm_map_round_page(size);
+       map_size = vm_map_round_page(size,
+                                    VM_MAP_PAGE_MASK(map));
 
        kr = vm_map_enter(map, &map_addr, map_size,
                          (vm_map_offset_t) 0, VM_FLAGS_ANYWHERE,
@@ -709,9 +993,12 @@ kmem_free(
                return;
        }
 
-       kr = vm_map_remove(map, vm_map_trunc_page(addr),
-                               vm_map_round_page(addr + size), 
-                               VM_MAP_REMOVE_KUNWIRE);
+       kr = vm_map_remove(map,
+                          vm_map_trunc_page(addr,
+                                            VM_MAP_PAGE_MASK(map)),
+                          vm_map_round_page(addr + size,
+                                            VM_MAP_PAGE_MASK(map)), 
+                          VM_MAP_REMOVE_KUNWIRE);
        if (kr != KERN_SUCCESS)
                panic("kmem_free");
 }
@@ -772,8 +1059,10 @@ kmem_remap_pages(
        /*
         *      Mark the pmap region as not pageable.
         */
-       map_start = vm_map_trunc_page(start);
-       map_end = vm_map_round_page(end);
+       map_start = vm_map_trunc_page(start,
+                                     VM_MAP_PAGE_MASK(kernel_map));
+       map_end = vm_map_round_page(end,
+                                   VM_MAP_PAGE_MASK(kernel_map));
 
        pmap_pageable(kernel_pmap, map_start, map_end, FALSE);
 
@@ -812,7 +1101,7 @@ kmem_remap_pages(
            mem->pmapped = TRUE;
            mem->wpmapped = TRUE;
 
-           PMAP_ENTER(kernel_pmap, map_start, mem, protection, 0, TRUE);
+           PMAP_ENTER(kernel_pmap, map_start, mem, protection, VM_PROT_NONE, 0, TRUE);
 
            map_start += PAGE_SIZE;
            offset += PAGE_SIZE;
@@ -848,7 +1137,8 @@ kmem_suballoc(
        vm_map_size_t   map_size;
        kern_return_t   kr;
 
-       map_size = vm_map_round_page(size);
+       map_size = vm_map_round_page(size,
+                                    VM_MAP_PAGE_MASK(parent));
 
        /*
         *      Need reference on submap object because it is internal
@@ -857,8 +1147,10 @@ kmem_suballoc(
         */
        vm_object_reference(vm_submap_object);
 
-       map_addr = (flags & VM_FLAGS_ANYWHERE) ?
-                  vm_map_min(parent) : vm_map_trunc_page(*addr);
+       map_addr = ((flags & VM_FLAGS_ANYWHERE)
+                   ? vm_map_min(parent)
+                   : vm_map_trunc_page(*addr,
+                                       VM_MAP_PAGE_MASK(parent)));
 
        kr = vm_map_enter(parent, &map_addr, map_size,
                          (vm_map_offset_t) 0, flags,
@@ -873,6 +1165,8 @@ kmem_suballoc(
        map = vm_map_create(vm_map_pmap(parent), map_addr, map_addr + map_size, pageable);
        if (map == VM_MAP_NULL)
                panic("kmem_suballoc: vm_map_create failed");   /* "can't happen" */
+       /* inherit the parent map's page size */
+       vm_map_set_page_shift(map, VM_MAP_PAGE_SHIFT(parent));
 
        kr = vm_map_submap(parent, map_addr, map_addr + map_size, map, map_addr, FALSE);
        if (kr != KERN_SUCCESS) {
@@ -903,8 +1197,10 @@ kmem_init(
        vm_map_offset_t map_start;
        vm_map_offset_t map_end;
 
-       map_start = vm_map_trunc_page(start);
-       map_end = vm_map_round_page(end);
+       map_start = vm_map_trunc_page(start,
+                                     VM_MAP_PAGE_MASK(kernel_map));
+       map_end = vm_map_round_page(end,
+                                   VM_MAP_PAGE_MASK(kernel_map));
 
        kernel_map = vm_map_create(pmap_kernel(),VM_MIN_KERNEL_AND_KEXT_ADDRESS,
                            map_end, FALSE);