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 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 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 145 err_out: 146 free(fwcfg); 147 free(ppi); 148 149 return (error); 150 } 151 152 static void 153 tpm_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 169 static int 170 tpm_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 219 static int 220 tpm_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 468 static 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 }; 475 TPM_PPI_SET(tpm_ppi_qemu); 476