diff -r 6cb4bb543186 arch/i386/Kconfig
--- a/arch/i386/Kconfig	Thu Jan 25 11:14:55 2007 -0500
+++ b/arch/i386/Kconfig	Thu Jan 25 11:29:54 2007 -0500
@@ -252,7 +252,7 @@ source "arch/i386/Kconfig.cpu"
 
 config LGUEST
 	tristate "Linux hypervisor example code"
-	depends on X86 && PARAVIRT && EXPERIMENTAL && !SMP && !X86_PAE
+	depends on X86 && PARAVIRT && EXPERIMENTAL && !X86_PAE
 	select LGUEST_GUEST
 	select HVC_DRIVER
 	---help---
diff -r 6cb4bb543186 arch/i386/kernel/lguest.c
--- a/arch/i386/kernel/lguest.c	Thu Jan 25 11:14:55 2007 -0500
+++ b/arch/i386/kernel/lguest.c	Fri Jan 26 16:34:17 2007 -0500
@@ -209,6 +209,14 @@ static fastcall unsigned long lguest_api
 {
 	return 0;
 }
+
+static void lguest_setup_boot_clock(void)
+{
+}
+
+static void lguest_setup_secondary_clock(void)
+{
+}
 #endif
 
 /* We move eflags word to lguest_page.irq_enabled to restore interrupt
@@ -418,6 +426,8 @@ static __attribute_used__ __init void lg
 	paravirt_ops.apic_write = lguest_apic_write;
 	paravirt_ops.apic_write_atomic = lguest_apic_write_atomic;
 	paravirt_ops.apic_read = lguest_apic_read;
+	paravirt_ops.setup_boot_clock = lguest_setup_boot_clock;
+	paravirt_ops.setup_secondary_clock = lguest_setup_secondary_clock;
 #endif
 	paravirt_ops.load_idt = lguest_load_idt;
 	paravirt_ops.iret = lguest_iret;
diff -r 6cb4bb543186 arch/i386/kernel/smp.c
--- a/arch/i386/kernel/smp.c	Thu Jan 25 11:14:55 2007 -0500
+++ b/arch/i386/kernel/smp.c	Fri Jan 26 16:33:46 2007 -0500
@@ -479,6 +479,7 @@ void flush_tlb_all(void)
 {
 	on_each_cpu(do_flush_tlb_all, NULL, 1, 1);
 }
+EXPORT_SYMBOL_GPL(flush_tlb_all);
 
 /*
  * this function sends a 'reschedule' IPI to another CPU.
diff -r 6cb4bb543186 drivers/lguest/core.c
--- a/drivers/lguest/core.c	Thu Jan 25 11:14:55 2007 -0500
+++ b/drivers/lguest/core.c	Fri Jan 26 18:50:27 2007 -0500
@@ -373,6 +373,8 @@ int run_guest(struct lguest_guest_info *
 				linfo->guest->gdt_table[GDT_ENTRY_TLS_MIN+i].a
 					|= linfo->tls_limits[i];
 		}
+		
+		lguest_flush_tlb();
 
 		local_irq_disable();
 		map_trap_page(linfo);
@@ -637,11 +639,12 @@ static void __init setup_hypervisor(stru
 static void __init setup_hypervisor(struct hypervisor *lguest)
 {
 	struct desc_struct lguest_cs, lguest_ds;
-	unsigned int i;
+	unsigned int i, cpu = get_cpu();
 
 	/* Insert lguest entries in GDT (copy kernel segments). */
-	lguest_cs = get_cpu_gdt_table(smp_processor_id())[GDT_ENTRY_KERNEL_CS];
-	lguest_ds = get_cpu_gdt_table(smp_processor_id())[GDT_ENTRY_KERNEL_DS];
+	lguest_cs = get_cpu_gdt_table(cpu)[GDT_ENTRY_KERNEL_CS];
+	lguest_ds = get_cpu_gdt_table(cpu)[GDT_ENTRY_KERNEL_DS];
+	put_cpu();
 	for_each_possible_cpu(i) {
 		get_cpu_gdt_table(i)[GDT_ENTRY_LGUEST_CS] = lguest_cs;
 		get_cpu_gdt_table(i)[GDT_ENTRY_LGUEST_DS] = lguest_ds;
diff -r 6cb4bb543186 drivers/lguest/lguest.h
--- a/drivers/lguest/lguest.h	Thu Jan 25 11:14:55 2007 -0500
+++ b/drivers/lguest/lguest.h	Fri Jan 26 16:43:03 2007 -0500
@@ -35,6 +35,7 @@
 #include <linux/binfmts.h>
 #include <linux/futex.h>
 #include <linux/lguest.h>
+#include <asm/tlbflush.h>
 #include <asm/lguest.h>
 #include <asm/semaphore.h>
 #include "irq_vectors.h"
@@ -250,5 +251,18 @@ struct lguest_guest
 	/* This is the stack on which we push our regs. */
 	struct lguest_regs regs;
 };
+
+#ifdef CONFIG_SMP
+static inline void lguest_flush_tlb(void)
+{
+	flush_tlb_all();
+}
+#else
+static inline void lguest_flush_tlb(void)
+{
+}
+#endif	/* CONFIG_SMP */
+
 #endif	/* __ASSEMBLY__ */
+
 #endif	/* _LGUEST_H */
diff -r 6cb4bb543186 drivers/lguest/page_tables.c
--- a/drivers/lguest/page_tables.c	Thu Jan 25 11:14:55 2007 -0500
+++ b/drivers/lguest/page_tables.c	Fri Jan 26 19:05:57 2007 -0500
@@ -5,7 +5,7 @@
 #include <linux/types.h>
 #include <linux/spinlock.h>
 #include <linux/random.h>
