Index: kernel/generic/src/mm/frame.c
===================================================================
--- kernel/generic/src/mm/frame.c	(revision 6645a1412e9ca7394bd56aab318e76c61f97f15f)
+++ kernel/generic/src/mm/frame.c	(revision 7aaed09d88be49fac8360d3017e3328ed9b0635c)
@@ -240,6 +240,6 @@
 NO_TRACE static bool zone_can_alloc(zone_t *zone, uint8_t order)
 {
-	return (zone_flags_available(zone->flags)
-	    && buddy_system_can_alloc(zone->buddy_system, order));
+	return ((zone->flags & ZONE_AVAILABLE) &&
+	    buddy_system_can_alloc(zone->buddy_system, order));
 }
 
@@ -265,5 +265,5 @@
 		 * Check whether the zone meets the search criteria.
 		 */
-		if ((zones.info[i].flags & flags) == flags) {
+		if (ZONE_FLAGS_MATCH(zones.info[i].flags, flags)) {
 			/*
 			 * Check if the zone has 2^order frames area available.
@@ -460,5 +460,5 @@
 NO_TRACE static pfn_t zone_frame_alloc(zone_t *zone, uint8_t order)
 {
-	ASSERT(zone_flags_available(zone->flags));
+	ASSERT(zone->flags & ZONE_AVAILABLE);
 	
 	/* Allocate frames from zone buddy system */
@@ -490,5 +490,5 @@
 NO_TRACE static size_t zone_frame_free(zone_t *zone, size_t frame_idx)
 {
-	ASSERT(zone_flags_available(zone->flags));
+	ASSERT(zone->flags & ZONE_AVAILABLE);
 	
 	frame_t *frame = &zone->frames[frame_idx];
@@ -518,5 +518,5 @@
 NO_TRACE static void zone_mark_unavailable(zone_t *zone, size_t frame_idx)
 {
-	ASSERT(zone_flags_available(zone->flags));
+	ASSERT(zone->flags & ZONE_AVAILABLE);
 	
 	frame_t *frame = zone_get_frame(zone, frame_idx);
@@ -549,6 +549,6 @@
     buddy_system_t *buddy)
 {
-	ASSERT(zone_flags_available(zones.info[z1].flags));
-	ASSERT(zone_flags_available(zones.info[z2].flags));
+	ASSERT(zones.info[z1].flags & ZONE_AVAILABLE);
+	ASSERT(zones.info[z2].flags & ZONE_AVAILABLE);
 	ASSERT(zones.info[z1].flags == zones.info[z2].flags);
 	ASSERT(zones.info[z1].base < zones.info[z2].base);
@@ -645,5 +645,5 @@
 NO_TRACE static void return_config_frames(size_t znum, pfn_t pfn, size_t count)
 {
-	ASSERT(zone_flags_available(zones.info[znum].flags));
+	ASSERT(zones.info[znum].flags & ZONE_AVAILABLE);
 	
 	size_t cframes = SIZE2FRAMES(zone_conf_size(count));
@@ -681,5 +681,5 @@
     size_t count)
 {
-	ASSERT(zone_flags_available(zones.info[znum].flags));
+	ASSERT(zones.info[znum].flags & ZONE_AVAILABLE);
 	ASSERT(frame_idx + count < zones.info[znum].count);
 	
@@ -723,9 +723,6 @@
 	 * set of flags
 	 */
-	if ((z1 >= zones.count) || (z2 >= zones.count)
-	    || (z2 - z1 != 1)
-	    || (!zone_flags_available(zones.info[z1].flags))
-	    || (!zone_flags_available(zones.info[z2].flags))
-	    || (zones.info[z1].flags != zones.info[z2].flags)) {
+	if ((z1 >= zones.count) || (z2 >= zones.count) || (z2 - z1 != 1) ||
+	    (zones.info[z1].flags != zones.info[z2].flags)) {
 		ret = false;
 		goto errout;
@@ -828,5 +825,5 @@
 	zone->buddy_system = buddy;
 	
-	if (zone_flags_available(flags)) {
+	if (flags & ZONE_AVAILABLE) {
 		/*
 		 * Compute order for buddy system and initialize
@@ -865,4 +862,13 @@
 {
 	return (count * sizeof(frame_t) + buddy_conf_size(fnzb(count)));
+}
+
+/** Allocate external configuration frames from low memory. */
+pfn_t zone_external_conf_alloc(size_t count)
+{
+	size_t size = zone_conf_size(count);
+	size_t order = ispwr2(size) ? fnzb(size) : (fnzb(size) + 1);
+
+	return ADDR2PFN((uintptr_t) frame_alloc(order - FRAME_WIDTH, FRAME_LOWMEM));
 }
 
@@ -888,5 +894,5 @@
 	irq_spinlock_lock(&zones.lock, true);
 	
-	if (zone_flags_available(flags)) {  /* Create available zone */
+	if (flags & ZONE_AVAILABLE) {  /* Create available zone */
 		/* Theoretically we could have NULL here, practically make sure
 		 * nobody tries to do that. If some platform requires, remove
@@ -894,4 +900,7 @@
 		 */
 		ASSERT(confframe != ADDR2PFN((uintptr_t ) NULL));
+
+		/* Update the known end of physical memory. */
+		config.physmem_end = max(config.physmem_end, PFN2ADDR(start + count));
 		
 		/* If confframe is supposed to be inside our zone, then make sure
@@ -1232,5 +1241,5 @@
 	
 	/* Tell the architecture to create some memory */
-	frame_arch_init();
+	frame_low_arch_init();
 	if (config.cpu_active == 1) {
 		frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
@@ -1255,4 +1264,37 @@
 		frame_mark_unavailable(0, 1);
 	}
+	frame_high_arch_init();
+}
+
+/** Adjust bounds of physical memory region according to low/high memory split.
+ *
+ * @param low[in]	If true, the adujstment is performed to make the region
+ *			fit in the low memory. Otherwise the adjustment is
+ *			performed to make the region fit in the high memory.
+ * @param basep[inout]	Pointer to a variable which contains the region's base
+ *			address and which may receive the adjusted base address.
+ * @param sizep[inout]	Pointer to a variable which contains the region's size
+ *			and which may receive the adjusted size.
+ * @retun		True if the region still exists even after the
+ *			adjustment, false otherwise.
+ */
+bool frame_adjust_zone_bounds(bool low, uintptr_t *basep, size_t *sizep)
+{
+	uintptr_t limit = config.identity_size;
+
+	if (low) {
+		if (*basep > limit)
+			return false;
+		if (*basep + *sizep > limit)
+			*sizep = limit - *basep;
+	} else {
+		if (*basep + *sizep <= limit)
+			return false;
+		if (*basep <= limit) {
+			*sizep -= limit - *basep;
+			*basep = limit;
+		}
+	}
+	return true;
 }
 
@@ -1293,5 +1335,5 @@
 		*total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
 		
-		if (zone_flags_available(zones.info[i].flags)) {
+		if (zones.info[i].flags & ZONE_AVAILABLE) {
 			*busy += (uint64_t) FRAMES2SIZE(zones.info[i].busy_count);
 			*free += (uint64_t) FRAMES2SIZE(zones.info[i].free_count);
@@ -1344,5 +1386,5 @@
 		irq_spinlock_unlock(&zones.lock, true);
 		
-		bool available = zone_flags_available(flags);
+		bool available = ((flags & ZONE_AVAILABLE) != 0);
 		
 		printf("%-4zu", i);
@@ -1356,8 +1398,10 @@
 #endif
 		
-		printf(" %12zu %c%c%c      ", count,
-		    available ? 'A' : ' ',
-		    (flags & ZONE_RESERVED) ? 'R' : ' ',
-		    (flags & ZONE_FIRMWARE) ? 'F' : ' ');
+		printf(" %12zu %c%c%c%c%c    ", count,
+		    available ? 'A' : '-',
+		    (flags & ZONE_RESERVED) ? 'R' : '-',
+		    (flags & ZONE_FIRMWARE) ? 'F' : '-',
+		    (flags & ZONE_LOWMEM) ? 'L' : '-',
+		    (flags & ZONE_HIGHMEM) ? 'H' : '-');
 		
 		if (available)
@@ -1401,5 +1445,5 @@
 	irq_spinlock_unlock(&zones.lock, true);
 	
-	bool available = zone_flags_available(flags);
+	bool available = ((flags & ZONE_AVAILABLE) != 0);
 	
 	uint64_t size;
@@ -1411,8 +1455,10 @@
 	printf("Zone size:         %zu frames (%" PRIu64 " %s)\n", count,
 	    size, size_suffix);
-	printf("Zone flags:        %c%c%c\n",
-	    available ? 'A' : ' ',
-	    (flags & ZONE_RESERVED) ? 'R' : ' ',
-	    (flags & ZONE_FIRMWARE) ? 'F' : ' ');
+	printf("Zone flags:        %c%c%c%c%c\n",
+	    available ? 'A' : '-',
+	    (flags & ZONE_RESERVED) ? 'R' : '-',
+	    (flags & ZONE_FIRMWARE) ? 'F' : '-',
+	    (flags & ZONE_LOWMEM) ? 'L' : '-',
+	    (flags & ZONE_HIGHMEM) ? 'H' : '-');
 	
 	if (available) {
Index: kernel/generic/src/mm/km.c
===================================================================
--- kernel/generic/src/mm/km.c	(revision 7aaed09d88be49fac8360d3017e3328ed9b0635c)
+++ kernel/generic/src/mm/km.c	(revision 7aaed09d88be49fac8360d3017e3328ed9b0635c)
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2011 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genericmm
+ * @{
+ */
+
+/**
+ * @file
+ * @brief Kernel virtual memory setup.
+ */
+
+#include <mm/km.h>
+#include <arch/mm/km.h>
+#include <config.h>
+#include <typedefs.h>
+#include <lib/ra.h>
+#include <debug.h>
+
+static ra_arena_t *km_ni_arena;
+
+/** Architecture dependent setup of identity-mapped kernel memory. */
+void km_identity_init(void)
+{
+	km_identity_arch_init();
+	config.identity_configured = true;
+}
+
+/** Architecture dependent setup of non-identity-mapped kernel memory. */
+void km_non_identity_init(void)
+{
+	km_ni_arena = ra_arena_create();
+	ASSERT(km_ni_arena != NULL);
+	km_non_identity_arch_init();
+	config.non_identity_configured = true;
+}
+
+bool km_is_non_identity(uintptr_t addr)
+{
+	return km_is_non_identity_arch(addr);
+}
+
+void km_non_identity_span_add(uintptr_t base, size_t size)
+{
+	bool span_added;
+
+	span_added = ra_span_add(km_ni_arena, base, size);
+	ASSERT(span_added);
+}
+
+uintptr_t km_page_alloc(size_t size, size_t align)
+{
+	return ra_alloc(km_ni_arena, size, align);
+}
+
+void km_page_free(uintptr_t page, size_t size)
+{
+	ra_free(km_ni_arena, page, size);
+}
+
+
+/** @}
+ */
+
Index: kernel/generic/src/mm/page.c
===================================================================
--- kernel/generic/src/mm/page.c	(revision 6645a1412e9ca7394bd56aab318e76c61f97f15f)
+++ kernel/generic/src/mm/page.c	(revision 7aaed09d88be49fac8360d3017e3328ed9b0635c)
@@ -65,4 +65,5 @@
 #include <arch/mm/asid.h>
 #include <mm/as.h>
+#include <mm/km.h>
 #include <mm/frame.h>
 #include <arch/barrier.h>
@@ -177,4 +178,24 @@
 }
 
+uintptr_t hw_map(uintptr_t physaddr, size_t size)
+{
+	uintptr_t virtaddr;
+	size_t asize;
+	pfn_t i;
+
+	asize = ALIGN_UP(size, PAGE_SIZE);
+	virtaddr = km_page_alloc(asize, PAGE_SIZE);
+
+	page_table_lock(AS_KERNEL, true);
+	for (i = 0; i < ADDR2PFN(asize); i++) {
+		uintptr_t addr = PFN2ADDR(i);
+		page_mapping_insert(AS_KERNEL, virtaddr + addr, physaddr + addr,
+		    PAGE_NOT_CACHEABLE | PAGE_WRITE);
+	}
+	page_table_unlock(AS_KERNEL, true);
+	
+	return virtaddr;
+}
+
 int page_find_mapping(uintptr_t virt, void **phys)
 {
Index: kernel/generic/src/mm/reserve.c
===================================================================
--- kernel/generic/src/mm/reserve.c	(revision 6645a1412e9ca7394bd56aab318e76c61f97f15f)
+++ kernel/generic/src/mm/reserve.c	(revision 7aaed09d88be49fac8360d3017e3328ed9b0635c)
@@ -42,4 +42,7 @@
 #include <typedefs.h>
 #include <arch/types.h>
+#include <debug.h>
+
+static bool reserve_initialized = false;
 
 IRQ_SPINLOCK_STATIC_INITIALIZE_NAME(reserve_lock, "reserve_lock");
@@ -54,4 +57,5 @@
 {
 	reserve = frame_total_free_get();
+	reserve_initialized = true;
 }
 
@@ -67,4 +71,6 @@
 {
 	bool reserved = false;
+
+	ASSERT(reserve_initialized);
 
 	irq_spinlock_lock(&reserve_lock, true);
@@ -111,4 +117,7 @@
 void reserve_force_alloc(size_t size)
 {
+	if (!reserve_initialized)
+		return;
+
 	irq_spinlock_lock(&reserve_lock, true);
 	reserve -= size;
@@ -122,4 +131,7 @@
 void reserve_free(size_t size)
 {
+	if (!reserve_initialized)
+		return;
+
 	irq_spinlock_lock(&reserve_lock, true);
 	reserve += size;
