Index: kernel/arch/riscv64/include/arch/arch.h
===================================================================
--- kernel/arch/riscv64/include/arch/arch.h	(revision c16479ee260b011bc30a8c323271c6cf78dc756e)
+++ kernel/arch/riscv64/include/arch/arch.h	(revision ccc362a131dc8d066e7c7037b3d91380306905a1)
@@ -38,4 +38,6 @@
 #include <arch/boot/boot.h>
 
+extern void riscv64_pre_main(bootinfo_t *);
+
 #endif
 
Index: kernel/arch/riscv64/include/arch/mm/asid.h
===================================================================
--- kernel/arch/riscv64/include/arch/mm/asid.h	(revision c16479ee260b011bc30a8c323271c6cf78dc756e)
+++ kernel/arch/riscv64/include/arch/mm/asid.h	(revision ccc362a131dc8d066e7c7037b3d91380306905a1)
@@ -42,4 +42,7 @@
 typedef uint32_t asid_t;
 
+#define asid_get()  (ASID_START + 1)
+#define asid_put(asid)
+
 #endif
 
Index: kernel/arch/riscv64/include/arch/mm/frame.h
===================================================================
--- kernel/arch/riscv64/include/arch/mm/frame.h	(revision c16479ee260b011bc30a8c323271c6cf78dc756e)
+++ kernel/arch/riscv64/include/arch/mm/frame.h	(revision ccc362a131dc8d066e7c7037b3d91380306905a1)
@@ -43,4 +43,11 @@
 #ifndef __ASM__
 
+#include <arch/boot/boot.h>
+
+extern uintptr_t physmem_start;
+extern uintptr_t htif_frame;
+extern uintptr_t pt_frame;
+extern memmap_t memmap;
+
 extern void frame_low_arch_init(void);
 extern void frame_high_arch_init(void);
Index: kernel/arch/riscv64/include/arch/mm/km.h
===================================================================
--- kernel/arch/riscv64/include/arch/mm/km.h	(revision c16479ee260b011bc30a8c323271c6cf78dc756e)
+++ kernel/arch/riscv64/include/arch/mm/km.h	(revision ccc362a131dc8d066e7c7037b3d91380306905a1)
@@ -39,4 +39,10 @@
 #include <stddef.h>
 
+#define KM_RISCV64_IDENTITY_START      UINT64_C(0xffff800000000000)
+#define KM_RISCV64_IDENTITY_SIZE       UINT64_C(0x0000400000000000)
+
+#define KM_RISCV64_NON_IDENTITY_START  UINT64_C(0xffffc00000000000)
+#define KM_RISCV64_NON_IDENTITY_SIZE   UINT64_C(0x0000400000000000)
+
 extern void km_identity_arch_init(void);
 extern void km_non_identity_arch_init(void);
Index: kernel/arch/riscv64/include/arch/mm/page.h
===================================================================
--- kernel/arch/riscv64/include/arch/mm/page.h	(revision c16479ee260b011bc30a8c323271c6cf78dc756e)
+++ kernel/arch/riscv64/include/arch/mm/page.h	(revision ccc362a131dc8d066e7c7037b3d91380306905a1)
@@ -50,48 +50,4 @@
 
 /*
- * Page table entry types.
- *
- * - PTE_TYPE_PTR:         pointer to next level PTE
- * - PTE_TYPE_PTR_GLOBAL:  pointer to next level PTE (global mapping)
- *
- * - PTE_TYPE_SRURX:       kernel read, user read execute
- * - PTE_TYPE_SRWURWX:     kernel read write, user read write execute
- * - PTE_TYPE_SRUR:        kernel read, user read
- * - PTE_TYPE_SRWURW:      kernel read write, user read write
- * - PTE_TYPE_SRXURX:      kernel read execute, user read execute
- * - PTE_TYPE_SRWXURWX:    kernel read write execute, user read write execute
- *
- * - PTE_TYPE_SR:          kernel read
- * - PTE_TYPE_SRW:         kernel read write
- * - PTE_TYPE_SRX:         kernel read execute
- * - PTE_TYPE_SRWX:        kernel read write execute
- *
- * - PTE_TYPE_SR_GLOBAL:   kernel read (global mapping)
- * - PTE_TYPE_SRW_GLOBAL:  kernel read write (global mapping)
- * - PTE_TYPE_SRX_GLOBAL:  kernel read execute (global mapping)
- * - PTE_TYPE_SRWX_GLOBAL: kernel read write execute (global mapping)
- */
-
-#define PTE_TYPE_PTR          0
-#define PTE_TYPE_PTR_GLOBAL   1
-
-#define PTE_TYPE_SRURX        2
-#define PTE_TYPE_SRWURWX      3
-#define PTE_TYPE_SRUR         4
-#define PTE_TYPE_SRWURW       5
-#define PTE_TYPE_SRXURX       6
-#define PTE_TYPE_SRWXURWX     7
-
-#define PTE_TYPE_SR           8
-#define PTE_TYPE_SRW          9
-#define PTE_TYPE_SRX          10
-#define PTE_TYPE_SRWX         11
-
-#define PTE_TYPE_SR_GLOBAL    12
-#define PTE_TYPE_SRW_GLOBAL   13
-#define PTE_TYPE_SRX_GLOBAL   14
-#define PTE_TYPE_SRWX_GLOBAL  15
-
-/*
  * Implementation of 4-level page table interface.
  *
@@ -125,4 +81,7 @@
 #define PTL3_INDEX_ARCH(vaddr)  (((vaddr) >> 12) & 0x1ff)
 
+/* Flags mask for non-leaf page table entries */
+#define NON_LEAF_MASK  (~(PAGE_READ | PAGE_WRITE | PAGE_EXEC))
+
 /* Get PTE address accessors for each level. */
 #define GET_PTL1_ADDRESS_ARCH(ptl0, i) \
