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