Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'tsm-for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/linux

Pull unified attestation reporting from Dan Williams:
"In an ideal world there would be a cross-vendor standard attestation
report format for confidential guests along with a common device
definition to act as the transport.

In the real world the situation ended up with multiple platform
vendors inventing their own attestation report formats with the
SEV-SNP implementation being a first mover to define a custom
sev-guest character device and corresponding ioctl(). Later, this
configfs-tsm proposal intercepted an attempt to add a tdx-guest
character device and a corresponding new ioctl(). It also anticipated
ARM and RISC-V showing up with more chardevs and more ioctls().

The proposal takes for granted that Linux tolerates the vendor report
format differentiation until a standard arrives. From talking with
folks involved, it sounds like that standardization work is unlikely
to resolve anytime soon. It also takes the position that kernfs ABIs
are easier to maintain than ioctl(). The result is a shared configfs
mechanism to return per-vendor report-blobs with the option to later
support a standard when that arrives.

Part of the goal here also is to get the community into the
"uncomfortable, but beneficial to the long term maintainability of the
kernel" state of talking to each other about their differentiation and
opportunities to collaborate. Think of this like the device-driver
equivalent of the common memory-management infrastructure for
confidential-computing being built up in KVM.

As for establishing an "upstream path for cross-vendor
confidential-computing device driver infrastructure" this is something
I want to discuss at Plumbers. At present, the multiple vendor
proposals for assigning devices to confidential computing VMs likely
needs a new dedicated repository and maintainer team, but that is a
discussion for v6.8.

For now, Greg and Thomas have acked this approach and this is passing
is AMD, Intel, and Google tests.

Summary:

- Introduce configfs-tsm as a shared ABI for confidential computing
attestation reports

- Convert sev-guest to additionally support configfs-tsm alongside
its vendor specific ioctl()

- Added signed attestation report retrieval to the tdx-guest driver
forgoing a new vendor specific ioctl()

- Misc cleanups and a new __free() annotation for kvfree()"

* tag 'tsm-for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/linux:
virt: tdx-guest: Add Quote generation support using TSM_REPORTS
virt: sevguest: Add TSM_REPORTS support for SNP_GET_EXT_REPORT
mm/slab: Add __free() support for kvfree
virt: sevguest: Prep for kernel internal get_ext_report()
configfs-tsm: Introduce a shared ABI for attestation reports
virt: coco: Add a coco/Makefile and coco/Kconfig
virt: sevguest: Fix passing a stack buffer as a scatterlist target