@@ -139,5 +98,6 @@
 
 /* Set PTE address accessors for each level. */
-#define SET_PTL0_ADDRESS_ARCH(ptl0)
+#define SET_PTL0_ADDRESS_ARCH(ptl0) \
+	(write_satp((uintptr_t) (ptl0)))
 
 #define SET_PTL1_ADDRESS_ARCH(ptl0, i, a) \
@@ -167,15 +127,15 @@
 
 /* Set PTE flags accessors for each level. */
-#define SET_PTL1_FLAGS_ARCH(ptl0, i, x) \
-	set_pt_flags((pte_t *) (ptl0), (size_t) (i), (x))
-
-#define SET_PTL2_FLAGS_ARCH(ptl1, i, x) \
-	set_pt_flags((pte_t *) (ptl1), (size_t) (i), (x))
-
-#define SET_PTL3_FLAGS_ARCH(ptl2, i, x) \
-	set_pt_flags((pte_t *) (ptl2), (size_t) (i), (x))
-
-#define SET_FRAME_FLAGS_ARCH(ptl3, i, x) \
-	set_pt_flags((pte_t *) (ptl3), (size_t) (i), (x))
+#define SET_PTL1_FLAGS_ARCH(ptl0, i, flags) \
+	set_pt_flags((pte_t *) (ptl0), (size_t) (i), ((flags) & NON_LEAF_MASK))
+
+#define SET_PTL2_FLAGS_ARCH(ptl1, i, flags) \
+	set_pt_flags((pte_t *) (ptl1), (size_t) (i), ((flags) & NON_LEAF_MASK))
+
+#define SET_PTL3_FLAGS_ARCH(ptl2, i, flags) \
+	set_pt_flags((pte_t *) (ptl2), (size_t) (i), ((flags) & NON_LEAF_MASK))
+
+#define SET_FRAME_FLAGS_ARCH(ptl3, i, flags) \
+	set_pt_flags((pte_t *) (ptl3), (size_t) (i), (flags))
 
 /* Set PTE present accessors for each level. */
@@ -196,15 +156,6 @@
 #define PTE_PRESENT_ARCH(pte)     ((pte)->valid != 0)
 #define PTE_GET_FRAME_ARCH(pte)   ((uintptr_t) (pte)->pfn << 12)
-
-#define PTE_WRITABLE_ARCH(pte) \
-	(((pte)->type == PTE_TYPE_SRWURWX) || \
-	((pte)->type == PTE_TYPE_SRWURW) || \
-	((pte)->type == PTE_TYPE_SRWXURWX))
-
-#define PTE_EXECUTABLE_ARCH(pte) \
-	(((pte)->type == PTE_TYPE_SRURX) || \
-	((pte)->type == PTE_TYPE_SRWURWX) || \
-	((pte)->type == PTE_TYPE_SRXURX) || \
-	((pte)->type == PTE_TYPE_SRWXURWX))
+#define PTE_WRITABLE_ARCH(pte)    ((pte)->writable != 0)
+#define PTE_EXECUTABLE_ARCH(pte)  ((pte)->executable != 0)
 
 #ifndef __ASM__
