xref: /illumos-gate/usr/src/cmd/bhyve/common/tpm_ppi_qemu.c (revision 5c4a5fe16715fb423db76577a6883b5bbecdbe45)
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