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/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 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 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 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 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 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