[PATCH v2 03/25] x86/virt/tdx: Read essential global metadata for KVM

Rick Edgecombe posted 25 patches 1 year, 3 months ago
[PATCH v2 03/25] x86/virt/tdx: Read essential global metadata for KVM
Posted by Rick Edgecombe 1 year, 3 months ago
From: Kai Huang <kai.huang@intel.com>

KVM needs two classes of global metadata to create and run TDX guests:

 - "TD Control Structures"
 - "TD Configurability"

The first class contains the sizes of TDX guest per-VM and per-vCPU
control structures.  KVM will need to use them to allocate enough space
for those control structures.

The second class contains info which reports things like which features
are configurable to TDX guest etc.  KVM will need to use them to
properly configure TDX guests.

Read them for KVM TDX to use.

The code change is auto-generated by re-running the script in [1] after
uncommenting the "td_conf" and "td_ctrl" part to regenerate the
tdx_global_metadata.{hc} and update them to the existing ones in the
kernel.

  #python tdx.py global_metadata.json tdx_global_metadata.h \
	tdx_global_metadata.c

The 'global_metadata.json' can be fetched from [2].

Link: https://lore.kernel.org/kvm/0853b155ec9aac09c594caa60914ed6ea4dc0a71.camel@intel.com/ [1]
Link: https://cdrdv2.intel.com/v1/dl/getContent/795381 [2]
Signed-off-by: Kai Huang <kai.huang@intel.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
uAPI breakout v2:
 - New patch
---
 arch/x86/include/asm/tdx_global_metadata.h  | 19 +++++++++
 arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 46 +++++++++++++++++++++
 2 files changed, 65 insertions(+)

diff --git a/arch/x86/include/asm/tdx_global_metadata.h b/arch/x86/include/asm/tdx_global_metadata.h
index fde370b855f1..206090c9952f 100644
--- a/arch/x86/include/asm/tdx_global_metadata.h
+++ b/arch/x86/include/asm/tdx_global_metadata.h
@@ -32,11 +32,30 @@ struct tdx_sys_info_cmr {
 	u64 cmr_size[32];
 };
 
+struct tdx_sys_info_td_ctrl {
+	u16 tdr_base_size;
+	u16 tdcs_base_size;
+	u16 tdvps_base_size;
+};
+
+struct tdx_sys_info_td_conf {
+	u64 attributes_fixed0;
+	u64 attributes_fixed1;
+	u64 xfam_fixed0;
+	u64 xfam_fixed1;
+	u16 num_cpuid_config;
+	u16 max_vcpus_per_td;
+	u64 cpuid_config_leaves[32];
+	u64 cpuid_config_values[32][2];
+};
+
 struct tdx_sys_info {
 	struct tdx_sys_info_version version;
 	struct tdx_sys_info_features features;
 	struct tdx_sys_info_tdmr tdmr;
 	struct tdx_sys_info_cmr cmr;
+	struct tdx_sys_info_td_ctrl td_ctrl;
+	struct tdx_sys_info_td_conf td_conf;
 };
 
 #endif
diff --git a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
index 2fe57e084453..44c2b3e079de 100644
--- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
+++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
@@ -76,6 +76,50 @@ static int get_tdx_sys_info_cmr(struct tdx_sys_info_cmr *sysinfo_cmr)
 	return ret;
 }
 
