|
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>
|
22 | 23 | #include <linux/sockptr.h>
|
| 24 | +#include <linux/cleanup.h> |
| 25 | +#include <linux/uuid.h> |
23 | 26 | #include <uapi/linux/sev-guest.h>
|
24 | 27 | #include <uapi/linux/psp-sev.h>
|
25 | 28 |
|
@@ -768,6 +771,130 @@ static u8 *get_vmpck(int id, struct snp_secrets_page_layout *layout, u32 **seqno
|
768 | 771 | return key;
|
769 | 772 | }
|
770 | 773 |
|
| 774 | +struct snp_msg_report_resp_hdr { |
| 775 | + u32 status; |
| 776 | + u32 report_size; |
| 777 | + u8 rsvd[24]; |
| 778 | +}; |
| 779 | + |
| 780 | +struct snp_msg_cert_entry { |
| 781 | + guid_t guid; |
| 782 | + u32 offset; |
| 783 | + u32 length; |
| 784 | +}; |
| 785 | + |
| 786 | +static int sev_report_new(struct tsm_report *report, void *data) |
| 787 | +{ |
| 788 | + struct snp_msg_cert_entry *cert_table; |
| 789 | + struct tsm_desc *desc = &report->desc; |
| 790 | + struct snp_guest_dev *snp_dev = data; |
| 791 | + struct snp_msg_report_resp_hdr hdr; |
| 792 | + const u32 report_size = SZ_4K; |
| 793 | + const u32 ext_size = SEV_FW_BLOB_MAX_SIZE; |
| 794 | + u32 certs_size, i, size = report_size + ext_size; |
| 795 | + int ret; |
| 796 | + |
| 797 | + if (desc->inblob_len != SNP_REPORT_USER_DATA_SIZE) |
| 798 | + return -EINVAL; |
| 799 | + |
| 800 | + void *buf __free(kvfree) = kvzalloc(size, GFP_KERNEL); |
| 801 | + if (!buf) |
| 802 | + return -ENOMEM; |
| 803 | + |
| 804 | + guard(mutex)(&snp_cmd_mutex); |
| 805 | + |
| 806 | + /* Check if the VMPCK is not empty */ |
| 807 | + if (is_vmpck_empty(snp_dev)) { |
| 808 | + dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n"); |
| 809 | + return -ENOTTY; |
| 810 | + } |
| 811 | + |
| 812 | + cert_table = buf + report_size; |
| 813 | + struct snp_ext_report_req ext_req = { |
| 814 | + .data = { .vmpl = desc->privlevel }, |
| 815 | + .certs_address = (__u64)cert_table, |
| 816 | + .certs_len = ext_size, |
| 817 | + }; |
| 818 | + memcpy(&ext_req.data.user_data, desc->inblob, desc->inblob_len); |
| 819 | + |
| 820 | + struct snp_guest_request_ioctl input = { |
| 821 | + .msg_version = 1, |
| 822 | + .req_data = (__u64)&ext_req, |
| 823 | + .resp_data = (__u64)buf, |
| 824 | + .exitinfo2 = 0xff, |
| 825 | + }; |
| 826 | + struct snp_req_resp io = { |
| 827 | + .req_data = KERNEL_SOCKPTR(&ext_req), |
| 828 | + .resp_data = KERNEL_SOCKPTR(buf), |
| 829 | + }; |
| 830 | + |
| 831 | + ret = get_ext_report(snp_dev, &input, &io); |
| 832 | + if (ret) |
| 833 | + return ret; |
| 834 | + |
| 835 | + memcpy(&hdr, buf, sizeof(hdr)); |
| 836 | + if (hdr.status == SEV_RET_INVALID_PARAM) |
| 837 | + return -EINVAL; |
| 838 | + if (hdr.status == SEV_RET_INVALID_KEY) |
| 839 | + return -EINVAL; |
| 840 | + if (hdr.status) |
| 841 | + return -ENXIO; |
| 842 | + if ((hdr.report_size + sizeof(hdr)) > report_size) |
| 843 | + return -ENOMEM; |
| 844 | + |
| 845 | + void *rbuf __free(kvfree) = kvzalloc(hdr.report_size, GFP_KERNEL); |
| 846 | + if (!rbuf) |
| 847 | + return -ENOMEM; |
| 848 | + |
| 849 | + memcpy(rbuf, buf + sizeof(hdr), hdr.report_size); |
| 850 | + report->outblob = no_free_ptr(rbuf); |
| 851 | + report->outblob_len = hdr.report_size; |
| 852 | + |
| 853 | + certs_size = 0; |
| 854 | + for (i = 0; i < ext_size / sizeof(struct snp_msg_cert_entry); i++) { |
| 855 | + struct snp_msg_cert_entry *ent = &cert_table[i]; |
| 856 | + |
| 857 | + if (guid_is_null(&ent->guid) && !ent->offset && !ent->length) |
| 858 | + break; |
| 859 | + certs_size = max(certs_size, ent->offset + ent->length); |
| 860 | + } |
| 861 | + |
| 862 | + /* Suspicious that the response populated entries without populating size */ |
| 863 | + if (!certs_size && i) |
| 864 | + dev_warn_ratelimited(snp_dev->dev, "certificate slots conveyed without size\n"); |
| 865 | + |
| 866 | + /* No certs to report */ |
| 867 | + if (!certs_size) |
| 868 | + return 0; |
| 869 | + |
| 870 | + /* Suspicious that the certificate blob size contract was violated |
| 871 | + */ |
| 872 | + if (certs_size > ext_size) { |
| 873 | + dev_warn_ratelimited(snp_dev->dev, "certificate data truncated\n"); |
| 874 | + certs_size = ext_size; |
| 875 | + } |
| 876 | + |
| 877 | + void *cbuf __free(kvfree) = kvzalloc(certs_size, GFP_KERNEL); |
| 878 | + if (!cbuf) |
| 879 | + return -ENOMEM; |
| 880 | + |
| 881 | + memcpy(cbuf, cert_table, certs_size); |
| 882 | + report->auxblob = no_free_ptr(cbuf); |
| 883 | + report->auxblob_len = certs_size; |
| 884 | + |
| 885 | + return 0; |
| 886 | +} |
| 887 | + |
| 888 | +static const struct tsm_ops sev_tsm_ops = { |
| 889 | + .name = KBUILD_MODNAME, |
| 890 | + .report_new = sev_report_new, |
| 891 | +}; |
| 892 | + |
| 893 | +static void unregister_sev_tsm(void *data) |
| 894 | +{ |
| 895 | + tsm_unregister(&sev_tsm_ops); |
| 896 | +} |
| 897 | + |
771 | 898 | static int __init sev_guest_probe(struct platform_device *pdev)
|
772 | 899 | {
|
773 | 900 | struct snp_secrets_page_layout *layout;
|
@@ -841,6 +968,14 @@ static int __init sev_guest_probe(struct platform_device *pdev)
|
841 | 968 | snp_dev->input.resp_gpa = __pa(snp_dev->response);
|
842 | 969 | snp_dev->input.data_gpa = __pa(snp_dev->certs_data);
|
843 | 970 |
|
| 971 | + ret = tsm_register(&sev_tsm_ops, snp_dev, &tsm_report_extra_type); |
| 972 | + if (ret) |
| 973 | + goto e_free_cert_data; |
| 974 | + |
| 975 | + ret = devm_add_action_or_reset(&pdev->dev, unregister_sev_tsm, NULL); |
| 976 | + if (ret) |
| 977 | + goto e_free_cert_data; |
| 978 | + |
844 | 979 | ret = misc_register(misc);
|
845 | 980 | if (ret)
|
846 | 981 | goto e_free_cert_data;
|
|
0 commit comments