diff mbox series

[v3,3/8] x86/virt/tdx: Prepare to support reading other global metadata fields

Message ID 0403cdb142b40b9838feeb222eb75a4831f6b46d.1724741926.git.kai.huang@intel.com (mailing list archive)
State New, archived
Headers show
Series TDX host: metadata reading tweaks, bug fix and info dump | expand

Commit Message

Kai Huang Aug. 27, 2024, 7:14 a.m. UTC
The TDX module provides a set of "global metadata fields".  They report
things like TDX module version, supported features, and fields related
to create/run TDX guests and so on.

For now the kernel only reads "TD Memory Region" (TDMR) related fields
for module initialization.  There are both immediate needs to read more
fields for module initialization and near-future needs for other kernel
components like KVM to run TDX guests.
will be organized in different structures depending on their meanings.

For now the kernel only reads TDMR related fields.  The TD_SYSINFO_MAP()
macro hard-codes the 'struct tdx_sys_info_tdmr' instance name.  To make
it work with different instances of different structures, extend it to
take the structure instance name as an argument.

This also means the current code which uses TD_SYSINFO_MAP() must type
'struct tdx_sys_info_tdmr' instance name explicitly for each use.  To
make the code easier to read, add a wrapper TD_SYSINFO_MAP_TDMR_INFO()
which hides the instance name.

TDX also support 8/16/32/64 bits metadata field element sizes.  For now
all TDMR related fields are 16-bit long thus the kernel only has one
helper:

  static int read_sys_metadata_field16(u64 field_id, u16 *val) {}

Future changes will need to read more metadata fields with different
element sizes.  To make the code short, extend the helper to take a
'void *' buffer and the buffer size so it can work with all element
sizes.

Note in this way the helper loses the type safety and the build-time
check inside the helper cannot work anymore because the compiler cannot
determine the exact value of the buffer size.

To resolve those, add a wrapper of the helper which only works with
u8/u16/u32/u64 directly and do build-time check, where the compiler
can easily know both the element size (from field ID) and the buffer
size(using sizeof()), before calling the helper.

An alternative option is to provide one helper for each element size:

  static int read_sys_metadata_field8(u64 field_id, u8 *val) {}
  static int read_sys_metadata_field16(u64 field_id, u16 *val) {}
  ...

But this will result in duplicated code given those helpers will look
exactly the same except for the type of buffer pointer.  It's feasible
to define a macro for the body of the helper and define one entry for
each element size to reduce the code, but it is a little bit
over-engineering.

Signed-off-by: Kai Huang <kai.huang@intel.com>
---

v2 -> v3:
 - Rename read_sys_metadata_field() to tdh_sys_rd() so the former can be
   used as the high level wrapper.  Get rid of "stbuf_" prefix since
   people don't like it.
 
 - Rewrite after removing 'struct field_mapping' and reimplementing
   TD_SYSINFO_MAP().
 
---
 arch/x86/virt/vmx/tdx/tdx.c | 45 +++++++++++++++++++++----------------
 arch/x86/virt/vmx/tdx/tdx.h |  3 ++-
 2 files changed, 28 insertions(+), 20 deletions(-)

Comments

Adrian Hunter Aug. 30, 2024, 6:43 a.m. UTC | #1
On 27/08/24 10:14, Kai Huang wrote:
> The TDX module provides a set of "global metadata fields".  They report

Global Metadata Fields

> things like TDX module version, supported features, and fields related
> to create/run TDX guests and so on.
> 
> For now the kernel only reads "TD Memory Region" (TDMR) related fields
> for module initialization.  There are both immediate needs to read more
> fields for module initialization and near-future needs for other kernel
> components like KVM to run TDX guests.
> will be organized in different structures depending on their meanings.

Line above seems lost

> 
> For now the kernel only reads TDMR related fields.  The TD_SYSINFO_MAP()
> macro hard-codes the 'struct tdx_sys_info_tdmr' instance name.  To make
> it work with different instances of different structures, extend it to
> take the structure instance name as an argument.
> 
> This also means the current code which uses TD_SYSINFO_MAP() must type
> 'struct tdx_sys_info_tdmr' instance name explicitly for each use.  To
> make the code easier to read, add a wrapper TD_SYSINFO_MAP_TDMR_INFO()
> which hides the instance name.

Note, were you to accept my suggestion for patch 2, TD_SYSINFO_MAP() would
have gone away, and no changes would be needed to get_tdx_sys_info_tdmr().
So the above 2 paragraphs could be dropped.

