commit-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnumach] 01/05: Imported Upstream version 1.6+git20160311


From: Samuel Thibault
Subject: [gnumach] 01/05: Imported Upstream version 1.6+git20160311
Date: Fri, 11 Mar 2016 23:22:34 +0000

This is an automated email from the git hooks/post-receive script.

sthibault pushed a commit to branch master
in repository gnumach.

commit f1e52f825e4dd5dceae317bf5b61b4251f835880
Author: Samuel Thibault <address@hidden>
Date:   Fri Mar 11 22:03:15 2016 +0000

    Imported Upstream version 1.6+git20160311
---
 ChangeLog                                | 530 ++++++++++++++++++
 Makefile.in                              |  85 ++-
 Makefrag.am                              |   9 +-
 build-aux/compile                        |   2 +-
 build-aux/config.guess                   |  47 +-
 build-aux/config.sub                     |  36 +-
 build-aux/depcomp                        |   2 +-
 build-aux/install-sh                     | 373 ++++++-------
 build-aux/mdate-sh                       |   8 +-
 build-aux/missing                        |   2 +-
 build-aux/test-driver                    |  15 +-
 configure                                |  20 +-
 device/dev_lookup.c                      |   2 +-
 device/dev_pager.c                       |   4 +-
 device/ds_routines.c                     |   4 +-
 device/net_io.c                          |   4 +-
 doc/mach.info                            | 242 ++++----
 doc/mach.info-1                          |  18 +-
 doc/mach.info-2                          |   6 +-
 doc/mach.texi                            |   5 +-
 doc/stamp-vti                            |   8 +-
 doc/version.texi                         |   8 +-
 i386/Makefrag.am                         |   4 +
 i386/i386/cpu.h                          | 110 ++++
 i386/i386/db_trace.c                     |   4 +-
 i386/i386/fpu.c                          |  51 +-
 i386/i386/fpu.h                          |   1 +
 i386/i386/locore.S                       |  14 +-
 i386/i386/machine_task.c                 |   2 +-
 i386/i386/pcb.c                          |   9 +-
 i386/i386/pcb.h                          |   2 +-
 i386/i386/thread.h                       |   1 +
 i386/i386/vm_param.h                     |  53 +-
 i386/i386at/biosmem.c                    | 910 +++++++++++++++++++++++++++++++
 i386/i386at/biosmem.h                    |  88 +++
 i386/i386at/elf.h                        |  61 +++
 i386/i386at/kd_mouse.c                   |   2 +
 i386/i386at/model_dep.c                  | 307 +----------
 i386/i386at/model_dep.h                  |   9 +
 i386/include/mach/i386/multiboot.h       | 105 ++++
 i386/include/mach/i386/vm_types.h        |   9 +
 i386/intel/pmap.c                        |   4 +-
 include/mach/mach.defs                   |  41 ++
 include/mach_debug/slab_info.h           |   6 -
 ipc/ipc_init.c                           |   8 +-
 ipc/ipc_marequest.c                      |   2 +-
 ipc/ipc_object.c                         |   3 +-
 kern/act.c                               |   2 +-
 kern/bootstrap.c                         |  16 +-
 kern/cpu_number.h                        |   2 +
 i386/i386at/model_dep.h => kern/exc.defs |  22 +-
 kern/log2.h                              |  50 ++
 kern/processor.c                         |   2 +-
 kern/rdxtree.c                           |   2 +-
 kern/sched_prim.c                        |   3 +
 kern/slab.c                              | 354 ++++++------
 kern/slab.h                              |  33 +-
 kern/startup.c                           |   2 +-
 kern/task.c                              |   2 +-
 kern/thread.c                            |  27 +-
 linux/dev/drivers/block/ahci.c           |  47 +-
 linux/dev/glue/glue.h                    |   4 +-
 linux/dev/glue/kmem.c                    |   6 +-
 linux/dev/include/linux/blk.h            |   1 +
 linux/dev/init/main.c                    | 143 +----
 linux/pcmcia-cs/glue/ds.c                |   6 -
 linux/src/arch/i386/kernel/bios32.c      |   2 +
 linux/src/drivers/block/ide.c            |  11 +-
 linux/src/include/linux/compiler-gcc6.h  |  67 +++
 version.m4                               |   2 +-
 vm/memory_object_proxy.c                 |   2 +-
 vm/pmap.h                                |  15 +-
 vm/vm_external.c                         |  10 +-
 vm/vm_external.h                         |   5 +
 vm/vm_fault.c                            |   6 +-
 vm/vm_init.c                             |   1 +
 vm/vm_map.c                              |  64 +--
 vm/vm_map.h                              |   3 -
 vm/vm_object.c                           | 237 ++++----
 vm/vm_object.h                           |  10 +-
 vm/vm_page.c                             | 782 ++++++++++++++++++++++++++
 vm/vm_page.h                             | 251 ++++++++-
 vm/vm_pageout.c                          |  27 +-
 vm/vm_resident.c                         | 593 ++++++--------------
 vm/vm_user.c                             |   2 +-
 85 files changed, 4248 insertions(+), 1802 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3d8d038..b909aba 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,508 @@
+2016-03-11  Samuel Thibault  <address@hidden>
+
+       Ship missing files
+       Makefrag.am (libkernel_a_SOURCES): Add kern/log2.h.
+       (EXTRA_DIST): Add kern/exc.defs.
+       i386/Makefrag.am (libkernel_a_SOURCES): Add i386/i386at/elf.h.
+
+2016-03-11  Richard Braun  <address@hidden>
+
+       Merge remote-tracking branch 'remotes/origin/rbraun/vm_cache_policy'
+       Finally ;-).
+
+2016-03-09  Richard Braun  <address@hidden>
+
+       Fix stack allocation on Xen
+       Stack allocation on Xen can fail because of fragmentation. This change
+       makes stack allocation use the slab allocator.
+
+       * kern/thread.c (thread_stack_cache): New global variable.
+       (stack_alloc): Use kmem_cache_alloc instead of vm_page_grab_contig.
+       (stack_collect): Use kmem_cache_free instead of vm_page_free_contig.
+       (kmem_cache_init): Initialize thread_stack_cache.
+
+2016-03-09  Richard Braun  <address@hidden>
+
+       Relax slab allocation alignment constraint
+       * kern/slab.c (kmem_pagealloc_virtual): Pass alignment to function,
+       call kmem_alloc_aligned when greater than a page.
+       (kmem_pagealloc): Pass alignment to function.
+       (kmem_slab_create): Update call to kmem_pagealloc.
+       (kalloc): Likewise.
+       (kmem_cache_compute_properties): Fix handling of color with large slab
+       sizes.
+       (kmem_cache_init): Allow alignment greater than the page size.
+
+2016-03-06  Samuel Thibault  <address@hidden>
+
+       Inherit fpu control word from parent to child
+       * i386/i386/thread.h (struct pcb): Add init_control field.
+       * i386/i386/fpu.h (fpinherit): New prototype.
+       * i386/i386/fpu.c (fpinit): Add thread parameter. When init_control 
field is
+       set, use that value instead of a hardcoded one.
+       (fpinherit): New function.
+       (fp_load): Pass thread parameter to fpinit().
+       * kern/thread.c (thread_create): Pass parent task to pcb_init().
+       * i386/i386/pcb.c (pcb_init): Add parent_task parameter, call fpinherit 
when
+       it is equal to current_task().
+
+2016-02-28  Justus Winter  <address@hidden>
+
+       ipc: add missing kernel object type
+       * ipc/ipc_object.c (ikot_print_array): Add entry for IKOT_PAGER_PROXY.
+
+       doc: clarify memory object initialization
+       * doc/mach.texi: Mention another way how a memory manager can signal
+       that an object is ready.
+
+       i386: add parts of cpu.h from x15
+       * i386/Makefrag.am (libkernel_a_SOURCES): Add new file.
+       * i386/i386/cpu.h: New file.
+
+2016-02-26  Justus Winter  <address@hidden>
+
+       include: avoid generating unused client stubs
+       * include/mach/mach.defs: Avoid generating unused client stubs, some
+       of which use an unreasonable amount of stack space and showed up in
+       compiler warnings.
+
+2016-02-26  Samuel Thibault  <address@hidden>
+
+       Document thread_sleep about events woken from interrupt handlers
+       * kern/sched_prim.c (thread_sleep): Document case of events woken from
+       interrupt handlers.
+
+       Document why code is not racy
+       * i386/i386at/kd_mouse.c (kd_mouse_read): Document why the
+       assert_wait/thread_block pair is not racy.
+
+2016-02-23  Justus Winter  <address@hidden>
+
+       Include the exception protocol in 'gnumach.msgids'
+       * Makefrag.am: Include the exception protocol in 'gnumach.msgids'.
+       * kern/exc.defs: New file.
+
+2016-02-22  Richard Braun  <address@hidden>
+
+       Remove kmem cache flags from the debugging interface
+       * include/mach_debug/slab_info.h (CACHE_FLAGS_NO_CPU_POOL,
+       CACHE_FLAGS_SLAB_EXTERNAL, CACHE_FLAGS_NO_RECLAIM,
+       CACHE_FLAGS_VERIFY, CACHE_FLAGS_DIRECT): Remove macros.
+       * kern/slab.c (host_slab_info): Pass raw cache flags to caller.
+
+2016-02-22  Richard Braun  <address@hidden>
+
+       Fix slab allocator option handling
+       The slab allocator has grown to use multiple ways to allocate slabs
+       as well as track them, which got a little messy. One consequence is
+       the breaking of the KMEM_CF_VERIFY option. In order to make the code
+       less confusing, this change expresses all options as explicit cache
+       flags and clearly defines their relationships.
+
+       The special kmem_slab and vm_map_entry caches are initialized
+       accordingly.
+
+       * kern/slab.c (KMEM_CF_DIRECTMAP): Rename to ...
+       (KMEM_CF_PHYSMEM): ... this new macro.
+       (KMEM_CF_DIRECT): Restore macro.
+       (KMEM_CF_USE_TREE, KMEM_CF_USE_PAGE): New macros.
+       (KMEM_CF_VERIFY): Update value.
+       (kmem_pagealloc_directmap): Rename to...
+       (kmem_pagealloc_physmem): ... this new function.
+       (kmem_pagefree_directmap): Rename to ...
+       (kmem_pagefree_physmem): ... this new function.
+       (kmem_pagealloc, kmem_pagefree): Update macro names.
+       (kmem_slab_use_tree): Remove function.
+       (kmem_slab_create, kmem_slab_destroy): Update according to the new
+       cache flags.
+       (kmem_cache_compute_sizes): Rename to ...
+       (kmem_cache_compute_properties): ... this new function, and update
+       to properly set cache flags.
+       (kmem_cache_init): Update call to kmem_cache_compute_properties.
+       (kmem_cache_alloc_from_slab): Check KMEM_CF_USE_TREE instead of
+       calling the defunct kmem_slab_use_tree function.
+       (kmem_cache_free_to_slab): Update according to the new cache flags.
+       kmem_cache_free_verify): Add assertion.
+       (slab_init): Update initialization of kmem_slab_cache.
+       * kern/slab.h (KMEM_CACHE_DIRECTMAP): Rename to ...
+       (KMEM_CACHE_PHYSMEM): ... this new macro.
+       * vm/vm_map.c (vm_map_init): Update initialization of 
vm_map_entry_cache.
+
+2016-02-22  Richard Braun  <address@hidden>
+
+       Optimize slab lookup on the free path
+       Caches that use external slab data but allocate slabs from the direct
+       physical mapping can look up slab data in constant time by associating
+       the slab data directly with the underlying page.
+
+       * kern/slab.c (kmem_slab_use_tree): Take KMEM_CF_DIRECTMAP into account.
+       (kmem_slab_create): Set page private data if relevant.
+       (kmem_slab_destroy): Clear page private data if relevant.
+       (kmem_cache_free_to_slab): Use page private data if relevant.
+       * vm/vm_page.c (vm_page_init_pa): Set `priv' member to NULL.
+       * vm/vm_page.h (vm_page_set_priv, vm_page_get_priv): New functions.
+
+2016-02-22  Richard Braun  <address@hidden>
+
+       Fix unused variable warnings
+       * kern/slab.c (slab_init): Remove unused variables.
+
+2016-02-20  Richard Braun  <address@hidden>
+
+       Avoid slab allocation failures caused by memory fragmentation
+       Since the slab allocator has been changed to sit directly on top of the
+       physical allocator, failures caused by fragmentation have been observed,
+       as one could expect. This change makes the slab allocator revert to
+       kernel virtual memory when allocating larger-than-page slabs. This
+       solution is motivated in part to avoid the complexity of other solutions
+       such as page mobility, and also because a microkernel cannot be extended
+       to new arbitrary uncontrolled usage patterns such as a monolithic kernel
+       with loadable modules. As such, large objects are rare, and their use
+       infrequent, which is compatible with the use of kernel virtual memory.
+
+       * kern/slab.c: Update module description.
+       (KMEM_CF_SLAB_EXTERNAL, KMEM_CF_VERIFY): Update values.
+       (KMEM_CF_DIRECT): Remove macro.
+       (KMEM_CF_DIRECTMAP): New macro.
+       (kmem_pagealloc_directmap, kmem_pagefree_directmap,
+       kmem_pagealloc_virtual, kmem_pagefree_virtual): New functions.
+       (kmem_pagealloc, kmem_pagefree, kmem_slab_create, kmem_slab_destroy,
+       kalloc, kfree): Update to use the new pagealloc functions.
+       (kmem_cache_compute_sizes): Update the algorithm used to determine slab
+       size and other cache properties.
+       (kmem_slab_use_tree, kmem_cache_free_to_slab, host_slab_info): Update to
+       correctly use the cache flags.
+       (slab_init): Add KMEM_CACHE_DIRECTMAP to the kmem_slab_cache init flags.
+       * kern/slab.h (KMEM_CACHE_VERIFY): Change value.
+       (KMEM_CACHE_DIRECTMAP): New macro.
+       * vm/vm_map.c (vm_map_init): Add KMEM_CACHE_DIRECTMAP to the
+       vm_map_entry_cache init flags.
+
+2016-02-16  Richard Braun  <address@hidden>
+
+       Avoid panics on physical memory exhaustion
+       * vm/vm_resident (vm_page_grab): Return NULL instead of calling panic
+       on memory exhaustion.
+
+2016-02-14  Samuel Thibault  <address@hidden>
+
+       Reduce VM_KERNEL_MAP_SIZE
+       Now that KMEM_MAP_SIZE (128MiB) has been removed.
+
+       * i386/i386/vm_param.h (VM_KERNEL_MAP_SIZE): Decrease by 128MiB.
+
+2016-02-07  Justus Winter  <address@hidden>
+
+       vm: initialize external maps
+       * vm/vm_external.c (vm_external_create): Initialize allocated maps.
+
+2016-02-07  Richard Braun  <address@hidden>
+
+       Fix page cache accounting
+       * vm/vm_object.c (vm_object_bootstrap): Set template object `cached'
+       member to FALSE.
+       (vm_object_cache_add, vm_object_cache_remove): New functions.
+       (vm_object_collect, vm_object_deallocate, vm_object_lookup,
+       vm_object_lookup_name, vm_object_destroy): Use new cache management 
functions.
+       (vm_object_terminate, vm_object_collapse): Make sure object isn't 
cached.
+       * vm/vm_object.h (struct vm_object): New `cached' member.
+
+2016-02-07  Justus Winter  <address@hidden>
+
+       vm: allocate a large map for all objects larger than SMALL_SIZE
+       * vm/vm_external.c (vm_external_create): Allocate a large map for all
+       objects larger than SMALL_SIZE.  'vm_external_state_{g,s}et' can deal
+       with offsets larger than 'LARGE_SIZE', so currently objects larger
+       than 'LARGE_SIZE' are missing out on the optimization.
+
+       vm: remove unused field from struct vm_external
+       * vm/vm_external.h (struct vm_external): Remove unused field
+       'existence_count'.
+
+2016-02-07  Richard Braun  <address@hidden>
+
+       Remove kmem map
+       Now that the slab allocator doesn't use kernel virtual memory any more,
+       this map has become irrelevant.
+
+       * kern/slab.c (KMEM_MAP_SIZE): Remove macro.
+       (kmem_map_store, kmem_map): Remove variables.
+       (slab_init): Remove call kmem_submap.
+       * kern/slab.h (kmem_map): Remove extern declaration.
+
+2016-02-06  Richard Braun  <address@hidden>
+
+       Change computation of slab size
+       Allocating directly out of the physical memory allocator makes the slab
+       allocator vulnerable to failures due to fragmentation. This change makes
+       the slab allocator use the lowest possible size for its slabs to reduce
+       the chance of contiguous allocation failures.
+
+       * kern/slab.c (KMEM_MIN_BUFS_PER_SLAB, KMEM_SLAB_SIZE_THRESHOLD): Remove
+       macros.
+       (kmem_cache_compute_sizes): Update the algorithm used to determine slab
+       size and other cache properties.
+
+2016-02-02  Richard Braun  <address@hidden>
+
+       Fix various memory managment errors
+       A few errors were introduced in the latest changes.
+
+       o Add VM_PAGE_WAIT calls around physical allocation attempts in case of
+       memory exhaustion.
+       o Fix stack release.
+       o Fix memory exhaustion report.
+       o Fix free page accounting.
+
+       * kern/slab.c (kmem_pagealloc, kmem_pagefree): New functions
+       (kmem_slab_create, kmem_slab_destroy, kalloc, kfree): Use kmem_pagealloc
+       and kmem_pagefree instead of the raw page allocation functions.
+       (kmem_cache_compute_sizes): Don't store slab order.
+       * kern/slab.h (struct kmem_cache): Remove `slab_order' member.
+       * kern/thread.c (stack_alloc): Call VM_PAGE_WAIT in case of memory
+       exhaustion.
+       (stack_collect): Call vm_page_free_contig instead of kmem_free to
+       release pages.
+       * vm/vm_page.c (vm_page_seg_alloc): Fix memory exhaustion report.
+       (vm_page_setup): Don't update vm_page_free_count.
+       (vm_page_free_pa): Check page parameter.
+       (vm_page_mem_free): New function.
+       * vm/vm_page.h (vm_page_free_count): Remove extern declaration.
+       (vm_page_mem_free): New prototype.
+       * vm/vm_pageout.c: Update comments not to refer to vm_page_free_count.
+       (vm_pageout_scan, vm_pageout_continue, vm_pageout): Use vm_page_mem_free
+       instead of vm_page_free_count, update types accordingly.
+       * vm/vm_resident.c (vm_page_free_count, vm_page_free_count_minimum):
+       Remove variables.
+       (vm_page_free_avail): New variable.
+       (vm_page_bootstrap, vm_page_grab, vm_page_release, vm_page_grab_contig,
+       vm_page_free_contig, vm_page_wait): Use vm_page_mem_free instead of 
vm_page_free_count,
+       update types accordingly, don't set vm_page_free_count_minimum.
+       * vm/vm_user.c (vm_statistics): Likewise.
+
+2016-02-02  Richard Braun  <address@hidden>
+
+       Fix unused variable warnings
+       * i386/i386at/biosmem.c (biosmem_bootstrap): Remove unused variables.
+
+2016-02-02  Richard Braun  <address@hidden>
+
+       Stack the slab allocator directly on top of the physical allocator
+       In order to increase the amount of memory available for kernel objects,
+       without reducing the amount of memory available for user processes,
+       a new allocation strategy is introduced in this change.
+
+       Instead of allocating kernel objects out of kernel virtual memory,
+       the slab allocator directly uses the direct mapping of physical
+       memory as its backend. This largely increases the kernel heap, and
+       removes the need for address translation updates.
+
+       In order to allow this strategy, an assumption made by the interrupt
+       code had to be removed. In addition, kernel stacks are now also
+       allocated directly from the physical allocator.
+
+       * i386/i386/db_trace.c: Include i386at/model_dep.h
+       (db_i386_reg_value): Update stack check.
+       * i386/i386/locore.S (trap_from_kernel, all_intrs,
+       int_from_intstack): Update interrupt handling.
+       * i386/i386at/model_dep.c: Include kern/macros.h.
+       (int_stack, int_stack_base): New variables.
+       (int_stack_high): Remove variable.
+       (i386at_init): Update interrupt stack initialization.
+       * i386/i386at/model_dep.h: Include i386/vm_param.h.
+       (int_stack_top, int_stack_base): New extern declarations.
+       (ON_INT_STACK): New macro.
+       * kern/slab.c: Include vm/vm_page.h
+       (KMEM_CF_NO_CPU_POOL, KMEM_CF_NO_RECLAIM): Remove macros.
+       (kmem_pagealloc, kmem_pagefree, kalloc_pagealloc, kalloc_pagefree): 
Remove
+       functions.
+       (kmem_slab_create): Allocate slab pages directly from the physical 
allocator.
+       (kmem_slab_destroy): Release slab pages directly to the physical 
allocator.
+       (kmem_cache_compute_sizes): Update the slab size computation algorithm 
to
+       return a power-of-two suitable for the physical allocator.
+       (kmem_cache_init): Remove custom allocation function pointers.
+       (kmem_cache_reap): Remove check on KMEM_CF_NO_RECLAIM.
+       (slab_init, kalloc_init): Update calls to kmem_cache_init.
+       (kalloc, kfree): Directly fall back on the physical allocator for big
+       allocation sizes.
+       (host_slab_info): Remove checks on defunct flags.
+       * kern/slab.h (kmem_slab_alloc_fn_t, kmem_slab_free_fn_t): Remove types.
+       (struct kmem_cache): Add `slab_order' member, remove `slab_alloc_fn' and
+       `slab_free_fn' members.
+       (KMEM_CACHE_NOCPUPOOL, KMEM_CACHE_NORECLAIM): Remove macros.
+       (kmem_cache_init): Update prototype, remove custom allocation functions.
+       * kern/thread.c (stack_alloc): Allocate stacks from the physical 
allocator.
+       * vm/vm_map.c (vm_map_kentry_cache, kentry_data, kentry_data_size): 
Remove
+       variables.
+       (kentry_pagealloc): Remove function.
+       (vm_map_init): Update calls to kmem_cache_init, remove initialization of
+       vm_map_kentry_cache.
+       (vm_map_create, _vm_map_entry_dispose, vm_map_copyout): Unconditionnally
+       use vm_map_entry_cache.
+       * vm/vm_map.h (kentry_data, kentry_data_size, kentry_count): Remove 
extern
+       declarations.
+       * vm/vm_page.h (VM_PT_STACK): New page type.
+       * device/dev_lookup.c (dev_lookup_init): Update calls to 
kmem_cache_init.
+       * device/dev_pager.c (dev_pager_hash_init, device_pager_init): Likewise.
+       * device/ds_routines.c (mach_device_init, mach_device_trap_init): 
Likewise.
+       * device/net_io.c (net_io_init): Likewise.
+       * i386/i386/fpu.c (fpu_module_init): Likewise.
+       * i386/i386/machine_task.c (machine_task_module_init): Likewise.
+       * i386/i386/pcb.c (pcb_module_init): Likewise.
+       * i386/intel/pmap.c (pmap_init): Likewise.
+       * ipc/ipc_init.c (ipc_bootstrap): Likewise.
+       * ipc/ipc_marequest.c (ipc_marequest_init): Likewise.
+       * kern/act.c (global_act_init): Likewise.
+       * kern/processor.c (pset_sys_init): Likewise.
+       * kern/rdxtree.c (rdxtree_cache_init): Likewise.
+       * kern/task.c (task_init): Likewise.
+       * vm/memory_object_proxy.c (memory_object_proxy_init): Likewise.
+       * vm/vm_external.c (vm_external_module_initialize): Likewise.
+       * vm/vm_fault.c (vm_fault_init): Likewise.
+       * vm/vm_object.c (vm_object_bootstrap): Likewise.
+       * vm/vm_resident.c (vm_page_module_init): Likewise.
+       (vm_page_bootstrap): Remove initialization of kentry_data.
+
+2016-01-30  Richard Braun  <address@hidden>
+
+       Fix early page allocation on Xen
+       The Xen target was completely ignored when porting the biosmem and
+       vm_page physical memory allocators. Let's fix this.
+
+       * i386/Makefrag.am (libkernel_a_SOURCES): Add i386/i386at/biosmem.{c,h}.
+       * i386/i386/vm_page.h (VM_PAGE_MAX_SEGS, VM_PAGE_DIRECTMAP_LIMIT,
+       VM_PAGE_HIGHMEM_LIMIT): Define for Xen.
+       * i386/i386at/biosmem.c: Include mach/xen.h.
+       (biosmem_panic_setup_msg): Comment out for Xen since it's unused.
+       (biosmem_map_build, biosmem_map_build_simple,
+       biosmem_save_cmdline_sizes, biosmem_find_boot_data_update,
+       biosmem_find_boot_data, biosmem_setup_allocator): Likewise.
+       (biosmem_bootstrap_common): New function.
+       (biosmem_xen_bootstrap): Likewise, for Xen.
+       (biosmem_bootalloc): Perform bottom-up allocations for Xen.
+       * i386/i386at/biosmem.h (biosmem_xen_bootstrap): New prototype, for Xen.
+       * i386/i386at/model_dep.c (i386at_init): Call biosmem_xen_bootstrap 
instead
+       of biosmem_bootstrap on Xen.
+       * i386/include/mach/i386/vm_types.h (phys_addr_t): Define as an unsigned
+       64-bits integer when PAE is enabled.
+
+2016-01-29  Samuel Thibault  <address@hidden>
+
+       Disable probing legacy IDE when AHCI driver works
+       * linux/src/drivers/block/ide.c (default_io_base): Do not qualify const.
+       (ide_disable_base): New function.
+       * linux/dev/include/linux/blk.h (ide_disable_base): New declaration.
+       * linux/dev/drivers/block/ahci.c (ahci_probe_dev): Call ide_disable_base
+       with each I/O BAR of the AHCI PCI card.
+
+       Use PCI macros
+       * linux/dev/drivers/block/ahci.c (ahci_probe_dev): Use
+       PCI_BASE_ADDRESS_SPACE_IO and PCI_BASE_ADDRESS_MEM_MASK macros instead 
of
+       hardcoded values.
+
+       ahci: print PCI bus/dev/fun in hexadecimal
+       * linux/dev/drivers/block/ahci.c: Print PCI bus/dev/fun number in
+       hexadecimal
+
+2016-01-29  Samuel Thibault  <address@hidden>
+
+       Fallback on direct PCI access when no BIOS32 is available
+       Some hardware start shippping with no BIOS32 at all, and we'll have to
+       implement ACPI to get the address of the mmconfig table.  In the 
meanwhile,
+       we can hope that the direct probe works (it does on HP820 for instance).
+
+       * linux/src/arch/i386/kernel/bios32.c (pcibios_init): Also try
+       check_direct_pci() when bios32 probing failed.
+
+2016-01-23  Richard Braun  <address@hidden>
+
+       Merge branch 'rbraun/vm_page'
+
+2016-01-23  Richard Braun  <address@hidden>
+
+       Use vm_page as the physical memory allocator
+       This change replaces the historical page allocator with a buddy 
allocator
+       implemented in vm/vm_page.c. This allocator allows easy contiguous 
allocations
+       and also manages memory inside segments. In a future change, these 
segments
+       will be used to service requests with special constraints, such as 
"usable
+       for 16-bits DMA" or "must be part of the direct physical mapping".
+
+       * Makefrag.am (libkernel_a_SOURCES): Add vm/vm_page.c.
+       * i386/Makefrag.am (libkernel_a_SOURCES): Add i386/i386at/biosmem.{c,h}.
+       * i386/i386/vm_param.h: Include kern/macros.h.
+       (VM_PAGE_DMA_LIMIT, VM_PAGE_MAX_SEGS, VM_PAGE_DMA32_LIMIT,
+       VM_PAGE_DIRECTMAP_LIMIT, VM_PAGE_HIGHMEM_LIMIT, VM_PAGE_SEG_DMA,
+       VM_PAGE_SEG_DMA32, VM_PAGE_SEG_DIRECTMAP, VM_PAGE_SEG_HIGHMEM): New 
macros.
+       * i386/i386at/model_dep.c: Include i386at/biosmem.h.
+       (avail_next, avail_remaining): Remove variables.
+       (mem_size_init): Remove function.
+       (i386at_init): Initialize and use the biosmem module for early physical
+       memory management.
+       (pmap_free_pages): Return phys_last_addr instead of avail_remaining.
+       (init_alloc_aligned): Turn into a wrapper for biosmem_bootalloc.
+       (pmap_grab_page): Directly call init_alloc_aligned instead of 
pmap_next_page.
+       * i386/include/mach/i386/vm_types.h (phys_addr_t): New type.
+       * kern/bootstrap.c (free_bootstrap_pages): New function.
+       (bootstrap_create): Call free_bootstrap_pages instead of vm_page_create.
+       * kern/cpu_number.h (CPU_L1_SIZE): New macro.
+       * kern/slab.h: Include kern/cpu_number.h.
+       (CPU_L1_SIZE): Remove macro, moved to kern/cpu_number.h.
+       * kern/startup.c (setup_main): Change the value of 
machine_info.memory_size.
+       * linux/dev/glue/glue.h (alloc_contig_mem, free_contig_mem): Update 
prototypes.
+       * linux/dev/glue/kmem.c (linux_kmem_init): Don't use defunct page queue.
+       * linux/dev/init/main.c (linux_init): Don't free unused memory.
+       (alloc_contig_mem, free_contig_mem): Turn into wrappers for the vm_page
+       allocator.
+       * linux/pcmcia-cs/glue/ds.c (PAGE_SHIFT): Don't undefine.
+       * vm/pmap.h (pmap_startup, pmap_next_page): Remove prototypes.
+       * vm/vm_fault.c (vm_fault_page): Update calls to vm_page_convert.
+       * vm/vm_init.c (vm_mem_init): Call vm_page_info_all.
+       * vm/vm_object.c (vm_object_page_map): Update call to vm_page_init.
+       * vm/vm_page.h (vm_page_queue_free): Remove variable declaration.
+       (vm_page_create, vm_page_release_fictitious, vm_page_release): Remove
+       declarations.
+       (vm_page_convert, vm_page_init): Update prototypes.
+       (vm_page_grab_contig, vm_page_free_contig): New prototypes.
+       * vm/vm_resident.c (vm_page_template, vm_page_queue_free,
+       vm_page_big_pagenum): Remove variables.
+       (vm_page_bootstrap): Update and call vm_page_setup.
+       (pmap_steal_memory): Update and call vm_page_bootalloc.
+       (pmap_startup, vm_page_create, vm_page_grab_contiguous_pages): Remove 
functions.
+       (vm_page_init_template, vm_page_grab_contig,
+       vm_page_free_contig): New functions.
+       (vm_page_init): Update and call vm_page_init_template.
+       (vm_page_release_fictitious): Make static.
+       (vm_page_more_fictitious): Update call to vm_page_init.
+       (vm_page_convert): Rewrite to comply with vm_page.
+       (vm_page_grab): Update and call vm_page_alloc_pa.
+       (vm_page_release): Update and call vm_page_free_pa.
+
+2016-01-23  Richard Braun  <address@hidden>
+
+       Import the vm_page module from X15 and relicense to GPLv2+
+       * vm/vm_page.c: New File.
+       * vm/vm_page.h: Merge vm_page.h from X15.
+       (struct vm_page): New members: node, type, seg_index, order,
+       vm_page_header. Turn phys_addr into a phys_addr_t.
+
+       Import the biosmem module from X15 and relicense to GPLv2+
+       * i386/i386at/biosmem.c: New file.
+       * i386/i386at/biosmem.h: Likewise.
+
+       Import the multiboot module from X15 and relicense to GPLv2+
+       * i386/include/mach/i386/multiboot.h: Merge multiboot.h from X15.
+
+       Import the elf module from X15 and relicense to GPLv2+
+       * i386/i386at/elf.h: New file.
+
+       Import the log2 module from X15 and relicense to GPLv2+
+       * kern/log2.h: New file.
+
+2016-01-14  Samuel Thibault  <address@hidden>
+
+       Fix build with gcc-6
+       * linux/src/include/linux/compiler-gcc6.h: New file.
+
 2016-01-13  Samuel Thibault  <address@hidden>
 
        Increase kernel map size
@@ -4308,6 +4813,31 @@
        Increase kernel map entry pool size
        * vm/vm_map.h (KENTRY_DATA_SIZE): Set to 256 pages.
 
+2013-10-09  Richard Braun  <address@hidden>
+
+       VM cache policy change
+       This patch lets the kernel unconditionnally cache non empty unreferenced
+       objects instead of using a fixed arbitrary limit. As the pageout daemon
+       evicts pages, it collects cached objects that have become empty. The
+       effective result is a graceful adjustment of the number of objects
+       related to memory management (virtual memory objects, their associated
+       ports, and potentially objects maintained in the external memory
+       managers). Physical memory can now be almost entirely filled up with
+       cached pages. In addition, these cached pages are not automatically
+       deactivated as objects can quickly be referenced again.
+
+       There are problems with this patch however. The first is that, on
+       machines with a large amount of physical memory (above 1 GiB but it also
+       depends on usage patterns), scalability issues are exposed. For example,
+       file systems which don't throttle their writeback requests can create
+       thread storms, strongly reducing system responsiveness. Other issues
+       such as linear scans of memory objects also add visible CPU overhead.
+
+       The second is that, as most memory is used, it increases the chances of
+       swapping deadlocks. Applications that map large objects and quickly
+       cause lots of page faults can still easily bring the system to its
+       knees.
+
 2013-09-28  Samuel Thibault  <address@hidden>
 
        Add files missing in distrib tarball
diff --git a/Makefile.in b/Makefile.in
index 90b4555..23a8556 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -953,8 +953,11 @@ noinst_PROGRAMS = gnumach.o$(EXEEXT)
 @address@hidden = \
 @HOST_ix86_TRUE@       i386/i386at/autoconf.c \
 @HOST_ix86_TRUE@       i386/i386at/autoconf.h \
address@hidden@ i386/i386at/biosmem.c \
address@hidden@ i386/i386at/biosmem.h \
 @HOST_ix86_TRUE@       i386/i386at/conf.c \
 @HOST_ix86_TRUE@       i386/i386at/cons_conf.c \
address@hidden@ i386/i386at/elf.h \
 @HOST_ix86_TRUE@       i386/i386at/idt.h \
 @HOST_ix86_TRUE@       i386/i386at/model_dep.c \
 @HOST_ix86_TRUE@       i386/i386at/model_dep.h \
@@ -1020,6 +1023,7 @@ noinst_PROGRAMS = gnumach.o$(EXEEXT)
 @HOST_ix86_TRUE@       i386/i386/ast.h \
 @HOST_ix86_TRUE@       i386/i386/ast_check.c \
 @HOST_ix86_TRUE@       i386/i386/ast_types.h \
address@hidden@ i386/i386/cpu.h \
 @HOST_ix86_TRUE@       i386/i386/cpu_number.h \
 @HOST_ix86_TRUE@       i386/i386/cswitch.S \
 @HOST_ix86_TRUE@       i386/i386/db_disasm.c \
@@ -1210,6 +1214,7 @@ nodist_lib_dep_tr_for_defs_a_OBJECTS = 
vm/lib_dep_tr_for_defs_a-memory_object_us
        kern/lib_dep_tr_for_defs_a-mach_debug.server.defs.$(OBJEXT) \
        kern/lib_dep_tr_for_defs_a-mach_host.server.defs.$(OBJEXT) \
        ipc/lib_dep_tr_for_defs_a-notify.none.defs.$(OBJEXT) \
+       kern/lib_dep_tr_for_defs_a-exc.none.defs.$(OBJEXT) \
        $(am__objects_1)
 lib_dep_tr_for_defs_a_OBJECTS =  \
        $(nodist_lib_dep_tr_for_defs_a_OBJECTS)
@@ -1249,7 +1254,7 @@ am__libkernel_a_SOURCES_DIST = ddb/db_access.c 
ddb/db_access.h \
        kern/ipc_kobject.h kern/ipc_mig.c kern/ipc_mig.h \
        kern/ipc_sched.c kern/ipc_sched.h kern/ipc_tt.c kern/ipc_tt.h \
        kern/kalloc.h kern/kern_types.h kern/list.h kern/lock.c \
-       kern/lock.h kern/lock_mon.c kern/mach_clock.c \
+       kern/lock.h kern/lock_mon.c kern/log2.h kern/mach_clock.c \
        kern/mach_clock.h kern/mach_factor.c kern/mach_factor.h \
        kern/machine.c kern/machine.h kern/macros.h kern/pc_sample.c \
        kern/pc_sample.h kern/printf.c kern/printf.h kern/priority.c \
@@ -1271,11 +1276,11 @@ am__libkernel_a_SOURCES_DIST = ddb/db_access.c 
ddb/db_access.h \
        vm/vm_external.c vm/vm_external.h vm/vm_fault.c vm/vm_fault.h \
        vm/vm_init.c vm/vm_init.h vm/vm_kern.c vm/vm_kern.h \
        vm/vm_map.c vm/vm_map.h vm/vm_object.c vm/vm_object.h \
-       vm/vm_page.h vm/vm_pageout.c vm/vm_pageout.h vm/vm_print.h \
-       vm/vm_resident.c vm/vm_resident.h vm/vm_types.h vm/vm_user.c \
-       vm/vm_user.h device/blkio.c device/blkio.h device/buf.h \
-       device/chario.c device/chario.h device/cirbuf.h device/conf.h \
-       device/cons.c device/cons.h device/device_emul.h \
+       vm/vm_page.c vm/vm_page.h vm/vm_pageout.c vm/vm_pageout.h \
+       vm/vm_print.h vm/vm_resident.c vm/vm_resident.h vm/vm_types.h \
+       vm/vm_user.c vm/vm_user.h device/blkio.c device/blkio.h \
+       device/buf.h device/chario.c device/chario.h device/cirbuf.h \
+       device/conf.h device/cons.c device/cons.h device/device_emul.h \
        device/dev_hdr.h device/dev_lookup.c device/dev_master.h \
        device/dev_name.c device/dev_pager.c device/dev_pager.h \
        device/device_init.c device/device_init.h device/device_port.h \
@@ -1306,7 +1311,8 @@ am__libkernel_a_SOURCES_DIST = ddb/db_access.c 
ddb/db_access.h \
        xen/net.c xen/net.h xen/ring.c xen/ring.h xen/store.c \
        xen/store.h xen/time.c xen/time.h xen/xen.c xen/xen.h \
        i386/i386at/autoconf.c i386/i386at/autoconf.h \
-       i386/i386at/conf.c i386/i386at/cons_conf.c i386/i386at/idt.h \
+       i386/i386at/biosmem.c i386/i386at/biosmem.h i386/i386at/conf.c \
+       i386/i386at/cons_conf.c i386/i386at/elf.h i386/i386at/idt.h \
        i386/i386at/model_dep.c i386/i386at/model_dep.h \
        i386/include/mach/sa/stdarg.h i386/i386at/boothdr.S \
        i386/i386at/com.c i386/i386at/com.h i386/i386at/comreg.h \
@@ -1326,7 +1332,7 @@ am__libkernel_a_SOURCES_DIST = ddb/db_access.c 
ddb/db_access.h \
        i386/grub/misc.h i386/grub/types.h i386/grub/time.h \
        i386/grub/i18n.h i386/grub/compiler.h i386/grub/glue.h \
        i386/i386at/lpr.c i386/i386at/lpr.h i386/i386/ast.h \
-       i386/i386/ast_check.c i386/i386/ast_types.h \
+       i386/i386/ast_check.c i386/i386/ast_types.h i386/i386/cpu.h \
        i386/i386/cpu_number.h i386/i386/cswitch.S \
        i386/i386/db_disasm.c i386/i386/db_interface.c \
        i386/i386/db_interface.h i386/i386/db_machdep.h \
@@ -1382,6 +1388,7 @@ am__libkernel_a_SOURCES_DIST = ddb/db_access.c 
ddb/db_access.h \
 @PLATFORM_xen_TRUE@    xen/ring.$(OBJEXT) xen/store.$(OBJEXT) \
 @PLATFORM_xen_TRUE@    xen/time.$(OBJEXT) xen/xen.$(OBJEXT)
 @address@hidden = i386/i386at/autoconf.$(OBJEXT) \
address@hidden@ i386/i386at/biosmem.$(OBJEXT) \
 @HOST_ix86_TRUE@       i386/i386at/conf.$(OBJEXT) \
 @HOST_ix86_TRUE@       i386/i386at/cons_conf.$(OBJEXT) \
 @HOST_ix86_TRUE@       i386/i386at/model_dep.$(OBJEXT)
@@ -1477,17 +1484,17 @@ am_libkernel_a_OBJECTS = $(am__objects_2) 
ipc/ipc_entry.$(OBJEXT) \
        vm/vm_debug.$(OBJEXT) vm/vm_external.$(OBJEXT) \
        vm/vm_fault.$(OBJEXT) vm/vm_init.$(OBJEXT) \
        vm/vm_kern.$(OBJEXT) vm/vm_map.$(OBJEXT) \
-       vm/vm_object.$(OBJEXT) vm/vm_pageout.$(OBJEXT) \
-       vm/vm_resident.$(OBJEXT) vm/vm_user.$(OBJEXT) \
-       device/blkio.$(OBJEXT) device/chario.$(OBJEXT) \
-       device/cons.$(OBJEXT) device/dev_lookup.$(OBJEXT) \
-       device/dev_name.$(OBJEXT) device/dev_pager.$(OBJEXT) \
-       device/device_init.$(OBJEXT) device/ds_routines.$(OBJEXT) \
-       device/net_io.$(OBJEXT) device/subrs.$(OBJEXT) \
-       $(am__objects_3) $(am__objects_4) $(am__objects_5) \
-       $(am__objects_6) $(am__objects_7) $(am__objects_8) \
-       $(am__objects_9) $(am__objects_10) $(am__objects_11) \
-       $(am__objects_12)
+       vm/vm_object.$(OBJEXT) vm/vm_page.$(OBJEXT) \
+       vm/vm_pageout.$(OBJEXT) vm/vm_resident.$(OBJEXT) \
+       vm/vm_user.$(OBJEXT) device/blkio.$(OBJEXT) \
+       device/chario.$(OBJEXT) device/cons.$(OBJEXT) \
+       device/dev_lookup.$(OBJEXT) device/dev_name.$(OBJEXT) \
+       device/dev_pager.$(OBJEXT) device/device_init.$(OBJEXT) \
+       device/ds_routines.$(OBJEXT) device/net_io.$(OBJEXT) \
+       device/subrs.$(OBJEXT) $(am__objects_3) $(am__objects_4) \
+       $(am__objects_5) $(am__objects_6) $(am__objects_7) \
+       $(am__objects_8) $(am__objects_9) $(am__objects_10) \
+       $(am__objects_11) $(am__objects_12)
 @address@hidden = i386/i386/mach_i386.server.$(OBJEXT)
 nodist_libkernel_a_OBJECTS = version.$(OBJEXT) \
        vm/memory_object_user.user.$(OBJEXT) \
@@ -2452,7 +2459,7 @@ DIST_SUBDIRS =
 # Building a distribution.
 #
 EXTRA_DIST = gensym.awk ipc/mach_port.srv ipc/notify.defs \
-       kern/mach.srv kern/mach4.srv kern/gnumach.srv \
+       kern/exc.defs kern/mach.srv kern/mach4.srv kern/gnumach.srv \
        kern/mach_debug.srv kern/mach_host.srv kern/task_notify.cli \
        vm/memory_object_default.cli vm/memory_object_user.cli \
        device/device.srv device/device_pager.srv \
@@ -2578,7 +2585,7 @@ libkernel_a_SOURCES = $(am__append_2) ipc/ipc_entry.c 
ipc/ipc_entry.h \
        kern/ipc_kobject.h kern/ipc_mig.c kern/ipc_mig.h \
        kern/ipc_sched.c kern/ipc_sched.h kern/ipc_tt.c kern/ipc_tt.h \
        kern/kalloc.h kern/kern_types.h kern/list.h kern/lock.c \
-       kern/lock.h kern/lock_mon.c kern/mach_clock.c \
+       kern/lock.h kern/lock_mon.c kern/log2.h kern/mach_clock.c \
        kern/mach_clock.h kern/mach_factor.c kern/mach_factor.h \
        kern/machine.c kern/machine.h kern/macros.h kern/pc_sample.c \
        kern/pc_sample.h kern/printf.c kern/printf.h kern/priority.c \
@@ -2600,11 +2607,11 @@ libkernel_a_SOURCES = $(am__append_2) ipc/ipc_entry.c 
ipc/ipc_entry.h \
        vm/vm_external.c vm/vm_external.h vm/vm_fault.c vm/vm_fault.h \
        vm/vm_init.c vm/vm_init.h vm/vm_kern.c vm/vm_kern.h \
        vm/vm_map.c vm/vm_map.h vm/vm_object.c vm/vm_object.h \
-       vm/vm_page.h vm/vm_pageout.c vm/vm_pageout.h vm/vm_print.h \
-       vm/vm_resident.c vm/vm_resident.h vm/vm_types.h vm/vm_user.c \
-       vm/vm_user.h device/blkio.c device/blkio.h device/buf.h \
-       device/chario.c device/chario.h device/cirbuf.h device/conf.h \
-       device/cons.c device/cons.h device/device_emul.h \
+       vm/vm_page.c vm/vm_page.h vm/vm_pageout.c vm/vm_pageout.h \
+       vm/vm_print.h vm/vm_resident.c vm/vm_resident.h vm/vm_types.h \
+       vm/vm_user.c vm/vm_user.h device/blkio.c device/blkio.h \
+       device/buf.h device/chario.c device/chario.h device/cirbuf.h \
+       device/conf.h device/cons.c device/cons.h device/device_emul.h \
        device/dev_hdr.h device/dev_lookup.c device/dev_master.h \
        device/dev_name.c device/dev_pager.c device/dev_pager.h \
        device/device_init.c device/device_init.h device/device_port.h \
@@ -2642,7 +2649,7 @@ nodist_libkernel_a_SOURCES = version.c 
vm/memory_object_user.user.h \
        kern/mach_debug.server.c kern/mach_debug.server.msgids \
        kern/mach_host.server.h kern/mach_host.server.c \
        kern/mach_host.server.msgids ipc/notify.none.msgids \
-       $(am__append_122)
+       kern/exc.none.msgids $(am__append_122)
 gnumach_o_LDADD = libkernel.a $(am__append_6) $(am__append_100)
 gnumach_SOURCES = 
 gnumach_LINKFLAGS = $(am__append_123) $(am__append_126)
@@ -2681,7 +2688,7 @@ nodist_lib_dep_tr_for_defs_a_SOURCES =  \
        kern/mach.server.defs.c kern/mach4.server.defs.c \
        kern/gnumach.server.defs.c kern/mach_debug.server.defs.c \
        kern/mach_host.server.defs.c ipc/notify.none.defs.c \
-       $(am__append_121)
+       kern/exc.none.defs.c $(am__append_121)
 # Preprocess only.
 lib_dep_tr_for_defs_a_CPPFLAGS = $(AM_CPPFLAGS) \
        -E
@@ -3065,6 +3072,8 @@ 
kern/lib_dep_tr_for_defs_a-mach_host.server.defs.$(OBJEXT):  \
        kern/$(am__dirstamp) kern/$(DEPDIR)/$(am__dirstamp)
 ipc/lib_dep_tr_for_defs_a-notify.none.defs.$(OBJEXT):  \
        ipc/$(am__dirstamp) ipc/$(DEPDIR)/$(am__dirstamp)
+kern/lib_dep_tr_for_defs_a-exc.none.defs.$(OBJEXT):  \
+       kern/$(am__dirstamp) kern/$(DEPDIR)/$(am__dirstamp)
 i386/i386/$(am__dirstamp):
        @$(MKDIR_P) i386/i386
        @: > i386/i386/$(am__dirstamp)
@@ -3270,6 +3279,7 @@ vm/vm_kern.$(OBJEXT): vm/$(am__dirstamp) 
vm/$(DEPDIR)/$(am__dirstamp)
 vm/vm_map.$(OBJEXT): vm/$(am__dirstamp) vm/$(DEPDIR)/$(am__dirstamp)
 vm/vm_object.$(OBJEXT): vm/$(am__dirstamp) \
        vm/$(DEPDIR)/$(am__dirstamp)
+vm/vm_page.$(OBJEXT): vm/$(am__dirstamp) vm/$(DEPDIR)/$(am__dirstamp)
 vm/vm_pageout.$(OBJEXT): vm/$(am__dirstamp) \
        vm/$(DEPDIR)/$(am__dirstamp)
 vm/vm_resident.$(OBJEXT): vm/$(am__dirstamp) \
@@ -3321,6 +3331,8 @@ i386/i386at/$(DEPDIR)/$(am__dirstamp):
        @: > i386/i386at/$(DEPDIR)/$(am__dirstamp)
 i386/i386at/autoconf.$(OBJEXT): i386/i386at/$(am__dirstamp) \
        i386/i386at/$(DEPDIR)/$(am__dirstamp)
+i386/i386at/biosmem.$(OBJEXT): i386/i386at/$(am__dirstamp) \
+       i386/i386at/$(DEPDIR)/$(am__dirstamp)
 i386/i386at/conf.$(OBJEXT): i386/i386at/$(am__dirstamp) \
        i386/i386at/$(DEPDIR)/$(am__dirstamp)
 i386/i386at/cons_conf.$(OBJEXT): i386/i386at/$(am__dirstamp) \
@@ -4232,6 +4244,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @address@hidden/i386at/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/i386at/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/i386at/$(DEPDIR)/address@hidden@
address@hidden@@am__include@ @address@hidden/i386at/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/i386at/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/i386at/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/i386at/$(DEPDIR)/address@hidden@
@@ -4292,6 +4305,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
@@ -4490,6 +4504,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
@@ -4745,6 +4760,20 @@ ipc/lib_dep_tr_for_defs_a-notify.none.defs.obj: 
ipc/notify.none.defs.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) 
$(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(address@hidden@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) 
$(INCLUDES) $(lib_dep_tr_for_defs_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) 
$(CFLAGS) -c -o ipc/lib_dep_tr_for_defs_a-notify.none.defs.obj `if test -f 
'ipc/notify.none.defs.c'; then $(CYGPATH_W) 'ipc/notify.none.defs.c'; else 
$(CYGPATH_W) '$(srcdir)/ipc/notify.none.defs.c'; fi`
 
+kern/lib_dep_tr_for_defs_a-exc.none.defs.o: kern/exc.none.defs.c
address@hidden@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) 
$(lib_dep_tr_for_defs_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT 
kern/lib_dep_tr_for_defs_a-exc.none.defs.o -MD -MP -MF 
kern/$(DEPDIR)/lib_dep_tr_for_defs_a-exc.none.defs.Tpo -c -o 
kern/lib_dep_tr_for_defs_a-exc.none.defs.o `test -f 'kern/exc.none.defs.c' || 
echo '$(srcdir)/'`kern/exc.none.defs.c
address@hidden@ $(AM_V_at)$(am__mv) 
kern/$(DEPDIR)/lib_dep_tr_for_defs_a-exc.none.defs.Tpo 
kern/$(DEPDIR)/lib_dep_tr_for_defs_a-exc.none.defs.Po
address@hidden@@am__fastdepCC_FALSE@    $(AM_V_CC)source='kern/exc.none.defs.c' 
object='kern/lib_dep_tr_for_defs_a-exc.none.defs.o' libtool=no @AMDEPBACKSLASH@
address@hidden@@am__fastdepCC_FALSE@    DEPDIR=$(DEPDIR) $(CCDEPMODE) 
$(depcomp) @AMDEPBACKSLASH@
address@hidden@ $(address@hidden@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) 
$(lib_dep_tr_for_defs_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o 
kern/lib_dep_tr_for_defs_a-exc.none.defs.o `test -f 'kern/exc.none.defs.c' || 
echo '$(srcdir)/'`kern/exc.none.defs.c
+
+kern/lib_dep_tr_for_defs_a-exc.none.defs.obj: kern/exc.none.defs.c
address@hidden@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) 
$(lib_dep_tr_for_defs_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT 
kern/lib_dep_tr_for_defs_a-exc.none.defs.obj -MD -MP -MF 
kern/$(DEPDIR)/lib_dep_tr_for_defs_a-exc.none.defs.Tpo -c -o 
kern/lib_dep_tr_for_defs_a-exc.none.defs.obj `if test -f 
'kern/exc.none.defs.c'; then $(CYGPATH_W) 'kern/exc.none.defs.c'; else 
$(CYGPATH_W) '$(srcdir)/kern/exc.none.defs.c'; fi`
address@hidden@ $(AM_V_at)$(am__mv) 
kern/$(DEPDIR)/lib_dep_tr_for_defs_a-exc.none.defs.Tpo 
kern/$(DEPDIR)/lib_dep_tr_for_defs_a-exc.none.defs.Po
address@hidden@@am__fastdepCC_FALSE@    $(AM_V_CC)source='kern/exc.none.defs.c' 
object='kern/lib_dep_tr_for_defs_a-exc.none.defs.obj' libtool=no 
@AMDEPBACKSLASH@
address@hidden@@am__fastdepCC_FALSE@    DEPDIR=$(DEPDIR) $(CCDEPMODE) 
$(depcomp) @AMDEPBACKSLASH@
address@hidden@ $(address@hidden@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) 
$(lib_dep_tr_for_defs_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o 
kern/lib_dep_tr_for_defs_a-exc.none.defs.obj `if test -f 
'kern/exc.none.defs.c'; then $(CYGPATH_W) 'kern/exc.none.defs.c'; else 
$(CYGPATH_W) '$(srcdir)/kern/exc.none.defs.c'; fi`
+
 i386/i386/lib_dep_tr_for_defs_a-mach_i386.server.defs.o: 
i386/i386/mach_i386.server.defs.c
 @am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) 
$(lib_dep_tr_for_defs_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT 
i386/i386/lib_dep_tr_for_defs_a-mach_i386.server.defs.o -MD -MP -MF 
i386/i386/$(DEPDIR)/lib_dep_tr_for_defs_a-mach_i386.server.defs.Tpo -c -o 
i386/i386/lib_dep_tr_for_defs_a-mach_i386.server.defs.o `test -f 
'i386/i386/mach_i386.server.defs.c' || echo 
'$(srcdir)/'`i386/i386/mach_i386.server.defs.c
 @am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) 
i386/i386/$(DEPDIR)/lib_dep_tr_for_defs_a-mach_i386.server.defs.Tpo 
i386/i386/$(DEPDIR)/lib_dep_tr_for_defs_a-mach_i386.server.defs.Po
diff --git a/Makefrag.am b/Makefrag.am
index 823ece5..6ffc8cc 100644
--- a/Makefrag.am
+++ b/Makefrag.am
@@ -162,6 +162,7 @@ libkernel_a_SOURCES += \
        kern/lock.c \
        kern/lock.h \
        kern/lock_mon.c \
+       kern/log2.h \
        kern/mach_clock.c \
        kern/mach_clock.h \
        kern/mach_factor.c \
@@ -217,6 +218,7 @@ libkernel_a_SOURCES += \
        kern/elf-load.c \
        kern/boot_script.c
 EXTRA_DIST += \
+       kern/exc.defs \
        kern/mach.srv \
        kern/mach4.srv \
        kern/gnumach.srv \
@@ -259,6 +261,7 @@ libkernel_a_SOURCES += \
        vm/vm_map.h \
        vm/vm_object.c \
        vm/vm_object.h \
+       vm/vm_page.c \
        vm/vm_page.h \
        vm/vm_pageout.c \
        vm/vm_pageout.h \
@@ -548,9 +551,11 @@ nodist_libkernel_a_SOURCES += \
 # Stand-alone rule to generate the list of message ids when neither
 # the client nor the server stubs are required.
 nodist_lib_dep_tr_for_defs_a_SOURCES += \
-       ipc/notify.none.defs.c
+       ipc/notify.none.defs.c \
+       kern/exc.none.defs.c
 nodist_libkernel_a_SOURCES += \
-       ipc/notify.none.msgids
+       ipc/notify.none.msgids \
+       kern/exc.none.msgids
 #      ipc/notify.none.defs
 
 # rpctrace can make use of that.
diff --git a/build-aux/compile b/build-aux/compile
index 531136b..a85b723 100755
--- a/build-aux/compile
+++ b/build-aux/compile
@@ -3,7 +3,7 @@
 
 scriptversion=2012-10-14.11; # UTC
 
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
 # Written by Tom Tromey <address@hidden>.
 #
 # This program is free software; you can redistribute it and/or modify
diff --git a/build-aux/config.guess b/build-aux/config.guess
index 1f5c50c..1659250 100755
--- a/build-aux/config.guess
+++ b/build-aux/config.guess
@@ -1,8 +1,8 @@
 #! /bin/sh
 # Attempt to guess a canonical system name.
-#   Copyright 1992-2014 Free Software Foundation, Inc.
+#   Copyright 1992-2015 Free Software Foundation, Inc.
 
-timestamp='2014-03-23'
+timestamp='2015-08-20'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -24,12 +24,12 @@ timestamp='2014-03-23'
 # program.  This Exception is an additional permission under section 7
 # of the GNU General Public License, version 3 ("GPLv3").
 #
-# Originally written by Per Bothner.
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
 #
 # You can get the latest version of this script from:
 # 
http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
 #
-# Please send patches with a ChangeLog entry to address@hidden
+# Please send patches to <address@hidden>.
 
 
 me=`echo "$0" | sed -e 's,.*/,,'`
@@ -50,7 +50,7 @@ version="\
 GNU config.guess ($timestamp)
 
 Originally written by Per Bothner.
-Copyright 1992-2014 Free Software Foundation, Inc.
+Copyright 1992-2015 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -168,20 +168,27 @@ case 
"${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
        # Note: NetBSD doesn't particularly care about the vendor
        # portion of the name.  We always set it to "unknown".
        sysctl="sysctl -n hw.machine_arch"
-       UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
-           /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+       UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+           /sbin/$sysctl 2>/dev/null || \
+           /usr/sbin/$sysctl 2>/dev/null || \
+           echo unknown)`
        case "${UNAME_MACHINE_ARCH}" in
            armeb) machine=armeb-unknown ;;
            arm*) machine=arm-unknown ;;
            sh3el) machine=shl-unknown ;;
            sh3eb) machine=sh-unknown ;;
            sh5el) machine=sh5le-unknown ;;
+           earmv*)
+               arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 
's,^e\(armv[0-9]\).*$,\1,'`
+               endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 
's,^.*\(eb\)$,\1,p'`
+               machine=${arch}${endian}-unknown
+               ;;
            *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
        esac
        # The Operating System including object format, if it has switched
        # to ELF recently, or will in the future.
        case "${UNAME_MACHINE_ARCH}" in
-           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+           arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax)
                eval $set_cc_for_build
                if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
                        | grep -q __ELF__
@@ -197,6 +204,13 @@ case 
"${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
                os=netbsd
                ;;
        esac
+       # Determine ABI tags.
+       case "${UNAME_MACHINE_ARCH}" in
+           earm*)
+               expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+               abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"`
+               ;;
+       esac
        # The OS release
        # Debian GNU/NetBSD machines have a different userland, and
        # thus, need a distinct triplet. However, they do not need
@@ -207,13 +221,13 @@ case 
"${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
                release='-gnu'
                ;;
            *)
-               release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+               release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. 
-f1,2`
                ;;
        esac
        # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
        # contains redundant information, the shorter form:
        # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
-       echo "${machine}-${os}${release}"
+       echo "${machine}-${os}${release}${abi}"
        exit ;;
     *:Bitrig:*:*)
        UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
@@ -235,6 +249,9 @@ case 
"${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
     *:MirBSD:*:*)
        echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
        exit ;;
+    *:Sortix:*:*)
+       echo ${UNAME_MACHINE}-unknown-sortix
+       exit ;;
     alpha:OSF1:*:*)
        case $UNAME_RELEASE in
        *4.0)
@@ -579,8 +596,9 @@ EOF
        else
                IBM_ARCH=powerpc
        fi
-       if [ -x /usr/bin/oslevel ] ; then
-               IBM_REV=`/usr/bin/oslevel`
+       if [ -x /usr/bin/lslpp ] ; then
+               IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+                          awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
        else
                IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
        fi
@@ -932,6 +950,9 @@ EOF
     crisv32:Linux:*:*)
        echo ${UNAME_MACHINE}-axis-linux-${LIBC}
        exit ;;
+    e2k:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       exit ;;
     frv:Linux:*:*)
        echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
        exit ;;
@@ -1020,7 +1041,7 @@ EOF
        echo ${UNAME_MACHINE}-dec-linux-${LIBC}
        exit ;;
     x86_64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo ${UNAME_MACHINE}-pc-linux-${LIBC}
        exit ;;
     xtensa*:Linux:*:*)
        echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
diff --git a/build-aux/config.sub b/build-aux/config.sub
index bba4efb..1acc966 100755
--- a/build-aux/config.sub
+++ b/build-aux/config.sub
@@ -1,8 +1,8 @@
 #! /bin/sh
 # Configuration validation subroutine script.
-#   Copyright 1992-2014 Free Software Foundation, Inc.
+#   Copyright 1992-2015 Free Software Foundation, Inc.
 
-timestamp='2014-09-11'
+timestamp='2015-08-20'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@ timestamp='2014-09-11'
 # of the GNU General Public License, version 3 ("GPLv3").
 
 
-# Please send patches with a ChangeLog entry to address@hidden
+# Please send patches to <address@hidden>.
 #
 # Configuration subroutine to validate and canonicalize a configuration type.
 # Supply the specified configuration type as an argument.
@@ -68,7 +68,7 @@ Report bugs and patches to <address@hidden>."
 version="\
 GNU config.sub ($timestamp)
 
-Copyright 1992-2014 Free Software Foundation, Inc.
+Copyright 1992-2015 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -117,7 +117,7 @@ maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
 case $maybe_os in
   nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
   linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | 
kfreebsd*-gnu* | \
-  knetbsd*-gnu* | netbsd*-gnu* | \
+  knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
   kopensolaris*-gnu* | \
   storm-chaos* | os2-emx* | rtmk-nova*)
     os=-$maybe_os
@@ -255,12 +255,13 @@ case $basic_machine in
        | arc | arceb \
        | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
        | avr | avr32 \
+       | ba \
        | be32 | be64 \
        | bfin \
        | c4x | c8051 | clipper \
        | d10v | d30v | dlx | dsp16xx \
-       | epiphany \
-       | fido | fr30 | frv \
+       | e2k | epiphany \
+       | fido | fr30 | frv | ft32 \
        | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
        | hexagon \
        | i370 | i860 | i960 | ia64 \
@@ -305,7 +306,7 @@ case $basic_machine in
        | riscv32 | riscv64 \
        | rl78 | rx \
        | score \
-       | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | 
shbe | shle | sh[1234]le | sh3ele \
+       | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | 
shbe | shle | sh[1234]le | sh3ele \
        | sh64 | sh64le \
        | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | 
sparclite \
        | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
@@ -313,6 +314,7 @@ case $basic_machine in
        | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
        | ubicom32 \
        | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+       | visium \
        | we32k \
        | x86 | xc16x | xstormy16 | xtensa \
        | z8k | z80)
@@ -327,6 +329,9 @@ case $basic_machine in
        c6x)
                basic_machine=tic6x-unknown
                ;;
+       leon|leon[3-9])
+               basic_machine=sparc-$basic_machine
+               ;;
        m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
                basic_machine=$basic_machine-unknown
                os=-none
@@ -372,12 +377,13 @@ case $basic_machine in
        | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
        | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
        | avr-* | avr32-* \
+       | ba-* \
        | be32-* | be64-* \
        | bfin-* | bs2000-* \
        | c[123]* | c30-* | [cjt]90-* | c4x-* \
        | c8051-* | clipper-* | craynv-* | cydra-* \
        | d10v-* | d30v-* | dlx-* \
-       | elxsi-* \
+       | e2k-* | elxsi-* \
        | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
        | h8300-* | h8500-* \
        | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
@@ -424,12 +430,13 @@ case $basic_machine in
        | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
        | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
        | pyramid-* \
+       | riscv32-* | riscv64-* \
        | rl78-* | romp-* | rs6000-* | rx-* \
        | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* 
| sheb-* | shbe-* \
        | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
        | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | 
sparclet-* \
        | sparclite-* \
-       | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+       | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
        | tahoe-* \
        | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
        | tile*-* \
@@ -437,6 +444,7 @@ case $basic_machine in
        | ubicom32-* \
        | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
        | vax-* \
+       | visium-* \
        | we32k-* \
        | x86-* | x86_64-* | xc16x-* | xps100-* \
        | xstormy16-* | xtensa*-* \
@@ -513,6 +521,9 @@ case $basic_machine in
                basic_machine=i386-pc
                os=-aros
                ;;
+        asmjs)
+               basic_machine=asmjs-unknown
+               ;;
        aux)
                basic_machine=m68k-apple
                os=-aux
@@ -774,6 +785,9 @@ case $basic_machine in
                basic_machine=m68k-isi
                os=-sysv
                ;;
+       leon-*|leon[3-9]-*)
+               basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
+               ;;
        m68knommu)
                basic_machine=m68k-unknown
                os=-linux
@@ -1365,7 +1379,7 @@ case $os in
              | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | 
-solaris* \
              | -sym* | -kopensolaris* | -plan9* \
              | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
-             | -aos* | -aros* \
+             | -aos* | -aros* | -cloudabi* | -sortix* \
              | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
              | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
              | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
diff --git a/build-aux/depcomp b/build-aux/depcomp
index 4ebd5b3..fc98710 100755
--- a/build-aux/depcomp
+++ b/build-aux/depcomp
@@ -3,7 +3,7 @@
 
 scriptversion=2013-05-30.07; # UTC
 
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
diff --git a/build-aux/install-sh b/build-aux/install-sh
index 377bb86..59990a1 100755
--- a/build-aux/install-sh
+++ b/build-aux/install-sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 # install - install a program, script, or datafile
 
-scriptversion=2011-11-20.07; # UTC
+scriptversion=2014-09-12.12; # UTC
 
 # This originates from X11R5 (mit/util/scripts/install.sh), which was
 # later released in X11R6 (xc/config/util/install.sh) with the
@@ -41,19 +41,15 @@ scriptversion=2011-11-20.07; # UTC
 # This script is compatible with the BSD install script, but was written
 # from scratch.
 
+tab='  '
 nl='
 '
-IFS=" ""       $nl"
+IFS=" $tab$nl"
 
-# set DOITPROG to echo to test this script
+# Set DOITPROG to "echo" to test this script.
 
-# Don't use :- since 4.3BSD and earlier shells don't like it.
 doit=${DOITPROG-}
-if test -z "$doit"; then
-  doit_exec=exec
-else
-  doit_exec=$doit
-fi
+doit_exec=${doit:-exec}
 
 # Put in absolute file names if you don't have them in your path;
 # or use environment vars.
@@ -68,17 +64,6 @@ mvprog=${MVPROG-mv}
 rmprog=${RMPROG-rm}
 stripprog=${STRIPPROG-strip}
 
-posix_glob='?'
-initialize_posix_glob='
-  test "$posix_glob" != "?" || {
-    if (set -f) 2>/dev/null; then
-      posix_glob=
-    else
-      posix_glob=:
-    fi
-  }
-'
-
 posix_mkdir=
 
 # Desired mode of installed file.
@@ -97,7 +82,7 @@ dir_arg=
 dst_arg=
 
 copy_on_change=false
-no_target_directory=
+is_target_a_directory=possibly
 
 usage="\
 Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
@@ -137,46 +122,57 @@ while test $# -ne 0; do
     -d) dir_arg=true;;
 
     -g) chgrpcmd="$chgrpprog $2"
-       shift;;
+        shift;;
 
     --help) echo "$usage"; exit $?;;
 
     -m) mode=$2
-       case $mode in
-         *' '* | *'    '* | *'
-'*       | *'*'* | *'?'* | *'['*)
-           echo "$0: invalid mode: $mode" >&2
-           exit 1;;
-       esac
-       shift;;
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
 
     -o) chowncmd="$chownprog $2"
-       shift;;
+        shift;;
 
     -s) stripcmd=$stripprog;;
 
-    -t) dst_arg=$2
-       # Protect names problematic for 'test' and other utilities.
-       case $dst_arg in
-         -* | [=\(\)!]) dst_arg=./$dst_arg;;
-       esac
-       shift;;
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
 
-    -T) no_target_directory=true;;
+    -T) is_target_a_directory=never;;
 
     --version) echo "$0 $scriptversion"; exit $?;;
 
-    --)        shift
-       break;;
+    --) shift
+        break;;
 
-    -*)        echo "$0: invalid option: $1" >&2
-       exit 1;;
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
 
     *)  break;;
   esac
   shift
 done
 
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
 if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
   # When -d is used, all remaining arguments are directories to create.
   # When -t is used, the destination is already specified.
@@ -208,6 +204,15 @@ if test $# -eq 0; then
 fi
 
 if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
+if test -z "$dir_arg"; then
   do_exit='(exit $ret); exit $ret'
   trap "ret=129; $do_exit" 1
   trap "ret=130; $do_exit" 2
@@ -223,16 +228,16 @@ if test -z "$dir_arg"; then
 
     *[0-7])
       if test -z "$stripcmd"; then
-       u_plus_rw=
+        u_plus_rw=
       else
-       u_plus_rw='% 200'
+        u_plus_rw='% 200'
       fi
       cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
     *)
       if test -z "$stripcmd"; then
-       u_plus_rw=
+        u_plus_rw=
       else
-       u_plus_rw=,u+rw
+        u_plus_rw=,u+rw
       fi
       cp_umask=$mode$u_plus_rw;;
   esac
@@ -269,41 +274,15 @@ do
     # If destination is a directory, append the input filename; won't work
     # if double slashes aren't ignored.
     if test -d "$dst"; then
-      if test -n "$no_target_directory"; then
-       echo "$0: $dst_arg: Is a directory" >&2
-       exit 1
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
       fi
       dstdir=$dst
       dst=$dstdir/`basename "$src"`
       dstdir_status=0
     else
-      # Prefer dirname, but fall back on a substitute if dirname fails.
-      dstdir=`
-       (dirname "$dst") 2>/dev/null ||
-       expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-            X"$dst" : 'X\(//\)[^/]' \| \
-            X"$dst" : 'X\(//\)$' \| \
-            X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
-       echo X"$dst" |
-           sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
-                  s//\1/
-                  q
-                }
-                /^X\(\/\/\)[^/].*/{
-                  s//\1/
-                  q
-                }
-                /^X\(\/\/\)$/{
-                  s//\1/
-                  q
-                }
-                /^X\(\/\).*/{
-                  s//\1/
-                  q
-                }
-                s/.*/./; q'
-      `
-
+      dstdir=`dirname "$dst"`
       test -d "$dstdir"
       dstdir_status=$?
     fi
@@ -314,74 +293,81 @@ do
   if test $dstdir_status != 0; then
     case $posix_mkdir in
       '')
-       # Create intermediate dirs using mode 755 as modified by the umask.
-       # This is like FreeBSD 'install' as of 1997-10-28.
-       umask=`umask`
-       case $stripcmd.$umask in
-         # Optimize common cases.
-         *[2367][2367]) mkdir_umask=$umask;;
-         .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
-         *[0-7])
-           mkdir_umask=`expr $umask + 22 \
-             - $umask % 100 % 40 + $umask % 20 \
-             - $umask % 10 % 4 + $umask % 2
-           `;;
-         *) mkdir_umask=$umask,go-w;;
-       esac
-
-       # With -d, create the new directory with the user-specified mode.
-       # Otherwise, rely on $mkdir_umask.
-       if test -n "$dir_arg"; then
-         mkdir_mode=-m$mode
-       else
-         mkdir_mode=
-       fi
-
-       posix_mkdir=false
-       case $umask in
-         *[123567][0-7][0-7])
-           # POSIX mkdir -p sets u+wx bits regardless of umask, which
-           # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
-           ;;
-         *)
-           tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
-           trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
-           if (umask $mkdir_umask &&
-               exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
-           then
-             if test -z "$dir_arg" || {
-                  # Check for POSIX incompatibilities with -m.
-                  # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
-                  # other-writable bit of parent directory when it shouldn't.
-                  # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
-                  ls_ld_tmpdir=`ls -ld "$tmpdir"`
-                  case $ls_ld_tmpdir in
-                    d????-?r-*) different_mode=700;;
-                    d????-?--*) different_mode=755;;
-                    *) false;;
-                  esac &&
-                  $mkdirprog -m$different_mode -p -- "$tmpdir" && {
-                    ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
-                    test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
-                  }
-                }
-             then posix_mkdir=:
-             fi
-             rmdir "$tmpdir/d" "$tmpdir"
-           else
-             # Remove any dirs left behind by ancient mkdir implementations.
-             rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
-           fi
-           trap '' 0;;
-       esac;;
+        # Create intermediate dirs using mode 755 as modified by the umask.
+        # This is like FreeBSD 'install' as of 1997-10-28.
+        umask=`umask`
+        case $stripcmd.$umask in
+          # Optimize common cases.
+          *[2367][2367]) mkdir_umask=$umask;;
+          .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+          *[0-7])
+            mkdir_umask=`expr $umask + 22 \
+              - $umask % 100 % 40 + $umask % 20 \
+              - $umask % 10 % 4 + $umask % 2
+            `;;
+          *) mkdir_umask=$umask,go-w;;
+        esac
+
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+        case $umask in
+          *[123567][0-7][0-7])
+            # POSIX mkdir -p sets u+wx bits regardless of umask, which
+            # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+            ;;
+          *)
+            # $RANDOM is not portable (e.g. dash);  use it when possible to
+            # lower collision chance
+            tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+            trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 
2>/dev/null; exit $ret' 0
+
+            # As "mkdir -p" follows symlinks and we work in /tmp possibly;  so
+            # create the $tmpdir first (and fail if unsuccessful) to make sure
+            # that nobody tries to guess the $tmpdir name.
+            if (umask $mkdir_umask &&
+                $mkdirprog $mkdir_mode "$tmpdir" &&
+                exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 
2>&1
+            then
+              if test -z "$dir_arg" || {
+                   # Check for POSIX incompatibilities with -m.
+                   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+                   # other-writable bit of parent directory when it shouldn't.
+                   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+                   test_tmpdir="$tmpdir/a"
+                   ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+                   case $ls_ld_tmpdir in
+                     d????-?r-*) different_mode=700;;
+                     d????-?--*) different_mode=755;;
+                     *) false;;
+                   esac &&
+                   $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+                     ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+                     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+                   }
+                 }
+              then posix_mkdir=:
+              fi
+              rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+            else
+              # Remove any dirs left behind by ancient mkdir implementations.
+              rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+            fi
+            trap '' 0;;
+        esac;;
     esac
 
     if
       $posix_mkdir && (
-       umask $mkdir_umask &&
-       $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
       )
     then :
     else
@@ -391,53 +377,51 @@ do
       # directory the slow way, step by step, checking for races as we go.
 
       case $dstdir in
-       /*) prefix='/';;
-       [-=\(\)!]*) prefix='./';;
-       *)  prefix='';;
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
       esac
 
-      eval "$initialize_posix_glob"
-
       oIFS=$IFS
       IFS=/
-      $posix_glob set -f
+      set -f
       set fnord $dstdir
       shift
-      $posix_glob set +f
+      set +f
       IFS=$oIFS
 
       prefixes=
 
       for d
       do
-       test X"$d" = X && continue
-
-       prefix=$prefix$d
-       if test -d "$prefix"; then
-         prefixes=
-       else
-         if $posix_mkdir; then
-           (umask=$mkdir_umask &&
-            $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
-           # Don't fail if two instances are running concurrently.
-           test -d "$prefix" || exit 1
-         else
-           case $prefix in
-             *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
-             *) qprefix=$prefix;;
-           esac
-           prefixes="$prefixes '$qprefix'"
-         fi
-       fi
-       prefix=$prefix/
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask=$mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
       done
 
       if test -n "$prefixes"; then
-       # Don't fail if two instances are running concurrently.
-       (umask $mkdir_umask &&
-        eval "\$doit_exec \$mkdirprog $prefixes") ||
-         test -d "$dstdir" || exit 1
-       obsolete_mkdir_used=true
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
       fi
     fi
   fi
@@ -472,15 +456,12 @@ do
 
     # If -C, don't bother to copy if it wouldn't change the file.
     if $copy_on_change &&
-       old=`LC_ALL=C ls -dlL "$dst"    2>/dev/null` &&
-       new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
-
-       eval "$initialize_posix_glob" &&
-       $posix_glob set -f &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
        set X $old && old=:$2:$4:$5:$6 &&
        set X $new && new=:$2:$4:$5:$6 &&
-       $posix_glob set +f &&
-
+       set +f &&
        test "$old" = "$new" &&
        $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
     then
@@ -493,24 +474,24 @@ do
       # to itself, or perhaps because mv is so ancient that it does not
       # support -f.
       {
-       # Now remove or move aside any old file at destination location.
-       # We try this two ways since rm can't unlink itself on some
-       # systems and the destination file might be busy for other
-       # reasons.  In this case, the final cleanup might fail but the new
-       # file should still install successfully.
-       {
-         test ! -f "$dst" ||
-         $doit $rmcmd -f "$dst" 2>/dev/null ||
-         { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
-           { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
-         } ||
-         { echo "$0: cannot unlink or rename $dst" >&2
-           (exit 1); exit 1
-         }
-       } &&
-
-       # Now rename the file to the real destination.
-       $doit $mvcmd "$dsttmp" "$dst"
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd -f "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
       }
     fi || exit 1
 
diff --git a/build-aux/mdate-sh b/build-aux/mdate-sh
index b3719cf..9e2c0c9 100755
--- a/build-aux/mdate-sh
+++ b/build-aux/mdate-sh
@@ -1,9 +1,9 @@
 #!/bin/sh
 # Get modification time of a file or directory and pretty-print it.
 
-scriptversion=2010-08-21.06; # UTC
+scriptversion=2015-04-09.19; # UTC
 
-# Copyright (C) 1995-2013 Free Software Foundation, Inc.
+# Copyright (C) 1995-2014 Free Software Foundation, Inc.
 # written by Ulrich Drepper <address@hidden>, June 1995
 #
 # This program is free software; you can redistribute it and/or modify
@@ -74,6 +74,10 @@ export LC_ALL
 LC_TIME=C
 export LC_TIME
 
+# Use UTC to get reproducible result
+TZ=UTC
+export TZ
+
 # GNU ls changes its time format in response to the TIME_STYLE
 # variable.  Since we cannot assume 'unset' works, revert this
 # variable to its documented default.
diff --git a/build-aux/missing b/build-aux/missing
index db98974..f62bbae 100755
--- a/build-aux/missing
+++ b/build-aux/missing
@@ -3,7 +3,7 @@
 
 scriptversion=2013-10-28.13; # UTC
 
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
 # Originally written by Fran,cois Pinard <address@hidden>, 1996.
 
 # This program is free software; you can redistribute it and/or modify
diff --git a/build-aux/test-driver b/build-aux/test-driver
index d306056..8e575b0 100755
--- a/build-aux/test-driver
+++ b/build-aux/test-driver
@@ -3,7 +3,7 @@
 
 scriptversion=2013-07-13.22; # UTC
 
-# Copyright (C) 2011-2013 Free Software Foundation, Inc.
+# Copyright (C) 2011-2014 Free Software Foundation, Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -106,11 +106,14 @@ trap "st=143; $do_exit" 15
 # Test script is run here.
 "$@" >$log_file 2>&1
 estatus=$?
+
 if test $enable_hard_errors = no && test $estatus -eq 99; then
-  estatus=1
+  tweaked_estatus=1
+else
+  tweaked_estatus=$estatus
 fi
 
-case $estatus:$expect_failure in
+case $tweaked_estatus:$expect_failure in
   0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
   0:*)   col=$grn res=PASS  recheck=no  gcopy=no;;
   77:*)  col=$blu res=SKIP  recheck=no  gcopy=yes;;
@@ -119,6 +122,12 @@ case $estatus:$expect_failure in
   *:*)   col=$red res=FAIL  recheck=yes gcopy=yes;;
 esac
 
+# Report the test outcome and exit status in the logs, so that one can
+# know whether the test passed or failed simply by looking at the '.log'
+# file, without the need of also peaking into the corresponding '.trs'
+# file (automake bug#11814).
+echo "$res $test_name (exit status: $estatus)" >>$log_file
+
 # Report outcome to console.
 echo "${col}${res}${std}: $test_name"
 
diff --git a/configure b/configure
index 5f29c10..f4e3f76 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for GNU Mach 1.6+git20160114.
+# Generated by GNU Autoconf 2.69 for GNU Mach 1.6+git20160311.
 #
 # Report bugs to <address@hidden>.
 #
@@ -579,8 +579,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='GNU Mach'
 PACKAGE_TARNAME='gnumach'
-PACKAGE_VERSION='1.6+git20160114'
-PACKAGE_STRING='GNU Mach 1.6+git20160114'
+PACKAGE_VERSION='1.6+git20160311'
+PACKAGE_STRING='GNU Mach 1.6+git20160311'
 PACKAGE_BUGREPORT='address@hidden'
 PACKAGE_URL=''
 
@@ -1595,7 +1595,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures GNU Mach 1.6+git20160114 to adapt to many kinds of 
systems.
+\`configure' configures GNU Mach 1.6+git20160311 to adapt to many kinds of 
systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1666,7 +1666,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of GNU Mach 1.6+git20160114:";;
+     short | recursive ) echo "Configuration of GNU Mach 1.6+git20160311:";;
    esac
   cat <<\_ACEOF
 
@@ -2014,7 +2014,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-GNU Mach configure 1.6+git20160114
+GNU Mach configure 1.6+git20160311
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2106,7 +2106,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by GNU Mach $as_me 1.6+git20160114, which was
+It was created by GNU Mach $as_me 1.6+git20160311, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2972,7 +2972,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='gnumach'
- VERSION='1.6+git20160114'
+ VERSION='1.6+git20160311'
 
 
 # Some tools Automake needs.
@@ -12128,7 +12128,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by GNU Mach $as_me 1.6+git20160114, which was
+This file was extended by GNU Mach $as_me 1.6+git20160311, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -12199,7 +12199,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; 
s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-GNU Mach config.status 1.6+git20160114
+GNU Mach config.status 1.6+git20160311
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git a/device/dev_lookup.c b/device/dev_lookup.c
index a80830c..9af7508 100644
--- a/device/dev_lookup.c
+++ b/device/dev_lookup.c
@@ -366,5 +366,5 @@ dev_lookup_init(void)
            queue_init(&dev_number_hash_table[i]);
 
        kmem_cache_init(&dev_hdr_cache, "mach_device",
-                       sizeof(struct mach_device), 0, NULL, NULL, NULL, 0);
+                       sizeof(struct mach_device), 0, NULL, 0);
 }
diff --git a/device/dev_pager.c b/device/dev_pager.c
index 815473a..4033170 100644
--- a/device/dev_pager.c
+++ b/device/dev_pager.c
@@ -173,7 +173,7 @@ void dev_pager_hash_init(void)
 
        size = sizeof(struct dev_pager_entry);
        kmem_cache_init(&dev_pager_hash_cache, "dev_pager_entry", size, 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
        for (i = 0; i < DEV_PAGER_HASH_COUNT; i++)
            queue_init(&dev_pager_hashtable[i]);
        simple_lock_init(&dev_pager_hash_lock);
@@ -705,7 +705,7 @@ void device_pager_init(void)
         */
        size = sizeof(struct dev_pager);
        kmem_cache_init(&dev_pager_cache, "dev_pager", size, 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
 
        /*
         *      Initialize the name port hashing stuff.
diff --git a/device/ds_routines.c b/device/ds_routines.c
index 43ed5b5..dbff7f8 100644
--- a/device/ds_routines.c
+++ b/device/ds_routines.c
@@ -1554,7 +1554,7 @@ void mach_device_init(void)
        device_io_map->wait_for_space = TRUE;
 
        kmem_cache_init(&io_inband_cache, "io_buf_ptr_inband",
-                       sizeof(io_buf_ptr_inband_t), 0, NULL, NULL, NULL, 0);
+                       sizeof(io_buf_ptr_inband_t), 0, NULL, 0);
 
        mach_device_trap_init();
 }
@@ -1598,7 +1598,7 @@ static void
 mach_device_trap_init(void)
 {
        kmem_cache_init(&io_trap_cache, "io_req", IOTRAP_REQSIZE, 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
 }
 
 /*
diff --git a/device/net_io.c b/device/net_io.c
index 47ef2ea..99af0b2 100644
--- a/device/net_io.c
+++ b/device/net_io.c
@@ -1495,11 +1495,11 @@ net_io_init(void)
 
        size = sizeof(struct net_rcv_port);
        kmem_cache_init(&net_rcv_cache, "net_rcv_port", size, 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
 
        size = sizeof(struct net_hash_entry);
        kmem_cache_init(&net_hash_entry_cache, "net_hash_entry", size, 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
 
        size = ikm_plus_overhead(sizeof(struct net_rcv_msg));
        net_kmsg_size = round_page(size);
diff --git a/doc/mach.info b/doc/mach.info
index 995b99a..463a76b 100644
--- a/doc/mach.info
+++ b/doc/mach.info
@@ -1,9 +1,9 @@
-This is mach.info, produced by makeinfo version 6.0 from mach.texi.
+This is mach.info, produced by makeinfo version 6.1 from mach.texi.
 
 This file documents the GNU Mach microkernel.
 
-   This is edition 0.4, last updated on 12 September 2015, of 'The GNU
-Mach Reference Manual', for version 1.6+git20160114.
+   This is edition 0.4, last updated on 10 March 2016, of 'The GNU Mach
+Reference Manual', for version 1.6+git20160311.
 
    Copyright (C) 2001, 2002, 2006, 2007, 2008 Free Software Foundation,
 Inc.
@@ -39,126 +39,126 @@ END-INFO-DIR-ENTRY
 
 
 Indirect:
-mach.info-1: 1643
-mach.info-2: 302570
+mach.info-1: 1639
+mach.info-2: 302600
 
 Tag Table:
 (Indirect)
-Node: Top1643
-Node: Introduction11284
-Node: Audience12115
-Node: Features13150
-Node: Overview14977
-Node: History16170
-Node: Installing16315
-Node: Binary Distributions17540
-Node: Compilation18348
-Node: Configuration19581
-Node: Cross-Compilation35992
-Node: Bootstrap36773
-Ref: Bootstrap-Footnote-137216
-Node: Bootloader37453
-Ref: Bootloader-Footnote-138733
-Node: Modules38819
-Node: Inter Process Communication39646
-Node: Major Concepts40269
-Node: Messaging Interface44074
-Node: Mach Message Call44804
-Node: Message Format48119
-Node: Exchanging Port Rights59311
-Ref: Exchanging Port Rights-Footnote-164873
-Node: Memory65045
-Ref: Memory-Footnote-168139
-Node: Message Send68481
-Ref: Message Send-Footnote-175503
-Node: Message Receive75786
-Ref: Message Receive-Footnote-185438
-Node: Atomicity85719
-Node: Port Manipulation Interface88493
-Node: Port Creation90048
-Node: Port Destruction94837
-Node: Port Names97980
-Node: Port Rights102227
-Node: Ports and other Tasks106016
-Node: Receive Rights110109
-Node: Port Sets117040
-Node: Request Notifications119443
-Node: Inherited Ports124247
-Node: Virtual Memory Interface127931
-Node: Memory Allocation129184
-Node: Memory Deallocation131709
-Node: Data Transfer133173
-Node: Memory Attributes136699
-Node: Mapping Memory Objects146138
-Node: Memory Statistics149447
-Node: External Memory Management151021
-Node: Memory Object Server151726
-Node: Memory Object Creation154435
-Node: Memory Object Termination158441
-Node: Memory Objects and Data161380
-Node: Memory Object Locking178526
-Node: Memory Object Attributes184421
-Node: Default Memory Manager190258
-Node: Threads and Tasks195980
-Node: Thread Interface196317
-Node: Thread Creation197313
-Node: Thread Termination198430
-Node: Thread Information198901
-Node: Thread Settings205000
-Node: Thread Execution206234
-Node: Scheduling213527
-Node: Thread Priority213882
-Node: Hand-Off Scheduling216516
-Node: Scheduling Policy221641
-Node: Thread Special Ports222973
-Node: Exceptions225419
-Node: Task Interface226289
-Node: Task Creation227301
-Node: Task Termination228636
-Node: Task Information229238
-Node: Task Execution236140
-Node: Task Special Ports240553
-Node: Syscall Emulation244407
-Node: Profiling245638
-Node: Host Interface249401
-Node: Host Ports250386
-Node: Host Information252459
-Node: Host Time257842
-Node: Host Reboot260509
-Node: Processors and Processor Sets261061
-Node: Processor Set Interface262039
-Node: Processor Set Ports262806
-Node: Processor Set Access263636
-Node: Processor Set Creation265896
-Node: Processor Set Destruction266923
-Node: Tasks and Threads on Sets267844
-Node: Processor Set Priority273011
-Node: Processor Set Policy274301
-Node: Processor Set Info275915
-Node: Processor Interface279728
-Node: Hosted Processors280453
-Node: Processor Control281444
-Node: Processors and Sets282910
-Node: Processor Info284788
-Node: Device Interface287530
-Node: Device Reply Server289145
-Node: Device Open290437
-Node: Device Close292560
-Node: Device Read293139
-Node: Device Write296058
-Node: Device Map298863
-Node: Device Status299754
-Node: Device Filter302570
-Node: Kernel Debugger308317
-Node: Operation309044
-Node: Commands312021
-Node: Variables325806
-Node: Expressions327194
-Node: Copying328543
-Node: Documentation License347772
-Node: GNU Free Documentation License348361
-Node: CMU License370760
-Node: Concept Index371995
-Node: Function and Data Index375841
+Node: Top1639
+Node: Introduction11276
+Node: Audience12107
+Node: Features13142
+Node: Overview14969
+Node: History16162
+Node: Installing16307
+Node: Binary Distributions17532
+Node: Compilation18340
+Node: Configuration19573
+Node: Cross-Compilation35984
+Node: Bootstrap36765
+Ref: Bootstrap-Footnote-137208
+Node: Bootloader37445
+Ref: Bootloader-Footnote-138725
+Node: Modules38811
+Node: Inter Process Communication39638
+Node: Major Concepts40261
+Node: Messaging Interface44066
+Node: Mach Message Call44796
+Node: Message Format48111
+Node: Exchanging Port Rights59303
+Ref: Exchanging Port Rights-Footnote-164865
+Node: Memory65037
+Ref: Memory-Footnote-168131
+Node: Message Send68473
+Ref: Message Send-Footnote-175495
+Node: Message Receive75778
+Ref: Message Receive-Footnote-185430
+Node: Atomicity85711
+Node: Port Manipulation Interface88485
+Node: Port Creation90040
+Node: Port Destruction94829
+Node: Port Names97972
+Node: Port Rights102219
+Node: Ports and other Tasks106008
+Node: Receive Rights110101
+Node: Port Sets117032
+Node: Request Notifications119435
+Node: Inherited Ports124239
+Node: Virtual Memory Interface127923
+Node: Memory Allocation129176
+Node: Memory Deallocation131701
+Node: Data Transfer133165
+Node: Memory Attributes136691
+Node: Mapping Memory Objects146130
+Node: Memory Statistics149439
+Node: External Memory Management151013
+Node: Memory Object Server151718
+Node: Memory Object Creation154427
+Node: Memory Object Termination158475
+Node: Memory Objects and Data161414
+Node: Memory Object Locking178560
+Node: Memory Object Attributes184455
+Node: Default Memory Manager190292
+Node: Threads and Tasks196014
+Node: Thread Interface196351
+Node: Thread Creation197347
+Node: Thread Termination198464
+Node: Thread Information198935
+Node: Thread Settings205034
+Node: Thread Execution206268
+Node: Scheduling213561
+Node: Thread Priority213916
+Node: Hand-Off Scheduling216550
+Node: Scheduling Policy221675
+Node: Thread Special Ports223007
+Node: Exceptions225453
+Node: Task Interface226323
+Node: Task Creation227335
+Node: Task Termination228670
+Node: Task Information229272
+Node: Task Execution236174
+Node: Task Special Ports240587
+Node: Syscall Emulation244441
+Node: Profiling245672
+Node: Host Interface249435
+Node: Host Ports250420
+Node: Host Information252493
+Node: Host Time257876
+Node: Host Reboot260543
+Node: Processors and Processor Sets261095
+Node: Processor Set Interface262073
+Node: Processor Set Ports262840
+Node: Processor Set Access263670
+Node: Processor Set Creation265930
+Node: Processor Set Destruction266957
+Node: Tasks and Threads on Sets267878
+Node: Processor Set Priority273045
+Node: Processor Set Policy274335
+Node: Processor Set Info275949
+Node: Processor Interface279762
+Node: Hosted Processors280487
+Node: Processor Control281478
+Node: Processors and Sets282944
+Node: Processor Info284822
+Node: Device Interface287564
+Node: Device Reply Server289179
+Node: Device Open290471
+Node: Device Close292594
+Node: Device Read293173
+Node: Device Write296092
+Node: Device Map298897
+Node: Device Status299788
+Node: Device Filter302600
+Node: Kernel Debugger308347
+Node: Operation309074
+Node: Commands312051
+Node: Variables325836
+Node: Expressions327224
+Node: Copying328573
+Node: Documentation License347802
+Node: GNU Free Documentation License348391
+Node: CMU License370790
+Node: Concept Index372025
+Node: Function and Data Index375871
 
 End Tag Table
diff --git a/doc/mach.info-1 b/doc/mach.info-1
index b95106f..41ec161 100644
--- a/doc/mach.info-1
+++ b/doc/mach.info-1
@@ -1,9 +1,9 @@
-This is mach.info, produced by makeinfo version 6.0 from mach.texi.
+This is mach.info, produced by makeinfo version 6.1 from mach.texi.
 
 This file documents the GNU Mach microkernel.
 
-   This is edition 0.4, last updated on 12 September 2015, of 'The GNU
-Mach Reference Manual', for version 1.6+git20160114.
+   This is edition 0.4, last updated on 10 March 2016, of 'The GNU Mach
+Reference Manual', for version 1.6+git20160311.
 
    Copyright (C) 2001, 2002, 2006, 2007, 2008 Free Software Foundation,
 Inc.
@@ -45,8 +45,8 @@ Main Menu
 
 This file documents the GNU Mach microkernel.
 
-   This is edition 0.4, last updated on 12 September 2015, of 'The GNU
-Mach Reference Manual', for version 1.6+git20160114.
+   This is edition 0.4, last updated on 10 March 2016, of 'The GNU Mach
+Reference Manual', for version 1.6+git20160311.
 
    Copyright (C) 2001, 2002, 2006, 2007, 2008 Free Software Foundation,
 Inc.
@@ -3524,10 +3524,10 @@ File: mach.info,  Node: Memory Object Creation,  Next: 
Memory Object Termination
      the calling kernel is included for planning purposes.
 
      When the memory manager is prepared to accept requests for data for
-     this object, it must call 'memory_object_ready' with the attribute.
-     Otherwise the kernel will not process requests on this object.  To
-     reject all mappings of this object, the memory manager may use
-     'memory_object_destroy'.
+     this object, it must call 'memory_object_ready', or set the ready
+     flag using 'memory_object_set_attributes'.  Otherwise the kernel
+     will not process requests on this object.  To reject all mappings
+     of this object, the memory manager may use 'memory_object_destroy'.
 
      The argument MEMORY_OBJECT is the port that represents the memory
      object data, as supplied to the kernel in a 'vm_map' call.
diff --git a/doc/mach.info-2 b/doc/mach.info-2
index 4e8ee4a..39fe14a 100644
--- a/doc/mach.info-2
+++ b/doc/mach.info-2
@@ -1,9 +1,9 @@
-This is mach.info, produced by makeinfo version 6.0 from mach.texi.
+This is mach.info, produced by makeinfo version 6.1 from mach.texi.
 
 This file documents the GNU Mach microkernel.
 
-   This is edition 0.4, last updated on 12 September 2015, of 'The GNU
-Mach Reference Manual', for version 1.6+git20160114.
+   This is edition 0.4, last updated on 10 March 2016, of 'The GNU Mach
+Reference Manual', for version 1.6+git20160311.
 
    Copyright (C) 2001, 2002, 2006, 2007, 2008 Free Software Foundation,
 Inc.
diff --git a/doc/mach.texi b/doc/mach.texi
index 6fc79f7..0aeed76 100644
--- a/doc/mach.texi
+++ b/doc/mach.texi
@@ -3526,8 +3526,9 @@ each will perform a @code{memory_object_init} call with 
new request and
 name ports.  The virtual page size that is used by the calling kernel is
 included for planning purposes.
 
-When the memory manager is prepared to accept requests for data for this
-object, it must call @code{memory_object_ready} with the attribute.
+When the memory manager is prepared to accept requests for data for
+this object, it must call @code{memory_object_ready}, or set the ready
+flag using @code{memory_object_set_attributes}.
 Otherwise the kernel will not process requests on this object.  To
 reject all mappings of this object, the memory manager may use
 @code{memory_object_destroy}.
diff --git a/doc/stamp-vti b/doc/stamp-vti
index 69dbe4d..fd5f466 100644
--- a/doc/stamp-vti
+++ b/doc/stamp-vti
@@ -1,4 +1,4 @@
address@hidden UPDATED 12 September 2015
address@hidden UPDATED-MONTH September 2015
address@hidden EDITION 1.6+git20160114
address@hidden VERSION 1.6+git20160114
address@hidden UPDATED 10 March 2016
address@hidden UPDATED-MONTH March 2016
address@hidden EDITION 1.6+git20160311
address@hidden VERSION 1.6+git20160311
diff --git a/doc/version.texi b/doc/version.texi
index 69dbe4d..fd5f466 100644
--- a/doc/version.texi
+++ b/doc/version.texi
@@ -1,4 +1,4 @@
address@hidden UPDATED 12 September 2015
address@hidden UPDATED-MONTH September 2015
address@hidden EDITION 1.6+git20160114
address@hidden VERSION 1.6+git20160114
address@hidden UPDATED 10 March 2016
address@hidden UPDATED-MONTH March 2016
address@hidden EDITION 1.6+git20160311
address@hidden VERSION 1.6+git20160311
diff --git a/i386/Makefrag.am b/i386/Makefrag.am
index ef393d5..8b0ef7f 100644
--- a/i386/Makefrag.am
+++ b/i386/Makefrag.am
@@ -20,8 +20,11 @@
 libkernel_a_SOURCES += \
        i386/i386at/autoconf.c \
        i386/i386at/autoconf.h \
+       i386/i386at/biosmem.c \
+       i386/i386at/biosmem.h \
        i386/i386at/conf.c \
        i386/i386at/cons_conf.c \
+       i386/i386at/elf.h \
        i386/i386at/idt.h \
        i386/i386at/model_dep.c \
        i386/i386at/model_dep.h \
@@ -95,6 +98,7 @@ libkernel_a_SOURCES += \
        i386/i386/ast.h \
        i386/i386/ast_check.c \
        i386/i386/ast_types.h \
+       i386/i386/cpu.h \
        i386/i386/cpu_number.h \
        i386/i386/cswitch.S \
        i386/i386/db_disasm.c \
diff --git a/i386/i386/cpu.h b/i386/i386/cpu.h
new file mode 100644
index 0000000..1bf40dc
--- /dev/null
+++ b/i386/i386/cpu.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2010-2014 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _X86_CPU_H
+#define _X86_CPU_H
+
+#include <kern/macros.h>
+
+/*
+ * EFLAGS register flags.
+ */
+#define CPU_EFL_ONE 0x00000002
+#define CPU_EFL_IF  0x00000200
+
+/*
+ * Return the content of the EFLAGS register.
+ *
+ * Implies a compiler barrier.
+ */
+static __always_inline unsigned long
+cpu_get_eflags(void)
+{
+    unsigned long eflags;
+
+    asm volatile("pushf\n"
+                 "pop %0\n"
+                 : "=r" (eflags)
+                 : : "memory");
+
+    return eflags;
+}
+
+/*
+ * Enable local interrupts.
+ *
+ * Implies a compiler barrier.
+ */
+static __always_inline void
+cpu_intr_enable(void)
+{
+    asm volatile("sti" : : : "memory");
+}
+
+/*
+ * Disable local interrupts.
+ *
+ * Implies a compiler barrier.
+ */
+static __always_inline void
+cpu_intr_disable(void)
+{
+    asm volatile("cli" : : : "memory");
+}
+
+/*
+ * Restore the content of the EFLAGS register, possibly enabling interrupts.
+ *
+ * Implies a compiler barrier.
+ */
+static __always_inline void
+cpu_intr_restore(unsigned long flags)
+{
+    asm volatile("push %0\n"
+                 "popf\n"
+                 : : "r" (flags)
+                 : "memory");
+}
+
+/*
+ * Disable local interrupts, returning the previous content of the EFLAGS
+ * register.
+ *
+ * Implies a compiler barrier.
+ */
+static __always_inline void
+cpu_intr_save(unsigned long *flags)
+{
+    *flags = cpu_get_eflags();
+    cpu_intr_disable();
+}
+
+/*
+ * Return true if interrupts are enabled.
+ *
+ * Implies a compiler barrier.
+ */
+static __always_inline int
+cpu_intr_enabled(void)
+{
+    unsigned long eflags;
+
+    eflags = cpu_get_eflags();
+    return (eflags & CPU_EFL_IF) ? 1 : 0;
+}
+
+#endif /* _X86_CPU_H */
diff --git a/i386/i386/db_trace.c b/i386/i386/db_trace.c
index ec33859..c8789e7 100644
--- a/i386/i386/db_trace.c
+++ b/i386/i386/db_trace.c
@@ -37,6 +37,7 @@
 #include <machine/machspl.h>
 #include <machine/db_interface.h>
 #include <machine/db_trace.h>
+#include <i386at/model_dep.h>
 
 #include <ddb/db_access.h>
 #include <ddb/db_command.h>
@@ -129,7 +130,6 @@ db_i386_reg_value(
        long                    *dp = 0;
        db_expr_t               null_reg = 0;
        thread_t                thread = ap->thread;
-       extern unsigned         int_stack_high;
 
        if (db_option(ap->modif, 'u')) {
            if (thread == THREAD_NULL) {
@@ -139,7 +139,7 @@ db_i386_reg_value(
            if (thread == current_thread()) {
                if (ddb_regs.cs & 0x3)
                    dp = vp->valuep;
-               else if (ddb_regs.ebp < int_stack_high)
+               else if (ON_INT_STACK(ddb_regs.ebp))
                    db_error("cannot get/set user registers in nested 
interrupt\n");
            }
        } else {
diff --git a/i386/i386/fpu.c b/i386/i386/fpu.c
index 0f34833..6ee2015 100644
--- a/i386/i386/fpu.c
+++ b/i386/i386/fpu.c
@@ -189,7 +189,7 @@ fpu_module_init(void)
 {
        kmem_cache_init(&ifps_cache, "i386_fpsave_state",
                        sizeof(struct i386_fpsave_state), 16,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
 }
 
 /*
@@ -494,27 +494,52 @@ ASSERT_IPL(SPL0);
  *
  * Use 53-bit precision.
  */
-void fpinit(void)
+static void fpinit(thread_t thread)
 {
        unsigned short  control;
 
 ASSERT_IPL(SPL0);
        clear_ts();
        fninit();
-       fnstcw(&control);
-       control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
-       control |= (FPC_PC_53 |         /* Set precision */ 
-                       FPC_RC_RN |     /* round-to-nearest */
-                       FPC_ZE |        /* Suppress zero-divide */
-                       FPC_OE |        /*  and overflow */
-                       FPC_UE |        /*  underflow */
-                       FPC_IE |        /* Allow NaNQs and +-INF */
-                       FPC_DE |        /* Allow denorms as operands  */
-                       FPC_PE);        /* No trap for precision loss */
+       if (thread->pcb->init_control) {
+               control = thread->pcb->init_control;
+       }
+       else
+       {
+               fnstcw(&control);
+               control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding 
control */
+               control |= (FPC_PC_53 |         /* Set precision */ 
+                               FPC_RC_RN |     /* round-to-nearest */
+                               FPC_ZE |        /* Suppress zero-divide */
+                               FPC_OE |        /*  and overflow */
+                               FPC_UE |        /*  underflow */
+                               FPC_IE |        /* Allow NaNQs and +-INF */
+                               FPC_DE |        /* Allow denorms as operands  */
+                               FPC_PE);        /* No trap for precision loss */
+       }
        fldcw(control);
 }
 
 /*
+ * Inherit FPU state from a parent to a child, if any
+ */
+void fpinherit(thread_t parent_thread, thread_t thread)
+{
+       pcb_t pcb = parent_thread->pcb;
+       struct i386_fpsave_state *ifps;
+
+       ifps = pcb->ims.ifps;
+       if (ifps) {
+               /* Parent does have a state, inherit it */
+               if (ifps->fp_valid == TRUE)
+                       thread->pcb->init_control = 
ifps->fp_save_state.fp_control;
+               else
+                       /* State is in the FPU, fetch from there */
+                       fnstcw(&thread->pcb->init_control);
+       }
+}
+
+/*
  * Coprocessor not present.
  */
 void
@@ -778,7 +803,7 @@ ASSERT_IPL(SPL0);
            ifps = (struct i386_fpsave_state *) kmem_cache_alloc(&ifps_cache);
            memset(ifps, 0, sizeof *ifps);
            pcb->ims.ifps = ifps;
-           fpinit();
+           fpinit(thread);
 #if 1
 /* 
  * I'm not sure this is needed. Does the fpu regenerate the interrupt in
diff --git a/i386/i386/fpu.h b/i386/i386/fpu.h
index 7bc6438..caade5d 100644
--- a/i386/i386/fpu.h
+++ b/i386/i386/fpu.h
@@ -125,5 +125,6 @@ extern void fpexterrflt(void);
 extern void fpastintr(void);
 extern void init_fpu(void);
 extern void fpintr(int unit);
+extern void fpinherit(thread_t parent_thread, thread_t thread);
 
 #endif /* _I386_FPU_H_ */
diff --git a/i386/i386/locore.S b/i386/i386/locore.S
index 8cefbcc..c715d95 100644
--- a/i386/i386/locore.S
+++ b/i386/i386/locore.S
@@ -542,8 +542,10 @@ trap_from_kernel:
 #if    MACH_KDB || MACH_TTD
        movl    %esp,%ebx               /* save current stack */
 
-       cmpl    EXT(int_stack_high),%esp        /* on an interrupt stack? */
-       jb      1f                      /* OK if so */
+       movl    %esp,%edx               /* on an interrupt stack? */
+       and     $(~(KERNEL_STACK_SIZE-1)),%edx
+       cmpl    EXT(int_stack_base),%edx
+       je      1f                      /* OK if so */
 
        CPU_NUMBER(%edx)                /* get CPU number */
        cmpl    CX(EXT(kernel_stack),%edx),%esp
@@ -647,8 +649,10 @@ ENTRY(all_intrs)
        pushl   %edx
        cld                             /* clear direction flag */
 
-       cmpl    %ss:EXT(int_stack_high),%esp /* on an interrupt stack? */
-       jb      int_from_intstack       /* if not: */
+       movl    %esp,%edx               /* on an interrupt stack? */
+       and     $(~(KERNEL_STACK_SIZE-1)),%edx
+       cmpl    %ss:EXT(int_stack_base),%edx
+       je      int_from_intstack       /* if not: */
 
        pushl   %ds                     /* save segment registers */
        pushl   %es
@@ -707,7 +711,7 @@ LEXT(return_to_iret)                        /* ( label for 
kdb_kintr and hardclock) */
        iret                            /* return to caller */
 
 int_from_intstack:
-       cmpl    $EXT(_intstack),%esp    /* seemingly looping? */
+       cmpl    EXT(int_stack_base),%esp        /* seemingly looping? */
        jb      stack_overflowed        /* if not: */
        call    EXT(interrupt)          /* call interrupt routine */
 _return_to_iret_i:                     /* ( label for kdb_kintr) */
diff --git a/i386/i386/machine_task.c b/i386/i386/machine_task.c
index 490b102..d592838 100644
--- a/i386/i386/machine_task.c
+++ b/i386/i386/machine_task.c
@@ -38,7 +38,7 @@ void
 machine_task_module_init (void)
 {
   kmem_cache_init (&machine_task_iopb_cache, "i386_task_iopb", IOPB_BYTES, 0,
-                  NULL, NULL, NULL, 0);
+                  NULL, 0);
 }
 
 
diff --git a/i386/i386/pcb.c b/i386/i386/pcb.c
index 3a0eba0..dd2042d 100644
--- a/i386/i386/pcb.c
+++ b/i386/i386/pcb.c
@@ -371,12 +371,12 @@ thread_t switch_context(
 void pcb_module_init(void)
 {
        kmem_cache_init(&pcb_cache, "pcb", sizeof(struct pcb), 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
 
        fpu_module_init();
 }
 
-void pcb_init(thread_t thread)
+void pcb_init(task_t parent_task, thread_t thread)
 {
        pcb_t           pcb;
 
@@ -406,6 +406,11 @@ void pcb_init(thread_t thread)
        pcb->iss.efl = EFL_USER_SET;
 
        thread->pcb = pcb;
+
+       /* This is a new thread for the current task, make it inherit our FPU
+          state.  */
+       if (parent_task == current_task())
+               fpinherit(current_thread(), thread);
 }
 
 void pcb_terminate(thread_t thread)
diff --git a/i386/i386/pcb.h b/i386/i386/pcb.h
index 708f30d..cf47694 100644
--- a/i386/i386/pcb.h
+++ b/i386/i386/pcb.h
@@ -31,7 +31,7 @@
 #include <mach/thread_status.h>
 #include <machine/thread.h>
 
-extern void pcb_init (thread_t thread);
+extern void pcb_init (task_t parent_task, thread_t thread);
 
 extern void pcb_terminate (thread_t thread);
 
diff --git a/i386/i386/thread.h b/i386/i386/thread.h
index 35a2802..9bda11f 100644
--- a/i386/i386/thread.h
+++ b/i386/i386/thread.h
@@ -177,6 +177,7 @@ typedef struct pcb {
        struct i386_saved_state iss;
        struct i386_machine_state ims;
        decl_simple_lock_data(, lock)
+       unsigned short init_control;            /* Initial FPU control to set */
 #ifdef LINUX_DEV
        void *data;
 #endif /* LINUX_DEV */
diff --git a/i386/i386/vm_param.h b/i386/i386/vm_param.h
index 6292ca2..2635c2c 100644
--- a/i386/i386/vm_param.h
+++ b/i386/i386/vm_param.h
@@ -23,6 +23,8 @@
 #ifndef _I386_KERNEL_I386_VM_PARAM_
 #define _I386_KERNEL_I386_VM_PARAM_
 
+#include <kern/macros.h>
+
 /* XXX use xu/vm_param.h */
 #include <mach/vm_param.h>
 #ifdef MACH_PV_PAGETABLES
@@ -56,9 +58,9 @@
 
 /* Reserve mapping room for kmem. */
 #ifdef MACH_XEN
-#define VM_KERNEL_MAP_SIZE (256 * 1024 * 1024)
+#define VM_KERNEL_MAP_SIZE (128 * 1024 * 1024)
 #else
-#define VM_KERNEL_MAP_SIZE (224 * 1024 * 1024)
+#define VM_KERNEL_MAP_SIZE (96 * 1024 * 1024)
 #endif
 
 /* The kernel virtual address space is actually located
@@ -101,4 +103,51 @@
 #define kvtolin(a)     ((vm_offset_t)(a) - VM_MIN_KERNEL_ADDRESS + 
LINEAR_MIN_KERNEL_ADDRESS)
 #define lintokv(a)     ((vm_offset_t)(a) - LINEAR_MIN_KERNEL_ADDRESS + 
VM_MIN_KERNEL_ADDRESS)
 
+/*
+ * Physical memory properties.
+ */
+#define VM_PAGE_DMA_LIMIT       DECL_CONST(0x1000000, UL)
+
+#ifdef MACH_XEN
+/* TODO Completely check Xen physical/virtual layout */
+#define VM_PAGE_MAX_SEGS 3
+#define VM_PAGE_DIRECTMAP_LIMIT (VM_MAX_KERNEL_ADDRESS \
+                                - VM_MIN_KERNEL_ADDRESS \
+                                - VM_KERNEL_MAP_SIZE)
+#define VM_PAGE_HIGHMEM_LIMIT   DECL_CONST(0x10000000000000, ULL)
+#else /* MACH_XEN */
+#ifdef __LP64__
+#define VM_PAGE_MAX_SEGS 4
+#define VM_PAGE_DMA32_LIMIT     DECL_CONST(0x100000000, UL)
+#define VM_PAGE_DIRECTMAP_LIMIT DECL_CONST(0x400000000000, UL)
+#define VM_PAGE_HIGHMEM_LIMIT   DECL_CONST(0x10000000000000, UL)
+#else /* __LP64__ */
+#define VM_PAGE_DIRECTMAP_LIMIT (VM_MAX_KERNEL_ADDRESS \
+                                - VM_MIN_KERNEL_ADDRESS \
+                                - VM_KERNEL_MAP_SIZE + 1)
+#ifdef PAE
+#define VM_PAGE_MAX_SEGS 3
+#define VM_PAGE_HIGHMEM_LIMIT   DECL_CONST(0x10000000000000, ULL)
+#else /* PAE */
+#define VM_PAGE_MAX_SEGS 3
+#define VM_PAGE_HIGHMEM_LIMIT   DECL_CONST(0xfffff000, UL)
+#endif /* PAE */
+#endif /* __LP64__ */
+#endif /* MACH_XEN */
+
+/*
+ * Physical segment indexes.
+ */
+#define VM_PAGE_SEG_DMA         0
+
+#ifdef __LP64__
+#define VM_PAGE_SEG_DMA32       1
+#define VM_PAGE_SEG_DIRECTMAP   2
+#define VM_PAGE_SEG_HIGHMEM     3
+#else /* __LP64__ */
+#define VM_PAGE_SEG_DMA32       1   /* Alias for the DIRECTMAP segment */
+#define VM_PAGE_SEG_DIRECTMAP   1
+#define VM_PAGE_SEG_HIGHMEM     2
+#endif /* __LP64__ */
+
 #endif /* _I386_KERNEL_I386_VM_PARAM_ */
diff --git a/i386/i386at/biosmem.c b/i386/i386at/biosmem.c
new file mode 100644
index 0000000..a7a440e
--- /dev/null
+++ b/i386/i386at/biosmem.c
@@ -0,0 +1,910 @@
+/*
+ * Copyright (c) 2010-2014 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <i386/model_dep.h>
+#include <i386at/biosmem.h>
+#include <i386at/elf.h>
+#include <kern/assert.h>
+#include <kern/debug.h>
+#include <kern/macros.h>
+#include <kern/printf.h>
+#include <mach/vm_param.h>
+#include <mach/xen.h>
+#include <mach/machine/multiboot.h>
+#include <sys/types.h>
+#include <vm/vm_page.h>
+
+#define __boot
+#define __bootdata
+#define __init
+
+#define boot_memmove    memmove
+#define boot_panic      panic
+#define boot_strlen     strlen
+
+#define BOOT_CGAMEM     phystokv(0xb8000)
+#define BOOT_CGACHARS   (80 * 25)
+#define BOOT_CGACOLOR   0x7
+
+extern char _start, _end;
+
+/*
+ * Maximum number of entries in the BIOS memory map.
+ *
+ * Because of adjustments of overlapping ranges, the memory map can grow
+ * to twice this size.
+ */
+#define BIOSMEM_MAX_MAP_SIZE 128
+
+/*
+ * Memory range types.
+ */
+#define BIOSMEM_TYPE_AVAILABLE  1
+#define BIOSMEM_TYPE_RESERVED   2
+#define BIOSMEM_TYPE_ACPI       3
+#define BIOSMEM_TYPE_NVS        4
+#define BIOSMEM_TYPE_UNUSABLE   5
+#define BIOSMEM_TYPE_DISABLED   6
+
+/*
+ * Memory map entry.
+ */
+struct biosmem_map_entry {
+    uint64_t base_addr;
+    uint64_t length;
+    unsigned int type;
+};
+
+/*
+ * Contiguous block of physical memory.
+ *
+ * Tha "available" range records what has been passed to the VM system as
+ * available inside the segment.
+ */
+struct biosmem_segment {
+    phys_addr_t start;
+    phys_addr_t end;
+    phys_addr_t avail_start;
+    phys_addr_t avail_end;
+};
+
+/*
+ * Memory map built from the information passed by the boot loader.
+ *
+ * If the boot loader didn't pass a valid memory map, a simple map is built
+ * based on the mem_lower and mem_upper multiboot fields.
+ */
+static struct biosmem_map_entry biosmem_map[BIOSMEM_MAX_MAP_SIZE * 2]
+    __bootdata;
+static unsigned int biosmem_map_size __bootdata;
+
+/*
+ * Physical segment boundaries.
+ */
+static struct biosmem_segment biosmem_segments[VM_PAGE_MAX_SEGS] __bootdata;
+
+/*
+ * Boundaries of the simple bootstrap heap.
+ *
+ * This heap is located above BIOS memory.
+ */
+static uint32_t biosmem_heap_start __bootdata;
+static uint32_t biosmem_heap_cur __bootdata;
+static uint32_t biosmem_heap_end __bootdata;
+
+static char biosmem_panic_toobig_msg[] __bootdata
+    = "biosmem: too many memory map entries";
+#ifndef MACH_HYP
+static char biosmem_panic_setup_msg[] __bootdata
+    = "biosmem: unable to set up the early memory allocator";
+#endif /* MACH_HYP */
+static char biosmem_panic_noseg_msg[] __bootdata
+    = "biosmem: unable to find any memory segment";
+static char biosmem_panic_inval_msg[] __bootdata
+    = "biosmem: attempt to allocate 0 page";
+static char biosmem_panic_nomem_msg[] __bootdata
+    = "biosmem: unable to allocate memory";
+
+#ifndef MACH_HYP
+
+static void __boot
+biosmem_map_build(const struct multiboot_raw_info *mbi)
+{
+    struct multiboot_raw_mmap_entry *mb_entry, *mb_end;
+    struct biosmem_map_entry *start, *entry, *end;
+    unsigned long addr;
+
+    addr = phystokv(mbi->mmap_addr);
+    mb_entry = (struct multiboot_raw_mmap_entry *)addr;
+    mb_end = (struct multiboot_raw_mmap_entry *)(addr + mbi->mmap_length);
+    start = biosmem_map;
+    entry = start;
+    end = entry + BIOSMEM_MAX_MAP_SIZE;
+
+    while ((mb_entry < mb_end) && (entry < end)) {
+        entry->base_addr = mb_entry->base_addr;
+        entry->length = mb_entry->length;
+        entry->type = mb_entry->type;
+
+        mb_entry = (void *)mb_entry + sizeof(mb_entry->size) + mb_entry->size;
+        entry++;
+    }
+
+    biosmem_map_size = entry - start;
+}
+
+static void __boot
+biosmem_map_build_simple(const struct multiboot_raw_info *mbi)
+{
+    struct biosmem_map_entry *entry;
+
+    entry = biosmem_map;
+    entry->base_addr = 0;
+    entry->length = mbi->mem_lower << 10;
+    entry->type = BIOSMEM_TYPE_AVAILABLE;
+
+    entry++;
+    entry->base_addr = BIOSMEM_END;
+    entry->length = mbi->mem_upper << 10;
+    entry->type = BIOSMEM_TYPE_AVAILABLE;
+
+    biosmem_map_size = 2;
+}
+
+#endif /* MACH_HYP */
+
+static int __boot
+biosmem_map_entry_is_invalid(const struct biosmem_map_entry *entry)
+{
+    return (entry->base_addr + entry->length) <= entry->base_addr;
+}
+
+static void __boot
+biosmem_map_filter(void)
+{
+    struct biosmem_map_entry *entry;
+    unsigned int i;
+
+    i = 0;
+
+    while (i < biosmem_map_size) {
+        entry = &biosmem_map[i];
+
+        if (biosmem_map_entry_is_invalid(entry)) {
+            biosmem_map_size--;
+            boot_memmove(entry, entry + 1,
+                         (biosmem_map_size - i) * sizeof(*entry));
+            continue;
+        }
+
+        i++;
+    }
+}
+
+static void __boot
+biosmem_map_sort(void)
+{
+    struct biosmem_map_entry tmp;
+    unsigned int i, j;
+
+    /*
+     * Simple insertion sort.
+     */
+    for (i = 1; i < biosmem_map_size; i++) {
+        tmp = biosmem_map[i];
+
+        for (j = i - 1; j < i; j--) {
+            if (biosmem_map[j].base_addr < tmp.base_addr)
+                break;
+
+            biosmem_map[j + 1] = biosmem_map[j];
+        }
+
+        biosmem_map[j + 1] = tmp;
+    }
+}
+
+static void __boot
+biosmem_map_adjust(void)
+{
+    struct biosmem_map_entry tmp, *a, *b, *first, *second;
+    uint64_t a_end, b_end, last_end;
+    unsigned int i, j, last_type;
+
+    biosmem_map_filter();
+
+    /*
+     * Resolve overlapping areas, giving priority to most restrictive
+     * (i.e. numerically higher) types.
+     */
+    for (i = 0; i < biosmem_map_size; i++) {
+        a = &biosmem_map[i];
+        a_end = a->base_addr + a->length;
+
+        j = i + 1;
+
+        while (j < biosmem_map_size) {
+            b = &biosmem_map[j];
+            b_end = b->base_addr + b->length;
+
+            if ((a->base_addr >= b_end) || (a_end <= b->base_addr)) {
+                j++;
+                continue;
+            }
+
+            if (a->base_addr < b->base_addr) {
+                first = a;
+                second = b;
+            } else {
+                first = b;
+                second = a;
+            }
+
+            if (a_end > b_end) {
+                last_end = a_end;
+                last_type = a->type;
+            } else {
+                last_end = b_end;
+                last_type = b->type;
+            }
+
+            tmp.base_addr = second->base_addr;
+            tmp.length = MIN(a_end, b_end) - tmp.base_addr;
+            tmp.type = MAX(a->type, b->type);
+            first->length = tmp.base_addr - first->base_addr;
+            second->base_addr += tmp.length;
+            second->length = last_end - second->base_addr;
+            second->type = last_type;
+
+            /*
+             * Filter out invalid entries.
+             */
+            if (biosmem_map_entry_is_invalid(a)
+                && biosmem_map_entry_is_invalid(b)) {
+                *a = tmp;
+                biosmem_map_size--;
+                memmove(b, b + 1, (biosmem_map_size - j) * sizeof(*b));
+                continue;
+            } else if (biosmem_map_entry_is_invalid(a)) {
+                *a = tmp;
+                j++;
+                continue;
+            } else if (biosmem_map_entry_is_invalid(b)) {
+                *b = tmp;
+                j++;
+                continue;
+            }
+
+            if (tmp.type == a->type)
+                first = a;
+            else if (tmp.type == b->type)
+                first = b;
+            else {
+
+                /*
+                 * If the overlapping area can't be merged with one of its
+                 * neighbors, it must be added as a new entry.
+                 */
+
+                if (biosmem_map_size >= ARRAY_SIZE(biosmem_map))
+                    boot_panic(biosmem_panic_toobig_msg);
+
+                biosmem_map[biosmem_map_size] = tmp;
+                biosmem_map_size++;
+                j++;
+                continue;
+            }
+
+            if (first->base_addr > tmp.base_addr)
+                first->base_addr = tmp.base_addr;
+
+            first->length += tmp.length;
+            j++;
+        }
+    }
+
+    biosmem_map_sort();
+}
+
+static int __boot
+biosmem_map_find_avail(phys_addr_t *phys_start, phys_addr_t *phys_end)
+{
+    const struct biosmem_map_entry *entry, *map_end;
+    phys_addr_t seg_start, seg_end;
+    uint64_t start, end;
+
+    seg_start = (phys_addr_t)-1;
+    seg_end = (phys_addr_t)-1;
+    map_end = biosmem_map + biosmem_map_size;
+
+    for (entry = biosmem_map; entry < map_end; entry++) {
+        if (entry->type != BIOSMEM_TYPE_AVAILABLE)
+            continue;
+
+        start = vm_page_round(entry->base_addr);
+
+        if (start >= *phys_end)
+            break;
+
+        end = vm_page_trunc(entry->base_addr + entry->length);
+
+        if ((start < end) && (start < *phys_end) && (end > *phys_start)) {
+            if (seg_start == (phys_addr_t)-1)
+                seg_start = start;
+
+            seg_end = end;
+        }
+    }
+
+    if ((seg_start == (phys_addr_t)-1) || (seg_end == (phys_addr_t)-1))
+        return -1;
+
+    if (seg_start > *phys_start)
+        *phys_start = seg_start;
+
+    if (seg_end < *phys_end)
+        *phys_end = seg_end;
+
+    return 0;
+}
+
+static void __boot
+biosmem_set_segment(unsigned int seg_index, phys_addr_t start, phys_addr_t end)
+{
+    biosmem_segments[seg_index].start = start;
+    biosmem_segments[seg_index].end = end;
+}
+
+static phys_addr_t __boot
+biosmem_segment_end(unsigned int seg_index)
+{
+    return biosmem_segments[seg_index].end;
+}
+
+static phys_addr_t __boot
+biosmem_segment_size(unsigned int seg_index)
+{
+    return biosmem_segments[seg_index].end - biosmem_segments[seg_index].start;
+}
+
+#ifndef MACH_HYP
+
+static void __boot
+biosmem_save_cmdline_sizes(struct multiboot_raw_info *mbi)
+{
+    struct multiboot_raw_module *mod;
+    uint32_t i, va;
+
+    if (mbi->flags & MULTIBOOT_LOADER_CMDLINE) {
+        va = phystokv(mbi->cmdline);
+        mbi->unused0 = boot_strlen((char *)va) + 1;
+    }
+
+    if (mbi->flags & MULTIBOOT_LOADER_MODULES) {
+        unsigned long addr;
+
+        addr = phystokv(mbi->mods_addr);
+
+        for (i = 0; i < mbi->mods_count; i++) {
+            mod = (struct multiboot_raw_module *)addr + i;
+            va = phystokv(mod->string);
+            mod->reserved = boot_strlen((char *)va) + 1;
+        }
+    }
+}
+
+static void __boot
+biosmem_find_boot_data_update(uint32_t min, uint32_t *start, uint32_t *end,
+                              uint32_t data_start, uint32_t data_end)
+{
+    if ((min <= data_start) && (data_start < *start)) {
+        *start = data_start;
+        *end = data_end;
+    }
+}
+
+/*
+ * Find the first boot data in the given range, and return their containing
+ * area (start address is returned directly, end address is returned in end).
+ * The following are considered boot data :
+ *  - the kernel
+ *  - the kernel command line
+ *  - the module table
+ *  - the modules
+ *  - the modules command lines
+ *  - the ELF section header table
+ *  - the ELF .shstrtab, .symtab and .strtab sections
+ *
+ * If no boot data was found, 0 is returned, and the end address isn't set.
+ */
+static uint32_t __boot
+biosmem_find_boot_data(const struct multiboot_raw_info *mbi, uint32_t min,
+                       uint32_t max, uint32_t *endp)
+{
+    struct multiboot_raw_module *mod;
+    struct elf_shdr *shdr;
+    uint32_t i, start, end = end;
+    unsigned long tmp;
+
+    start = max;
+
+    biosmem_find_boot_data_update(min, &start, &end, _kvtophys(&_start),
+                                  _kvtophys(&_end));
+
+    if ((mbi->flags & MULTIBOOT_LOADER_CMDLINE) && (mbi->cmdline != 0))
+        biosmem_find_boot_data_update(min, &start, &end, mbi->cmdline,
+                                      mbi->cmdline + mbi->unused0);
+
+    if (mbi->flags & MULTIBOOT_LOADER_MODULES) {
+        i = mbi->mods_count * sizeof(struct multiboot_raw_module);
+        biosmem_find_boot_data_update(min, &start, &end, mbi->mods_addr,
+                                      mbi->mods_addr + i);
+        tmp = phystokv(mbi->mods_addr);
+
+        for (i = 0; i < mbi->mods_count; i++) {
+            mod = (struct multiboot_raw_module *)tmp + i;
+            biosmem_find_boot_data_update(min, &start, &end, mod->mod_start,
+                                          mod->mod_end);
+
+            if (mod->string != 0)
+                biosmem_find_boot_data_update(min, &start, &end, mod->string,
+                                              mod->string + mod->reserved);
+        }
+    }
+
+    if (mbi->flags & MULTIBOOT_LOADER_SHDR) {
+        tmp = mbi->shdr_num * mbi->shdr_size;
+        biosmem_find_boot_data_update(min, &start, &end, mbi->shdr_addr,
+                                      mbi->shdr_addr + tmp);
+        tmp = phystokv(mbi->shdr_addr);
+
+        for (i = 0; i < mbi->shdr_num; i++) {
+            shdr = (struct elf_shdr *)(tmp + (i * mbi->shdr_size));
+
+            if ((shdr->type != ELF_SHT_SYMTAB)
+                && (shdr->type != ELF_SHT_STRTAB))
+                continue;
+
+            biosmem_find_boot_data_update(min, &start, &end, shdr->addr,
+                                          shdr->addr + shdr->size);
+        }
+    }
+
+    if (start == max)
+        return 0;
+
+    *endp = end;
+    return start;
+}
+
+static void __boot
+biosmem_setup_allocator(struct multiboot_raw_info *mbi)
+{
+    uint32_t heap_start, heap_end, max_heap_start, max_heap_end;
+    uint32_t mem_end, next;
+
+    /*
+     * Find some memory for the heap. Look for the largest unused area in
+     * upper memory, carefully avoiding all boot data.
+     */
+    mem_end = vm_page_trunc((mbi->mem_upper + 1024) << 10);
+
+#ifndef __LP64__
+    if (mem_end > VM_PAGE_DIRECTMAP_LIMIT)
+        mem_end = VM_PAGE_DIRECTMAP_LIMIT;
+#endif /* __LP64__ */
+
+    max_heap_start = 0;
+    max_heap_end = 0;
+    next = BIOSMEM_END;
+
+    do {
+        heap_start = next;
+        heap_end = biosmem_find_boot_data(mbi, heap_start, mem_end, &next);
+
+        if (heap_end == 0) {
+            heap_end = mem_end;
+            next = 0;
+        }
+
+        if ((heap_end - heap_start) > (max_heap_end - max_heap_start)) {
+            max_heap_start = heap_start;
+            max_heap_end = heap_end;
+        }
+    } while (next != 0);
+
+    max_heap_start = vm_page_round(max_heap_start);
+    max_heap_end = vm_page_trunc(max_heap_end);
+
+    if (max_heap_start >= max_heap_end)
+        boot_panic(biosmem_panic_setup_msg);
+
+    biosmem_heap_start = max_heap_start;
+    biosmem_heap_end = max_heap_end;
+    biosmem_heap_cur = biosmem_heap_end;
+}
+
+#endif /* MACH_HYP */
+
+static void __boot
+biosmem_bootstrap_common(void)
+{
+    phys_addr_t phys_start, phys_end, last_addr;
+    int error;
+
+    biosmem_map_adjust();
+
+    phys_start = BIOSMEM_BASE;
+    phys_end = VM_PAGE_DMA_LIMIT;
+    error = biosmem_map_find_avail(&phys_start, &phys_end);
+
+    if (error)
+        boot_panic(biosmem_panic_noseg_msg);
+
+    biosmem_set_segment(VM_PAGE_SEG_DMA, phys_start, phys_end);
+    last_addr = phys_end;
+
+    phys_start = VM_PAGE_DMA_LIMIT;
+#ifdef VM_PAGE_DMA32_LIMIT
+    phys_end = VM_PAGE_DMA32_LIMIT;
+    error = biosmem_map_find_avail(&phys_start, &phys_end);
+
+    if (error)
+        goto out;
+
+    biosmem_set_segment(VM_PAGE_SEG_DMA32, phys_start, phys_end);
+    last_addr = phys_end;
+
+    phys_start = VM_PAGE_DMA32_LIMIT;
+#endif /* VM_PAGE_DMA32_LIMIT */
+    phys_end = VM_PAGE_DIRECTMAP_LIMIT;
+    error = biosmem_map_find_avail(&phys_start, &phys_end);
+
+    if (error)
+        goto out;
+
+    biosmem_set_segment(VM_PAGE_SEG_DIRECTMAP, phys_start, phys_end);
+    last_addr = phys_end;
+
+    phys_start = VM_PAGE_DIRECTMAP_LIMIT;
+    phys_end = VM_PAGE_HIGHMEM_LIMIT;
+    error = biosmem_map_find_avail(&phys_start, &phys_end);
+
+    if (error)
+        goto out;
+
+    biosmem_set_segment(VM_PAGE_SEG_HIGHMEM, phys_start, phys_end);
+
+out:
+    /* XXX phys_last_addr must be part of the direct physical mapping */
+    phys_last_addr = last_addr;
+}
+
+#ifdef MACH_HYP
+
+void
+biosmem_xen_bootstrap(void)
+{
+    struct biosmem_map_entry *entry;
+
+    entry = biosmem_map;
+    entry->base_addr = 0;
+    entry->length = boot_info.nr_pages << PAGE_SHIFT;
+    entry->type = BIOSMEM_TYPE_AVAILABLE;
+
+    biosmem_map_size = 1;
+
+    biosmem_bootstrap_common();
+
+    biosmem_heap_start = _kvtophys(boot_info.pt_base)
+                         + (boot_info.nr_pt_frames + 3) * 0x1000;
+    biosmem_heap_end = boot_info.nr_pages << PAGE_SHIFT;
+
+#ifndef __LP64__
+    /* TODO Check that this actually makes sense */
+    if (biosmem_heap_end > VM_PAGE_DIRECTMAP_LIMIT)
+        biosmem_heap_end = VM_PAGE_DIRECTMAP_LIMIT;
+#endif /* __LP64__ */
+
+    /*
+     * XXX Allocation on Xen must be bottom-up :
+     * At the "start of day", only 512k are available after the boot
+     * data. The pmap module then creates a 4g mapping so all physical
+     * memory is available, but it uses this allocator to do so.
+     * Therefore, it must return pages from this small 512k regions
+     * first.
+     */
+    biosmem_heap_cur = biosmem_heap_start;
+}
+
+#else /* MACH_HYP */
+
+void __boot
+biosmem_bootstrap(struct multiboot_raw_info *mbi)
+{
+    if (mbi->flags & MULTIBOOT_LOADER_MMAP)
+        biosmem_map_build(mbi);
+    else
+        biosmem_map_build_simple(mbi);
+
+    biosmem_bootstrap_common();
+
+    /*
+     * The kernel and modules command lines will be memory mapped later
+     * during initialization. Their respective sizes must be saved.
+     */
+    biosmem_save_cmdline_sizes(mbi);
+    biosmem_setup_allocator(mbi);
+}
+
+#endif /* MACH_HYP */
+
+unsigned long __boot
+biosmem_bootalloc(unsigned int nr_pages)
+{
+    unsigned long addr, size;
+
+    assert(!vm_page_ready());
+
+    size = vm_page_ptoa(nr_pages);
+
+    if (size == 0)
+        boot_panic(biosmem_panic_inval_msg);
+
+#ifdef MACH_HYP
+    addr = biosmem_heap_cur;
+#else /* MACH_HYP */
+    /* Top-down allocation to avoid unnecessarily filling DMA segments */
+    addr = biosmem_heap_cur - size;
+#endif /* MACH_HYP */
+
+    if ((addr < biosmem_heap_start) || (addr > biosmem_heap_cur))
+        boot_panic(biosmem_panic_nomem_msg);
+
+#ifdef MACH_HYP
+    biosmem_heap_cur += size;
+#else /* MACH_HYP */
+    biosmem_heap_cur = addr;
+#endif /* MACH_HYP */
+
+    return addr;
+}
+
+phys_addr_t __boot
+biosmem_directmap_size(void)
+{
+    if (biosmem_segment_size(VM_PAGE_SEG_DIRECTMAP) != 0)
+        return biosmem_segment_end(VM_PAGE_SEG_DIRECTMAP);
+    else if (biosmem_segment_size(VM_PAGE_SEG_DMA32) != 0)
+        return biosmem_segment_end(VM_PAGE_SEG_DMA32);
+    else
+        return biosmem_segment_end(VM_PAGE_SEG_DMA);
+}
+
+static const char * __init
+biosmem_type_desc(unsigned int type)
+{
+    switch (type) {
+    case BIOSMEM_TYPE_AVAILABLE:
+        return "available";
+    case BIOSMEM_TYPE_RESERVED:
+        return "reserved";
+    case BIOSMEM_TYPE_ACPI:
+        return "ACPI";
+    case BIOSMEM_TYPE_NVS:
+        return "ACPI NVS";
+    case BIOSMEM_TYPE_UNUSABLE:
+        return "unusable";
+    default:
+        return "unknown (reserved)";
+    }
+}
+
+static void __init
+biosmem_map_show(void)
+{
+    const struct biosmem_map_entry *entry, *end;
+
+    printf("biosmem: physical memory map:\n");
+
+    for (entry = biosmem_map, end = entry + biosmem_map_size;
+         entry < end;
+         entry++)
+        printf("biosmem: %018llx:%018llx, %s\n", entry->base_addr,
+               entry->base_addr + entry->length,
+               biosmem_type_desc(entry->type));
+
+    printf("biosmem: heap: %x-%x\n", biosmem_heap_start, biosmem_heap_end);
+}
+
+static void __init
+biosmem_load_segment(struct biosmem_segment *seg, uint64_t max_phys_end,
+                     phys_addr_t phys_start, phys_addr_t phys_end,
+                     phys_addr_t avail_start, phys_addr_t avail_end)
+{
+    unsigned int seg_index;
+
+    seg_index = seg - biosmem_segments;
+
+    if (phys_end > max_phys_end) {
+        if (max_phys_end <= phys_start) {
+            printf("biosmem: warning: segment %s physically unreachable, "
+                   "not loaded\n", vm_page_seg_name(seg_index));
+            return;
+        }
+
+        printf("biosmem: warning: segment %s truncated to %#llx\n",
+               vm_page_seg_name(seg_index), max_phys_end);
+        phys_end = max_phys_end;
+    }
+
+    if ((avail_start < phys_start) || (avail_start >= phys_end))
+        avail_start = phys_start;
+
+    if ((avail_end <= phys_start) || (avail_end > phys_end))
+        avail_end = phys_end;
+
+    seg->avail_start = avail_start;
+    seg->avail_end = avail_end;
+    vm_page_load(seg_index, phys_start, phys_end, avail_start, avail_end);
+}
+
+void __init
+biosmem_setup(void)
+{
+    struct biosmem_segment *seg;
+    unsigned int i;
+
+    biosmem_map_show();
+
+    for (i = 0; i < ARRAY_SIZE(biosmem_segments); i++) {
+        if (biosmem_segment_size(i) == 0)
+            break;
+
+        seg = &biosmem_segments[i];
+        biosmem_load_segment(seg, VM_PAGE_HIGHMEM_LIMIT, seg->start, seg->end,
+                             biosmem_heap_start, biosmem_heap_cur);
+    }
+}
+
+static void __init
+biosmem_free_usable_range(phys_addr_t start, phys_addr_t end)
+{
+    struct vm_page *page;
+
+    printf("biosmem: release to vm_page: %llx-%llx (%lluk)\n",
+           (unsigned long long)start, (unsigned long long)end,
+           (unsigned long long)((end - start) >> 10));
+
+    while (start < end) {
+        page = vm_page_lookup_pa(start);
+        assert(page != NULL);
+        vm_page_manage(page);
+        start += PAGE_SIZE;
+    }
+}
+
+static void __init
+biosmem_free_usable_update_start(phys_addr_t *start, phys_addr_t res_start,
+                                 phys_addr_t res_end)
+{
+    if ((*start >= res_start) && (*start < res_end))
+        *start = res_end;
+}
+
+static phys_addr_t __init
+biosmem_free_usable_start(phys_addr_t start)
+{
+    const struct biosmem_segment *seg;
+    unsigned int i;
+
+    biosmem_free_usable_update_start(&start, _kvtophys(&_start),
+                                     _kvtophys(&_end));
+    biosmem_free_usable_update_start(&start, biosmem_heap_start,
+                                     biosmem_heap_end);
+
+    for (i = 0; i < ARRAY_SIZE(biosmem_segments); i++) {
+        seg = &biosmem_segments[i];
+        biosmem_free_usable_update_start(&start, seg->avail_start,
+                                         seg->avail_end);
+    }
+
+    return start;
+}
+
+static int __init
+biosmem_free_usable_reserved(phys_addr_t addr)
+{
+    const struct biosmem_segment *seg;
+    unsigned int i;
+
+    if ((addr >= _kvtophys(&_start))
+        && (addr < _kvtophys(&_end)))
+        return 1;
+
+    if ((addr >= biosmem_heap_start) && (addr < biosmem_heap_end))
+        return 1;
+
+    for (i = 0; i < ARRAY_SIZE(biosmem_segments); i++) {
+        seg = &biosmem_segments[i];
+
+        if ((addr >= seg->avail_start) && (addr < seg->avail_end))
+            return 1;
+    }
+
+    return 0;
+}
+
+static phys_addr_t __init
+biosmem_free_usable_end(phys_addr_t start, phys_addr_t entry_end)
+{
+    while (start < entry_end) {
+        if (biosmem_free_usable_reserved(start))
+            break;
+
+        start += PAGE_SIZE;
+    }
+
+    return start;
+}
+
+static void __init
+biosmem_free_usable_entry(phys_addr_t start, phys_addr_t end)
+{
+    phys_addr_t entry_end;
+
+    entry_end = end;
+
+    for (;;) {
+        start = biosmem_free_usable_start(start);
+
+        if (start >= entry_end)
+            return;
+
+        end = biosmem_free_usable_end(start, entry_end);
+        biosmem_free_usable_range(start, end);
+        start = end;
+    }
+}
+
+void __init
+biosmem_free_usable(void)
+{
+    struct biosmem_map_entry *entry;
+    uint64_t start, end;
+    unsigned int i;
+
+    for (i = 0; i < biosmem_map_size; i++) {
+        entry = &biosmem_map[i];
+
+        if (entry->type != BIOSMEM_TYPE_AVAILABLE)
+            continue;
+
+        start = vm_page_round(entry->base_addr);
+
+        if (start >= VM_PAGE_HIGHMEM_LIMIT)
+            break;
+
+        end = vm_page_trunc(entry->base_addr + entry->length);
+
+        if (start < BIOSMEM_BASE)
+            start = BIOSMEM_BASE;
+
+        biosmem_free_usable_entry(start, end);
+    }
+}
diff --git a/i386/i386at/biosmem.h b/i386/i386at/biosmem.h
new file mode 100644
index 0000000..1db63f9
--- /dev/null
+++ b/i386/i386at/biosmem.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2010-2014 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _X86_BIOSMEM_H
+#define _X86_BIOSMEM_H
+
+#include <mach/machine/vm_types.h>
+#include <mach/machine/multiboot.h>
+
+/*
+ * Address where the address of the Extended BIOS Data Area segment can be
+ * found.
+ */
+#define BIOSMEM_EBDA_PTR 0x40e
+
+/*
+ * Significant low memory addresses.
+ *
+ * The first 64 KiB are reserved for various reasons (e.g. to preserve BIOS
+ * data and to work around data corruption on some hardware).
+ */
+#define BIOSMEM_BASE        0x010000
+#define BIOSMEM_BASE_END    0x0a0000
+#define BIOSMEM_EXT_ROM     0x0e0000
+#define BIOSMEM_ROM         0x0f0000
+#define BIOSMEM_END         0x100000
+
+/*
+ * Early initialization of the biosmem module.
+ *
+ * This function processes the given multiboot data for BIOS-provided
+ * memory information, and sets up a bootstrap physical page allocator.
+ *
+ * It is called before paging is enabled.
+ */
+#ifdef MACH_HYP
+void biosmem_xen_bootstrap(void);
+#else /* MACH_HYP */
+void biosmem_bootstrap(struct multiboot_raw_info *mbi);
+#endif /* MACH_HYP */
+
+/*
+ * Allocate contiguous physical pages during bootstrap.
+ *
+ * This function is called before paging is enabled. It should only be used
+ * to allocate initial page table pages. Those pages are later loaded into
+ * the VM system (as reserved pages) which means they can be freed like other
+ * regular pages. Users should fix up the type of those pages once the VM
+ * system is initialized.
+ */
+unsigned long biosmem_bootalloc(unsigned int nr_pages);
+
+/*
+ * Return the amount of physical memory that can be directly mapped.
+ *
+ * This includes the size of both the DMA/DMA32 and DIRECTMAP segments.
+ */
+phys_addr_t biosmem_directmap_size(void);
+
+/*
+ * Set up physical memory based on the information obtained during bootstrap
+ * and load it in the VM system.
+ */
+void biosmem_setup(void);
+
+/*
+ * Free all usable memory.
+ *
+ * This includes ranges that weren't part of the bootstrap allocator initial
+ * heap, e.g. because they contained boot data.
+ */
+void biosmem_free_usable(void);
+
+#endif /* _X86_BIOSMEM_H */
diff --git a/i386/i386at/elf.h b/i386/i386at/elf.h
new file mode 100644
index 0000000..26f4d87
--- /dev/null
+++ b/i386/i386at/elf.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _X86_ELF_H
+#define _X86_ELF_H
+
+#define ELF_SHT_SYMTAB  2
+#define ELF_SHT_STRTAB  3
+
+struct elf_shdr {
+    unsigned int name;
+    unsigned int type;
+    unsigned int flags;
+    unsigned long addr;
+    unsigned long offset;
+    unsigned int size;
+    unsigned int link;
+    unsigned int info;
+    unsigned int addralign;
+    unsigned int entsize;
+};
+
+#ifdef __LP64__
+
+struct elf_sym {
+    unsigned int name;
+    unsigned char info;
+    unsigned char other;
+    unsigned short shndx;
+    unsigned long value;
+    unsigned long size;
+};
+
+#else /* __LP64__ */
+
+struct elf_sym {
+    unsigned int name;
+    unsigned long value;
+    unsigned long size;
+    unsigned char info;
+    unsigned char other;
+    unsigned short shndx;
+};
+
+#endif /* __LP64__ */
+
+#endif /* _X86_ELF_H */
diff --git a/i386/i386at/kd_mouse.c b/i386/i386at/kd_mouse.c
index 0f1881f..ece13ef 100644
--- a/i386/i386at/kd_mouse.c
+++ b/i386/i386at/kd_mouse.c
@@ -630,6 +630,8 @@ int kd_mouse_read(void)
        while (mousebufindex <= mouse_char_index) {
            mouse_char_wanted = TRUE;
            assert_wait((event_t) &mousebuf, FALSE);
+           /* We are at tty SPL level, interrupts can not happen between
+            * assert_wait and thread_block.  */
            thread_block((void (*)()) 0);
        }
 
diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c
index 04cf695..62763ae 100644
--- a/i386/i386at/model_dep.c
+++ b/i386/i386at/model_dep.c
@@ -47,6 +47,7 @@
 #include <kern/cpu_number.h>
 #include <kern/debug.h>
 #include <kern/mach_clock.h>
+#include <kern/macros.h>
 #include <kern/printf.h>
 #include <kern/startup.h>
 #include <sys/time.h>
@@ -64,6 +65,7 @@
 #include <i386/locore.h>
 #include <i386/model_dep.h>
 #include <i386at/autoconf.h>
+#include <i386at/biosmem.h>
 #include <i386at/idt.h>
 #include <i386at/int_init.h>
 #include <i386at/kd.h>
@@ -127,27 +129,14 @@ struct multiboot_info boot_info;
 /* Command line supplied to kernel.  */
 char *kernel_cmdline = "";
 
-/* This is used for memory initialization:
-   it gets bumped up through physical memory
-   that exists and is not occupied by boot gunk.
-   It is not necessarily page-aligned.  */
-static vm_offset_t avail_next
-#ifndef MACH_HYP
-       = RESERVED_BIOS /* XX end of BIOS data area */
-#endif /* MACH_HYP */
-       ;
-
-/* Possibly overestimated amount of available memory
-   still remaining to be handed to the VM system.  */
-static vm_size_t avail_remaining;
-
 extern char    version[];
 
 /* If set, reboot the system on ctrl-alt-delete.  */
 boolean_t      rebootflag = FALSE;     /* exported to kdintr */
 
-/* XX interrupt stack pointer and highwater mark, for locore.S.  */
-vm_offset_t int_stack_top, int_stack_high;
+/* Interrupt stack.  */
+static char int_stack[KERNEL_STACK_SIZE] __aligned(KERNEL_STACK_SIZE);
+vm_offset_t int_stack_top, int_stack_base;
 
 #ifdef LINUX_DEV
 extern void linux_init(void);
@@ -275,91 +264,6 @@ void db_reset_cpu(void)
        halt_all_cpus(1);
 }
 
-
-/*
- * Compute physical memory size and other parameters.
- */
-void
-mem_size_init(void)
-{
-       vm_offset_t max_phys_size;
-
-       /* Physical memory on all PCs starts at physical address 0.
-          XX make it a constant.  */
-       phys_first_addr = 0;
-
-#ifdef MACH_HYP
-       if (boot_info.nr_pages >= 0x100000) {
-               printf("Truncating memory size to 4GiB\n");
-               phys_last_addr = 0xffffffffU;
-       } else
-               phys_last_addr = boot_info.nr_pages * 0x1000;
-#else  /* MACH_HYP */
-       vm_size_t phys_last_kb;
-
-       if (boot_info.flags & MULTIBOOT_MEM_MAP) {
-               struct multiboot_mmap *map, *map_end;
-
-               map = (void*) phystokv(boot_info.mmap_addr);
-               map_end = (void*) map + boot_info.mmap_count;
-
-               while (map + 1 <= map_end) {
-                       if (map->Type == MB_ARD_MEMORY) {
-                               unsigned long long start = map->BaseAddr, end = 
map->BaseAddr + map->Length;;
-
-                               if (start >= 0x100000000ULL) {
-                                       printf("Ignoring %luMiB RAM region 
above 4GiB\n", (unsigned long) (map->Length >> 20));
-                               } else {
-                                       if (end >= 0x100000000ULL) {
-                                               printf("Truncating memory 
region to 4GiB\n");
-                                               end = 0x0ffffffffU;
-                                       }
-                                       if (end > phys_last_addr)
-                                               phys_last_addr = end;
-
-                                       printf("AT386 boot: physical memory map 
from 0x%lx to 0x%lx\n",
-                                               (unsigned long) start,
-                                               (unsigned long) end);
-                               }
-                       }
-                       map = (void*) map + map->size + sizeof(map->size);
-               }
-       } else {
-               phys_last_kb = 0x400 + boot_info.mem_upper;
-               /* Avoid 4GiB overflow.  */
-               if (phys_last_kb < 0x400 || phys_last_kb >= 0x400000) {
-                       printf("Truncating memory size to 4GiB\n");
-                       phys_last_addr = 0xffffffffU;
-               } else
-                       phys_last_addr = phys_last_kb * 0x400;
-       }
-#endif /* MACH_HYP */
-
-       printf("AT386 boot: physical memory from 0x%lx to 0x%lx\n",
-              phys_first_addr, phys_last_addr);
-
-       /* Reserve room for virtual mappings.
-        * Yes, this loses memory.  Blame i386.  */
-       max_phys_size = VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS - 
VM_KERNEL_MAP_SIZE;
-       if (phys_last_addr - phys_first_addr > max_phys_size) {
-               phys_last_addr = phys_first_addr + max_phys_size;
-               printf("Truncating memory to %luMiB\n", (phys_last_addr - 
phys_first_addr) / (1024 * 1024));
-               /* TODO Xen: be nice, free lost memory */
-       }
-
-       phys_first_addr = round_page(phys_first_addr);
-       phys_last_addr = trunc_page(phys_last_addr);
-
-#ifdef MACH_HYP
-       /* Memory is just contiguous */
-       avail_remaining = phys_last_addr;
-#else  /* MACH_HYP */
-       avail_remaining
-         = phys_last_addr - (0x100000 - (boot_info.mem_lower * 0x400)
-                             - RESERVED_BIOS);
-#endif /* MACH_HYP */
-}
-
 /*
  * Basic PC VM initialization.
  * Turns on paging and changes the kernel segments to use high linear 
addresses.
@@ -382,9 +286,13 @@ i386at_init(void)
 #endif /* MACH_HYP */
 
        /*
-        * Find memory size parameters.
+        * Read memory map and load it into the physical page allocator.
         */
-       mem_size_init();
+#ifdef MACH_HYP
+       biosmem_xen_bootstrap();
+#else /* MACH_HYP */
+       biosmem_bootstrap((struct multiboot_raw_info *) &boot_info);
+#endif /* MACH_HYP */
 
 #ifdef MACH_XEN
        kernel_cmdline = (char*) boot_info.cmd_line;
@@ -432,6 +340,13 @@ i386at_init(void)
        pmap_bootstrap();
 
        /*
+        *      Load physical segments into the VM system.
+        *      The early allocation functions become unusable after
+        *      this point.
+        */
+       biosmem_setup();
+
+       /*
         * We'll have to temporarily install a direct mapping
         * between physical memory and low linear memory,
         * until we start using our new kernel segment descriptors.
@@ -485,11 +400,6 @@ i386at_init(void)
        pmap_clear_bootstrap_pagetable((void *)boot_info.pt_base);
 #endif /* MACH_PV_PAGETABLES */
 
-       /* Interrupt stacks are allocated in physical memory,
-          while kernel stacks are allocated in kernel virtual memory,
-          so phys_last_addr serves as a convenient dividing point.  */
-       int_stack_high = phystokv(phys_last_addr);
-
        /*
         * Initialize and activate the real i386 protected-mode structures.
         */
@@ -535,10 +445,8 @@ i386at_init(void)
        hyp_p2m_init();
 #endif /* MACH_XEN */
 
-       /* XXX We'll just use the initialization stack we're already running on
-          as the interrupt stack for now.  Later this will have to change,
-          because the init stack will get freed after bootup.  */
-       asm("movl %%esp,%0" : "=m" (int_stack_top));
+       int_stack_base = (vm_offset_t)&int_stack;
+       int_stack_top = int_stack_base + KERNEL_STACK_SIZE - 4;
 }
 
 /*
@@ -706,187 +614,20 @@ resettodr(void)
 
 unsigned int pmap_free_pages(void)
 {
-       return atop(avail_remaining);
+       return vm_page_atop(phys_last_addr); /* XXX */
 }
 
-/* Always returns page-aligned regions.  */
 boolean_t
 init_alloc_aligned(vm_size_t size, vm_offset_t *addrp)
 {
-       vm_offset_t addr;
-
-#ifdef MACH_HYP
-       /* There is none */
-       if (!avail_next)
-               avail_next = _kvtophys(boot_info.pt_base) + 
(boot_info.nr_pt_frames + 3) * 0x1000;
-#else  /* MACH_HYP */
-       extern char start[], end[];
-       int i;
-       static int wrapped = 0;
-
-       /* Memory regions to skip.  */
-       vm_offset_t cmdline_start_pa = boot_info.flags & MULTIBOOT_CMDLINE
-               ? boot_info.cmdline : 0;
-       vm_offset_t cmdline_end_pa = cmdline_start_pa
-               ? cmdline_start_pa+strlen((char*)phystokv(cmdline_start_pa))+1
-               : 0;
-       vm_offset_t mods_start_pa = boot_info.flags & MULTIBOOT_MODS
-               ? boot_info.mods_addr : 0;
-       vm_offset_t mods_end_pa = mods_start_pa
-               ? mods_start_pa
-                 + boot_info.mods_count * sizeof(struct multiboot_module)
-               : 0;
-
-       retry:
-#endif /* MACH_HYP */
-
-       /* Page-align the start address.  */
-       avail_next = round_page(avail_next);
+       *addrp = biosmem_bootalloc(vm_page_atop(vm_page_round(size)));
 
-#ifndef MACH_HYP
-       /* Start with memory above 16MB, reserving the low memory for later. */
-       /* Don't care on Xen */
-       if (!wrapped && phys_last_addr > 16 * 1024*1024)
-         {
-           if (avail_next < 16 * 1024*1024)
-             avail_next = 16 * 1024*1024;
-           else if (avail_next == phys_last_addr)
-             {
-               /* We have used all the memory above 16MB, so now start on
-                  the low memory.  This will wind up at the end of the list
-                  of free pages, so it should not have been allocated to any
-                  other use in early initialization before the Linux driver
-                  glue initialization needs to allocate low memory.  */
-               avail_next = RESERVED_BIOS;
-               wrapped = 1;
-             }
-         }
-#endif /* MACH_HYP */
-
-       /* Check if we have reached the end of memory.  */
-        if (avail_next == 
-               (
-#ifndef MACH_HYP
-               wrapped ? 16 * 1024*1024 : 
-#endif /* MACH_HYP */
-               phys_last_addr))
+       if (*addrp == 0)
                return FALSE;
 
-       /* Tentatively assign the current location to the caller.  */
-       addr = avail_next;
-
-       /* Bump the pointer past the newly allocated region
-          and see where that puts us.  */
-       avail_next += size;
-
-#ifndef MACH_HYP
-       /* Skip past the I/O and ROM area.  */
-       if (boot_info.flags & MULTIBOOT_MEM_MAP)
-       {
-               struct multiboot_mmap *map, *map_end, *current = NULL, *next = 
NULL;
-               unsigned long long minimum_next = ~0ULL;
-
-               map = (void*) phystokv(boot_info.mmap_addr);
-               map_end = (void*) map + boot_info.mmap_count;
-
-               /* Find both our current map, and the next one */
-               while (map + 1 <= map_end)
-               {
-                       if (map->Type == MB_ARD_MEMORY)
-                       {
-                               unsigned long long start = map->BaseAddr;
-                               unsigned long long end = start + map->Length;;
-
-                               if (start <= addr && avail_next <= end)
-                               {
-                                       /* Ok, fits in the current map */
-                                       current = map;
-                                       break;
-                               }
-                               else if (avail_next <= start && start < 
minimum_next)
-                               {
-                                       /* This map is not far from avail_next 
*/
-                                       next = map;
-                                       minimum_next = start;
-                               }
-                       }
-                       map = (void*) map + map->size + sizeof(map->size);
-               }
-
-               if (!current) {
-                       /* Area does not fit in the current map, switch to next
-                        * map if any */
-                       if (!next || next->BaseAddr >= phys_last_addr)
-                       {
-                               /* No further reachable map, we have reached
-                                * the end of memory, but possibly wrap around
-                                * 16MiB. */
-                               avail_next = phys_last_addr;
-                               goto retry;
-                       }
-
-                       /* Start from next map */
-                       avail_next = next->BaseAddr;
-                       goto retry;
-               }
-       }
-       else if ((avail_next > (boot_info.mem_lower * 0x400)) && (addr < 
0x100000))
-       {
-               avail_next = 0x100000;
-               goto retry;
-       }
-
-       /* Skip our own kernel code, data, and bss.  */
-       if ((phystokv(avail_next) > (vm_offset_t)start) && (phystokv(addr) < 
(vm_offset_t)end))
-       {
-               avail_next = _kvtophys(end);
-               goto retry;
-       }
-
-       /* Skip any areas occupied by valuable boot_info data.  */
-       if ((avail_next > cmdline_start_pa) && (addr < cmdline_end_pa))
-       {
-               avail_next = cmdline_end_pa;
-               goto retry;
-       }
-       if ((avail_next > mods_start_pa) && (addr < mods_end_pa))
-       {
-               avail_next = mods_end_pa;
-               goto retry;
-       }
-       if ((phystokv(avail_next) > kern_sym_start) && (phystokv(addr) < 
kern_sym_end))
-       {
-               avail_next = _kvtophys(kern_sym_end);
-               goto retry;
-       }
-       if (boot_info.flags & MULTIBOOT_MODS)
-       {
-               struct multiboot_module *m = (struct multiboot_module *)
-                       phystokv(boot_info.mods_addr);
-               for (i = 0; i < boot_info.mods_count; i++)
-               {
-                       if ((avail_next > m[i].mod_start)
-                           && (addr < m[i].mod_end))
-                       {
-                               avail_next = m[i].mod_end;
-                               goto retry;
-                       }
-                       /* XXX string */
-               }
-       }
-#endif /* MACH_HYP */
-
-       avail_remaining -= size;
-
-       *addrp = addr;
        return TRUE;
 }
 
-boolean_t pmap_next_page(vm_offset_t *addrp)
-{
-       return init_alloc_aligned(PAGE_SIZE, addrp);
-}
-
 /* Grab a physical page:
    the standard memory allocation mechanism
    during system initialization.  */
@@ -894,7 +635,7 @@ vm_offset_t
 pmap_grab_page(void)
 {
        vm_offset_t addr;
-       if (!pmap_next_page(&addr))
+       if (!init_alloc_aligned(PAGE_SIZE, &addr))
                panic("Not enough memory to initialize Mach");
        return addr;
 }
diff --git a/i386/i386at/model_dep.h b/i386/i386at/model_dep.h
index aa24032..47551b8 100644
--- a/i386/i386at/model_dep.h
+++ b/i386/i386at/model_dep.h
@@ -19,8 +19,17 @@
 #ifndef _MODEL_DEP_H_
 #define _MODEL_DEP_H_
 
+#include <i386/vm_param.h>
 #include <mach/vm_prot.h>
 
+/*
+ * Interrupt stack.
+ */
+extern vm_offset_t int_stack_top, int_stack_base;
+
+/* Check whether P points to the interrupt stack.  */
+#define ON_INT_STACK(P)        (((P) & ~(KERNEL_STACK_SIZE-1)) == 
int_stack_base)
+
 extern int timemmap(dev_t dev, vm_offset_t off, vm_prot_t prot);
 
 void inittodr(void);
diff --git a/i386/include/mach/i386/multiboot.h 
b/i386/include/mach/i386/multiboot.h
index 8f1c47b..c66ca03 100644
--- a/i386/include/mach/i386/multiboot.h
+++ b/i386/include/mach/i386/multiboot.h
@@ -188,5 +188,110 @@ struct multiboot_mmap
 /* usable memory "Type", all others are reserved.  */
 #define MB_ARD_MEMORY       1
 
+/*
+ * Copyright (c) 2010, 2012 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Versions used by the biosmem module.
+ */
+
+#include <kern/macros.h>
+
+/*
+ * Magic number provided by the OS to the boot loader.
+ */
+#define MULTIBOOT_OS_MAGIC 0x1badb002
+
+/*
+ * Multiboot flags requesting services from the boot loader.
+ */
+#define MULTIBOOT_OS_MEMORY_INFO 0x2
+
+#define MULTIBOOT_OS_FLAGS MULTIBOOT_OS_MEMORY_INFO
+
+/*
+ * Magic number to identify a multiboot compliant boot loader.
+ */
+#define MULTIBOOT_LOADER_MAGIC 0x2badb002
+
+/*
+ * Multiboot flags set by the boot loader.
+ */
+#define MULTIBOOT_LOADER_MEMORY     0x01
+#define MULTIBOOT_LOADER_CMDLINE    0x04
+#define MULTIBOOT_LOADER_MODULES    0x08
+#define MULTIBOOT_LOADER_SHDR       0x20
+#define MULTIBOOT_LOADER_MMAP       0x40
+
+/*
+ * A multiboot module.
+ */
+struct multiboot_raw_module {
+    uint32_t mod_start;
+    uint32_t mod_end;
+    uint32_t string;
+    uint32_t reserved;
+} __packed;
+
+/*
+ * Memory map entry.
+ */
+struct multiboot_raw_mmap_entry {
+    uint32_t size;
+    uint64_t base_addr;
+    uint64_t length;
+    uint32_t type;
+} __packed;
+
+/*
+ * Multiboot information structure as passed by the boot loader.
+ */
+struct multiboot_raw_info {
+    uint32_t flags;
+    uint32_t mem_lower;
+    uint32_t mem_upper;
+    uint32_t unused0;
+    uint32_t cmdline;
+    uint32_t mods_count;
+    uint32_t mods_addr;
+    uint32_t shdr_num;
+    uint32_t shdr_size;
+    uint32_t shdr_addr;
+    uint32_t shdr_strndx;
+    uint32_t mmap_length;
+    uint32_t mmap_addr;
+    uint32_t unused1[9];
+} __packed;
+
+/*
+ * Versions of the multiboot structures suitable for use with 64-bit pointers.
+ */
+
+struct multiboot_os_module {
+    void *mod_start;
+    void *mod_end;
+    char *string;
+};
+
+struct multiboot_os_info {
+    uint32_t flags;
+    char *cmdline;
+    struct multiboot_module *mods_addr;
+    uint32_t mods_count;
+};
 
 #endif /* _MACH_I386_MULTIBOOT_H_ */
diff --git a/i386/include/mach/i386/vm_types.h 
b/i386/include/mach/i386/vm_types.h
index 1439940..4a58b1c 100644
--- a/i386/include/mach/i386/vm_types.h
+++ b/i386/include/mach/i386/vm_types.h
@@ -77,6 +77,15 @@ typedef      unsigned long   vm_offset_t;
 typedef        vm_offset_t *   vm_offset_array_t;
 
 /*
+ * A type for physical addresses.
+ */
+#ifdef PAE
+typedef unsigned long long phys_addr_t;
+#else /* PAE */
+typedef unsigned long phys_addr_t;
+#endif /* PAE */
+
+/*
  * A vm_size_t is the proper type for e.g.
  * expressing the difference between two
  * vm_offset_t entities.
diff --git a/i386/intel/pmap.c b/i386/intel/pmap.c
index 22e20c9..0771a08 100644
--- a/i386/intel/pmap.c
+++ b/i386/intel/pmap.c
@@ -978,9 +978,9 @@ void pmap_init(void)
         *      and of the physical-to-virtual entries.
         */
        s = (vm_size_t) sizeof(struct pmap);
-       kmem_cache_init(&pmap_cache, "pmap", s, 0, NULL, NULL, NULL, 0);
+       kmem_cache_init(&pmap_cache, "pmap", s, 0, NULL, 0);
        s = (vm_size_t) sizeof(struct pv_entry);
-       kmem_cache_init(&pv_list_cache, "pv_entry", s, 0, NULL, NULL, NULL, 0);
+       kmem_cache_init(&pv_list_cache, "pv_entry", s, 0, NULL, 0);
 
 #if    NCPUS > 1
        /*
diff --git a/include/mach/mach.defs b/include/mach/mach.defs
index 3786f65..20dc863 100644
--- a/include/mach/mach.defs
+++ b/include/mach/mach.defs
@@ -58,6 +58,9 @@ skip; /* old port_select */
 skip;  /* old port_set_backlog */
 skip;  /* old port_status */
 
+/* We use only a handful of RPCs as client.  Skip the rest.  */
+#if    ! KERNEL_USER
+
 /*
  *     Create a new task with an empty set of IPC rights,
  *     and having an address space constructed from the
@@ -310,6 +313,18 @@ skip;      /* old u*x_pid */
 skip;  /* old netipc_listen */
 skip;  /* old netipc_ignore */
 
+#else  /* ! KERNEL_USER */
+
+skip;  skip;   skip;   skip;   skip;
+skip;  skip;   skip;   skip;   skip;
+skip;  skip;   skip;   skip;   skip;
+skip;  skip;   skip;   skip;   skip;
+skip;  skip;   skip;   skip;   skip;
+skip;  skip;   skip;   skip;   skip;
+skip;
+
+#endif /* ! KERNEL_USER */
+
 /*
  *     Provide the data contents of a range of the given memory
  *     object, with the access restriction specified.  [Only
@@ -351,6 +366,8 @@ routine memory_object_get_attributes(
        out     may_cache       : boolean_t;
        out     copy_strategy   : memory_object_copy_strategy_t);
 
+#if    ! KERNEL_USER
+
 /*
  *     Sets the default memory manager, the port to which
  *     newly-created temporary memory objects are delivered.
@@ -361,6 +378,12 @@ routine vm_set_default_memory_manager(
                host_priv       : host_priv_t;
        inout   default_manager : mach_port_make_send_t);
 
+#else  /* ! KERNEL_USER */
+
+skip;
+
+#endif /* ! KERNEL_USER */
+
 skip;  /* old pager_flush_request */
 
 /*
@@ -413,6 +436,8 @@ skip;       /* old netport_enter */
 skip;  /* old netport_remove */
 skip;  /* old thread_set_priority */
 
+#if    ! KERNEL_USER
+
 /*
  *     Increment the suspend count for the target task.
  *     No threads within a task may run when the suspend
@@ -613,6 +638,18 @@ routine vm_map(
                inheritance     : vm_inherit_t);
 #endif /* EMULATOR */
 
+#else  /* ! KERNEL_USER */
+
+skip;  skip;   skip;   skip;   skip;
+skip;  skip;   skip;   skip;   skip;
+skip;  skip;   skip;   skip;   skip;
+skip;  skip;   skip;   skip;   skip;
+skip;  skip;   skip;   skip;   skip;
+skip;  skip;   skip;   skip;   skip;
+skip;  skip;   skip;   skip;
+
+#endif /* ! KERNEL_USER */
+
 /*
  *     Indicate that a range of the specified memory object cannot
  *     be provided at this time.  [Threads waiting for memory pages
@@ -684,6 +721,8 @@ simpleroutine memory_object_change_attributes(
                reply_to        : mach_port_t =
                        MACH_MSG_TYPE_MAKE_SEND_ONCE|polymorphic);
 
+#if ! KERNEL_USER
+
 skip;  /* old host_callout_statistics_reset */
 skip;  /* old port_set_select */
 skip;  /* old port_set_backup */
@@ -702,6 +741,8 @@ routine vm_machine_attribute(
 
 skip;  /* old host_fpa_counters_reset */
 
+#endif /* ! KERNEL_USER */
+
 /*
  *     There is no more room in this interface for additional calls.
  */
diff --git a/include/mach_debug/slab_info.h b/include/mach_debug/slab_info.h
index 37dcb8c..7d12cc1 100644
--- a/include/mach_debug/slab_info.h
+++ b/include/mach_debug/slab_info.h
@@ -36,12 +36,6 @@
 
 #define CACHE_NAME_MAX_LEN 32
 
-#define CACHE_FLAGS_NO_CPU_POOL                0x01
-#define CACHE_FLAGS_SLAB_EXTERNAL      0x02
-#define CACHE_FLAGS_NO_RECLAIM         0x04
-#define CACHE_FLAGS_VERIFY             0x08
-#define CACHE_FLAGS_DIRECT             0x10
-
 typedef struct cache_info {
        int flags;
        size_t cpu_pool_size;
diff --git a/ipc/ipc_init.c b/ipc/ipc_init.c
index 2c58a6e..5ed800f 100644
--- a/ipc/ipc_init.c
+++ b/ipc/ipc_init.c
@@ -73,16 +73,16 @@ ipc_bootstrap(void)
        ipc_port_timestamp_data = 0;
 
        kmem_cache_init(&ipc_space_cache, "ipc_space",
-                       sizeof(struct ipc_space), 0, NULL, NULL, NULL, 0);
+                       sizeof(struct ipc_space), 0, NULL, 0);
 
        kmem_cache_init(&ipc_entry_cache, "ipc_entry",
-                       sizeof(struct ipc_entry), 0, NULL, NULL, NULL, 0);
+                       sizeof(struct ipc_entry), 0, NULL, 0);
 
        kmem_cache_init(&ipc_object_caches[IOT_PORT], "ipc_port",
-                       sizeof(struct ipc_port), 0, NULL, NULL, NULL, 0);
+                       sizeof(struct ipc_port), 0, NULL, 0);
 
        kmem_cache_init(&ipc_object_caches[IOT_PORT_SET], "ipc_pset",
-                       sizeof(struct ipc_pset), 0, NULL, NULL, NULL, 0);
+                       sizeof(struct ipc_pset), 0, NULL, 0);
 
        /* create special spaces */
 
diff --git a/ipc/ipc_marequest.c b/ipc/ipc_marequest.c
index ded1711..736db83 100644
--- a/ipc/ipc_marequest.c
+++ b/ipc/ipc_marequest.c
@@ -137,7 +137,7 @@ ipc_marequest_init(void)
        }
 
        kmem_cache_init(&ipc_marequest_cache, "ipc_marequest",
-                       sizeof(struct ipc_marequest), 0, NULL, NULL, NULL, 0);
+                       sizeof(struct ipc_marequest), 0, NULL, 0);
 }
 
 /*
diff --git a/ipc/ipc_object.c b/ipc/ipc_object.c
index 320fbcb..a6457c3 100644
--- a/ipc/ipc_object.c
+++ b/ipc/ipc_object.c
@@ -998,7 +998,8 @@ char *ikot_print_array[IKOT_MAX_TYPE] = {
        "(SEMAPHORE)        ",
        "(LOCK_SET)         ",
        "(CLOCK)            ",
-       "(CLOCK_CTRL)       ",  /* 26 */
+       "(CLOCK_CTRL)       ",
+       "(PAGER_PROXY)      ",  /* 27 */
                                /* << new entries here  */
        "(UNKNOWN)     "        /* magic catchall       */
 };     /* Please keep in sync with kern/ipc_kobject.h  */
diff --git a/kern/act.c b/kern/act.c
index 3186f7e..3819ef3 100644
--- a/kern/act.c
+++ b/kern/act.c
@@ -68,7 +68,7 @@ global_act_init(void)
 {
 #ifndef ACT_STATIC_KLUDGE
        kmem_cache_init(&act_cache, "Act", sizeof(struct Act), 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
 #else
        int i;
 
diff --git a/kern/bootstrap.c b/kern/bootstrap.c
index 249c605..0836276 100644
--- a/kern/bootstrap.c
+++ b/kern/bootstrap.c
@@ -107,6 +107,20 @@ task_insert_send_right(
        return name;
 }
 
+static void
+free_bootstrap_pages(phys_addr_t start, phys_addr_t end)
+{
+  struct vm_page *page;
+
+  while (start < end)
+    {
+      page = vm_page_lookup_pa(start);
+      assert(page != NULL);
+      vm_page_manage(page);
+      start += PAGE_SIZE;
+    }
+}
+
 void bootstrap_create(void)
 {
   int compat;
@@ -265,7 +279,7 @@ void bootstrap_create(void)
   /* XXX we could free the memory used
      by the boot loader's descriptors and such.  */
   for (n = 0; n < boot_info.mods_count; n++)
-    vm_page_create(bmods[n].mod_start, bmods[n].mod_end);
+    free_bootstrap_pages(bmods[n].mod_start, bmods[n].mod_end);
 }
 
 static void
diff --git a/kern/cpu_number.h b/kern/cpu_number.h
index 44bbd64..650f404 100644
--- a/kern/cpu_number.h
+++ b/kern/cpu_number.h
@@ -37,5 +37,7 @@ int   master_cpu;     /* 'master' processor - keeps time */
        /* cpu number is always 0 on a single processor system */
 #define        cpu_number()    (0)
 
+#define CPU_L1_SIZE (1 << CPU_L1_SHIFT)
+
 #endif /* NCPUS == 1 */
 #endif /* _KERN_CPU_NUMBER_H_ */
diff --git a/i386/i386at/model_dep.h b/kern/exc.defs
similarity index 56%
copy from i386/i386at/model_dep.h
copy to kern/exc.defs
index aa24032..e614fff 100644
--- a/i386/i386at/model_dep.h
+++ b/kern/exc.defs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Free Software Foundation.
+ * Copyright (c) 2016 Free Software Foundation.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -11,20 +11,12 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef _MODEL_DEP_H_
-#define _MODEL_DEP_H_
+/* We use custom functions to send exceptions.  These functions can
+   be found in `exception.c'.  We use this file merely to produce the
+   list of message ids.  */
 
-#include <mach/vm_prot.h>
-
-extern int timemmap(dev_t dev, vm_offset_t off, vm_prot_t prot);
-
-void inittodr(void);
-
-boolean_t init_alloc_aligned(vm_size_t size, vm_offset_t *addrp);
-
-#endif /* _MODEL_DEP_H_ */
+#include <mach/exc.defs>
diff --git a/kern/log2.h b/kern/log2.h
new file mode 100644
index 0000000..0e67701
--- /dev/null
+++ b/kern/log2.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Integer base 2 logarithm operations.
+ */
+
+#ifndef _KERN_LOG2_H
+#define _KERN_LOG2_H
+
+#include <kern/assert.h>
+
+#ifdef __LP64__
+#define LONG_BIT 64
+#else /* __LP64__ */
+#define LONG_BIT 32
+#endif /* __LP64__ */
+
+static inline unsigned int
+ilog2(unsigned long x)
+{
+    assert(x != 0);
+    return LONG_BIT - __builtin_clzl(x) - 1;
+}
+
+static inline unsigned int
+iorder2(unsigned long size)
+{
+    assert(size != 0);
+
+    if (size == 1)
+        return 0;
+
+    return ilog2(size - 1) + 1;
+}
+
+#endif /* _KERN_LOG2_H */
diff --git a/kern/processor.c b/kern/processor.c
index 48e9273..0a88469 100644
--- a/kern/processor.c
+++ b/kern/processor.c
@@ -109,7 +109,7 @@ void pset_sys_init(void)
         * Allocate the cache for processor sets.
         */
        kmem_cache_init(&pset_cache, "processor_set",
-                       sizeof(struct processor_set), 0, NULL, NULL, NULL, 0);
+                       sizeof(struct processor_set), 0, NULL, 0);
 
        /*
         * Give each processor a control port.
diff --git a/kern/rdxtree.c b/kern/rdxtree.c
index 78868b1..a23d6e7 100644
--- a/kern/rdxtree.c
+++ b/kern/rdxtree.c
@@ -124,7 +124,7 @@ void
 rdxtree_cache_init(void)
 {
     kmem_cache_init(&rdxtree_node_cache, "rdxtree_node",
-                   sizeof(struct rdxtree_node), 0, NULL, NULL, NULL, 0);
+                   sizeof(struct rdxtree_node), 0, NULL, 0);
 }
 
 #ifdef RDXTREE_ENABLE_NODE_CREATION_FAILURES
diff --git a/kern/sched_prim.c b/kern/sched_prim.c
index 580ca43..0cef160 100644
--- a/kern/sched_prim.c
+++ b/kern/sched_prim.c
@@ -446,6 +446,9 @@ void thread_wakeup_prim(
  *     occurs.  The specified lock is unlocked before releasing
  *     the cpu.  (This is a convenient way to sleep without manually
  *     calling assert_wait).
+ *
+ *     Note: if the event may be woken from an interrupt handler, this must be
+ *     called at an spl level that prevents such interrupts.
  */
 void thread_sleep(
        event_t         event,
diff --git a/kern/slab.c b/kern/slab.c
index 8bc1b06..eeb94f8 100644
--- a/kern/slab.c
+++ b/kern/slab.c
@@ -58,15 +58,10 @@
  * over a hash table. Unlike a hash table, a BST provides a "lookup nearest"
  * operation, so obtaining the slab data (whether it is embedded in the slab or
  * off slab) from a buffer address simply consists of a "lookup nearest towards
- * 0" tree search. Storing slabs instead of buffers also considerably reduces
- * the number of elements to retain. Finally, a self-balancing tree is a true
- * self-scaling data structure, whereas a hash table requires periodic
- * maintenance and complete resizing, which is expensive. The only drawback is
- * that releasing a buffer to the slab layer takes logarithmic time instead of
- * constant time. But as the data set size is kept reasonable (because slabs
- * are stored instead of buffers) and because the CPU pool layer services most
- * requests, avoiding many accesses to the slab layer, it is considered an
- * acceptable tradeoff.
+ * 0" tree search. Finally, a self-balancing tree is a true self-scaling data
+ * structure, whereas a hash table requires periodic maintenance and complete
+ * resizing, which is expensive. The only drawback is that releasing a buffer
+ * to the slab layer takes logarithmic time instead of constant time.
  *
  * This implementation uses per-cpu pools of objects, which service most
  * allocation requests. These pools act as caches (but are named differently
@@ -87,6 +82,7 @@
 #include <mach/vm_param.h>
 #include <mach/machine/vm_types.h>
 #include <vm/vm_kern.h>
+#include <vm/vm_page.h>
 #include <vm/vm_types.h>
 #include <sys/types.h>
 
@@ -111,19 +107,6 @@
 #define KMEM_ALIGN_MIN 8
 
 /*
- * Minimum number of buffers per slab.
- *
- * This value is ignored when the slab size exceeds a threshold.
- */
-#define KMEM_MIN_BUFS_PER_SLAB 8
-
-/*
- * Special slab size beyond which the minimum number of buffers per slab is
- * ignored when computing the slab size of a cache.
- */
-#define KMEM_SLAB_SIZE_THRESHOLD (8 * PAGE_SIZE)
-
-/*
  * Special buffer size under which slab data is unconditionnally allocated
  * from its associated slab.
  */
@@ -163,11 +146,6 @@
 #define KMEM_REDZONE_BYTE 0xbb
 
 /*
- * Size of the VM submap from which default backend functions allocate.
- */
-#define KMEM_MAP_SIZE (128 * 1024 * 1024)
-
-/*
  * Shift for the first kalloc cache size.
  */
 #define KALLOC_FIRST_SHIFT 5
@@ -217,11 +195,17 @@
  *
  * The flags don't change once set and can be tested without locking.
  */
-#define KMEM_CF_NO_CPU_POOL     0x01    /* CPU pool layer disabled */
-#define KMEM_CF_SLAB_EXTERNAL   0x02    /* Slab data is off slab */
-#define KMEM_CF_NO_RECLAIM      0x04    /* Slabs are not reclaimable */
-#define KMEM_CF_VERIFY          0x08    /* Debugging facilities enabled */
-#define KMEM_CF_DIRECT          0x10    /* No buf-to-slab tree lookup */
+#define KMEM_CF_SLAB_EXTERNAL   0x01    /* Slab data is off slab */
+#define KMEM_CF_PHYSMEM         0x02    /* Allocate from physical memory */
+#define KMEM_CF_DIRECT          0x04    /* Direct buf-to-slab translation
+                                           (implies !KMEM_CF_SLAB_EXTERNAL) */
+#define KMEM_CF_USE_TREE        0x08    /* Use red-black tree to track slab
+                                           data */
+#define KMEM_CF_USE_PAGE        0x10    /* Use page private data to track slab
+                                           data (implies KMEM_CF_SLAB_EXTERNAL
+                                           and KMEM_CF_PHYSMEM) */
+#define KMEM_CF_VERIFY          0x20    /* Debugging facilities enabled
+                                           (implies KMEM_CF_USE_TREE) */
 
 /*
  * Options for kmem_cache_alloc_verify().
@@ -278,12 +262,6 @@ static unsigned int kmem_nr_caches;
 static simple_lock_data_t __attribute__((used)) kmem_cache_list_lock;
 
 /*
- * VM submap for slab caches.
- */
-static struct vm_map kmem_map_store;
-vm_map_t kmem_map = &kmem_map_store;
-
-/*
  * Time of the last memory reclaim, in clock ticks.
  */
 static unsigned long kmem_gc_last_tick;
@@ -384,12 +362,49 @@ static inline void * kmem_bufctl_to_buf(union kmem_bufctl 
*bufctl,
     return (void *)bufctl - cache->bufctl_dist;
 }
 
-static vm_offset_t kmem_pagealloc(vm_size_t size)
+static vm_offset_t
+kmem_pagealloc_physmem(vm_size_t size)
+{
+    struct vm_page *page;
+
+    assert(size == PAGE_SIZE);
+
+    for (;;) {
+        page = vm_page_grab_contig(size, VM_PAGE_SEL_DIRECTMAP);
+
+        if (page != NULL)
+            break;
+
+        VM_PAGE_WAIT(NULL);
+    }
+
+    return phystokv(vm_page_to_pa(page));
+}
+
+static void
+kmem_pagefree_physmem(vm_offset_t addr, vm_size_t size)
+{
+    struct vm_page *page;
+
+    assert(size == PAGE_SIZE);
+    page = vm_page_lookup_pa(kvtophys(addr));
+    assert(page != NULL);
+    vm_page_free_contig(page, size);
+}
+
+static vm_offset_t
+kmem_pagealloc_virtual(vm_size_t size, vm_size_t align)
 {
     vm_offset_t addr;
     kern_return_t kr;
 
-    kr = kmem_alloc_wired(kmem_map, &addr, size);
+    assert(size > PAGE_SIZE);
+    size = vm_page_round(size);
+
+    if (align <= PAGE_SIZE)
+        kr = kmem_alloc_wired(kernel_map, &addr, size);
+    else
+        kr = kmem_alloc_aligned(kernel_map, &addr, size);
 
     if (kr != KERN_SUCCESS)
         return 0;
@@ -397,9 +412,29 @@ static vm_offset_t kmem_pagealloc(vm_size_t size)
     return addr;
 }
 
-static void kmem_pagefree(vm_offset_t ptr, vm_size_t size)
+static void
+kmem_pagefree_virtual(vm_offset_t addr, vm_size_t size)
 {
-    kmem_free(kmem_map, ptr, size);
+    assert(size > PAGE_SIZE);
+    size = vm_page_round(size);
+    kmem_free(kernel_map, addr, size);
+}
+
+static vm_offset_t
+kmem_pagealloc(vm_size_t size, vm_size_t align, int flags)
+{
+    assert(align <= size);
+    return (flags & KMEM_CF_PHYSMEM)
+           ? kmem_pagealloc_physmem(size)
+           : kmem_pagealloc_virtual(size, align);
+}
+
+static void
+kmem_pagefree(vm_offset_t addr, vm_size_t size, int flags)
+{
+    return (flags & KMEM_CF_PHYSMEM)
+           ? kmem_pagefree_physmem(addr, size)
+           : kmem_pagefree_virtual(addr, size);
 }
 
 static void kmem_slab_create_verify(struct kmem_slab *slab,
@@ -434,28 +469,28 @@ static struct kmem_slab * kmem_slab_create(struct 
kmem_cache *cache,
     union kmem_bufctl *bufctl;
     size_t buf_size;
     unsigned long buffers;
-    void *slab_buf;
+    vm_offset_t slab_buf;
 
-    if (cache->slab_alloc_fn == NULL)
-        slab_buf = (void *)kmem_pagealloc(cache->slab_size);
-    else
-        slab_buf = (void *)cache->slab_alloc_fn(cache->slab_size);
+    slab_buf = kmem_pagealloc(cache->slab_size, cache->align, cache->flags);
 
-    if (slab_buf == NULL)
+    if (slab_buf == 0)
         return NULL;
 
     if (cache->flags & KMEM_CF_SLAB_EXTERNAL) {
-        assert(!(cache->flags & KMEM_CF_NO_RECLAIM));
         slab = (struct kmem_slab *)kmem_cache_alloc(&kmem_slab_cache);
 
         if (slab == NULL) {
-            if (cache->slab_free_fn == NULL)
-                kmem_pagefree((vm_offset_t)slab_buf, cache->slab_size);
-            else
-                cache->slab_free_fn((vm_offset_t)slab_buf, cache->slab_size);
-
+            kmem_pagefree(slab_buf, cache->slab_size, cache->flags);
             return NULL;
         }
+
+        if (cache->flags & KMEM_CF_USE_PAGE) {
+            struct vm_page *page;
+
+            page = vm_page_lookup_pa(kvtophys(slab_buf));
+            assert(page != NULL);
+            vm_page_set_priv(page, slab);
+        }
     } else {
         slab = (struct kmem_slab *)(slab_buf + cache->slab_size) - 1;
     }
@@ -464,7 +499,7 @@ static struct kmem_slab * kmem_slab_create(struct 
kmem_cache *cache,
     rbtree_node_init(&slab->tree_node);
     slab->nr_refs = 0;
     slab->first_free = NULL;
-    slab->addr = slab_buf + color;
+    slab->addr = (void *)(slab_buf + color);
 
     buf_size = cache->buf_size;
     bufctl = kmem_buf_to_bufctl(slab->addr, cache);
@@ -518,25 +553,26 @@ static void kmem_slab_destroy(struct kmem_slab *slab, 
struct kmem_cache *cache)
 
     assert(slab->nr_refs == 0);
     assert(slab->first_free != NULL);
-    assert(!(cache->flags & KMEM_CF_NO_RECLAIM));
 
     if (cache->flags & KMEM_CF_VERIFY)
         kmem_slab_destroy_verify(slab, cache);
 
     slab_buf = (vm_offset_t)P2ALIGN((unsigned long)slab->addr, PAGE_SIZE);
 
-    if (cache->slab_free_fn == NULL)
-        kmem_pagefree(slab_buf, cache->slab_size);
-    else
-        cache->slab_free_fn(slab_buf, cache->slab_size);
+    if (cache->flags & KMEM_CF_SLAB_EXTERNAL) {
+        if (cache->flags & KMEM_CF_USE_PAGE) {
+            struct vm_page *page;
+
+            /* Not strictly needed, but let's increase safety */
+            page = vm_page_lookup_pa(kvtophys(slab_buf));
+            assert(page != NULL);
+            vm_page_set_priv(page, NULL);
+        }
 
-    if (cache->flags & KMEM_CF_SLAB_EXTERNAL)
         kmem_cache_free(&kmem_slab_cache, (vm_offset_t)slab);
-}
+    }
 
-static inline int kmem_slab_use_tree(int flags)
-{
-    return !(flags & KMEM_CF_DIRECT) || (flags & KMEM_CF_VERIFY);
+    kmem_pagefree(slab_buf, cache->slab_size, cache->flags);
 }
 
 static inline int kmem_slab_cmp_lookup(const void *addr,
@@ -694,82 +730,81 @@ static void kmem_cache_error(struct kmem_cache *cache, 
void *buf, int error,
 }
 
 /*
- * Compute an appropriate slab size for the given cache.
+ * Compute properties such as slab size for the given cache.
  *
  * Once the slab size is known, this function sets the related properties
- * (buffers per slab and maximum color). It can also set the KMEM_CF_DIRECT
- * and/or KMEM_CF_SLAB_EXTERNAL flags depending on the resulting layout.
+ * (buffers per slab and maximum color). It can also set some KMEM_CF_xxx
+ * flags depending on the resulting layout.
  */
-static void kmem_cache_compute_sizes(struct kmem_cache *cache, int flags)
+static void kmem_cache_compute_properties(struct kmem_cache *cache, int flags)
 {
-    size_t i, buffers, buf_size, slab_size, free_slab_size, optimal_size = 0;
-    size_t waste, waste_min;
-    int embed, optimal_embed = 0;
-
-    buf_size = cache->buf_size;
+    size_t size, waste;
+    int embed;
 
-    if (buf_size < KMEM_BUF_SIZE_THRESHOLD)
+    if (cache->buf_size < KMEM_BUF_SIZE_THRESHOLD)
         flags |= KMEM_CACHE_NOOFFSLAB;
 
-    i = 0;
-    waste_min = (size_t)-1;
-
-    do {
-        i++;
-        slab_size = P2ROUND(i * buf_size, PAGE_SIZE);
-        free_slab_size = slab_size;
-
-        if (flags & KMEM_CACHE_NOOFFSLAB)
-            free_slab_size -= sizeof(struct kmem_slab);
-
-        buffers = free_slab_size / buf_size;
-        waste = free_slab_size % buf_size;
-
-        if (buffers > i)
-            i = buffers;
+    cache->slab_size = PAGE_SIZE;
 
+    for (;;) {
         if (flags & KMEM_CACHE_NOOFFSLAB)
             embed = 1;
-        else if (sizeof(struct kmem_slab) <= waste) {
-            embed = 1;
-            waste -= sizeof(struct kmem_slab);
-        } else {
-            embed = 0;
+        else {
+            waste = cache->slab_size % cache->buf_size;
+            embed = (sizeof(struct kmem_slab) <= waste);
         }
 
-        if (waste <= waste_min) {
-            waste_min = waste;
-            optimal_size = slab_size;
-            optimal_embed = embed;
-        }
-    } while ((buffers < KMEM_MIN_BUFS_PER_SLAB)
-             && (slab_size < KMEM_SLAB_SIZE_THRESHOLD));
+        size = cache->slab_size;
 
-    assert(optimal_size > 0);
-    assert(!(flags & KMEM_CACHE_NOOFFSLAB) || optimal_embed);
+        if (embed)
+            size -= sizeof(struct kmem_slab);
 
-    cache->slab_size = optimal_size;
-    slab_size = cache->slab_size - (optimal_embed
-                ? sizeof(struct kmem_slab)
-                : 0);
-    cache->bufs_per_slab = slab_size / buf_size;
-    cache->color_max = slab_size % buf_size;
+        if (size >= cache->buf_size)
+            break;
+
+        cache->slab_size += PAGE_SIZE;
+    }
+
+    cache->bufs_per_slab = size / cache->buf_size;
+    cache->color_max = size % cache->buf_size;
 
     if (cache->color_max >= PAGE_SIZE)
-        cache->color_max = PAGE_SIZE - 1;
+        cache->color_max = 0;
+
+    if (!embed)
+        cache->flags |= KMEM_CF_SLAB_EXTERNAL;
+
+    if ((flags & KMEM_CACHE_PHYSMEM) || (cache->slab_size == PAGE_SIZE)) {
+        cache->flags |= KMEM_CF_PHYSMEM;
 
-    if (optimal_embed) {
+        /*
+         * Avoid using larger-than-page slabs backed by the direct physical
+         * mapping to completely prevent physical memory fragmentation from
+         * making slab allocations fail.
+         */
+        if (cache->slab_size != PAGE_SIZE)
+            panic("slab: invalid cache parameters");
+    }
+
+    if (cache->flags & KMEM_CF_VERIFY)
+        cache->flags |= KMEM_CF_USE_TREE;
+
+    if (cache->flags & KMEM_CF_SLAB_EXTERNAL) {
+        if (cache->flags & KMEM_CF_PHYSMEM)
+            cache->flags |= KMEM_CF_USE_PAGE;
+        else
+            cache->flags |= KMEM_CF_USE_TREE;
+    } else {
         if (cache->slab_size == PAGE_SIZE)
             cache->flags |= KMEM_CF_DIRECT;
-    } else {
-        cache->flags |= KMEM_CF_SLAB_EXTERNAL;
+        else
+            cache->flags |= KMEM_CF_USE_TREE;
     }
 }
 
 void kmem_cache_init(struct kmem_cache *cache, const char *name,
-                     size_t obj_size, size_t align, kmem_cache_ctor_t ctor,
-                     kmem_slab_alloc_fn_t slab_alloc_fn,
-                     kmem_slab_free_fn_t slab_free_fn, int flags)
+                     size_t obj_size, size_t align,
+                     kmem_cache_ctor_t ctor, int flags)
 {
 #if SLAB_USE_CPU_POOLS
     struct kmem_cpu_pool_type *cpu_pool_type;
@@ -783,15 +818,6 @@ void kmem_cache_init(struct kmem_cache *cache, const char 
*name,
     cache->flags = 0;
 #endif /* SLAB_VERIFY */
 
-    if (flags & KMEM_CACHE_NOCPUPOOL)
-        cache->flags |= KMEM_CF_NO_CPU_POOL;
-
-    if (flags & KMEM_CACHE_NORECLAIM) {
-        assert(slab_free_fn == NULL);
-        flags |= KMEM_CACHE_NOOFFSLAB;
-        cache->flags |= KMEM_CF_NO_RECLAIM;
-    }
-
     if (flags & KMEM_CACHE_VERIFY)
         cache->flags |= KMEM_CF_VERIFY;
 
@@ -800,7 +826,6 @@ void kmem_cache_init(struct kmem_cache *cache, const char 
*name,
 
     assert(obj_size > 0);
     assert(ISP2(align));
-    assert(align < PAGE_SIZE);
 
     buf_size = P2ROUND(obj_size, align);
 
@@ -819,8 +844,6 @@ void kmem_cache_init(struct kmem_cache *cache, const char 
*name,
     cache->nr_slabs = 0;
     cache->nr_free_slabs = 0;
     cache->ctor = ctor;
-    cache->slab_alloc_fn = slab_alloc_fn;
-    cache->slab_free_fn = slab_free_fn;
     strncpy(cache->name, name, sizeof(cache->name));
     cache->name[sizeof(cache->name) - 1] = '\0';
     cache->buftag_dist = 0;
@@ -835,7 +858,7 @@ void kmem_cache_init(struct kmem_cache *cache, const char 
*name,
         cache->buf_size = buf_size;
     }
 
-    kmem_cache_compute_sizes(cache, flags);
+    kmem_cache_compute_properties(cache, flags);
 
 #if SLAB_USE_CPU_POOLS
     for (cpu_pool_type = kmem_cpu_pool_types;
@@ -908,9 +931,6 @@ static void kmem_cache_reap(struct kmem_cache *cache)
     struct list dead_slabs;
     unsigned long nr_free_slabs;
 
-    if (cache->flags & KMEM_CF_NO_RECLAIM)
-        return;
-
     simple_lock(&cache->lock);
     list_set_head(&dead_slabs, &cache->free_slabs);
     list_init(&cache->free_slabs);
@@ -971,7 +991,7 @@ static void * kmem_cache_alloc_from_slab(struct kmem_cache 
*cache)
         cache->nr_free_slabs--;
     }
 
-    if ((slab->nr_refs == 1) && kmem_slab_use_tree(cache->flags))
+    if ((slab->nr_refs == 1) && (cache->flags & KMEM_CF_USE_TREE))
         rbtree_insert(&cache->active_slabs, &slab->tree_node,
                       kmem_slab_cmp_insert);
 
@@ -992,17 +1012,26 @@ static void kmem_cache_free_to_slab(struct kmem_cache 
*cache, void *buf)
         assert(cache->slab_size == PAGE_SIZE);
         slab = (struct kmem_slab *)P2END((unsigned long)buf, cache->slab_size)
                - 1;
+    } else if (cache->flags & KMEM_CF_USE_PAGE) {
+        struct vm_page *page;
+
+        page = vm_page_lookup_pa(kvtophys((vm_offset_t)buf));
+        assert(page != NULL);
+        slab = vm_page_get_priv(page);
     } else {
         struct rbtree_node *node;
 
+        assert(cache->flags & KMEM_CF_USE_TREE);
         node = rbtree_lookup_nearest(&cache->active_slabs, buf,
                                      kmem_slab_cmp_lookup, RBTREE_LEFT);
         assert(node != NULL);
         slab = rbtree_entry(node, struct kmem_slab, tree_node);
-        assert((unsigned long)buf < (P2ALIGN((unsigned long)slab->addr
-                                             + cache->slab_size, PAGE_SIZE)));
     }
 
+    assert((unsigned long)buf >= (unsigned long)slab->addr);
+    assert(((unsigned long)buf + cache->buf_size)
+           <= vm_page_trunc((unsigned long)slab->addr + cache->slab_size));
+
     assert(slab->nr_refs >= 1);
     assert(slab->nr_refs <= cache->bufs_per_slab);
     bufctl = kmem_buf_to_bufctl(buf, cache);
@@ -1014,7 +1043,7 @@ static void kmem_cache_free_to_slab(struct kmem_cache 
*cache, void *buf)
     if (slab->nr_refs == 0) {
         /* The slab has become free */
 
-        if (kmem_slab_use_tree(cache->flags))
+        if (cache->flags & KMEM_CF_USE_TREE)
             rbtree_remove(&cache->active_slabs, &slab->tree_node);
 
         if (cache->bufs_per_slab > 1)
@@ -1135,6 +1164,8 @@ static void kmem_cache_free_verify(struct kmem_cache 
*cache, void *buf)
     unsigned char *redzone_byte;
     unsigned long slabend;
 
+    assert(cache->flags & KMEM_CF_USE_TREE);
+
     simple_lock(&cache->lock);
     node = rbtree_lookup_nearest(&cache->active_slabs, buf,
                                  kmem_slab_cmp_lookup, RBTREE_LEFT);
@@ -1280,16 +1311,12 @@ void slab_bootstrap(void)
 
 void slab_init(void)
 {
-    vm_offset_t min, max;
-
 #if SLAB_USE_CPU_POOLS
     struct kmem_cpu_pool_type *cpu_pool_type;
     char name[KMEM_CACHE_NAME_SIZE];
     size_t i, size;
 #endif /* SLAB_USE_CPU_POOLS */
 
-    kmem_submap(kmem_map, kernel_map, &min, &max, KMEM_MAP_SIZE, FALSE);
-
 #if SLAB_USE_CPU_POOLS
     for (i = 0; i < ARRAY_SIZE(kmem_cpu_pool_types); i++) {
         cpu_pool_type = &kmem_cpu_pool_types[i];
@@ -1297,7 +1324,7 @@ void slab_init(void)
         sprintf(name, "kmem_cpu_array_%d", cpu_pool_type->array_size);
         size = sizeof(void *) * cpu_pool_type->array_size;
         kmem_cache_init(cpu_pool_type->array_cache, name, size,
-                        cpu_pool_type->array_align, NULL, NULL, NULL, 0);
+                        cpu_pool_type->array_align, NULL, 0);
     }
 #endif /* SLAB_USE_CPU_POOLS */
 
@@ -1305,25 +1332,7 @@ void slab_init(void)
      * Prevent off slab data for the slab cache to avoid infinite recursion.
      */
     kmem_cache_init(&kmem_slab_cache, "kmem_slab", sizeof(struct kmem_slab),
-                    0, NULL, NULL, NULL, KMEM_CACHE_NOOFFSLAB);
-}
-
-static vm_offset_t kalloc_pagealloc(vm_size_t size)
-{
-    vm_offset_t addr;
-    kern_return_t kr;
-
-    kr = kmem_alloc_wired(kmem_map, &addr, size);
-
-    if (kr != KERN_SUCCESS)
-        return 0;
-
-    return addr;
-}
-
-static void kalloc_pagefree(vm_offset_t ptr, vm_size_t size)
-{
-    kmem_free(kmem_map, ptr, size);
+                    0, NULL, KMEM_CACHE_NOOFFSLAB);
 }
 
 void kalloc_init(void)
@@ -1335,8 +1344,7 @@ void kalloc_init(void)
 
     for (i = 0; i < ARRAY_SIZE(kalloc_caches); i++) {
         sprintf(name, "kalloc_%lu", size);
-        kmem_cache_init(&kalloc_caches[i], name, size, 0, NULL,
-                        kalloc_pagealloc, kalloc_pagefree, 0);
+        kmem_cache_init(&kalloc_caches[i], name, size, 0, NULL, 0);
         size <<= 1;
     }
 }
@@ -1387,8 +1395,9 @@ vm_offset_t kalloc(vm_size_t size)
 
         if ((buf != 0) && (cache->flags & KMEM_CF_VERIFY))
             kalloc_verify(cache, buf, size);
-    } else
-        buf = (void *)kalloc_pagealloc(size);
+    } else {
+        buf = (void *)kmem_pagealloc_virtual(size, 0);
+    }
 
     return (vm_offset_t)buf;
 }
@@ -1429,7 +1438,7 @@ void kfree(vm_offset_t data, vm_size_t size)
 
         kmem_cache_free(cache, data);
     } else {
-        kalloc_pagefree(data, size);
+        kmem_pagefree_virtual(data, size);
     }
 }
 
@@ -1529,16 +1538,7 @@ kern_return_t host_slab_info(host_t host, 
cache_info_array_t *infop,
 
     list_for_each_entry(&kmem_cache_list, cache, node) {
         simple_lock(&cache->lock);
-        info[i].flags = ((cache->flags & KMEM_CF_NO_CPU_POOL)
-                         ? CACHE_FLAGS_NO_CPU_POOL : 0)
-                        | ((cache->flags & KMEM_CF_SLAB_EXTERNAL)
-                           ? CACHE_FLAGS_SLAB_EXTERNAL : 0)
-                        | ((cache->flags & KMEM_CF_NO_RECLAIM)
-                           ? CACHE_FLAGS_NO_RECLAIM : 0)
-                        | ((cache->flags & KMEM_CF_VERIFY)
-                           ? CACHE_FLAGS_VERIFY : 0)
-                        | ((cache->flags & KMEM_CF_DIRECT)
-                           ? CACHE_FLAGS_DIRECT : 0);
+        info[i].flags = cache->flags;
 #if SLAB_USE_CPU_POOLS
         info[i].cpu_pool_size = cache->cpu_pool_type->array_size;
 #else /* SLAB_USE_CPU_POOLS */
diff --git a/kern/slab.h b/kern/slab.h
index 77db7c1..8527c9d 100644
--- a/kern/slab.h
+++ b/kern/slab.h
@@ -48,6 +48,7 @@
 #define _KERN_SLAB_H
 
 #include <cache.h>
+#include <kern/cpu_number.h>
 #include <kern/lock.h>
 #include <kern/list.h>
 #include <kern/rbtree.h>
@@ -56,10 +57,6 @@
 #include <vm/vm_types.h>
 
 #if SLAB_USE_CPU_POOLS
-/*
- * L1 cache line size.
- */
-#define CPU_L1_SIZE (1 << CPU_L1_SHIFT)
 
 /*
  * Per-processor cache of pre-constructed objects.
@@ -140,14 +137,6 @@ struct kmem_slab {
 typedef void (*kmem_cache_ctor_t)(void *obj);
 
 /*
- * Types for slab allocation/free functions.
- *
- * All addresses and sizes must be page-aligned.
- */
-typedef vm_offset_t (*kmem_slab_alloc_fn_t)(vm_size_t);
-typedef void (*kmem_slab_free_fn_t)(vm_offset_t, vm_size_t);
-
-/*
  * Cache name buffer size.  The size is chosen so that struct
  * kmem_cache fits into two cache lines.  The size of a cache line on
  * a typical CPU is 64 bytes.
@@ -192,8 +181,6 @@ struct kmem_cache {
     size_t color_max;
     unsigned long nr_bufs;  /* Total number of buffers */
     unsigned long nr_slabs;
-    kmem_slab_alloc_fn_t slab_alloc_fn;
-    kmem_slab_free_fn_t slab_free_fn;
     char name[KMEM_CACHE_NAME_SIZE];
     size_t buftag_dist; /* Distance from buffer to buftag */
     size_t redzone_pad; /* Bytes from end of object to redzone word */
@@ -206,26 +193,18 @@ typedef struct kmem_cache *kmem_cache_t;
 #define KMEM_CACHE_NULL ((kmem_cache_t) 0)
 
 /*
- * VM submap for slab allocations.
- */
-extern vm_map_t kmem_map;
-
-/*
  * Cache initialization flags.
  */
-#define KMEM_CACHE_NOCPUPOOL    0x1 /* Don't use the per-cpu pools */
-#define KMEM_CACHE_NOOFFSLAB    0x2 /* Don't allocate external slab data */
-#define KMEM_CACHE_NORECLAIM    0x4 /* Never give slabs back to their source,
-                                       implies KMEM_CACHE_NOOFFSLAB */
-#define KMEM_CACHE_VERIFY       0x8 /* Use debugging facilities */
+#define KMEM_CACHE_NOOFFSLAB    0x1 /* Don't allocate external slab data */
+#define KMEM_CACHE_PHYSMEM      0x2 /* Allocate from physical memory */
+#define KMEM_CACHE_VERIFY       0x4 /* Use debugging facilities */
 
 /*
  * Initialize a cache.
  */
 void kmem_cache_init(struct kmem_cache *cache, const char *name,
-                     size_t obj_size, size_t align, kmem_cache_ctor_t ctor,
-                     kmem_slab_alloc_fn_t slab_alloc_fn,
-                     kmem_slab_free_fn_t slab_free_fn, int flags);
+                     size_t obj_size, size_t align,
+                     kmem_cache_ctor_t ctor, int flags);
 
 /*
  * Allocate an object from a cache.
diff --git a/kern/startup.c b/kern/startup.c
index 30cff5c..bd29694 100644
--- a/kern/startup.c
+++ b/kern/startup.c
@@ -136,7 +136,7 @@ void setup_main(void)
        mapable_time_init();
 
        machine_info.max_cpus = NCPUS;
-       machine_info.memory_size = phys_last_addr - phys_first_addr; /* XXX 
mem_size */
+       machine_info.memory_size = vm_page_mem_size(); /* XXX phys_addr_t -> 
vm_size_t */
        machine_info.avail_cpus = 0;
        machine_info.major_version = KERNEL_MAJOR_VERSION;
        machine_info.minor_version = KERNEL_MINOR_VERSION;
diff --git a/kern/task.c b/kern/task.c
index e9e6ba2..0f24e44 100644
--- a/kern/task.c
+++ b/kern/task.c
@@ -63,7 +63,7 @@ ipc_port_t new_task_notification = NULL;
 void task_init(void)
 {
        kmem_cache_init(&task_cache, "task", sizeof(struct task), 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
 
        eml_init();
        machine_task_module_init ();
diff --git a/kern/thread.c b/kern/thread.c
index 3e90079..ce44ed1 100644
--- a/kern/thread.c
+++ b/kern/thread.c
@@ -70,6 +70,7 @@ thread_t active_threads[NCPUS];
 vm_offset_t active_stacks[NCPUS];
 
 struct kmem_cache thread_cache;
+struct kmem_cache thread_stack_cache;
 
 queue_head_t           reaper_queue;
 decl_simple_lock_data(,        reaper_lock)
@@ -195,16 +196,8 @@ kern_return_t stack_alloc(
        (void) splx(s);
 
        if (stack == 0) {
-               kern_return_t kr;
-               /*
-                *      Kernel stacks should be naturally aligned,
-                *      so that it is easy to find the starting/ending
-                *      addresses of a stack given an address in the middle.
-                */
-               kr = kmem_alloc_aligned(kmem_map, &stack, KERNEL_STACK_SIZE);
-               if (kr != KERN_SUCCESS)
-                       return kr;
-
+               stack = kmem_cache_alloc(&thread_stack_cache);
+               assert(stack != 0);
 #if    MACH_DEBUG
                stack_init(stack);
 #endif /* MACH_DEBUG */
@@ -265,7 +258,7 @@ void stack_collect(void)
 #if    MACH_DEBUG
                stack_finalize(stack);
 #endif /* MACH_DEBUG */
-               kmem_free(kmem_map, stack, KERNEL_STACK_SIZE);
+               kmem_cache_free(&thread_stack_cache, stack);
 
                s = splsched();
                stack_lock();
@@ -298,7 +291,15 @@ void stack_privilege(
 void thread_init(void)
 {
        kmem_cache_init(&thread_cache, "thread", sizeof(struct thread), 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
+       /*
+        *      Kernel stacks should be naturally aligned,
+        *      so that it is easy to find the starting/ending
+        *      addresses of a stack given an address in the middle.
+        */
+       kmem_cache_init(&thread_stack_cache, "thread_stack",
+                       KERNEL_STACK_SIZE, KERNEL_STACK_SIZE,
+                       NULL, 0);
 
        /*
         *      Fill in a template thread for fast initialization.
@@ -430,7 +431,7 @@ kern_return_t thread_create(
         *      Create a pcb.  The kernel stack is created later,
         *      when the thread is swapped-in.
         */
-       pcb_init(new_thread);
+       pcb_init(parent_task, new_thread);
 
        ipc_thread_init(new_thread);
 
diff --git a/linux/dev/drivers/block/ahci.c b/linux/dev/drivers/block/ahci.c
index c243982..b60f1a1 100644
--- a/linux/dev/drivers/block/ahci.c
+++ b/linux/dev/drivers/block/ahci.c
@@ -826,41 +826,56 @@ static void ahci_probe_dev(unsigned char bus, unsigned 
char device)
 
        /* Get configuration */
        if (pcibios_read_config_byte(bus, device, PCI_HEADER_TYPE, &hdrtype) != 
PCIBIOS_SUCCESSFUL) {
-               printk("ahci: %02u:%02u.%u: Can not read configuration", bus, 
dev, fun);
+               printk("ahci: %02x:%02x.%x: Can not read configuration", bus, 
dev, fun);
                return;
        }
 
        if (hdrtype != 0) {
-               printk("ahci: %02u:%02u.%u: Unknown hdrtype %d\n", bus, dev, 
fun, hdrtype);
+               printk("ahci: %02x:%02x.%x: Unknown hdrtype %d\n", bus, dev, 
fun, hdrtype);
                return;
        }
 
        if (pcibios_read_config_dword(bus, device, PCI_BASE_ADDRESS_5, &bar) != 
PCIBIOS_SUCCESSFUL) {
-               printk("ahci: %02u:%02u.%u: Can not read BAR 5", bus, dev, fun);
+               printk("ahci: %02x:%02x.%x: Can not read BAR 5", bus, dev, fun);
                return;
        }
-       if (bar & 0x01) {
-               printk("ahci: %02u:%02u.%u: BAR 5 is I/O?!", bus, dev, fun);
+       if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
+               printk("ahci: %02x:%02x.%x: BAR 5 is I/O?!", bus, dev, fun);
                return;
        }
-       bar &= ~0x0f;
+       bar &= PCI_BASE_ADDRESS_MEM_MASK;
 
        if (pcibios_read_config_byte(bus, device, PCI_INTERRUPT_LINE, &irq) != 
PCIBIOS_SUCCESSFUL) {
-               printk("ahci: %02u:%02u.%u: Can not read IRQ", bus, dev, fun);
+               printk("ahci: %02x:%02x.%x: Can not read IRQ", bus, dev, fun);
                return;
        }
 
-       printk("AHCI SATA %02u:%02u.%u BAR 0x%x IRQ %u\n", bus, dev, fun, bar, 
irq);
+       printk("AHCI SATA %02x:%02x.%x BAR 0x%x IRQ %u\n", bus, dev, fun, bar, 
irq);
 
        /* Map mmio */
        ahci_host = vremap(bar, 0x2000);
 
        /* Request IRQ */
        if (request_irq(irq, &ahci_interrupt, SA_SHIRQ, "ahci", (void*) 
ahci_host)) {
-               printk("ahci: %02u:%02u.%u: Can not get irq %u\n", bus, dev, 
fun, irq);
+               printk("ahci: %02x:%02x.%x: Can not get irq %u\n", bus, dev, 
fun, irq);
                return;
        }
 
+#ifdef CONFIG_BLK_DEV_IDE
+       /* OK, we will handle it. Disable probing on legacy IDE ports it may 
have.  */
+       for (i = 0; i < 6; i++)
+       {
+               unsigned mybar;
+               if (pcibios_read_config_dword(bus, device, PCI_BASE_ADDRESS_0 + 
i*4, &mybar) == PCIBIOS_SUCCESSFUL) {
+                       if (!(bar & PCI_BASE_ADDRESS_SPACE_IO))
+                               /* Memory, don't care */
+                               continue;
+                       /* printk("ahci: %02x:%02x.%x: BAR %d is %x\n", bus, 
dev, fun, i, mybar); */
+                       ide_disable_base(bar & PCI_BASE_ADDRESS_IO_MASK);
+               }
+       }
+#endif
+
        nports = (readl(&ahci_host->cap) & 0x1f) + 1;
        port_map = readl(&ahci_host->pi);
 
@@ -869,7 +884,7 @@ static void ahci_probe_dev(unsigned char bus, unsigned char 
device)
                        n++;
 
        if (nports != n) {
-               printk("ahci: %02u:%02u.%u: Odd number of ports %u, assuming %u 
is correct\n", bus, dev, fun, n, nports);
+               printk("ahci: %02x:%02x.%x: Odd number of ports %u, assuming %u 
is correct\n", bus, dev, fun, n, nports);
                port_map = 0;
        }
        if (!port_map) {
@@ -893,16 +908,16 @@ static void ahci_probe_dev(unsigned char bus, unsigned 
char device)
                                /* Device not present */
                                continue;
                        case 0x1:
-                               printk("ahci: %02u:%02u.%u: Port %u 
communication not established. TODO: power on device\n", bus, dev, fun, i);
+                               printk("ahci: %02x:%02x.%x: Port %u 
communication not established. TODO: power on device\n", bus, dev, fun, i);
                                continue;
                        case 0x3:
                                /* Present and communication established */
                                break;
                        case 0x4:
-                               printk("ahci: %02u:%02u.%u: Port %u phy 
offline?!\n", bus, dev, fun, i);
+                               printk("ahci: %02x:%02x.%x: Port %u phy 
offline?!\n", bus, dev, fun, i);
                                continue;
                        default:
-                               printk("ahci: %02u:%02u.%u: Unknown port %u SPD 
%x\n", bus, dev, fun, i, spd);
+                               printk("ahci: %02x:%02x.%x: Unknown port %u SPD 
%x\n", bus, dev, fun, i, spd);
                                continue;
                }
 
@@ -916,13 +931,13 @@ static void ahci_probe_dev(unsigned char bus, unsigned 
char device)
                                /* Active */
                                break;
                        case 0x2:
-                               printk("ahci: %02u:%02u.%u: Port %u in Partial 
power management. TODO: power on device\n", bus, dev, fun, i);
+                               printk("ahci: %02x:%02x.%x: Port %u in Partial 
power management. TODO: power on device\n", bus, dev, fun, i);
                                continue;
                        case 0x6:
-                               printk("ahci: %02u:%02u.%u: Port %u in Slumber 
power management. TODO: power on device\n", bus, dev, fun, i);
+                               printk("ahci: %02x:%02x.%x: Port %u in Slumber 
power management. TODO: power on device\n", bus, dev, fun, i);
                                continue;
                        default:
-                               printk("ahci: %02u:%02u.%u: Unknown port %u IPM 
%x\n", bus, dev, fun, i, ipm);
+                               printk("ahci: %02x:%02x.%x: Unknown port %u IPM 
%x\n", bus, dev, fun, i, ipm);
                                continue;
                }
 
diff --git a/linux/dev/glue/glue.h b/linux/dev/glue/glue.h
index 5d4f6d8..8cb118c 100644
--- a/linux/dev/glue/glue.h
+++ b/linux/dev/glue/glue.h
@@ -25,8 +25,8 @@
 extern int linux_auto_config;
 extern int linux_intr_pri;
 
-extern void *alloc_contig_mem (unsigned, unsigned, unsigned, vm_page_t *);
-extern void free_contig_mem (vm_page_t);
+extern unsigned long alloc_contig_mem (unsigned, unsigned, unsigned, vm_page_t 
*);
+extern void free_contig_mem (vm_page_t, unsigned);
 extern void init_IRQ (void);
 extern void restore_IRQ (void);
 extern void linux_kmem_init (void);
diff --git a/linux/dev/glue/kmem.c b/linux/dev/glue/kmem.c
index ff052ff..ed57610 100644
--- a/linux/dev/glue/kmem.c
+++ b/linux/dev/glue/kmem.c
@@ -111,10 +111,8 @@ linux_kmem_init ()
       for (p = pages, j = 0; j < MEM_CHUNK_SIZE - PAGE_SIZE; j += PAGE_SIZE)
        {
          assert (p->phys_addr < MEM_DMA_LIMIT);
-         assert (p->phys_addr + PAGE_SIZE
-                 == ((vm_page_t) p->pageq.next)->phys_addr);
-
-         p = (vm_page_t) p->pageq.next;
+         assert (p->phys_addr + PAGE_SIZE == (p + 1)->phys_addr);
+         p++;
        }
 
       pages_free[i].end = pages_free[i].start + MEM_CHUNK_SIZE;
diff --git a/linux/dev/include/linux/blk.h b/linux/dev/include/linux/blk.h
index 156d91c..b924a14 100644
--- a/linux/dev/include/linux/blk.h
+++ b/linux/dev/include/linux/blk.h
@@ -78,6 +78,7 @@ extern int hd_init(void);
 #endif
 #ifdef CONFIG_BLK_DEV_IDE
 extern int ide_init(void);
+extern void ide_disable_base(unsigned base);
 #endif
 #ifdef CONFIG_BLK_DEV_XD
 extern int xd_init(void);
diff --git a/linux/dev/init/main.c b/linux/dev/init/main.c
index 8737b62..d69b3fc 100644
--- a/linux/dev/init/main.c
+++ b/linux/dev/init/main.c
@@ -98,7 +98,7 @@ void
 linux_init (void)
 {
   int addr;
-  unsigned memory_start, memory_end;
+  unsigned long memory_start, memory_end;
   vm_page_t pages;
 
   /*
@@ -131,9 +131,7 @@ linux_init (void)
   /*
    * Allocate contiguous memory below 16 MB.
    */
-  memory_start = (unsigned long) alloc_contig_mem (CONTIG_ALLOC,
-                                                  16 * 1024 * 1024,
-                                                  0, &pages);
+  memory_start = alloc_contig_mem (CONTIG_ALLOC, 16 * 1024 * 1024, 0, &pages);
   if (memory_start == 0)
     panic ("linux_init: alloc_contig_mem failed");
   memory_end = memory_start + CONTIG_ALLOC;
@@ -147,14 +145,6 @@ linux_init (void)
     panic ("linux_init: ran out memory");
 
   /*
-   * Free unused memory.
-   */
-  while (pages && phystokv(pages->phys_addr) < round_page (memory_start))
-    pages = (vm_page_t) pages->pageq.next;
-  if (pages)
-    free_contig_mem (pages);
-
-  /*
    * Initialize devices.
    */
 #ifdef CONFIG_INET
@@ -182,140 +172,31 @@ linux_init (void)
 
 /*
  * Allocate contiguous memory with the given constraints.
- * This routine is horribly inefficient but it is presently
- * only used during initialization so it's not that bad.
  */
-void *
+unsigned long
 alloc_contig_mem (unsigned size, unsigned limit,
                  unsigned mask, vm_page_t * pages)
 {
-  int i, j, bits_len;
-  unsigned *bits, len;
-  void *m;
-  vm_page_t p, page_list, tail, prev;
-  vm_offset_t addr = 0, max_addr;
-
-  if (size == 0)
-    return (NULL);
-  size = round_page (size);
-  if ((size >> PAGE_SHIFT) > vm_page_free_count)
-    return (NULL);
-
-  /* Allocate bit array.  */
-  max_addr = phys_last_addr;
-  if (max_addr > limit)
-    max_addr = limit;
-  bits_len = ((((max_addr >> PAGE_SHIFT) + NBPW - 1) / NBPW)
-             * sizeof (unsigned));
-  bits = (unsigned *) kalloc (bits_len);
-  if (!bits)
-    return (NULL);
-  memset (bits, 0, bits_len);
+  vm_page_t p;
 
-  /*
-   * Walk the page free list and set a bit for every usable page.
-   */
-  simple_lock (&vm_page_queue_free_lock);
-  p = vm_page_queue_free;
-  while (p)
-    {
-      if (p->phys_addr < limit)
-       (bits[(p->phys_addr >> PAGE_SHIFT) / NBPW]
-        |= 1 << ((p->phys_addr >> PAGE_SHIFT) % NBPW));
-      p = (vm_page_t) p->pageq.next;
-    }
+  p = vm_page_grab_contig(size, VM_PAGE_SEL_DMA);
 
-  /*
-   * Scan bit array for contiguous pages.
-   */
-  len = 0;
-  m = NULL;
-  for (i = 0; len < size && i < bits_len / sizeof (unsigned); i++)
-    for (j = 0; len < size && j < NBPW; j++)
-      if (!(bits[i] & (1 << j)))
-       {
-         len = 0;
-         m = NULL;
-       }
-      else
-       {
-         if (len == 0)
-           {
-             addr = ((vm_offset_t) (i * NBPW + j)
-                     << PAGE_SHIFT);
-             if ((addr & mask) == 0)
-               {
-                 len += PAGE_SIZE;
-                 m = (void *) addr;
-               }
-           }
-         else
-           len += PAGE_SIZE;
-       }
-
-  if (len != size)
-    {
-      simple_unlock (&vm_page_queue_free_lock);
-      kfree ((vm_offset_t) bits, bits_len);
-      return (NULL);
-    }
-
-  /*
-   * Remove pages from free list
-   * and construct list to return to caller.
-   */
-  page_list = NULL;
-  for (len = 0; len < size; len += PAGE_SIZE, addr += PAGE_SIZE)
-    {
-      prev = NULL;
-      for (p = vm_page_queue_free; p; p = (vm_page_t) p->pageq.next)
-       {
-         if (p->phys_addr == addr)
-           break;
-         prev = p;
-       }
-      if (!p)
-       panic ("alloc_contig_mem: page not on free list");
-      if (prev)
-       prev->pageq.next = p->pageq.next;
-      else
-       vm_page_queue_free = (vm_page_t) p->pageq.next;
-      p->free = FALSE;
-      p->pageq.next = NULL;
-      if (!page_list)
-       page_list = tail = p;
-      else
-       {
-         tail->pageq.next = (queue_entry_t) p;
-         tail = p;
-       }
-      vm_page_free_count--;
-    }
+  if (p == NULL)
+    return 0;
 
-  simple_unlock (&vm_page_queue_free_lock);
-  kfree ((vm_offset_t) bits, bits_len);
   if (pages)
-    *pages = page_list;
-  return (void *) phystokv(m);
+    *pages = p;
+
+  return phystokv(vm_page_to_pa(p));
 }
 
 /*
  * Free memory allocated by alloc_contig_mem.
  */
 void
-free_contig_mem (vm_page_t pages)
+free_contig_mem (vm_page_t pages, unsigned size)
 {
-  int i;
-  vm_page_t p;
-
-  for (p = pages, i = 0; p->pageq.next; p = (vm_page_t) p->pageq.next, i++)
-    p->free = TRUE;
-  p->free = TRUE;
-  simple_lock (&vm_page_queue_free_lock);
-  vm_page_free_count += i + 1;
-  p->pageq.next = (queue_entry_t) vm_page_queue_free;
-  vm_page_queue_free = pages;
-  simple_unlock (&vm_page_queue_free_lock);
+  vm_page_free_contig(pages, size);
 }
 
 /* This is the number of bits of precision for the loops_per_second.  Each
diff --git a/linux/pcmcia-cs/glue/ds.c b/linux/pcmcia-cs/glue/ds.c
index 8f88b55..cc4b92b 100644
--- a/linux/pcmcia-cs/glue/ds.c
+++ b/linux/pcmcia-cs/glue/ds.c
@@ -24,12 +24,6 @@
 /* This file is included from linux/pcmcia-cs/modules/ds.c.  */
 
 /*
- * Prepare the namespace for inclusion of Mach header files.
- */
-
-#undef PAGE_SHIFT
-
-/*
  * This is really ugly.  But this is glue code, so...  It's about the `kfree'
  * symbols in <linux/malloc.h> and <kern/kalloc.h>.
  */
diff --git a/linux/src/arch/i386/kernel/bios32.c 
b/linux/src/arch/i386/kernel/bios32.c
index b069ce4..c10cc0c 100644
--- a/linux/src/arch/i386/kernel/bios32.c
+++ b/linux/src/arch/i386/kernel/bios32.c
@@ -909,6 +909,8 @@ unsigned long pcibios_init(unsigned long memory_start, 
unsigned long memory_end)
        }
        if (bios32_entry && check_pcibios())
                access_pci = &pci_bios_access;
+       else
+               access_pci = check_direct_pci();
 #endif
        return memory_start;
 }
diff --git a/linux/src/drivers/block/ide.c b/linux/src/drivers/block/ide.c
index 41a2601..dc20fcb 100644
--- a/linux/src/drivers/block/ide.c
+++ b/linux/src/drivers/block/ide.c
@@ -325,7 +325,7 @@
 #endif /* CONFIG_BLK_DEV_PROMISE */
 
 static const byte      ide_hwif_to_major[MAX_HWIFS] = {IDE0_MAJOR, IDE1_MAJOR, 
IDE2_MAJOR, IDE3_MAJOR};
-static const unsigned short default_io_base[MAX_HWIFS] = {0x1f0, 0x170, 0x1e8, 
0x168};
+static unsigned short default_io_base[MAX_HWIFS] = {0x1f0, 0x170, 0x1e8, 
0x168};
 static const byte      default_irqs[MAX_HWIFS]     = {14, 15, 11, 10};
 static int     idebus_parameter; /* holds the "idebus=" parameter */
 static int     system_bus_speed; /* holds what we think is VESA/PCI bus speed 
*/
@@ -367,6 +367,15 @@ static void set_recovery_timer (ide_hwif_t *hwif)
 
 #endif /* DISK_RECOVERY_TIME */
 
+/* Called by other drivers to disable the legacy IDE driver on a given IDE 
base.  */
+void ide_disable_base(unsigned base)
+{
+       unsigned i;
+       for (i = 0; i < MAX_HWIFS; i++)
+               if (default_io_base[i] == base)
+                       default_io_base[i] = 0;
+}
+
 
 /*
  * Do not even *think* about calling this!
diff --git a/linux/src/include/linux/compiler-gcc6.h 
b/linux/src/include/linux/compiler-gcc6.h
new file mode 100644
index 0000000..cc2e86a
--- /dev/null
+++ b/linux/src/include/linux/compiler-gcc6.h
@@ -0,0 +1,67 @@
+#ifndef __LINUX_COMPILER_H
+#error "Please don't include <linux/compiler-gcc6.h> directly, include 
<linux/compiler.h> instead."
+#endif
+
+#define __used                         __attribute__((__used__))
+#define __must_check                   __attribute__((warn_unused_result))
+#define __compiler_offsetof(a, b)      __builtin_offsetof(a, b)
+
+/* Mark functions as cold. gcc will assume any path leading to a call
+   to them will be unlikely.  This means a lot of manual unlikely()s
+   are unnecessary now for any paths leading to the usual suspects
+   like BUG(), printk(), panic() etc. [but let's keep them for now for
+   older compilers]
+
+   Early snapshots of gcc 4.3 don't support this and we can't detect this
+   in the preprocessor, but we can live with this because they're unreleased.
+   Maketime probing would be overkill here.
+
+   gcc also has a __attribute__((__hot__)) to move hot functions into
+   a special section, but I don't see any sense in this right now in
+   the kernel context */
+#define __cold                 __attribute__((__cold__))
+
+#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
+
+#ifndef __CHECKER__
+# define __compiletime_warning(message) __attribute__((warning(message)))
+# define __compiletime_error(message) __attribute__((error(message)))
+#endif /* __CHECKER__ */
+
+/*
+ * Mark a position in code as unreachable.  This can be used to
+ * suppress control flow warnings after asm blocks that transfer
+ * control elsewhere.
+ *
+ * Early snapshots of gcc 4.5 don't support this and we can't detect
+ * this in the preprocessor, but we can live with this because they're
+ * unreleased.  Really, we need to have autoconf for the kernel.
+ */
+#define unreachable() __builtin_unreachable()
+
+/* Mark a function definition as prohibited from being cloned. */
+#define __noclone      __attribute__((__noclone__))
+
+/*
+ * Tell the optimizer that something else uses this function or variable.
+ */
+#define __visible __attribute__((externally_visible))
+
+/*
+ * GCC 'asm goto' miscompiles certain code sequences:
+ *
+ *   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58670
+ *
+ * Work it around via a compiler barrier quirk suggested by Jakub Jelinek.
+ *
+ * (asm goto is automatically volatile - the naming reflects this.)
+ */
+#define asm_volatile_goto(x...)        do { asm goto(x); asm (""); } while (0)
+
+#ifdef CONFIG_ARCH_USE_BUILTIN_BSWAP
+#define __HAVE_BUILTIN_BSWAP32__
+#define __HAVE_BUILTIN_BSWAP64__
+#define __HAVE_BUILTIN_BSWAP16__
+#endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */
+
+#define KASAN_ABI_VERSION 4
diff --git a/version.m4 b/version.m4
index 5d14353..029631f 100644
--- a/version.m4
+++ b/version.m4
@@ -1,4 +1,4 @@
 m4_define([AC_PACKAGE_NAME],[GNU Mach])
-m4_define([AC_PACKAGE_VERSION],[1.6+git20160114])
+m4_define([AC_PACKAGE_VERSION],[1.6+git20160311])
 m4_define([AC_PACKAGE_BUGREPORT],address@hidden)
 m4_define([AC_PACKAGE_TARNAME],[gnumach])
diff --git a/vm/memory_object_proxy.c b/vm/memory_object_proxy.c
index a64bfcc..01bce2a 100644
--- a/vm/memory_object_proxy.c
+++ b/vm/memory_object_proxy.c
@@ -64,7 +64,7 @@ void
 memory_object_proxy_init (void)
 {
   kmem_cache_init (&memory_object_proxy_cache, "memory_object_proxy",
-                  sizeof (struct memory_object_proxy), 0, NULL, NULL, NULL, 0);
+                  sizeof (struct memory_object_proxy), 0, NULL, 0);
 }
   
 /* Lookup a proxy memory object by its port.  */
diff --git a/vm/pmap.h b/vm/pmap.h
index 134f9c6..9bbcdc3 100644
--- a/vm/pmap.h
+++ b/vm/pmap.h
@@ -67,9 +67,6 @@
 extern vm_offset_t     pmap_steal_memory(vm_size_t);
 /* During VM initialization, report remaining unused physical pages.  */
 extern unsigned int    pmap_free_pages(void);
-/* During VM initialization, use remaining physical pages to allocate page
- * frames.  */
-extern void            pmap_startup(vm_offset_t *, vm_offset_t *);
 /* Initialization, after kernel runs in virtual memory.  */
 extern void            pmap_init(void);
 
@@ -80,18 +77,14 @@ extern void         pmap_init(void);
  *     Otherwise, it must implement
  *             pmap_free_pages
  *             pmap_virtual_space
- *             pmap_next_page
  *             pmap_init
- *     and vm/vm_resident.c implements pmap_steal_memory and pmap_startup
- *     using pmap_free_pages, pmap_next_page, pmap_virtual_space,
- *     and pmap_enter.  pmap_free_pages may over-estimate the number
- *     of unused physical pages, and pmap_next_page may return FALSE
- *     to indicate that there are no more unused pages to return.
+ *     and vm/vm_resident.c implements pmap_steal_memory using
+ *     pmap_free_pages, pmap_virtual_space, and pmap_enter.
+ *
+ *     pmap_free_pages may over-estimate the number of unused physical pages.
  *     However, for best performance pmap_free_pages should be accurate.
  */
 
-/* During VM initialization, return the next unused physical page.  */
-extern boolean_t       pmap_next_page(vm_offset_t *);
 /* During VM initialization, report virtual space available for the kernel.  */
 extern void            pmap_virtual_space(vm_offset_t *, vm_offset_t *);
 #endif /* MACHINE_PAGES */
diff --git a/vm/vm_external.c b/vm/vm_external.c
index 2e2593b..3b1a287 100644
--- a/vm/vm_external.c
+++ b/vm/vm_external.c
@@ -35,6 +35,7 @@
 #include <vm/vm_external.h>
 #include <mach/vm_param.h>
 #include <kern/assert.h>
+#include <string.h>
 
 
 
@@ -69,11 +70,12 @@ vm_external_t       vm_external_create(vm_offset_t size)
                result->existence_map =
                 (char *) 
kmem_cache_alloc(&vm_object_small_existence_map_cache);
                result->existence_size = SMALL_SIZE;
-       } else if (bytes <= LARGE_SIZE) {
+       } else {
                result->existence_map =
                 (char *) 
kmem_cache_alloc(&vm_object_large_existence_map_cache);
                result->existence_size = LARGE_SIZE;
        }
+       memset (result->existence_map, 0, result->existence_size);
        return(result);
 }
 
@@ -138,13 +140,13 @@ void              vm_external_module_initialize(void)
        vm_size_t       size = (vm_size_t) sizeof(struct vm_external);
 
        kmem_cache_init(&vm_external_cache, "vm_external", size, 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
 
        kmem_cache_init(&vm_object_small_existence_map_cache,
                        "small_existence_map", SMALL_SIZE, 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
 
        kmem_cache_init(&vm_object_large_existence_map_cache,
                        "large_existence_map", LARGE_SIZE, 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
 }
diff --git a/vm/vm_external.h b/vm/vm_external.h
index 55c9e48..4e44ddf 100644
--- a/vm/vm_external.h
+++ b/vm/vm_external.h
@@ -46,9 +46,14 @@ typedef struct vm_external {
                                         * been written to backing
                                         * storage.
                                         */
+#if 0
+       /* XXX: Currently, existence_count is not used.  I guess it
+          could be useful to get rid of the map if the count drops to
+          zero.  */
        int             existence_count;/* Number of bits turned on in
                                         * existence_map.
                                         */
+#endif
 } *vm_external_t;
 
 #define        VM_EXTERNAL_NULL        ((vm_external_t) 0)
diff --git a/vm/vm_fault.c b/vm/vm_fault.c
index 46779f6..09e2c54 100644
--- a/vm/vm_fault.c
+++ b/vm/vm_fault.c
@@ -105,7 +105,7 @@ extern struct db_watchpoint *db_watchpoint_list;
 void vm_fault_init(void)
 {
        kmem_cache_init(&vm_fault_state_cache, "vm_fault_state",
-                       sizeof(vm_fault_state_t), 0, NULL, NULL, NULL, 0);
+                       sizeof(vm_fault_state_t), 0, NULL, 0);
 }
 
 /*
@@ -607,7 +607,7 @@ vm_fault_return_t vm_fault_page(
                                 *      won't block for pages.
                                 */
 
-                               if (m->fictitious && !vm_page_convert(m, 
FALSE)) {
+                               if (m->fictitious && !vm_page_convert(&m, 
FALSE)) {
                                        VM_PAGE_FREE(m);
                                        vm_fault_cleanup(object, first_m);
                                        return(VM_FAULT_MEMORY_SHORTAGE);
@@ -725,7 +725,7 @@ vm_fault_return_t vm_fault_page(
                        assert(m->object == object);
                        first_m = VM_PAGE_NULL;
 
-                       if (m->fictitious && !vm_page_convert(m, 
!object->internal)) {
+                       if (m->fictitious && !vm_page_convert(&m, 
!object->internal)) {
                                VM_PAGE_FREE(m);
                                vm_fault_cleanup(object, VM_PAGE_NULL);
                                return(VM_FAULT_MEMORY_SHORTAGE);
diff --git a/vm/vm_init.c b/vm/vm_init.c
index 3d1081c..23d5d46 100644
--- a/vm/vm_init.c
+++ b/vm/vm_init.c
@@ -83,4 +83,5 @@ void vm_mem_init(void)
 {
        vm_object_init();
        memory_object_proxy_init();
+       vm_page_info_all();
 }
diff --git a/vm/vm_map.c b/vm/vm_map.c
index 3a231de..89a2b38 100644
--- a/vm/vm_map.c
+++ b/vm/vm_map.c
@@ -126,7 +126,6 @@ MACRO_END
 
 struct kmem_cache    vm_map_cache;             /* cache for vm_map structures 
*/
 struct kmem_cache    vm_map_entry_cache;       /* cache for vm_map_entry 
structures */
-struct kmem_cache    vm_map_kentry_cache;      /* cache for kernel entry 
structures */
 struct kmem_cache    vm_map_copy_cache;        /* cache for vm_map_copy 
structures */
 
 /*
@@ -147,47 +146,24 @@ vm_object_t               vm_submap_object = 
&vm_submap_object_store;
  *     Map and entry structures are allocated from caches -- we must
  *     initialize those caches.
  *
- *     There are three caches of interest:
+ *     There are two caches of interest:
  *
  *     vm_map_cache:           used to allocate maps.
  *     vm_map_entry_cache:     used to allocate map entries.
- *     vm_map_kentry_cache:    used to allocate map entries for the kernel.
  *
- *     Kernel map entries are allocated from a special cache, using a custom
- *     page allocation function to avoid recursion. It would be difficult
- *     (perhaps impossible) for the kernel to allocate more memory to an entry
- *     cache when it became empty since the very act of allocating memory
- *     implies the creation of a new entry.
+ *     We make sure the map entry cache allocates memory directly from the
+ *     physical allocator to avoid recursion with this module.
  */
 
-vm_offset_t    kentry_data;
-vm_size_t      kentry_data_size = KENTRY_DATA_SIZE;
-
-static vm_offset_t kentry_pagealloc(vm_size_t size)
-{
-       vm_offset_t result;
-
-       if (size > kentry_data_size)
-               panic("vm_map: kentry memory exhausted");
-
-       result = kentry_data;
-       kentry_data += size;
-       kentry_data_size -= size;
-       return result;
-}
-
 void vm_map_init(void)
 {
        kmem_cache_init(&vm_map_cache, "vm_map", sizeof(struct vm_map), 0,
-                       NULL, NULL, NULL, 0);
+                       NULL, 0);
        kmem_cache_init(&vm_map_entry_cache, "vm_map_entry",
-                       sizeof(struct vm_map_entry), 0, NULL, NULL, NULL, 0);
-       kmem_cache_init(&vm_map_kentry_cache, "vm_map_kentry",
-                       sizeof(struct vm_map_entry), 0, NULL, kentry_pagealloc,
-                       NULL, KMEM_CACHE_NOCPUPOOL | KMEM_CACHE_NOOFFSLAB
-                             | KMEM_CACHE_NORECLAIM);
+                       sizeof(struct vm_map_entry), 0, NULL,
+                       KMEM_CACHE_NOOFFSLAB | KMEM_CACHE_PHYSMEM);
        kmem_cache_init(&vm_map_copy_cache, "vm_map_copy",
-                       sizeof(struct vm_map_copy), 0, NULL, NULL, NULL, 0);
+                       sizeof(struct vm_map_copy), 0, NULL, 0);
 
        /*
         *      Submap object is initialized by vm_object_init.
@@ -261,15 +237,9 @@ vm_map_t vm_map_create(
 vm_map_entry_t _vm_map_entry_create(map_header)
        const struct vm_map_header *map_header;
 {
-       kmem_cache_t cache;
        vm_map_entry_t  entry;
 
-       if (map_header->entries_pageable)
-           cache = &vm_map_entry_cache;
-       else
-           cache = &vm_map_kentry_cache;
-
-       entry = (vm_map_entry_t) kmem_cache_alloc(cache);
+       entry = (vm_map_entry_t) kmem_cache_alloc(&vm_map_entry_cache);
        if (entry == VM_MAP_ENTRY_NULL)
                panic("vm_map_entry_create");
 
@@ -291,14 +261,9 @@ void _vm_map_entry_dispose(map_header, entry)
        const struct vm_map_header *map_header;
        vm_map_entry_t  entry;
 {
-       kmem_cache_t cache;
-
-       if (map_header->entries_pageable)
-           cache = &vm_map_entry_cache;
-       else
-           cache = &vm_map_kentry_cache;
+       (void)map_header;
 
-       kmem_cache_free(cache, (vm_offset_t) entry);
+       kmem_cache_free(&vm_map_entry_cache, (vm_offset_t) entry);
 }
 
 /*
@@ -2539,15 +2504,8 @@ kern_return_t vm_map_copyout(
             * Mismatches occur when dealing with the default
             * pager.
             */
-           kmem_cache_t        old_cache;
            vm_map_entry_t      next, new;
 
-           /*
-            * Find the cache that the copies were allocated from
-            */
-           old_cache = (copy->cpy_hdr.entries_pageable)
-                       ? &vm_map_entry_cache
-                       : &vm_map_kentry_cache;
            entry = vm_map_copy_first_entry(copy);
 
            /*
@@ -2571,7 +2529,7 @@ kern_return_t vm_map_copyout(
                                vm_map_copy_last_entry(copy),
                                new);
                next = entry->vme_next;
-               kmem_cache_free(old_cache, (vm_offset_t) entry);
+               kmem_cache_free(&vm_map_entry_cache, (vm_offset_t) entry);
                entry = next;
            }
        }
diff --git a/vm/vm_map.h b/vm/vm_map.h
index 9b31f90..b4ba7c7 100644
--- a/vm/vm_map.h
+++ b/vm/vm_map.h
@@ -363,9 +363,6 @@ MACRO_END
  *     Exported procedures that operate on vm_map_t.
  */
 
-extern vm_offset_t     kentry_data;
-extern vm_size_t       kentry_data_size;
-extern int             kentry_count;
 /* Initialize the module */
 extern void            vm_map_init(void);
 
diff --git a/vm/vm_object.c b/vm/vm_object.c
index 6666fcb..bc30128 100644
--- a/vm/vm_object.c
+++ b/vm/vm_object.c
@@ -59,6 +59,11 @@
 #include <ddb/db_output.h>
 #endif /* MACH_KDB */
 
+void memory_object_release(
+       ipc_port_t      pager,
+       pager_request_t pager_request,
+       ipc_port_t      pager_name); /* forward */
+
 /*
  *     Virtual memory objects maintain the actual data
  *     associated with allocated virtual memory.  A given
@@ -159,8 +164,9 @@ vm_object_t         kernel_object = &kernel_object_store;
  *
  *     The kernel may choose to terminate objects from this
  *     queue in order to reclaim storage.  The current policy
- *     is to permit a fixed maximum number of unreferenced
- *     objects (vm_object_cached_max).
+ *     is to let memory pressure dynamically adjust the number
+ *     of unreferenced objects. The pageout daemon attempts to
+ *     collect objects after removing pages from them.
  *
  *     A simple lock (accessed by routines
  *     vm_object_cache_{lock,lock_try,unlock}) governs the
@@ -176,7 +182,6 @@ vm_object_t         kernel_object = &kernel_object_store;
  */
 queue_head_t   vm_object_cached_list;
 int            vm_object_cached_count;
-int            vm_object_cached_max = 4000;    /* may be patched*/
 
 decl_simple_lock_data(,vm_object_cached_lock_data)
 
@@ -262,7 +267,7 @@ vm_object_t vm_object_allocate(
 void vm_object_bootstrap(void)
 {
        kmem_cache_init(&vm_object_cache, "vm_object",
-                       sizeof(struct vm_object), 0, NULL, NULL, NULL, 0);
+                       sizeof(struct vm_object), 0, NULL, 0);
 
        queue_init(&vm_object_cached_list);
        simple_lock_init(&vm_object_cached_lock_data);
@@ -298,6 +303,7 @@ void vm_object_bootstrap(void)
 
        vm_object_template.paging_in_progress = 0;
        vm_object_template.can_persist = FALSE;
+       vm_object_template.cached = FALSE;
        vm_object_template.internal = TRUE;
        vm_object_template.temporary = TRUE;
        vm_object_template.alive = TRUE;
@@ -344,6 +350,60 @@ void vm_object_init(void)
 }
 
 /*
+ *     Object cache management functions.
+ *
+ *     Both the cache and the object must be locked
+ *     before calling these functions.
+ */
+
+static void vm_object_cache_add(
+       vm_object_t     object)
+{
+       assert(!object->cached);
+       queue_enter(&vm_object_cached_list, object, vm_object_t, cached_list);
+       vm_object_cached_count++;
+       vm_object_cached_pages_update(object->resident_page_count);
+       object->cached = TRUE;
+}
+
+static void vm_object_cache_remove(
+       vm_object_t     object)
+{
+       assert(object->cached);
+       queue_remove(&vm_object_cached_list, object, vm_object_t, cached_list);
+       vm_object_cached_count--;
+       vm_object_cached_pages_update(-object->resident_page_count);
+       object->cached = FALSE;
+}
+
+void vm_object_collect(
+       register vm_object_t    object)
+{
+       vm_object_unlock(object);
+
+       /*
+        *      The cache lock must be acquired in the proper order.
+        */
+
+       vm_object_cache_lock();
+       vm_object_lock(object);
+
+       /*
+        *      If the object was referenced while the lock was
+        *      dropped, cancel the termination.
+        */
+
+       if (!vm_object_collectable(object)) {
+               vm_object_unlock(object);
+               vm_object_cache_unlock();
+               return;
+       }
+
+       vm_object_cache_remove(object);
+       vm_object_terminate(object);
+}
+
+/*
  *     vm_object_reference:
  *
  *     Gets another reference to the given object.
@@ -403,103 +463,31 @@ void vm_object_deallocate(
 
                /*
                 *      See whether this object can persist.  If so, enter
-                *      it in the cache, then deactivate all of its
-                *      pages.
+                *      it in the cache.
                 */
-               if (object->can_persist) {
-                       boolean_t       overflow;
-
-                       /*
-                        *      Enter the object onto the queue
-                        *      of "cached" objects.  Remember whether
-                        *      we've caused the queue to overflow,
-                        *      as a hint.
-                        */
-
-                       queue_enter(&vm_object_cached_list, object,
-                               vm_object_t, cached_list);
-                       overflow = (++vm_object_cached_count > 
vm_object_cached_max);
-                       
vm_object_cached_pages_update(object->resident_page_count);
+               if (object->can_persist && (object->resident_page_count > 0)) {
+                       vm_object_cache_add(object);
                        vm_object_cache_unlock();
-
-                       vm_object_deactivate_pages(object);
                        vm_object_unlock(object);
+                       return;
+               }
 
-                       /*
-                        *      If we didn't overflow, or if the queue has
-                        *      been reduced back to below the specified
-                        *      minimum, then quit.
-                        */
-                       if (!overflow)
-                               return;
-
-                       while (TRUE) {
-                               vm_object_cache_lock();
-                               if (vm_object_cached_count <=
-                                   vm_object_cached_max) {
-                                       vm_object_cache_unlock();
-                                       return;
-                               }
-
-                               /*
-                                *      If we must trim down the queue, take
-                                *      the first object, and proceed to
-                                *      terminate it instead of the original
-                                *      object.  Have to wait for pager init.
-                                *  if it's in progress.
-                                */
-                               object= (vm_object_t)
-                                   queue_first(&vm_object_cached_list);
-                               vm_object_lock(object);
-
-                               if (!(object->pager_created &&
-                                   !object->pager_initialized)) {
-
-                                       /*
-                                        *  Ok to terminate, hang on to lock.
-                                        */
-                                       break;
-                               }
-
-                               vm_object_assert_wait(object,
-                                       VM_OBJECT_EVENT_INITIALIZED, FALSE);
-                               vm_object_unlock(object);
-                               vm_object_cache_unlock();
-                               thread_block((void (*)()) 0);
-
-                               /*
-                                *  Continue loop to check if cache still
-                                *  needs to be trimmed.
-                                */
-                       }
+               if (object->pager_created &&
+                   !object->pager_initialized) {
 
                        /*
-                        *      Actually remove object from cache.
+                        *      Have to wait for initialization.
+                        *      Put reference back and retry
+                        *      when it's initialized.
                         */
 
-                       queue_remove(&vm_object_cached_list, object,
-                                       vm_object_t, cached_list);
-                       vm_object_cached_count--;
-
-                       assert(object->ref_count == 0);
-               }
-               else {
-                       if (object->pager_created &&
-                           !object->pager_initialized) {
-
-                               /*
-                                *      Have to wait for initialization.
-                                *      Put reference back and retry
-                                *      when it's initialized.
-                                */
-                               object->ref_count++;
-                               vm_object_assert_wait(object,
-                                       VM_OBJECT_EVENT_INITIALIZED, FALSE);
-                               vm_object_unlock(object);
-                               vm_object_cache_unlock();
-                               thread_block((void (*)()) 0);
-                               continue;
-                         }
+                       object->ref_count++;
+                       vm_object_assert_wait(object,
+                               VM_OBJECT_EVENT_INITIALIZED, FALSE);
+                       vm_object_unlock(object);
+                       vm_object_cache_unlock();
+                       thread_block((void (*)()) 0);
+                       continue;
                }
 
                /*
@@ -630,6 +618,7 @@ void vm_object_terminate(
 
        assert(object->ref_count == 0);
        assert(object->paging_in_progress == 0);
+       assert(!object->cached);
 
        /*
         *      Throw away port rights... note that they may
@@ -862,28 +851,6 @@ kern_return_t memory_object_destroy(
 }
 
 /*
- *     vm_object_deactivate_pages
- *
- *     Deactivate all pages in the specified object.  (Keep its pages
- *     in memory even though it is no longer referenced.)
- *
- *     The object must be locked.
- */
-void vm_object_deactivate_pages(
-       vm_object_t     object)
-{
-       vm_page_t       p;
-
-       queue_iterate(&object->memq, p, vm_page_t, listq) {
-               vm_page_lock_queues();
-               if (!p->busy)
-                       vm_page_deactivate(p);
-               vm_page_unlock_queues();
-       }
-}
-
-
-/*
  *     Routine:        vm_object_pmap_protect
  *
  *     Purpose:
@@ -1837,12 +1804,8 @@ vm_object_t vm_object_lookup(
 
                        assert(object->alive);
 
-                       if (object->ref_count == 0) {
-                               queue_remove(&vm_object_cached_list, object,
-                                            vm_object_t, cached_list);
-                               vm_object_cached_count--;
-                               
vm_object_cached_pages_update(-object->resident_page_count);
-                       }
+                       if (object->ref_count == 0)
+                               vm_object_cache_remove(object);
 
                        object->ref_count++;
                        vm_object_unlock(object);
@@ -1869,12 +1832,8 @@ vm_object_t vm_object_lookup_name(
 
                        assert(object->alive);
 
-                       if (object->ref_count == 0) {
-                               queue_remove(&vm_object_cached_list, object,
-                                            vm_object_t, cached_list);
-                               vm_object_cached_count--;
-                               
vm_object_cached_pages_update(-object->resident_page_count);
-                       }
+                       if (object->ref_count == 0)
+                               vm_object_cache_remove(object);
 
                        object->ref_count++;
                        vm_object_unlock(object);
@@ -1906,12 +1865,8 @@ void vm_object_destroy(
 
        object = (vm_object_t) pager->ip_kobject;
        vm_object_lock(object);
-       if (object->ref_count == 0) {
-               queue_remove(&vm_object_cached_list, object,
-                               vm_object_t, cached_list);
-               vm_object_cached_count--;
-               vm_object_cached_pages_update(-object->resident_page_count);
-       }
+       if (object->ref_count == 0)
+               vm_object_cache_remove(object);
        object->ref_count++;
 
        object->can_persist = FALSE;
@@ -2059,12 +2014,8 @@ restart:
 
        if ((object != VM_OBJECT_NULL) && !must_init) {
                vm_object_lock(object);
-               if (object->ref_count == 0) {
-                       queue_remove(&vm_object_cached_list, object,
-                                       vm_object_t, cached_list);
-                       vm_object_cached_count--;
-                       
vm_object_cached_pages_update(-object->resident_page_count);
-               }
+               if (object->ref_count == 0)
+                       vm_object_cache_remove(object);
                object->ref_count++;
                vm_object_unlock(object);
 
@@ -2573,6 +2524,7 @@ void vm_object_collapse(
                        );
 
                        assert(backing_object->alive);
+                       assert(!backing_object->cached);
                        backing_object->alive = FALSE;
                        vm_object_unlock(backing_object);
 
@@ -2701,7 +2653,7 @@ void vm_object_page_remove(
         *      It balances vm_object_lookup vs iteration.
         */
 
-       if (atop(end - start) < (unsigned)object->resident_page_count/16) {
+       if (atop(end - start) < object->resident_page_count/16) {
                vm_object_page_remove_lookup++;
 
                for (; start < end; start += PAGE_SIZE) {
@@ -2891,7 +2843,8 @@ vm_object_page_map(
                VM_PAGE_FREE(old_page);
            }
 
-           vm_page_init(m, addr);
+           vm_page_init(m);
+           m->phys_addr = addr;
            m->private = TRUE;          /* don`t free page */
            m->wire_count = 1;
            vm_page_lock_queues();
@@ -2927,7 +2880,7 @@ void vm_object_print(
                (vm_offset_t) object, (vm_offset_t) object->size,
                object->ref_count);
        printf("\n");
-       iprintf("%d resident pages,", object->resident_page_count);
+       iprintf("%lu resident pages,", object->resident_page_count);
         printf(" %d absent pages,", object->absent_count);
         printf(" %d paging ops\n", object->paging_in_progress);
        indent += 1;
diff --git a/vm/vm_object.h b/vm/vm_object.h
index 71c8545..eb8a0c2 100644
--- a/vm/vm_object.h
+++ b/vm/vm_object.h
@@ -72,7 +72,7 @@ struct vm_object {
                                                 */
 
        int                     ref_count;      /* Number of references */
-       int                     resident_page_count;
+       unsigned long           resident_page_count;
                                                /* number of resident pages */
 
        struct vm_object        *copy;          /* Object that should receive
@@ -148,8 +148,9 @@ struct vm_object {
                                                 */
        /* boolean_t */         use_shared_copy : 1,/* Use shared (i.e.,
                                                 * delayed) copy on write */
-       /* boolean_t */         shadowed: 1;    /* Shadow may exist */
+       /* boolean_t */         shadowed: 1,    /* Shadow may exist */
 
+       /* boolean_t */         cached: 1;      /* Object is cached */
        queue_chain_t           cached_list;    /* Attachment point for the list
                                                 * of objects cached as a result
                                                 * of their can_persist value
@@ -169,6 +170,7 @@ vm_object_t kernel_object;          /* the single kernel 
object */
 
 extern void            vm_object_bootstrap(void);
 extern void            vm_object_init(void);
+extern void            vm_object_collect(vm_object_t);
 extern void            vm_object_terminate(vm_object_t);
 extern vm_object_t     vm_object_allocate(vm_size_t);
 extern void            vm_object_reference(vm_object_t);
@@ -290,6 +292,10 @@ vm_object_t vm_object_copy_delayed(
  *     Routines implemented as macros
  */
 
+#define vm_object_collectable(object)                                  \
+       (((object)->ref_count == 0)                                     \
+       && ((object)->resident_page_count == 0))
+
 #define        vm_object_paging_begin(object)                                  
\
        ((object)->paging_in_progress++)
 
diff --git a/vm/vm_page.c b/vm/vm_page.c
new file mode 100644
index 0000000..a868fce
--- /dev/null
+++ b/vm/vm_page.c
@@ -0,0 +1,782 @@
+/*
+ * Copyright (c) 2010-2014 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * This implementation uses the binary buddy system to manage its heap.
+ * Descriptions of the buddy system can be found in the following works :
+ * - "UNIX Internals: The New Frontiers", by Uresh Vahalia.
+ * - "Dynamic Storage Allocation: A Survey and Critical Review",
+ *    by Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles.
+ *
+ * In addition, this allocator uses per-CPU pools of pages for order 0
+ * (i.e. single page) allocations. These pools act as caches (but are named
+ * differently to avoid confusion with CPU caches) that reduce contention on
+ * multiprocessor systems. When a pool is empty and cannot provide a page,
+ * it is filled by transferring multiple pages from the backend buddy system.
+ * The symmetric case is handled likewise.
+ */
+
+#include <string.h>
+#include <kern/assert.h>
+#include <kern/cpu_number.h>
+#include <kern/debug.h>
+#include <kern/list.h>
+#include <kern/lock.h>
+#include <kern/macros.h>
+#include <kern/printf.h>
+#include <kern/thread.h>
+#include <mach/vm_param.h>
+#include <machine/pmap.h>
+#include <sys/types.h>
+#include <vm/vm_page.h>
+
+#define __init
+#define __initdata
+#define __read_mostly
+
+#define thread_pin()
+#define thread_unpin()
+
+/*
+ * Number of free block lists per segment.
+ */
+#define VM_PAGE_NR_FREE_LISTS 11
+
+/*
+ * The size of a CPU pool is computed by dividing the number of pages in its
+ * containing segment by this value.
+ */
+#define VM_PAGE_CPU_POOL_RATIO 1024
+
+/*
+ * Maximum number of pages in a CPU pool.
+ */
+#define VM_PAGE_CPU_POOL_MAX_SIZE 128
+
+/*
+ * The transfer size of a CPU pool is computed by dividing the pool size by
+ * this value.
+ */
+#define VM_PAGE_CPU_POOL_TRANSFER_RATIO 2
+
+/*
+ * Per-processor cache of pages.
+ */
+struct vm_page_cpu_pool {
+    simple_lock_data_t lock;
+    int size;
+    int transfer_size;
+    int nr_pages;
+    struct list pages;
+} __aligned(CPU_L1_SIZE);
+
+/*
+ * Special order value for pages that aren't in a free list. Such pages are
+ * either allocated, or part of a free block of pages but not the head page.
+ */
+#define VM_PAGE_ORDER_UNLISTED ((unsigned short)-1)
+
+/*
+ * Doubly-linked list of free blocks.
+ */
+struct vm_page_free_list {
+    unsigned long size;
+    struct list blocks;
+};
+
+/*
+ * Segment name buffer size.
+ */
+#define VM_PAGE_NAME_SIZE 16
+
+/*
+ * Segment of contiguous memory.
+ */
+struct vm_page_seg {
+    struct vm_page_cpu_pool cpu_pools[NCPUS];
+
+    phys_addr_t start;
+    phys_addr_t end;
+    struct vm_page *pages;
+    struct vm_page *pages_end;
+    simple_lock_data_t lock;
+    struct vm_page_free_list free_lists[VM_PAGE_NR_FREE_LISTS];
+    unsigned long nr_free_pages;
+};
+
+/*
+ * Bootstrap information about a segment.
+ */
+struct vm_page_boot_seg {
+    phys_addr_t start;
+    phys_addr_t end;
+    phys_addr_t avail_start;
+    phys_addr_t avail_end;
+};
+
+static int vm_page_is_ready __read_mostly;
+
+/*
+ * Segment table.
+ *
+ * The system supports a maximum of 4 segments :
+ *  - DMA: suitable for DMA
+ *  - DMA32: suitable for DMA when devices support 32-bits addressing
+ *  - DIRECTMAP: direct physical mapping, allows direct access from
+ *    the kernel with a simple offset translation
+ *  - HIGHMEM: must be mapped before it can be accessed
+ *
+ * Segments are ordered by priority, 0 being the lowest priority. Their
+ * relative priorities are DMA < DMA32 < DIRECTMAP < HIGHMEM. Some segments
+ * may actually be aliases for others, e.g. if DMA is always possible from
+ * the direct physical mapping, DMA and DMA32 are aliases for DIRECTMAP,
+ * in which case the segment table contains DIRECTMAP and HIGHMEM only.
+ */
+static struct vm_page_seg vm_page_segs[VM_PAGE_MAX_SEGS];
+
+/*
+ * Bootstrap segment table.
+ */
+static struct vm_page_boot_seg vm_page_boot_segs[VM_PAGE_MAX_SEGS] __initdata;
+
+/*
+ * Number of loaded segments.
+ */
+static unsigned int vm_page_segs_size __read_mostly;
+
+static void __init
+vm_page_init_pa(struct vm_page *page, unsigned short seg_index, phys_addr_t pa)
+{
+    memset(page, 0, sizeof(*page));
+    vm_page_init(page); /* vm_resident members */
+    page->type = VM_PT_RESERVED;
+    page->seg_index = seg_index;
+    page->order = VM_PAGE_ORDER_UNLISTED;
+    page->priv = NULL;
+    page->phys_addr = pa;
+}
+
+void
+vm_page_set_type(struct vm_page *page, unsigned int order, unsigned short type)
+{
+    unsigned int i, nr_pages;
+
+    nr_pages = 1 << order;
+
+    for (i = 0; i < nr_pages; i++)
+        page[i].type = type;
+}
+
+static void __init
+vm_page_free_list_init(struct vm_page_free_list *free_list)
+{
+    free_list->size = 0;
+    list_init(&free_list->blocks);
+}
+
+static inline void
+vm_page_free_list_insert(struct vm_page_free_list *free_list,
+                         struct vm_page *page)
+{
+    assert(page->order == VM_PAGE_ORDER_UNLISTED);
+
+    free_list->size++;
+    list_insert_head(&free_list->blocks, &page->node);
+}
+
+static inline void
+vm_page_free_list_remove(struct vm_page_free_list *free_list,
+                         struct vm_page *page)
+{
+    assert(page->order != VM_PAGE_ORDER_UNLISTED);
+
+    free_list->size--;
+    list_remove(&page->node);
+}
+
+static struct vm_page *
+vm_page_seg_alloc_from_buddy(struct vm_page_seg *seg, unsigned int order)
+{
+    struct vm_page_free_list *free_list = free_list;
+    struct vm_page *page, *buddy;
+    unsigned int i;
+
+    assert(order < VM_PAGE_NR_FREE_LISTS);
+
+    for (i = order; i < VM_PAGE_NR_FREE_LISTS; i++) {
+        free_list = &seg->free_lists[i];
+
+        if (free_list->size != 0)
+            break;
+    }
+
+    if (i == VM_PAGE_NR_FREE_LISTS)
+        return NULL;
+
+    page = list_first_entry(&free_list->blocks, struct vm_page, node);
+    vm_page_free_list_remove(free_list, page);
+    page->order = VM_PAGE_ORDER_UNLISTED;
+
+    while (i > order) {
+        i--;
+        buddy = &page[1 << i];
+        vm_page_free_list_insert(&seg->free_lists[i], buddy);
+        buddy->order = i;
+    }
+
+    seg->nr_free_pages -= (1 << order);
+    return page;
+}
+
+static void
+vm_page_seg_free_to_buddy(struct vm_page_seg *seg, struct vm_page *page,
+                          unsigned int order)
+{
+    struct vm_page *buddy;
+    phys_addr_t pa, buddy_pa;
+    unsigned int nr_pages;
+
+    assert(page >= seg->pages);
+    assert(page < seg->pages_end);
+    assert(page->order == VM_PAGE_ORDER_UNLISTED);
+    assert(order < VM_PAGE_NR_FREE_LISTS);
+
+    nr_pages = (1 << order);
+    pa = page->phys_addr;
+
+    while (order < (VM_PAGE_NR_FREE_LISTS - 1)) {
+        buddy_pa = pa ^ vm_page_ptoa(1 << order);
+
+        if ((buddy_pa < seg->start) || (buddy_pa >= seg->end))
+            break;
+
+        buddy = &seg->pages[vm_page_atop(buddy_pa - seg->start)];
+
+        if (buddy->order != order)
+            break;
+
+        vm_page_free_list_remove(&seg->free_lists[order], buddy);
+        buddy->order = VM_PAGE_ORDER_UNLISTED;
+        order++;
+        pa &= -vm_page_ptoa(1 << order);
+        page = &seg->pages[vm_page_atop(pa - seg->start)];
+    }
+
+    vm_page_free_list_insert(&seg->free_lists[order], page);
+    page->order = order;
+    seg->nr_free_pages += nr_pages;
+}
+
+static void __init
+vm_page_cpu_pool_init(struct vm_page_cpu_pool *cpu_pool, int size)
+{
+    simple_lock_init(&cpu_pool->lock);
+    cpu_pool->size = size;
+    cpu_pool->transfer_size = (size + VM_PAGE_CPU_POOL_TRANSFER_RATIO - 1)
+                              / VM_PAGE_CPU_POOL_TRANSFER_RATIO;
+    cpu_pool->nr_pages = 0;
+    list_init(&cpu_pool->pages);
+}
+
+static inline struct vm_page_cpu_pool *
+vm_page_cpu_pool_get(struct vm_page_seg *seg)
+{
+    return &seg->cpu_pools[cpu_number()];
+}
+
+static inline struct vm_page *
+vm_page_cpu_pool_pop(struct vm_page_cpu_pool *cpu_pool)
+{
+    struct vm_page *page;
+
+    assert(cpu_pool->nr_pages != 0);
+    cpu_pool->nr_pages--;
+    page = list_first_entry(&cpu_pool->pages, struct vm_page, node);
+    list_remove(&page->node);
+    return page;
+}
+
+static inline void
+vm_page_cpu_pool_push(struct vm_page_cpu_pool *cpu_pool, struct vm_page *page)
+{
+    assert(cpu_pool->nr_pages < cpu_pool->size);
+    cpu_pool->nr_pages++;
+    list_insert_head(&cpu_pool->pages, &page->node);
+}
+
+static int
+vm_page_cpu_pool_fill(struct vm_page_cpu_pool *cpu_pool,
+                      struct vm_page_seg *seg)
+{
+    struct vm_page *page;
+    int i;
+
+    assert(cpu_pool->nr_pages == 0);
+
+    simple_lock(&seg->lock);
+
+    for (i = 0; i < cpu_pool->transfer_size; i++) {
+        page = vm_page_seg_alloc_from_buddy(seg, 0);
+
+        if (page == NULL)
+            break;
+
+        vm_page_cpu_pool_push(cpu_pool, page);
+    }
+
+    simple_unlock(&seg->lock);
+
+    return i;
+}
+
+static void
+vm_page_cpu_pool_drain(struct vm_page_cpu_pool *cpu_pool,
+                       struct vm_page_seg *seg)
+{
+    struct vm_page *page;
+    int i;
+
+    assert(cpu_pool->nr_pages == cpu_pool->size);
+
+    simple_lock(&seg->lock);
+
+    for (i = cpu_pool->transfer_size; i > 0; i--) {
+        page = vm_page_cpu_pool_pop(cpu_pool);
+        vm_page_seg_free_to_buddy(seg, page, 0);
+    }
+
+    simple_unlock(&seg->lock);
+}
+
+static phys_addr_t __init
+vm_page_seg_size(struct vm_page_seg *seg)
+{
+    return seg->end - seg->start;
+}
+
+static int __init
+vm_page_seg_compute_pool_size(struct vm_page_seg *seg)
+{
+    phys_addr_t size;
+
+    size = vm_page_atop(vm_page_seg_size(seg)) / VM_PAGE_CPU_POOL_RATIO;
+
+    if (size == 0)
+        size = 1;
+    else if (size > VM_PAGE_CPU_POOL_MAX_SIZE)
+        size = VM_PAGE_CPU_POOL_MAX_SIZE;
+
+    return size;
+}
+
+static void __init
+vm_page_seg_init(struct vm_page_seg *seg, phys_addr_t start, phys_addr_t end,
+                 struct vm_page *pages)
+{
+    phys_addr_t pa;
+    int pool_size;
+    unsigned int i;
+
+    seg->start = start;
+    seg->end = end;
+    pool_size = vm_page_seg_compute_pool_size(seg);
+
+    for (i = 0; i < ARRAY_SIZE(seg->cpu_pools); i++)
+        vm_page_cpu_pool_init(&seg->cpu_pools[i], pool_size);
+
+    seg->pages = pages;
+    seg->pages_end = pages + vm_page_atop(vm_page_seg_size(seg));
+    simple_lock_init(&seg->lock);
+
+    for (i = 0; i < ARRAY_SIZE(seg->free_lists); i++)
+        vm_page_free_list_init(&seg->free_lists[i]);
+
+    seg->nr_free_pages = 0;
+    i = seg - vm_page_segs;
+
+    for (pa = seg->start; pa < seg->end; pa += PAGE_SIZE)
+        vm_page_init_pa(&pages[vm_page_atop(pa - seg->start)], i, pa);
+}
+
+static struct vm_page *
+vm_page_seg_alloc(struct vm_page_seg *seg, unsigned int order,
+                  unsigned short type)
+{
+    struct vm_page_cpu_pool *cpu_pool;
+    struct vm_page *page;
+    int filled;
+
+    assert(order < VM_PAGE_NR_FREE_LISTS);
+
+    if (order == 0) {
+        thread_pin();
+        cpu_pool = vm_page_cpu_pool_get(seg);
+        simple_lock(&cpu_pool->lock);
+
+        if (cpu_pool->nr_pages == 0) {
+            filled = vm_page_cpu_pool_fill(cpu_pool, seg);
+
+            if (!filled) {
+                simple_unlock(&cpu_pool->lock);
+                thread_unpin();
+                return NULL;
+            }
+        }
+
+        page = vm_page_cpu_pool_pop(cpu_pool);
+        simple_unlock(&cpu_pool->lock);
+        thread_unpin();
+    } else {
+        simple_lock(&seg->lock);
+        page = vm_page_seg_alloc_from_buddy(seg, order);
+        simple_unlock(&seg->lock);
+
+        if (page == NULL)
+            return NULL;
+    }
+
+    assert(page->type == VM_PT_FREE);
+    vm_page_set_type(page, order, type);
+    return page;
+}
+
+static void
+vm_page_seg_free(struct vm_page_seg *seg, struct vm_page *page,
+                 unsigned int order)
+{
+    struct vm_page_cpu_pool *cpu_pool;
+
+    assert(page->type != VM_PT_FREE);
+    assert(order < VM_PAGE_NR_FREE_LISTS);
+
+    vm_page_set_type(page, order, VM_PT_FREE);
+
+    if (order == 0) {
+        thread_pin();
+        cpu_pool = vm_page_cpu_pool_get(seg);
+        simple_lock(&cpu_pool->lock);
+
+        if (cpu_pool->nr_pages == cpu_pool->size)
+            vm_page_cpu_pool_drain(cpu_pool, seg);
+
+        vm_page_cpu_pool_push(cpu_pool, page);
+        simple_unlock(&cpu_pool->lock);
+        thread_unpin();
+    } else {
+        simple_lock(&seg->lock);
+        vm_page_seg_free_to_buddy(seg, page, order);
+        simple_unlock(&seg->lock);
+    }
+}
+
+void __init
+vm_page_load(unsigned int seg_index, phys_addr_t start, phys_addr_t end,
+             phys_addr_t avail_start, phys_addr_t avail_end)
+{
+    struct vm_page_boot_seg *seg;
+
+    assert(seg_index < ARRAY_SIZE(vm_page_boot_segs));
+    assert(vm_page_aligned(start));
+    assert(vm_page_aligned(end));
+    assert(vm_page_aligned(avail_start));
+    assert(vm_page_aligned(avail_end));
+    assert(start < end);
+    assert(start <= avail_start);
+    assert(avail_end <= end);
+    assert(vm_page_segs_size < ARRAY_SIZE(vm_page_boot_segs));
+
+    seg = &vm_page_boot_segs[seg_index];
+    seg->start = start;
+    seg->end = end;
+    seg->avail_start = avail_start;
+    seg->avail_end = avail_end;
+    vm_page_segs_size++;
+}
+
+int
+vm_page_ready(void)
+{
+    return vm_page_is_ready;
+}
+
+static unsigned int
+vm_page_select_alloc_seg(unsigned int selector)
+{
+    unsigned int seg_index;
+
+    switch (selector) {
+    case VM_PAGE_SEL_DMA:
+        seg_index = VM_PAGE_SEG_DMA;
+        break;
+    case VM_PAGE_SEL_DMA32:
+        seg_index = VM_PAGE_SEG_DMA32;
+        break;
+    case VM_PAGE_SEL_DIRECTMAP:
+        seg_index = VM_PAGE_SEG_DIRECTMAP;
+        break;
+    case VM_PAGE_SEL_HIGHMEM:
+        seg_index = VM_PAGE_SEG_HIGHMEM;
+        break;
+    default:
+        panic("vm_page: invalid selector");
+    }
+
+    return MIN(vm_page_segs_size - 1, seg_index);
+}
+
+static int __init
+vm_page_boot_seg_loaded(const struct vm_page_boot_seg *seg)
+{
+    return (seg->end != 0);
+}
+
+static void __init
+vm_page_check_boot_segs(void)
+{
+    unsigned int i;
+    int expect_loaded;
+
+    if (vm_page_segs_size == 0)
+        panic("vm_page: no physical memory loaded");
+
+    for (i = 0; i < ARRAY_SIZE(vm_page_boot_segs); i++) {
+        expect_loaded = (i < vm_page_segs_size);
+
+        if (vm_page_boot_seg_loaded(&vm_page_boot_segs[i]) == expect_loaded)
+            continue;
+
+        panic("vm_page: invalid boot segment table");
+    }
+}
+
+static phys_addr_t __init
+vm_page_boot_seg_size(struct vm_page_boot_seg *seg)
+{
+    return seg->end - seg->start;
+}
+
+static phys_addr_t __init
+vm_page_boot_seg_avail_size(struct vm_page_boot_seg *seg)
+{
+    return seg->avail_end - seg->avail_start;
+}
+
+unsigned long __init
+vm_page_bootalloc(size_t size)
+{
+    struct vm_page_boot_seg *seg;
+    phys_addr_t pa;
+    unsigned int i;
+
+    for (i = vm_page_select_alloc_seg(VM_PAGE_SEL_DIRECTMAP);
+         i < vm_page_segs_size;
+         i--) {
+        seg = &vm_page_boot_segs[i];
+
+        if (size <= vm_page_boot_seg_avail_size(seg)) {
+            pa = seg->avail_start;
+            seg->avail_start += vm_page_round(size);
+            return pa;
+        }
+    }
+
+    panic("vm_page: no physical memory available");
+}
+
+void __init
+vm_page_setup(void)
+{
+    struct vm_page_boot_seg *boot_seg;
+    struct vm_page_seg *seg;
+    struct vm_page *table, *page, *end;
+    size_t nr_pages, table_size;
+    unsigned long va;
+    unsigned int i;
+    phys_addr_t pa;
+
+    vm_page_check_boot_segs();
+
+    /*
+     * Compute the page table size.
+     */
+    nr_pages = 0;
+
+    for (i = 0; i < vm_page_segs_size; i++)
+        nr_pages += vm_page_atop(vm_page_boot_seg_size(&vm_page_boot_segs[i]));
+
+    table_size = vm_page_round(nr_pages * sizeof(struct vm_page));
+    printf("vm_page: page table size: %lu entries (%luk)\n", nr_pages,
+           table_size >> 10);
+    table = (struct vm_page *)pmap_steal_memory(table_size);
+    va = (unsigned long)table;
+
+    /*
+     * Initialize the segments, associating them to the page table. When
+     * the segments are initialized, all their pages are set allocated.
+     * Pages are then released, which populates the free lists.
+     */
+    for (i = 0; i < vm_page_segs_size; i++) {
+        seg = &vm_page_segs[i];
+        boot_seg = &vm_page_boot_segs[i];
+        vm_page_seg_init(seg, boot_seg->start, boot_seg->end, table);
+        page = seg->pages + vm_page_atop(boot_seg->avail_start
+                                         - boot_seg->start);
+        end = seg->pages + vm_page_atop(boot_seg->avail_end
+                                        - boot_seg->start);
+
+        while (page < end) {
+            page->type = VM_PT_FREE;
+            vm_page_seg_free_to_buddy(seg, page, 0);
+            page++;
+        }
+
+        table += vm_page_atop(vm_page_seg_size(seg));
+    }
+
+    while (va < (unsigned long)table) {
+        pa = pmap_extract(kernel_pmap, va);
+        page = vm_page_lookup_pa(pa);
+        assert((page != NULL) && (page->type == VM_PT_RESERVED));
+        page->type = VM_PT_TABLE;
+        va += PAGE_SIZE;
+    }
+
+    vm_page_is_ready = 1;
+}
+
+void __init
+vm_page_manage(struct vm_page *page)
+{
+    assert(page->seg_index < ARRAY_SIZE(vm_page_segs));
+    assert(page->type == VM_PT_RESERVED);
+
+    vm_page_set_type(page, 0, VM_PT_FREE);
+    vm_page_seg_free_to_buddy(&vm_page_segs[page->seg_index], page, 0);
+}
+
+struct vm_page *
+vm_page_lookup_pa(phys_addr_t pa)
+{
+    struct vm_page_seg *seg;
+    unsigned int i;
+
+    for (i = 0; i < vm_page_segs_size; i++) {
+        seg = &vm_page_segs[i];
+
+        if ((pa >= seg->start) && (pa < seg->end))
+            return &seg->pages[vm_page_atop(pa - seg->start)];
+    }
+
+    return NULL;
+}
+
+struct vm_page *
+vm_page_alloc_pa(unsigned int order, unsigned int selector, unsigned short 
type)
+{
+    struct vm_page *page;
+    unsigned int i;
+
+    for (i = vm_page_select_alloc_seg(selector); i < vm_page_segs_size; i--) {
+        page = vm_page_seg_alloc(&vm_page_segs[i], order, type);
+
+        if (page != NULL)
+            return page;
+    }
+
+    if (type == VM_PT_PMAP)
+        panic("vm_page: unable to allocate pmap page");
+
+    return NULL;
+}
+
+void
+vm_page_free_pa(struct vm_page *page, unsigned int order)
+{
+    assert(page != NULL);
+    assert(page->seg_index < ARRAY_SIZE(vm_page_segs));
+
+    vm_page_seg_free(&vm_page_segs[page->seg_index], page, order);
+}
+
+const char *
+vm_page_seg_name(unsigned int seg_index)
+{
+    /* Don't use a switch statement since segments can be aliased */
+    if (seg_index == VM_PAGE_SEG_HIGHMEM)
+        return "HIGHMEM";
+    else if (seg_index == VM_PAGE_SEG_DIRECTMAP)
+        return "DIRECTMAP";
+    else if (seg_index == VM_PAGE_SEG_DMA32)
+        return "DMA32";
+    else if (seg_index == VM_PAGE_SEG_DMA)
+        return "DMA";
+    else
+        panic("vm_page: invalid segment index");
+}
+
+void
+vm_page_info_all(void)
+{
+    struct vm_page_seg *seg;
+    unsigned long pages;
+    unsigned int i;
+
+    for (i = 0; i < vm_page_segs_size; i++) {
+        seg = &vm_page_segs[i];
+        pages = (unsigned long)(seg->pages_end - seg->pages);
+        printf("vm_page: %s: pages: %lu (%luM), free: %lu (%luM)\n",
+               vm_page_seg_name(i), pages, pages >> (20 - PAGE_SHIFT),
+               seg->nr_free_pages, seg->nr_free_pages >> (20 - PAGE_SHIFT));
+    }
+}
+
+phys_addr_t
+vm_page_mem_size(void)
+{
+    phys_addr_t total;
+    unsigned int i;
+
+    total = 0;
+
+    for (i = 0; i < vm_page_segs_size; i++) {
+        /* XXX */
+        if (i > VM_PAGE_SEG_DIRECTMAP)
+            continue;
+
+        total += vm_page_seg_size(&vm_page_segs[i]);
+    }
+
+    return total;
+}
+
+unsigned long
+vm_page_mem_free(void)
+{
+    unsigned long total;
+    unsigned int i;
+
+    total = 0;
+
+    for (i = 0; i < vm_page_segs_size; i++) {
+        /* XXX */
+        if (i >  VM_PAGE_SEG_DIRECTMAP)
+            continue;
+
+        total += vm_page_segs[i].nr_free_pages;
+    }
+
+    return total;
+}
diff --git a/vm/vm_page.h b/vm/vm_page.h
index e6a8c49..f2e20a7 100644
--- a/vm/vm_page.h
+++ b/vm/vm_page.h
@@ -36,11 +36,12 @@
 
 #include <mach/boolean.h>
 #include <mach/vm_prot.h>
-#include <mach/vm_param.h>
+#include <machine/vm_param.h>
 #include <vm/vm_object.h>
 #include <vm/vm_types.h>
 #include <kern/queue.h>
 #include <kern/lock.h>
+#include <kern/log2.h>
 
 #include <kern/macros.h>
 #include <kern/sched_prim.h>   /* definitions of wait/wakeup */
@@ -76,6 +77,23 @@
  */
 
 struct vm_page {
+       /* Members used in the vm_page module only */
+       struct list node;
+       unsigned short type;
+       unsigned short seg_index;
+       unsigned short order;
+       void *priv;
+
+       /*
+        * This member is used throughout the code and may only change for
+        * fictitious pages.
+        */
+       phys_addr_t phys_addr;
+
+       /* We use an empty struct as the delimiter.  */
+       struct {} vm_page_header;
+#define VM_PAGE_HEADER_SIZE    offsetof(struct vm_page, vm_page_header)
+
        queue_chain_t   pageq;          /* queue info for FIFO
                                         * queue or free list (P) */
        queue_chain_t   listq;          /* all pages in same object (O) */
@@ -110,8 +128,6 @@ struct vm_page {
                                         * without having data. (O)
                                         * [See vm_object_overwrite] */
 
-       vm_offset_t     phys_addr;      /* Physical address of page, passed
-                                        *  to pmap_enter (read-only) */
        vm_prot_t       page_lock;      /* Uses prohibited by data manager (O) 
*/
        vm_prot_t       unlock_request; /* Outstanding unlock request (O) */
 };
@@ -140,8 +156,6 @@ struct vm_page {
  */
 
 extern
-vm_page_t      vm_page_queue_free;     /* memory free queue */
-extern
 vm_page_t      vm_page_queue_fictitious;       /* fictitious free queue */
 extern
 queue_head_t   vm_page_queue_active;   /* active memory queue */
@@ -149,8 +163,6 @@ extern
 queue_head_t   vm_page_queue_inactive; /* inactive memory queue */
 
 extern
-int    vm_page_free_count;     /* How many pages are free? */
-extern
 int    vm_page_fictitious_count;/* How many fictitious pages are free? */
 extern
 int    vm_page_active_count;   /* How many pages are active? */
@@ -196,25 +208,21 @@ extern void               vm_page_bootstrap(
        vm_offset_t     *endp);
 extern void            vm_page_module_init(void);
 
-extern void            vm_page_create(
-       vm_offset_t     start,
-       vm_offset_t     end);
 extern vm_page_t       vm_page_lookup(
        vm_object_t     object,
        vm_offset_t     offset);
 extern vm_page_t       vm_page_grab_fictitious(void);
-extern void            vm_page_release_fictitious(vm_page_t);
-extern boolean_t       vm_page_convert(vm_page_t, boolean_t);
+extern boolean_t       vm_page_convert(vm_page_t *, boolean_t);
 extern void            vm_page_more_fictitious(void);
 extern vm_page_t       vm_page_grab(boolean_t);
-extern void            vm_page_release(vm_page_t, boolean_t);
+extern vm_page_t       vm_page_grab_contig(vm_size_t, unsigned int);
+extern void            vm_page_free_contig(vm_page_t, vm_size_t);
 extern void            vm_page_wait(void (*)(void));
 extern vm_page_t       vm_page_alloc(
        vm_object_t     object,
        vm_offset_t     offset);
 extern void            vm_page_init(
-       vm_page_t       mem,
-       vm_offset_t     phys_addr);
+       vm_page_t       mem);
 extern void            vm_page_free(vm_page_t);
 extern void            vm_page_activate(vm_page_t);
 extern void            vm_page_deactivate(vm_page_t);
@@ -312,4 +320,217 @@ extern unsigned int       vm_page_info(
        }                                                       \
        MACRO_END
 
+/*
+ * Copyright (c) 2010-2014 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Physical page management.
+ */
+
+/*
+ * Address/page conversion and rounding macros (not inline functions to
+ * be easily usable on both virtual and physical addresses, which may not
+ * have the same type size).
+ */
+#define vm_page_atop(addr)      ((addr) >> PAGE_SHIFT)
+#define vm_page_ptoa(page)      ((page) << PAGE_SHIFT)
+#define vm_page_trunc(addr)     P2ALIGN(addr, PAGE_SIZE)
+#define vm_page_round(addr)     P2ROUND(addr, PAGE_SIZE)
+#define vm_page_aligned(addr)   P2ALIGNED(addr, PAGE_SIZE)
+
+/*
+ * Segment selectors.
+ *
+ * Selector-to-segment-list translation table :
+ * DMA          DMA
+ * DMA32        DMA32 DMA
+ * DIRECTMAP    DIRECTMAP DMA32 DMA
+ * HIGHMEM      HIGHMEM DIRECTMAP DMA32 DMA
+ */
+#define VM_PAGE_SEL_DMA         0
+#define VM_PAGE_SEL_DMA32       1
+#define VM_PAGE_SEL_DIRECTMAP   2
+#define VM_PAGE_SEL_HIGHMEM     3
+
+/*
+ * Page usage types.
+ *
+ * Failing to allocate pmap pages will cause a kernel panic.
+ * TODO Obviously, this needs to be addressed, e.g. with a reserved pool of
+ * pages.
+ */
+#define VM_PT_FREE          0   /* Page unused */
+#define VM_PT_RESERVED      1   /* Page reserved at boot time */
+#define VM_PT_TABLE         2   /* Page is part of the page table */
+#define VM_PT_PMAP          3   /* Page stores pmap-specific data */
+#define VM_PT_KMEM          4   /* Page is part of a kmem slab */
+#define VM_PT_STACK         5   /* Type for generic kernel allocations */
+#define VM_PT_KERNEL        6   /* Type for generic kernel allocations */
+
+static inline unsigned short
+vm_page_type(const struct vm_page *page)
+{
+    return page->type;
+}
+
+void vm_page_set_type(struct vm_page *page, unsigned int order,
+                      unsigned short type);
+
+static inline unsigned int
+vm_page_order(size_t size)
+{
+    return iorder2(vm_page_atop(vm_page_round(size)));
+}
+
+static inline phys_addr_t
+vm_page_to_pa(const struct vm_page *page)
+{
+    return page->phys_addr;
+}
+
+#if 0
+static inline unsigned long
+vm_page_direct_va(phys_addr_t pa)
+{
+    assert(pa < VM_PAGE_DIRECTMAP_LIMIT);
+    return ((unsigned long)pa + VM_MIN_DIRECTMAP_ADDRESS);
+}
+
+static inline phys_addr_t
+vm_page_direct_pa(unsigned long va)
+{
+    assert(va >= VM_MIN_DIRECTMAP_ADDRESS);
+    assert(va < VM_MAX_DIRECTMAP_ADDRESS);
+    return (va - VM_MIN_DIRECTMAP_ADDRESS);
+}
+
+static inline void *
+vm_page_direct_ptr(const struct vm_page *page)
+{
+    return (void *)vm_page_direct_va(vm_page_to_pa(page));
+}
+#endif
+
+/*
+ * Associate private data with a page.
+ */
+static inline void
+vm_page_set_priv(struct vm_page *page, void *priv)
+{
+    page->priv = priv;
+}
+
+static inline void *
+vm_page_get_priv(const struct vm_page *page)
+{
+    return page->priv;
+}
+
+/*
+ * Load physical memory into the vm_page module at boot time.
+ *
+ * The avail_start and avail_end parameters are used to maintain a simple
+ * heap for bootstrap allocations.
+ *
+ * All addresses must be page-aligned. Segments can be loaded in any order.
+ */
+void vm_page_load(unsigned int seg_index, phys_addr_t start, phys_addr_t end,
+                  phys_addr_t avail_start, phys_addr_t avail_end);
+
+/*
+ * Return true if the vm_page module is completely initialized, false
+ * otherwise, in which case only vm_page_bootalloc() can be used for
+ * allocations.
+ */
+int vm_page_ready(void);
+
+/*
+ * Early allocation function.
+ *
+ * This function is used by the vm_resident module to implement
+ * pmap_steal_memory. It can be used after physical segments have been loaded
+ * and before the vm_page module is initialized.
+ */
+unsigned long vm_page_bootalloc(size_t size);
+
+/*
+ * Set up the vm_page module.
+ *
+ * Architecture-specific code must have loaded segments before calling this
+ * function. Segments must comply with the selector-to-segment-list table,
+ * e.g. HIGHMEM is loaded if and only if DIRECTMAP, DMA32 and DMA are loaded,
+ * notwithstanding segment aliasing.
+ *
+ * Once this function returns, the vm_page module is ready, and normal
+ * allocation functions can be used.
+ */
+void vm_page_setup(void);
+
+/*
+ * Make the given page managed by the vm_page module.
+ *
+ * If additional memory can be made usable after the VM system is initialized,
+ * it should be reported through this function.
+ */
+void vm_page_manage(struct vm_page *page);
+
+/*
+ * Return the page descriptor for the given physical address.
+ */
+struct vm_page * vm_page_lookup_pa(phys_addr_t pa);
+
+/*
+ * Allocate a block of 2^order physical pages.
+ *
+ * The selector is used to determine the segments from which allocation can
+ * be attempted.
+ *
+ * This function should only be used by the vm_resident module.
+ */
+struct vm_page * vm_page_alloc_pa(unsigned int order, unsigned int selector,
+                                  unsigned short type);
+
+/*
+ * Release a block of 2^order physical pages.
+ *
+ * This function should only be used by the vm_resident module.
+ */
+void vm_page_free_pa(struct vm_page *page, unsigned int order);
+
+/*
+ * Return the name of the given segment.
+ */
+const char * vm_page_seg_name(unsigned int seg_index);
+
+/*
+ * Display internal information about the module.
+ */
+void vm_page_info_all(void);
+
+/*
+ * Return the total amount of physical memory.
+ */
+phys_addr_t vm_page_mem_size(void);
+
+/*
+ * Return the amount of free (unused) pages.
+ *
+ * XXX This currently relies on the kernel being non preemptible and
+ * uniprocessor.
+ */
+unsigned long vm_page_mem_free(void);
+
 #endif /* _VM_VM_PAGE_H_ */
diff --git a/vm/vm_pageout.c b/vm/vm_pageout.c
index 51a6a0d..72f96cb 100644
--- a/vm/vm_pageout.c
+++ b/vm/vm_pageout.c
@@ -82,7 +82,7 @@
  *     of active+inactive pages that should be inactive.
  *     The pageout daemon uses it to update vm_page_inactive_target.
  *
- *     If vm_page_free_count falls below vm_page_free_target and
+ *     If the number of free pages falls below vm_page_free_target and
  *     vm_page_inactive_count is below vm_page_inactive_target,
  *     then the pageout daemon starts running.
  */
@@ -93,7 +93,7 @@
 
 /*
  *     Once the pageout daemon starts running, it keeps going
- *     until vm_page_free_count meets or exceeds vm_page_free_target.
+ *     until the number of free pages meets or exceeds vm_page_free_target.
  */
 
 #ifndef        VM_PAGE_FREE_TARGET
@@ -101,7 +101,7 @@
 #endif /* VM_PAGE_FREE_TARGET */
 
 /*
- *     The pageout daemon always starts running once vm_page_free_count
+ *     The pageout daemon always starts running once the number of free pages
  *     falls below vm_page_free_min.
  */
 
@@ -125,7 +125,7 @@
 #endif  /* VM_PAGE_EXTERNAL_TARGET */
 
 /*
- *     When vm_page_free_count falls below vm_page_free_reserved,
+ *     When the number of free pages falls below vm_page_free_reserved,
  *     only vm-privileged threads can allocate pages.  vm-privilege
  *     allows the pageout daemon and default pager (and any other
  *     associated threads needed for default pageout) to continue
@@ -136,7 +136,7 @@
 #endif /* VM_PAGE_FREE_RESERVED */
 
 /*
- *     When vm_page_free_count falls below vm_pageout_reserved_internal,
+ *     When the number of free pages falls below vm_pageout_reserved_internal,
  *     the pageout daemon no longer trusts external pagers to clean pages.
  *     External pagers are probably all wedged waiting for a free page.
  *     It forcibly double-pages dirty pages belonging to external objects,
@@ -148,7 +148,7 @@
 #endif /* VM_PAGEOUT_RESERVED_INTERNAL */
 
 /*
- *     When vm_page_free_count falls below vm_pageout_reserved_really,
+ *     When the number of free pages falls below vm_pageout_reserved_really,
  *     the pageout daemon stops work entirely to let the default pager
  *     catch up (assuming the default pager has pages to clean).
  *     Beyond this point, it is too dangerous to consume memory
@@ -559,7 +559,7 @@ void vm_pageout_scan(void)
        for (burst_count = 0;;) {
                vm_page_t m;
                vm_object_t object;
-               unsigned int free_count;
+               unsigned long free_count;
 
                /*
                 *      Recalculate vm_page_inactivate_target.
@@ -630,7 +630,7 @@ void vm_pageout_scan(void)
                 */
 
                simple_lock(&vm_page_queue_free_lock);
-               free_count = vm_page_free_count;
+               free_count = vm_page_mem_free();
                if ((free_count >= vm_page_free_target) &&
                    (vm_page_external_count <= vm_page_external_target) &&
                    (vm_page_free_wanted == 0)) {
@@ -748,7 +748,12 @@ void vm_pageout_scan(void)
                    reclaim_page:
                        vm_page_free(m);
                        vm_page_unlock_queues();
-                       vm_object_unlock(object);
+
+                       if (vm_object_collectable(object))
+                               vm_object_collect(object);
+                       else
+                               vm_object_unlock(object);
+
                        continue;
                }
 
@@ -910,7 +915,7 @@ void vm_pageout_continue(void)
 
 void vm_pageout(void)
 {
-       int             free_after_reserve;
+       unsigned long free_after_reserve;
 
        current_thread()->vm_privilege = TRUE;
        stack_privilege(current_thread());
@@ -946,7 +951,7 @@ void vm_pageout(void)
                vm_pageout_reserved_really =
                        VM_PAGEOUT_RESERVED_REALLY(vm_page_free_reserved);
 
-       free_after_reserve = vm_page_free_count - vm_page_free_reserved;
+       free_after_reserve = vm_page_mem_free() - vm_page_free_reserved;
 
        if (vm_page_external_limit == 0)
                vm_page_external_limit = 
diff --git a/vm/vm_resident.c b/vm/vm_resident.c
index c70fa73..fa7a337 100644
--- a/vm/vm_resident.c
+++ b/vm/vm_resident.c
@@ -72,7 +72,7 @@
 /*
  *     These variables record the values returned by vm_page_bootstrap,
  *     for debugging purposes.  The implementation of pmap_steal_memory
- *     and pmap_startup here also uses them internally.
+ *     here also uses them internally.
  */
 
 vm_offset_t virtual_space_start;
@@ -95,29 +95,18 @@ vm_page_bucket_t *vm_page_buckets;          /* Array of 
buckets */
 unsigned int   vm_page_bucket_count = 0;       /* How big is array? */
 unsigned int   vm_page_hash_mask;              /* Mask for hash function */
 
-/*
- *     Resident page structures are initialized from
- *     a template (see vm_page_alloc).
- *
- *     When adding a new field to the virtual memory
- *     object structure, be sure to add initialization
- *     (see vm_page_bootstrap).
- */
-struct vm_page vm_page_template;
-
-/*
- *     Resident pages that represent real memory
- *     are allocated from a free list.
- */
-vm_page_t      vm_page_queue_free;
 vm_page_t      vm_page_queue_fictitious;
 decl_simple_lock_data(,vm_page_queue_free_lock)
 unsigned int   vm_page_free_wanted;
-int            vm_page_free_count;
 int            vm_page_fictitious_count;
 int            vm_page_external_count;
 
-unsigned int   vm_page_free_count_minimum;     /* debugging */
+/*
+ * This variable isn't directly used. It's merely a placeholder for the
+ * address used to synchronize threads waiting for pages to become
+ * available. The real value is returned by vm_page_free_mem().
+ */
+unsigned int   vm_page_free_avail;
 
 /*
  *     Occasionally, the virtual memory system uses
@@ -192,48 +181,15 @@ void vm_page_bootstrap(
        vm_offset_t *startp,
        vm_offset_t *endp)
 {
-       vm_page_t m;
        int i;
 
        /*
-        *      Initialize the vm_page template.
-        */
-
-       m = &vm_page_template;
-       m->object = VM_OBJECT_NULL;     /* reset later */
-       m->offset = 0;                  /* reset later */
-       m->wire_count = 0;
-
-       m->inactive = FALSE;
-       m->active = FALSE;
-       m->laundry = FALSE;
-       m->free = FALSE;
-       m->external = FALSE;
-
-       m->busy = TRUE;
-       m->wanted = FALSE;
-       m->tabled = FALSE;
-       m->fictitious = FALSE;
-       m->private = FALSE;
-       m->absent = FALSE;
-       m->error = FALSE;
-       m->dirty = FALSE;
-       m->precious = FALSE;
-       m->reference = FALSE;
-
-       m->phys_addr = 0;               /* reset later */
-
-       m->page_lock = VM_PROT_NONE;
-       m->unlock_request = VM_PROT_NONE;
-
-       /*
         *      Initialize the page queues.
         */
 
        simple_lock_init(&vm_page_queue_free_lock);
        simple_lock_init(&vm_page_queue_lock);
 
-       vm_page_queue_free = VM_PAGE_NULL;
        vm_page_queue_fictitious = VM_PAGE_NULL;
        queue_init(&vm_page_queue_active);
        queue_init(&vm_page_queue_inactive);
@@ -241,12 +197,6 @@ void vm_page_bootstrap(
        vm_page_free_wanted = 0;
 
        /*
-        *      Steal memory for the kernel map entries.
-        */
-
-       kentry_data = pmap_steal_memory(kentry_data_size);
-
-       /*
         *      Allocate (and initialize) the virtual-to-physical
         *      table hash buckets.
         *
@@ -280,29 +230,19 @@ void vm_page_bootstrap(
                simple_lock_init(&bucket->lock);
        }
 
-       /*
-        *      Machine-dependent code allocates the resident page table.
-        *      It uses vm_page_init to initialize the page frames.
-        *      The code also returns to us the virtual space available
-        *      to the kernel.  We don't trust the pmap module
-        *      to get the alignment right.
-        */
+       vm_page_setup();
 
-       pmap_startup(&virtual_space_start, &virtual_space_end);
        virtual_space_start = round_page(virtual_space_start);
        virtual_space_end = trunc_page(virtual_space_end);
 
        *startp = virtual_space_start;
        *endp = virtual_space_end;
-
-       /*      printf("vm_page_bootstrap: %d free pages\n", 
vm_page_free_count);*/
-       vm_page_free_count_minimum = vm_page_free_count;
 }
 
 #ifndef        MACHINE_PAGES
 /*
- *     We implement pmap_steal_memory and pmap_startup with the help
- *     of two simpler functions, pmap_virtual_space and pmap_next_page.
+ *     We implement pmap_steal_memory with the help
+ *     of two simpler functions, pmap_virtual_space and vm_page_bootalloc.
  */
 
 vm_offset_t pmap_steal_memory(
@@ -310,11 +250,7 @@ vm_offset_t pmap_steal_memory(
 {
        vm_offset_t addr, vaddr, paddr;
 
-       /*
-        *      We round the size to an integer multiple.
-        */
-
-       size = (size + 3) &~ 3;
+       size = round_page(size);
 
        /*
         *      If this is the first call to pmap_steal_memory,
@@ -347,8 +283,7 @@ vm_offset_t pmap_steal_memory(
        for (vaddr = round_page(addr);
             vaddr < addr + size;
             vaddr += PAGE_SIZE) {
-               if (!pmap_next_page(&paddr))
-                       panic("pmap_steal_memory");
+               paddr = vm_page_bootalloc(PAGE_SIZE);
 
                /*
                 *      XXX Logically, these mappings should be wired,
@@ -361,64 +296,6 @@ vm_offset_t pmap_steal_memory(
 
        return addr;
 }
-
-void pmap_startup(
-       vm_offset_t *startp,
-       vm_offset_t *endp)
-{
-       unsigned int i, npages, pages_initialized;
-       vm_page_t pages;
-       vm_offset_t paddr;
-
-       /*
-        *      We calculate how many page frames we will have
-        *      and then allocate the page structures in one chunk.
-        */
-
-       npages = ((PAGE_SIZE * pmap_free_pages() +
-                  (round_page(virtual_space_start) - virtual_space_start)) /
-                 (PAGE_SIZE + sizeof *pages));
-
-       pages = (vm_page_t) pmap_steal_memory(npages * sizeof *pages);
-
-       /*
-        *      Initialize the page frames.
-        */
-
-       for (i = 0, pages_initialized = 0; i < npages; i++) {
-               if (!pmap_next_page(&paddr))
-                       break;
-
-               vm_page_init(&pages[i], paddr);
-               pages_initialized++;
-       }
-       i = 0;
-       while (pmap_next_page(&paddr))
-               i++;
-       if (i)
-               printf("%u memory page(s) left away\n", i);
-
-       /*
-        * Release pages in reverse order so that physical pages
-        * initially get allocated in ascending addresses. This keeps
-        * the devices (which must address physical memory) happy if
-        * they require several consecutive pages.
-        */
-
-       for (i = pages_initialized; i > 0; i--) {
-               vm_page_release(&pages[i - 1], FALSE);
-       }
-
-       /*
-        *      We have to re-align virtual_space_start,
-        *      because pmap_steal_memory has been using it.
-        */
-
-       virtual_space_start = round_page(virtual_space_start);
-
-       *startp = virtual_space_start;
-       *endp = virtual_space_end;
-}
 #endif /* MACHINE_PAGES */
 
 /*
@@ -430,35 +307,7 @@ void pmap_startup(
 void           vm_page_module_init(void)
 {
        kmem_cache_init(&vm_page_cache, "vm_page", sizeof(struct vm_page), 0,
-                       NULL, NULL, NULL, 0);
-}
-
-/*
- *     Routine:        vm_page_create
- *     Purpose:
- *             After the VM system is up, machine-dependent code
- *             may stumble across more physical memory.  For example,
- *             memory that it was reserving for a frame buffer.
- *             vm_page_create turns this memory into available pages.
- */
-
-void vm_page_create(
-       vm_offset_t     start,
-       vm_offset_t     end)
-{
-       vm_offset_t paddr;
-       vm_page_t m;
-
-       for (paddr = round_page(start);
-            paddr < trunc_page(end);
-            paddr += PAGE_SIZE) {
-               m = (vm_page_t) kmem_cache_alloc(&vm_page_cache);
-               if (m == VM_PAGE_NULL)
-                       panic("vm_page_create");
-
-               vm_page_init(m, paddr);
-               vm_page_release(m, FALSE);
-       }
+                       NULL, 0);
 }
 
 /*
@@ -523,7 +372,7 @@ void vm_page_insert(
         */
 
        object->resident_page_count++;
-       assert(object->resident_page_count >= 0);
+       assert(object->resident_page_count != 0);
 
        if (object->can_persist && (object->ref_count == 0))
                vm_object_cached_pages_update(1);
@@ -630,7 +479,7 @@ void vm_page_replace(
         */
 
        object->resident_page_count++;
-       assert(object->resident_page_count >= 0);
+       assert(object->resident_page_count != 0);
 
        if (object->can_persist && (object->ref_count == 0))
                vm_object_cached_pages_update(1);
@@ -750,6 +599,33 @@ void vm_page_rename(
        vm_page_unlock_queues();
 }
 
+static void vm_page_init_template(vm_page_t m)
+{
+       m->object = VM_OBJECT_NULL;     /* reset later */
+       m->offset = 0;                  /* reset later */
+       m->wire_count = 0;
+
+       m->inactive = FALSE;
+       m->active = FALSE;
+       m->laundry = FALSE;
+       m->free = FALSE;
+       m->external = FALSE;
+
+       m->busy = TRUE;
+       m->wanted = FALSE;
+       m->tabled = FALSE;
+       m->fictitious = FALSE;
+       m->private = FALSE;
+       m->absent = FALSE;
+       m->error = FALSE;
+       m->dirty = FALSE;
+       m->precious = FALSE;
+       m->reference = FALSE;
+
+       m->page_lock = VM_PROT_NONE;
+       m->unlock_request = VM_PROT_NONE;
+}
+
 /*
  *     vm_page_init:
  *
@@ -758,11 +634,9 @@ void vm_page_rename(
  *     so that it can be given to vm_page_release or vm_page_insert.
  */
 void vm_page_init(
-       vm_page_t       mem,
-       vm_offset_t     phys_addr)
+       vm_page_t       mem)
 {
-       *mem = vm_page_template;
-       mem->phys_addr = phys_addr;
+       vm_page_init_template(mem);
 }
 
 /*
@@ -794,7 +668,7 @@ vm_page_t vm_page_grab_fictitious(void)
  *     Release a fictitious page to the free list.
  */
 
-void vm_page_release_fictitious(
+static void vm_page_release_fictitious(
        vm_page_t m)
 {
        simple_lock(&vm_page_queue_free_lock);
@@ -826,7 +700,8 @@ void vm_page_more_fictitious(void)
                if (m == VM_PAGE_NULL)
                        panic("vm_page_more_fictitious");
 
-               vm_page_init(m, vm_page_fictitious_addr);
+               vm_page_init(m);
+               m->phys_addr = vm_page_fictitious_addr;
                m->fictitious = TRUE;
                vm_page_release_fictitious(m);
        }
@@ -836,25 +711,46 @@ void vm_page_more_fictitious(void)
  *     vm_page_convert:
  *
  *     Attempt to convert a fictitious page into a real page.
+ *
+ *     The object referenced by *MP must be locked.
  */
 
 boolean_t vm_page_convert(
-       vm_page_t m,
+       struct vm_page **mp,
        boolean_t external)
 {
-       vm_page_t real_m;
+       struct vm_page *real_m, *fict_m;
+       vm_object_t object;
+       vm_offset_t offset;
+
+       fict_m = *mp;
+
+       assert(fict_m->fictitious);
+       assert(fict_m->phys_addr == vm_page_fictitious_addr);
+       assert(!fict_m->active);
+       assert(!fict_m->inactive);
 
        real_m = vm_page_grab(external);
        if (real_m == VM_PAGE_NULL)
                return FALSE;
 
-       m->phys_addr = real_m->phys_addr;
-       m->fictitious = FALSE;
+       object = fict_m->object;
+       offset = fict_m->offset;
+       vm_page_remove(fict_m);
 
-       real_m->phys_addr = vm_page_fictitious_addr;
-       real_m->fictitious = TRUE;
+       memcpy(&real_m->vm_page_header,
+              &fict_m->vm_page_header,
+              sizeof(*fict_m) - VM_PAGE_HEADER_SIZE);
+       real_m->fictitious = FALSE;
 
-       vm_page_release_fictitious(real_m);
+       vm_page_insert(real_m, object, offset);
+
+       assert(real_m->phys_addr != vm_page_fictitious_addr);
+       assert(fict_m->fictitious);
+       assert(fict_m->phys_addr == vm_page_fictitious_addr);
+
+       vm_page_release_fictitious(fict_m);
+       *mp = real_m;
        return TRUE;
 }
 
@@ -878,7 +774,7 @@ vm_page_t vm_page_grab(
         *      for externally-managed pages.
         */
 
-       if (((vm_page_free_count < vm_page_free_reserved)
+       if (((vm_page_mem_free() < vm_page_free_reserved)
             || (external
                 && (vm_page_external_count > vm_page_external_limit)))
            && !current_thread()->vm_privilege) {
@@ -886,15 +782,16 @@ vm_page_t vm_page_grab(
                return VM_PAGE_NULL;
        }
 
-       if (vm_page_queue_free == VM_PAGE_NULL)
-               panic("vm_page_grab");
+       mem = vm_page_alloc_pa(0, VM_PAGE_SEL_DIRECTMAP, VM_PT_KERNEL);
+
+       if (mem == NULL) {
+               simple_unlock(&vm_page_queue_free_lock);
+               return NULL;
+       }
 
-       if (--vm_page_free_count < vm_page_free_count_minimum)
-               vm_page_free_count_minimum = vm_page_free_count;
        if (external)
                vm_page_external_count++;
-       mem = vm_page_queue_free;
-       vm_page_queue_free = (vm_page_t) mem->pageq.next;
+
        mem->free = FALSE;
        mem->extcounted = mem->external = external;
        simple_unlock(&vm_page_queue_free_lock);
@@ -910,8 +807,8 @@ vm_page_t vm_page_grab(
         *      it doesn't really matter.
         */
 
-       if ((vm_page_free_count < vm_page_free_min) ||
-           ((vm_page_free_count < vm_page_free_target) &&
+       if ((vm_page_mem_free() < vm_page_free_min) ||
+           ((vm_page_mem_free() < vm_page_free_target) &&
             (vm_page_inactive_count < vm_page_inactive_target)))
                thread_wakeup((event_t) &vm_page_free_wanted);
 
@@ -928,208 +825,92 @@ vm_offset_t vm_page_grab_phys_addr(void)
 }
 
 /*
- *     vm_page_grab_contiguous_pages:
- *
- *     Take N pages off the free list, the pages should
- *     cover a contiguous range of physical addresses.
- *     [Used by device drivers to cope with DMA limitations]
+ *     vm_page_release:
  *
- *     Returns the page descriptors in ascending order, or
- *     Returns KERN_RESOURCE_SHORTAGE if it could not.
+ *     Return a page to the free list.
  */
 
-/* Biggest phys page number for the pages we handle in VM */
-
-vm_size_t      vm_page_big_pagenum = 0;        /* Set this before call! */
-
-kern_return_t
-vm_page_grab_contiguous_pages(
-       int             npages,
-       vm_page_t       pages[],
-       natural_t       *bits,
-       boolean_t       external)
+static void vm_page_release(
+       vm_page_t       mem,
+       boolean_t       external)
 {
-       int             first_set;
-       int             size, alloc_size;
-       kern_return_t   ret;
-       vm_page_t       mem, *prevmemp;
-
-#ifndef        NBBY
-#define        NBBY    8       /* size in bits of sizeof()`s unity */
-#endif
-
-#define        NBPEL   (sizeof(natural_t)*NBBY)
-
-       size = (vm_page_big_pagenum + NBPEL - 1)
-               & ~(NBPEL - 1);                         /* in bits */
-
-       size = size / NBBY;                             /* in bytes */
-
-       /*
-        * If we are called before the VM system is fully functional
-        * the invoker must provide us with the work space. [one bit
-        * per page starting at phys 0 and up to vm_page_big_pagenum]
-        */
-       if (bits == 0) {
-               alloc_size = round_page(size);
-               if (kmem_alloc_wired(kernel_map,
-                                    (vm_offset_t *)&bits,
-                                    alloc_size)
-                       != KERN_SUCCESS)
-                   return KERN_RESOURCE_SHORTAGE;
-       } else
-               alloc_size = 0;
-
-       memset(bits, 0, size);
-
-       /*
-        * A very large granularity call, its rare so that is ok
-        */
        simple_lock(&vm_page_queue_free_lock);
+       if (mem->free)
+               panic("vm_page_release");
+       mem->free = TRUE;
+       vm_page_free_pa(mem, 0);
+       if (external)
+               vm_page_external_count--;
 
        /*
-        *      Do not dip into the reserved pool.
-        */
-
-       if ((vm_page_free_count < vm_page_free_reserved)
-           || (vm_page_external_count >= vm_page_external_limit)) {
-               printf_once("no more room for vm_page_grab_contiguous_pages");
-               simple_unlock(&vm_page_queue_free_lock);
-               return KERN_RESOURCE_SHORTAGE;
-       }
-
-       /*
-        *      First pass through, build a big bit-array of
-        *      the pages that are free.  It is not going to
-        *      be too large anyways, in 4k we can fit info
-        *      for 32k pages.
+        *      Check if we should wake up someone waiting for page.
+        *      But don't bother waking them unless they can allocate.
+        *
+        *      We wakeup only one thread, to prevent starvation.
+        *      Because the scheduling system handles wait queues FIFO,
+        *      if we wakeup all waiting threads, one greedy thread
+        *      can starve multiple niceguy threads.  When the threads
+        *      all wakeup, the greedy threads runs first, grabs the page,
+        *      and waits for another page.  It will be the first to run
+        *      when the next page is freed.
+        *
+        *      However, there is a slight danger here.
+        *      The thread we wake might not use the free page.
+        *      Then the other threads could wait indefinitely
+        *      while the page goes unused.  To forestall this,
+        *      the pageout daemon will keep making free pages
+        *      as long as vm_page_free_wanted is non-zero.
         */
-       mem = vm_page_queue_free;
-       while (mem) {
-               int word_index, bit_index;
 
-               bit_index = (mem->phys_addr >> PAGE_SHIFT);
-               word_index = bit_index / NBPEL;
-               bit_index = bit_index - (word_index * NBPEL);
-               bits[word_index] |= 1 << bit_index;
-
-               mem = (vm_page_t) mem->pageq.next;
+       if ((vm_page_free_wanted > 0) &&
+           (vm_page_mem_free() >= vm_page_free_reserved)) {
+               vm_page_free_wanted--;
+               thread_wakeup_one((event_t) &vm_page_free_avail);
        }
 
-       /*
-        *      Second loop. Scan the bit array for NPAGES
-        *      contiguous bits.  That gives us, if any,
-        *      the range of pages we will be grabbing off
-        *      the free list.
-        */
-       {
-           int bits_so_far = 0, i;
+       simple_unlock(&vm_page_queue_free_lock);
+}
 
-               first_set = 0;
+/*
+ *     vm_page_grab_contig:
+ *
+ *     Remove a block of contiguous pages from the free list.
+ *     Returns VM_PAGE_NULL if the request fails.
+ */
 
-               for (i = 0; i < size; i += sizeof(natural_t)) {
+vm_page_t vm_page_grab_contig(
+       vm_size_t size,
+       unsigned int selector)
+{
+       unsigned int i, order, nr_pages;
+       vm_page_t mem;
 
-                   natural_t   v = bits[i / sizeof(natural_t)];
-                   int         bitpos;
+       order = vm_page_order(size);
+       nr_pages = 1 << order;
 
-                   /*
-                    * Bitscan this one word
-                    */
-                   if (v) {
-                       /*
-                        * keep counting them beans ?
-                        */
-                       bitpos = 0;
+       simple_lock(&vm_page_queue_free_lock);
 
-                       if (bits_so_far) {
-count_ones:
-                           while (v & 1) {
-                               bitpos++;
-                               /*
-                                * got enough beans ?
-                                */
-                               if (++bits_so_far == npages)
-                                   goto found_em;
-                               v >>= 1;
-                           }
-                           /* if we are being lucky, roll again */
-                           if (bitpos == NBPEL)
-                               continue;
-                       }
+       /*
+        *      Only let privileged threads (involved in pageout)
+        *      dip into the reserved pool or exceed the limit
+        *      for externally-managed pages.
+        */
 
-                       /*
-                        * search for beans here
-                        */
-                       bits_so_far = 0;
-                       while ((bitpos < NBPEL) && ((v & 1) == 0)) {
-                           bitpos++;
-                           v >>= 1;
-                       }
-                       if (v & 1) {
-                           first_set = (i * NBBY) + bitpos;
-                           goto count_ones;
-                       }
-                   }
-                   /*
-                    * No luck
-                    */
-                   bits_so_far = 0;
-               }
+       if (((vm_page_mem_free() - nr_pages) <= vm_page_free_reserved)
+           && !current_thread()->vm_privilege) {
+               simple_unlock(&vm_page_queue_free_lock);
+               return VM_PAGE_NULL;
        }
 
-       /*
-        *      We could not find enough contiguous pages.
-        */
-       simple_unlock(&vm_page_queue_free_lock);
+       /* TODO Allow caller to pass type */
+       mem = vm_page_alloc_pa(order, selector, VM_PT_KERNEL);
 
-       printf_once("no contiguous room for vm_page_grab_contiguous_pages");
-       ret = KERN_RESOURCE_SHORTAGE;
-       goto out;
+       if (mem == NULL)
+               panic("vm_page_grab_contig");
 
-       /*
-        *      Final pass. Now we know which pages we want.
-        *      Scan the list until we find them all, grab
-        *      pages as we go.  FIRST_SET tells us where
-        *      in the bit-array our pages start.
-        */
-found_em:
-       vm_page_free_count -= npages;
-       if (vm_page_free_count < vm_page_free_count_minimum)
-               vm_page_free_count_minimum = vm_page_free_count;
-       if (external)
-               vm_page_external_count += npages;
-       {
-           vm_offset_t first_phys, last_phys;
-
-           /* cache values for compare */
-           first_phys = first_set << PAGE_SHIFT;
-           last_phys = first_phys + (npages << PAGE_SHIFT);/* not included */
-
-           /* running pointers */
-           mem = vm_page_queue_free;
-           prevmemp = &vm_page_queue_free;
-
-           while (mem) {
-
-               vm_offset_t     addr;
-
-               addr = mem->phys_addr;
-
-               if ((addr >= first_phys) &&
-                   (addr <  last_phys)) {
-                   *prevmemp = (vm_page_t) mem->pageq.next;
-                   pages[(addr - first_phys) >> PAGE_SHIFT] = mem;
-                   mem->free = FALSE;
-                   mem->extcounted = mem->external = external;
-                   /*
-                    * Got them all ?
-                    */
-                   if (--npages == 0) break;
-               } else
-                   prevmemp = (vm_page_t *) &mem->pageq.next;
-
-               mem = (vm_page_t) mem->pageq.next;
-           }
+       for (i = 0; i < nr_pages; i++) {
+               mem[i].free = FALSE;
+               mem[i].extcounted = mem[i].external = 0;
        }
 
        simple_unlock(&vm_page_queue_free_lock);
@@ -1145,63 +926,42 @@ found_em:
         *      it doesn't really matter.
         */
 
-       if ((vm_page_free_count < vm_page_free_min) ||
-           ((vm_page_free_count < vm_page_free_target) &&
+       if ((vm_page_mem_free() < vm_page_free_min) ||
+           ((vm_page_mem_free() < vm_page_free_target) &&
             (vm_page_inactive_count < vm_page_inactive_target)))
-               thread_wakeup(&vm_page_free_wanted);
-
-       ret = KERN_SUCCESS;
-out:
-       if (alloc_size)
-               kmem_free(kernel_map, (vm_offset_t) bits, alloc_size);
+               thread_wakeup((event_t) &vm_page_free_wanted);
 
-       return ret;
+       return mem;
 }
 
 /*
- *     vm_page_release:
+ *     vm_page_free_contig:
  *
- *     Return a page to the free list.
+ *     Return a block of contiguous pages to the free list.
  */
 
-void vm_page_release(
-       vm_page_t       mem,
-       boolean_t       external)
+void vm_page_free_contig(vm_page_t mem, vm_size_t size)
 {
+       unsigned int i, order, nr_pages;
+
+       order = vm_page_order(size);
+       nr_pages = 1 << order;
+
        simple_lock(&vm_page_queue_free_lock);
-       if (mem->free)
-               panic("vm_page_release");
-       mem->free = TRUE;
-       mem->pageq.next = (queue_entry_t) vm_page_queue_free;
-       vm_page_queue_free = mem;
-       vm_page_free_count++;
-       if (external)
-               vm_page_external_count--;
 
-       /*
-        *      Check if we should wake up someone waiting for page.
-        *      But don't bother waking them unless they can allocate.
-        *
-        *      We wakeup only one thread, to prevent starvation.
-        *      Because the scheduling system handles wait queues FIFO,
-        *      if we wakeup all waiting threads, one greedy thread
-        *      can starve multiple niceguy threads.  When the threads
-        *      all wakeup, the greedy threads runs first, grabs the page,
-        *      and waits for another page.  It will be the first to run
-        *      when the next page is freed.
-        *
-        *      However, there is a slight danger here.
-        *      The thread we wake might not use the free page.
-        *      Then the other threads could wait indefinitely
-        *      while the page goes unused.  To forestall this,
-        *      the pageout daemon will keep making free pages
-        *      as long as vm_page_free_wanted is non-zero.
-        */
+       for (i = 0; i < nr_pages; i++) {
+               if (mem[i].free)
+                       panic("vm_page_free_contig");
+
+               mem[i].free = TRUE;
+       }
+
+       vm_page_free_pa(mem, order);
 
        if ((vm_page_free_wanted > 0) &&
-           (vm_page_free_count >= vm_page_free_reserved)) {
+           (vm_page_mem_free() >= vm_page_free_reserved)) {
                vm_page_free_wanted--;
-               thread_wakeup_one((event_t) &vm_page_free_count);
+               thread_wakeup_one((event_t) &vm_page_free_avail);
        }
 
        simple_unlock(&vm_page_queue_free_lock);
@@ -1227,11 +987,11 @@ void vm_page_wait(
         */
 
        simple_lock(&vm_page_queue_free_lock);
-       if ((vm_page_free_count < vm_page_free_target)
+       if ((vm_page_mem_free() < vm_page_free_target)
            || (vm_page_external_count > vm_page_external_limit)) {
                if (vm_page_free_wanted++ == 0)
                        thread_wakeup((event_t)&vm_page_free_wanted);
-               assert_wait((event_t)&vm_page_free_count, FALSE);
+               assert_wait((event_t)&vm_page_free_avail, FALSE);
                simple_unlock(&vm_page_queue_free_lock);
                if (continuation != 0) {
                        counter(c_vm_page_wait_block_user++);
@@ -1310,12 +1070,13 @@ void vm_page_free(
         */
 
        if (mem->private || mem->fictitious) {
-               vm_page_init(mem, vm_page_fictitious_addr);
+               vm_page_init(mem);
+               mem->phys_addr = vm_page_fictitious_addr;
                mem->fictitious = TRUE;
                vm_page_release_fictitious(mem);
        } else {
                int external = mem->external && mem->extcounted;
-               vm_page_init(mem, mem->phys_addr);
+               vm_page_init(mem);
                vm_page_release(mem, external);
        }
 }
diff --git a/vm/vm_user.c b/vm/vm_user.c
index c71e9f5..e65f6d5 100644
--- a/vm/vm_user.c
+++ b/vm/vm_user.c
@@ -182,7 +182,7 @@ kern_return_t vm_statistics(
        *stat = vm_stat;
 
        stat->pagesize = PAGE_SIZE;
-       stat->free_count = vm_page_free_count;
+       stat->free_count = vm_page_mem_free();
        stat->active_count = vm_page_active_count;
        stat->inactive_count = vm_page_inactive_count;
        stat->wire_count = vm_page_wire_count;

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-hurd/gnumach.git



reply via email to

[Prev in Thread] Current Thread [Next in Thread]