+static int get_tdx_sys_info_td_ctrl(struct tdx_sys_info_td_ctrl *sysinfo_td_ctrl)
+{
+	int ret = 0;
+	u64 val;
+
+	if (!ret && !(ret = read_sys_metadata_field(0x9800000100000000, &val)))
+		sysinfo_td_ctrl->tdr_base_size = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x9800000100000100, &val)))
+		sysinfo_td_ctrl->tdcs_base_size = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x9800000100000200, &val)))
+		sysinfo_td_ctrl->tdvps_base_size = val;
+
+	return ret;
+}
+
+static int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sysinfo_td_conf)
+{
+	int ret = 0;
+	u64 val;
+	int i, j;
+
+	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000000, &val)))
+		sysinfo_td_conf->attributes_fixed0 = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000001, &val)))
+		sysinfo_td_conf->attributes_fixed1 = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000002, &val)))
+		sysinfo_td_conf->xfam_fixed0 = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000003, &val)))
+		sysinfo_td_conf->xfam_fixed1 = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x9900000100000004, &val)))
+		sysinfo_td_conf->num_cpuid_config = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x9900000100000008, &val)))
+		sysinfo_td_conf->max_vcpus_per_td = val;
+	for (i = 0; i < sysinfo_td_conf->num_cpuid_config; i++)
+		if (!ret && !(ret = read_sys_metadata_field(0x9900000300000400 + i, &val)))
+			sysinfo_td_conf->cpuid_config_leaves[i] = val;
+	for (i = 0; i < sysinfo_td_conf->num_cpuid_config; i++)
+		for (j = 0; j < 2; j++)
+			if (!ret && !(ret = read_sys_metadata_field(0x9900000300000500 + i * 2 + j, &val)))
+				sysinfo_td_conf->cpuid_config_values[i][j] = val;
+
+	return ret;
+}
+
 static int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
 {
 	int ret = 0;
@@ -84,6 +128,8 @@ static int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
 	ret = ret ?: get_tdx_sys_info_features(&sysinfo->features);
 	ret = ret ?: get_tdx_sys_info_tdmr(&sysinfo->tdmr);
 	ret = ret ?: get_tdx_sys_info_cmr(&sysinfo->cmr);
+	ret = ret ?: get_tdx_sys_info_td_ctrl(&sysinfo->td_ctrl);
+	ret = ret ?: get_tdx_sys_info_td_conf(&sysinfo->td_conf);
 
 	return ret;
 }
