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