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