-- 
2.47.0
Re: [PATCH v2 03/25] x86/virt/tdx: Read essential global metadata for KVM
Posted by Xiaoyao Li 1 year, 2 months ago
On 10/31/2024 3:00 AM, Rick Edgecombe wrote:
> From: Kai Huang <kai.huang@intel.com>
> 
> KVM needs two classes of global metadata to create and run TDX guests:
> 
>   - "TD Control Structures"
>   - "TD Configurability"
> 
> The first class contains the sizes of TDX guest per-VM and per-vCPU
> control structures.  KVM will need to use them to allocate enough space
> for those control structures.
> 
> The second class contains info which reports things like which features
> are configurable to TDX guest etc.  KVM will need to use them to
> properly configure TDX guests.
> 
> Read them for KVM TDX to use.
> 
> The code change is auto-generated by re-running the script in [1] after
> uncommenting the "td_conf" and "td_ctrl" part to regenerate the
> tdx_global_metadata.{hc} and update them to the existing ones in the
> kernel.
> 
>    #python tdx.py global_metadata.json tdx_global_metadata.h \
> 	tdx_global_metadata.c
> 
> The 'global_metadata.json' can be fetched from [2].
> 
> Link: https://lore.kernel.org/kvm/0853b155ec9aac09c594caa60914ed6ea4dc0a71.camel@intel.com/ [1]
> Link: https://cdrdv2.intel.com/v1/dl/getContent/795381 [2]
> Signed-off-by: Kai Huang <kai.huang@intel.com>
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> ---
> uAPI breakout v2:
>   - New patch
> ---
>   arch/x86/include/asm/tdx_global_metadata.h  | 19 +++++++++
>   arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 46 +++++++++++++++++++++
>   2 files changed, 65 insertions(+)
> 
> diff --git a/arch/x86/include/asm/tdx_global_metadata.h b/arch/x86/include/asm/tdx_global_metadata.h
> index fde370b855f1..206090c9952f 100644
> --- a/arch/x86/include/asm/tdx_global_metadata.h
> +++ b/arch/x86/include/asm/tdx_global_metadata.h
> @@ -32,11 +32,30 @@ struct tdx_sys_info_cmr {
>   	u64 cmr_size[32];
>   };
>   
> +struct tdx_sys_info_td_ctrl {
> +	u16 tdr_base_size;
> +	u16 tdcs_base_size;
> +	u16 tdvps_base_size;
> +};
> +
> +struct tdx_sys_info_td_conf {
> +	u64 attributes_fixed0;
> +	u64 attributes_fixed1;
> +	u64 xfam_fixed0;
> +	u64 xfam_fixed1;
> +	u16 num_cpuid_config;
> +	u16 max_vcpus_per_td;
> +	u64 cpuid_config_leaves[32];
> +	u64 cpuid_config_values[32][2];
> +};
> +
>   struct tdx_sys_info {
>   	struct tdx_sys_info_version version;
>   	struct tdx_sys_info_features features;
>   	struct tdx_sys_info_tdmr tdmr;
>   	struct tdx_sys_info_cmr cmr;
> +	struct tdx_sys_info_td_ctrl td_ctrl;
> +	struct tdx_sys_info_td_conf td_conf;
>   };
>   
>   #endif
> diff --git a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
> index 2fe57e084453..44c2b3e079de 100644
> --- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
> +++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
> @@ -76,6 +76,50 @@ static int get_tdx_sys_info_cmr(struct tdx_sys_info_cmr *sysinfo_cmr)
>   	return ret;
>   }
>   
> +static int get_tdx_sys_info_td_ctrl(struct tdx_sys_info_td_ctrl *sysinfo_td_ctrl)
> +{
> +	int ret = 0;
> +	u64 val;
> +
> +	if (!ret && !(ret = read_sys_metadata_field(0x9800000100000000, &val)))
> +		sysinfo_td_ctrl->tdr_base_size = val;
> +	if (!ret && !(ret = read_sys_metadata_field(0x9800000100000100, &val)))
> +		sysinfo_td_ctrl->tdcs_base_size = val;
> +	if (!ret && !(ret = read_sys_metadata_field(0x9800000100000200, &val)))
> +		sysinfo_td_ctrl->tdvps_base_size = val;
> +
> +	return ret;
> +}
> +
> +static int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sysinfo_td_conf)
> +{
> +	int ret = 0;
> +	u64 val;
> +	int i, j;
> +
> +	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000000, &val)))
> +		sysinfo_td_conf->attributes_fixed0 = val;
> +	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000001, &val)))
> +		sysinfo_td_conf->attributes_fixed1 = val;
> +	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000002, &val)))
> +		sysinfo_td_conf->xfam_fixed0 = val;
> +	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000003, &val)))
> +		sysinfo_td_conf->xfam_fixed1 = val;
> +	if (!ret && !(ret = read_sys_metadata_field(0x9900000100000004, &val)))
> +		sysinfo_td_conf->num_cpuid_config = val;
> +	if (!ret && !(ret = read_sys_metadata_field(0x9900000100000008, &val)))
> +		sysinfo_td_conf->max_vcpus_per_td = val;
> +	for (i = 0; i < sysinfo_td_conf->num_cpuid_config; i++)

It is not safe. We need to check

	sysinfo_td_conf->num_cpuid_config <= 32.

If the TDX module version is not matched with the json file that was 
used to generate the tdx_global_metadata.h, the num_cpuid_config 
reported by the actual TDX module might exceed 32 which causes 
out-of-bound array access.

