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