@@ -217,8 +168,12 @@
 typedef struct {
 	unsigned long valid : 1;       /**< Valid bit. */
-	unsigned long type : 4;        /**< Entry type. */
-	unsigned long referenced : 1;  /**< Refenced bit. */
+	unsigned long readable : 1;    /**< Readable bit. */
+	unsigned long writable : 1;    /**< Writable bit. */
+	unsigned long executable : 1;  /**< Executable bit. */
+	unsigned long user : 1;        /**< User mode accessible bit. */
+	unsigned long global : 1;      /**< Global mapping bit. */
+	unsigned long accessed : 1;    /**< Accessed bit. */
 	unsigned long dirty : 1;       /**< Dirty bit. */
-	unsigned long reserved : 3;    /**< Reserved bits. */
+	unsigned long reserved : 2;    /**< Reserved bits. */
 	unsigned long pfn : 54;        /**< Physical frame number. */
 } pte_t;
@@ -229,9 +184,9 @@
 	
 	return (((!entry->valid) << PAGE_PRESENT_SHIFT) |
-	    ((entry->type < PTE_TYPE_SR) << PAGE_USER_SHIFT) |
-	    PAGE_READ |
-	    (PTE_WRITABLE_ARCH(entry) << PAGE_WRITE_SHIFT) |
-	    (PTE_EXECUTABLE_ARCH(entry) << PAGE_EXEC_SHIFT) |
-	    ((entry->type >= PTE_TYPE_SR_GLOBAL) << PAGE_GLOBAL_SHIFT));
+	    (entry->user << PAGE_USER_SHIFT) |
+	    (entry->readable << PAGE_READ_SHIFT) |
+	    (entry->writable << PAGE_WRITE_SHIFT) |
+	    (entry->executable << PAGE_EXEC_SHIFT) |
+	    (entry->global << PAGE_GLOBAL_SHIFT));
 }
 
@@ -241,16 +196,11 @@
 	
 	entry->valid = !(flags & PAGE_NOT_PRESENT);
-	
-	if ((flags & PAGE_WRITE) != 0) {
-		if ((flags & PAGE_EXEC) != 0)
-			entry->type = PTE_TYPE_SRWXURWX;
-		else
-			entry->type = PTE_TYPE_SRWURW;
-	} else {
-		if ((flags & PAGE_EXEC) != 0)
-			entry->type = PTE_TYPE_SRXURX;
-		else
-			entry->type = PTE_TYPE_SRUR;
-	}
+	entry->readable = (flags & PAGE_READ) != 0;
+	entry->writable = (flags & PAGE_WRITE) != 0;
+	entry->executable = (flags & PAGE_EXEC) != 0;
+	entry->user = (flags & PAGE_USER) != 0;
+	entry->global = (flags & PAGE_GLOBAL) != 0;
+	entry->accessed = 1;
+	entry->dirty = 1;
 }
 
@@ -264,4 +214,5 @@
 extern void page_arch_init(void);
 extern void page_fault(unsigned int, istate_t *);
+extern void write_satp(uintptr_t);
 
 #endif /* __ASM__ */
Index: kernel/arch/riscv64/src/mm/as.c
===================================================================
--- kernel/arch/riscv64/src/mm/as.c	(revision c16479ee260b011bc30a8c323271c6cf78dc756e)
+++ kernel/arch/riscv64/src/mm/as.c	(revision ccc362a131dc8d066e7c7037b3d91380306905a1)
@@ -40,4 +40,15 @@
 }
 
+/** Install address space.
+ *
+ * Install ASID.
+ *
+ * @param as Address space structure.
+ */
+void as_install_arch(as_t *as)
+{
+	// FIXME
+}
+
 /** @}
  */
Index: kernel/arch/riscv64/src/mm/frame.c
===================================================================
--- kernel/arch/riscv64/src/mm/frame.c	(revision c16479ee260b011bc30a8c323271c6cf78dc756e)
+++ kernel/arch/riscv64/src/mm/frame.c	(revision ccc362a131dc8d066e7c7037b3d91380306905a1)
@@ -32,5 +32,7 @@
 
 #include <mm/frame.h>