> +		if (!ret && !(ret = read_sys_metadata_field(0x9900000300000400 + i, &val)))
> +			sysinfo_td_conf->cpuid_config_leaves[i] = val;
> +	for (i = 0; i < sysinfo_td_conf->num_cpuid_config; i++)
> +		for (j = 0; j < 2; j++)
> +			if (!ret && !(ret = read_sys_metadata_field(0x9900000300000500 + i * 2 + j, &val)))
> +				sysinfo_td_conf->cpuid_config_values[i][j] = val;
> +
> +	return ret;
> +}
> +
>   static int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
>   {
>   	int ret = 0;
> @@ -84,6 +128,8 @@ static int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
>   	ret = ret ?: get_tdx_sys_info_features(&sysinfo->features);
>   	ret = ret ?: get_tdx_sys_info_tdmr(&sysinfo->tdmr);
>   	ret = ret ?: get_tdx_sys_info_cmr(&sysinfo->cmr);
> +	ret = ret ?: get_tdx_sys_info_td_ctrl(&sysinfo->td_ctrl);
> +	ret = ret ?: get_tdx_sys_info_td_conf(&sysinfo->td_conf);
>   
>   	return ret;
>   }
Re: [PATCH v2 03/25] x86/virt/tdx: Read essential global metadata for KVM
Posted by Huang, Kai 1 year, 2 months ago
On Fri, 2024-12-06 at 16:37 +0800, Xiaoyao Li wrote:
>  +static int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sysinfo_td_conf)
> > +{
> > +	int ret = 0;
> > +	u64 val;
> > +	int i, j;
> > +
> > +	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000000, &val)))
> > +		sysinfo_td_conf->attributes_fixed0 = val;
> > +	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000001, &val)))
> > +		sysinfo_td_conf->attributes_fixed1 = val;
> > +	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000002, &val)))
> > +		sysinfo_td_conf->xfam_fixed0 = val;
> > +	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000003, &val)))
> > +		sysinfo_td_conf->xfam_fixed1 = val;
> > +	if (!ret && !(ret = read_sys_metadata_field(0x9900000100000004, &val)))
> > +		sysinfo_td_conf->num_cpuid_config = val;
> > +	if (!ret && !(ret = read_sys_metadata_field(0x9900000100000008, &val)))
> > +		sysinfo_td_conf->max_vcpus_per_td = val;
> > +	for (i = 0; i < sysinfo_td_conf->num_cpuid_config; i++)
> 
> It is not safe. We need to check
> 
> 	sysinfo_td_conf->num_cpuid_config <= 32.
> 
> If the TDX module version is not matched with the json file that was 
> used to generate the tdx_global_metadata.h, the num_cpuid_config 
> reported by the actual TDX module might exceed 32 which causes 
> out-of-bound array access.

+Dave.

I thought 32 (which is also auto-generated from the "Num Fields" in the JSON
file) is architectural, but looking at the TDX 1.5 spec, it seems there's no
place mentioning such.

I think we can add:

	if (sysinfo_td_conf->num_cpuid_config <= 32)
		return -EINVAL;

.. which will make reading global metadata failure, and result in module
initialization failure.  Basically it means if one day some TDX module comes
with >32 entries, some old versions of kernel won't be able to supportit.  But I
think it should be fine.

This also reminds me reading the CMRs is similar, but I don't think we need to
do similar check for reading CMRs because "maximum number of CMRs is 32" is
architectural behaviour as mentioned in the TDX 1.5 base spec:

4.1.3.1 Intel TDX ISA Background: Convertible Memory Ranges (CMRs)

...

* The maximum number of CMRs is implementation specific. It is not explicitly
enumerated; it is deduced from Family/Model/Stepping information provided by
CPUID.
   * The maximum number of CMRs is 32.

Hi Dave,

Do you have any comments?
Re: [PATCH v2 03/25] x86/virt/tdx: Read essential global metadata for KVM
Posted by Dave Hansen 1 year, 2 months ago
On 12/6/24 08:13, Huang, Kai wrote:
> It is not safe. We need to check
> 
>       sysinfo_td_conf->num_cpuid_config <= 32.
> 
> If the TDX module version is not matched with the json file that was
> used to generate the tdx_global_metadata.h, the num_cpuid_config
> reported by the actual TDX module might exceed 32 which causes
> out-of-bound array access.

The JSON *IS* the ABI description. It can't change between versions of
the TDX module. It can only be extended. The "32" is not in the spec
because the spec refers to the JSON!
RE: [PATCH v2 03/25] x86/virt/tdx: Read essential global metadata for KVM
Posted by Huang, Kai 1 year, 2 months ago
> On 12/6/24 08:13, Huang, Kai wrote:
> > It is not safe. We need to check
> >
> >       sysinfo_td_conf->num_cpuid_config <= 32.
> >
> > If the TDX module version is not matched with the json file that was
> > used to generate the tdx_global_metadata.h, the num_cpuid_config
> > reported by the actual TDX module might exceed 32 which causes
> > out-of-bound array access.
> 
> The JSON *IS* the ABI description. It can't change between versions of the
> TDX module. It can only be extended. The "32" is not in the spec because the
> spec refers to the JSON!

