diff --git a/sys/arch/x86/conf/files.x86 b/sys/arch/x86/conf/files.x86
index 17ccac2..1b14f08 100644
--- a/sys/arch/x86/conf/files.x86
+++ b/sys/arch/x86/conf/files.x86
@@ -25,7 +25,7 @@ define  ipmibus {}
 # CPU features
 #
 device	cpu: cpufeaturebus
-attach	cpu at cpubus
+attach	cpu at cpubus with lapic
 file 	arch/x86/x86/cpu.c 		cpu
 file	arch/x86/x86/cpu_rng.c		cpu
 
diff --git a/sys/arch/x86/include/cpu.h b/sys/arch/x86/include/cpu.h
index bd6e7cf..778c547 100644
--- a/sys/arch/x86/include/cpu.h
+++ b/sys/arch/x86/include/cpu.h
@@ -335,6 +335,10 @@ void cpu_load_pmap(struct pmap *, struct pmap *);
 void cpu_broadcast_halt(void);
 void cpu_kick(struct cpu_info *);
 
+void cpu_attach_common(device_t, device_t, void *);
+int cpu_rescan(device_t, const char *, const int *);
+void cpu_childdetached(device_t, device_t);
+	
 #define	curcpu()		x86_curcpu()
 #define	curlwp			x86_curlwp()
 #define	curpcb			((struct pcb *)lwp_getpcb(curlwp))
diff --git a/sys/arch/x86/include/cpuvar.h b/sys/arch/x86/include/cpuvar.h
index c0ea0c5..8dfdd64 100644
--- a/sys/arch/x86/include/cpuvar.h
+++ b/sys/arch/x86/include/cpuvar.h
@@ -95,6 +95,12 @@ struct cpufeature_attach_args {
 	const char *name;
 };
 
+struct cpu_softc {
+	device_t sc_dev;		/* device tree glue */
+	struct cpu_info *sc_info;	/* pointer to CPU info */
+	bool sc_wasonline;
+};
+
 #ifdef _KERNEL
 #include <sys/kcpuset.h>
 #if defined(_KERNEL_OPT)
diff --git a/sys/arch/x86/include/i82489var.h b/sys/arch/x86/include/i82489var.h
index fd2be1e..f34693a 100644
--- a/sys/arch/x86/include/i82489var.h
+++ b/sys/arch/x86/include/i82489var.h
@@ -88,7 +88,6 @@ struct cpu_info;
 extern void lapic_boot_init(paddr_t);
 extern void lapic_set_lvt(void);
 extern void lapic_enable(void);
-extern void lapic_calibrate_timer(struct cpu_info *ci);
 extern void lapic_initclocks(void);
 
 extern uint32_t lapic_readreg(u_int);
diff --git a/sys/arch/x86/x86/cpu.c b/sys/arch/x86/x86/cpu.c
index 4f31ee4..66ce4aa 100644
--- a/sys/arch/x86/x86/cpu.c
+++ b/sys/arch/x86/x86/cpu.c
@@ -124,19 +124,11 @@ __KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.132 2017/07/28 14:12:26 riastradh Exp $");
 static int	cpu_match(device_t, cfdata_t, void *);
 static void	cpu_attach(device_t, device_t, void *);
 static void	cpu_defer(device_t);
-static int	cpu_rescan(device_t, const char *, const int *);
-static void	cpu_childdetached(device_t, device_t);
 static bool	cpu_stop(device_t);
 static bool	cpu_suspend(device_t, const pmf_qual_t *);
 static bool	cpu_resume(device_t, const pmf_qual_t *);
 static bool	cpu_shutdown(device_t, int);
 
-struct cpu_softc {
-	device_t sc_dev;		/* device tree glue */
-	struct cpu_info *sc_info;	/* pointer to CPU info */
-	bool sc_wasonline;
-};
-
 #ifdef MULTIPROCESSOR
 int mp_cpu_start(struct cpu_info *, paddr_t);
 void mp_cpu_start_cleanup(struct cpu_info *);
@@ -296,15 +288,20 @@ cpu_attach(device_t parent, device_t self, void *aux)
 {
 	struct cpu_softc *sc = device_private(self);
 	struct cpu_attach_args *caa = aux;
+	sc->sc_dev = self;
+
+	cpu_attach_common(parent, self, caa);
+}
+void
+cpu_attach_common(device_t parent, device_t self, void *aux)
+{
+	struct cpu_softc *sc = device_private(self);
+	struct cpu_attach_args *caa = aux;
 	struct cpu_info *ci;
 	uintptr_t ptr;
-#if NLAPIC > 0
-	int cpunum = caa->cpu_number;
-#endif
+	
 	static bool again;
-
-	sc->sc_dev = self;
-
+	
 	if (ncpu == maxcpus) {
 #ifndef _LP64
 		aprint_error(": too many CPUs, please use NetBSD/amd64\n");
@@ -338,22 +335,6 @@ cpu_attach(device_t parent, device_t self, void *aux)
 		aprint_naive(": %s Processor\n",
 		    caa->cpu_role == CPU_ROLE_SP ? "Single" : "Boot");
 		ci = &cpu_info_primary;
-#if NLAPIC > 0
-		if (cpunum != lapic_cpu_number()) {
-			/* XXX should be done earlier. */
-			uint32_t reg;
-			aprint_verbose("\n");
-			aprint_verbose_dev(self, "running CPU at apic %d"
-			    " instead of at expected %d", lapic_cpu_number(),
-			    cpunum);
-			reg = lapic_readreg(LAPIC_ID);
-			lapic_writereg(LAPIC_ID, (reg & ~LAPIC_ID_MASK) |
-			    (cpunum << LAPIC_ID_SHIFT));
-		}
-		if (cpunum != lapic_cpu_number()) {
-			aprint_error_dev(self, "unable to reset apic id\n");
-		}
-#endif
 	}
 
 	ci->ci_self = ci;
@@ -397,14 +378,6 @@ cpu_attach(device_t parent, device_t self, void *aux)
 		cpu_init(ci);
 		cpu_set_tss_gates(ci);
 		pmap_cpu_init_late(ci);
-#if NLAPIC > 0
-		if (caa->cpu_role != CPU_ROLE_SP) {
-			/* Enable lapic. */
-			lapic_enable();
-			lapic_set_lvt();
-			lapic_calibrate_timer(ci);
-		}
-#endif
 		/* Make sure DELAY() is initialized. */
 		DELAY(1);
 		again = true;
@@ -490,7 +463,7 @@ cpu_defer(device_t self)
 	cpu_rescan(self, NULL, NULL);
 }
 
