Subject: [PATCH 3/3] arm64: cpuinfo: display product info in /proc/cpuinfo

From: [email-masked] (Mark Rutland)
Date: Wed, 27 Sep 2017 12:36:48 +0100

Hi Al,

On Tue, Sep 26, 2017 at 04:23:24PM -0600, Al Stone wrote:
> While it is very useful to know what CPU is being used, it is also
> useful to know who made the platform being used.  On servers, this
> can point to the right person to contact when a server is having
> trouble.
> 
> Go get the product info -- manufacturer, product name and version --
> from DMI (aka SMBIOS) and display it in /proc/cpuinfo.  To look more
> like other server platforms, include the CPU type and frequency when
> displaying the product info, too.

As mentioned on the cover letter, NAK to modifiying /proc/cpuinfo.

I note this is all DMI information, which I thought we already exposed
under sysfs (in an architecture-neutral format).

Is that not the case?

... or do existing tools not pick that up today?

Thanks,
Mark.

> 
> Signed-off-by: Al Stone <[email-masked]>
> Cc: Catalin Marinas <[email-masked]>
> Cc: Will Deacon <[email-masked]>
> Cc: Suzuki K Poulose <[email-masked]>
> Cc: Mark Rutland <[email-masked]>
> ---
>  arch/arm64/kernel/cpuinfo.c | 135 +++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 134 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
> index 0b4261884862..6a9dbad5ee3f 100644
> --- a/arch/arm64/kernel/cpuinfo.c
> +++ b/arch/arm64/kernel/cpuinfo.c
> @@ -19,10 +19,12 @@
>  #include <asm/cpu.h>
>  #include <asm/cputype.h>
>  #include <asm/cpufeature.h>
> +#include <asm/unaligned.h>
>  
>  #include <linux/bitops.h>
>  #include <linux/bug.h>
>  #include <linux/compat.h>
> +#include <linux/dmi.h>
>  #include <linux/elf.h>
>  #include <linux/init.h>
>  #include <linux/kernel.h>
> @@ -31,6 +33,7 @@
>  #include <linux/printk.h>
>  #include <linux/seq_file.h>
>  #include <linux/sched.h>
> +#include <linux/slab.h>
>  #include <linux/smp.h>
>  #include <linux/delay.h>
>  #include <linux/cpuinfo.h>
> @@ -167,6 +170,111 @@ static const char *const compat_hwcap2_str[] = {
>  };
>  #endif /* CONFIG_COMPAT */
>  
> +/* Details needed when extracting fields from DMI info */
> +#define DMI_ENTRY_BASEBOARD_MIN_LENGTH	8
> +#define DMI_ENTRY_PROCESSOR_MIN_LENGTH	48
> +
> +#define DMI_BASEBOARD_MANUFACTURER	0x04
> +#define DMI_BASEBOARD_PRODUCT		0x05
> +#define DMI_BASEBOARD_VERSION		0x06
> +#define DMI_PROCESSOR_MAX_SPEED		0x14
> +
> +#define DMI_MAX_STRLEN			80
> +
> +/* Values captured from DMI info */
> +static u64 dmi_max_mhz;
> +static char *dmi_product_info;
> +
> +/* Callback function used to retrieve the max frequency from DMI */
> +static void find_dmi_mhz(const struct dmi_header *dm, void *private)
> +{
> +	const u8 *dmi_data = (const u8 *)dm;
> +	u16 *mhz = (u16 *)private;
> +
> +	if (dm->type == DMI_ENTRY_PROCESSOR &&
> +	    dm->length >= DMI_ENTRY_PROCESSOR_MIN_LENGTH) {
> +		u16 val = (u16)get_unaligned((const u16 *)
> +				(dmi_data + DMI_PROCESSOR_MAX_SPEED));
> +		*mhz = val > *mhz ? val : *mhz;
> +	}
> +}
> +
> +/* Look up the max frequency in DMI */
> +static u64 get_dmi_max_mhz(void)
> +{
> +	u16 mhz = 0;
> +
> +	dmi_walk(find_dmi_mhz, &mhz);
> +
> +	/*
> +	 * Real stupid fallback value, just in case there is no
> +	 * actual value set.
> +	 */
> +	mhz = mhz ? mhz : 1;
> +
> +	return (u64)mhz;
> +}
> +
> +/* Helper function for the product info callback */
> +static char *copy_string_n(char *dst, char *table, int idx)
> +{
> +	char *d = dst;
> +	char *ptr = table;
> +	int ii;
> +
> +	/* skip the first idx-1 strings */
> +	for (ii = 1; ii < idx; ii++) {
> +		while (*ptr)
> +			ptr++;
> +		ptr++;
> +	}
> +
> +	/* copy in the string we need */
> +	while (*ptr && (d - dst) < (DMI_MAX_STRLEN - 2))
> +		*d++ = *ptr++;
> +
> +	return d;
> +}
> +
> +/* Callback function used to retrieve the product info DMI */
> +static void find_dmi_product_info(const struct dmi_header *dm, void *private)
> +{
> +	const u8 *dmi_data = (const u8 *)dm;
> +	char *ptr = (char *)private;
> +
> +	if (dm->type == DMI_ENTRY_BASEBOARD &&
> +	    dm->length >= DMI_ENTRY_BASEBOARD_MIN_LENGTH) {
> +		int idx;
> +
> +		idx = (int)get_unaligned((const u8 *)
> +				(dmi_data + DMI_BASEBOARD_MANUFACTURER));
> +		ptr = copy_string_n(ptr, (char *)(dmi_data + dm->length), idx);
> +		*ptr++ = ' ';
> +
> +		idx = (int)get_unaligned((const u8 *)
> +				(dmi_data + DMI_BASEBOARD_PRODUCT));
> +		ptr = copy_string_n(ptr, (char *)(dmi_data + dm->length), idx);
> +		*ptr++ = ' ';
> +
> +		idx = (int)get_unaligned((const u8 *)
> +				(dmi_data + DMI_BASEBOARD_VERSION));
> +		ptr = copy_string_n(ptr, (char *)(dmi_data + dm->length), idx);
> +	}
> +}
> +
> +/* Look up the baseboard info in DMI */
> +static void get_dmi_product_info(void)
> +{
> +	if (!dmi_product_info) {
> +		dmi_product_info = kcalloc(DMI_MAX_STRLEN,
> +					   sizeof(char), GFP_KERNEL);
> +		if (!dmi_product_info)
> +			return;
> +	}
> +
> +	dmi_walk(find_dmi_product_info, dmi_product_info);
> +}
> +
>  static int c_show(struct seq_file *m, void *v)
>  {
>  	int i, j;
> @@ -190,6 +298,31 @@ static int c_show(struct seq_file *m, void *v)
>  			seq_printf(m, "model name\t: ARMv8 Processor rev %d (%s)\n",
>  				   MIDR_REVISION(midr), COMPAT_ELF_PLATFORM);
>  
> +		if (IS_ENABLED(CONFIG_DMI)) {
> +			seq_puts(m, "product name\t: ");
> +
> +			if (!dmi_product_info)
> +				get_dmi_product_info();
> +			if (dmi_product_info)
> +				seq_printf(m, "%s", dmi_product_info);
> +			else
> +				seq_puts(m, "<unknown>");
> +
> +			seq_printf(m, ", ARM 8.%d (r%dp%d) CPU",
> +				   MIDR_VARIANT(midr),
> +				   MIDR_VARIANT(midr),
> +				   MIDR_REVISION(midr));
> +
> +			if (!dmi_max_mhz)
> +				dmi_max_mhz = get_dmi_max_mhz();
> +			if (dmi_max_mhz)
> +				seq_printf(m, " @ %d.%02dGHz\n",
> +					   (int)(dmi_max_mhz / 1000),
> +					   (int)(dmi_max_mhz % 1000));
> +			else
> +				seq_puts(m, " @ <unknown>GHz\n");
> +		}
> +
>  		impl = (u8) MIDR_IMPLEMENTOR(midr);
>  		for (j = 0; hw_implementer[j].id != 0; j++) {
>  			if (hw_implementer[j].id == impl) {
> @@ -208,7 +341,7 @@ static int c_show(struct seq_file *m, void *v)
>  		part = (u16) MIDR_PARTNUM(midr);
>  		for (j = 0; parts[j].id != (-1); j++) {
>  			if (parts[j].id == part) {
> -				seq_printf(m, "%s\n", parts[j].name);
> +				seq_printf(m, "%s ", parts[j].name);
>  				break;
>  			}
>  		}
> -- 
> 2.13.5
>