+#include <arch/boot/boot.h>
 #include <arch/mm/frame.h>
+#include <arch/drivers/ucb.h>
 #include <mm/as.h>
 #include <config.h>
@@ -39,9 +41,10 @@
 #include <align.h>
 #include <macros.h>
-
 #include <print.h>
 
-size_t hardcoded_unmapped_ktext_size = 0;
-size_t hardcoded_unmapped_kdata_size = 0;
+uintptr_t physmem_start;
+uintptr_t htif_frame;
+uintptr_t pt_frame;
+memmap_t memmap;
 
 void physmem_print(void)
@@ -49,11 +52,51 @@
 }
 
+static void frame_common_arch_init(bool low)
+{
+	pfn_t minconf =
+	    max3(ADDR2PFN(physmem_start), htif_frame + 1, pt_frame + 1);
+	
+	for (size_t i = 0; i < memmap.cnt; i++) {
+		/* To be safe, make the available zone possibly smaller */
+		uintptr_t base = ALIGN_UP((uintptr_t) memmap.zones[i].start,
+		    FRAME_SIZE);
+		size_t size = ALIGN_DOWN(memmap.zones[i].size -
+		    (base - ((uintptr_t) memmap.zones[i].start)), FRAME_SIZE);
+		
+		if (!frame_adjust_zone_bounds(low, &base, &size))
+			return;
+		
+		pfn_t pfn = ADDR2PFN(base);
+		size_t count = SIZE2FRAMES(size);
+		pfn_t conf;
+		
+		if (low) {
+			if ((minconf < pfn) || (minconf >= pfn + count))
+				conf = pfn;
+			else
+				conf = minconf;
+			
+			zone_create(pfn, count, conf,
+			    ZONE_AVAILABLE | ZONE_LOWMEM);
+		} else {
+			conf = zone_external_conf_alloc(count);
+			if (conf != 0)
+				zone_create(pfn, count, conf,
+				    ZONE_AVAILABLE | ZONE_HIGHMEM);
+		}
+	}
+}
 
 void frame_low_arch_init(void)
 {
+	frame_common_arch_init(true);
+	
+	frame_mark_unavailable(htif_frame, 1);
+	frame_mark_unavailable(pt_frame, 1);
 }
 
 void frame_high_arch_init(void)
 {
+	frame_common_arch_init(false);
 }
 
Index: kernel/arch/riscv64/src/mm/km.c
===================================================================
--- kernel/arch/riscv64/src/mm/km.c	(revision c16479ee260b011bc30a8c323271c6cf78dc756e)
+++ kernel/arch/riscv64/src/mm/km.c	(revision ccc362a131dc8d066e7c7037b3d91380306905a1)
@@ -32,18 +32,25 @@
 
 #include <arch/mm/km.h>
+#include <mm/km.h>
 #include <stdbool.h>
 #include <typedefs.h>
+#include <macros.h>
 
 void km_identity_arch_init(void)
 {
+	config.identity_base = KM_RISCV64_IDENTITY_START;
+	config.identity_size = KM_RISCV64_IDENTITY_SIZE;
 }
 
 void km_non_identity_arch_init(void)
 {
+	km_non_identity_span_add(KM_RISCV64_NON_IDENTITY_START,
+	    KM_RISCV64_NON_IDENTITY_SIZE);
 }
 
 bool km_is_non_identity_arch(uintptr_t addr)
 {
-	return false;
+	return iswithin(KM_RISCV64_NON_IDENTITY_START,
+	    KM_RISCV64_NON_IDENTITY_SIZE, addr, 1);
 }
 
Index: kernel/arch/riscv64/src/mm/page.c
===================================================================
--- kernel/arch/riscv64/src/mm/page.c	(revision c16479ee260b011bc30a8c323271c6cf78dc756e)
+++ kernel/arch/riscv64/src/mm/page.c	(revision ccc362a131dc8d066e7c7037b3d91380306905a1)
@@ -48,8 +48,36 @@
 #include <interrupt.h>
 