-static int
+int
 cpu_rescan(device_t self, const char *ifattr, const int *locators)
 {
 	struct cpu_softc *sc = device_private(self);
@@ -530,7 +503,7 @@ cpu_rescan(device_t self, const char *ifattr, const int *locators)
 	return 0;
 }
 
-static void
+void
 cpu_childdetached(device_t self, device_t child)
 {
 	struct cpu_softc *sc = device_private(self);
diff --git a/sys/arch/x86/x86/lapic.c b/sys/arch/x86/x86/lapic.c
index 9c341bc..79d5416 100644
--- a/sys/arch/x86/x86/lapic.c
+++ b/sys/arch/x86/x86/lapic.c
@@ -92,6 +92,7 @@ extern int ddb_vec;
 /* Referenced from vector.S */
 void		lapic_clockintr(void *, struct intrframe *);
 
+static void lapic_calibrate_timer(device_t);
 static void	lapic_delay(unsigned int);
 static uint32_t lapic_gettick(void);
 static void 	lapic_setup_bsp(paddr_t);
@@ -601,8 +602,8 @@ extern void (*initclock_func)(void); /* XXX put in header file */
  *
  * We're actually using the IRQ0 timer.  Hmm.
  */
-void
-lapic_calibrate_timer(struct cpu_info *ci)
+static void
+lapic_calibrate_timer(device_t dev)
 {
 	unsigned int seen, delta, initial_i8254, initial_lapic;
 	unsigned int cur_i8254, cur_lapic;
@@ -610,7 +611,7 @@ lapic_calibrate_timer(struct cpu_info *ci)
 	int i;
 	char tbuf[9];
 
-	aprint_debug_dev(ci->ci_dev, "calibrating local timer\n");
+	aprint_debug_dev(dev, "calibrating local timer\n");
 
 	/*
 	 * Configure timer to one-shot, interrupt masked,
@@ -642,7 +643,7 @@ lapic_calibrate_timer(struct cpu_info *ci)
 
 	humanize_number(tbuf, sizeof(tbuf), lapic_per_second, "Hz", 1000);
 
-	aprint_debug_dev(ci->ci_dev, "apic clock running at %s\n", tbuf);
+	aprint_debug_dev(dev, "apic clock running at %s\n", tbuf);
 
 	if (lapic_per_second != 0) {
 		/*
@@ -935,3 +936,64 @@ lapic_dump(void)
 	apic_format_redir(device_xname(ci->ci_dev), "err", 0, 0,
 	    lapic_readreg(LAPIC_LVERR));
 }
+
+/*
+ * cpu attach helpers
+ * These are called via x86/mpacpi.c or x86/mpbios.c
+ */
+
+static int	lapic_match(device_t, cfdata_t, void *);
+static void	lapic_attach(device_t, device_t, void *);
+
+CFATTACH_DECL2_NEW(lapic, sizeof(struct cpu_softc),
+    lapic_match, lapic_attach, NULL, NULL, cpu_rescan, cpu_childdetached);
+
+static int
+lapic_match(device_t parent, cfdata_t match, void *aux)
+{
+	/* XXX: "detect" lapic ? */
+	return 1;
+}
+
+static void
+lapic_attach(device_t parent, device_t self, void *aux)
+{
+	struct cpu_softc *sc = device_private(self);
+	struct cpu_attach_args *caa = aux;
+
+	int cpunum = caa->cpu_number;
+
+	static bool again;
+
+	sc->sc_dev = self;
+
+	cpu_attach_common(parent, self, caa);
+	
+	if (caa->cpu_role != CPU_ROLE_AP) {
+		if (cpunum != lapic_cpu_number()) {
+			/* XXX should be done earlier. */
+			uint32_t reg;
+			aprint_verbose("\n");
+			aprint_verbose_dev(self, "running CPU at apic %d"
+			    " instead of at expected %d", lapic_cpu_number(),
+			    cpunum);
+			reg = lapic_readreg(LAPIC_ID);
+			lapic_writereg(LAPIC_ID, (reg & ~LAPIC_ID_MASK) |
+			    (cpunum << LAPIC_ID_SHIFT));
+		}
+		if (cpunum != lapic_cpu_number()) {
+			aprint_error_dev(self, "unable to reset apic id\n");
+		}
+	}
+
+	if (!again) {
+		if (caa->cpu_role != CPU_ROLE_SP) {
+			/* Enable lapic. */
+			lapic_enable();
+			lapic_set_lvt();
+			lapic_calibrate_timer(self);
+		}
+
+		again = true;
+	}
+}
