1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
5 * Author: Corvin Köhne <c.koehne@beckhoff.com>
6 */
7
8 #include <sys/types.h>
9 #include <sys/param.h>
10 #include <sys/endian.h>
11 #include <sys/linker_set.h>
12
13 #include <machine/vmm.h>
14
15 #include <assert.h>
16 #include <err.h>
17 #include <errno.h>
18 #include <vmmapi.h>
19
20 #include "acpi.h"
21 #include "acpi_device.h"
22 #include "config.h"
23 #include "mem.h"
24 #include "qemu_fwcfg.h"
25 #include "tpm_ppi.h"
26
27 #define TPM_PPI_ADDRESS 0xFED45000
28 #define TPM_PPI_SIZE 0x400
29
30 #define TPM_PPI_FWCFG_FILE "etc/tpm/config"
31
32 #define TPM_PPI_QEMU_NAME "qemu"
33
34 struct tpm_ppi_qemu {
35 uint8_t func[256]; // FUNC
36 uint8_t in; // PPIN
37 uint32_t ip; // PPIP
38 uint32_t response; // PPRP
39 uint32_t request; // PPRQ
40 uint32_t request_parameter; // PPRM
41 uint32_t last_request; // LPPR
42 uint32_t func_ret; // FRET
43 uint8_t _reserved1[0x40]; // RES1
44 uint8_t next_step; // next_step
45 } __packed;
46 static_assert(sizeof(struct tpm_ppi_qemu) <= TPM_PPI_SIZE,
47 "Wrong size of tpm_ppi_qemu");
48
49 struct tpm_ppi_fwcfg {
50 uint32_t ppi_address;
51 uint8_t tpm_version;
52 uint8_t ppi_version;
53 } __packed;
54
55 static int
tpm_ppi_mem_handler(struct vcpu * const vcpu __unused,const int dir,const uint64_t addr,const int size,uint64_t * const val,void * const arg1,const long arg2 __unused)56 tpm_ppi_mem_handler(struct vcpu *const vcpu __unused, const int dir,
57 const uint64_t addr, const int size, uint64_t *const val, void *const arg1,
58 const long arg2 __unused)
59 {
60 struct tpm_ppi_qemu *ppi;
61 uint8_t *ptr;
62 uint64_t off;
63
64 if ((addr & (size - 1)) != 0) {
65 warnx("%s: unaligned %s access @ %16lx [size = %x]", __func__,
66 (dir == MEM_F_READ) ? "read" : "write", addr, size);
67 }
68
69 ppi = arg1;
70
71 off = addr - TPM_PPI_ADDRESS;
72 ptr = (uint8_t *)ppi + off;
73
74 if (off > TPM_PPI_SIZE || off + size > TPM_PPI_SIZE) {
75 return (EINVAL);
76 }
77
78 assert(size == 1 || size == 2 || size == 4 || size == 8);
79 if (dir == MEM_F_READ) {
80 memcpy(val, ptr, size);
81 } else {
82 memcpy(ptr, val, size);
83 }
84
85 return (0);
86 }
87
88 static struct mem_range ppi_mmio = {
89 .name = "ppi-mmio",
90 .base = TPM_PPI_ADDRESS,
91 .size = TPM_PPI_SIZE,
92 .flags = MEM_F_RW,
93 .handler = tpm_ppi_mem_handler,
94 };
95
96 static int
tpm_ppi_init(void ** sc)97 tpm_ppi_init(void **sc)
98 {
99 struct tpm_ppi_qemu *ppi = NULL;
100 struct tpm_ppi_fwcfg *fwcfg = NULL;
101 int error;
102
103 #ifdef __FreeBSD__
104 ppi = calloc(1, TPM_PPI_SIZE);
105 #else
106 // SMATCH: double check that we're allocating correct size: 346 vs 1024
107 void *ptr = calloc(1, TPM_PPI_SIZE);
108 ppi = (struct tpm_ppi_qemu *)ptr;
109 #endif
110 if (ppi == NULL) {
111 warnx("%s: failed to allocate acpi region for ppi", __func__);
112 error = ENOMEM;
113 goto err_out;
114 }
115
116 fwcfg = calloc(1, sizeof(struct tpm_ppi_fwcfg));
117 if (fwcfg == NULL) {
118 warnx("%s: failed to allocate fwcfg item", __func__);
119 error = ENOMEM;
120 goto err_out;
121 }
122
123 fwcfg->ppi_address = htole32(TPM_PPI_ADDRESS);
124 fwcfg->tpm_version = 2;
125 fwcfg->ppi_version = 1;
126
127 error = qemu_fwcfg_add_file(TPM_PPI_FWCFG_FILE,
128 sizeof(struct tpm_ppi_fwcfg), fwcfg);
129 if (error) {
130 warnx("%s: failed to add fwcfg file", __func__);
131 goto err_out;
132 }
133
134 /*
135 * We would just need to create some guest memory for the PPI region.
136 * Sadly, bhyve has a strange memory interface. We can't just add more
137 * memory to the VM. So, create a trap instead which reads and writes to
138 * the ppi region. It's very slow but ppi shouldn't be used frequently.
139 */
140 ppi_mmio.arg1 = ppi;
141 error = register_mem(&ppi_mmio);
142 if (error) {
143 warnx("%s: failed to create trap for ppi accesses", __func__);
144 goto err_out;
145 }
146
147 *sc = ppi;
148
149 return (0);
150
151 err_out:
152 free(fwcfg);
153 free(ppi);
154
155 return (error);
156 }
157
158 static void
tpm_ppi_deinit(void * sc)159 tpm_ppi_deinit(void *sc)
160 {
161 struct tpm_ppi_qemu *ppi;
162 int error;
163
164 if (sc == NULL)
165 return;
166
167 ppi = sc;
168
169 error = unregister_mem(&ppi_mmio);
170 assert(error == 0);
171
172 free(ppi);
173 }
174
175 static int
tpm_ppi_write_dsdt_regions(void * sc __unused)176 tpm_ppi_write_dsdt_regions(void *sc __unused)
177 {
178 /*
179 * struct tpm_ppi_qemu
180 */
181 /*
182 * According to qemu the Windows ACPI parser has a bug that DerefOf is
183 * broken for SYSTEM_MEMORY. Due to that bug, qemu uses a dynamic
184 * operation region inside a method.
185 */
186 dsdt_line("Method(TPFN, 1, Serialized)");
187 dsdt_line("{");
188 dsdt_line(" If(LGreaterEqual(Arg0, 0x100))");
189 dsdt_line(" {");
190 dsdt_line(" Return(Zero)");
191 dsdt_line(" }");
192 dsdt_line(
193 " OperationRegion(TPP1, SystemMemory, Add(0x%8x, Arg0), One)",
194 TPM_PPI_ADDRESS);
195 dsdt_line(" Field(TPP1, ByteAcc, NoLock, Preserve)");
196 dsdt_line(" {");
197 dsdt_line(" TPPF, 8,");
198 dsdt_line(" }");
199 dsdt_line(" Return(TPPF)");
200 dsdt_line("}");
201 dsdt_line("OperationRegion(TPP2, SystemMemory, 0x%8x, 0x%x)",
202 TPM_PPI_ADDRESS + 0x100, 0x5A);
203 dsdt_line("Field(TPP2, AnyAcc, NoLock, Preserve)");
204 dsdt_line("{");
205 dsdt_line(" PPIN, 8,");
206 dsdt_line(" PPIP, 32,");
207 dsdt_line(" PPRP, 32,");
208 dsdt_line(" PPRQ, 32,");
209 dsdt_line(" PPRM, 32,");
210 dsdt_line(" LPPR, 32,");
211 dsdt_line("}");
212 /*
213 * Used for TCG Platform Reset Attack Mitigation
214 */
215 dsdt_line("OperationRegion(TPP3, SystemMemory, 0x%8x, 1)",
216 TPM_PPI_ADDRESS + sizeof(struct tpm_ppi_qemu));
217 dsdt_line("Field(TPP3, ByteAcc, NoLock, Preserve)");
218 dsdt_line("{");
219 dsdt_line(" MOVV, 8,");
220 dsdt_line("}");
221
222 return (0);
223 }
224
225 static int
tpm_ppi_write_dsdt_dsm(void * sc __unused)226 tpm_ppi_write_dsdt_dsm(void *sc __unused)
227 {
228 /*
229 * Physical Presence Interface
230 */
231 dsdt_line(
232 "If(LEqual(Arg0, ToUUID(\"3DDDFAA6-361B-4EB4-A424-8D10089D1653\"))) /* UUID */");
233 dsdt_line("{");
234 /*
235 * Function 0 - _DSM Query Function
236 * Arguments:
237 * Empty Package
238 * Return:
239 * Buffer - Index field of supported functions
240 */
241 dsdt_line(" If(LEqual(Arg2, 0)) /* Function */");
242 dsdt_line(" {");
243 dsdt_line(" Return(Buffer(0x02)");
244 dsdt_line(" {");
245 dsdt_line(" 0xFF, 0x01");
246 dsdt_line(" })");
247 dsdt_line(" }");
248 /*
249 * Function 1 - Get Physical Presence Interface Version
250 * Arguments:
251 * Empty Package
252 * Return:
253 * String - Supported Physical Presence Interface revision
254 */
255 dsdt_line(" If(LEqual(Arg2, 1)) /* Function */");
256 dsdt_line(" {");
257 dsdt_line(" Return(\"1.3\")");
258 dsdt_line(" }");
259 /*
260 * Function 2 - Submit TPM Operation Request to Pre-OS Environment
261 * !!!DEPRECATED BUT MANDATORY!!!
262 * Arguments:
263 * Integer - Operation Value of the Request
264 * Return:
265 * Integer - Function Return Code
266 * 0 - Success
267 * 1 - Operation Value of the Request Not Supported
268 * 2 - General Failure
269 */
270 dsdt_line(" If(LEqual(Arg2, 2)) /* Function */");
271 dsdt_line(" {");
272 dsdt_line(" Store(DerefOf(Index(Arg3, 0)), Local0)");
273 dsdt_line(" Store(TPFN(Local0), Local1)");
274 dsdt_line(" If (LEqual(And(Local1, 7), 0))");
275 dsdt_line(" {");
276 dsdt_line(" Return(1)");
277 dsdt_line(" }");
278 dsdt_line(" Store(Local0, PPRQ)");
279 dsdt_line(" Store(0, PPRM)");
280 dsdt_line(" Return(0)");
281 dsdt_line(" }");
282 /*
283 * Function 3 - Get Pending TPM Operation Request By the OS
284 * Arguments:
285 * Empty Package
286 * Return:
287 * Package
288 * Integer 1 - Function Return Code
289 * 0 - Success
290 * 1 - General Failure
291 * Integer 2 - Pending operation requested by the OS
292 * 0 - None
293 * >0 - Operation Value of the Pending Request
294 * Integer 3 - Optional argument to pending operation requested by
295 * the OS
296 * 0 - None
297 * >0 - Argument of the Pending Request
298 */
299 dsdt_line(" If(LEqual(Arg2, 3)) /* Function */");
300 dsdt_line(" {");
301 dsdt_line(" If(LEqual(Arg1, 1)) /* Revision */");
302 dsdt_line(" {");
303 dsdt_line(" Store(PPRQ, Index(TPM2, 1))");
304 dsdt_line(" Return(TPM2)");
305 dsdt_line(" }");
306 dsdt_line(" If(LEqual(Arg1, 2)) /* Revision */");
307 dsdt_line(" {");
308 dsdt_line(" Store(PPRQ, Index(TPM3, 1))");
309 dsdt_line(" Store(PPRM, Index(TPM3, 2))");
310 dsdt_line(" Return(TPM3)");
311 dsdt_line(" }");
312 dsdt_line(" }");
313 /*
314 * Function 4 - Get Platform-Specific Action to Transition to Pre-OS
315 * Environment
316 * Arguments:
317 * Empty Package
318 * Return:
319 * Integer - Action that the OS should take to transition to the
320 * pre-OS environment for execution of a requested operation
321 * 0 - None
322 * 1 - Shutdown
323 * 2 - Reboot
324 * 3 - OS Vendor-specific
325 */
326 dsdt_line(" If(LEqual(Arg2, 4)) /* Function */");
327 dsdt_line(" {");
328 dsdt_line(" Return(2)");
329 dsdt_line(" }");
330 /*
331 * Function 5 - Return TPM Operation Response to OS Environment
332 * Arguments:
333 * Empty Package
334 * Return:
335 * Package
336 * Integer 1 - Function Return Code
337 * 0 - Success
338 * 1 - General Failure
339 * Integer 2 - Most recent operation request
340 * 0 - None
341 * >0 - Operation value of the most recent request
342 * Integer 3 - Response to the most recent operation request
343 * 0 - Success
344 * 0x00000001..0x000000FF - Corresponding TPM error code
345 * 0xFFFFFFF0 - User Abort or timeout of dialog
346 * 0xFFFFFFF1 - firmware failure
347 */
348 dsdt_line(" If(LEqual(Arg2, 5)) /* Function */");
349 dsdt_line(" {");
350 dsdt_line(" Store(LPPR, Index(TPM3, 1))");
351 dsdt_line(" Store(PPRP, Index(TPM3, 2))");
352 dsdt_line(" Return(TPM3)");
353 dsdt_line(" }");
354 /*
355 * Function 6 - Submit preferred user language
356 * !!!DEPRECATED BUT MANDATORY!!!
357 * Arguments:
358 * Package
359 * String - Preferred language code
360 * Return:
361 * Integer
362 * 3 - Not implemented
363 */
364 dsdt_line(" If(LEqual(Arg2, 6)) /* Function */");
365 dsdt_line(" {");
366 dsdt_line(" Return(3)");
367 dsdt_line(" }");
368 /*
369 * Function 7 - Submit TPM Operation Request to Pre-OS Environment 2
370 * Arguments:
371 * Package
372 * Integer 1 - Operation Value of the Request
373 * Integer 2 - Argument for Operation
374 * Return:
375 * Integer - Function Return Code
376 * 0 - Success
377 * 1 - Not Implemented
378 * 2 - General Failure
379 * 3 - Operation blocked by current firmware settings
380 */
381 dsdt_line(" If(LEqual(Arg2, 7)) /* Function */");
382 dsdt_line(" {");
383 dsdt_line(" Store(DerefOf(Index(Arg3, 0)), Local0)");
384 dsdt_line(" Store(TPFN(Local0), Local1)");
385 dsdt_line(" If (LEqual(And(Local1, 7), 0)) /* Not Implemented */");
386 dsdt_line(" {");
387 dsdt_line(" Return(1)");
388 dsdt_line(" }");
389 dsdt_line(" If (LEqual(And(Local1, 7), 2)) /* Blocked */ ");
390 dsdt_line(" {");
391 dsdt_line(" Return(3)");
392 dsdt_line(" }");
393 dsdt_line(" If(LEqual(Arg1, 1)) /* Revision */");
394 dsdt_line(" {");
395 dsdt_line(" Store(Local0, PPRQ)");
396 dsdt_line(" Store(0, PPRM)");
397 dsdt_line(" }");
398 dsdt_line(" If(LEqual(Arg1, 2)) /* Revision */");
399 dsdt_line(" {");
400 dsdt_line(" Store(Local0, PPRQ)");
401 dsdt_line(" Store(DerefOf(Index(Arg3, 1)), PPRM)");
402 dsdt_line(" }");
403 dsdt_line(" Return(0)");
404 dsdt_line(" }");
405 /*
406 * Function 8 - Get User Confirmation Status for Operation
407 * Arguments:
408 * Package
409 * Integer - Operation Value that may need user confirmation
410 * Return:
411 * Integer - Function Return Code
412 * 0 - Not implemented
413 * 1 - Firmware only
414 * 2 - Blocked for OS by firmware configuration
415 * 3 - Allowed and physically present user required
416 * 4 - Allowed and physically present user not required
417 */
418 dsdt_line(" If(LEqual(Arg2, 8)) /* Function */");
419 dsdt_line(" {");
420 dsdt_line(" Store(DerefOf(Index(Arg3, 0)), Local0)");
421 dsdt_line(" Store(TPFN(Local0), Local1)");
422 dsdt_line(" Return(And(Local1, 7))");
423 dsdt_line(" }");
424 /*
425 * Unknown function
426 */
427 dsdt_line(" Return(Buffer(1)");
428 dsdt_line(" {");
429 dsdt_line(" 0x00");
430 dsdt_line(" })");
431 dsdt_line("}");
432
433 /*
434 * TCG Platform Reset Attack Mitigation
435 */
436 dsdt_line(
437 "If(LEqual(Arg0, ToUUID(\"376054ED-CC13-4675-901C-4756D7F2D45D\"))) /* UUID */");
438 dsdt_line("{");
439 /*
440 * Function 0 - _DSM Query Function
441 * Arguments:
442 * Empty Package
443 * Return:
444 * Buffer - Index field of supported functions
445 */
446 dsdt_line(" If(LEqual(Arg2, 0)) /* Function */");
447 dsdt_line(" {");
448 dsdt_line(" Return(Buffer(1)");
449 dsdt_line(" {");
450 dsdt_line(" 0x03");
451 dsdt_line(" })");
452 dsdt_line(" }");
453 /*
454 * Function 1 - Memory Clear
455 * Arguments:
456 * Package
457 * Integer - Operation Value of the Request
458 * Return:
459 * Integer - Function Return Code
460 * 0 - Success
461 * 1 - General Failure
462 */
463 dsdt_line(" If(LEqual(Arg2, 1)) /* Function */");
464 dsdt_line(" {");
465 dsdt_line(" Store(DerefOf(Index(Arg3, 0)), Local0)");
466 dsdt_line(" Store(Local0, MOVV)");
467 dsdt_line(" Return(0)");
468 dsdt_line(" }");
469 dsdt_line("}");
470
471 return (0);
472 }
473
474 static struct tpm_ppi tpm_ppi_qemu = {
475 .name = TPM_PPI_QEMU_NAME,
476 .init = tpm_ppi_init,
477 .deinit = tpm_ppi_deinit,
478 .write_dsdt_regions = tpm_ppi_write_dsdt_regions,
479 .write_dsdt_dsm = tpm_ppi_write_dsdt_dsm,
480 };
481 TPM_PPI_SET(tpm_ppi_qemu);
482