Ah, yeah, agreed, the "spec refers to the JSON".  :-)
Re: [PATCH v2 03/25] x86/virt/tdx: Read essential global metadata for KVM
Posted by Edgecombe, Rick P 1 year, 1 month ago
On Sat, 2024-12-07 at 00:00 +0000, Huang, Kai wrote:
> > On 12/6/24 08:13, Huang, Kai wrote:
> > > It is not safe. We need to check
> > > 
> > >        sysinfo_td_conf->num_cpuid_config <= 32.
> > > 
> > > If the TDX module version is not matched with the json file that was
> > > used to generate the tdx_global_metadata.h, the num_cpuid_config
> > > reported by the actual TDX module might exceed 32 which causes
> > > out-of-bound array access.
> > 
> > The JSON *IS* the ABI description. It can't change between versions of the
> > TDX module. It can only be extended. The "32" is not in the spec because the
> > spec refers to the JSON!
> 
> Ah, yeah, agreed, the "spec refers to the JSON".  :-)

So we heard back from TDX module folks that they were thinking the 32 could
change to be larger (thanks Kai for checking). We need to continue education
with them around what KVM is depending on as TDX Module ABI. And we should get
something clearer than these JSONs.

But in the meantime, we could tell TDX module team they need an opt-in to change
this field. We could also add an actual check to fail cleanly:

diff --git a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
index 44c2b3e079de..744549bdf1dd 100644
--- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
+++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
@@ -97,6 +97,10 @@ static int get_tdx_sys_info_td_conf(struct
tdx_sys_info_td_conf *sysinfo_td_conf
        u64 val;
        int i, j;
 
+       if (sysinfo_td_conf->num_cpuid_config >
+           ARRAY_SIZE(sysinfo_td_conf->cpuid_config_leaves))
+               return 1;
+
        if (!ret && !(ret = read_sys_metadata_field(0x1900000300000000, &val)))
                sysinfo_td_conf->attributes_fixed0 = val;
        if (!ret && !(ret = read_sys_metadata_field(0x1900000300000001, &val)))

Or we could dynamically allocate these arrays based on num_cpuid_config.

I'd lean towards switching to the dynamic allocation, because it will be cleaner
and less churn for this array expanding in the future.


Re: [PATCH v2 03/25] x86/virt/tdx: Read essential global metadata for KVM
Posted by Huang, Kai 1 year, 1 month ago

On 12/12/2024 8:31 am, Edgecombe, Rick P wrote:
> On Sat, 2024-12-07 at 00:00 +0000, Huang, Kai wrote:
>>> On 12/6/24 08:13, Huang, Kai wrote:
>>>> It is not safe. We need to check
>>>>
>>>>         sysinfo_td_conf->num_cpuid_config <= 32.
>>>>
>>>> If the TDX module version is not matched with the json file that was
>>>> used to generate the tdx_global_metadata.h, the num_cpuid_config
>>>> reported by the actual TDX module might exceed 32 which causes
>>>> out-of-bound array access.
>>>
>>> The JSON *IS* the ABI description. It can't change between versions of the
>>> TDX module. It can only be extended. The "32" is not in the spec because the
>>> spec refers to the JSON!
>>
>> Ah, yeah, agreed, the "spec refers to the JSON".  :-)
> 
> So we heard back from TDX module folks that they were thinking the 32 could
> change to be larger (thanks Kai for checking). We need to continue education
> with them around what KVM is depending on as TDX Module ABI. And we should get
> something clearer than these JSONs.
> 
> But in the meantime, we could tell TDX module team they need an opt-in to change
> this field. We could also add an actual check to fail cleanly:
> 

Hi Paolo/Sean/Dave,

TDX module team has acked changing 32 to a higher value in future 
modules is a breaking of ABI.  They also promised 128 is the maximum 
value they reserved for CPUID_CONFIGs thus won't change for all modules. 
  They will update the JSON to address.

I just send out an updated v2.1 of this patch to bump array size for 
CPUID_CONFIGs to 128 and add paranoid checks to protect kernel from 
potential TDX module breakage on this.

Appreciate if you can help to review, but for now, wish you have a 
wonderful Christmas :-)
Re: [PATCH v2 03/25] x86/virt/tdx: Read essential global metadata for KVM
Posted by Huang, Kai 1 year, 2 months ago
On Fri, 2024-12-06 at 16:13 +0000, Huang, Kai wrote:
> I think we can add:
> 
> 	if (sysinfo_td_conf->num_cpuid_config <= 32)
> 		return -EINVAL;