> 
> TDX also support 8/16/32/64 bits metadata field element sizes.  For now
> all TDMR related fields are 16-bit long thus the kernel only has one
> helper:
> 
>   static int read_sys_metadata_field16(u64 field_id, u16 *val) {}
> 
> Future changes will need to read more metadata fields with different
> element sizes.  To make the code short, extend the helper to take a
> 'void *' buffer and the buffer size so it can work with all element
> sizes.
> 
> Note in this way the helper loses the type safety and the build-time
> check inside the helper cannot work anymore because the compiler cannot
> determine the exact value of the buffer size.
> 
> To resolve those, add a wrapper of the helper which only works with
> u8/u16/u32/u64 directly and do build-time check, where the compiler
> can easily know both the element size (from field ID) and the buffer
> size(using sizeof()), before calling the helper.
> 
> An alternative option is to provide one helper for each element size:

IMHO, it is not necessary to describe alternative options. Better to
explain why the actual code change is good, rather than why something
that wasn't done, wasn't so good.  That discussion can stay on the
mailing list.  For example, this commit message can just have:

  Add a build-time check that the element size is correct, which
  enables a common function to safely read data of different sizes.

Perhaps end up with:

  TDX supports 8/16/32/64 bit metadata field element sizes.  For now all
  TDMR related fields are 16-bits long.  Future changes will need to read
  more metadata fields with different element sizes.

  So add a build-time check that the element size is correct, which
  enables a common function to safely read data of different sizes.

