[PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry

Mario Limonciello (AMD) posted 6 patches 2 weeks, 3 days ago
There is a newer version of this series
[PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
Posted by Mario Limonciello (AMD) 2 weeks, 3 days ago
From: Yazen Ghannam <yazen.ghannam@amd.com>

Type 40 entries (Additional Information) are summarized in section 7.41
as part of the SMBIOS specification.  Generally, these entries aren't
interesting to save.

However on some AMD Zen systems, the AGESA version is stored here. This
is useful to save to the kernel message logs for debugging. It can be
used to cross-reference issues.

Implement an iterator for the Additional Information entries. Use this
to find and print the AGESA string. Do so in AMD code, since the use
case is AMD-specific.

Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
Co-developed-by: "Mario Limonciello (AMD)" <superm1@kernel.org>
Signed-off-by: "Mario Limonciello (AMD)" <superm1@kernel.org>
---
v4:
 * New patch (based upon older versions though)
---
 arch/x86/kernel/cpu/amd.c   | 53 +++++++++++++++++++++++++++++++++++++
 drivers/firmware/dmi_scan.c |  3 ++-
 include/linux/dmi.h         | 18 +++++++++++++
 3 files changed, 73 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index c19c4ee74dd1f..5cd60855a85b0 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -3,6 +3,7 @@
 
 #include <linux/export.h>
 #include <linux/bitops.h>
+#include <linux/dmi.h>
 #include <linux/elf.h>
 #include <linux/mm.h>
 #include <linux/kvm_types.h>
@@ -1406,3 +1407,55 @@ static __init int print_s5_reset_status_mmio(void)
 	return 0;
 }
 late_initcall(print_s5_reset_status_mmio);
+
+static void __init amd_dmi_scan_additional(const struct dmi_header *d, void *p)
+{
+	struct dmi_a_info *info = (struct dmi_a_info *)d;
+	void *next, *end;
+
+	/*
+	 * DMI Additional Info table has a 'count' field. But it's not very
+	 * helpful since the entries are variable length. So don't use it.
+	 */
+	if (info->header.type != DMI_ENTRY_ADDITIONAL ||
+	    info->header.length < DMI_A_INFO_MIN_SIZE)
+		return;
+
+	/*
+	 * Get the first entry.
+	 * The minimum table size guarantees at least one entry is present.
+	 */
+	next = (void *)(info + 1);
+	end  = (void *)info + info->header.length;
+
+	do {
+		struct dmi_a_info_entry *entry;
+		const char *string_ptr;
+
+		entry = (struct dmi_a_info_entry *)next;
+
+		/*
+		 * Not much can be done to validate data. At least the entry
+		 * length shouldn't be 0.
+		 */
+		if (!entry->length)
+			return;
+
+		string_ptr = dmi_string_nosave(&info->header, entry->str_num);
+
+		/* Only one AGESA string is expected. */
+		if (!strncmp(string_ptr, "AGESA", 5)) {
+			pr_info("%s\n", string_ptr);
+			break;
+		}
+
+		next += entry->length;
+	} while (end - next >= DMI_A_INFO_ENT_MIN_SIZE);
+}
+
+static __init int print_dmi_agesa(void)
+{
+	dmi_walk(amd_dmi_scan_additional, NULL);
+	return 0;
+}
+late_initcall(print_dmi_agesa);
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index ed6235ac576b6..a3f7dabd49554 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -47,7 +47,7 @@ static struct dmi_memdev_info {
 static int dmi_memdev_nr;
 static int dmi_memdev_populated_nr __initdata;
 
-static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
+const char *dmi_string_nosave(const struct dmi_header *dm, u8 s)
 {
 	const u8 *bp = ((u8 *) dm) + dm->length;
 	const u8 *nsp;
@@ -66,6 +66,7 @@ static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
 
 	return dmi_empty_string;
 }
+EXPORT_SYMBOL_GPL(dmi_string_nosave);
 
 static const char * __init dmi_string(const struct dmi_header *dm, u8 s)
 {
diff --git a/include/linux/dmi.h b/include/linux/dmi.h
index 2eedf44e68012..b7865fa387ccb 100644
--- a/include/linux/dmi.h
+++ b/include/linux/dmi.h
@@ -91,6 +91,21 @@ struct dmi_device {
 	void *device_data;	/* Type specific data */
 };
 
+#define DMI_A_INFO_ENT_MIN_SIZE 0x6
+struct dmi_a_info_entry {
+	u8 length;
+	u16 handle;
+	u8 offset;
+	u8 str_num;
+	u8 value[];
+} __packed;
+
+#define DMI_A_INFO_MIN_SIZE	0xB
+struct dmi_a_info {
+	struct dmi_header header;
+	u8 count;
+} __packed;
+
 #ifdef CONFIG_DMI
 
 struct dmi_dev_onboard {
@@ -120,6 +135,7 @@ extern void dmi_memdev_name(u16 handle, const char **bank, const char **device);
 extern u64 dmi_memdev_size(u16 handle);
 extern u8 dmi_memdev_type(u16 handle);
 extern u16 dmi_memdev_handle(int slot);
+const char *dmi_string_nosave(const struct dmi_header *dm, u8 s);
 
 #else
 
@@ -153,6 +169,8 @@ static inline u8 dmi_memdev_type(u16 handle) { return 0x0; }
 static inline u16 dmi_memdev_handle(int slot) { return 0xffff; }
 static inline const struct dmi_system_id *
 	dmi_first_match(const struct dmi_system_id *list) { return NULL; }
+static inline const char *
+	dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
 
 #endif
 
-- 
2.43.0
Re: [PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
Posted by kernel test robot 2 weeks, 2 days ago
Hi Mario,

kernel test robot noticed the following build warnings:

[auto build test WARNING on tip/x86/core]
[also build test WARNING on tip/master linus/master v6.19-rc6 next-20260120]
[cannot apply to jdelvare-staging/dmi-for-next bp/for-next tip/auto-latest]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Mario-Limonciello-AMD/firmware-dmi-Correct-an-indexing-error-in-dmi-h/20260121-140800
base:   tip/x86/core
patch link:    https://lore.kernel.org/r/20260121060431.432350-7-superm1%40kernel.org
patch subject: [PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
config: i386-buildonly-randconfig-003-20260121 (https://download.01.org/0day-ci/archive/20260122/202601220139.ymxKIu3a-lkp@intel.com/config)
compiler: gcc-13 (Debian 13.3.0-16) 13.3.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260122/202601220139.ymxKIu3a-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601220139.ymxKIu3a-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from arch/x86/kernel/cpu/amd.c:6:
   include/linux/dmi.h: In function 'dmi_string_nosave':
   include/linux/dmi.h:173:71: error: 'dmi_empty_string' undeclared (first use in this function)
     173 |         dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
         |                                                                       ^~~~~~~~~~~~~~~~
   include/linux/dmi.h:173:71: note: each undeclared identifier is reported only once for each function it appears in
>> include/linux/dmi.h:173:89: warning: control reaches end of non-void function [-Wreturn-type]
     173 |         dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
         |                                                                                         ^


vim +173 include/linux/dmi.h

   141	
   142	static inline int dmi_check_system(const struct dmi_system_id *list) { return 0; }
   143	static inline const char * dmi_get_system_info(int field) { return NULL; }
   144	static inline const struct dmi_device * dmi_find_device(int type, const char *name,
   145		const struct dmi_device *from) { return NULL; }
   146	static inline void dmi_setup(void) { }
   147	static inline bool dmi_get_date(int field, int *yearp, int *monthp, int *dayp)
   148	{
   149		if (yearp)
   150			*yearp = 0;
   151		if (monthp)
   152			*monthp = 0;
   153		if (dayp)
   154			*dayp = 0;
   155		return false;
   156	}
   157	static inline int dmi_get_bios_year(void) { return -ENXIO; }
   158	static inline int dmi_name_in_vendors(const char *s) { return 0; }
   159	static inline int dmi_name_in_serial(const char *s) { return 0; }
   160	#define dmi_available 0
   161	static inline int dmi_walk(void (*decode)(const struct dmi_header *, void *),
   162		void *private_data) { return -ENXIO; }
   163	static inline bool dmi_match(enum dmi_field f, const char *str)
   164		{ return false; }
   165	static inline void dmi_memdev_name(u16 handle, const char **bank,
   166			const char **device) { }
   167	static inline u64 dmi_memdev_size(u16 handle) { return ~0ul; }
   168	static inline u8 dmi_memdev_type(u16 handle) { return 0x0; }
   169	static inline u16 dmi_memdev_handle(int slot) { return 0xffff; }
   170	static inline const struct dmi_system_id *
   171		dmi_first_match(const struct dmi_system_id *list) { return NULL; }
   172	static inline const char *
 > 173		dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
   174	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
Posted by kernel test robot 2 weeks, 3 days ago
Hi Mario,

kernel test robot noticed the following build errors:

[auto build test ERROR on tip/x86/core]
[also build test ERROR on tip/master linus/master v6.19-rc6 next-20260120]
[cannot apply to bp/for-next tip/auto-latest]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Mario-Limonciello-AMD/firmware-dmi-Correct-an-indexing-error-in-dmi-h/20260121-140800
base:   tip/x86/core
patch link:    https://lore.kernel.org/r/20260121060431.432350-7-superm1%40kernel.org
patch subject: [PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
config: loongarch-allnoconfig (https://download.01.org/0day-ci/archive/20260121/202601212323.7qxI0awN-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 9b8addffa70cee5b2acc5454712d9cf78ce45710)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260121/202601212323.7qxI0awN-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601212323.7qxI0awN-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from arch/loongarch/kernel/setup.c:16:
>> include/linux/dmi.h:173:64: error: use of undeclared identifier 'dmi_empty_string'
     173 |         dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
         |                                                                       ^~~~~~~~~~~~~~~~
   1 error generated.


vim +/dmi_empty_string +173 include/linux/dmi.h

   141	
   142	static inline int dmi_check_system(const struct dmi_system_id *list) { return 0; }
   143	static inline const char * dmi_get_system_info(int field) { return NULL; }
   144	static inline const struct dmi_device * dmi_find_device(int type, const char *name,
   145		const struct dmi_device *from) { return NULL; }
   146	static inline void dmi_setup(void) { }
   147	static inline bool dmi_get_date(int field, int *yearp, int *monthp, int *dayp)
   148	{
   149		if (yearp)
   150			*yearp = 0;
   151		if (monthp)
   152			*monthp = 0;
   153		if (dayp)
   154			*dayp = 0;
   155		return false;
   156	}
   157	static inline int dmi_get_bios_year(void) { return -ENXIO; }
   158	static inline int dmi_name_in_vendors(const char *s) { return 0; }
   159	static inline int dmi_name_in_serial(const char *s) { return 0; }
   160	#define dmi_available 0
   161	static inline int dmi_walk(void (*decode)(const struct dmi_header *, void *),
   162		void *private_data) { return -ENXIO; }
   163	static inline bool dmi_match(enum dmi_field f, const char *str)
   164		{ return false; }
   165	static inline void dmi_memdev_name(u16 handle, const char **bank,
   166			const char **device) { }
   167	static inline u64 dmi_memdev_size(u16 handle) { return ~0ul; }
   168	static inline u8 dmi_memdev_type(u16 handle) { return 0x0; }
   169	static inline u16 dmi_memdev_handle(int slot) { return 0xffff; }
   170	static inline const struct dmi_system_id *
   171		dmi_first_match(const struct dmi_system_id *list) { return NULL; }
   172	static inline const char *
 > 173		dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
   174	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
Posted by Yazen Ghannam 2 weeks, 3 days ago
On Wed, Jan 21, 2026 at 12:04:31AM -0600, Mario Limonciello (AMD) wrote:
> From: Yazen Ghannam <yazen.ghannam@amd.com>
> 
> Type 40 entries (Additional Information) are summarized in section 7.41
> as part of the SMBIOS specification.  Generally, these entries aren't
> interesting to save.
> 
> However on some AMD Zen systems, the AGESA version is stored here. This
> is useful to save to the kernel message logs for debugging. It can be
> used to cross-reference issues.
> 
> Implement an iterator for the Additional Information entries. Use this
> to find and print the AGESA string. Do so in AMD code, since the use
> case is AMD-specific.
> 
> Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
> Co-developed-by: "Mario Limonciello (AMD)" <superm1@kernel.org>
> Signed-off-by: "Mario Limonciello (AMD)" <superm1@kernel.org>
> ---
> v4:
>  * New patch (based upon older versions though)
> ---
>  arch/x86/kernel/cpu/amd.c   | 53 +++++++++++++++++++++++++++++++++++++
>  drivers/firmware/dmi_scan.c |  3 ++-
>  include/linux/dmi.h         | 18 +++++++++++++
>  3 files changed, 73 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
> index c19c4ee74dd1f..5cd60855a85b0 100644
> --- a/arch/x86/kernel/cpu/amd.c
> +++ b/arch/x86/kernel/cpu/amd.c
> @@ -3,6 +3,7 @@
>  
>  #include <linux/export.h>
>  #include <linux/bitops.h>
> +#include <linux/dmi.h>
>  #include <linux/elf.h>
>  #include <linux/mm.h>
>  #include <linux/kvm_types.h>
> @@ -1406,3 +1407,55 @@ static __init int print_s5_reset_status_mmio(void)
>  	return 0;
>  }
>  late_initcall(print_s5_reset_status_mmio);
> +
> +static void __init amd_dmi_scan_additional(const struct dmi_header *d, void *p)
> +{
> +	struct dmi_a_info *info = (struct dmi_a_info *)d;
> +	void *next, *end;
> +
> +	/*
> +	 * DMI Additional Info table has a 'count' field. But it's not very
> +	 * helpful since the entries are variable length. So don't use it.
> +	 */
> +	if (info->header.type != DMI_ENTRY_ADDITIONAL ||
> +	    info->header.length < DMI_A_INFO_MIN_SIZE)
> +		return;
> +
> +	/*
> +	 * Get the first entry.
> +	 * The minimum table size guarantees at least one entry is present.
> +	 */
> +	next = (void *)(info + 1);
> +	end  = (void *)info + info->header.length;
> +
> +	do {
> +		struct dmi_a_info_entry *entry;
> +		const char *string_ptr;
> +
> +		entry = (struct dmi_a_info_entry *)next;
> +
> +		/*
> +		 * Not much can be done to validate data. At least the entry
> +		 * length shouldn't be 0.
> +		 */
> +		if (!entry->length)
> +			return;
> +
> +		string_ptr = dmi_string_nosave(&info->header, entry->str_num);
> +
> +		/* Only one AGESA string is expected. */
> +		if (!strncmp(string_ptr, "AGESA", 5)) {
> +			pr_info("%s\n", string_ptr);
> +			break;
> +		}
> +
> +		next += entry->length;
> +	} while (end - next >= DMI_A_INFO_ENT_MIN_SIZE);
> +}
> +
> +static __init int print_dmi_agesa(void)
> +{
> +	dmi_walk(amd_dmi_scan_additional, NULL);
> +	return 0;
> +}
> +late_initcall(print_dmi_agesa);
> diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
> index ed6235ac576b6..a3f7dabd49554 100644
> --- a/drivers/firmware/dmi_scan.c
> +++ b/drivers/firmware/dmi_scan.c
> @@ -47,7 +47,7 @@ static struct dmi_memdev_info {
>  static int dmi_memdev_nr;
>  static int dmi_memdev_populated_nr __initdata;
>  
> -static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
> +const char *dmi_string_nosave(const struct dmi_header *dm, u8 s)
>  {
>  	const u8 *bp = ((u8 *) dm) + dm->length;
>  	const u8 *nsp;
> @@ -66,6 +66,7 @@ static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
>  
>  	return dmi_empty_string;
>  }
> +EXPORT_SYMBOL_GPL(dmi_string_nosave);
>  
>  static const char * __init dmi_string(const struct dmi_header *dm, u8 s)
>  {
> diff --git a/include/linux/dmi.h b/include/linux/dmi.h
> index 2eedf44e68012..b7865fa387ccb 100644
> --- a/include/linux/dmi.h
> +++ b/include/linux/dmi.h
> @@ -91,6 +91,21 @@ struct dmi_device {
>  	void *device_data;	/* Type specific data */
>  };
>  
> +#define DMI_A_INFO_ENT_MIN_SIZE 0x6
> +struct dmi_a_info_entry {
> +	u8 length;
> +	u16 handle;
> +	u8 offset;
> +	u8 str_num;
> +	u8 value[];
> +} __packed;
> +
> +#define DMI_A_INFO_MIN_SIZE	0xB
> +struct dmi_a_info {
> +	struct dmi_header header;
> +	u8 count;
> +} __packed;
> +
>  #ifdef CONFIG_DMI
>  
>  struct dmi_dev_onboard {
> @@ -120,6 +135,7 @@ extern void dmi_memdev_name(u16 handle, const char **bank, const char **device);
>  extern u64 dmi_memdev_size(u16 handle);
>  extern u8 dmi_memdev_type(u16 handle);
>  extern u16 dmi_memdev_handle(int slot);
> +const char *dmi_string_nosave(const struct dmi_header *dm, u8 s);
>  
>  #else
>  
> @@ -153,6 +169,8 @@ static inline u8 dmi_memdev_type(u16 handle) { return 0x0; }
>  static inline u16 dmi_memdev_handle(int slot) { return 0xffff; }
>  static inline const struct dmi_system_id *
>  	dmi_first_match(const struct dmi_system_id *list) { return NULL; }
> +static inline const char *
> +	dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
>  

The dmi_empty_string needs to be moved to this header file from
dmi_scan.c.

Otherwise, there's a build issue as the test bot reported.

Thanks,
Yazen
Re: [PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
Posted by Mario Limonciello 2 weeks, 2 days ago
On 1/21/26 8:26 AM, Yazen Ghannam wrote:
> On Wed, Jan 21, 2026 at 12:04:31AM -0600, Mario Limonciello (AMD) wrote:
>> From: Yazen Ghannam <yazen.ghannam@amd.com>
>>
>> Type 40 entries (Additional Information) are summarized in section 7.41
>> as part of the SMBIOS specification.  Generally, these entries aren't
>> interesting to save.
>>
>> However on some AMD Zen systems, the AGESA version is stored here. This
>> is useful to save to the kernel message logs for debugging. It can be
>> used to cross-reference issues.
>>
>> Implement an iterator for the Additional Information entries. Use this
>> to find and print the AGESA string. Do so in AMD code, since the use
>> case is AMD-specific.
>>
>> Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
>> Co-developed-by: "Mario Limonciello (AMD)" <superm1@kernel.org>
>> Signed-off-by: "Mario Limonciello (AMD)" <superm1@kernel.org>
>> ---
>> v4:
>>   * New patch (based upon older versions though)
>> ---
>>   arch/x86/kernel/cpu/amd.c   | 53 +++++++++++++++++++++++++++++++++++++
>>   drivers/firmware/dmi_scan.c |  3 ++-
>>   include/linux/dmi.h         | 18 +++++++++++++
>>   3 files changed, 73 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
>> index c19c4ee74dd1f..5cd60855a85b0 100644
>> --- a/arch/x86/kernel/cpu/amd.c
>> +++ b/arch/x86/kernel/cpu/amd.c
>> @@ -3,6 +3,7 @@
>>   
>>   #include <linux/export.h>
>>   #include <linux/bitops.h>
>> +#include <linux/dmi.h>
>>   #include <linux/elf.h>
>>   #include <linux/mm.h>
>>   #include <linux/kvm_types.h>
>> @@ -1406,3 +1407,55 @@ static __init int print_s5_reset_status_mmio(void)
>>   	return 0;
>>   }
>>   late_initcall(print_s5_reset_status_mmio);
>> +
>> +static void __init amd_dmi_scan_additional(const struct dmi_header *d, void *p)
>> +{
>> +	struct dmi_a_info *info = (struct dmi_a_info *)d;
>> +	void *next, *end;
>> +
>> +	/*
>> +	 * DMI Additional Info table has a 'count' field. But it's not very
>> +	 * helpful since the entries are variable length. So don't use it.
>> +	 */
>> +	if (info->header.type != DMI_ENTRY_ADDITIONAL ||
>> +	    info->header.length < DMI_A_INFO_MIN_SIZE)
>> +		return;
>> +
>> +	/*
>> +	 * Get the first entry.
>> +	 * The minimum table size guarantees at least one entry is present.
>> +	 */
>> +	next = (void *)(info + 1);
>> +	end  = (void *)info + info->header.length;
>> +
>> +	do {
>> +		struct dmi_a_info_entry *entry;
>> +		const char *string_ptr;
>> +
>> +		entry = (struct dmi_a_info_entry *)next;
>> +
>> +		/*
>> +		 * Not much can be done to validate data. At least the entry
>> +		 * length shouldn't be 0.
>> +		 */
>> +		if (!entry->length)
>> +			return;
>> +
>> +		string_ptr = dmi_string_nosave(&info->header, entry->str_num);
>> +
>> +		/* Only one AGESA string is expected. */
>> +		if (!strncmp(string_ptr, "AGESA", 5)) {
>> +			pr_info("%s\n", string_ptr);
>> +			break;
>> +		}
>> +
>> +		next += entry->length;
>> +	} while (end - next >= DMI_A_INFO_ENT_MIN_SIZE);
>> +}
>> +
>> +static __init int print_dmi_agesa(void)
>> +{
>> +	dmi_walk(amd_dmi_scan_additional, NULL);
>> +	return 0;
>> +}
>> +late_initcall(print_dmi_agesa);
>> diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
>> index ed6235ac576b6..a3f7dabd49554 100644
>> --- a/drivers/firmware/dmi_scan.c
>> +++ b/drivers/firmware/dmi_scan.c
>> @@ -47,7 +47,7 @@ static struct dmi_memdev_info {
>>   static int dmi_memdev_nr;
>>   static int dmi_memdev_populated_nr __initdata;
>>   
>> -static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
>> +const char *dmi_string_nosave(const struct dmi_header *dm, u8 s)
>>   {
>>   	const u8 *bp = ((u8 *) dm) + dm->length;
>>   	const u8 *nsp;
>> @@ -66,6 +66,7 @@ static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
>>   
>>   	return dmi_empty_string;
>>   }
>> +EXPORT_SYMBOL_GPL(dmi_string_nosave);
>>   
>>   static const char * __init dmi_string(const struct dmi_header *dm, u8 s)
>>   {
>> diff --git a/include/linux/dmi.h b/include/linux/dmi.h
>> index 2eedf44e68012..b7865fa387ccb 100644
>> --- a/include/linux/dmi.h
>> +++ b/include/linux/dmi.h
>> @@ -91,6 +91,21 @@ struct dmi_device {
>>   	void *device_data;	/* Type specific data */
>>   };
>>   
>> +#define DMI_A_INFO_ENT_MIN_SIZE 0x6
>> +struct dmi_a_info_entry {
>> +	u8 length;
>> +	u16 handle;
>> +	u8 offset;
>> +	u8 str_num;
>> +	u8 value[];
>> +} __packed;
>> +
>> +#define DMI_A_INFO_MIN_SIZE	0xB
>> +struct dmi_a_info {
>> +	struct dmi_header header;
>> +	u8 count;
>> +} __packed;
>> +
>>   #ifdef CONFIG_DMI
>>   
>>   struct dmi_dev_onboard {
>> @@ -120,6 +135,7 @@ extern void dmi_memdev_name(u16 handle, const char **bank, const char **device);
>>   extern u64 dmi_memdev_size(u16 handle);
>>   extern u8 dmi_memdev_type(u16 handle);
>>   extern u16 dmi_memdev_handle(int slot);
>> +const char *dmi_string_nosave(const struct dmi_header *dm, u8 s);
>>   
>>   #else
>>   
>> @@ -153,6 +169,8 @@ static inline u8 dmi_memdev_type(u16 handle) { return 0x0; }
>>   static inline u16 dmi_memdev_handle(int slot) { return 0xffff; }
>>   static inline const struct dmi_system_id *
>>   	dmi_first_match(const struct dmi_system_id *list) { return NULL; }
>> +static inline const char *
>> +	dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
>>   
> 
> The dmi_empty_string needs to be moved to this header file from
> dmi_scan.c.
> 
> Otherwise, there's a build issue as the test bot reported.
> 
> Thanks,
> Yazen

I don't think it's actually appropriate to move dmi_empty_string in this 
case.  It's a static variable, shouldn't really be in a header.

I would think it's better to just return NULL.
Re: [PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
Posted by Yazen Ghannam 2 weeks, 2 days ago
On Wed, Jan 21, 2026 at 02:50:49PM -0600, Mario Limonciello wrote:
> On 1/21/26 8:26 AM, Yazen Ghannam wrote:

[...]

> > > +	do {
> > > +		struct dmi_a_info_entry *entry;
> > > +		const char *string_ptr;
> > > +
> > > +		entry = (struct dmi_a_info_entry *)next;
> > > +
> > > +		/*
> > > +		 * Not much can be done to validate data. At least the entry
> > > +		 * length shouldn't be 0.
> > > +		 */
> > > +		if (!entry->length)
> > > +			return;
> > > +
> > > +		string_ptr = dmi_string_nosave(&info->header, entry->str_num);
> > > +
> > > +		/* Only one AGESA string is expected. */
> > > +		if (!strncmp(string_ptr, "AGESA", 5)) {
> > > +			pr_info("%s\n", string_ptr);
> > > +			break;
> > > +		}
> > > +

[...]

> > > +static inline const char *
> > > +	dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
> > 
> > The dmi_empty_string needs to be moved to this header file from
> > dmi_scan.c.
> > 
> > Otherwise, there's a build issue as the test bot reported.
> > 
> > Thanks,
> > Yazen
> 
> I don't think it's actually appropriate to move dmi_empty_string in this
> case.  It's a static variable, shouldn't really be in a header.
> 
> I would think it's better to just return NULL.

dmi_string_nosave() should always return a string. The callers expect
it. That's why we don't need a NULL pointer check above.

 +static inline const char *
 +	dmi_string_nosave(const struct dmi_header *dm, u8 s) { return ""; }

Rather than move the variable, just return the empty string directly.

Thanks,
Yazen
Re: [PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
Posted by Mario Limonciello (AMD) (kernel.org) 2 weeks, 2 days ago

On 1/21/2026 3:30 PM, Yazen Ghannam wrote:
> On Wed, Jan 21, 2026 at 02:50:49PM -0600, Mario Limonciello wrote:
>> On 1/21/26 8:26 AM, Yazen Ghannam wrote:
> 
> [...]
> 
>>>> +	do {
>>>> +		struct dmi_a_info_entry *entry;
>>>> +		const char *string_ptr;
>>>> +
>>>> +		entry = (struct dmi_a_info_entry *)next;
>>>> +
>>>> +		/*
>>>> +		 * Not much can be done to validate data. At least the entry
>>>> +		 * length shouldn't be 0.
>>>> +		 */
>>>> +		if (!entry->length)
>>>> +			return;
>>>> +
>>>> +		string_ptr = dmi_string_nosave(&info->header, entry->str_num);
>>>> +
>>>> +		/* Only one AGESA string is expected. */
>>>> +		if (!strncmp(string_ptr, "AGESA", 5)) {
>>>> +			pr_info("%s\n", string_ptr);
>>>> +			break;
>>>> +		}
>>>> +
> 
> [...]
> 
>>>> +static inline const char *
>>>> +	dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
>>>
>>> The dmi_empty_string needs to be moved to this header file from
>>> dmi_scan.c.
>>>
>>> Otherwise, there's a build issue as the test bot reported.
>>>
>>> Thanks,
>>> Yazen
>>
>> I don't think it's actually appropriate to move dmi_empty_string in this
>> case.  It's a static variable, shouldn't really be in a header.
>>
>> I would think it's better to just return NULL.
> 
> dmi_string_nosave() should always return a string. The callers expect
> it. That's why we don't need a NULL pointer check above.
> 
>   +static inline const char *
>   +	dmi_string_nosave(const struct dmi_header *dm, u8 s) { return ""; }
> 
> Rather than move the variable, just return the empty string directly.
> 
> Thanks,
> Yazen

Got it; thanks for clarifying.
Re: [PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
Posted by Mario Limonciello (AMD) (kernel.org) 2 weeks, 3 days ago

On 1/21/2026 8:26 AM, Yazen Ghannam wrote:
> On Wed, Jan 21, 2026 at 12:04:31AM -0600, Mario Limonciello (AMD) wrote:
>> From: Yazen Ghannam <yazen.ghannam@amd.com>
>>
>> Type 40 entries (Additional Information) are summarized in section 7.41
>> as part of the SMBIOS specification.  Generally, these entries aren't
>> interesting to save.
>>
>> However on some AMD Zen systems, the AGESA version is stored here. This
>> is useful to save to the kernel message logs for debugging. It can be
>> used to cross-reference issues.
>>
>> Implement an iterator for the Additional Information entries. Use this
>> to find and print the AGESA string. Do so in AMD code, since the use
>> case is AMD-specific.
>>
>> Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
>> Co-developed-by: "Mario Limonciello (AMD)" <superm1@kernel.org>
>> Signed-off-by: "Mario Limonciello (AMD)" <superm1@kernel.org>
>> ---
>> v4:
>>   * New patch (based upon older versions though)
>> ---
>>   arch/x86/kernel/cpu/amd.c   | 53 +++++++++++++++++++++++++++++++++++++
>>   drivers/firmware/dmi_scan.c |  3 ++-
>>   include/linux/dmi.h         | 18 +++++++++++++
>>   3 files changed, 73 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
>> index c19c4ee74dd1f..5cd60855a85b0 100644
>> --- a/arch/x86/kernel/cpu/amd.c
>> +++ b/arch/x86/kernel/cpu/amd.c
>> @@ -3,6 +3,7 @@
>>   
>>   #include <linux/export.h>
>>   #include <linux/bitops.h>
>> +#include <linux/dmi.h>
>>   #include <linux/elf.h>
>>   #include <linux/mm.h>
>>   #include <linux/kvm_types.h>
>> @@ -1406,3 +1407,55 @@ static __init int print_s5_reset_status_mmio(void)
>>   	return 0;
>>   }
>>   late_initcall(print_s5_reset_status_mmio);
>> +
>> +static void __init amd_dmi_scan_additional(const struct dmi_header *d, void *p)
>> +{
>> +	struct dmi_a_info *info = (struct dmi_a_info *)d;
>> +	void *next, *end;
>> +
>> +	/*
>> +	 * DMI Additional Info table has a 'count' field. But it's not very
>> +	 * helpful since the entries are variable length. So don't use it.
>> +	 */
>> +	if (info->header.type != DMI_ENTRY_ADDITIONAL ||
>> +	    info->header.length < DMI_A_INFO_MIN_SIZE)
>> +		return;
>> +
>> +	/*
>> +	 * Get the first entry.
>> +	 * The minimum table size guarantees at least one entry is present.
>> +	 */
>> +	next = (void *)(info + 1);
>> +	end  = (void *)info + info->header.length;
>> +
>> +	do {
>> +		struct dmi_a_info_entry *entry;
>> +		const char *string_ptr;
>> +
>> +		entry = (struct dmi_a_info_entry *)next;
>> +
>> +		/*
>> +		 * Not much can be done to validate data. At least the entry
>> +		 * length shouldn't be 0.
>> +		 */
>> +		if (!entry->length)
>> +			return;
>> +
>> +		string_ptr = dmi_string_nosave(&info->header, entry->str_num);
>> +
>> +		/* Only one AGESA string is expected. */
>> +		if (!strncmp(string_ptr, "AGESA", 5)) {
>> +			pr_info("%s\n", string_ptr);
>> +			break;
>> +		}
>> +
>> +		next += entry->length;
>> +	} while (end - next >= DMI_A_INFO_ENT_MIN_SIZE);
>> +}
>> +
>> +static __init int print_dmi_agesa(void)
>> +{
>> +	dmi_walk(amd_dmi_scan_additional, NULL);
>> +	return 0;
>> +}
>> +late_initcall(print_dmi_agesa);
>> diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
>> index ed6235ac576b6..a3f7dabd49554 100644
>> --- a/drivers/firmware/dmi_scan.c
>> +++ b/drivers/firmware/dmi_scan.c
>> @@ -47,7 +47,7 @@ static struct dmi_memdev_info {
>>   static int dmi_memdev_nr;
>>   static int dmi_memdev_populated_nr __initdata;
>>   
>> -static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
>> +const char *dmi_string_nosave(const struct dmi_header *dm, u8 s)
>>   {
>>   	const u8 *bp = ((u8 *) dm) + dm->length;
>>   	const u8 *nsp;
>> @@ -66,6 +66,7 @@ static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
>>   
>>   	return dmi_empty_string;
>>   }
>> +EXPORT_SYMBOL_GPL(dmi_string_nosave);
>>   
>>   static const char * __init dmi_string(const struct dmi_header *dm, u8 s)
>>   {
>> diff --git a/include/linux/dmi.h b/include/linux/dmi.h
>> index 2eedf44e68012..b7865fa387ccb 100644
>> --- a/include/linux/dmi.h
>> +++ b/include/linux/dmi.h
>> @@ -91,6 +91,21 @@ struct dmi_device {
>>   	void *device_data;	/* Type specific data */
>>   };
>>   
>> +#define DMI_A_INFO_ENT_MIN_SIZE 0x6
>> +struct dmi_a_info_entry {
>> +	u8 length;
>> +	u16 handle;
>> +	u8 offset;
>> +	u8 str_num;
>> +	u8 value[];
>> +} __packed;
>> +
>> +#define DMI_A_INFO_MIN_SIZE	0xB
>> +struct dmi_a_info {
>> +	struct dmi_header header;
>> +	u8 count;
>> +} __packed;
>> +
>>   #ifdef CONFIG_DMI
>>   
>>   struct dmi_dev_onboard {
>> @@ -120,6 +135,7 @@ extern void dmi_memdev_name(u16 handle, const char **bank, const char **device);
>>   extern u64 dmi_memdev_size(u16 handle);
>>   extern u8 dmi_memdev_type(u16 handle);
>>   extern u16 dmi_memdev_handle(int slot);
>> +const char *dmi_string_nosave(const struct dmi_header *dm, u8 s);
>>   
>>   #else
>>   
>> @@ -153,6 +169,8 @@ static inline u8 dmi_memdev_type(u16 handle) { return 0x0; }
>>   static inline u16 dmi_memdev_handle(int slot) { return 0xffff; }
>>   static inline const struct dmi_system_id *
>>   	dmi_first_match(const struct dmi_system_id *list) { return NULL; }
>> +static inline const char *
>> +	dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
>>   
> 
> The dmi_empty_string needs to be moved to this header file from
> dmi_scan.c.
> 
> Otherwise, there's a build issue as the test bot reported.
> 
> Thanks,
> Yazen

Yeah; it makes sense now.  Will move in next version.
Re: [PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
Posted by kernel test robot 2 weeks, 3 days ago
Hi Mario,

kernel test robot noticed the following build errors:

[auto build test ERROR on tip/x86/core]
[also build test ERROR on tip/master linus/master v6.19-rc6 next-20260120]
[cannot apply to jdelvare-staging/dmi-for-next bp/for-next tip/auto-latest]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Mario-Limonciello-AMD/firmware-dmi-Correct-an-indexing-error-in-dmi-h/20260121-140800
base:   tip/x86/core
patch link:    https://lore.kernel.org/r/20260121060431.432350-7-superm1%40kernel.org
patch subject: [PATCH v4 6/6] x86/CPU/AMD: Print AGESA string from DMI additional information entry
config: microblaze-defconfig (https://download.01.org/0day-ci/archive/20260121/202601211912.0hwXBwKQ-lkp@intel.com/config)
compiler: microblaze-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260121/202601211912.0hwXBwKQ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601211912.0hwXBwKQ-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from drivers/pci/pci.c:14:
   include/linux/dmi.h: In function 'dmi_string_nosave':
>> include/linux/dmi.h:173:71: error: 'dmi_empty_string' undeclared (first use in this function)
     173 |         dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
         |                                                                       ^~~~~~~~~~~~~~~~
   include/linux/dmi.h:173:71: note: each undeclared identifier is reported only once for each function it appears in


vim +/dmi_empty_string +173 include/linux/dmi.h

   141	
   142	static inline int dmi_check_system(const struct dmi_system_id *list) { return 0; }
   143	static inline const char * dmi_get_system_info(int field) { return NULL; }
   144	static inline const struct dmi_device * dmi_find_device(int type, const char *name,
   145		const struct dmi_device *from) { return NULL; }
   146	static inline void dmi_setup(void) { }
   147	static inline bool dmi_get_date(int field, int *yearp, int *monthp, int *dayp)
   148	{
   149		if (yearp)
   150			*yearp = 0;
   151		if (monthp)
   152			*monthp = 0;
   153		if (dayp)
   154			*dayp = 0;
   155		return false;
   156	}
   157	static inline int dmi_get_bios_year(void) { return -ENXIO; }
   158	static inline int dmi_name_in_vendors(const char *s) { return 0; }
   159	static inline int dmi_name_in_serial(const char *s) { return 0; }
   160	#define dmi_available 0
   161	static inline int dmi_walk(void (*decode)(const struct dmi_header *, void *),
   162		void *private_data) { return -ENXIO; }
   163	static inline bool dmi_match(enum dmi_field f, const char *str)
   164		{ return false; }
   165	static inline void dmi_memdev_name(u16 handle, const char **bank,
   166			const char **device) { }
   167	static inline u64 dmi_memdev_size(u16 handle) { return ~0ul; }
   168	static inline u8 dmi_memdev_type(u16 handle) { return 0x0; }
   169	static inline u16 dmi_memdev_handle(int slot) { return 0xffff; }
   170	static inline const struct dmi_system_id *
   171		dmi_first_match(const struct dmi_system_id *list) { return NULL; }
   172	static inline const char *
 > 173		dmi_string_nosave(const struct dmi_header *dm, u8 s) { return dmi_empty_string; }
   174	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki