Index: kernel/arch/amd64/src/vreg.c
===================================================================
--- kernel/arch/amd64/src/vreg.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/arch/amd64/src/vreg.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -62,6 +62,4 @@
 	frame = frame_alloc(1, FRAME_ATOMIC | FRAME_HIGHMEM, 0);
 	if (!frame)
-		frame = frame_alloc(1, FRAME_ATOMIC | FRAME_LOWMEM, 0);
-	if (!frame)
 		panic("Cannot allocate VREG frame.");
 
Index: kernel/arch/arm32/src/mm/page.c
===================================================================
--- kernel/arch/arm32/src/mm/page.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/arch/arm32/src/mm/page.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -69,5 +69,5 @@
 #ifdef HIGH_EXCEPTION_VECTORS
 	/* Create mapping for exception table at high offset */
-	uintptr_t ev_frame = frame_alloc(1, FRAME_NONE, 0);
+	uintptr_t ev_frame = frame_alloc(1, FRAME_HIGHMEM, 0);
 	page_mapping_insert(AS_KERNEL, EXC_BASE_ADDRESS, ev_frame, flags);
 #else
Index: kernel/arch/arm32/src/ras.c
===================================================================
--- kernel/arch/arm32/src/ras.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/arch/arm32/src/ras.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -51,8 +51,5 @@
 void ras_init(void)
 {
-	uintptr_t frame =
-	    frame_alloc(1, FRAME_ATOMIC | FRAME_HIGHMEM, 0);
-	if (!frame)
-		frame = frame_alloc(1, FRAME_LOWMEM, 0);
+	uintptr_t frame = frame_alloc(1, FRAME_HIGHMEM, 0);
 
 	ras_page = (uintptr_t *) km_map(frame, PAGE_SIZE, PAGE_SIZE,
Index: kernel/arch/ia32/src/vreg.c
===================================================================
--- kernel/arch/ia32/src/vreg.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/arch/ia32/src/vreg.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -63,6 +63,4 @@
 	frame = frame_alloc(1, FRAME_ATOMIC | FRAME_HIGHMEM, 0);
 	if (!frame)
-		frame = frame_alloc(1, FRAME_ATOMIC | FRAME_LOWMEM, 0);
-	if (!frame)
 		panic("Cannot allocate VREG frame.");
 
Index: kernel/arch/ia64/src/mm/vhpt.c
===================================================================
--- kernel/arch/ia64/src/mm/vhpt.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/arch/ia64/src/mm/vhpt.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -44,5 +44,5 @@
 {
 	uintptr_t vhpt_frame =
-	    frame_alloc(SIZE2FRAMES(VHPT_SIZE), FRAME_ATOMIC, 0);
+	    frame_alloc(SIZE2FRAMES(VHPT_SIZE), FRAME_LOWMEM | FRAME_ATOMIC, 0);
 	if (!vhpt_frame)
 		panic("Kernel configured with VHPT but no memory for table.");
Index: kernel/arch/sparc64/src/mm/sun4u/as.c
===================================================================
--- kernel/arch/sparc64/src/mm/sun4u/as.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/arch/sparc64/src/mm/sun4u/as.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -63,5 +63,6 @@
 {
 #ifdef CONFIG_TSB
-	uintptr_t tsb_base = frame_alloc(TSB_FRAMES, flags, TSB_SIZE - 1);
+	uintptr_t tsb_base = frame_alloc(TSB_FRAMES, FRAME_LOWMEM | flags,
+	    TSB_SIZE - 1);
 	if (!tsb_base)
 		return ENOMEM;
Index: kernel/arch/sparc64/src/mm/sun4v/as.c
===================================================================
--- kernel/arch/sparc64/src/mm/sun4v/as.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/arch/sparc64/src/mm/sun4v/as.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -66,5 +66,6 @@
 {
 #ifdef CONFIG_TSB
-	uintptr_t tsb_base = frame_alloc(TSB_FRAMES, flags, TSB_SIZE - 1);
+	uintptr_t tsb_base = frame_alloc(TSB_FRAMES, FRAME_LOWMEM | flags,
+	    TSB_SIZE - 1);
 	if (!tsb_base)
 		return ENOMEM;
Index: kernel/generic/include/mm/frame.h
===================================================================
--- kernel/generic/include/mm/frame.h	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/generic/include/mm/frame.h	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -59,6 +59,13 @@
 /** Allocate a frame which can be identity-mapped. */
 #define FRAME_LOWMEM      0x08
-/** Allocate a frame which cannot be identity-mapped. */
+/**
+ * Allocate a frame outside the identity-mapped region if possible.
+ * Fall back to low memory if that fails.
+ */
 #define FRAME_HIGHMEM     0x10
+
+// NOTE: If neither FRAME_LOWMEM nor FRAME_HIGHMEM is set, FRAME_LOWMEM is
+//       assumed as a safe default, and a runtime warning may be issued.
+//       If both are set, FRAME_LOWMEM takes priority.
 
 typedef uint8_t zone_flags_t;
@@ -78,10 +85,4 @@
 /** Mask of zone bits that must be matched exactly. */
 #define ZONE_EF_MASK  0x07
-
-#define FRAME_TO_ZONE_FLAGS(ff) \
-	((((ff) & FRAME_LOWMEM) ? ZONE_LOWMEM : \
-	    (((ff) & FRAME_HIGHMEM) ? ZONE_HIGHMEM : \
-	    ZONE_LOWMEM /* | ZONE_HIGHMEM */)) | \
-	    ZONE_AVAILABLE)
 
 #define ZONE_FLAGS_MATCH(zf, f) \
Index: kernel/generic/src/ddi/ddi.c
===================================================================
--- kernel/generic/src/ddi/ddi.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/generic/src/ddi/ddi.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -385,4 +385,5 @@
 		return EINVAL;
 
+	// FIXME: probably need to ensure that the memory is suitable for DMA
 	*phys = frame_alloc(frames, FRAME_ATOMIC, constraint);
 	if (*phys == 0)
Index: kernel/generic/src/mm/frame.c
===================================================================
--- kernel/generic/src/mm/frame.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/generic/src/mm/frame.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -815,4 +815,18 @@
 }
 
+static size_t try_find_zone(size_t count, bool lowmem,
+    pfn_t frame_constraint, size_t hint)
+{
+	if (!lowmem) {
+		size_t znum = find_free_zone(count,
+		    ZONE_HIGHMEM | ZONE_AVAILABLE, frame_constraint, hint);
+		if (znum != (size_t) -1)
+			return znum;
+	}
+
+	return find_free_zone(count, ZONE_LOWMEM | ZONE_AVAILABLE,
+	    frame_constraint, hint);
+}
+
 /** Allocate frames of physical memory.
  *
@@ -843,9 +857,11 @@
 	irq_spinlock_lock(&zones.lock, true);
 
+	// TODO: Print diagnostic if neither is explicitly specified.
+	bool lowmem = (flags & FRAME_LOWMEM) || !(flags & FRAME_HIGHMEM);
+
 	/*
 	 * First, find suitable frame zone.
 	 */
-	size_t znum = find_free_zone(count, FRAME_TO_ZONE_FLAGS(flags),
-	    frame_constraint, hint);
+	size_t znum = try_find_zone(count, lowmem, frame_constraint, hint);
 
 	/*
@@ -859,5 +875,5 @@
 
 		if (freed > 0)
-			znum = find_free_zone(count, FRAME_TO_ZONE_FLAGS(flags),
+			znum = try_find_zone(count, lowmem,
 			    frame_constraint, hint);
 
@@ -868,5 +884,5 @@
 
 			if (freed > 0)
-				znum = find_free_zone(count, FRAME_TO_ZONE_FLAGS(flags),
+				znum = try_find_zone(count, lowmem,
 				    frame_constraint, hint);
 		}
Index: kernel/generic/src/mm/km.c
===================================================================
--- kernel/generic/src/mm/km.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/generic/src/mm/km.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -121,5 +121,5 @@
 		return base;
 	else
-		return (uintptr_t) NULL;
+		panic("Kernel ran out of virtual address space.");
 }
 
@@ -256,18 +256,9 @@
 	uintptr_t frame;
 
-	frame = frame_alloc(1, FRAME_HIGHMEM | FRAME_ATOMIC | flags, 0);
-	if (frame) {
+	frame = frame_alloc(1, FRAME_HIGHMEM | flags, 0);
+	if (frame >= config.identity_size) {
 		page = km_map(frame, PAGE_SIZE, PAGE_SIZE,
 		    PAGE_READ | PAGE_WRITE | PAGE_CACHEABLE);
-		if (!page) {
-			frame_free(frame, 1);
-			goto lowmem;
-		}
 	} else {
-	lowmem:
-		frame = frame_alloc(1, FRAME_LOWMEM | flags, 0);
-		if (!frame)
-			return (uintptr_t) NULL;
-
 		page = PA2KA(frame);
 	}
Index: kernel/generic/src/mm/slab.c
===================================================================
--- kernel/generic/src/mm/slab.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/generic/src/mm/slab.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -188,5 +188,5 @@
 
 	uintptr_t data_phys =
-	    frame_alloc_generic(cache->frames, flags, 0, &zone);
+	    frame_alloc_generic(cache->frames, FRAME_LOWMEM | flags, 0, &zone);
 	if (!data_phys)
 		return NULL;
Index: kernel/generic/src/time/clock.c
===================================================================
--- kernel/generic/src/time/clock.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/generic/src/time/clock.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -81,5 +81,5 @@
 void clock_counter_init(void)
 {
-	uintptr_t faddr = frame_alloc(1, FRAME_ATOMIC, 0);
+	uintptr_t faddr = frame_alloc(1, FRAME_LOWMEM | FRAME_ATOMIC, 0);
 	if (faddr == 0)
 		panic("Cannot allocate page for clock.");
Index: kernel/test/mm/mapping1.c
===================================================================
--- kernel/test/mm/mapping1.c	(revision 377818191c87c5eca4e9c6ab5eb18e5d7d8b9103)
+++ kernel/test/mm/mapping1.c	(revision 482f968b8b53156bb4454e6c369c1e92dc426ea9)
@@ -41,5 +41,5 @@
 const char *test_mapping1(void)
 {
-	uintptr_t frame = frame_alloc(1, FRAME_NONE, 0);
+	uintptr_t frame = frame_alloc(1, FRAME_HIGHMEM, 0);
 
 	uintptr_t page0 = km_map(frame, FRAME_SIZE, FRAME_SIZE,