+1055 -37
+82
Documentation/ABI/testing/configfs-tsm
··· 1 + What: /sys/kernel/config/tsm/report/$name/inblob 2 + Date: September, 2023 3 + KernelVersion: v6.7 4 + Contact: linux-coco@lists.linux.dev 5 + Description: 6 + (WO) Up to 64 bytes of user specified binary data. For replay 7 + protection this should include a nonce, but the kernel does not 8 + place any restrictions on the content. 9 + 10 + What: /sys/kernel/config/tsm/report/$name/outblob 11 + Date: September, 2023 12 + KernelVersion: v6.7 13 + Contact: linux-coco@lists.linux.dev 14 + Description: 15 + (RO) Binary attestation report generated from @inblob and other 16 + options The format of the report is implementation specific 17 + where the implementation is conveyed via the @provider 18 + attribute. 19 + 20 + What: /sys/kernel/config/tsm/report/$name/auxblob 21 + Date: October, 2023 22 + KernelVersion: v6.7 23 + Contact: linux-coco@lists.linux.dev 24 + Description: 25 + (RO) Optional supplemental data that a TSM may emit, visibility 26 + of this attribute depends on TSM, and may be empty if no 27 + auxiliary data is available. 28 + 29 + When @provider is "sev_guest" this file contains the 30 + "cert_table" from SEV-ES Guest-Hypervisor Communication Block 31 + Standardization v2.03 Section 4.1.8.1 MSG_REPORT_REQ. 32 + https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56421.pdf 33 + 34 + What: /sys/kernel/config/tsm/report/$name/provider 35 + Date: September, 2023 36 + KernelVersion: v6.7 37 + Contact: linux-coco@lists.linux.dev 38 + Description: 39 + (RO) A name for the format-specification of @outblob like 40 + "sev_guest" [1] or "tdx_guest" [2] in the near term, or a 41 + common standard format in the future. 42 + 43 + [1]: SEV Secure Nested Paging Firmware ABI Specification 44 + Revision 1.55 Table 22 45 + https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56860.pdf 46 + 47 + [2]: Intel® Trust Domain Extensions Data Center Attestation 48 + Primitives : Quote Generation Library and Quote Verification 49 + Library Revision 0.8 Appendix 4,5 50 + https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf 51 + 52 + What: /sys/kernel/config/tsm/report/$name/generation 53 + Date: September, 2023 54 + KernelVersion: v6.7 55 + Contact: linux-coco@lists.linux.dev 56 + Description: 57 + (RO) The value in this attribute increments each time @inblob or 58 + any option is written. Userspace can detect conflicts by 59 + checking generation before writing to any attribute and making 60 + sure the number of writes matches expectations after reading 61 + @outblob, or it can prevent conflicts by creating a report 62 + instance per requesting context. 63 + 64 + What: /sys/kernel/config/tsm/report/$name/privlevel 65 + Date: September, 2023 66 + KernelVersion: v6.7 67 + Contact: linux-coco@lists.linux.dev 68 + Description: 69 + (WO) Attribute is visible if a TSM implementation provider 70 + supports the concept of attestation reports for TVMs running at 71 + different privilege levels, like SEV-SNP "VMPL", specify the 72 + privilege level via this attribute. The minimum acceptable 73 + value is conveyed via @privlevel_floor and the maximum 74 + acceptable value is TSM_PRIVLEVEL_MAX (3). 75 + 76 + What: /sys/kernel/config/tsm/report/$name/privlevel_floor 77 + Date: September, 2023 78 + KernelVersion: v6.7 79 + Contact: linux-coco@lists.linux.dev 80 + Description: 81 + (RO) Indicates the minimum permissible value that can be written 82 + to @privlevel.
+8
MAINTAINERS
··· 22058 22058 T: git git://github.com/srcres258/linux-doc.git doc-zh-tw 22059 22059 F: Documentation/translations/zh_TW/ 22060 22060 22061 + TRUSTED SECURITY MODULE (TSM) ATTESTATION REPORTS 22062 + M: Dan Williams <dan.j.williams@intel.com> 22063 + L: linux-coco@lists.linux.dev 22064 + S: Maintained 22065 + F: Documentation/ABI/testing/configfs-tsm 22066 + F: drivers/virt/coco/tsm.c 22067 + F: include/linux/tsm.h 22068 + 22061 22069 TTY LAYER AND SERIAL DRIVERS 22062 22070 M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> 22063 22071 M: Jiri Slaby <jirislaby@kernel.org>
+21
arch/x86/coco/tdx/tdx.c
··· 106 106 } 107 107 EXPORT_SYMBOL_GPL(tdx_mcall_get_report0); 108 108 109 + /** 110 + * tdx_hcall_get_quote() - Wrapper to request TD Quote using GetQuote 111 + * hypercall. 112 + * @buf: Address of the directly mapped shared kernel buffer which 113 + * contains TDREPORT. The same buffer will be used by VMM to 114 + * store the generated TD Quote output. 115 + * @size: size of the tdquote buffer (4KB-aligned). 116 + * 117 + * Refer to section titled "TDG.VP.VMCALL<GetQuote>" in the TDX GHCI 118 + * v1.0 specification for more information on GetQuote hypercall. 119 + * It is used in the TDX guest driver module to get the TD Quote. 120 + * 121 + * Return 0 on success or error code on failure. 122 + */ 123 + u64 tdx_hcall_get_quote(u8 *buf, size_t size) 124 + { 125 + /* Since buf is a shared memory, set the shared (decrypted) bits */ 126 + return _tdx_hypercall(TDVMCALL_GET_QUOTE, cc_mkdec(virt_to_phys(buf)), size, 0, 0); 127 + } 128 + EXPORT_SYMBOL_GPL(tdx_hcall_get_quote); 129 + 109 130 static void __noreturn tdx_panic(const char *msg) 110 131 { 111 132 struct tdx_module_args args = {
+1
arch/x86/include/asm/shared/tdx.h
··· 23 23 24 24 /* TDX hypercall Leaf IDs */ 25 25 #define TDVMCALL_MAP_GPA 0x10001 26 + #define TDVMCALL_GET_QUOTE 0x10002 26 27 #define TDVMCALL_REPORT_FATAL_ERROR 0x10003 27 28 28 29 #define TDVMCALL_STATUS_RETRY 1
+2
arch/x86/include/asm/tdx.h
··· 56 56 57 57 int tdx_mcall_get_report0(u8 *reportdata, u8 *tdreport); 58 58 59 + u64 tdx_hcall_get_quote(u8 *buf, size_t size); 60 + 59 61 #else 60 62 61 63 static inline void tdx_early_init(void) { };
+1 -5
drivers/virt/Kconfig
··· 48 48 49 49 source "drivers/virt/acrn/Kconfig" 50 50 51 - source "drivers/virt/coco/efi_secret/Kconfig" 52 - 53 - source "drivers/virt/coco/sev-guest/Kconfig" 54 - 55 - source "drivers/virt/coco/tdx-guest/Kconfig" 51 + source "drivers/virt/coco/Kconfig" 56 52 57 53 endif
+1 -3
drivers/virt/Makefile
··· 9 9 10 10 obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ 11 11 obj-$(CONFIG_ACRN_HSM) += acrn/ 12 - obj-$(CONFIG_EFI_SECRET) += coco/efi_secret/ 13 - obj-$(CONFIG_SEV_GUEST) += coco/sev-guest/ 14 - obj-$(CONFIG_INTEL_TDX_GUEST) += coco/tdx-guest/ 12 + obj-y += coco/
+14
drivers/virt/coco/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Confidential computing related collateral 4 + # 5 + 6 + config TSM_REPORTS 7 + select CONFIGFS_FS 8 + tristate 9 + 10 + source "drivers/virt/coco/efi_secret/Kconfig" 11 + 12 + source "drivers/virt/coco/sev-guest/Kconfig" 13 + 14 + source "drivers/virt/coco/tdx-guest/Kconfig"
+8
drivers/virt/coco/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Confidential computing related collateral 4 + # 5 + obj-$(CONFIG_TSM_REPORTS) += tsm.o 6 + obj-$(CONFIG_EFI_SECRET) += efi_secret/ 7 + obj-$(CONFIG_SEV_GUEST) += sev-guest/ 8 + obj-$(CONFIG_INTEL_TDX_GUEST) += tdx-guest/
+1
drivers/virt/coco/sev-guest/Kconfig
··· 5 5 select CRYPTO 6 6 select CRYPTO_AEAD2 7 7 select CRYPTO_GCM 8 + select TSM_REPORTS 8 9 help 9 10 SEV-SNP firmware provides the guest a mechanism to communicate with 10 11 the PSP without risk from a malicious hypervisor who wishes to read,
+187 -27
drivers/virt/coco/sev-guest/sev-guest.c
··· 16 16 #include <linux/miscdevice.h> 17 17 #include <linux/set_memory.h> 18 18 #include <linux/fs.h> 19 + #include <linux/tsm.h> 19 20 #include <crypto/aead.h> 20 21 #include <linux/scatterlist.h> 21 22 #include <linux/psp-sev.h> 23 + #include <linux/sockptr.h> 24 + #include <linux/cleanup.h> 25 + #include <linux/uuid.h> 22 26 #include <uapi/linux/sev-guest.h> 23 27 #include <uapi/linux/psp-sev.h> 24 28 ··· 61 57 62 58 struct snp_secrets_page_layout *layout; 63 59 struct snp_req_data input; 60 + union { 61 + struct snp_report_req report; 62 + struct snp_derived_key_req derived_key; 63 + struct snp_ext_report_req ext_report; 64 + } req; 64 65 u32 *os_area_msg_seqno; 65 66 u8 *vmpck; 66 67 }; ··· 479 470 return 0; 480 471 } 481 472 473 + struct snp_req_resp { 474 + sockptr_t req_data; 475 + sockptr_t resp_data; 476 + }; 477 + 482 478 static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg) 483 479 { 484 480 struct snp_guest_crypto *crypto = snp_dev->crypto; 481 + struct snp_report_req *req = &snp_dev->req.report; 485 482 struct snp_report_resp *resp; 486 - struct snp_report_req req; 487 483 int rc, resp_len; 488 484 489 485 lockdep_assert_held(&snp_cmd_mutex); ··· 496 482 if (!arg->req_data || !arg->resp_data) 497 483 return -EINVAL; 498 484 499 - if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req))) 485 + if (copy_from_user(req, (void __user *)arg->req_data, sizeof(*req))) 500 486 return -EFAULT; 501 487 502 488 /* ··· 510 496 return -ENOMEM; 511 497 512 498 rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg, 513 - SNP_MSG_REPORT_REQ, &req, sizeof(req), resp->data, 499 + SNP_MSG_REPORT_REQ, req, sizeof(*req), resp->data, 514 500 resp_len); 515 501 if (rc) 516 502 goto e_free; ··· 525 511 526 512 static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg) 527 513 { 514 + struct snp_derived_key_req *req = &snp_dev->req.derived_key; 528 515 struct snp_guest_crypto *crypto = snp_dev->crypto; 529 516 struct snp_derived_key_resp resp = {0}; 530 - struct snp_derived_key_req req; 531 517 int rc, resp_len; 532 518 /* Response data is 64 bytes and max authsize for GCM is 16 bytes. */ 533 519 u8 buf[64 + 16]; ··· 546 532 if (sizeof(buf) < resp_len) 547 533 return -ENOMEM; 548 534 549 - if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req))) 535 + if (copy_from_user(req, (void __user *)arg->req_data, sizeof(*req))) 550 536 return -EFAULT; 551 537 552 538 rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg, 553 - SNP_MSG_KEY_REQ, &req, sizeof(req), buf, resp_len); 539 + SNP_MSG_KEY_REQ, req, sizeof(*req), buf, resp_len); 554 540 if (rc) 555 541 return rc; 556 542 ··· 564 550 return rc; 565 551 } 566 552 567 - static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg) 553 + static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg, 554 + struct snp_req_resp *io) 555 + 568 556 { 557 + struct snp_ext_report_req *req = &snp_dev->req.ext_report; 569 558 struct snp_guest_crypto *crypto = snp_dev->crypto; 570 - struct snp_ext_report_req req; 571 559 struct snp_report_resp *resp; 572 560 int ret, npages = 0, resp_len; 561 + sockptr_t certs_address; 573 562 574 563 lockdep_assert_held(&snp_cmd_mutex); 575 564 576 - if (!arg->req_data || !arg->resp_data) 565 + if (sockptr_is_null(io->req_data) || sockptr_is_null(io->resp_data)) 577 566 return -EINVAL; 578 567 579 - if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req))) 568 + if (copy_from_sockptr(req, io->req_data, sizeof(*req))) 580 569 return -EFAULT; 581 570 582 - /* userspace does not want certificate data */ 583 - if (!req.certs_len || !req.certs_address) 571 + /* caller does not want certificate data */ 572 + if (!req->certs_len || !req->certs_address) 584 573 goto cmd; 585 574 586 - if (req.certs_len > SEV_FW_BLOB_MAX_SIZE || 587 - !IS_ALIGNED(req.certs_len, PAGE_SIZE)) 575 + if (req->certs_len > SEV_FW_BLOB_MAX_SIZE || 576 + !IS_ALIGNED(req->certs_len, PAGE_SIZE)) 588 577 return -EINVAL; 589 578 590 - if (!access_ok((const void __user *)req.certs_address, req.certs_len)) 591 - return -EFAULT; 579 + if (sockptr_is_kernel(io->resp_data)) { 580 + certs_address = KERNEL_SOCKPTR((void *)req->certs_address); 581 + } else { 582 + certs_address = USER_SOCKPTR((void __user *)req->certs_address); 583 + if (!access_ok(certs_address.user, req->certs_len)) 584 + return -EFAULT; 585 + } 592 586 593 587 /* 594 588 * Initialize the intermediate buffer with all zeros. This buffer ··· 604 582 * the host. If host does not supply any certs in it, then copy 605 583 * zeros to indicate that certificate data was not provided. 606 584 */ 607 - memset(snp_dev->certs_data, 0, req.certs_len); 608 - npages = req.certs_len >> PAGE_SHIFT; 585 + memset(snp_dev->certs_data, 0, req->certs_len); 586 + npages = req->certs_len >> PAGE_SHIFT; 609 587 cmd: 610 588 /* 611 589 * The intermediate response buffer is used while decrypting the ··· 619 597 620 598 snp_dev->input.data_npages = npages; 621 599 ret = handle_guest_request(snp_dev, SVM_VMGEXIT_EXT_GUEST_REQUEST, arg, 622 - SNP_MSG_REPORT_REQ, &req.data, 623 - sizeof(req.data), resp->data, resp_len); 600 + SNP_MSG_REPORT_REQ, &req->data, 601 + sizeof(req->data), resp->data, resp_len); 624 602 625 603 /* If certs length is invalid then copy the returned length */ 626 604 if (arg->vmm_error == SNP_GUEST_VMM_ERR_INVALID_LEN) { 627 - req.certs_len = snp_dev->input.data_npages << PAGE_SHIFT; 605 + req->certs_len = snp_dev->input.data_npages << PAGE_SHIFT; 628 606 629 - if (copy_to_user((void __user *)arg->req_data, &req, sizeof(req))) 607 + if (copy_to_sockptr(io->req_data, req, sizeof(*req))) 630 608 ret = -EFAULT; 631 609 } 632 610 633 611 if (ret) 634 612 goto e_free; 635 613 636 - if (npages && 637 - copy_to_user((void __user *)req.certs_address, snp_dev->certs_data, 638 - req.certs_len)) { 614 + if (npages && copy_to_sockptr(certs_address, snp_dev->certs_data, req->certs_len)) { 639 615 ret = -EFAULT; 640 616 goto e_free; 641 617 } 642 618 643 - if (copy_to_user((void __user *)arg->resp_data, resp, sizeof(*resp))) 619 + if (copy_to_sockptr(io->resp_data, resp, sizeof(*resp))) 644 620 ret = -EFAULT; 645 621 646 622 e_free: ··· 651 631 struct snp_guest_dev *snp_dev = to_snp_dev(file); 652 632 void __user *argp = (void __user *)arg; 653 633 struct snp_guest_request_ioctl input; 634 + struct snp_req_resp io; 654 635 int ret = -ENOTTY; 655 636 656 637 if (copy_from_user(&input, argp, sizeof(input))) ··· 680 659 ret = get_derived_key(snp_dev, &input); 681 660 break; 682 661 case SNP_GET_EXT_REPORT: 683 - ret = get_ext_report(snp_dev, &input); 662 + /* 663 + * As get_ext_report() may be called from the ioctl() path and a 664 + * kernel internal path (configfs-tsm), decorate the passed 665 + * buffers as user pointers. 666 + */ 667 + io.req_data = USER_SOCKPTR((void __user *)input.req_data); 668 + io.resp_data = USER_SOCKPTR((void __user *)input.resp_data); 669 + ret = get_ext_report(snp_dev, &input, &io); 684 670 break; 685 671 default: 686 672 break; ··· 771 743 return key; 772 744 } 773 745 746 + struct snp_msg_report_resp_hdr { 747 + u32 status; 748 + u32 report_size; 749 + u8 rsvd[24]; 750 + }; 751 + 752 + struct snp_msg_cert_entry { 753 + guid_t guid; 754 + u32 offset; 755 + u32 length; 756 + }; 757 + 758 + static int sev_report_new(struct tsm_report *report, void *data) 759 + { 760 + struct snp_msg_cert_entry *cert_table; 761 + struct tsm_desc *desc = &report->desc; 762 + struct snp_guest_dev *snp_dev = data; 763 + struct snp_msg_report_resp_hdr hdr; 764 + const u32 report_size = SZ_4K; 765 + const u32 ext_size = SEV_FW_BLOB_MAX_SIZE; 766 + u32 certs_size, i, size = report_size + ext_size; 767 + int ret; 768 + 769 + if (desc->inblob_len != SNP_REPORT_USER_DATA_SIZE) 770 + return -EINVAL; 771 + 772 + void *buf __free(kvfree) = kvzalloc(size, GFP_KERNEL); 773 + if (!buf) 774 + return -ENOMEM; 775 + 776 + guard(mutex)(&snp_cmd_mutex); 777 + 778 + /* Check if the VMPCK is not empty */ 779 + if (is_vmpck_empty(snp_dev)) { 780 + dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n"); 781 + return -ENOTTY; 782 + } 783 + 784 + cert_table = buf + report_size; 785 + struct snp_ext_report_req ext_req = { 786 + .data = { .vmpl = desc->privlevel }, 787 + .certs_address = (__u64)cert_table, 788 + .certs_len = ext_size, 789 + }; 790 + memcpy(&ext_req.data.user_data, desc->inblob, desc->inblob_len); 791 + 792 + struct snp_guest_request_ioctl input = { 793 + .msg_version = 1, 794 + .req_data = (__u64)&ext_req, 795 + .resp_data = (__u64)buf, 796 + .exitinfo2 = 0xff, 797 + }; 798 + struct snp_req_resp io = { 799 + .req_data = KERNEL_SOCKPTR(&ext_req), 800 + .resp_data = KERNEL_SOCKPTR(buf), 801 + }; 802 + 803 + ret = get_ext_report(snp_dev, &input, &io); 804 + if (ret) 805 + return ret; 806 + 807 + memcpy(&hdr, buf, sizeof(hdr)); 808 + if (hdr.status == SEV_RET_INVALID_PARAM) 809 + return -EINVAL; 810 + if (hdr.status == SEV_RET_INVALID_KEY) 811 + return -EINVAL; 812 + if (hdr.status) 813 + return -ENXIO; 814 + if ((hdr.report_size + sizeof(hdr)) > report_size) 815 + return -ENOMEM; 816 + 817 + void *rbuf __free(kvfree) = kvzalloc(hdr.report_size, GFP_KERNEL); 818 + if (!rbuf) 819 + return -ENOMEM; 820 + 821 + memcpy(rbuf, buf + sizeof(hdr), hdr.report_size); 822 + report->outblob = no_free_ptr(rbuf); 823 + report->outblob_len = hdr.report_size; 824 + 825 + certs_size = 0; 826 + for (i = 0; i < ext_size / sizeof(struct snp_msg_cert_entry); i++) { 827 + struct snp_msg_cert_entry *ent = &cert_table[i]; 828 + 829 + if (guid_is_null(&ent->guid) && !ent->offset && !ent->length) 830 + break; 831 + certs_size = max(certs_size, ent->offset + ent->length); 832 + } 833 + 834 + /* Suspicious that the response populated entries without populating size */ 835 + if (!certs_size && i) 836 + dev_warn_ratelimited(snp_dev->dev, "certificate slots conveyed without size\n"); 837 + 838 + /* No certs to report */ 839 + if (!certs_size) 840 + return 0; 841 + 842 + /* Suspicious that the certificate blob size contract was violated 843 + */ 844 + if (certs_size > ext_size) { 845 + dev_warn_ratelimited(snp_dev->dev, "certificate data truncated\n"); 846 + certs_size = ext_size; 847 + } 848 + 849 + void *cbuf __free(kvfree) = kvzalloc(certs_size, GFP_KERNEL); 850 + if (!cbuf) 851 + return -ENOMEM; 852 + 853 + memcpy(cbuf, cert_table, certs_size); 854 + report->auxblob = no_free_ptr(cbuf); 855 + report->auxblob_len = certs_size; 856 + 857 + return 0; 858 + } 859 + 860 + static const struct tsm_ops sev_tsm_ops = { 861 + .name = KBUILD_MODNAME, 862 + .report_new = sev_report_new, 863 + }; 864 + 865 + static void unregister_sev_tsm(void *data) 866 + { 867 + tsm_unregister(&sev_tsm_ops); 868 + } 869 + 774 870 static int __init sev_guest_probe(struct platform_device *pdev) 775 871 { 776 872 struct snp_secrets_page_layout *layout; ··· 967 815 snp_dev->input.req_gpa = __pa(snp_dev->request); 968 816 snp_dev->input.resp_gpa = __pa(snp_dev->response); 969 817 snp_dev->input.data_gpa = __pa(snp_dev->certs_data); 818 + 819 + ret = tsm_register(&sev_tsm_ops, snp_dev, &tsm_report_extra_type); 820 + if (ret) 821 + goto e_free_cert_data; 822 + 823 + ret = devm_add_action_or_reset(&pdev->dev, unregister_sev_tsm, NULL); 824 + if (ret) 825 + goto e_free_cert_data; 970 826 971 827 ret = misc_register(misc); 972 828 if (ret)
+1
drivers/virt/coco/tdx-guest/Kconfig
··· 1 1 config TDX_GUEST_DRIVER 2 2 tristate "TDX Guest driver" 3 3 depends on INTEL_TDX_GUEST 4 + select TSM_REPORTS 4 5 help 5 6 The driver provides userspace interface to communicate with 6 7 the TDX module to request the TDX guest details like attestation
+228 -1
drivers/virt/coco/tdx-guest/tdx-guest.c
··· 12 12 #include <linux/mod_devicetable.h> 13 13 #include <linux/string.h> 14 14 #include <linux/uaccess.h> 15 + #include <linux/set_memory.h> 16 + #include <linux/io.h> 17 + #include <linux/delay.h> 18 + #include <linux/tsm.h> 19 + #include <linux/sizes.h> 15 20 16 21 #include <uapi/linux/tdx-guest.h> 17 22 18 23 #include <asm/cpu_device_id.h> 19 24 #include <asm/tdx.h> 25 + 26 + /* 27 + * Intel's SGX QE implementation generally uses Quote size less 28 + * than 8K (2K Quote data + ~5K of certificate blob). 29 + */ 30 + #define GET_QUOTE_BUF_SIZE SZ_8K 31 + 32 + #define GET_QUOTE_CMD_VER 1 33 + 34 + /* TDX GetQuote status codes */ 35 + #define GET_QUOTE_SUCCESS 0 36 + #define GET_QUOTE_IN_FLIGHT 0xffffffffffffffff 37 + 38 + /* struct tdx_quote_buf: Format of Quote request buffer. 39 + * @version: Quote format version, filled by TD. 40 + * @status: Status code of Quote request, filled by VMM. 41 + * @in_len: Length of TDREPORT, filled by TD. 42 + * @out_len: Length of Quote data, filled by VMM. 43 + * @data: Quote data on output or TDREPORT on input. 44 + * 45 + * More details of Quote request buffer can be found in TDX 46 + * Guest-Host Communication Interface (GHCI) for Intel TDX 1.0, 47 + * section titled "TDG.VP.VMCALL<GetQuote>" 48 + */ 49 + struct tdx_quote_buf { 50 + u64 version; 51 + u64 status; 52 + u32 in_len; 53 + u32 out_len; 54 + u8 data[]; 55 + }; 56 + 57 + /* Quote data buffer */ 58 + static void *quote_data; 59 + 60 + /* Lock to streamline quote requests */ 61 + static DEFINE_MUTEX(quote_lock); 62 + 63 + /* 64 + * GetQuote request timeout in seconds. Expect that 30 seconds 65 + * is enough time for QE to respond to any Quote requests. 66 + */ 67 + static u32 getquote_timeout = 30; 20 68 21 69 static long tdx_get_report0(struct tdx_report_req __user *req) 22 70 { ··· 101 53 return ret; 102 54 } 103 55 56 + static void free_quote_buf(void *buf) 57 + { 58 + size_t len = PAGE_ALIGN(GET_QUOTE_BUF_SIZE); 59 + unsigned int count = len >> PAGE_SHIFT; 60 + 61 + if (set_memory_encrypted((unsigned long)buf, count)) { 62 + pr_err("Failed to restore encryption mask for Quote buffer, leak it\n"); 63 + return; 64 + } 65 + 66 + free_pages_exact(buf, len); 67 + } 68 + 69 + static void *alloc_quote_buf(void) 70 + { 71 + size_t len = PAGE_ALIGN(GET_QUOTE_BUF_SIZE); 72 + unsigned int count = len >> PAGE_SHIFT; 73 + void *addr; 74 + 75 + addr = alloc_pages_exact(len, GFP_KERNEL | __GFP_ZERO); 76 + if (!addr) 77 + return NULL; 78 + 79 + if (set_memory_decrypted((unsigned long)addr, count)) { 80 + free_pages_exact(addr, len); 81 + return NULL; 82 + } 83 + 84 + return addr; 85 + } 86 + 87 + /* 88 + * wait_for_quote_completion() - Wait for Quote request completion 89 + * @quote_buf: Address of Quote buffer. 90 + * @timeout: Timeout in seconds to wait for the Quote generation. 91 + * 92 + * As per TDX GHCI v1.0 specification, sec titled "TDG.VP.VMCALL<GetQuote>", 93 + * the status field in the Quote buffer will be set to GET_QUOTE_IN_FLIGHT 94 + * while VMM processes the GetQuote request, and will change it to success 95 + * or error code after processing is complete. So wait till the status 96 + * changes from GET_QUOTE_IN_FLIGHT or the request being timed out. 97 + */ 98 + static int wait_for_quote_completion(struct tdx_quote_buf *quote_buf, u32 timeout) 99 + { 100 + int i = 0; 101 + 102 + /* 103 + * Quote requests usually take a few seconds to complete, so waking up 104 + * once per second to recheck the status is fine for this use case. 105 + */ 106 + while (quote_buf->status == GET_QUOTE_IN_FLIGHT && i++ < timeout) { 107 + if (msleep_interruptible(MSEC_PER_SEC)) 108 + return -EINTR; 109 + } 110 + 111 + return (i == timeout) ? -ETIMEDOUT : 0; 112 + } 113 + 114 + static int tdx_report_new(struct tsm_report *report, void *data) 115 + { 116 + u8 *buf, *reportdata = NULL, *tdreport = NULL; 117 + struct tdx_quote_buf *quote_buf = quote_data; 118 + struct tsm_desc *desc = &report->desc; 119 + int ret; 120 + u64 err; 121 + 122 + /* TODO: switch to guard(mutex_intr) */ 123 + if (mutex_lock_interruptible(&quote_lock)) 124 + return -EINTR; 125 + 126 + /* 127 + * If the previous request is timedout or interrupted, and the 128 + * Quote buf status is still in GET_QUOTE_IN_FLIGHT (owned by 129 + * VMM), don't permit any new request. 130 + */ 131 + if (quote_buf->status == GET_QUOTE_IN_FLIGHT) { 132 + ret = -EBUSY; 133 + goto done; 134 + } 135 + 136 + if (desc->inblob_len != TDX_REPORTDATA_LEN) { 137 + ret = -EINVAL; 138 + goto done; 139 + } 140 + 141 + reportdata = kmalloc(TDX_REPORTDATA_LEN, GFP_KERNEL); 142 + if (!reportdata) { 143 + ret = -ENOMEM; 144 + goto done; 145 + } 146 + 147 + tdreport = kzalloc(TDX_REPORT_LEN, GFP_KERNEL); 148 + if (!tdreport) { 149 + ret = -ENOMEM; 150 + goto done; 151 + } 152 + 153 + memcpy(reportdata, desc->inblob, desc->inblob_len); 154 + 155 + /* Generate TDREPORT0 using "TDG.MR.REPORT" TDCALL */ 156 + ret = tdx_mcall_get_report0(reportdata, tdreport); 157 + if (ret) { 158 + pr_err("GetReport call failed\n"); 159 + goto done; 160 + } 161 + 162 + memset(quote_data, 0, GET_QUOTE_BUF_SIZE); 163 + 164 + /* Update Quote buffer header */ 165 + quote_buf->version = GET_QUOTE_CMD_VER; 166 + quote_buf->in_len = TDX_REPORT_LEN; 167 + 168 + memcpy(quote_buf->data, tdreport, TDX_REPORT_LEN); 169 + 170 + err = tdx_hcall_get_quote(quote_data, GET_QUOTE_BUF_SIZE); 171 + if (err) { 172 + pr_err("GetQuote hypercall failed, status:%llx\n", err); 173 + ret = -EIO; 174 + goto done; 175 + } 176 + 177 + ret = wait_for_quote_completion(quote_buf, getquote_timeout); 178 + if (ret) { 179 + pr_err("GetQuote request timedout\n"); 180 + goto done; 181 + } 182 + 183 + buf = kvmemdup(quote_buf->data, quote_buf->out_len, GFP_KERNEL); 184 + if (!buf) { 185 + ret = -ENOMEM; 186 + goto done; 187 + } 188 + 189 + report->outblob = buf; 190 + report->outblob_len = quote_buf->out_len; 191 + 192 + /* 193 + * TODO: parse the PEM-formatted cert chain out of the quote buffer when 194 + * provided 195 + */ 196 + done: 197 + mutex_unlock(&quote_lock); 198 + kfree(reportdata); 199 + kfree(tdreport); 200 + 201 + return ret; 202 + } 203 + 104 204 static long tdx_guest_ioctl(struct file *file, unsigned int cmd, 105 205 unsigned long arg) 106 206 { ··· 278 82 }; 279 83 MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); 280 84 85 + static const struct tsm_ops tdx_tsm_ops = { 86 + .name = KBUILD_MODNAME, 87 + .report_new = tdx_report_new, 88 + }; 89 + 281 90 static int __init tdx_guest_init(void) 282 91 { 92 + int ret; 93 + 283 94 if (!x86_match_cpu(tdx_guest_ids)) 284 95 return -ENODEV; 285 96 286 - return misc_register(&tdx_misc_dev); 97 + ret = misc_register(&tdx_misc_dev); 98 + if (ret) 99 + return ret; 100 + 101 + quote_data = alloc_quote_buf(); 102 + if (!quote_data) { 103 + pr_err("Failed to allocate Quote buffer\n"); 104 + ret = -ENOMEM; 105 + goto free_misc; 106 + } 107 + 108 + ret = tsm_register(&tdx_tsm_ops, NULL, NULL); 109 + if (ret) 110 + goto free_quote; 111 + 112 + return 0; 113 + 114 + free_quote: 115 + free_quote_buf(quote_data); 116 + free_misc: 117 + misc_deregister(&tdx_misc_dev); 118 + 119 + return ret; 287 120 } 288 121 module_init(tdx_guest_init); 289 122 290 123 static void __exit tdx_guest_exit(void) 291 124 { 125 + tsm_unregister(&tdx_tsm_ops); 126 + free_quote_buf(quote_data); 292 127 misc_deregister(&tdx_misc_dev); 293 128 } 294 129 module_exit(tdx_guest_exit);
+425
drivers/virt/coco/tsm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Copyright(c) 2023 Intel Corporation. All rights reserved. */ 3 + 4 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 5 + 6 + #include <linux/tsm.h> 7 + #include <linux/err.h> 8 + #include <linux/slab.h> 9 + #include <linux/rwsem.h> 10 + #include <linux/string.h> 11 + #include <linux/module.h> 12 + #include <linux/cleanup.h> 13 + #include <linux/configfs.h> 14 + 15 + static struct tsm_provider { 16 + const struct tsm_ops *ops; 17 + const struct config_item_type *type; 18 + void *data; 19 + } provider; 20 + static DECLARE_RWSEM(tsm_rwsem); 21 + 22 + /** 23 + * DOC: Trusted Security Module (TSM) Attestation Report Interface 24 + * 25 + * The TSM report interface is a common provider of blobs that facilitate 26 + * attestation of a TVM (confidential computing guest) by an attestation 27 + * service. A TSM report combines a user-defined blob (likely a public-key with 28 + * a nonce for a key-exchange protocol) with a signed attestation report. That 29 + * combined blob is then used to obtain secrets provided by an agent that can 30 + * validate the attestation report. The expectation is that this interface is 31 + * invoked infrequently, however configfs allows for multiple agents to 32 + * own their own report generation instances to generate reports as 33 + * often as needed. 34 + * 35 + * The attestation report format is TSM provider specific, when / if a standard 36 + * materializes that can be published instead of the vendor layout. Until then 37 + * the 'provider' attribute indicates the format of 'outblob', and optionally 38 + * 'auxblob'. 39 + */ 40 + 41 + struct tsm_report_state { 42 + struct tsm_report report; 43 + unsigned long write_generation; 44 + unsigned long read_generation; 45 + struct config_item cfg; 46 + }; 47 + 48 + enum tsm_data_select { 49 + TSM_REPORT, 50 + TSM_CERTS, 51 + }; 52 + 53 + static struct tsm_report *to_tsm_report(struct config_item *cfg) 54 + { 55 + struct tsm_report_state *state = 56 + container_of(cfg, struct tsm_report_state, cfg); 57 + 58 + return &state->report; 59 + } 60 + 61 + static struct tsm_report_state *to_state(struct tsm_report *report) 62 + { 63 + return container_of(report, struct tsm_report_state, report); 64 + } 65 + 66 + static int try_advance_write_generation(struct tsm_report *report) 67 + { 68 + struct tsm_report_state *state = to_state(report); 69 + 70 + lockdep_assert_held_write(&tsm_rwsem); 71 + 72 + /* 73 + * Malicious or broken userspace has written enough times for 74 + * read_generation == write_generation by modular arithmetic without an 75 + * interim read. Stop accepting updates until the current report 76 + * configuration is read. 77 + */ 78 + if (state->write_generation == state->read_generation - 1) 79 + return -EBUSY; 80 + state->write_generation++; 81 + return 0; 82 + } 83 + 84 + static ssize_t tsm_report_privlevel_store(struct config_item *cfg, 85 + const char *buf, size_t len) 86 + { 87 + struct tsm_report *report = to_tsm_report(cfg); 88 + unsigned int val; 89 + int rc; 90 + 91 + rc = kstrtouint(buf, 0, &val); 92 + if (rc) 93 + return rc; 94 + 95 + /* 96 + * The valid privilege levels that a TSM might accept, if it accepts a 97 + * privilege level setting at all, are a max of TSM_PRIVLEVEL_MAX (see 98 + * SEV-SNP GHCB) and a minimum of a TSM selected floor value no less 99 + * than 0. 100 + */ 101 + if (provider.ops->privlevel_floor > val || val > TSM_PRIVLEVEL_MAX) 102 + return -EINVAL; 103 + 104 + guard(rwsem_write)(&tsm_rwsem); 105 + rc = try_advance_write_generation(report); 106 + if (rc) 107 + return rc; 108 + report->desc.privlevel = val; 109 + 110 + return len; 111 + } 112 + CONFIGFS_ATTR_WO(tsm_report_, privlevel); 113 + 114 + static ssize_t tsm_report_privlevel_floor_show(struct config_item *cfg, 115 + char *buf) 116 + { 117 + guard(rwsem_read)(&tsm_rwsem); 118 + return sysfs_emit(buf, "%u\n", provider.ops->privlevel_floor); 119 + } 120 + CONFIGFS_ATTR_RO(tsm_report_, privlevel_floor); 121 + 122 + static ssize_t tsm_report_inblob_write(struct config_item *cfg, 123 + const void *buf, size_t count) 124 + { 125 + struct tsm_report *report = to_tsm_report(cfg); 126 + int rc; 127 + 128 + guard(rwsem_write)(&tsm_rwsem); 129 + rc = try_advance_write_generation(report); 130 + if (rc) 131 + return rc; 132 + 133 + report->desc.inblob_len = count; 134 + memcpy(report->desc.inblob, buf, count); 135 + return count; 136 + } 137 + CONFIGFS_BIN_ATTR_WO(tsm_report_, inblob, NULL, TSM_INBLOB_MAX); 138 + 139 + static ssize_t tsm_report_generation_show(struct config_item *cfg, char *buf) 140 + { 141 + struct tsm_report *report = to_tsm_report(cfg); 142 + struct tsm_report_state *state = to_state(report); 143 + 144 + guard(rwsem_read)(&tsm_rwsem); 145 + return sysfs_emit(buf, "%lu\n", state->write_generation); 146 + } 147 + CONFIGFS_ATTR_RO(tsm_report_, generation); 148 + 149 + static ssize_t tsm_report_provider_show(struct config_item *cfg, char *buf) 150 + { 151 + guard(rwsem_read)(&tsm_rwsem); 152 + return sysfs_emit(buf, "%s\n", provider.ops->name); 153 + } 154 + CONFIGFS_ATTR_RO(tsm_report_, provider); 155 + 156 + static ssize_t __read_report(struct tsm_report *report, void *buf, size_t count, 157 + enum tsm_data_select select) 158 + { 159 + loff_t offset = 0; 160 + ssize_t len; 161 + u8 *out; 162 + 163 + if (select == TSM_REPORT) { 164 + out = report->outblob; 165 + len = report->outblob_len; 166 + } else { 167 + out = report->auxblob; 168 + len = report->auxblob_len; 169 + } 170 + 171 + /* 172 + * Recall that a NULL @buf is configfs requesting the size of 173 + * the buffer. 174 + */ 175 + if (!buf) 176 + return len; 177 + return memory_read_from_buffer(buf, count, &offset, out, len); 178 + } 179 + 180 + static ssize_t read_cached_report(struct tsm_report *report, void *buf, 181 + size_t count, enum tsm_data_select select) 182 + { 183 + struct tsm_report_state *state = to_state(report); 184 + 185 + guard(rwsem_read)(&tsm_rwsem); 186 + if (!report->desc.inblob_len) 187 + return -EINVAL; 188 + 189 + /* 190 + * A given TSM backend always fills in ->outblob regardless of 191 + * whether the report includes an auxblob or not. 192 + */ 193 + if (!report->outblob || 194 + state->read_generation != state->write_generation) 195 + return -EWOULDBLOCK; 196 + 197 + return __read_report(report, buf, count, select); 198 + } 199 + 200 + static ssize_t tsm_report_read(struct tsm_report *report, void *buf, 201 + size_t count, enum tsm_data_select select) 202 + { 203 + struct tsm_report_state *state = to_state(report); 204 + const struct tsm_ops *ops; 205 + ssize_t rc; 206 + 207 + /* try to read from the existing report if present and valid... */ 208 + rc = read_cached_report(report, buf, count, select); 209 + if (rc >= 0 || rc != -EWOULDBLOCK) 210 + return rc; 211 + 212 + /* slow path, report may need to be regenerated... */ 213 + guard(rwsem_write)(&tsm_rwsem); 214 + ops = provider.ops; 215 + if (!ops) 216 + return -ENOTTY; 217 + if (!report->desc.inblob_len) 218 + return -EINVAL; 219 + 220 + /* did another thread already generate this report? */ 221 + if (report->outblob && 222 + state->read_generation == state->write_generation) 223 + goto out; 224 + 225 + kvfree(report->outblob); 226 + kvfree(report->auxblob); 227 + report->outblob = NULL; 228 + report->auxblob = NULL; 229 + rc = ops->report_new(report, provider.data); 230 + if (rc < 0) 231 + return rc; 232 + state->read_generation = state->write_generation; 233 + out: 234 + return __read_report(report, buf, count, select); 235 + } 236 + 237 + static ssize_t tsm_report_outblob_read(struct config_item *cfg, void *buf, 238 + size_t count) 239 + { 240 + struct tsm_report *report = to_tsm_report(cfg); 241 + 242 + return tsm_report_read(report, buf, count, TSM_REPORT); 243 + } 244 + CONFIGFS_BIN_ATTR_RO(tsm_report_, outblob, NULL, TSM_OUTBLOB_MAX); 245 + 246 + static ssize_t tsm_report_auxblob_read(struct config_item *cfg, void *buf, 247 + size_t count) 248 + { 249 + struct tsm_report *report = to_tsm_report(cfg); 250 + 251 + return tsm_report_read(report, buf, count, TSM_CERTS); 252 + } 253 + CONFIGFS_BIN_ATTR_RO(tsm_report_, auxblob, NULL, TSM_OUTBLOB_MAX); 254 + 255 + #define TSM_DEFAULT_ATTRS() \ 256 + &tsm_report_attr_generation, \ 257 + &tsm_report_attr_provider 258 + 259 + static struct configfs_attribute *tsm_report_attrs[] = { 260 + TSM_DEFAULT_ATTRS(), 261 + NULL, 262 + }; 263 + 264 + static struct configfs_attribute *tsm_report_extra_attrs[] = { 265 + TSM_DEFAULT_ATTRS(), 266 + &tsm_report_attr_privlevel, 267 + &tsm_report_attr_privlevel_floor, 268 + NULL, 269 + }; 270 + 271 + #define TSM_DEFAULT_BIN_ATTRS() \ 272 + &tsm_report_attr_inblob, \ 273 + &tsm_report_attr_outblob 274 + 275 + static struct configfs_bin_attribute *tsm_report_bin_attrs[] = { 276 + TSM_DEFAULT_BIN_ATTRS(), 277 + NULL, 278 + }; 279 + 280 + static struct configfs_bin_attribute *tsm_report_bin_extra_attrs[] = { 281 + TSM_DEFAULT_BIN_ATTRS(), 282 + &tsm_report_attr_auxblob, 283 + NULL, 284 + }; 285 + 286 + static void tsm_report_item_release(struct config_item *cfg) 287 + { 288 + struct tsm_report *report = to_tsm_report(cfg); 289 + struct tsm_report_state *state = to_state(report); 290 + 291 + kvfree(report->auxblob); 292 + kvfree(report->outblob); 293 + kfree(state); 294 + } 295 + 296 + static struct configfs_item_operations tsm_report_item_ops = { 297 + .release = tsm_report_item_release, 298 + }; 299 + 300 + const struct config_item_type tsm_report_default_type = { 301 + .ct_owner = THIS_MODULE, 302 + .ct_bin_attrs = tsm_report_bin_attrs, 303 + .ct_attrs = tsm_report_attrs, 304 + .ct_item_ops = &tsm_report_item_ops, 305 + }; 306 + EXPORT_SYMBOL_GPL(tsm_report_default_type); 307 + 308 + const struct config_item_type tsm_report_extra_type = { 309 + .ct_owner = THIS_MODULE, 310 + .ct_bin_attrs = tsm_report_bin_extra_attrs, 311 + .ct_attrs = tsm_report_extra_attrs, 312 + .ct_item_ops = &tsm_report_item_ops, 313 + }; 314 + EXPORT_SYMBOL_GPL(tsm_report_extra_type); 315 + 316 + static struct config_item *tsm_report_make_item(struct config_group *group, 317 + const char *name) 318 + { 319 + struct tsm_report_state *state; 320 + 321 + guard(rwsem_read)(&tsm_rwsem); 322 + if (!provider.ops) 323 + return ERR_PTR(-ENXIO); 324 + 325 + state = kzalloc(sizeof(*state), GFP_KERNEL); 326 + if (!state) 327 + return ERR_PTR(-ENOMEM); 328 + 329 + config_item_init_type_name(&state->cfg, name, provider.type); 330 + return &state->cfg; 331 + } 332 + 333 + static struct configfs_group_operations tsm_report_group_ops = { 334 + .make_item = tsm_report_make_item, 335 + }; 336 + 337 + static const struct config_item_type tsm_reports_type = { 338 + .ct_owner = THIS_MODULE, 339 + .ct_group_ops = &tsm_report_group_ops, 340 + }; 341 + 342 + static const struct config_item_type tsm_root_group_type = { 343 + .ct_owner = THIS_MODULE, 344 + }; 345 + 346 + static struct configfs_subsystem tsm_configfs = { 347 + .su_group = { 348 + .cg_item = { 349 + .ci_namebuf = "tsm", 350 + .ci_type = &tsm_root_group_type, 351 + }, 352 + }, 353 + .su_mutex = __MUTEX_INITIALIZER(tsm_configfs.su_mutex), 354 + }; 355 + 356 + int tsm_register(const struct tsm_ops *ops, void *priv, 357 + const struct config_item_type *type) 358 + { 359 + const struct tsm_ops *conflict; 360 + 361 + if (!type) 362 + type = &tsm_report_default_type; 363 + if (!(type == &tsm_report_default_type || type == &tsm_report_extra_type)) 364 + return -EINVAL; 365 + 366 + guard(rwsem_write)(&tsm_rwsem); 367 + conflict = provider.ops; 368 + if (conflict) { 369 + pr_err("\"%s\" ops already registered\n", conflict->name); 370 + return -EBUSY; 371 + } 372 + 373 + provider.ops = ops; 374 + provider.data = priv; 375 + provider.type = type; 376 + return 0; 377 + } 378 + EXPORT_SYMBOL_GPL(tsm_register); 379 + 380 + int tsm_unregister(const struct tsm_ops *ops) 381 + { 382 + guard(rwsem_write)(&tsm_rwsem); 383 + if (ops != provider.ops) 384 + return -EBUSY; 385 + provider.ops = NULL; 386 + provider.data = NULL; 387 + provider.type = NULL; 388 + return 0; 389 + } 390 + EXPORT_SYMBOL_GPL(tsm_unregister); 391 + 392 + static struct config_group *tsm_report_group; 393 + 394 + static int __init tsm_init(void) 395 + { 396 + struct config_group *root = &tsm_configfs.su_group; 397 + struct config_group *tsm; 398 + int rc; 399 + 400 + config_group_init(root); 401 + rc = configfs_register_subsystem(&tsm_configfs); 402 + if (rc) 403 + return rc; 404 + 405 + tsm = configfs_register_default_group(root, "report", 406 + &tsm_reports_type); 407 + if (IS_ERR(tsm)) { 408 + configfs_unregister_subsystem(&tsm_configfs); 409 + return PTR_ERR(tsm); 410 + } 411 + tsm_report_group = tsm; 412 + 413 + return 0; 414 + } 415 + module_init(tsm_init); 416 + 417 + static void __exit tsm_exit(void) 418 + { 419 + configfs_unregister_default_group(tsm_report_group); 420 + configfs_unregister_subsystem(&tsm_configfs); 421 + } 422 + module_exit(tsm_exit); 423 + 424 + MODULE_LICENSE("GPL"); 425 + MODULE_DESCRIPTION("Provide Trusted Security Module attestation reports via configfs");
+2
include/linux/slab.h
··· 764 764 extern void *kvrealloc(const void *p, size_t oldsize, size_t newsize, gfp_t flags) 765 765 __realloc_size(3); 766 766 extern void kvfree(const void *addr); 767 + DEFINE_FREE(kvfree, void *, if (_T) kvfree(_T)) 768 + 767 769 extern void kvfree_sensitive(const void *addr, size_t len); 768 770 769 771 unsigned int kmem_cache_size(struct kmem_cache *s);
+69
include/linux/tsm.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef __TSM_H 3 + #define __TSM_H 4 + 5 + #include <linux/sizes.h> 6 + #include <linux/types.h> 7 + 8 + #define TSM_INBLOB_MAX 64 9 + #define TSM_OUTBLOB_MAX SZ_32K 10 + 11 + /* 12 + * Privilege level is a nested permission concept to allow confidential 13 + * guests to partition address space, 4-levels are supported. 14 + */ 15 + #define TSM_PRIVLEVEL_MAX 3 16 + 17 + /** 18 + * struct tsm_desc - option descriptor for generating tsm report blobs 19 + * @privlevel: optional privilege level to associate with @outblob 20 + * @inblob_len: sizeof @inblob 21 + * @inblob: arbitrary input data 22 + */ 23 + struct tsm_desc { 24 + unsigned int privlevel; 25 + size_t inblob_len; 26 + u8 inblob[TSM_INBLOB_MAX]; 27 + }; 28 + 29 + /** 30 + * struct tsm_report - track state of report generation relative to options 31 + * @desc: input parameters to @report_new() 32 + * @outblob_len: sizeof(@outblob) 33 + * @outblob: generated evidence to provider to the attestation agent 34 + * @auxblob_len: sizeof(@auxblob) 35 + * @auxblob: (optional) auxiliary data to the report (e.g. certificate data) 36 + */ 37 + struct tsm_report { 38 + struct tsm_desc desc; 39 + size_t outblob_len; 40 + u8 *outblob; 41 + size_t auxblob_len; 42 + u8 *auxblob; 43 + }; 44 + 45 + /** 46 + * struct tsm_ops - attributes and operations for tsm instances 47 + * @name: tsm id reflected in /sys/kernel/config/tsm/report/$report/provider 48 + * @privlevel_floor: convey base privlevel for nested scenarios 49 + * @report_new: Populate @report with the report blob and auxblob 50 + * (optional), return 0 on successful population, or -errno otherwise 51 + * 52 + * Implementation specific ops, only one is expected to be registered at 53 + * a time i.e. only one of "sev-guest", "tdx-guest", etc. 54 + */ 55 + struct tsm_ops { 56 + const char *name; 57 + const unsigned int privlevel_floor; 58 + int (*report_new)(struct tsm_report *report, void *data); 59 + }; 60 + 61 + extern const struct config_item_type tsm_report_default_type; 62 + 63 + /* publish @privlevel, @privlevel_floor, and @auxblob attributes */ 64 + extern const struct config_item_type tsm_report_extra_type; 65 + 66 + int tsm_register(const struct tsm_ops *ops, void *priv, 67 + const struct config_item_type *type); 68 + int tsm_unregister(const struct tsm_ops *ops); 69 + #endif /* __TSM_H */
+1
include/uapi/linux/psp-sev.h
··· 68 68 SEV_RET_INVALID_PARAM, 69 69 SEV_RET_RESOURCE_LIMIT, 70 70 SEV_RET_SECURE_DATA_INVALID, 71 + SEV_RET_INVALID_KEY = 0x27, 71 72 SEV_RET_MAX, 72 73 } sev_ret_code; 73 74
+3 -1
include/uapi/linux/sev-guest.h
··· 14 14 15 15 #include <linux/types.h> 16 16 17 + #define SNP_REPORT_USER_DATA_SIZE 64 18 + 17 19 struct snp_report_req { 18 20 /* user data that should be included in the report */ 19 - __u8 user_data[64]; 21 + __u8 user_data[SNP_REPORT_USER_DATA_SIZE]; 20 22 21 23 /* The vmpl level to be included in the report */ 22 24 __u32 vmpl;