1 /*
2 * smp.c: Secondary processor bringup and initialisation.
3 *
4 * Copyright (c) 2008, Citrix Systems, Inc.
5 *
6 * Authors:
7 * Keir Fraser <keir@xen.org>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms and conditions of the GNU General Public License,
11 * version 2, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program; If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "util.h"
23 #include "config.h"
24 #include "apic_regs.h"
25 #include "hypercall.h"
26
27 #include <xen/asm/x86-defns.h>
28 #include <xen/hvm/hvm_vcpu.h>
29
30 #include <xen/vcpu.h>
31
32 static int ap_callin;
33
34 /** True if x2apic support is exposed to the guest. */
35 static bool has_x2apic;
36
37 /**
38 * Lookup table of APIC IDs.
39 *
40 * Each entry is populated for its respective CPU as they come online. This is
41 * required for generating the MADT with minimal assumptions about ID
42 * relationships.
43 */
44 uint32_t *cpu_to_apicid;
45
read_apic_id(void)46 static uint32_t read_apic_id(void)
47 {
48 uint32_t apic_id;
49
50 if ( has_x2apic )
51 cpuid(0xb, NULL, NULL, NULL, &apic_id);
52 else
53 {
54 cpuid(1, NULL, &apic_id, NULL, NULL);
55 apic_id >>= 24;
56 }
57
58 return apic_id;
59 }
60
cpu_setup(unsigned int cpu)61 static void cpu_setup(unsigned int cpu)
62 {
63 uint32_t apicid = cpu_to_apicid[cpu] = read_apic_id();
64
65 printf(" - CPU%u APIC ID %u ... ", cpu, apicid);
66 cacheattr_init();
67 printf("done.\n");
68
69 if ( !cpu ) /* Used on the BSP too */
70 return;
71
72 wmb();
73 ap_callin = 1;
74
75 /* After this point, the BSP will shut us down. */
76
77 for ( ;; )
78 asm volatile ( "hlt" );
79 }
80
boot_cpu(unsigned int cpu)81 static void boot_cpu(unsigned int cpu)
82 {
83 static uint8_t ap_stack[PAGE_SIZE] __attribute__ ((aligned (16)));
84 static struct vcpu_hvm_context ap;
85
86 /* Initialise shared variables. */
87 ap_callin = 0;
88 wmb();
89
90 /* Wake up the secondary processor */
91 ap = (struct vcpu_hvm_context) {
92 .mode = VCPU_HVM_MODE_32B,
93 .cpu_regs.x86_32 = {
94 .eip = (unsigned long)cpu_setup,
95 .esp = (unsigned long)ap_stack + ARRAY_SIZE(ap_stack),
96
97 .eax = cpu,
98
99 /* Protected Mode, no paging. */
100 .cr0 = X86_CR0_PE,
101
102 /* 32bit Flat Mode */
103 .cs_limit = -1U,
104 .ds_limit = -1U,
105 .ss_limit = -1U,
106 .es_limit = -1U,
107 .tr_limit = 0x67,
108 .cs_ar = 0xc9b,
109 .ds_ar = 0xc93,
110 .es_ar = 0xc93,
111 .ss_ar = 0xc93,
112 .tr_ar = 0x8b,
113 },
114 };
115
116 if ( hypercall_vcpu_op(VCPUOP_initialise, cpu, &ap) )
117 BUG();
118 if ( hypercall_vcpu_op(VCPUOP_up, cpu, NULL) )
119 BUG();
120
121 /*
122 * Wait for the secondary processor to complete initialisation.
123 * Do not touch shared resources meanwhile.
124 */
125 while ( !ap_callin )
126 cpu_relax();
127
128 /* Take the secondary processor offline. */
129 if ( hypercall_vcpu_op(VCPUOP_down, cpu, NULL) )
130 BUG();
131 }
132
smp_initialise(void)133 void smp_initialise(void)
134 {
135 unsigned int i, nr_cpus = hvm_info->nr_vcpus;
136 uint32_t ecx, max_leaf;
137
138 cpuid(0, &max_leaf, NULL, NULL, NULL);
139 if ( max_leaf >= 0xb )
140 {
141 cpuid(1, NULL, NULL, &ecx, NULL);
142 has_x2apic = (ecx >> 21) & 1;
143 if ( has_x2apic )
144 printf("x2APIC supported\n");
145 }
146
147 printf("Multiprocessor initialisation:\n");
148 cpu_to_apicid = scratch_alloc(sizeof(*cpu_to_apicid) * nr_cpus,
149 sizeof(*cpu_to_apicid));
150 cpu_setup(0);
151 for ( i = 1; i < nr_cpus; i++ )
152 boot_cpu(i);
153 }
154
155 /*
156 * Local variables:
157 * mode: C
158 * c-file-style: "BSD"
159 * c-basic-offset: 4
160 * tab-width: 4
161 * indent-tabs-mode: nil
162 * End:
163 */
164