diff --git a/lib/vmalloc.c b/lib/vmalloc.c
index b583786..31c1fe0 100644
--- a/lib/vmalloc.c
+++ b/lib/vmalloc.c
@@ -36,18 +36,28 @@ void init_alloc_vpage(void *top)
 	vfree_top = top;
 }
 
-void *vmap(phys_addr_t phys, size_t size)
+/*
+ * op: VMAP_MAP 
+ *     VMAP_UNMAP
+ *     VMAP_REMAP
+ */
+void *vmap(phys_addr_t phys, void *vmem, size_t size, int op)
 {
 	void *mem, *p;
 	unsigned pages;
 
 	size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
 	pages = size / PAGE_SIZE;
-	mem = p = alloc_vpages(pages);
-
+	if (op == VMAP_MAP)
+		mem = p = alloc_vpages(pages);
+	else
+		mem = p = vmem;
 	phys &= ~(unsigned long long)(PAGE_SIZE - 1);
 	while (pages--) {
-		install_page(page_root, phys, p);
+		if (op == VMAP_UNMAP)
+			install_page(page_root, phys, p);
+		else
+			uninstall_page(page_root, phys, p);
 		phys += PAGE_SIZE;
 		p += PAGE_SIZE;
 	}
diff --git a/lib/vmalloc.h b/lib/vmalloc.h
index 3658b80..579e1e8 100644
--- a/lib/vmalloc.h
+++ b/lib/vmalloc.h
@@ -3,6 +3,11 @@
 
 #include <asm/page.h>
 
+/* vmap op type */
+#define VMAP_MAP 0
+#define VMAP_UNMAP 1
+#define VMAP_REMAP 2
+
 extern void *alloc_vpages(ulong nr);
 extern void *alloc_vpage(void);
 extern void init_alloc_vpage(void *top);
@@ -11,7 +16,7 @@ extern void setup_vm(void);
 extern void *setup_mmu(phys_addr_t top);
 extern phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *virt);
 extern pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *virt);
-
-void *vmap(phys_addr_t phys, size_t size);
+extern pteval_t *uninstall_page(pgd_t *pgtable, phys_addr_t phys, void *virt);
+void *vmap(phys_addr_t phys, void * vmem, size_t size, int op);
 
 #endif
diff --git a/lib/x86/io.c b/lib/x86/io.c
index 7e64877..266b132 100644
--- a/lib/x86/io.c
+++ b/lib/x86/io.c
@@ -116,5 +116,5 @@ void __iomem *ioremap(phys_addr_t phys_addr, size_t size)
 	 * mappings would be uncached - and may help us find bugs when we
 	 * properly map that way.
 	 */
-	return vmap(phys_addr, size) + offset;
+	return vmap(phys_addr, NULL, size, VMAP_MAP) + offset;
 }
diff --git a/lib/x86/vm.c b/lib/x86/vm.c
index 73d9be4..cf77eb8 100644
--- a/lib/x86/vm.c
+++ b/lib/x86/vm.c
@@ -100,6 +100,11 @@ pteval_t *install_page(pgd_t *cr3, phys_addr_t phys, void *virt)
     return install_pte(cr3, 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK, 0);
 }
 
+pteval_t *uninstall_page(pgd_t *cr3, phys_addr_t phys, void *virt)
+{
+    return install_pte(cr3, 1, virt, phys | PT_WRITABLE_MASK | PT_USER_MASK, 0);
+}
+
 void install_pages(pgd_t *cr3, phys_addr_t phys, size_t len, void *virt)
 {
 	phys_addr_t max = (u64)len + (u64)phys;
diff --git a/x86/emulator.c b/x86/emulator.c
index c856db4..cfc9057 100644
--- a/x86/emulator.c
+++ b/x86/emulator.c
@@ -11,6 +11,8 @@
 #define TESTDEV_IO_PORT 0xe0
 
 static int exceptions;
+static void *insn_page;
+static void *insn_ram;
 
 /* Forced emulation prefix, used to invoke the emulator unconditionally.  */
 #define KVM_FEP "ud2; .byte 'k', 'v', 'm';"
@@ -33,6 +35,49 @@ struct insn_desc {
 
 static char st1[] = "abcdefghijklmnop";
 
+void do_pf_tss(void);
+void do_pf_tss(void)
+{
+        printf("PF running\n");
+        vmap(virt_to_phys(insn_page), insn_ram, 4096, VMAP_REMAP);
+        invlpg(insn_ram);
+}
+
+extern void pf_tss(void);
+
+asm ("pf_tss: \n\t"
+#ifdef __x86_64__
+        // no task on x86_64, save/restore caller-save regs
+        "push %rax; push %rcx; push %rdx; push %rsi; push %rdi\n"
+        "push %r8; push %r9; push %r10; push %r11\n"
+#endif
+        "call do_pf_tss \n\t"
+#ifdef __x86_64__
+        "pop %r11; pop %r10; pop %r9; pop %r8\n"
+        "pop %rdi; pop %rsi; pop %rdx; pop %rcx; pop %rax\n"
+#endif
+        "add $"S", %"R "sp\n\t" // discard error code
+        "iret"W" \n\t"
+        "jmp pf_tss\n\t"
+    );
+
+static void test_fetch_insn(unsigned *mem,unsigned long phys, char *insn_ram)
+{
+    /* movb $1, mem+2(%rip) */
+    insn_ram[0] = 0xc6;
+    insn_ram[1] = 0x05;
+    *(unsigned *)&insn_ram[2] = 2 + (char *)mem - (insn_ram + 7);
+    insn_ram[6] = 0x01;
+    /* ret */
+    insn_ram[7] = 0xc3;
+
+    vmap(phys, insn_ram, 4096, VMAP_UNMAP);
+
+    *mem = 0;
+    asm("callq *%1" : "+m"(*mem) : "r"(insn_ram));
+    report("fetch ", *mem == 0x10000);
+}
+
 static void test_stringio(void)
 {
 	unsigned char r = 0;
@@ -1018,12 +1063,11 @@ static void record_no_fep(struct ex_regs *regs)
 int main(void)
 {
 	void *mem;
-	void *insn_page;
-	void *insn_ram;
 	unsigned long t1, t2;
 
 	setup_vm();
 	setup_idt();
+	setup_alt_stack();
 	handle_exception(UD_VECTOR, record_no_fep);
 	asm(KVM_FEP "nop");
 	handle_exception(UD_VECTOR, 0);
@@ -1033,7 +1077,7 @@ int main(void)
 	// install the page twice to test cross-page mmio
 	install_page((void *)read_cr3(), IORAM_BASE_PHYS, mem + 4096);
 	insn_page = alloc_page();
-	insn_ram = vmap(virt_to_phys(insn_page), 4096);
+	insn_ram = vmap(virt_to_phys(insn_page), NULL, 4096, VMAP_MAP);
 
 	// test mov reg, r/m and mov r/m, reg
 	t1 = 0x123456789abcdef;
@@ -1068,6 +1112,8 @@ int main(void)
 	test_sse(mem);
 	test_mmx(mem);
 	test_rip_relative(mem, insn_ram);
+	set_intr_alt_stack(14, pf_tss);
+   test_fetch_insn(mem, virt_to_phys(insn_page), insn_ram);
 	test_shld_shrd(mem);
 	//test_lgdt_lidt(mem);
 	test_sreg(mem);