+#define SATP_PFN_MASK  UINT64_C(0x00000fffffffffff)
+
+#define SATP_MODE_MASK  UINT64_C(0xf000000000000000)
+#define SATP_MODE_BARE  UINT64_C(0x0000000000000000)
+#define SATP_MODE_SV39  UINT64_C(0x8000000000000000)
+#define SATP_MODE_SV48  UINT64_C(0x9000000000000000)
+
 void page_arch_init(void)
 {
-	if (config.cpu_active == 1)
+	if (config.cpu_active == 1) {
 		page_mapping_operations = &pt_mapping_operations;
+		
+		page_table_lock(AS_KERNEL, true);
+		
+		/*
+		 * PA2KA(identity) mapping for all low-memory frames.
+		 */
+		for (uintptr_t cur = 0;
+		    cur < min(config.identity_size, config.physmem_end);
+		    cur += FRAME_SIZE)
+			page_mapping_insert(AS_KERNEL, PA2KA(cur), cur,
+			    PAGE_GLOBAL | PAGE_CACHEABLE | PAGE_EXEC | PAGE_WRITE | PAGE_READ);
+		
+		page_table_unlock(AS_KERNEL, true);
+		
+		// FIXME: register page fault extension handler
+		
+		write_satp((uintptr_t) AS_KERNEL->genarch.page_table);
+		
+		/* The boot page table is no longer needed. */
+		// FIXME: frame_mark_available(pt_frame, 1);
+	}
 }
 
@@ -58,4 +86,15 @@
 }
 
+void write_satp(uintptr_t ptl0)
+{
+	uint64_t satp = ((ptl0 >> FRAME_WIDTH) & SATP_PFN_MASK) |
+	    SATP_MODE_SV48;
+	
+	asm volatile (
+		"csrw sptbr, %[satp]\n"
+		:: [satp] "r" (satp)
+	);
+}
+
 /** @}
  */
Index: kernel/arch/riscv64/src/riscv64.c
===================================================================
--- kernel/arch/riscv64/src/riscv64.c	(revision c16479ee260b011bc30a8c323271c6cf78dc756e)
+++ kernel/arch/riscv64/src/riscv64.c	(revision ccc362a131dc8d066e7c7037b3d91380306905a1)
@@ -49,12 +49,51 @@
 #include <console/console.h>
 #include <mem.h>
+#include <str.h>
 
 char memcpy_from_uspace_failover_address;
 char memcpy_to_uspace_failover_address;
 
+static void riscv64_post_mm_init(void);
+
 arch_ops_t riscv64_ops = {
+	.post_mm_init = riscv64_post_mm_init
 };
 
 arch_ops_t *arch_ops = &riscv64_ops;
+
+void riscv64_pre_main(bootinfo_t *bootinfo)
+{
+	physmem_start = bootinfo->physmem_start;
+	htif_frame = bootinfo->htif_frame;
+	pt_frame = bootinfo->pt_frame;
+	
+	htif_init(bootinfo->ucbinfo.tohost, bootinfo->ucbinfo.fromhost);
+	
+	/* Copy tasks map. */
+	init.cnt = min3(bootinfo->taskmap.cnt, TASKMAP_MAX_RECORDS,
+	    CONFIG_INIT_TASKS);
+	
+	for (size_t i = 0; i < init.cnt; i++) {
+		init.tasks[i].paddr = KA2PA(bootinfo->taskmap.tasks[i].addr);
+		init.tasks[i].size = bootinfo->taskmap.tasks[i].size;
+		str_cpy(init.tasks[i].name, CONFIG_TASK_NAME_BUFLEN,
+		    bootinfo->taskmap.tasks[i].name);
+	}
+	
+	/* Copy physical memory map. */
+	memmap.total = bootinfo->memmap.total;
+	memmap.cnt = min(bootinfo->memmap.cnt, MEMMAP_MAX_RECORDS);
+	for (size_t i = 0; i < memmap.cnt; i++) {
+		memmap.zones[i].start = bootinfo->memmap.zones[i].start;
+		memmap.zones[i].size = bootinfo->memmap.zones[i].size;
+	}
+}
+
+void riscv64_post_mm_init(void)
+{
+	outdev_t *htifout = htifout_init();
+	if (htifout)
+		stdout_wire(htifout);
+}
 
 void calibrate_delay_loop(void)
@@ -90,14 +129,4 @@
 }
 
-int context_save_arch(context_t *ctx)
-{
-	return 1;
-}
-
-void context_restore_arch(context_t *ctx)
-{
-	while (true);
-}
-
 void fpu_init(void)
 {
@@ -122,8 +151,4 @@
 }
 
-void early_putchar(wchar_t ch)
-{
-}
-
 /** @}
  */