-#include <asm/tlbflush.h>
+#include <linux/percpu.h>
 #include "lguest.h"
 
 /* Everyone shares pagetable pool; it just gets slower with more guests. */
@@ -19,7 +19,9 @@ struct pgtable_page_info {
 };
 
 static struct pgtable_page_info pg_info[MAX_PAGETABLE_PAGES];
-static u32 *hypervisor_pte_page;
+static DEFINE_PER_CPU(u32 *, hypervisor_pte_pages) = { NULL };
+
+#define hypervisor_pte_page(cpu) per_cpu(hypervisor_pte_pages, cpu)
 
 /* These access the real versions. */
 static u32 *toplev(struct lguest_guest_info *linfo, unsigned long vaddr)
@@ -74,10 +76,7 @@ static void release_pagetable_page(struc
 
 static void flush_other_guest(unsigned int guestid)
 {
-#ifdef CONFIG_SMP
-	/* On UP, this is unnecessary, and paravirt_ops isn't exported. */
-	flush_tlb_all();
-#endif
+	lguest_flush_tlb();
 }
 
 static u32 *get_pagetable_page(struct lguest_guest_info *linfo,
@@ -311,10 +310,6 @@ int init_guest_pagetable(struct lguest_g
 	linfo->pgdir = (u32 *)get_zeroed_page(GFP_KERNEL);
 	if (!linfo->pgdir)
 		return -ENOMEM;
-
-	/* Since hypervisor less that 4MB, we simply mug top pte page. */
-	linfo->pgdir[PAGE_SIZE/sizeof(u32)-1] 
-		= (__pa(hypervisor_pte_page) | _PAGE_KERNEL);
 	return 0;
 }
 
@@ -353,21 +348,62 @@ void guest_set_pud(struct lguest_guest_i
 	pin_stack_pages(linfo);
 }
 
+/* Caller must be preempt-safe */
 void map_trap_page(struct lguest_guest_info *linfo)
 {
-	/* FIXME: For SMP, we need one pte page per CPU: altering it
-	 * for current guest only works on UP. */
-	hypervisor_pte_page[0] = (__pa(linfo->trap_page)|_PAGE_PRESENT);
+	int cpu = smp_processor_id();
+	
+	hypervisor_pte_page(cpu)[0] = (__pa(linfo->trap_page)|_PAGE_PRESENT);
+
+	/* Since hypervisor less that 4MB, we simply mug top pte page. */
+	linfo->pgdir[PAGE_SIZE/sizeof(u32)-1] =
+				(__pa(hypervisor_pte_page(cpu))| _PAGE_KERNEL);
+}
+
+static __exit void free_hypervisor_pte_pages(void)
+{
+	int i;
+	
+	for_each_possible_cpu(i)
+		free_page((long)hypervisor_pte_page(i));
+}
+
+static __init int alloc_hypervisor_pte_pages(void)
+{
+	int i;
+	
+	for_each_possible_cpu(i) {
+		u32 *p = (u32 *)get_zeroed_page(GFP_KERNEL);
+
+		hypervisor_pte_page(i) = p;
+		if (!hypervisor_pte_page(i)) {
+			free_hypervisor_pte_pages();
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+static __init void populate_hypervisor_pte_page(int cpu)
+{
+	int i;
+	u32 *pte;
+
+	pte = hypervisor_pte_page(cpu) + (lguest_address() / PAGE_SIZE) % 1024;
+	for (i = 0; i < HYPERVISOR_PAGES; i++) {
+		pte[i] = ((page_to_pfn(&hype_pages[i]) << PAGE_SHIFT) 
+			  | _PAGE_KERNEL_EXEC);
+	}
 }
 
 __init int init_pagetables(struct page hype_pages[])
 {
-	unsigned int i;
-	u32 *pte;
-
-	hypervisor_pte_page = (u32 *)get_zeroed_page(GFP_KERNEL);
-	if (!hypervisor_pte_page)
-		return -ENOMEM;
+	int ret;
+	unsigned int i;
+
+	ret = alloc_hypervisor_pte_pages();
+	if (ret)
+		return ret;
 
 	for (i = 0; i < ARRAY_SIZE(pg_info); i++) {
 		pg_info[i].guestid = NO_GUEST;
@@ -376,17 +412,15 @@ __init int init_pagetables(struct page h
 			unsigned int j;
 			for (j = 0; j < i; j++)
 				free_page((long)pg_info[j].page);
-			free_page((long)hypervisor_pte_page);
+			free_hypervisor_pte_pages();
 			return -ENOMEM;
 		}
 	}
 
-	/* Now populate hypervisor pagetable page. */
-	pte = hypervisor_pte_page + (lguest_address() / PAGE_SIZE) % 1024;
-	for (i = 0; i < HYPERVISOR_PAGES; i++) {
-		pte[i] = ((page_to_pfn(&hype_pages[i]) << PAGE_SHIFT) 
-			  | _PAGE_KERNEL_EXEC);
-	}
+	/* Now populate per-cpu hypervisor pagetable page. */
+	for_each_possible_cpu(i)
+		populate_hypervisor_pte_page(i);
+
 	return 0;
 }
 
@@ -395,5 +429,5 @@ __exit void free_pagetables(void)
 	unsigned int i;
 	for (i = 0; i < ARRAY_SIZE(pg_info); i++)
 		free_page((long)pg_info[i].page);
-	free_page((long)hypervisor_pte_page);
-}
+	free_hypervisor_pte_pages();
+}