Sorry it should be:

	if (sysinfo_td_conf->num_cpuid_config > 32)
		return -EINVAL;
[PATCH v2.1 03/25] x86/virt/tdx: Read essential global metadata for KVM
Posted by Kai Huang 1 year, 1 month ago
KVM needs two classes of global metadata to create and run TDX guests:

 - "TD Control Structures"
 - "TD Configurability"

The first class contains the sizes of TDX guest per-VM and per-vCPU
control structures.  KVM will need to use them to allocate enough space
for those control structures.

The second class contains info which reports things like which features
are configurable to TDX guests.  KVM will need to use them to properly
configure TDX guests.

Read them for KVM TDX to use.

Basically, the code change is auto-generated by adding below to the
script in [1]:

    "td_ctrl": [
        "TDR_BASE_SIZE",
        "TDCS_BASE_SIZE",
        "TDVPS_BASE_SIZE",
    ],
    "td_conf": [
        "ATTRIBUTES_FIXED0",
        "ATTRIBUTES_FIXED1",
        "XFAM_FIXED0",
        "XFAM_FIXED1",
        "NUM_CPUID_CONFIG",
        "MAX_VCPUS_PER_TD",
        "CPUID_CONFIG_LEAVES",
        "CPUID_CONFIG_VALUES",
    ],

.. and re-running the script:

  #python tdx_global_metadata.py global_metadata.json \
  	tdx_global_metadata.h tdx_global_metadata.c

.. but unfortunately with some tweaks:

The "Intel TDX Module v1.5.09 ABI Definitions" JSON files[2], which
describe the TDX module ABI to the kernel, were expected to maintain
backward compatibility.  However, it turns out there are plans to change
the JSON per module release.  Specifically, the maximum number of
CPUID_CONFIGs, i.e., CPUID_CONFIG_{LEAVES|VALUES} is one of the fields
expected to change.

This is obviously problematic for the kernel, and needs to be addressed
by the TDX Module team.  Negotiations on clarifying ABI boundary in the
spec for future models are ongoing.  In the meantime, the TDX module
team has agreed to not increase this specific field beyond 128 entries
without an opt in.

So for now just tweak the JSON to change "Num Fields" from 32 to 128 and
generate a fixed-size (128) array for CPUID_CONFIG_{LEAVES|VALUES}.

Also, due to all those ABI breakages (and module bugs), be paranoid by
generating additional checks to make sure NUM_CPUID_CONFIG will never
exceed the array size of CPUID_CONFIG_{LEAVES|VALUES} to protect the
kernel from the module breakages.  With those checks, detecting a
breakage will just result in module initialization failure.

Link: https://lore.kernel.org/762a50133300710771337398284567b299a86f67.camel@intel.com/ [1]
Link: https://cdrdv2.intel.com/v1/dl/getContent/795381 [2]
Signed-off-by: Kai Huang <kai.huang@intel.com>
---

v2 -> v2.1
 - Bump array size for CPUID_CONFIGs to 128
 - Add paranoid checks to protect against incorrect NUM_CPUID_CONFIG.
 - Update changelog accordingly.

 Note: this is based on kvm-coco-queue which has v7 of TDX host metadata
 series which has patches to read TDX module version and CMRs.  It will
 have conflicts to resolve when rebasing to the v9 patches currently
 queued in tip/x86/tdx.

uAPI breakout v2:
 - New patch

---
 arch/x86/include/asm/tdx_global_metadata.h  | 19 ++++++++
 arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 50 +++++++++++++++++++++
 2 files changed, 69 insertions(+)

diff --git a/arch/x86/include/asm/tdx_global_metadata.h b/arch/x86/include/asm/tdx_global_metadata.h
index fde370b855f1..cfef9e5e4d93 100644
--- a/arch/x86/include/asm/tdx_global_metadata.h
+++ b/arch/x86/include/asm/tdx_global_metadata.h
@@ -32,11 +32,30 @@ struct tdx_sys_info_cmr {
 	u64 cmr_size[32];
 };
 
