Message ID | 20220909192708.1113126-2-sathyanarayanan.kuppuswamy@linux.intel.com |
---|---|
State | New |
Headers | show |
Series | Add TDX Guest Attestation support | expand |
On Fri, Sep 09, 2022 at 12:27:06PM -0700, Kuppuswamy Sathyanarayanan wrote: > diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c > index 928dcf7a20d9..8b5c59110321 100644 > --- a/arch/x86/coco/tdx/tdx.c > +++ b/arch/x86/coco/tdx/tdx.c > @@ -5,16 +5,21 @@ > #define pr_fmt(fmt) "tdx: " fmt > > #include <linux/cpufeature.h> > +#include <linux/miscdevice.h> > +#include <linux/mm.h> > +#include <linux/io.h> > #include <asm/coco.h> > #include <asm/tdx.h> > #include <asm/vmx.h> > #include <asm/insn.h> > #include <asm/insn-eval.h> > #include <asm/pgtable.h> > +#include <uapi/asm/tdx.h> > > /* TDX module Call Leaf IDs */ > #define TDX_GET_INFO 1 > #define TDX_GET_VEINFO 3 > +#define TDX_GET_REPORT 4 > #define TDX_ACCEPT_PAGE 6 > > /* TDX hypercall Leaf IDs */ > @@ -775,3 +780,113 @@ void __init tdx_early_init(void) > > pr_info("Guest detected\n"); > } > + > +static long tdx_get_report(void __user *argp) > +{ > + u8 *reportdata, *tdreport; > + struct tdx_report_req req; > + u8 reserved[7] = {0}; > + long ret; > + > + if (copy_from_user(&req, argp, sizeof(req))) > + return -EFAULT; > + > + /* > + * Per TDX Module 1.0 specification, section titled > + * "TDG.MR.REPORT", REPORTDATA length is fixed as > + * TDX_REPORTDATA_LEN, TDREPORT length is fixed as > + * TDX_REPORT_LEN, and TDREPORT subtype is fixed as > + * 0. Also check for valid user pointers and make sure > + * reserved entries values are zero. > + */ > + if (!req.reportdata || !req.tdreport || req.subtype || > + req.rpd_len != TDX_REPORTDATA_LEN || > + req.tdr_len != TDX_REPORT_LEN || > + memcmp(req.reserved, reserved, 7)) > + return -EINVAL; Maybe make several checks instead of the monstrous one? !req.reportdata and !req.tdreport look redundant. copy_from/to_user() will catch them (and other bad address cases). And -EFAULT is more appropriate in this case. > + > + reportdata = kmalloc(req.rpd_len, GFP_KERNEL); > + if (!reportdata) > + return -ENOMEM; > + > + tdreport = kzalloc(req.tdr_len, GFP_KERNEL); > + if (!tdreport) { > + kfree(reportdata); > + return -ENOMEM; > + } > + > + if (copy_from_user(reportdata, u64_to_user_ptr(req.reportdata), > + req.rpd_len)) { > + ret = -EFAULT; > + goto out; > + } > + > + /* > + * Generate TDREPORT using "TDG.MR.REPORT" TDCALL. > + * > + * Get the TDREPORT using REPORTDATA as input. Refer to > + * section 22.3.3 TDG.MR.REPORT leaf in the TDX Module 1.0 > + * Specification for detailed information. > + */ > + ret = __tdx_module_call(TDX_GET_REPORT, virt_to_phys(tdreport), > + virt_to_phys(reportdata), req.subtype, > + 0, NULL); > + if (ret) { > + ret = -EIO; The spec says that it generate an error if invalid operand or busy. Maybe translate the TDX error codes to errnos? BTW, regarding busy case: do we want to protect against two parallel TDX_GET_REPORT? What happens if we run the second TDX_GET_REPORT when the first hasn't complete? > + goto out; > + } > + > + if (copy_to_user(u64_to_user_ptr(req.tdreport), tdreport, req.tdr_len)) > + ret = -EFAULT; > + > +out: > + kfree(reportdata); > + kfree(tdreport); > + return ret; > +} > +static long tdx_guest_ioctl(struct file *file, unsigned int cmd, > + unsigned long arg) > +{ > + void __user *argp = (void __user *)arg; > + long ret = -ENOTTY; Not a typewriter? Huh? > + > + switch (cmd) { > + case TDX_CMD_GET_REPORT: > + ret = tdx_get_report(argp); > + break; > + default: > + pr_debug("cmd %d not supported\n", cmd); > + break; > + } > + > + return ret; > +}
On 9/12/22 3:22 PM, Kirill A . Shutemov wrote: > On Fri, Sep 09, 2022 at 12:27:06PM -0700, Kuppuswamy Sathyanarayanan wrote: >> diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c >> index 928dcf7a20d9..8b5c59110321 100644 >> --- a/arch/x86/coco/tdx/tdx.c >> +++ b/arch/x86/coco/tdx/tdx.c >> @@ -5,16 +5,21 @@ >> #define pr_fmt(fmt) "tdx: " fmt >> >> #include <linux/cpufeature.h> >> +#include <linux/miscdevice.h> >> +#include <linux/mm.h> >> +#include <linux/io.h> >> #include <asm/coco.h> >> #include <asm/tdx.h> >> #include <asm/vmx.h> >> #include <asm/insn.h> >> #include <asm/insn-eval.h> >> #include <asm/pgtable.h> >> +#include <uapi/asm/tdx.h> >> >> /* TDX module Call Leaf IDs */ >> #define TDX_GET_INFO 1 >> #define TDX_GET_VEINFO 3 >> +#define TDX_GET_REPORT 4 >> #define TDX_ACCEPT_PAGE 6 >> >> /* TDX hypercall Leaf IDs */ >> @@ -775,3 +780,113 @@ void __init tdx_early_init(void) >> >> pr_info("Guest detected\n"); >> } >> + >> +static long tdx_get_report(void __user *argp) >> +{ >> + u8 *reportdata, *tdreport; >> + struct tdx_report_req req; >> + u8 reserved[7] = {0}; >> + long ret; >> + >> + if (copy_from_user(&req, argp, sizeof(req))) >> + return -EFAULT; >> + >> + /* >> + * Per TDX Module 1.0 specification, section titled >> + * "TDG.MR.REPORT", REPORTDATA length is fixed as >> + * TDX_REPORTDATA_LEN, TDREPORT length is fixed as >> + * TDX_REPORT_LEN, and TDREPORT subtype is fixed as >> + * 0. Also check for valid user pointers and make sure >> + * reserved entries values are zero. >> + */ >> + if (!req.reportdata || !req.tdreport || req.subtype || >> + req.rpd_len != TDX_REPORTDATA_LEN || >> + req.tdr_len != TDX_REPORT_LEN || >> + memcmp(req.reserved, reserved, 7)) >> + return -EINVAL; > > Maybe make several checks instead of the monstrous one? Agree. I will split it into two checks. One for spec related checks and another for ABI validation. > > !req.reportdata and !req.tdreport look redundant. copy_from/to_user() will > catch them (and other bad address cases). And -EFAULT is more appropriate > in this case. We don't have to allocate kernel memory if we check it here. But I am not against letting copy_from/to_user() handle it. I will remove the NULL check. > >> + >> + reportdata = kmalloc(req.rpd_len, GFP_KERNEL); >> + if (!reportdata) >> + return -ENOMEM; >> + >> + tdreport = kzalloc(req.tdr_len, GFP_KERNEL); >> + if (!tdreport) { >> + kfree(reportdata); >> + return -ENOMEM; >> + } >> + >> + if (copy_from_user(reportdata, u64_to_user_ptr(req.reportdata), >> + req.rpd_len)) { >> + ret = -EFAULT; >> + goto out; >> + } >> + >> + /* >> + * Generate TDREPORT using "TDG.MR.REPORT" TDCALL. >> + * >> + * Get the TDREPORT using REPORTDATA as input. Refer to >> + * section 22.3.3 TDG.MR.REPORT leaf in the TDX Module 1.0 >> + * Specification for detailed information. >> + */ >> + ret = __tdx_module_call(TDX_GET_REPORT, virt_to_phys(tdreport), >> + virt_to_phys(reportdata), req.subtype, >> + 0, NULL); >> + if (ret) { >> + ret = -EIO; > > The spec says that it generate an error if invalid operand or busy. Maybe > translate the TDX error codes to errnos? User space has no need to know about the error code. In both error cases, if user wants report, request has to re-submitted. So there is no use in translating the error codes. > > BTW, regarding busy case: do we want to protect against two parallel > TDX_GET_REPORT? What happens if we run the second TDX_GET_REPORT when the > first hasn't complete? We don't have to protect against it here. It is a blocking call. So if user makes a parallel request, we will wait for TDX Module to service them in sequence. > >> + goto out; >> + } >> + >> + if (copy_to_user(u64_to_user_ptr(req.tdreport), tdreport, req.tdr_len)) >> + ret = -EFAULT; >> + >> +out: >> + kfree(reportdata); >> + kfree(tdreport); >> + return ret; >> +} >> +static long tdx_guest_ioctl(struct file *file, unsigned int cmd, >> + unsigned long arg) >> +{ >> + void __user *argp = (void __user *)arg; >> + long ret = -ENOTTY; > > Not a typewriter? Huh? It is the recommended return code for invalid IOCTL commands. https://www.kernel.org/doc/html/latest/driver-api/ioctl.html > >> + >> + switch (cmd) { >> + case TDX_CMD_GET_REPORT: >> + ret = tdx_get_report(argp); >> + break; >> + default: >> + pr_debug("cmd %d not supported\n", cmd); >> + break; >> + } >> + >> + return ret; >> +} >
On Fri, 2022-09-09 at 12:27 -0700, Kuppuswamy Sathyanarayanan wrote: > diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c > index 928dcf7a20d9..8b5c59110321 100644 > --- a/arch/x86/coco/tdx/tdx.c > +++ b/arch/x86/coco/tdx/tdx.c > @@ -5,16 +5,21 @@ > #define pr_fmt(fmt) "tdx: " fmt > > #include <linux/cpufeature.h> > +#include <linux/miscdevice.h> > +#include <linux/mm.h> > +#include <linux/io.h> Sorry perhaps I am missing something, but what is the reason to include <linux/mm.h>? <linux/io.h> is for virt_to_phys()? And should we explicitly include <linux/uaccess.h> for copy_{from|to}_user(), and include the header (<linux/string.h> ?) for memchr_inv()?
On 9/12/22 6:25 PM, Huang, Kai wrote: >> diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c >> index 928dcf7a20d9..8b5c59110321 100644 >> --- a/arch/x86/coco/tdx/tdx.c >> +++ b/arch/x86/coco/tdx/tdx.c >> @@ -5,16 +5,21 @@ >> #define pr_fmt(fmt) "tdx: " fmt >> >> #include <linux/cpufeature.h> >> +#include <linux/miscdevice.h> >> +#include <linux/mm.h> >> +#include <linux/io.h> > Sorry perhaps I am missing something, but what is the reason to include > <linux/mm.h>? It is included for kmalloc/kfree, file related structs and copy_{from|to}_user(). > > <linux/io.h> is for virt_to_phys()? Yes > > And should we explicitly include <linux/uaccess.h> for copy_{from|to}_user(), mm.h covers it. So I don't think we should explicitly include it. > and include the header (<linux/string.h> ?) for memchr_inv()? One of the previous headers includes linux/string.h (I am not sure which one). So why include it explicitly?
On Mon, 2022-09-12 at 19:44 -0700, Sathyanarayanan Kuppuswamy wrote: > > On 9/12/22 6:25 PM, Huang, Kai wrote: > > > diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c > > > index 928dcf7a20d9..8b5c59110321 100644 > > > --- a/arch/x86/coco/tdx/tdx.c > > > +++ b/arch/x86/coco/tdx/tdx.c > > > @@ -5,16 +5,21 @@ > > > #define pr_fmt(fmt) "tdx: " fmt > > > > > > #include <linux/cpufeature.h> > > > +#include <linux/miscdevice.h> > > > +#include <linux/mm.h> > > > +#include <linux/io.h> > > Sorry perhaps I am missing something, but what is the reason to include > > <linux/mm.h>? > > It is included for kmalloc/kfree, file related structs and copy_{from|to}_user(). > > > > > <linux/io.h> is for virt_to_phys()? > > Yes > > > > > And should we explicitly include <linux/uaccess.h> for copy_{from|to}_user(), > > mm.h covers it. So I don't think we should explicitly include it. > > > and include the header (<linux/string.h> ?) for memchr_inv()? > > One of the previous headers includes linux/string.h (I am not sure which one). > So why include it explicitly? > OK.
On 9/12/22 19:44, Sathyanarayanan Kuppuswamy wrote: >> and include the header (<linux/string.h> ?) for memchr_inv()? > One of the previous headers includes linux/string.h (I am not sure which one). > So why include it explicitly? Because it's a best practice. What happens is that you ride along on the coat tails of another #include, someone sees that include is no longer used and removes it. Then, your code is busted on some weird .config. *OR*, the header itself changes and doesn't #include the dependency you need. I guess you can go add this advice to Documentation/ if it's not there already somewhere.
Hi, On 9/13/22 2:01 AM, Dave Hansen wrote: > On 9/12/22 19:44, Sathyanarayanan Kuppuswamy wrote: >>> and include the header (<linux/string.h> ?) for memchr_inv()? >> One of the previous headers includes linux/string.h (I am not sure which one). >> So why include it explicitly? > Because it's a best practice. What happens is that you ride along on > the coat tails of another #include, someone sees that include is no > longer used and removes it. Then, your code is busted on some weird > .config. > > *OR*, the header itself changes and doesn't #include the dependency you > need. > > I guess you can go add this advice to Documentation/ if it's not there > already somewhere. Ok. I will include it explicitly.
On 9/9/22 12:27, Kuppuswamy Sathyanarayanan wrote: > > arch/x86/coco/tdx/tdx.c | 115 ++++++++++++++++++++++++++++++++ > arch/x86/include/uapi/asm/tdx.h | 56 ++++++++++++++++ > 2 files changed, 171 insertions(+) > create mode 100644 arch/x86/include/uapi/asm/tdx.h The SEV equivalent of this in in: drivers/virt/coco/sev-guest/sev-guest.c right? Why did you choose a different location? Also, can you please study the SEV implementation a bit? It might help you find problems like the ioctl() return code issue. The SEV driver appears to have gotten that right.
On 9/14/22 4:36 AM, Dave Hansen wrote: > On 9/9/22 12:27, Kuppuswamy Sathyanarayanan wrote: >> >> arch/x86/coco/tdx/tdx.c | 115 ++++++++++++++++++++++++++++++++ >> arch/x86/include/uapi/asm/tdx.h | 56 ++++++++++++++++ >> 2 files changed, 171 insertions(+) >> create mode 100644 arch/x86/include/uapi/asm/tdx.h > > The SEV equivalent of this in in: > > drivers/virt/coco/sev-guest/sev-guest.c > > right? > > Why did you choose a different location? Also, can you please study the When we initially submitted the attestation patches, virt/coco folder was not created. I initially kept this driver in platform/x86/, but later moved to arch/x86/coco based on the review comments in v4. There was a discussion about the need for a new config and the location of the driver. The outcome of that discussion is, since this is not a traditional driver, but a basic TDX feature, we don't need a special config and the code can be maintained in the arch/x86/coco folder. https://lore.kernel.org/lkml/YmEfgn7fMcZ2oCnr@zn.tnic/ > SEV implementation a bit? It might help you find problems like the > ioctl() return code issue. The SEV driver appears to have gotten that > right. Ok.
On 9/14/22 08:36, Sathyanarayanan Kuppuswamy wrote: > When we initially submitted the attestation patches, virt/coco folder > was not created. I initially kept this driver in platform/x86/, but > later moved to arch/x86/coco based on the review comments in v4. There > was a discussion about the need for a new config and the location of > the driver. The outcome of that discussion is, since this is not a > traditional driver, but a basic TDX feature, we don't need a special > config and the code can be maintained in the arch/x86/coco folder. Could you please include the following in this set somewhere: "The code to do the SEV analog of this TDX functionality is in ___insert_path_here____. This code is different from that because ______reason______ so it is instead placed in ____other_path____." ?
On 9/14/22 9:12 AM, Dave Hansen wrote: > On 9/14/22 08:36, Sathyanarayanan Kuppuswamy wrote: >> When we initially submitted the attestation patches, virt/coco folder >> was not created. I initially kept this driver in platform/x86/, but >> later moved to arch/x86/coco based on the review comments in v4. There >> was a discussion about the need for a new config and the location of >> the driver. The outcome of that discussion is, since this is not a >> traditional driver, but a basic TDX feature, we don't need a special >> config and the code can be maintained in the arch/x86/coco folder. > > Could you please include the following in this set somewhere: > > "The code to do the SEV analog of this TDX functionality is in > ___insert_path_here____. This code is different from that because > ______reason______ so it is instead placed in ____other_path____." > > ? Ok. I will include it in the cover letter.
On 9/14/22 9:12 AM, Dave Hansen wrote: > On 9/14/22 08:36, Sathyanarayanan Kuppuswamy wrote: >> When we initially submitted the attestation patches, virt/coco folder >> was not created. I initially kept this driver in platform/x86/, but >> later moved to arch/x86/coco based on the review comments in v4. There >> was a discussion about the need for a new config and the location of >> the driver. The outcome of that discussion is, since this is not a >> traditional driver, but a basic TDX feature, we don't need a special >> config and the code can be maintained in the arch/x86/coco folder. > > Could you please include the following in this set somewhere: > > "The code to do the SEV analog of this TDX functionality is in > ___insert_path_here____. This code is different from that because > ______reason______ so it is instead placed in ____other_path____." > > ? I have also included info about why we don't use a separate config option for it. The code for the SEV equivalent of this TDX attestation functionality can be found in drivers/virt/coco/sev-guest/. It is implemented as a platform module driver, and it can be enabled using the CONFIG_SEV_GUEST config option. However, in the case of TDX, it is implemented as a built-in driver in the arch/x86/coco/tdx/tdx.c because of the following reasons: 1. Attestation is expected to be needed by all distributions that support TDX. Therefore, using a separate configuration option is not necessary. With TDX support, it can be enabled by default, and a built-in driver model will work better in this use case. 2. Since it is not a conventional device driver and the code is very simple, creating an individual driver for it may be an overkill.
On Wed, Sep 14, 2022 at 05:30:45PM -0700, Sathyanarayanan Kuppuswamy wrote: > I have also included info about why we don't use a separate config > option for it. > > The code for the SEV equivalent of this TDX attestation functionality > can be found in drivers/virt/coco/sev-guest/. It is implemented as a > platform module driver, and it can be enabled using the CONFIG_SEV_GUEST > config option. However, in the case of TDX, it is implemented as a > built-in driver in the arch/x86/coco/tdx/tdx.c because of the following > reasons: > > 1. Attestation is expected to be needed by all distributions that support > TDX. Therefore, using a separate configuration option is not necessary. > With TDX support, it can be enabled by default, and a built-in driver > model will work better in this use case. No, that's not valid. Distros want to enable everything, but only load stuff that is only present. You don't allow this for this code, which isn't ok. > 2. Since it is not a conventional device driver and the code is very simple, > creating an individual driver for it may be an overkill. "simple" is not the issue, again, this should be a "normal" driver that autoloads when the hardware is present and not load when the hardware is not present. This is not "special" to avoid the normal functionality of all other drivers. So again, no, this is not ok, fix this properly, don't be lazy. thanks, greg k-h
On Fri, Sep 09, 2022 at 12:27:06PM -0700, Kuppuswamy Sathyanarayanan wrote: > +static int __init tdx_guest_init(void) > +{ > + int ret; > + > + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) > + return -EIO; > + > + ret = misc_register(&tdx_misc_dev); > + if (ret) { > + pr_err("misc device registration failed\n"); > + return ret; > + } > + > + return 0; > +} > +device_initcall(tdx_guest_init) As mentioned elsewhere, make this a normal module_init() format and only load the module if the hardware is present. Don't just always be built/loaded, that's not ok. thanks, greg k-h
Hi, On 9/15/22 4:09 AM, Greg Kroah-Hartman wrote: > On Fri, Sep 09, 2022 at 12:27:06PM -0700, Kuppuswamy Sathyanarayanan wrote: >> +static int __init tdx_guest_init(void) >> +{ >> + int ret; >> + >> + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) >> + return -EIO; >> + >> + ret = misc_register(&tdx_misc_dev); >> + if (ret) { >> + pr_err("misc device registration failed\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> +device_initcall(tdx_guest_init) > > As mentioned elsewhere, make this a normal module_init() format and only > load the module if the hardware is present. Don't just always be This feature needs to be enabled by default for all valid TDX guests. If TDX support is enabled and the guest is a valid TDX guest, the "X86 FEATURE TDX GUEST" feature flag will be set. So looking for "if(!cpu feature enabled(X86 FEATURE TDX GUEST))" will ensure that the interface is only created in a valid TDX guest. Even if we make it into a separate driver and use module init(), we'll have to use the same "if(!cpu feature enabled(X86 FEATURE TDX GUEST))" check to create and load the device. This approach was used in earlier versions of this driver. We later changed it to initcall because it appeared to be a roundabout approach. Let me know if you still suggest to use module_init() model. Following is the sample implementation with module_init() and this code will be compiled with CONFIG_INTEL_TDX_GUEST=y. +static struct platform_driver tdx_attest_driver = { + .probe = tdx_attest_probe, + .remove = tdx_attest_remove, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init tdx_attest_init(void) +{ + int ret; + + /* Make sure we are in a valid TDX platform */ + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) + return -EIO; + + ret = platform_driver_register(&tdx_attest_driver); + if (ret) { + pr_err("failed to register driver, err=%d\n", ret); + return ret; + } + + pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); + pr_err("failed to allocate device, err=%d\n", ret); + platform_driver_unregister(&tdx_attest_driver); + return ret; + } + + return 0; +} + +static void __exit tdx_attest_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&tdx_attest_driver); +} + +module_init(tdx_attest_init); +module_exit(tdx_attest_exit); > built/loaded, that's not ok. > > thanks, > > greg k-h
On Thu, Sep 15, 2022 at 08:22:37AM -0700, Sathyanarayanan Kuppuswamy wrote: > Hi, > > On 9/15/22 4:09 AM, Greg Kroah-Hartman wrote: > > On Fri, Sep 09, 2022 at 12:27:06PM -0700, Kuppuswamy Sathyanarayanan wrote: > >> +static int __init tdx_guest_init(void) > >> +{ > >> + int ret; > >> + > >> + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) > >> + return -EIO; > >> + > >> + ret = misc_register(&tdx_misc_dev); > >> + if (ret) { > >> + pr_err("misc device registration failed\n"); > >> + return ret; > >> + } > >> + > >> + return 0; > >> +} > >> +device_initcall(tdx_guest_init) > > > > As mentioned elsewhere, make this a normal module_init() format and only > > load the module if the hardware is present. Don't just always be > > This feature needs to be enabled by default for all valid TDX guests. Why? What is so needed by userspace to require this brand new char device node just to use TDX? > If TDX support is enabled and the guest is a valid TDX guest, the > "X86 FEATURE TDX GUEST" feature flag will be set. So looking for > "if(!cpu feature enabled(X86 FEATURE TDX GUEST))" will ensure that > the interface is only created in a valid TDX guest. Yes, but that's not the point. We don't just "build all drivers into the kernel and only bind to hardware we actually have". That's not how Linux works, sorry. > Even if we make it into a separate driver and use module init(), we'll > have to use the same "if(!cpu feature enabled(X86 FEATURE TDX GUEST))" > check to create and load the device. This approach was used in earlier > versions of this driver. We later changed it to initcall because it > appeared to be a roundabout approach. Sorry, no, do it properly, have it be autoloaded only on the systems that have the cpu feature. > Let me know if you still suggest to use module_init() model. Yes, it is a requirement. Do you want every driver to try to copy what you are doing here? > Following is the sample implementation with module_init() and this code > will be compiled with CONFIG_INTEL_TDX_GUEST=y. > > +static struct platform_driver tdx_attest_driver = { > + .probe = tdx_attest_probe, > + .remove = tdx_attest_remove, > + .driver = { > + .name = DRIVER_NAME, > + }, > +}; > + > +static int __init tdx_attest_init(void) > +{ > + int ret; > + > + /* Make sure we are in a valid TDX platform */ > + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) > + return -EIO; > + > + ret = platform_driver_register(&tdx_attest_driver); > + if (ret) { > + pr_err("failed to register driver, err=%d\n", ret); > + return ret; > + } > + > + pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); > + if (IS_ERR(pdev)) { > + ret = PTR_ERR(pdev); > + pr_err("failed to allocate device, err=%d\n", ret); > + platform_driver_unregister(&tdx_attest_driver); > + return ret; > + } > + > + return 0; > +} > + > +static void __exit tdx_attest_exit(void) > +{ > + platform_device_unregister(pdev); > + platform_driver_unregister(&tdx_attest_driver); > +} > + > +module_init(tdx_attest_init); > +module_exit(tdx_attest_exit); Sorry, no, this too is not ok as you are not telling userspace if it needs to load your driver or not automatically. Please do this properly. Basic issues like this shouldn't be showing up in v13 of a patch series. Please take the time and start over and go and get a lot of internal review before sending anything out again. thanks, greg k-h
diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 928dcf7a20d9..8b5c59110321 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -5,16 +5,21 @@ #define pr_fmt(fmt) "tdx: " fmt #include <linux/cpufeature.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/io.h> #include <asm/coco.h> #include <asm/tdx.h> #include <asm/vmx.h> #include <asm/insn.h> #include <asm/insn-eval.h> #include <asm/pgtable.h> +#include <uapi/asm/tdx.h> /* TDX module Call Leaf IDs */ #define TDX_GET_INFO 1 #define TDX_GET_VEINFO 3 +#define TDX_GET_REPORT 4 #define TDX_ACCEPT_PAGE 6 /* TDX hypercall Leaf IDs */ @@ -775,3 +780,113 @@ void __init tdx_early_init(void) pr_info("Guest detected\n"); } + +static long tdx_get_report(void __user *argp) +{ + u8 *reportdata, *tdreport; + struct tdx_report_req req; + u8 reserved[7] = {0}; + long ret; + + if (copy_from_user(&req, argp, sizeof(req))) + return -EFAULT; + + /* + * Per TDX Module 1.0 specification, section titled + * "TDG.MR.REPORT", REPORTDATA length is fixed as + * TDX_REPORTDATA_LEN, TDREPORT length is fixed as + * TDX_REPORT_LEN, and TDREPORT subtype is fixed as + * 0. Also check for valid user pointers and make sure + * reserved entries values are zero. + */ + if (!req.reportdata || !req.tdreport || req.subtype || + req.rpd_len != TDX_REPORTDATA_LEN || + req.tdr_len != TDX_REPORT_LEN || + memcmp(req.reserved, reserved, 7)) + return -EINVAL; + + reportdata = kmalloc(req.rpd_len, GFP_KERNEL); + if (!reportdata) + return -ENOMEM; + + tdreport = kzalloc(req.tdr_len, GFP_KERNEL); + if (!tdreport) { + kfree(reportdata); + return -ENOMEM; + } + + if (copy_from_user(reportdata, u64_to_user_ptr(req.reportdata), + req.rpd_len)) { + ret = -EFAULT; + goto out; + } + + /* + * Generate TDREPORT using "TDG.MR.REPORT" TDCALL. + * + * Get the TDREPORT using REPORTDATA as input. Refer to + * section 22.3.3 TDG.MR.REPORT leaf in the TDX Module 1.0 + * Specification for detailed information. + */ + ret = __tdx_module_call(TDX_GET_REPORT, virt_to_phys(tdreport), + virt_to_phys(reportdata), req.subtype, + 0, NULL); + if (ret) { + ret = -EIO; + goto out; + } + + if (copy_to_user(u64_to_user_ptr(req.tdreport), tdreport, req.tdr_len)) + ret = -EFAULT; + +out: + kfree(reportdata); + kfree(tdreport); + return ret; +} +static long tdx_guest_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + long ret = -ENOTTY; + + switch (cmd) { + case TDX_CMD_GET_REPORT: + ret = tdx_get_report(argp); + break; + default: + pr_debug("cmd %d not supported\n", cmd); + break; + } + + return ret; +} + +static const struct file_operations tdx_guest_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = tdx_guest_ioctl, + .llseek = no_llseek, +}; + +static struct miscdevice tdx_misc_dev = { + .name = TDX_GUEST_DEVICE, + .minor = MISC_DYNAMIC_MINOR, + .fops = &tdx_guest_fops, +}; + +static int __init tdx_guest_init(void) +{ + int ret; + + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) + return -EIO; + + ret = misc_register(&tdx_misc_dev); + if (ret) { + pr_err("misc device registration failed\n"); + return ret; + } + + return 0; +} +device_initcall(tdx_guest_init) diff --git a/arch/x86/include/uapi/asm/tdx.h b/arch/x86/include/uapi/asm/tdx.h new file mode 100644 index 000000000000..687c86c9e3fb --- /dev/null +++ b/arch/x86/include/uapi/asm/tdx.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_X86_TDX_H +#define _UAPI_ASM_X86_TDX_H + +#include <linux/types.h> +#include <linux/ioctl.h> + +#define TDX_GUEST_DEVICE "tdx-guest" + +/* Length of the REPORTDATA used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORTDATA_LEN 64 + +/* Length of TDREPORT used in TDG.MR.REPORT TDCALL */ +#define TDX_REPORT_LEN 1024 + +/** + * struct tdx_report_req: Get TDREPORT using REPORTDATA as input. + * + * @reportdata : User-defined REPORTDATA to be included into + * TDREPORT. Typically it can be some nonce + * provided by attestation service, so the + * generated TDREPORT can be uniquely verified. + * @tdreport : TDREPORT output from TDCALL[TDG.MR.REPORT]. + * @rpd_len : Length of the REPORTDATA (fixed as 64 bytes by + * the TDX Module specification, but parameter is + * added to handle future extension). + * @tdr_len : Length of the TDREPORT (fixed as 1024 bytes by + * the TDX Module specification, but a parameter + * is added to accommodate future extension). + * @subtype : Subtype of TDREPORT (fixed as 0 by TDX Module + * specification, but added a parameter to handle + * future extension). + * @reserved : Reserved entries to handle future requirements. + * Default acceptable value is 0. + * + * Used in TDX_CMD_GET_REPORT IOCTL request. + */ +struct tdx_report_req { + __u64 reportdata; + __u64 tdreport; + __u32 rpd_len; + __u32 tdr_len; + __u8 subtype; + __u8 reserved[7]; +}; + +/* + * TDX_CMD_GET_REPORT - Get TDREPORT using TDCALL[TDG.MR.REPORT] + * + * Return 0 on success, -EIO on TDCALL execution failure, and + * standard errno on other general error cases. + * + */ +#define TDX_CMD_GET_REPORT _IOWR('T', 0x01, __u64) + +#endif /* _UAPI_ASM_X86_TDX_H */