1 | /* |
---|
2 | * Copyright (c) 2004, Intel Corporation. |
---|
3 | * Copyright (c) 2006, Keir Fraser, XenSource Inc. |
---|
4 | * |
---|
5 | * This program is free software; you can redistribute it and/or modify it |
---|
6 | * under the terms and conditions of the GNU General Public License, version |
---|
7 | * 2, as published by the Free Software Foundation. |
---|
8 | * |
---|
9 | * This program is distributed in the hope it will be useful, but WITHOUT ANY |
---|
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
---|
11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
---|
12 | * details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU General Public License along with |
---|
15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
---|
16 | * Place - Suite 330, Boston, MA 02111-1307 USA. |
---|
17 | */ |
---|
18 | |
---|
19 | #include "acpi2_0.h" |
---|
20 | #include "ssdt_tpm.h" |
---|
21 | #include "../config.h" |
---|
22 | #include "../util.h" |
---|
23 | |
---|
24 | #define align16(sz) (((sz) + 15) & ~15) |
---|
25 | #define fixed_strcpy(d, s) strncpy((d), (s), sizeof(d)) |
---|
26 | |
---|
27 | extern struct acpi_20_rsdp Rsdp; |
---|
28 | extern struct acpi_20_rsdt Rsdt; |
---|
29 | extern struct acpi_20_xsdt Xsdt; |
---|
30 | extern struct acpi_20_fadt Fadt; |
---|
31 | extern struct acpi_20_facs Facs; |
---|
32 | extern unsigned char AmlCode[]; |
---|
33 | extern int DsdtLen; |
---|
34 | |
---|
35 | static void set_checksum( |
---|
36 | void *table, uint32_t checksum_offset, uint32_t length) |
---|
37 | { |
---|
38 | uint8_t *p, sum = 0; |
---|
39 | |
---|
40 | p = table; |
---|
41 | p[checksum_offset] = 0; |
---|
42 | |
---|
43 | while ( length-- ) |
---|
44 | sum = sum + *p++; |
---|
45 | |
---|
46 | p = table; |
---|
47 | p[checksum_offset] = -sum; |
---|
48 | } |
---|
49 | |
---|
50 | int construct_madt(struct acpi_20_madt *madt) |
---|
51 | { |
---|
52 | struct acpi_20_madt_intsrcovr *intsrcovr; |
---|
53 | struct acpi_20_madt_ioapic *io_apic; |
---|
54 | struct acpi_20_madt_lapic *lapic; |
---|
55 | int i, offset = 0; |
---|
56 | |
---|
57 | memset(madt, 0, sizeof(*madt)); |
---|
58 | madt->header.signature = ACPI_2_0_MADT_SIGNATURE; |
---|
59 | madt->header.revision = ACPI_2_0_MADT_REVISION; |
---|
60 | fixed_strcpy(madt->header.oem_id, ACPI_OEM_ID); |
---|
61 | fixed_strcpy(madt->header.oem_table_id, ACPI_OEM_TABLE_ID); |
---|
62 | madt->header.oem_revision = ACPI_OEM_REVISION; |
---|
63 | madt->header.creator_id = ACPI_CREATOR_ID; |
---|
64 | madt->header.creator_revision = ACPI_CREATOR_REVISION; |
---|
65 | madt->lapic_addr = LAPIC_BASE_ADDRESS; |
---|
66 | madt->flags = ACPI_PCAT_COMPAT; |
---|
67 | offset += sizeof(*madt); |
---|
68 | |
---|
69 | intsrcovr = (struct acpi_20_madt_intsrcovr *)(madt + 1); |
---|
70 | for ( i = 0; i < 16; i++ ) |
---|
71 | { |
---|
72 | memset(intsrcovr, 0, sizeof(*intsrcovr)); |
---|
73 | intsrcovr->type = ACPI_INTERRUPT_SOURCE_OVERRIDE; |
---|
74 | intsrcovr->length = sizeof(*intsrcovr); |
---|
75 | intsrcovr->source = i; |
---|
76 | |
---|
77 | if ( i == 0 ) |
---|
78 | { |
---|
79 | /* ISA IRQ0 routed to IOAPIC GSI 2. */ |
---|
80 | intsrcovr->gsi = 2; |
---|
81 | intsrcovr->flags = 0x0; |
---|
82 | } |
---|
83 | else if ( PCI_ISA_IRQ_MASK & (1U << i) ) |
---|
84 | { |
---|
85 | /* PCI: active-low level-triggered. */ |
---|
86 | intsrcovr->gsi = i; |
---|
87 | intsrcovr->flags = 0xf; |
---|
88 | } |
---|
89 | else |
---|
90 | { |
---|
91 | /* No need for a INT source override structure. */ |
---|
92 | continue; |
---|
93 | } |
---|
94 | |
---|
95 | offset += sizeof(*intsrcovr); |
---|
96 | intsrcovr++; |
---|
97 | } |
---|
98 | |
---|
99 | io_apic = (struct acpi_20_madt_ioapic *)intsrcovr; |
---|
100 | memset(io_apic, 0, sizeof(*io_apic)); |
---|
101 | io_apic->type = ACPI_IO_APIC; |
---|
102 | io_apic->length = sizeof(*io_apic); |
---|
103 | io_apic->ioapic_id = IOAPIC_ID; |
---|
104 | io_apic->ioapic_addr = IOAPIC_BASE_ADDRESS; |
---|
105 | offset += sizeof(*io_apic); |
---|
106 | |
---|
107 | lapic = (struct acpi_20_madt_lapic *)(io_apic + 1); |
---|
108 | for ( i = 0; i < get_vcpu_nr(); i++ ) |
---|
109 | { |
---|
110 | memset(lapic, 0, sizeof(*lapic)); |
---|
111 | lapic->type = ACPI_PROCESSOR_LOCAL_APIC; |
---|
112 | lapic->length = sizeof(*lapic); |
---|
113 | /* Processor ID must match processor-object IDs in the DSDT. */ |
---|
114 | lapic->acpi_processor_id = i; |
---|
115 | lapic->apic_id = LAPIC_ID(i); |
---|
116 | lapic->flags = ACPI_LOCAL_APIC_ENABLED; |
---|
117 | offset += sizeof(*lapic); |
---|
118 | lapic++; |
---|
119 | } |
---|
120 | |
---|
121 | madt->header.length = offset; |
---|
122 | set_checksum(madt, offsetof(struct acpi_header, checksum), offset); |
---|
123 | |
---|
124 | return align16(offset); |
---|
125 | } |
---|
126 | |
---|
127 | int construct_hpet(struct acpi_20_hpet *hpet) |
---|
128 | { |
---|
129 | int offset; |
---|
130 | |
---|
131 | memset(hpet, 0, sizeof(*hpet)); |
---|
132 | hpet->header.signature = ACPI_2_0_HPET_SIGNATURE; |
---|
133 | hpet->header.revision = ACPI_2_0_HPET_REVISION; |
---|
134 | fixed_strcpy(hpet->header.oem_id, ACPI_OEM_ID); |
---|
135 | fixed_strcpy(hpet->header.oem_table_id, ACPI_OEM_TABLE_ID); |
---|
136 | hpet->header.oem_revision = ACPI_OEM_REVISION; |
---|
137 | hpet->header.creator_id = ACPI_CREATOR_ID; |
---|
138 | hpet->header.creator_revision = ACPI_CREATOR_REVISION; |
---|
139 | hpet->timer_block_id = 0x8086a201; |
---|
140 | hpet->addr.address = ACPI_HPET_ADDRESS; |
---|
141 | offset = sizeof(*hpet); |
---|
142 | |
---|
143 | hpet->header.length = offset; |
---|
144 | set_checksum(hpet, offsetof(struct acpi_header, checksum), offset); |
---|
145 | |
---|
146 | return offset; |
---|
147 | } |
---|
148 | |
---|
149 | int construct_processor_objects(uint8_t *buf) |
---|
150 | { |
---|
151 | static const char pdat[13] = { 0x5b, 0x83, 0x0b, 0x50, 0x52 }; |
---|
152 | static const char hex[] = "0123456789ABCDEF"; |
---|
153 | static const char pr_scope[] = "\\_PR_"; |
---|
154 | unsigned int i, length, nr_cpus = get_vcpu_nr(); |
---|
155 | struct acpi_header *hdr; |
---|
156 | uint8_t *p = buf; |
---|
157 | |
---|
158 | /* |
---|
159 | * 1. Table Header. |
---|
160 | */ |
---|
161 | |
---|
162 | hdr = (struct acpi_header *)p; |
---|
163 | hdr->signature = ASCII32('S','S','D','T'); |
---|
164 | hdr->revision = 2; |
---|
165 | fixed_strcpy(hdr->oem_id, ACPI_OEM_ID); |
---|
166 | fixed_strcpy(hdr->oem_table_id, ACPI_OEM_TABLE_ID); |
---|
167 | hdr->oem_revision = ACPI_OEM_REVISION; |
---|
168 | hdr->creator_id = ACPI_CREATOR_ID; |
---|
169 | hdr->creator_revision = ACPI_CREATOR_REVISION; |
---|
170 | p += sizeof(*hdr); |
---|
171 | |
---|
172 | /* |
---|
173 | * 2. Scope Definition. |
---|
174 | */ |
---|
175 | |
---|
176 | /* ScopeOp */ |
---|
177 | *p++ = 0x10; |
---|
178 | |
---|
179 | /* PkgLength (includes length bytes!). */ |
---|
180 | length = 1 + strlen(pr_scope) + (nr_cpus * sizeof(pdat)); |
---|
181 | if ( length <= 0x3f ) |
---|
182 | { |
---|
183 | *p++ = length; |
---|
184 | } |
---|
185 | else if ( ++length <= 0xfff ) |
---|
186 | { |
---|
187 | *p++ = 0x40 | (length & 0xf); |
---|
188 | *p++ = length >> 4; |
---|
189 | } |
---|
190 | else |
---|
191 | { |
---|
192 | length++; |
---|
193 | *p++ = 0x80 | (length & 0xf); |
---|
194 | *p++ = (length >> 4) & 0xff; |
---|
195 | *p++ = (length >> 12) & 0xff; |
---|
196 | } |
---|
197 | |
---|
198 | /* NameString */ |
---|
199 | strncpy(p, pr_scope, strlen(pr_scope)); |
---|
200 | p += strlen(pr_scope); |
---|
201 | |
---|
202 | /* |
---|
203 | * 3. Processor Objects. |
---|
204 | */ |
---|
205 | |
---|
206 | for ( i = 0; i < nr_cpus; i++ ) |
---|
207 | { |
---|
208 | memcpy(p, pdat, sizeof(pdat)); |
---|
209 | /* ProcessorName */ |
---|
210 | p[5] = hex[(i>>4)&15]; |
---|
211 | p[6] = hex[(i>>0)&15]; |
---|
212 | /* ProcessorID */ |
---|
213 | p[7] = i; |
---|
214 | p += sizeof(pdat); |
---|
215 | } |
---|
216 | |
---|
217 | hdr->length = p - buf; |
---|
218 | set_checksum(hdr, offsetof(struct acpi_header, checksum), hdr->length); |
---|
219 | |
---|
220 | return hdr->length; |
---|
221 | } |
---|
222 | |
---|
223 | int construct_secondary_tables(uint8_t *buf, unsigned long *table_ptrs) |
---|
224 | { |
---|
225 | int offset = 0, nr_tables = 0; |
---|
226 | struct acpi_20_madt *madt; |
---|
227 | struct acpi_20_hpet *hpet; |
---|
228 | struct acpi_20_tcpa *tcpa; |
---|
229 | static const uint16_t tis_signature[] = {0x0001, 0x0001, 0x0001}; |
---|
230 | uint16_t *tis_hdr; |
---|
231 | |
---|
232 | /* MADT. */ |
---|
233 | if ( (get_vcpu_nr() > 1) || get_apic_mode() ) |
---|
234 | { |
---|
235 | madt = (struct acpi_20_madt *)&buf[offset]; |
---|
236 | offset += construct_madt(madt); |
---|
237 | table_ptrs[nr_tables++] = (unsigned long)madt; |
---|
238 | } |
---|
239 | |
---|
240 | /* HPET. */ |
---|
241 | hpet = (struct acpi_20_hpet *)&buf[offset]; |
---|
242 | offset += construct_hpet(hpet); |
---|
243 | table_ptrs[nr_tables++] = (unsigned long)hpet; |
---|
244 | |
---|
245 | /* Processor Object SSDT. */ |
---|
246 | table_ptrs[nr_tables++] = (unsigned long)&buf[offset]; |
---|
247 | offset += construct_processor_objects(&buf[offset]); |
---|
248 | |
---|
249 | /* TPM TCPA and SSDT. */ |
---|
250 | tis_hdr = (uint16_t *)0xFED40F00; |
---|
251 | if ( (tis_hdr[0] == tis_signature[0]) && |
---|
252 | (tis_hdr[1] == tis_signature[1]) && |
---|
253 | (tis_hdr[2] == tis_signature[2]) ) |
---|
254 | { |
---|
255 | memcpy(&buf[offset], AmlCode_TPM, sizeof(AmlCode_TPM)); |
---|
256 | table_ptrs[nr_tables++] = (unsigned long)&buf[offset]; |
---|
257 | offset += align16(sizeof(AmlCode_TPM)); |
---|
258 | |
---|
259 | tcpa = (struct acpi_20_tcpa *)&buf[offset]; |
---|
260 | memset(tcpa, 0, sizeof(*tcpa)); |
---|
261 | offset += align16(sizeof(*tcpa)); |
---|
262 | table_ptrs[nr_tables++] = (unsigned long)tcpa; |
---|
263 | |
---|
264 | tcpa->header.signature = ACPI_2_0_TCPA_SIGNATURE; |
---|
265 | tcpa->header.length = sizeof(*tcpa); |
---|
266 | tcpa->header.revision = ACPI_2_0_TCPA_REVISION; |
---|
267 | fixed_strcpy(tcpa->header.oem_id, ACPI_OEM_ID); |
---|
268 | fixed_strcpy(tcpa->header.oem_table_id, ACPI_OEM_TABLE_ID); |
---|
269 | tcpa->header.oem_revision = ACPI_OEM_REVISION; |
---|
270 | tcpa->header.creator_id = ACPI_CREATOR_ID; |
---|
271 | tcpa->header.creator_revision = ACPI_CREATOR_REVISION; |
---|
272 | tcpa->lasa = e820_malloc(ACPI_2_0_TCPA_LAML_SIZE); |
---|
273 | if ( tcpa->lasa ) |
---|
274 | { |
---|
275 | tcpa->laml = ACPI_2_0_TCPA_LAML_SIZE; |
---|
276 | memset((char *)(unsigned long)tcpa->lasa, 0, tcpa->laml); |
---|
277 | set_checksum(tcpa, |
---|
278 | offsetof(struct acpi_header, checksum), |
---|
279 | tcpa->header.length); |
---|
280 | } |
---|
281 | } |
---|
282 | |
---|
283 | table_ptrs[nr_tables] = 0; |
---|
284 | return align16(offset); |
---|
285 | } |
---|
286 | |
---|
287 | /* Copy all the ACPI table to buffer. */ |
---|
288 | int acpi_build_tables(uint8_t *buf) |
---|
289 | { |
---|
290 | struct acpi_20_rsdp *rsdp; |
---|
291 | struct acpi_20_rsdt *rsdt; |
---|
292 | struct acpi_20_xsdt *xsdt; |
---|
293 | struct acpi_20_fadt *fadt; |
---|
294 | struct acpi_10_fadt *fadt_10; |
---|
295 | struct acpi_20_facs *facs; |
---|
296 | unsigned char *dsdt; |
---|
297 | unsigned long secondary_tables[16]; |
---|
298 | int offset = 0, i; |
---|
299 | |
---|
300 | facs = (struct acpi_20_facs *)&buf[offset]; |
---|
301 | memcpy(facs, &Facs, sizeof(struct acpi_20_facs)); |
---|
302 | offset += align16(sizeof(struct acpi_20_facs)); |
---|
303 | |
---|
304 | dsdt = (unsigned char *)&buf[offset]; |
---|
305 | memcpy(dsdt, &AmlCode, DsdtLen); |
---|
306 | offset += align16(DsdtLen); |
---|
307 | |
---|
308 | /* |
---|
309 | * N.B. ACPI 1.0 operating systems may not handle FADT with revision 2 |
---|
310 | * or above properly, notably Windows 2000, which tries to copy FADT |
---|
311 | * into a 116 bytes buffer thus causing an overflow. The solution is to |
---|
312 | * link the higher revision FADT with the XSDT only and introduce a |
---|
313 | * compatible revision 1 FADT that is linked with the RSDT. Refer to: |
---|
314 | * http://www.acpi.info/presentations/S01USMOBS169_OS%20new.ppt |
---|
315 | */ |
---|
316 | fadt_10 = (struct acpi_10_fadt *)&buf[offset]; |
---|
317 | memcpy(fadt_10, &Fadt, sizeof(struct acpi_10_fadt)); |
---|
318 | offset += align16(sizeof(struct acpi_10_fadt)); |
---|
319 | fadt_10->header.length = sizeof(struct acpi_10_fadt); |
---|
320 | fadt_10->header.revision = ACPI_1_0_FADT_REVISION; |
---|
321 | fadt_10->dsdt = (unsigned long)dsdt; |
---|
322 | fadt_10->firmware_ctrl = (unsigned long)facs; |
---|
323 | set_checksum(fadt_10, |
---|
324 | offsetof(struct acpi_header, checksum), |
---|
325 | sizeof(struct acpi_10_fadt)); |
---|
326 | |
---|
327 | fadt = (struct acpi_20_fadt *)&buf[offset]; |
---|
328 | memcpy(fadt, &Fadt, sizeof(struct acpi_20_fadt)); |
---|
329 | offset += align16(sizeof(struct acpi_20_fadt)); |
---|
330 | fadt->dsdt = (unsigned long)dsdt; |
---|
331 | fadt->x_dsdt = (unsigned long)dsdt; |
---|
332 | fadt->firmware_ctrl = (unsigned long)facs; |
---|
333 | fadt->x_firmware_ctrl = (unsigned long)facs; |
---|
334 | set_checksum(fadt, |
---|
335 | offsetof(struct acpi_header, checksum), |
---|
336 | sizeof(struct acpi_20_fadt)); |
---|
337 | |
---|
338 | offset += construct_secondary_tables(&buf[offset], secondary_tables); |
---|
339 | |
---|
340 | xsdt = (struct acpi_20_xsdt *)&buf[offset]; |
---|
341 | memcpy(xsdt, &Xsdt, sizeof(struct acpi_header)); |
---|
342 | xsdt->entry[0] = (unsigned long)fadt; |
---|
343 | for ( i = 0; secondary_tables[i]; i++ ) |
---|
344 | xsdt->entry[i+1] = secondary_tables[i]; |
---|
345 | xsdt->header.length = sizeof(struct acpi_header) + (i+1)*sizeof(uint64_t); |
---|
346 | offset += align16(xsdt->header.length); |
---|
347 | set_checksum(xsdt, |
---|
348 | offsetof(struct acpi_header, checksum), |
---|
349 | xsdt->header.length); |
---|
350 | |
---|
351 | rsdt = (struct acpi_20_rsdt *)&buf[offset]; |
---|
352 | memcpy(rsdt, &Rsdt, sizeof(struct acpi_header)); |
---|
353 | rsdt->entry[0] = (unsigned long)fadt_10; |
---|
354 | for ( i = 0; secondary_tables[i]; i++ ) |
---|
355 | rsdt->entry[i+1] = secondary_tables[i]; |
---|
356 | rsdt->header.length = sizeof(struct acpi_header) + (i+1)*sizeof(uint32_t); |
---|
357 | offset += align16(rsdt->header.length); |
---|
358 | set_checksum(rsdt, |
---|
359 | offsetof(struct acpi_header, checksum), |
---|
360 | rsdt->header.length); |
---|
361 | |
---|
362 | rsdp = (struct acpi_20_rsdp *)&buf[offset]; |
---|
363 | memcpy(rsdp, &Rsdp, sizeof(struct acpi_20_rsdp)); |
---|
364 | offset += align16(sizeof(struct acpi_20_rsdp)); |
---|
365 | rsdp->rsdt_address = (unsigned long)rsdt; |
---|
366 | rsdp->xsdt_address = (unsigned long)xsdt; |
---|
367 | set_checksum(rsdp, |
---|
368 | offsetof(struct acpi_10_rsdp, checksum), |
---|
369 | sizeof(struct acpi_10_rsdp)); |
---|
370 | set_checksum(rsdp, |
---|
371 | offsetof(struct acpi_20_rsdp, extended_checksum), |
---|
372 | sizeof(struct acpi_20_rsdp)); |
---|
373 | |
---|
374 | return offset; |
---|
375 | } |
---|
376 | |
---|
377 | /* |
---|
378 | * Local variables: |
---|
379 | * mode: C |
---|
380 | * c-set-style: "BSD" |
---|
381 | * c-basic-offset: 4 |
---|
382 | * tab-width: 4 |
---|
383 | * indent-tabs-mode: nil |
---|
384 | * End: |
---|
385 | */ |
---|