--- /dev/null
+/*
+ * smp.c: Secondary processor bringup and initialisation.
+ *
+ * Copyright (c) 2008, Citrix Systems, Inc.
+ *
+ * Authors:
+ * Keir Fraser <keir.fraser@citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "util.h"
+#include "config.h"
+#include "apic_regs.h"
+
+#define AP_BOOT_EIP 0x1000
+extern char ap_boot_start[], ap_boot_end[];
+
+static int ap_callin, ap_cpuid;
+
+asm (
+ " .text \n"
+ " .code16 \n"
+ "ap_boot_start: .code16 \n"
+ " mov %cs,%ax \n"
+ " mov %ax,%ds \n"
+ " lgdt gdt_desr-ap_boot_start\n"
+ " xor %ax, %ax \n"
+ " inc %ax \n"
+ " lmsw %ax \n"
+ " ljmpl $0x08,$1f \n"
+ "gdt_desr: \n"
+ " .word gdt_end - gdt - 1 \n"
+ " .long gdt \n"
+ "ap_boot_end: .code32 \n"
+ "1: mov $0x10,%eax \n"
+ " mov %eax,%ds \n"
+ " mov %eax,%es \n"
+ " mov %eax,%ss \n"
+ " movl $stack_top,%esp \n"
+ " movl %esp,%ebp \n"
+ " call ap_start \n"
+ "1: hlt \n"
+ " jmp 1b \n"
+ " \n"
+ " .align 8 \n"
+ "gdt: \n"
+ " .quad 0x0000000000000000 \n"
+ " .quad 0x00cf9a000000ffff \n" /* 0x08: Flat code segment */
+ " .quad 0x00cf92000000ffff \n" /* 0x10: Flat data segment */
+ "gdt_end: \n"
+ " \n"
+ " .bss \n"
+ " .align 8 \n"
+ "stack: \n"
+ " .skip 0x4000 \n"
+ "stack_top: \n"
+ );
+
+/*static*/ void ap_start(void)
+{
+ printf(" - CPU%d ... ", ap_cpuid);
+
+ printf("done.\n");
+ wmb();
+ ap_callin = 1;
+}
+
+static void lapic_wait_ready(void)
+{
+ while ( lapic_read(APIC_ICR) & APIC_ICR_BUSY )
+ cpu_relax();
+}
+
+static void boot_cpu(unsigned int cpu)
+{
+ unsigned int icr2 = SET_APIC_DEST_FIELD(LAPIC_ID(cpu));
+
+ /* Initialise shared variables. */
+ ap_cpuid = cpu;
+ ap_callin = 0;
+ wmb();
+
+ /* Wake up the secondary processor: INIT-SIPI-SIPI... */
+ lapic_wait_ready();
+ lapic_write(APIC_ICR2, icr2);
+ lapic_write(APIC_ICR, APIC_DM_INIT);
+ lapic_wait_ready();
+ lapic_write(APIC_ICR2, icr2);
+ lapic_write(APIC_ICR, APIC_DM_STARTUP | (AP_BOOT_EIP >> 12));
+ lapic_wait_ready();
+ lapic_write(APIC_ICR2, icr2);
+ lapic_write(APIC_ICR, APIC_DM_STARTUP | (AP_BOOT_EIP >> 12));
+ lapic_wait_ready();
+
+ /*
+ * Wait for the secondary processor to complete initialisation.
+ * Do not touch shared resources meanwhile.
+ */
+ while ( !ap_callin )
+ cpu_relax();
+
+ /* Take the secondary processor offline. */
+ lapic_write(APIC_ICR2, icr2);
+ lapic_write(APIC_ICR, APIC_DM_INIT);
+ lapic_wait_ready();
+}
+
+void smp_initialise(void)
+{
+ unsigned int i, nr_cpus = get_vcpu_nr();
+
+ if ( nr_cpus <= 1 )
+ return;
+
+ memcpy((void *)AP_BOOT_EIP, ap_boot_start, ap_boot_end - ap_boot_start);
+
+ printf("Multiprocessor initialisation:\n");
+ for ( i = 1; i < nr_cpus; i++ )
+ boot_cpu(i);
+}