+/*
+ * Used by APs to determine their APIC IDs; assumes master CPU has initialized
+ * the local APIC interfaces.
+ */
+uint32_t
+lapic_safe_apicid(void)
+{
+ uint32_t lo;
+ uint32_t hi;
+ boolean_t is_lapic_enabled, is_local_x2apic;
+
+ rdmsr(MSR_IA32_APIC_BASE, lo, hi);
+ is_lapic_enabled = (lo & MSR_IA32_APIC_BASE_ENABLE) != 0;
+ is_local_x2apic = (lo & MSR_IA32_APIC_BASE_EXTENDED) != 0;
+
+ if (is_lapic_enabled && is_local_x2apic) {
+ return x2apic_read(ID);
+ } else if (is_lapic_enabled) {
+ return (*LAPIC_MMIO(ID) >> LAPIC_ID_SHIFT) & LAPIC_ID_MASK;
+ } else {
+ panic("Unknown Local APIC state!");
+ /*NORETURN*/
+ }
+}
+
+static void
+lapic_reinit(bool for_wake)
+{
+ uint32_t lo;
+ uint32_t hi;
+ boolean_t is_boot_processor;
+ boolean_t is_lapic_enabled;
+ boolean_t is_local_x2apic;
+
+ rdmsr(MSR_IA32_APIC_BASE, lo, hi);
+ is_boot_processor = (lo & MSR_IA32_APIC_BASE_BSP) != 0;
+ is_lapic_enabled = (lo & MSR_IA32_APIC_BASE_ENABLE) != 0;
+ is_local_x2apic = (lo & MSR_IA32_APIC_BASE_EXTENDED) != 0;
+
+ /*
+ * If we're configured for x2apic mode and we're being asked to transition
+ * to legacy APIC mode, OR if we're in legacy APIC mode and we're being
+ * asked to transition to x2apic mode, call LAPIC_INIT().
+ */
+ if ((!is_local_x2apic && is_x2apic) || (is_local_x2apic && !is_x2apic)) {
+ LAPIC_INIT();
+ /* Now re-read after LAPIC_INIT() */
+ rdmsr(MSR_IA32_APIC_BASE, lo, hi);
+ is_lapic_enabled = (lo & MSR_IA32_APIC_BASE_ENABLE) != 0;
+ is_local_x2apic = (lo & MSR_IA32_APIC_BASE_EXTENDED) != 0;
+ }
+
+ if ((!is_lapic_enabled && !is_local_x2apic)) {
+ panic("Unexpected local APIC state\n");
+ }
+
+ /*
+ * If we did not select the same APIC mode as we had before sleep, flag
+ * that as an error (and panic on debug/development kernels). Note that
+ * we might get here with for_wake == true for the first boot case. In
+ * that case, apic_mode_before_sleep will be UNKNOWN (since we haven't
+ * slept yet), so we do not need to do any APIC checks.
+ */
+ if (for_wake &&
+ ((apic_mode_before_sleep == APIC_MODE_XAPIC && !is_lapic_enabled) ||
+ (apic_mode_before_sleep == APIC_MODE_X2APIC && !is_local_x2apic))) {
+ kprintf("Inconsistent APIC state after wake (was %d before sleep, "
+ "now is %d)", apic_mode_before_sleep,
+ is_lapic_enabled ? APIC_MODE_XAPIC : APIC_MODE_X2APIC);
+#if DEBUG || DEVELOPMENT
+ kprintf("HALTING.\n");
+ /*
+ * Unfortunately, we cannot safely panic here because the
+ * executing CPU might not be fully initialized. The best
+ * we can do is just print a message to the console and
+ * halt.
+ */
+ asm volatile ("cli; hlt;" ::: "memory");
+#endif
+ }
+}
+
+void
+lapic_init_slave(void)
+{
+ lapic_reinit(false);
+#if DEBUG || DEVELOPMENT
+ if (rdmsr64(MSR_IA32_APIC_BASE) & MSR_IA32_APIC_BASE_BSP) {
+ panic("Calling lapic_init_slave() on the boot processor\n");
+ }
+#endif
+}