1 /*
2  * mp_tables.c: Dynamically writes MP table info into the ROMBIOS.
3  *
4  * In order to work with various VCPU counts, this code reads the VCPU count
5  * for the HVM partition and creates the correct MP tables for the VCPU count
6  * and places the information into a predetermined location set aside in the
7  * ROMBIOS during build time.
8  *
9  * Please note that many of the values, such as the CPU's
10  * family/model/stepping, are hard-coded based upon the values that were used
11  * in the ROMBIOS and may need to be modified or calculated dynamically to
12  * correspond with what an HVM guest's CPUID returns.
13  *
14  * Travis Betak, travis.betak@amd.com
15  * Copyright (c) 2006, AMD.
16  *
17  * This program is free software; you can redistribute it and/or modify it
18  * under the terms and conditions of the GNU General Public License,
19  * version 2, as published by the Free Software Foundation.
20  *
21  * This program is distributed in the hope it will be useful, but WITHOUT
22  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
24  * more details.
25  *
26  * You should have received a copy of the GNU General Public License along with
27  * this program; If not, see <http://www.gnu.org/licenses/>.
28  */
29 
30 #include <stdint.h>
31 #include "config.h"
32 
33 /* number of non-processor MP table entries */
34 #define NR_NONPROC_ENTRIES     18
35 
36 #define ENTRY_TYPE_PROCESSOR   0
37 #define ENTRY_TYPE_BUS         1
38 #define ENTRY_TYPE_IOAPIC      2
39 #define ENTRY_TYPE_IO_INTR     3
40 #define ENTRY_TYPE_LOCAL_INTR  4
41 
42 #define CPU_FLAG_ENABLED       0x01
43 #define CPU_FLAG_BSP           0x02
44 
45 /* TODO change this to correspond with what the guest's see's from CPUID */
46 #define CPU_SIG_FAMILY         0x06
47 #define CPU_SIG_MODEL          0x00
48 #define CPU_SIG_STEPPING       0x00
49 #define CPU_SIGNATURE        ((CPU_SIG_FAMILY << 8)  \
50                              | (CPU_SIG_MODEL << 4)  \
51                              | (CPU_SIG_STEPPING))
52 #define CPU_FEATURE_FPU       (1U << 0)
53 #define CPU_FEATURE_MCE       (1U << 7)
54 #define CPU_FEATURE_CX8       (1U << 8)
55 #define CPU_FEATURE_APIC      (1U << 9)
56 #define CPU_FEATURES          (CPU_FEATURE_FPU | CPU_FEATURE_APIC)
57 
58 #define BUS_TYPE_LENGTH        6
59 #define BUS_TYPE_STR_ISA       "ISA   "
60 #define BUS_ID_ISA             0
61 
62 #define INTR_TYPE_INT          0
63 #define INTR_TYPE_NMI          1
64 #define INTR_TYPE_SMI          2
65 #define INTR_TYPE_EXTINT       3
66 
67 #define INTR_MAX_NR            16
68 
69 #include "util.h"
70 
71 /*
72  * The following structures are defined in the MuliProcessor Specifiation v1.4
73  */
74 
75 /* MP Floating Pointer Structure */
76 struct mp_floating_pointer_struct {
77     uint8_t signature[4];
78     uint32_t mp_table;
79     uint8_t length;
80     uint8_t revision;
81     uint8_t checksum;
82     uint8_t feature[5];
83 };
84 
85 /* MP Configuration Table */
86 struct mp_config_table {
87     uint8_t signature[4];
88     uint16_t length;
89     uint8_t revision;
90     uint8_t checksum;
91     uint8_t oem_id[8];
92     uint8_t vendor_id[12];
93     uint32_t oem_table;
94     uint16_t oem_table_sz;
95     uint16_t nr_entries;
96     uint32_t lapic;
97     uint16_t extended_length;
98     uint8_t extended_checksum;
99     uint8_t reserved;
100 };
101 
102 /* MP Processor Entry */
103 struct mp_proc_entry {
104     uint8_t type;
105     uint8_t lapic_id;
106     uint8_t lapic_version;
107     uint8_t cpu_flags;
108     uint32_t cpu_signature;
109     uint32_t feature_flags;
110     uint8_t reserved[8];
111 };
112 
113 /* MP Bus Entry */
114 struct mp_bus_entry {
115     uint8_t type;
116     uint8_t bus_id;
117     uint8_t bus_type_str[6];
118 };
119 
120 /* MP IOAPIC Entry */
121 struct mp_ioapic_entry {
122     uint8_t type;
123     uint8_t ioapic_id;
124     uint8_t ioapic_version;
125     uint8_t ioapic_flags;
126     uint32_t ioapic_addr;
127 };
128 
129 /* MP IO Interrupt Entry */
130 struct mp_io_intr_entry {
131     uint8_t type;
132     uint8_t intr_type;
133     uint16_t io_intr_flags;
134     uint8_t src_bus_id;
135     uint8_t src_bus_irq;
136     uint8_t dst_ioapic_id;
137     uint8_t dst_ioapic_intin;
138 };
139 
140 /* MP Local Interrupt Entry */
141 struct mp_local_intr_entry {
142     uint8_t type;
143     uint8_t intr_type;
144     uint16_t local_intr_flags;
145     uint8_t src_bus_id;
146     uint8_t src_bus_irq;
147     uint8_t dst_lapic_id;
148     uint8_t dst_lapic_lintin;
149 };
150 
151 
fill_mp_config_table(struct mp_config_table * mpct,int length)152 static void fill_mp_config_table(struct mp_config_table *mpct, int length)
153 {
154     int vcpu_nr, i;
155     uint8_t checksum;
156 
157     vcpu_nr = hvm_info->nr_vcpus;
158 
159     /* fill in the MP configuration table signature, "PCMP" */
160     mpct->signature[0] = 'P';
161     mpct->signature[1] = 'C';
162     mpct->signature[2] = 'M';
163     mpct->signature[3] = 'P';
164 
165     mpct->length = length;
166 
167     mpct->revision = 4;
168 
169     /* fill in the OEM ID string, "_HVMCPU_" */
170     mpct->oem_id[0] = '_'; mpct->oem_id[3] = 'M'; mpct->oem_id[6] = 'U';
171     mpct->oem_id[1] = 'H'; mpct->oem_id[4] = 'C'; mpct->oem_id[7] = '_';
172     mpct->oem_id[2] = 'V'; mpct->oem_id[5] = 'P';
173 
174     /* fill in the Vendor ID string, "XEN         " */
175     mpct->vendor_id[0] = 'X'; mpct->vendor_id[6] =  ' ';
176     mpct->vendor_id[1] = 'E'; mpct->vendor_id[7] =  ' ';
177     mpct->vendor_id[2] = 'N'; mpct->vendor_id[8] =  ' ';
178     mpct->vendor_id[3] = ' '; mpct->vendor_id[9] =  ' ';
179     mpct->vendor_id[4] = ' '; mpct->vendor_id[10] = ' ';
180     mpct->vendor_id[5] = ' '; mpct->vendor_id[11] = ' ';
181 
182     mpct->oem_table = 0;
183     mpct->oem_table_sz = 0;
184 
185     mpct->nr_entries = vcpu_nr + NR_NONPROC_ENTRIES;
186 
187     mpct->lapic = LAPIC_BASE_ADDRESS;
188     mpct->extended_length = 0;
189     mpct->extended_checksum = 0;
190 
191     /* Finally, fill in the checksum. */
192     mpct->checksum = checksum = 0;
193     for ( i = 0; i < length; i++ )
194         checksum += ((uint8_t *)(mpct))[i];
195     mpct->checksum = -checksum;
196 }
197 
198 /* fills in an MP processor entry for VCPU 'vcpu_id' */
fill_mp_proc_entry(struct mp_proc_entry * mppe,int vcpu_id)199 static void fill_mp_proc_entry(struct mp_proc_entry *mppe, int vcpu_id)
200 {
201     mppe->type = ENTRY_TYPE_PROCESSOR;
202     mppe->lapic_id = LAPIC_ID(vcpu_id);
203     mppe->lapic_version = 0x11;
204     mppe->cpu_flags = CPU_FLAG_ENABLED;
205     if ( vcpu_id == 0 )
206         mppe->cpu_flags |= CPU_FLAG_BSP;
207     mppe->cpu_signature = CPU_SIGNATURE;
208     mppe->feature_flags = CPU_FEATURES;
209 }
210 
211 
212 /* fills in an MP bus entry of type 'type' and bus ID 'bus_id' */
fill_mp_bus_entry(struct mp_bus_entry * mpbe,int bus_id,const char * type)213 static void fill_mp_bus_entry(
214     struct mp_bus_entry *mpbe, int bus_id, const char *type)
215 {
216     int i;
217 
218     mpbe->type = ENTRY_TYPE_BUS;
219     mpbe->bus_id = bus_id;
220     for ( i = 0; i < BUS_TYPE_LENGTH; i++ )
221         mpbe->bus_type_str[i] = type[i]; /* FIXME length check? */
222 }
223 
224 
225 /* fills in an MP IOAPIC entry for IOAPIC 'ioapic_id' */
fill_mp_ioapic_entry(struct mp_ioapic_entry * mpie)226 static void fill_mp_ioapic_entry(struct mp_ioapic_entry *mpie)
227 {
228     mpie->type = ENTRY_TYPE_IOAPIC;
229     mpie->ioapic_id = IOAPIC_ID;
230     mpie->ioapic_version = ioapic_version;
231     mpie->ioapic_flags = 1; /* enabled */
232     mpie->ioapic_addr = ioapic_base_address;
233 }
234 
235 
236 /* fill in the mp floating processor structure */
fill_mpfps(struct mp_floating_pointer_struct * mpfps,uint32_t mpct)237 static void fill_mpfps(struct mp_floating_pointer_struct *mpfps, uint32_t mpct)
238 {
239     int i;
240     uint8_t checksum;
241 
242 
243     mpfps->signature[0] = '_';
244     mpfps->signature[1] = 'M';
245     mpfps->signature[2] = 'P';
246     mpfps->signature[3] = '_';
247 
248     mpfps->mp_table = mpct;
249     mpfps->length = 1;
250     mpfps->revision = 4;
251     mpfps->checksum = 0;
252     for (i = 0; i < 5; ++i)
253         mpfps->feature[i] = 0;
254 
255     /* compute the checksum for our new table */
256     checksum = 0;
257     for ( i = 0; i < sizeof(struct mp_floating_pointer_struct); i++ )
258         checksum += ((uint8_t *)(mpfps))[i];
259     mpfps->checksum = -checksum;
260 }
261 
262 /* create_mp_tables - creates MP tables for the guest based upon config data */
create_mp_tables(void * _mpfps)263 unsigned long create_mp_tables(void *_mpfps)
264 {
265     char *p;
266     int vcpu_nr, i, length;
267     void *base;
268     struct mp_io_intr_entry *mpiie;
269     struct mp_floating_pointer_struct *mpfps;
270 
271     vcpu_nr = hvm_info->nr_vcpus;
272 
273     printf("Creating MP tables ...\n");
274 
275     if ( _mpfps == NULL )
276     {
277         int sz;
278 
279         sz  = sizeof(struct mp_floating_pointer_struct);
280         sz += sizeof(struct mp_config_table);
281         sz += sizeof(struct mp_proc_entry) * vcpu_nr;
282         sz += sizeof(struct mp_bus_entry);
283         sz += sizeof(struct mp_ioapic_entry);
284         sz += sizeof(struct mp_io_intr_entry) * 16;
285 
286         _mpfps = mem_alloc(sz, 0);
287     }
288 
289     mpfps = _mpfps;
290 
291     base = &mpfps[1];
292 
293     p = base + sizeof(struct mp_config_table);
294 
295     for ( i = 0; i < vcpu_nr; i++ )
296     {
297         fill_mp_proc_entry((struct mp_proc_entry *)p, i);
298         p += sizeof(struct mp_proc_entry);
299     }
300 
301     fill_mp_bus_entry((struct mp_bus_entry *)p, BUS_ID_ISA, BUS_TYPE_STR_ISA);
302     p += sizeof(struct mp_bus_entry);
303 
304     fill_mp_ioapic_entry((struct mp_ioapic_entry *)p);
305     p += sizeof(struct mp_ioapic_entry);
306 
307     /* I/O interrupt assignment: IOAPIC pin 0 is connected to 8259 ExtInt. */
308     mpiie = (struct mp_io_intr_entry *)p;
309     memset(mpiie, 0, sizeof(*mpiie));
310     mpiie->type = ENTRY_TYPE_IO_INTR;
311     mpiie->intr_type = INTR_TYPE_EXTINT;
312     mpiie->dst_ioapic_id = IOAPIC_ID;
313     p += sizeof(*mpiie);
314 
315     /* I/O interrupt assignment for every legacy 8259 interrupt source. */
316     for ( i = 0; i < 16; i++ )
317     {
318         if ( i == 2 )
319             continue; /* skip the slave PIC connection */
320         mpiie = (struct mp_io_intr_entry *)p;
321         mpiie->type = ENTRY_TYPE_IO_INTR;
322         mpiie->intr_type = INTR_TYPE_INT;
323         mpiie->io_intr_flags = (PCI_ISA_IRQ_MASK & (1U << i)) ? 0xf : 0x0;
324         mpiie->src_bus_id = BUS_ID_ISA;
325         mpiie->src_bus_irq = i;
326         mpiie->dst_ioapic_id = IOAPIC_ID;
327         mpiie->dst_ioapic_intin = (i == 0) ? 2 : i;
328         p += sizeof(*mpiie);
329     }
330 
331     length = p - (char *)base;
332 
333     fill_mp_config_table((struct mp_config_table *)base, length);
334 
335     fill_mpfps(mpfps, (uint32_t)base);
336 
337     return (unsigned long)mpfps;
338 }
339 
340 /*
341  * Local variables:
342  * mode: C
343  * c-file-style: "BSD"
344  * c-basic-offset: 4
345  * tab-width: 4
346  * indent-tabs-mode: nil
347  * End:
348  */
349