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 0x1000
29
30#define TPM_PPI_FWCFG_FILE "etc/tpm/config"
31
32#define TPM_PPI_QEMU_NAME "qemu"
33
34struct 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;
46static_assert(sizeof(struct tpm_ppi_qemu) <= TPM_PPI_SIZE,
47    "Wrong size of tpm_ppi_qemu");
48
49struct tpm_ppi_fwcfg {
50	uint32_t ppi_address;
51	uint8_t tpm_version;
52	uint8_t ppi_version;
53} __packed;
54
55static int
56tpm_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
88static 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
96static int
97tpm_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, sizeof(*ppi));
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
145err_out:
146	free(fwcfg);
147	free(ppi);
148
149	return (error);
150}
151
152static void
153tpm_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
169static int
170tpm_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
219static int
220tpm_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
468static 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};
475TPM_PPI_SET(tpm_ppi_qemu);
476