> 
>   static int read_sys_metadata_field8(u64 field_id, u8 *val) {}
>   static int read_sys_metadata_field16(u64 field_id, u16 *val) {}
>   ...
> 
> But this will result in duplicated code given those helpers will look
> exactly the same except for the type of buffer pointer.  It's feasible
> to define a macro for the body of the helper and define one entry for
> each element size to reduce the code, but it is a little bit
> over-engineering.
> 
> Signed-off-by: Kai Huang <kai.huang@intel.com>
> ---
> 
> v2 -> v3:
>  - Rename read_sys_metadata_field() to tdh_sys_rd() so the former can be
>    used as the high level wrapper.  Get rid of "stbuf_" prefix since
>    people don't like it.
>  
>  - Rewrite after removing 'struct field_mapping' and reimplementing
>    TD_SYSINFO_MAP().
>  
> ---
>  arch/x86/virt/vmx/tdx/tdx.c | 45 +++++++++++++++++++++----------------
>  arch/x86/virt/vmx/tdx/tdx.h |  3 ++-
>  2 files changed, 28 insertions(+), 20 deletions(-)
> 
> diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> index 7e75c1b10838..1cd9035c783f 100644
> --- a/arch/x86/virt/vmx/tdx/tdx.c
> +++ b/arch/x86/virt/vmx/tdx/tdx.c
> @@ -250,7 +250,7 @@ static int build_tdx_memlist(struct list_head *tmb_list)
>  	return ret;
>  }
>  
> -static int read_sys_metadata_field(u64 field_id, u64 *data)
> +static int tdh_sys_rd(u64 field_id, u64 *data)
>  {
>  	struct tdx_module_args args = {};
>  	int ret;
> @@ -270,43 +270,50 @@ static int read_sys_metadata_field(u64 field_id, u64 *data)
>  	return 0;
>  }
>  
> -static int read_sys_metadata_field16(u64 field_id, u16 *val)
> +static int __read_sys_metadata_field(u64 field_id, void *val, int size)
>  {
>  	u64 tmp;
>  	int ret;
>  
> -	BUILD_BUG_ON(MD_FIELD_ID_ELE_SIZE_CODE(field_id) !=
> -			MD_FIELD_ID_ELE_SIZE_16BIT);
> -
> -	ret = read_sys_metadata_field(field_id, &tmp);
> +	ret = tdh_sys_rd(field_id, &tmp);
>  	if (ret)
>  		return ret;
>  
> -	*val = tmp;
> +	memcpy(val, &tmp, size);
>  
>  	return 0;
>  }
>  
> +/* Wrapper to read one global metadata to u8/u16/u32/u64 */
> +#define read_sys_metadata_field(_field_id, _val)					\
> +	({										\
> +		BUILD_BUG_ON(MD_FIELD_ELE_SIZE(_field_id) != sizeof(typeof(*(_val))));	\
> +		__read_sys_metadata_field(_field_id, _val, sizeof(typeof(*(_val))));	\
> +	})
> +
>  /*
> - * Assumes locally defined @ret and @sysinfo_tdmr to convey the error
> - * code and the 'struct tdx_sys_info_tdmr' instance to fill out.
> + * Read one global metadata field to a member of a structure instance,
> + * assuming locally defined @ret to convey the error code.
>   */
> -#define TD_SYSINFO_MAP(_field_id, _member)						\
> -	({										\
> -		if (!ret)								\
> -			ret = read_sys_metadata_field16(MD_FIELD_ID_##_field_id,	\
> -					&sysinfo_tdmr->_member);			\
> +#define TD_SYSINFO_MAP(_field_id, _stbuf, _member)				\
> +	({									\
> +		if (!ret)							\
> +			ret = read_sys_metadata_field(MD_FIELD_ID_##_field_id,	\
> +					&_stbuf->_member);			\
>  	})
>  
>  static int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_tdmr)
>  {
>  	int ret = 0;
>  
> -	TD_SYSINFO_MAP(MAX_TDMRS,	      max_tdmrs);
> -	TD_SYSINFO_MAP(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr);
> -	TD_SYSINFO_MAP(PAMT_4K_ENTRY_SIZE,    pamt_entry_size[TDX_PS_4K]);
> -	TD_SYSINFO_MAP(PAMT_2M_ENTRY_SIZE,    pamt_entry_size[TDX_PS_2M]);
> -	TD_SYSINFO_MAP(PAMT_1G_ENTRY_SIZE,    pamt_entry_size[TDX_PS_1G]);
> +#define TD_SYSINFO_MAP_TDMR_INFO(_field_id, _member)	\
> +	TD_SYSINFO_MAP(_field_id, sysinfo_tdmr, _member)
> +
> +	TD_SYSINFO_MAP_TDMR_INFO(MAX_TDMRS,	        max_tdmrs);
> +	TD_SYSINFO_MAP_TDMR_INFO(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr);
> +	TD_SYSINFO_MAP_TDMR_INFO(PAMT_4K_ENTRY_SIZE,    pamt_entry_size[TDX_PS_4K]);
> +	TD_SYSINFO_MAP_TDMR_INFO(PAMT_2M_ENTRY_SIZE,    pamt_entry_size[TDX_PS_2M]);
> +	TD_SYSINFO_MAP_TDMR_INFO(PAMT_1G_ENTRY_SIZE,    pamt_entry_size[TDX_PS_1G]);
>  
>  	return ret;
>  }
> diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
> index 148f9b4d1140..7458f6717873 100644
> --- a/arch/x86/virt/vmx/tdx/tdx.h
> +++ b/arch/x86/virt/vmx/tdx/tdx.h
> @@ -53,7 +53,8 @@
>  #define MD_FIELD_ID_ELE_SIZE_CODE(_field_id)	\
>  		(((_field_id) & GENMASK_ULL(33, 32)) >> 32)
>  
> -#define MD_FIELD_ID_ELE_SIZE_16BIT	1
> +#define MD_FIELD_ELE_SIZE(_field_id)	\
> +	(1 << MD_FIELD_ID_ELE_SIZE_CODE(_field_id))
>  
>  struct tdmr_reserved_area {
>  	u64 offset;
Nikolay Borisov Aug. 30, 2024, 9:05 a.m. UTC | #2
On 27.08.24 г. 10:14 ч., Kai Huang wrote:
> The TDX module provides a set of "global metadata fields".  They report
> things like TDX module version, supported features, and fields related
> to create/run TDX guests and so on.
> 
> For now the kernel only reads "TD Memory Region" (TDMR) related fields
> for module initialization.  There are both immediate needs to read more
> fields for module initialization and near-future needs for other kernel
> components like KVM to run TDX guests.
> will be organized in different structures depending on their meanings.
> 
> For now the kernel only reads TDMR related fields.  The TD_SYSINFO_MAP()
> macro hard-codes the 'struct tdx_sys_info_tdmr' instance name.  To make
> it work with different instances of different structures, extend it to
> take the structure instance name as an argument.
> 
> This also means the current code which uses TD_SYSINFO_MAP() must type
> 'struct tdx_sys_info_tdmr' instance name explicitly for each use.  To
> make the code easier to read, add a wrapper TD_SYSINFO_MAP_TDMR_INFO()
> which hides the instance name.
> 
> TDX also support 8/16/32/64 bits metadata field element sizes.  For now
> all TDMR related fields are 16-bit long thus the kernel only has one
> helper:
> 
>    static int read_sys_metadata_field16(u64 field_id, u16 *val) {}
> 
> Future changes will need to read more metadata fields with different
> element sizes.  To make the code short, extend the helper to take a
> 'void *' buffer and the buffer size so it can work with all element
> sizes.
> 
> Note in this way the helper loses the type safety and the build-time
> check inside the helper cannot work anymore because the compiler cannot
> determine the exact value of the buffer size.
> 
> To resolve those, add a wrapper of the helper which only works with
> u8/u16/u32/u64 directly and do build-time check, where the compiler
> can easily know both the element size (from field ID) and the buffer
> size(using sizeof()), before calling the helper.
> 
> An alternative option is to provide one helper for each element size:
> 
>    static int read_sys_metadata_field8(u64 field_id, u8 *val) {}
>    static int read_sys_metadata_field16(u64 field_id, u16 *val) {}
>    ...
> 
> But this will result in duplicated code given those helpers will look
> exactly the same except for the type of buffer pointer.  It's feasible
> to define a macro for the body of the helper and define one entry for
> each element size to reduce the code, but it is a little bit
> over-engineering.
> 
> Signed-off-by: Kai Huang <kai.huang@intel.com>
> ---
> 
> v2 -> v3:
>   - Rename read_sys_metadata_field() to tdh_sys_rd() so the former can be
>     used as the high level wrapper.  Get rid of "stbuf_" prefix since
>     people don't like it.
>   
>   - Rewrite after removing 'struct field_mapping' and reimplementing
>     TD_SYSINFO_MAP().
>   
> ---
>   arch/x86/virt/vmx/tdx/tdx.c | 45 +++++++++++++++++++++----------------
>   arch/x86/virt/vmx/tdx/tdx.h |  3 ++-
>   2 files changed, 28 insertions(+), 20 deletions(-)
> 
> diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> index 7e75c1b10838..1cd9035c783f 100644
> --- a/arch/x86/virt/vmx/tdx/tdx.c
> +++ b/arch/x86/virt/vmx/tdx/tdx.c
> @@ -250,7 +250,7 @@ static int build_tdx_memlist(struct list_head *tmb_list)
>   	return ret;
>   }
>   
> -static int read_sys_metadata_field(u64 field_id, u64 *data)
> +static int tdh_sys_rd(u64 field_id, u64 *data)
>   {
>   	struct tdx_module_args args = {};
>   	int ret;
> @@ -270,43 +270,50 @@ static int read_sys_metadata_field(u64 field_id, u64 *data)
>   	return 0;
>   }
>   
> -static int read_sys_metadata_field16(u64 field_id, u16 *val)
> +static int __read_sys_metadata_field(u64 field_id, void *val, int size)

The type of 'size' should be size_t.

>   {
>   	u64 tmp;
>   	int ret;
>   
> -	BUILD_BUG_ON(MD_FIELD_ID_ELE_SIZE_CODE(field_id) !=
> -			MD_FIELD_ID_ELE_SIZE_16BIT);
> -
> -	ret = read_sys_metadata_field(field_id, &tmp);
> +	ret = tdh_sys_rd(field_id, &tmp);
>   	if (ret)
>   		return ret;
>   
> -	*val = tmp;
> +	memcpy(val, &tmp, size);
>   
>   	return 0;
>   }
>   
> +/* Wrapper to read one global metadata to u8/u16/u32/u64 */
> +#define read_sys_metadata_field(_field_id, _val)					\
> +	({										\
> +		BUILD_BUG_ON(MD_FIELD_ELE_SIZE(_field_id) != sizeof(typeof(*(_val))));	\
> +		__read_sys_metadata_field(_field_id, _val, sizeof(typeof(*(_val))));	\
> +	})
> +
>   /*
> - * Assumes locally defined @ret and @sysinfo_tdmr to convey the error
> - * code and the 'struct tdx_sys_info_tdmr' instance to fill out.
> + * Read one global metadata field to a member of a structure instance,
> + * assuming locally defined @ret to convey the error code.
>    */
> -#define TD_SYSINFO_MAP(_field_id, _member)						\
> -	({										\
> -		if (!ret)								\
> -			ret = read_sys_metadata_field16(MD_FIELD_ID_##_field_id,	\
> -					&sysinfo_tdmr->_member);			\
> +#define TD_SYSINFO_MAP(_field_id, _stbuf, _member)				\
> +	({									\
> +		if (!ret)							\
> +			ret = read_sys_metadata_field(MD_FIELD_ID_##_field_id,	\
> +					&_stbuf->_member);			\
>   	})
>   
>   static int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_tdmr)
>   {
>   	int ret = 0;
>   
> -	TD_SYSINFO_MAP(MAX_TDMRS,	      max_tdmrs);
> -	TD_SYSINFO_MAP(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr);
> -	TD_SYSINFO_MAP(PAMT_4K_ENTRY_SIZE,    pamt_entry_size[TDX_PS_4K]);
> -	TD_SYSINFO_MAP(PAMT_2M_ENTRY_SIZE,    pamt_entry_size[TDX_PS_2M]);
> -	TD_SYSINFO_MAP(PAMT_1G_ENTRY_SIZE,    pamt_entry_size[TDX_PS_1G]);
> +#define TD_SYSINFO_MAP_TDMR_INFO(_field_id, _member)	\
> +	TD_SYSINFO_MAP(_field_id, sysinfo_tdmr, _member)

nit: I guess its a personal preference but honestly I think the amount 
of macro indirection (3 levels) here is crazy, despite each being rather 
simple. Just use TD_SYSINFO_MAP directly, saving the typing of 
"sysinfo_tdmr" doesn't seem like a big deal.

You can probably take it even a bit further and simply opencode 
read_sys_metadata_field macro inside TD_SYSINFO_MAP and be left with 
just it, no ? No other patch in this series uses read_sys_metadata_field 
stand alone, if anything factoring it out could be deferred until the 
first users gets introduced.

> +
> +	TD_SYSINFO_MAP_TDMR_INFO(MAX_TDMRS,	        max_tdmrs);
> +	TD_SYSINFO_MAP_TDMR_INFO(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr);
> +	TD_SYSINFO_MAP_TDMR_INFO(PAMT_4K_ENTRY_SIZE,    pamt_entry_size[TDX_PS_4K]);
> +	TD_SYSINFO_MAP_TDMR_INFO(PAMT_2M_ENTRY_SIZE,    pamt_entry_size[TDX_PS_2M]);
> +	TD_SYSINFO_MAP_TDMR_INFO(PAMT_1G_ENTRY_SIZE,    pamt_entry_size[TDX_PS_1G]);
>   
>   	return ret;
>   }
> diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
> index 148f9b4d1140..7458f6717873 100644
> --- a/arch/x86/virt/vmx/tdx/tdx.h
> +++ b/arch/x86/virt/vmx/tdx/tdx.h
> @@ -53,7 +53,8 @@
>   #define MD_FIELD_ID_ELE_SIZE_CODE(_field_id)	\
>   		(((_field_id) & GENMASK_ULL(33, 32)) >> 32)
>   
> -#define MD_FIELD_ID_ELE_SIZE_16BIT	1
> +#define MD_FIELD_ELE_SIZE(_field_id)	\

That ELE seems a bit ambiguous, ELEM seems more natural and is in line 
with other macros in the kernel.

> +	(1 << MD_FIELD_ID_ELE_SIZE_CODE(_field_id))
>   
>   struct tdmr_reserved_area {
>   	u64 offset;
Kai Huang Aug. 30, 2024, 11:02 a.m. UTC | #3
> > 
> > For now the kernel only reads "TD Memory Region" (TDMR) related fields
> > for module initialization.  There are both immediate needs to read more
> > fields for module initialization and near-future needs for other kernel
> > components like KVM to run TDX guests.
> > will be organized in different structures depending on their meanings.
> 
> Line above seems lost

Hmm.. It should be removed.  I thought I have done the spell check for all the
patches :-(
> 
> > 
> > For now the kernel only reads TDMR related fields.  The TD_SYSINFO_MAP()
> > macro hard-codes the 'struct tdx_sys_info_tdmr' instance name.  To make
> > it work with different instances of different structures, extend it to
> > take the structure instance name as an argument.
> > 
> > This also means the current code which uses TD_SYSINFO_MAP() must type
> > 'struct tdx_sys_info_tdmr' instance name explicitly for each use.  To
> > make the code easier to read, add a wrapper TD_SYSINFO_MAP_TDMR_INFO()
> > which hides the instance name.
> 
> Note, were you to accept my suggestion for patch 2, TD_SYSINFO_MAP() would
> have gone away, and no changes would be needed to get_tdx_sys_info_tdmr().
> So the above 2 paragraphs could be dropped.

Yeah seems better to me.  I'll use your way unless someone objects.

> 
> > 
> > TDX also support 8/16/32/64 bits metadata field element sizes.  For now
> > all TDMR related fields are 16-bit long thus the kernel only has one
> > helper:
> > 
> >   static int read_sys_metadata_field16(u64 field_id, u16 *val) {}
> > 
> > Future changes will need to read more metadata fields with different
> > element sizes.  To make the code short, extend the helper to take a
> > 'void *' buffer and the buffer size so it can work with all element
> > sizes.
> > 
> > Note in this way the helper loses the type safety and the build-time
> > check inside the helper cannot work anymore because the compiler cannot
> > determine the exact value of the buffer size.
> > 
> > To resolve those, add a wrapper of the helper which only works with
> > u8/u16/u32/u64 directly and do build-time check, where the compiler
> > can easily know both the element size (from field ID) and the buffer
> > size(using sizeof()), before calling the helper.
> > 
> > An alternative option is to provide one helper for each element size:
> 
> IMHO, it is not necessary to describe alternative options. 
> 

I am not sure?  My understanding is we should mention those alternatives in
the changelog so that the reviewers can have a better view?

[...]
Kai Huang Aug. 30, 2024, 11:09 a.m. UTC | #4
> > -static int read_sys_metadata_field16(u64 field_id, u16 *val)
> > +static int __read_sys_metadata_field(u64 field_id, void *val, int size)
> 
> The type of 'size' should be size_t.

OK will do.

> 
> >   {
> >   	u64 tmp;
> >   	int ret;
> >   
> > -	BUILD_BUG_ON(MD_FIELD_ID_ELE_SIZE_CODE(field_id) !=
> > -			MD_FIELD_ID_ELE_SIZE_16BIT);
> > -
> > -	ret = read_sys_metadata_field(field_id, &tmp);
> > +	ret = tdh_sys_rd(field_id, &tmp);
> >   	if (ret)
> >   		return ret;
> >   
> > -	*val = tmp;
> > +	memcpy(val, &tmp, size);
> >   
> >   	return 0;
> >   }
> >   
> > +/* Wrapper to read one global metadata to u8/u16/u32/u64 */
> > +#define read_sys_metadata_field(_field_id, _val)					\
> > +	({										\
> > +		BUILD_BUG_ON(MD_FIELD_ELE_SIZE(_field_id) != sizeof(typeof(*(_val))));	\
> > +		__read_sys_metadata_field(_field_id, _val, sizeof(typeof(*(_val))));	\
> > +	})
> > +
> >   /*
> > - * Assumes locally defined @ret and @sysinfo_tdmr to convey the error
> > - * code and the 'struct tdx_sys_info_tdmr' instance to fill out.
> > + * Read one global metadata field to a member of a structure instance,
> > + * assuming locally defined @ret to convey the error code.
> >    */
> > -#define TD_SYSINFO_MAP(_field_id, _member)						\
> > -	({										\
> > -		if (!ret)								\
> > -			ret = read_sys_metadata_field16(MD_FIELD_ID_##_field_id,	\
> > -					&sysinfo_tdmr->_member);			\
> > +#define TD_SYSINFO_MAP(_field_id, _stbuf, _member)				\
> > +	({									\
> > +		if (!ret)							\
> > +			ret = read_sys_metadata_field(MD_FIELD_ID_##_field_id,	\
> > +					&_stbuf->_member);			\
> >   	})
> >   
> >   static int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_tdmr)
> >   {
> >   	int ret = 0;
> >   
> > -	TD_SYSINFO_MAP(MAX_TDMRS,	      max_tdmrs);
> > -	TD_SYSINFO_MAP(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr);
> > -	TD_SYSINFO_MAP(PAMT_4K_ENTRY_SIZE,    pamt_entry_size[TDX_PS_4K]);
> > -	TD_SYSINFO_MAP(PAMT_2M_ENTRY_SIZE,    pamt_entry_size[TDX_PS_2M]);
> > -	TD_SYSINFO_MAP(PAMT_1G_ENTRY_SIZE,    pamt_entry_size[TDX_PS_1G]);
> > +#define TD_SYSINFO_MAP_TDMR_INFO(_field_id, _member)	\
> > +	TD_SYSINFO_MAP(_field_id, sysinfo_tdmr, _member)
> 
> nit: I guess its a personal preference but honestly I think the amount 
> of macro indirection (3 levels) here is crazy, despite each being rather 
> simple. Just use TD_SYSINFO_MAP directly, saving the typing of 
> "sysinfo_tdmr" doesn't seem like a big deal.

We have a different interpretation of "crazy" :-)

> 
> You can probably take it even a bit further and simply opencode 
> read_sys_metadata_field macro inside TD_SYSINFO_MAP and be left with 
> just it, no ? No other patch in this series uses read_sys_metadata_field 
> stand alone, if anything factoring it out could be deferred until the 
> first users gets introduced.

Joke aside, anyway, with what Adrian suggested we just need the
TD_SYSINFO_MAP() but don't need those wrappers anymore.  I'll go with this if
no one says differently.

> 
> > +
> > +	TD_SYSINFO_MAP_TDMR_INFO(MAX_TDMRS,	        max_tdmrs);
> > +	TD_SYSINFO_MAP_TDMR_INFO(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr);
> > +	TD_SYSINFO_MAP_TDMR_INFO(PAMT_4K_ENTRY_SIZE,    pamt_entry_size[TDX_PS_4K]);
> > +	TD_SYSINFO_MAP_TDMR_INFO(PAMT_2M_ENTRY_SIZE,    pamt_entry_size[TDX_PS_2M]);
> > +	TD_SYSINFO_MAP_TDMR_INFO(PAMT_1G_ENTRY_SIZE,    pamt_entry_size[TDX_PS_1G]);
> >   
> >   	return ret;
> >   }
> > diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
> > index 148f9b4d1140..7458f6717873 100644
> > --- a/arch/x86/virt/vmx/tdx/tdx.h
> > +++ b/arch/x86/virt/vmx/tdx/tdx.h
> > @@ -53,7 +53,8 @@
> >   #define MD_FIELD_ID_ELE_SIZE_CODE(_field_id)	\
> >   		(((_field_id) & GENMASK_ULL(33, 32)) >> 32)
> >   
> > -#define MD_FIELD_ID_ELE_SIZE_16BIT	1
> > +#define MD_FIELD_ELE_SIZE(_field_id)	\
> 
> That ELE seems a bit ambiguous, ELEM seems more natural and is in line 
> with other macros in the kernel.

OK will do.  Thanks for the review!
Dan Williams Sept. 6, 2024, 9:34 p.m. UTC | #5
Kai Huang wrote:
> Future changes will need to read more metadata fields with different
> element sizes.  To make the code short, extend the helper to take a
> 'void *' buffer and the buffer size so it can work with all element
> sizes.

The whole point was to have compile time type safety for global struct
member and the read routine. So, no, using 'void *' is a step backwards.

Take some inspiration from build_mmio_read() and just have a macro that
takes the size as an argument to the macro, not the runtime routine. The
macro statically selects the right routine to call and does not need to
grow a size parameter itself.
Kai Huang Sept. 9, 2024, 12:28 p.m. UTC | #6
On Fri, 2024-09-06 at 14:34 -0700, Dan Williams wrote:
> Kai Huang wrote:
> > Future changes will need to read more metadata fields with different
> > element sizes.  To make the code short, extend the helper to take a
> > 'void *' buffer and the buffer size so it can work with all element
> > sizes.
> 
> The whole point was to have compile time type safety for global struct
> member and the read routine. So, no, using 'void *' is a step backwards.
> 
> Take some inspiration from build_mmio_read() and just have a macro that
> takes the size as an argument to the macro, not the runtime routine. 
> 

AFAICT build_mmio_read() macro defines the body of the assembly to read
requested size from a port.  But instead of a single macro which can be used
universally for all port reading, the kernel uses build_mmio_read() to define
multiple read functions:

build_mmio_read(readb, "b", unsigned char, "=q", :"memory")
build_mmio_read(readw, "w", unsigned short, "=r", :"memory")
build_mmio_read(readl, "l", unsigned int, "=r", :"memory")
...

So just to understand correctly, do you mean something like below?

#define build_sysmd_read(_bits) \
static __maybe_unused int       \
__read_sys_metadata_field##_bits(u64 field_id, u##_bits *val)   \
{                                                               \
        u64 tmp;                                                \
        int ret;                                                \
                                                                \
        ret = tdh_sys_rd(field_id, &tmp);                       \
        if (ret)                                                \
                return ret;                                     \
        *val = tmp;                                             \
        return ret;                                             \
}

build_sysmd_read(8)
build_sysmd_read(16)
build_sysmd_read(32)
build_sysmd_read(64)

> The
> macro statically selects the right routine to call and does not need to
> grow a size parameter itself.

Sorry for my ignorance.  Do you mean using switch-case statement to select right
routine?
diff mbox series

Patch

diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 7e75c1b10838..1cd9035c783f 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -250,7 +250,7 @@  static int build_tdx_memlist(struct list_head *tmb_list)
 	return ret;
 }
 
-static int read_sys_metadata_field(u64 field_id, u64 *data)
+static int tdh_sys_rd(u64 field_id, u64 *data)
 {
 	struct tdx_module_args args = {};
 	int ret;
@@ -270,43 +270,50 @@  static int read_sys_metadata_field(u64 field_id, u64 *data)
 	return 0;
 }
 
-static int read_sys_metadata_field16(u64 field_id, u16 *val)
+static int __read_sys_metadata_field(u64 field_id, void *val, int size)
 {
 	u64 tmp;
 	int ret;
 
-	BUILD_BUG_ON(MD_FIELD_ID_ELE_SIZE_CODE(field_id) !=
-			MD_FIELD_ID_ELE_SIZE_16BIT);
-
-	ret = read_sys_metadata_field(field_id, &tmp);
+	ret = tdh_sys_rd(field_id, &tmp);
 	if (ret)
 		return ret;
 
-	*val = tmp;
+	memcpy(val, &tmp, size);
 
 	return 0;
 }
 
+/* Wrapper to read one global metadata to u8/u16/u32/u64 */
+#define read_sys_metadata_field(_field_id, _val)					\
+	({										\
+		BUILD_BUG_ON(MD_FIELD_ELE_SIZE(_field_id) != sizeof(typeof(*(_val))));	\
+		__read_sys_metadata_field(_field_id, _val, sizeof(typeof(*(_val))));	\
+	})
+
 /*
- * Assumes locally defined @ret and @sysinfo_tdmr to convey the error
- * code and the 'struct tdx_sys_info_tdmr' instance to fill out.
+ * Read one global metadata field to a member of a structure instance,
+ * assuming locally defined @ret to convey the error code.
  */
-#define TD_SYSINFO_MAP(_field_id, _member)						\
-	({										\
-		if (!ret)								\
-			ret = read_sys_metadata_field16(MD_FIELD_ID_##_field_id,	\
-					&sysinfo_tdmr->_member);			\
+#define TD_SYSINFO_MAP(_field_id, _stbuf, _member)				\
+	({									\
+		if (!ret)							\
+			ret = read_sys_metadata_field(MD_FIELD_ID_##_field_id,	\
+					&_stbuf->_member);			\
 	})
 
 static int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_tdmr)
 {
 	int ret = 0;
 
-	TD_SYSINFO_MAP(MAX_TDMRS,	      max_tdmrs);
-	TD_SYSINFO_MAP(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr);
-	TD_SYSINFO_MAP(PAMT_4K_ENTRY_SIZE,    pamt_entry_size[TDX_PS_4K]);
-	TD_SYSINFO_MAP(PAMT_2M_ENTRY_SIZE,    pamt_entry_size[TDX_PS_2M]);
-	TD_SYSINFO_MAP(PAMT_1G_ENTRY_SIZE,    pamt_entry_size[TDX_PS_1G]);
+#define TD_SYSINFO_MAP_TDMR_INFO(_field_id, _member)	\
+	TD_SYSINFO_MAP(_field_id, sysinfo_tdmr, _member)
+
+	TD_SYSINFO_MAP_TDMR_INFO(MAX_TDMRS,	        max_tdmrs);
+	TD_SYSINFO_MAP_TDMR_INFO(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr);
+	TD_SYSINFO_MAP_TDMR_INFO(PAMT_4K_ENTRY_SIZE,    pamt_entry_size[TDX_PS_4K]);
+	TD_SYSINFO_MAP_TDMR_INFO(PAMT_2M_ENTRY_SIZE,    pamt_entry_size[TDX_PS_2M]);
+	TD_SYSINFO_MAP_TDMR_INFO(PAMT_1G_ENTRY_SIZE,    pamt_entry_size[TDX_PS_1G]);
 
 	return ret;
 }
diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
index 148f9b4d1140..7458f6717873 100644
--- a/arch/x86/virt/vmx/tdx/tdx.h
+++ b/arch/x86/virt/vmx/tdx/tdx.h
@@ -53,7 +53,8 @@ 
 #define MD_FIELD_ID_ELE_SIZE_CODE(_field_id)	\
 		(((_field_id) & GENMASK_ULL(33, 32)) >> 32)
 
-#define MD_FIELD_ID_ELE_SIZE_16BIT	1
+#define MD_FIELD_ELE_SIZE(_field_id)	\
+	(1 << MD_FIELD_ID_ELE_SIZE_CODE(_field_id))
 
 struct tdmr_reserved_area {
 	u64 offset;