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