Message ID | 20221020045828.2354731-3-sathyanarayanan.kuppuswamy@linux.intel.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | ] Add TDX Guest Attestation support | expand |
On Wed, Oct 19, 2022 at 09:58:27PM -0700, Kuppuswamy Sathyanarayanan wrote: > +static long tdx_get_report(void __user *argp) > +{ > + u8 *reportdata, *tdreport; > + struct tdx_report_req req; > + 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. > + */ > + if (req.subtype || req.rpd_len != TDX_REPORTDATA_LEN || > + req.tdr_len != TDX_REPORT_LEN) { > + pr_err("TDX_CMD_GET_REPORT: invalid req: subtype:%u rpd_len:%u tdr_len:%u\n", > + req.subtype, req.rpd_len, req.tdr_len); You are allowing userspace to spam the kernel logs, please do not do that. Also, you have a real device here, use it and call dev_*() instead of pr_*(). Your code should not have any pr_* calls. > + return -EINVAL; > + } > + > + if (memchr_inv(req.reserved, 0, sizeof(req.reserved))) { > + pr_err("TDX_CMD_GET_REPORT: Non zero value in reserved field\n"); > + return -EINVAL; > + } > + > + reportdata = kmalloc(req.rpd_len, GFP_KERNEL); > + if (!reportdata) > + return -ENOMEM; > + > + tdreport = kzalloc(req.tdr_len, GFP_KERNEL); > + if (!tdreport) { > + ret = -ENOMEM; > + goto out; > + } > + > + 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 */ > + ret = tdx_mcall_get_report(reportdata, tdreport, req.subtype); > + if (ret) { > + pr_err("TDX_CMD_GET_REPORT: TDCALL failed\n"); > + 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) > +{ > + switch (cmd) { > + case TDX_CMD_GET_REPORT: > + return tdx_get_report((void __user *)arg); > + default: > + return -ENOTTY; > + } > +} > + > +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 = KBUILD_MODNAME, > + .minor = MISC_DYNAMIC_MINOR, > + .fops = &tdx_guest_fops, > +}; > + > +static int __init tdx_guest_init(void) > +{ > + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) > + return -ENODEV; > + > + return misc_register(&tdx_misc_dev); > +} > +module_init(tdx_guest_init); > + > +static void __exit tdx_guest_exit(void) > +{ > + misc_deregister(&tdx_misc_dev); > +} > +module_exit(tdx_guest_exit); > + > +#ifdef MODULE > +static const struct x86_cpu_id tdx_guest_ids[] = { > + X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), > + {} > +}; > +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); > +#endif Why the #ifdef? Should not be needed, right? > + > +MODULE_AUTHOR("Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>"); > +MODULE_DESCRIPTION("TDX Guest Driver"); > +MODULE_LICENSE("GPL"); > diff --git a/include/uapi/linux/tdx-guest.h b/include/uapi/linux/tdx-guest.h > new file mode 100644 > index 000000000000..fd71774a90ac > --- /dev/null > +++ b/include/uapi/linux/tdx-guest.h > @@ -0,0 +1,51 @@ > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > +#ifndef _UAPI_LINUX_TDX_GUEST_H_ > +#define _UAPI_LINUX_TDX_GUEST_H_ No Intel copyright notice? Are you sure you ran this code through internal Intel review properly? {sigh} > + > +#include <linux/ioctl.h> > +#include <linux/types.h> > + > +/* 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 a 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 the TDX Module specification, > + * but added a parameter to handle future extension). > + * @reserved: Reserved entries to handle future requirements. Should be s/Should/Must/ thanks, greg k-h
Hi, On 10/19/22 10:38 PM, Greg Kroah-Hartman wrote: > On Wed, Oct 19, 2022 at 09:58:27PM -0700, Kuppuswamy Sathyanarayanan wrote: >> +static long tdx_get_report(void __user *argp) >> +{ >> + u8 *reportdata, *tdreport; >> + struct tdx_report_req req; >> + 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. >> + */ >> + if (req.subtype || req.rpd_len != TDX_REPORTDATA_LEN || >> + req.tdr_len != TDX_REPORT_LEN) { >> + pr_err("TDX_CMD_GET_REPORT: invalid req: subtype:%u rpd_len:%u tdr_len:%u\n", >> + req.subtype, req.rpd_len, req.tdr_len); > > You are allowing userspace to spam the kernel logs, please do not do > that. Added it to help userspace understand the reason for the failure (only for the cases like request param issues and TDCALL failure). Boris recommended adding it in the previous review. > > Also, you have a real device here, use it and call dev_*() instead of > pr_*(). Your code should not have any pr_* calls. Ok. I will use dev_err variant. > > >> + return -EINVAL; >> + } >> + >> + if (memchr_inv(req.reserved, 0, sizeof(req.reserved))) { >> + pr_err("TDX_CMD_GET_REPORT: Non zero value in reserved field\n"); >> + return -EINVAL; >> + } >> + >> + reportdata = kmalloc(req.rpd_len, GFP_KERNEL); >> + if (!reportdata) >> + return -ENOMEM; >> + >> + tdreport = kzalloc(req.tdr_len, GFP_KERNEL); >> + if (!tdreport) { >> + ret = -ENOMEM; >> + goto out; >> + } >> + >> + 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 */ >> + ret = tdx_mcall_get_report(reportdata, tdreport, req.subtype); >> + if (ret) { >> + pr_err("TDX_CMD_GET_REPORT: TDCALL failed\n"); >> + 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) >> +{ >> + switch (cmd) { >> + case TDX_CMD_GET_REPORT: >> + return tdx_get_report((void __user *)arg); >> + default: >> + return -ENOTTY; >> + } >> +} >> + >> +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 = KBUILD_MODNAME, >> + .minor = MISC_DYNAMIC_MINOR, >> + .fops = &tdx_guest_fops, >> +}; >> + >> +static int __init tdx_guest_init(void) >> +{ >> + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) >> + return -ENODEV; >> + >> + return misc_register(&tdx_misc_dev); >> +} >> +module_init(tdx_guest_init); >> + >> +static void __exit tdx_guest_exit(void) >> +{ >> + misc_deregister(&tdx_misc_dev); >> +} >> +module_exit(tdx_guest_exit); >> + >> +#ifdef MODULE >> +static const struct x86_cpu_id tdx_guest_ids[] = { >> + X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), >> + {} >> +}; >> +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); >> +#endif > > Why the #ifdef? Should not be needed, right? I have added it to fix the following warning reported by 0-day. https://lore.kernel.org/lkml/202209211607.tCtTWKbV-lkp@intel.com/ It is related to nullifying the MODULE_DEVICE_TABLE in #ifndef MODULE case in linux/module.h. > > thanks, > > greg k-h
On Thu, Oct 20, 2022 at 05:00:27PM -0700, Sathyanarayanan Kuppuswamy wrote: > Hi, > > On 10/19/22 10:38 PM, Greg Kroah-Hartman wrote: > > On Wed, Oct 19, 2022 at 09:58:27PM -0700, Kuppuswamy Sathyanarayanan wrote: > >> +static long tdx_get_report(void __user *argp) > >> +{ > >> + u8 *reportdata, *tdreport; > >> + struct tdx_report_req req; > >> + 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. > >> + */ > >> + if (req.subtype || req.rpd_len != TDX_REPORTDATA_LEN || > >> + req.tdr_len != TDX_REPORT_LEN) { > >> + pr_err("TDX_CMD_GET_REPORT: invalid req: subtype:%u rpd_len:%u tdr_len:%u\n", > >> + req.subtype, req.rpd_len, req.tdr_len); > > > > You are allowing userspace to spam the kernel logs, please do not do > > that. > > Added it to help userspace understand the reason for the failure (only for > the cases like request param issues and TDCALL failure). Boris recommended > adding it in the previous review. Again, you just created a vector for userspace to spam the kernel log. No kernel driver should ever do that. > >> +#ifdef MODULE > >> +static const struct x86_cpu_id tdx_guest_ids[] = { > >> + X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), > >> + {} > >> +}; > >> +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); > >> +#endif > > > > Why the #ifdef? Should not be needed, right? > > I have added it to fix the following warning reported by 0-day. > > https://lore.kernel.org/lkml/202209211607.tCtTWKbV-lkp@intel.com/ > > It is related to nullifying the MODULE_DEVICE_TABLE in #ifndef MODULE > case in linux/module.h. Then fix it properly, by correctly using that structure no matter what. You don't do that here... greg k-h
Hi Greg, On 10/20/22 9:39 PM, Greg Kroah-Hartman wrote: >>>> +#ifdef MODULE >>>> +static const struct x86_cpu_id tdx_guest_ids[] = { >>>> + X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), >>>> + {} >>>> +}; >>>> +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); >>>> +#endif >>> Why the #ifdef? Should not be needed, right? >> I have added it to fix the following warning reported by 0-day. >> >> https://lore.kernel.org/lkml/202209211607.tCtTWKbV-lkp@intel.com/ >> >> It is related to nullifying the MODULE_DEVICE_TABLE in #ifndef MODULE >> case in linux/module.h. > Then fix it properly, by correctly using that structure no matter what. > You don't do that here... I think we can use __maybe_unused attribute to fix this warning like mentioned below. Are you fine with it? --- a/drivers/virt/coco/tdx-guest/tdx-guest.c +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c @@ -118,13 +118,11 @@ static void __exit tdx_guest_exit(void) } module_exit(tdx_guest_exit); -#ifdef MODULE -static const struct x86_cpu_id tdx_guest_ids[] = { +static const struct x86_cpu_id __maybe_unused tdx_guest_ids[] = { X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), {} }; MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); -#endif Solution 2: ----------- We can also modify the code to use this structure in all cases like below. But it requires me to use slower x86_match_cpu() in place of cpu_feature_enabled() which I think is unnecessary. --- a/drivers/virt/coco/tdx-guest/tdx-guest.c +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c @@ -103,9 +103,15 @@ static struct miscdevice tdx_misc_dev = { .fops = &tdx_guest_fops, }; +static const struct x86_cpu_id tdx_guest_ids[] = { + X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); + static int __init tdx_guest_init(void) { - if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) + if (!x86_match_cpu(tdx_guest_ids)) return -ENODEV; return misc_register(&tdx_misc_dev); @@ -118,14 +124,6 @@ static void __exit tdx_guest_exit(void) } module_exit(tdx_guest_exit); -#ifdef MODULE -static const struct x86_cpu_id tdx_guest_ids[] = { - X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), - {} -}; -MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); -#endif
On Fri, Oct 21, 2022 at 04:51:34PM -0700, Sathyanarayanan Kuppuswamy wrote: > Hi Greg, > > On 10/20/22 9:39 PM, Greg Kroah-Hartman wrote: > >>>> +#ifdef MODULE > >>>> +static const struct x86_cpu_id tdx_guest_ids[] = { > >>>> + X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), > >>>> + {} > >>>> +}; > >>>> +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); > >>>> +#endif > >>> Why the #ifdef? Should not be needed, right? > >> I have added it to fix the following warning reported by 0-day. > >> > >> https://lore.kernel.org/lkml/202209211607.tCtTWKbV-lkp@intel.com/ > >> > >> It is related to nullifying the MODULE_DEVICE_TABLE in #ifndef MODULE > >> case in linux/module.h. > > Then fix it properly, by correctly using that structure no matter what. > > You don't do that here... > > I think we can use __maybe_unused attribute to fix this warning like > mentioned below. Are you fine with it? > > --- a/drivers/virt/coco/tdx-guest/tdx-guest.c > +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c > @@ -118,13 +118,11 @@ static void __exit tdx_guest_exit(void) > } > module_exit(tdx_guest_exit); > > -#ifdef MODULE > -static const struct x86_cpu_id tdx_guest_ids[] = { > +static const struct x86_cpu_id __maybe_unused tdx_guest_ids[] = { > X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), > {} > }; > MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); > -#endif > > Solution 2: > ----------- > > We can also modify the code to use this structure in all cases like > below. But it requires me to use slower x86_match_cpu() in place of > cpu_feature_enabled() which I think is unnecessary. > > --- a/drivers/virt/coco/tdx-guest/tdx-guest.c > +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c > @@ -103,9 +103,15 @@ static struct miscdevice tdx_misc_dev = { > .fops = &tdx_guest_fops, > }; > > +static const struct x86_cpu_id tdx_guest_ids[] = { > + X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), > + {} > +}; > +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); > + > static int __init tdx_guest_init(void) > { > - if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) > + if (!x86_match_cpu(tdx_guest_ids)) Please use this as it's what all other users of the x86cpu module device table code uses, right? And what is the "speed" difference here? Is is measurable and where does it matter? thanks, greg k-h
On 10/21/22 11:05 PM, Greg Kroah-Hartman wrote: > On Fri, Oct 21, 2022 at 04:51:34PM -0700, Sathyanarayanan Kuppuswamy wrote: >> Hi Greg, >> >> On 10/20/22 9:39 PM, Greg Kroah-Hartman wrote: >>>>>> +#ifdef MODULE >>>>>> +static const struct x86_cpu_id tdx_guest_ids[] = { >>>>>> + X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), >>>>>> + {} >>>>>> +}; >>>>>> +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); >>>>>> +#endif >>>>> Why the #ifdef? Should not be needed, right? >>>> I have added it to fix the following warning reported by 0-day. >>>> >>>> https://lore.kernel.org/lkml/202209211607.tCtTWKbV-lkp@intel.com/ >>>> >>>> It is related to nullifying the MODULE_DEVICE_TABLE in #ifndef MODULE >>>> case in linux/module.h. >>> Then fix it properly, by correctly using that structure no matter what. >>> You don't do that here... >> >> I think we can use __maybe_unused attribute to fix this warning like >> mentioned below. Are you fine with it? >> >> --- a/drivers/virt/coco/tdx-guest/tdx-guest.c >> +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c >> @@ -118,13 +118,11 @@ static void __exit tdx_guest_exit(void) >> } >> module_exit(tdx_guest_exit); >> >> -#ifdef MODULE >> -static const struct x86_cpu_id tdx_guest_ids[] = { >> +static const struct x86_cpu_id __maybe_unused tdx_guest_ids[] = { >> X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), >> {} >> }; >> MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); >> -#endif >> >> Solution 2: >> ----------- >> >> We can also modify the code to use this structure in all cases like >> below. But it requires me to use slower x86_match_cpu() in place of >> cpu_feature_enabled() which I think is unnecessary. >> >> --- a/drivers/virt/coco/tdx-guest/tdx-guest.c >> +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c >> @@ -103,9 +103,15 @@ static struct miscdevice tdx_misc_dev = { >> .fops = &tdx_guest_fops, >> }; >> >> +static const struct x86_cpu_id tdx_guest_ids[] = { >> + X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), >> + {} >> +}; >> +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); >> + >> static int __init tdx_guest_init(void) >> { >> - if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) >> + if (!x86_match_cpu(tdx_guest_ids)) > > Please use this as it's what all other users of the x86cpu module device Ok. I will use it. > table code uses, right? Not all, but most of them use the above model. Following two drivers seems to use __maybe_unused method. ./drivers/cpufreq/acpi-cpufreq.c ./drivers/char/hw_random/via-rng.c and following two drivers uses #ifdef MODULE method. ./arch/x86/kvm/vmx/vmx.c ./arch/x86/kvm/svm/svm.c > > And what is the "speed" difference here? Is is measurable and where > does it matter? Speed difference does not really matter in init code. So I am fine with using this approach. > > thanks, > > greg k-h
On 10/20/22 9:39 PM, Greg Kroah-Hartman wrote: >>> You are allowing userspace to spam the kernel logs, please do not do >>> that. >> Added it to help userspace understand the reason for the failure (only for >> the cases like request param issues and TDCALL failure). Boris recommended >> adding it in the previous review. > Again, you just created a vector for userspace to spam the kernel log. > No kernel driver should ever do that. > Brois, any comments? Do you also agree?
On Sun, Oct 23, 2022 at 1:13 PM Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@linux.intel.com> wrote: > > > > On 10/20/22 9:39 PM, Greg Kroah-Hartman wrote: > >>> You are allowing userspace to spam the kernel logs, please do not do > >>> that. > >> Added it to help userspace understand the reason for the failure (only for > >> the cases like request param issues and TDCALL failure). Boris recommended > >> adding it in the previous review. > > Again, you just created a vector for userspace to spam the kernel log. > > No kernel driver should ever do that. > > > > Brois, any comments? Do you also agree? > Maybe dev_err_once() does the job?
On Mon, Oct 24, 2022 at 09:57:53AM -0300, Wander Lairson Costa wrote: > On Sun, Oct 23, 2022 at 1:13 PM Sathyanarayanan Kuppuswamy > <sathyanarayanan.kuppuswamy@linux.intel.com> wrote: > > > > > > > > On 10/20/22 9:39 PM, Greg Kroah-Hartman wrote: > > >>> You are allowing userspace to spam the kernel logs, please do not do > > >>> that. > > >> Added it to help userspace understand the reason for the failure (only for > > >> the cases like request param issues and TDCALL failure). Boris recommended > > >> adding it in the previous review. > > > Again, you just created a vector for userspace to spam the kernel log. > > > No kernel driver should ever do that. > > > > > > > Brois, any comments? Do you also agree? > > > > Maybe dev_err_once() does the job? That does not make any sense when the userspace input can be different each time. This is just yet-another-ioctl, there's nothing special about it. Return an error for invalid input and don't log anything. Worst case, make it a dev_dbg() call if you really really really want to see it. thanks, greg k-h
On 10/23/22 09:13, Sathyanarayanan Kuppuswamy wrote: > On 10/20/22 9:39 PM, Greg Kroah-Hartman wrote: >>>> You are allowing userspace to spam the kernel logs, please do not do >>>> that. >>> Added it to help userspace understand the reason for the failure (only for >>> the cases like request param issues and TDCALL failure). Boris recommended >>> adding it in the previous review. >> Again, you just created a vector for userspace to spam the kernel log. >> No kernel driver should ever do that. >> > Brois, any comments? Do you also agree? ... > + if (req.subtype || req.rpd_len != TDX_REPORTDATA_LEN || > + req.tdr_len != TDX_REPORT_LEN) { > + pr_err("TDX_CMD_GET_REPORT: invalid req: subtype:%u rpd_len:%u tdr_len:%u\n", > + req.subtype, req.rpd_len, req.tdr_len); This is _clearly_ debugging code. There are a billion if(foo){return -EINVAL;}'s in the kernel, and very few of them have printk()'s to go along with them. They do help figure out what happened when userspace sees an -EINVAL and can't figure out what it did to cause it. But, if the kernel spammed dmesg for every time userspace does something stupid, it'd be filled up with noise. There are other ways to debug stuff like this if userspace gets confused. If folks are OK with dev_dbg(), then I'd move over to that. But, frankly, I don't think this rises to the level of needing its own error message. Heck, I'm not even sure why this code exits in the first place. I guess we don't want userspace making random requests to the host. But, of course, none of _that_ information about what the code is actually there for made it into the patch, and it just consumes comment space regurgitating the TDX spec. This branch of the thread frankly isn't about a pr_err(). It's about nobody really knowing (or caring) why that line of code is there, when it might happen, and what precise function it serves.
On 10/24/22 6:54 AM, Greg Kroah-Hartman wrote: >>>>>> You are allowing userspace to spam the kernel logs, please do not do >>>>>> that. >>>>> Added it to help userspace understand the reason for the failure (only for >>>>> the cases like request param issues and TDCALL failure). Boris recommended >>>>> adding it in the previous review. >>>> Again, you just created a vector for userspace to spam the kernel log. >>>> No kernel driver should ever do that. >>>> >>> Brois, any comments? Do you also agree? >>> >> Maybe dev_err_once() does the job? > That does not make any sense when the userspace input can be different > each time. > > This is just yet-another-ioctl, there's nothing special about it. > Return an error for invalid input and don't log anything. Worst case, > make it a dev_dbg() call if you really really really want to see it. It is a nice to have debug info, but not very important. So I will remove it.
Hi Dave, On 10/24/22 7:17 AM, Dave Hansen wrote: > On 10/23/22 09:13, Sathyanarayanan Kuppuswamy wrote: >> On 10/20/22 9:39 PM, Greg Kroah-Hartman wrote: >>>>> You are allowing userspace to spam the kernel logs, please do not do >>>>> that. >>>> Added it to help userspace understand the reason for the failure (only for >>>> the cases like request param issues and TDCALL failure). Boris recommended >>>> adding it in the previous review. >>> Again, you just created a vector for userspace to spam the kernel log. >>> No kernel driver should ever do that. >>> >> Brois, any comments? Do you also agree? > ... >> + if (req.subtype || req.rpd_len != TDX_REPORTDATA_LEN || >> + req.tdr_len != TDX_REPORT_LEN) { >> + pr_err("TDX_CMD_GET_REPORT: invalid req: subtype:%u rpd_len:%u tdr_len:%u\n", >> + req.subtype, req.rpd_len, req.tdr_len); > > This is _clearly_ debugging code. There are a billion if(foo){return > -EINVAL;}'s in the kernel, and very few of them have printk()'s to go > along with them. > > They do help figure out what happened when userspace sees an -EINVAL and > can't figure out what it did to cause it. But, if the kernel spammed > dmesg for every time userspace does something stupid, it'd be filled up > with noise. > > There are other ways to debug stuff like this if userspace gets confused. > > If folks are OK with dev_dbg(), then I'd move over to that. But, > frankly, I don't think this rises to the level of needing its own error > message. > > Heck, I'm not even sure why this code exits in the first place. I guess > we don't want userspace making random requests to the host. But, of > course, none of _that_ information about what the code is actually there > for made it into the patch, and it just consumes comment space > regurgitating the TDX spec. > > This branch of the thread frankly isn't about a pr_err(). It's about > nobody really knowing (or caring) why that line of code is there, when > it might happen, and what precise function it serves. It is added to ensure the user does not make random requests and the user input aligns with the defined IOCTL ABI. Returning -EINVAL for the input parameter error will help userspace better understand the reason for the failure than failing after making the TDCALL request. I have added the spec reference mainly for the reader to understand the origin of the checks involved. Would you prefer a comment like "Check for valid user input"?
diff --git a/Documentation/virt/coco/tdx-guest.rst b/Documentation/virt/coco/tdx-guest.rst new file mode 100644 index 000000000000..4fe72829bdd0 --- /dev/null +++ b/Documentation/virt/coco/tdx-guest.rst @@ -0,0 +1,42 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=================================================================== +TDX Guest API Documentation +=================================================================== + +1. General description +====================== + +The TDX guest driver exposes IOCTL interfaces via /dev/tdx-guest misc +device to allow userspace to get certain TDX guest specific details. + +2. API description +================== + +In this section, for each supported IOCTL, following information is +provided along with a generic description. + +:Input parameters: Parameters passed to the IOCTL and related details. +:Output: Details about output data and return value (with details about the non + common error values). + +2.1 TDX_CMD_GET_REPORT +---------------------- + +:Input parameters: struct tdx_report_req +:Output: Upon successful execution, TDREPORT data is copied to + tdx_report_req.tdreport and return 0. Return -EIO on + TDCALL failure or standard error number on other common + failures. + +The TDX_CMD_GET_REPORT IOCTL can be used by the attestation software to +get the TDREPORT from the TDX module using TDCALL[TDG.MR.REPORT]. + +Reference +--------- + +TDX reference material is collected here: + +https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html + +The driver is based on TDX module specification v1.0 and TDX GHCI specification v1.0. diff --git a/Documentation/virt/index.rst b/Documentation/virt/index.rst index 2f1cffa87b1b..56e003ff28ff 100644 --- a/Documentation/virt/index.rst +++ b/Documentation/virt/index.rst @@ -14,6 +14,7 @@ Linux Virtualization Support ne_overview acrn/index coco/sev-guest + coco/tdx-guest hyperv/index .. only:: html and subproject diff --git a/Documentation/x86/tdx.rst b/Documentation/x86/tdx.rst index b8fa4329e1a5..014b769923a4 100644 --- a/Documentation/x86/tdx.rst +++ b/Documentation/x86/tdx.rst @@ -210,6 +210,49 @@ converted to shared on boot. For coherent DMA allocation, the DMA buffer gets converted on the allocation. Check force_dma_unencrypted() for details. +Attestation +=========== + +Attestation is used to verify the TDX guest trustworthiness to other +entities before provisioning secrets to the guest. For example, a key +server may want to use attestation to verify that the guest is the +desired one before releasing the encryption keys to mount the encrypted +rootfs or secondary drive. + +The TDX module records the state of the TDX guest in various stages of +the guest boot process using build time measurement register (MRTD) and +runtime measurement registers (RTMR). Measurements related to guest +initial configuration and firmware image are recorded in the MRTD +register. Measurements related to initial state, kernel image, firmware +image, command line options, initrd, ACPI tables, etc are recorded in +RTMR registers. For more details as an example, please refer to TDX +Virtual Firmware design specification, sec titled "TD Measurement". At +TDX guest runtime, the attestation process is used to attest to these +measurements. + +The attestation process consists of two steps: TDREPORT generation and +Quote generation. + +TDX guest uses TDCALL[TDG.MR.REPORT] to get the TDREPORT (TDREPORT_STRUCT) +from the TDX module. TDREPORT is a fixed-size data structure generated by +the TDX module which contains guest-specific information (such as build +and boot measurements), platform security version, and the MAC to protect +the integrity of the TDREPORT. A user-provided 64-Byte REPORTDATA is used +as input and included in the TDREPORT. Typically it can be some nonce +provided by attestation service so the TDREPORT can be verified uniquely. +More details about the TDREPORT can be found in Intel TDX Module +specification, section titled "TDG.MR.REPORT Leaf". + +After getting the TDREPORT, the second step of the attestation process +is to send it to the Quoting Enclave (QE) to generate the Quote. TDREPORT +by design can only be verified on the local platform as the MAC key is +bound to the platform. To support remote verification of the TDREPORT, +TDX leverages Intel SGX Quoting Enclave to verify the TDREPORT locally +and convert it to a remotely verifiable Quote. Method of sending TDREPORT +to QE is implementation specific. Attestation software can choose +whatever communication channel available (i.e. vsock or TCP/IP) to +send the TDREPORT to QE and receive the Quote. + References ========== diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig index 87ef258cec64..f79ab13a5c28 100644 --- a/drivers/virt/Kconfig +++ b/drivers/virt/Kconfig @@ -52,4 +52,6 @@ source "drivers/virt/coco/efi_secret/Kconfig" source "drivers/virt/coco/sev-guest/Kconfig" +source "drivers/virt/coco/tdx-guest/Kconfig" + endif diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index 093674e05c40..e9aa6fc96fab 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ obj-$(CONFIG_ACRN_HSM) += acrn/ obj-$(CONFIG_EFI_SECRET) += coco/efi_secret/ obj-$(CONFIG_SEV_GUEST) += coco/sev-guest/ +obj-$(CONFIG_INTEL_TDX_GUEST) += coco/tdx-guest/ diff --git a/drivers/virt/coco/tdx-guest/Kconfig b/drivers/virt/coco/tdx-guest/Kconfig new file mode 100644 index 000000000000..14246fc2fb02 --- /dev/null +++ b/drivers/virt/coco/tdx-guest/Kconfig @@ -0,0 +1,10 @@ +config TDX_GUEST_DRIVER + tristate "TDX Guest driver" + depends on INTEL_TDX_GUEST + help + The driver provides userspace interface to communicate with + the TDX module to request the TDX guest details like attestation + report. + + To compile this driver as module, choose M here. The module will + be called tdx-guest. diff --git a/drivers/virt/coco/tdx-guest/Makefile b/drivers/virt/coco/tdx-guest/Makefile new file mode 100644 index 000000000000..4396ec319589 --- /dev/null +++ b/drivers/virt/coco/tdx-guest/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_TDX_GUEST_DRIVER) += tdx-guest.o diff --git a/drivers/virt/coco/tdx-guest/tdx-guest.c b/drivers/virt/coco/tdx-guest/tdx-guest.c new file mode 100644 index 000000000000..568eeff35901 --- /dev/null +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TDX guest user interface driver + * + * Copyright (C) 2022 Intel Corporation + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/string.h> +#include <linux/uaccess.h> + +#include <uapi/linux/tdx-guest.h> + +#include <asm/cpu_device_id.h> +#include <asm/tdx.h> + +static long tdx_get_report(void __user *argp) +{ + u8 *reportdata, *tdreport; + struct tdx_report_req req; + 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. + */ + if (req.subtype || req.rpd_len != TDX_REPORTDATA_LEN || + req.tdr_len != TDX_REPORT_LEN) { + pr_err("TDX_CMD_GET_REPORT: invalid req: subtype:%u rpd_len:%u tdr_len:%u\n", + req.subtype, req.rpd_len, req.tdr_len); + return -EINVAL; + } + + if (memchr_inv(req.reserved, 0, sizeof(req.reserved))) { + pr_err("TDX_CMD_GET_REPORT: Non zero value in reserved field\n"); + return -EINVAL; + } + + reportdata = kmalloc(req.rpd_len, GFP_KERNEL); + if (!reportdata) + return -ENOMEM; + + tdreport = kzalloc(req.tdr_len, GFP_KERNEL); + if (!tdreport) { + ret = -ENOMEM; + goto out; + } + + 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 */ + ret = tdx_mcall_get_report(reportdata, tdreport, req.subtype); + if (ret) { + pr_err("TDX_CMD_GET_REPORT: TDCALL failed\n"); + 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) +{ + switch (cmd) { + case TDX_CMD_GET_REPORT: + return tdx_get_report((void __user *)arg); + default: + return -ENOTTY; + } +} + +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 = KBUILD_MODNAME, + .minor = MISC_DYNAMIC_MINOR, + .fops = &tdx_guest_fops, +}; + +static int __init tdx_guest_init(void) +{ + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) + return -ENODEV; + + return misc_register(&tdx_misc_dev); +} +module_init(tdx_guest_init); + +static void __exit tdx_guest_exit(void) +{ + misc_deregister(&tdx_misc_dev); +} +module_exit(tdx_guest_exit); + +#ifdef MODULE +static const struct x86_cpu_id tdx_guest_ids[] = { + X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids); +#endif + +MODULE_AUTHOR("Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>"); +MODULE_DESCRIPTION("TDX Guest Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/tdx-guest.h b/include/uapi/linux/tdx-guest.h new file mode 100644 index 000000000000..fd71774a90ac --- /dev/null +++ b/include/uapi/linux/tdx-guest.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_TDX_GUEST_H_ +#define _UAPI_LINUX_TDX_GUEST_H_ + +#include <linux/ioctl.h> +#include <linux/types.h> + +/* 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 a 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 the TDX Module specification, + * but added a parameter to handle future extension). + * @reserved: Reserved entries to handle future requirements. Should be + * filled with zeroes. + * + * 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', 1, struct tdx_report_req) + +#endif /* _UAPI_LINUX_TDX_GUEST_H_ */