+struct tdx_sys_info_td_ctrl {
+	u16 tdr_base_size;
+	u16 tdcs_base_size;
+	u16 tdvps_base_size;
+};
+
+struct tdx_sys_info_td_conf {
+	u64 attributes_fixed0;
+	u64 attributes_fixed1;
+	u64 xfam_fixed0;
+	u64 xfam_fixed1;
+	u16 num_cpuid_config;
+	u16 max_vcpus_per_td;
+	u64 cpuid_config_leaves[128];
+	u64 cpuid_config_values[128][2];
+};
+
 struct tdx_sys_info {
 	struct tdx_sys_info_version version;
 	struct tdx_sys_info_features features;
 	struct tdx_sys_info_tdmr tdmr;
 	struct tdx_sys_info_cmr cmr;
+	struct tdx_sys_info_td_ctrl td_ctrl;
+	struct tdx_sys_info_td_conf td_conf;
 };
 
 #endif
diff --git a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
index 2fe57e084453..d96dbfb43574 100644
--- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
+++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
@@ -76,6 +76,54 @@ static int get_tdx_sys_info_cmr(struct tdx_sys_info_cmr *sysinfo_cmr)
 	return ret;
 }
 
+static int get_tdx_sys_info_td_ctrl(struct tdx_sys_info_td_ctrl *sysinfo_td_ctrl)
+{
+	int ret = 0;
+	u64 val;
+
+	if (!ret && !(ret = read_sys_metadata_field(0x9800000100000000, &val)))
+		sysinfo_td_ctrl->tdr_base_size = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x9800000100000100, &val)))
+		sysinfo_td_ctrl->tdcs_base_size = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x9800000100000200, &val)))
+		sysinfo_td_ctrl->tdvps_base_size = val;
+
+	return ret;
+}
+
+static int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sysinfo_td_conf)
+{
+	int ret = 0;
+	u64 val;
+	int i, j;
+
+	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000000, &val)))
+		sysinfo_td_conf->attributes_fixed0 = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000001, &val)))
+		sysinfo_td_conf->attributes_fixed1 = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000002, &val)))
+		sysinfo_td_conf->xfam_fixed0 = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x1900000300000003, &val)))
+		sysinfo_td_conf->xfam_fixed1 = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x9900000100000004, &val)))
+		sysinfo_td_conf->num_cpuid_config = val;
+	if (!ret && !(ret = read_sys_metadata_field(0x9900000100000008, &val)))
+		sysinfo_td_conf->max_vcpus_per_td = val;
+	if (sysinfo_td_conf->num_cpuid_config > ARRAY_SIZE(sysinfo_td_conf->cpuid_config_leaves))
+		return -EINVAL;
+	for (i = 0; i < sysinfo_td_conf->num_cpuid_config; i++)
+		if (!ret && !(ret = read_sys_metadata_field(0x9900000300000400 + i, &val)))
+			sysinfo_td_conf->cpuid_config_leaves[i] = val;
+	if (sysinfo_td_conf->num_cpuid_config > ARRAY_SIZE(sysinfo_td_conf->cpuid_config_values))
+		return -EINVAL;
+	for (i = 0; i < sysinfo_td_conf->num_cpuid_config; i++)
+		for (j = 0; j < 2; j++)
+			if (!ret && !(ret = read_sys_metadata_field(0x9900000300000500 + i * 2 + j, &val)))
+				sysinfo_td_conf->cpuid_config_values[i][j] = val;
+
+	return ret;
+}
+
 static int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
 {
 	int ret = 0;
@@ -84,6 +132,8 @@ static int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
 	ret = ret ?: get_tdx_sys_info_features(&sysinfo->features);
 	ret = ret ?: get_tdx_sys_info_tdmr(&sysinfo->tdmr);
 	ret = ret ?: get_tdx_sys_info_cmr(&sysinfo->cmr);
+	ret = ret ?: get_tdx_sys_info_td_ctrl(&sysinfo->td_ctrl);
+	ret = ret ?: get_tdx_sys_info_td_conf(&sysinfo->td_conf);
 
 	return ret;
 }
-- 
2.43.0