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, write to the Free Software Foundation, Inc., 59 Temple |
---|
28 | * Place - Suite 330, Boston, MA 02111-1307 USA. |
---|
29 | */ |
---|
30 | |
---|
31 | #include "config.h" |
---|
32 | |
---|
33 | /* FIXME find a header that already has types defined!!! */ |
---|
34 | typedef unsigned char uint8_t; |
---|
35 | typedef signed char int8_t; |
---|
36 | typedef unsigned short uint16_t; |
---|
37 | typedef signed short int16_t; |
---|
38 | typedef unsigned int uint32_t; |
---|
39 | typedef signed int int32_t; |
---|
40 | #ifdef __i386__ |
---|
41 | typedef unsigned long long uint64_t; |
---|
42 | typedef signed long long int64_t; |
---|
43 | #else |
---|
44 | typedef unsigned long uint64_t; |
---|
45 | typedef signed long int64_t; |
---|
46 | #endif |
---|
47 | |
---|
48 | /* number of non-processor MP table entries */ |
---|
49 | #define NR_NONPROC_ENTRIES 18 |
---|
50 | |
---|
51 | #define ENTRY_TYPE_PROCESSOR 0 |
---|
52 | #define ENTRY_TYPE_BUS 1 |
---|
53 | #define ENTRY_TYPE_IOAPIC 2 |
---|
54 | #define ENTRY_TYPE_IO_INTR 3 |
---|
55 | #define ENTRY_TYPE_LOCAL_INTR 4 |
---|
56 | |
---|
57 | #define CPU_FLAG_ENABLED 0x01 |
---|
58 | #define CPU_FLAG_BSP 0x02 |
---|
59 | |
---|
60 | /* TODO change this to correspond with what the guest's see's from CPUID */ |
---|
61 | #define CPU_SIG_FAMILY 0x06 |
---|
62 | #define CPU_SIG_MODEL 0x00 |
---|
63 | #define CPU_SIG_STEPPING 0x00 |
---|
64 | #define CPU_SIGNATURE ((CPU_SIG_FAMILY << 8) \ |
---|
65 | | (CPU_SIG_MODEL << 4) \ |
---|
66 | | (CPU_SIG_STEPPING)) |
---|
67 | #define CPU_FEATURE_FPU (1U << 0) |
---|
68 | #define CPU_FEATURE_MCE (1U << 7) |
---|
69 | #define CPU_FEATURE_CX8 (1U << 8) |
---|
70 | #define CPU_FEATURE_APIC (1U << 9) |
---|
71 | #define CPU_FEATURES (CPU_FEATURE_FPU | CPU_FEATURE_APIC) |
---|
72 | |
---|
73 | #define BUS_TYPE_LENGTH 6 |
---|
74 | #define BUS_TYPE_STR_ISA "ISA " |
---|
75 | #define BUS_ID_ISA 0 |
---|
76 | |
---|
77 | #define INTR_TYPE_INT 0 |
---|
78 | #define INTR_TYPE_NMI 1 |
---|
79 | #define INTR_TYPE_SMI 2 |
---|
80 | #define INTR_TYPE_EXTINT 3 |
---|
81 | |
---|
82 | #define INTR_MAX_NR 16 |
---|
83 | |
---|
84 | #include "util.h" |
---|
85 | |
---|
86 | extern int get_vcpu_nr(void); /* for the guest's VCPU count */ |
---|
87 | |
---|
88 | /* |
---|
89 | * The following structures are defined in the MuliProcessor Specifiation v1.4 |
---|
90 | */ |
---|
91 | |
---|
92 | /* MP Floating Pointer Structure */ |
---|
93 | struct mp_floating_pointer_struct { |
---|
94 | uint8_t signature[4]; |
---|
95 | uint32_t mp_table; |
---|
96 | uint8_t length; |
---|
97 | uint8_t revision; |
---|
98 | uint8_t checksum; |
---|
99 | uint8_t feature[5]; |
---|
100 | }; |
---|
101 | |
---|
102 | /* MP Configuration Table */ |
---|
103 | struct mp_config_table { |
---|
104 | uint8_t signature[4]; |
---|
105 | uint16_t length; |
---|
106 | uint8_t revision; |
---|
107 | uint8_t checksum; |
---|
108 | uint8_t oem_id[8]; |
---|
109 | uint8_t vendor_id[12]; |
---|
110 | uint32_t oem_table; |
---|
111 | uint16_t oem_table_sz; |
---|
112 | uint16_t nr_entries; |
---|
113 | uint32_t lapic; |
---|
114 | uint16_t extended_length; |
---|
115 | uint8_t extended_checksum; |
---|
116 | uint8_t reserved; |
---|
117 | }; |
---|
118 | |
---|
119 | /* MP Processor Entry */ |
---|
120 | struct mp_proc_entry { |
---|
121 | uint8_t type; |
---|
122 | uint8_t lapic_id; |
---|
123 | uint8_t lapic_version; |
---|
124 | uint8_t cpu_flags; |
---|
125 | uint32_t cpu_signature; |
---|
126 | uint32_t feature_flags; |
---|
127 | uint8_t reserved[8]; |
---|
128 | }; |
---|
129 | |
---|
130 | /* MP Bus Entry */ |
---|
131 | struct mp_bus_entry { |
---|
132 | uint8_t type; |
---|
133 | uint8_t bus_id; |
---|
134 | uint8_t bus_type_str[6]; |
---|
135 | }; |
---|
136 | |
---|
137 | /* MP IOAPIC Entry */ |
---|
138 | struct mp_ioapic_entry { |
---|
139 | uint8_t type; |
---|
140 | uint8_t ioapic_id; |
---|
141 | uint8_t ioapic_version; |
---|
142 | uint8_t ioapic_flags; |
---|
143 | uint32_t ioapic_addr; |
---|
144 | }; |
---|
145 | |
---|
146 | /* MP IO Interrupt Entry */ |
---|
147 | struct mp_io_intr_entry { |
---|
148 | uint8_t type; |
---|
149 | uint8_t intr_type; |
---|
150 | uint16_t io_intr_flags; |
---|
151 | uint8_t src_bus_id; |
---|
152 | uint8_t src_bus_irq; |
---|
153 | uint8_t dst_ioapic_id; |
---|
154 | uint8_t dst_ioapic_intin; |
---|
155 | }; |
---|
156 | |
---|
157 | /* MP Local Interrupt Entry */ |
---|
158 | struct mp_local_intr_entry { |
---|
159 | uint8_t type; |
---|
160 | uint8_t intr_type; |
---|
161 | uint16_t local_intr_flags; |
---|
162 | uint8_t src_bus_id; |
---|
163 | uint8_t src_bus_irq; |
---|
164 | uint8_t dst_lapic_id; |
---|
165 | uint8_t dst_lapic_lintin; |
---|
166 | }; |
---|
167 | |
---|
168 | |
---|
169 | void fill_mp_config_table(struct mp_config_table *mpct, int length) |
---|
170 | { |
---|
171 | int vcpu_nr, i; |
---|
172 | uint8_t checksum; |
---|
173 | |
---|
174 | vcpu_nr = get_vcpu_nr(); |
---|
175 | |
---|
176 | /* fill in the MP configuration table signature, "PCMP" */ |
---|
177 | mpct->signature[0] = 'P'; |
---|
178 | mpct->signature[1] = 'C'; |
---|
179 | mpct->signature[2] = 'M'; |
---|
180 | mpct->signature[3] = 'P'; |
---|
181 | |
---|
182 | mpct->length = length; |
---|
183 | |
---|
184 | mpct->revision = 4; |
---|
185 | |
---|
186 | /* fill in the OEM ID string, "_HVMCPU_" */ |
---|
187 | mpct->oem_id[0] = '_'; mpct->oem_id[3] = 'M'; mpct->oem_id[6] = 'U'; |
---|
188 | mpct->oem_id[1] = 'H'; mpct->oem_id[4] = 'C'; mpct->oem_id[7] = '_'; |
---|
189 | mpct->oem_id[2] = 'V'; mpct->oem_id[5] = 'P'; |
---|
190 | |
---|
191 | /* fill in the Vendor ID string, "XEN " */ |
---|
192 | mpct->vendor_id[0] = 'X'; mpct->vendor_id[6] = ' '; |
---|
193 | mpct->vendor_id[1] = 'E'; mpct->vendor_id[7] = ' '; |
---|
194 | mpct->vendor_id[2] = 'N'; mpct->vendor_id[8] = ' '; |
---|
195 | mpct->vendor_id[3] = ' '; mpct->vendor_id[9] = ' '; |
---|
196 | mpct->vendor_id[4] = ' '; mpct->vendor_id[10] = ' '; |
---|
197 | mpct->vendor_id[5] = ' '; mpct->vendor_id[11] = ' '; |
---|
198 | |
---|
199 | mpct->oem_table = 0; |
---|
200 | mpct->oem_table_sz = 0; |
---|
201 | |
---|
202 | mpct->nr_entries = vcpu_nr + NR_NONPROC_ENTRIES; |
---|
203 | |
---|
204 | mpct->lapic = LAPIC_BASE_ADDRESS; |
---|
205 | mpct->extended_length = 0; |
---|
206 | mpct->extended_checksum = 0; |
---|
207 | |
---|
208 | /* Finally, fill in the checksum. */ |
---|
209 | mpct->checksum = checksum = 0; |
---|
210 | for ( i = 0; i < length; i++ ) |
---|
211 | checksum += ((uint8_t *)(mpct))[i]; |
---|
212 | mpct->checksum = -checksum; |
---|
213 | } |
---|
214 | |
---|
215 | /* fills in an MP processor entry for VCPU 'vcpu_id' */ |
---|
216 | void fill_mp_proc_entry(struct mp_proc_entry *mppe, int vcpu_id) |
---|
217 | { |
---|
218 | mppe->type = ENTRY_TYPE_PROCESSOR; |
---|
219 | mppe->lapic_id = LAPIC_ID(vcpu_id); |
---|
220 | mppe->lapic_version = 0x11; |
---|
221 | mppe->cpu_flags = CPU_FLAG_ENABLED; |
---|
222 | if ( vcpu_id == 0 ) |
---|
223 | mppe->cpu_flags |= CPU_FLAG_BSP; |
---|
224 | mppe->cpu_signature = CPU_SIGNATURE; |
---|
225 | mppe->feature_flags = CPU_FEATURES; |
---|
226 | } |
---|
227 | |
---|
228 | |
---|
229 | /* fills in an MP bus entry of type 'type' and bus ID 'bus_id' */ |
---|
230 | void fill_mp_bus_entry(struct mp_bus_entry *mpbe, int bus_id, const char *type) |
---|
231 | { |
---|
232 | int i; |
---|
233 | |
---|
234 | mpbe->type = ENTRY_TYPE_BUS; |
---|
235 | mpbe->bus_id = bus_id; |
---|
236 | for ( i = 0; i < BUS_TYPE_LENGTH; i++ ) |
---|
237 | mpbe->bus_type_str[i] = type[i]; /* FIXME length check? */ |
---|
238 | } |
---|
239 | |
---|
240 | |
---|
241 | /* fills in an MP IOAPIC entry for IOAPIC 'ioapic_id' */ |
---|
242 | void fill_mp_ioapic_entry(struct mp_ioapic_entry *mpie) |
---|
243 | { |
---|
244 | mpie->type = ENTRY_TYPE_IOAPIC; |
---|
245 | mpie->ioapic_id = IOAPIC_ID; |
---|
246 | mpie->ioapic_version = IOAPIC_VERSION; |
---|
247 | mpie->ioapic_flags = 1; /* enabled */ |
---|
248 | mpie->ioapic_addr = IOAPIC_BASE_ADDRESS; |
---|
249 | } |
---|
250 | |
---|
251 | |
---|
252 | /* fills in an IO interrupt entry for IOAPIC 'ioapic_id' */ |
---|
253 | void fill_mp_io_intr_entry( |
---|
254 | struct mp_io_intr_entry *mpiie, |
---|
255 | int src_bus_id, int src_bus_irq, int ioapic_id, int dst_ioapic_intin) |
---|
256 | { |
---|
257 | mpiie->type = ENTRY_TYPE_IO_INTR; |
---|
258 | mpiie->intr_type = INTR_TYPE_INT; |
---|
259 | mpiie->io_intr_flags = (PCI_ISA_IRQ_MASK & (1U<<src_bus_irq)) ? 0xf : 0x0; |
---|
260 | mpiie->src_bus_id = src_bus_id; |
---|
261 | mpiie->src_bus_irq = src_bus_irq; |
---|
262 | mpiie->dst_ioapic_id = ioapic_id; |
---|
263 | mpiie->dst_ioapic_intin = dst_ioapic_intin; |
---|
264 | } |
---|
265 | |
---|
266 | |
---|
267 | /* fill in the mp floating processor structure */ |
---|
268 | void fill_mpfps(struct mp_floating_pointer_struct *mpfps, uint32_t mpct) |
---|
269 | { |
---|
270 | int i; |
---|
271 | uint8_t checksum; |
---|
272 | |
---|
273 | |
---|
274 | mpfps->signature[0] = '_'; |
---|
275 | mpfps->signature[1] = 'M'; |
---|
276 | mpfps->signature[2] = 'P'; |
---|
277 | mpfps->signature[3] = '_'; |
---|
278 | |
---|
279 | mpfps->mp_table = mpct; |
---|
280 | mpfps->length = 1; |
---|
281 | mpfps->revision = 4; |
---|
282 | mpfps->checksum = 0; |
---|
283 | for (i = 0; i < 5; ++i) |
---|
284 | mpfps->feature[i] = 0; |
---|
285 | |
---|
286 | /* compute the checksum for our new table */ |
---|
287 | checksum = 0; |
---|
288 | for ( i = 0; i < sizeof(struct mp_floating_pointer_struct); i++ ) |
---|
289 | checksum += ((uint8_t *)(mpfps))[i]; |
---|
290 | mpfps->checksum = -checksum; |
---|
291 | } |
---|
292 | |
---|
293 | |
---|
294 | /* |
---|
295 | * find_mp_table_start - searchs through BIOS memory for '___HVMMP' signature |
---|
296 | * |
---|
297 | * The '___HVMMP' signature is created by the ROMBIOS and designates a chunk |
---|
298 | * of space inside the ROMBIOS that is safe for us to write our MP table info |
---|
299 | */ |
---|
300 | void* get_mp_table_start(void) |
---|
301 | { |
---|
302 | char *bios_mem; |
---|
303 | |
---|
304 | for ( bios_mem = (char *)ROMBIOS_BEGIN; |
---|
305 | bios_mem != (char *)ROMBIOS_END; |
---|
306 | bios_mem++ ) |
---|
307 | { |
---|
308 | if ( strncmp(bios_mem, "___HVMMP", 8) == 0) |
---|
309 | return bios_mem; |
---|
310 | } |
---|
311 | |
---|
312 | return NULL; |
---|
313 | } |
---|
314 | |
---|
315 | |
---|
316 | /* recalculate the new ROMBIOS checksum after adding MP tables */ |
---|
317 | void reset_bios_checksum(void) |
---|
318 | { |
---|
319 | uint32_t i; |
---|
320 | uint8_t checksum; |
---|
321 | |
---|
322 | checksum = 0; |
---|
323 | for (i = 0; i < ROMBIOS_MAXOFFSET; ++i) |
---|
324 | checksum += ((uint8_t *)(ROMBIOS_BEGIN))[i]; |
---|
325 | |
---|
326 | *((uint8_t *)(ROMBIOS_BEGIN + ROMBIOS_MAXOFFSET)) = -checksum; |
---|
327 | } |
---|
328 | |
---|
329 | |
---|
330 | /* create_mp_tables - creates MP tables for the guest based upon config data */ |
---|
331 | void create_mp_tables(void) |
---|
332 | { |
---|
333 | void *mp_table_base; |
---|
334 | char *p; |
---|
335 | int vcpu_nr, i, length; |
---|
336 | |
---|
337 | vcpu_nr = get_vcpu_nr(); |
---|
338 | |
---|
339 | printf("Creating MP tables ...\n"); |
---|
340 | |
---|
341 | /* Find the 'safe' place in ROMBIOS for the MP tables. */ |
---|
342 | mp_table_base = get_mp_table_start(); |
---|
343 | if ( mp_table_base == NULL ) |
---|
344 | { |
---|
345 | printf("Couldn't find start point for MP tables\n"); |
---|
346 | return; |
---|
347 | } |
---|
348 | |
---|
349 | p = mp_table_base + sizeof(struct mp_config_table); |
---|
350 | |
---|
351 | for ( i = 0; i < vcpu_nr; i++ ) |
---|
352 | { |
---|
353 | fill_mp_proc_entry((struct mp_proc_entry *)p, i); |
---|
354 | p += sizeof(struct mp_proc_entry); |
---|
355 | } |
---|
356 | |
---|
357 | fill_mp_bus_entry((struct mp_bus_entry *)p, BUS_ID_ISA, BUS_TYPE_STR_ISA); |
---|
358 | p += sizeof(struct mp_bus_entry); |
---|
359 | |
---|
360 | fill_mp_ioapic_entry((struct mp_ioapic_entry *)p); |
---|
361 | p += sizeof(struct mp_ioapic_entry); |
---|
362 | |
---|
363 | for ( i = 0; i < 16; i++ ) |
---|
364 | { |
---|
365 | if ( i == 2 ) continue; /* skip the slave PIC connection */ |
---|
366 | fill_mp_io_intr_entry((struct mp_io_intr_entry *)p, |
---|
367 | BUS_ID_ISA, i, IOAPIC_ID, (i == 0) ? 2 : i); |
---|
368 | p += sizeof(struct mp_io_intr_entry); |
---|
369 | } |
---|
370 | |
---|
371 | length = p - (char *)mp_table_base; |
---|
372 | |
---|
373 | /* find the next 16-byte boundary to place the mp floating pointer */ |
---|
374 | while ( (unsigned long)p & 0xF ) |
---|
375 | p++; |
---|
376 | |
---|
377 | fill_mpfps((struct mp_floating_pointer_struct *)p, |
---|
378 | (uint32_t)mp_table_base); |
---|
379 | |
---|
380 | fill_mp_config_table((struct mp_config_table *)mp_table_base, length); |
---|
381 | reset_bios_checksum(); |
---|
382 | } |
---|