162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Implementation of get_cpuid().
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright IBM Corp. 2014, 2018
662306a36Sopenharmony_ci * Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
762306a36Sopenharmony_ci *	      Thomas Richter <tmricht@linux.vnet.ibm.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <sys/types.h>
1162306a36Sopenharmony_ci#include <errno.h>
1262306a36Sopenharmony_ci#include <unistd.h>
1362306a36Sopenharmony_ci#include <stdio.h>
1462306a36Sopenharmony_ci#include <string.h>
1562306a36Sopenharmony_ci#include <linux/ctype.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/zalloc.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "../../util/header.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define SYSINFO_MANU	"Manufacturer:"
2262306a36Sopenharmony_ci#define SYSINFO_TYPE	"Type:"
2362306a36Sopenharmony_ci#define SYSINFO_MODEL	"Model:"
2462306a36Sopenharmony_ci#define SRVLVL_CPUMF	"CPU-MF:"
2562306a36Sopenharmony_ci#define SRVLVL_VERSION	"version="
2662306a36Sopenharmony_ci#define SRVLVL_AUTHORIZATION	"authorization="
2762306a36Sopenharmony_ci#define SYSINFO		"/proc/sysinfo"
2862306a36Sopenharmony_ci#define SRVLVL		"/proc/service_levels"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciint get_cpuid(char *buffer, size_t sz)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	char *cp, *line = NULL, *line2;
3362306a36Sopenharmony_ci	char type[8], model[33], version[8], manufacturer[32], authorization[8];
3462306a36Sopenharmony_ci	int tpsize = 0, mdsize = 0, vssize = 0, mfsize = 0, atsize = 0;
3562306a36Sopenharmony_ci	int read;
3662306a36Sopenharmony_ci	unsigned long line_sz;
3762306a36Sopenharmony_ci	size_t nbytes;
3862306a36Sopenharmony_ci	FILE *sysinfo;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	/*
4162306a36Sopenharmony_ci	 * Scan /proc/sysinfo line by line and read out values for
4262306a36Sopenharmony_ci	 * Manufacturer:, Type: and Model:, for example:
4362306a36Sopenharmony_ci	 * Manufacturer:    IBM
4462306a36Sopenharmony_ci	 * Type:            2964
4562306a36Sopenharmony_ci	 * Model:           702              N96
4662306a36Sopenharmony_ci	 * The first word is the Model Capacity and the second word is
4762306a36Sopenharmony_ci	 * Model (can be omitted). Both words have a maximum size of 16
4862306a36Sopenharmony_ci	 * bytes.
4962306a36Sopenharmony_ci	 */
5062306a36Sopenharmony_ci	memset(manufacturer, 0, sizeof(manufacturer));
5162306a36Sopenharmony_ci	memset(type, 0, sizeof(type));
5262306a36Sopenharmony_ci	memset(model, 0, sizeof(model));
5362306a36Sopenharmony_ci	memset(version, 0, sizeof(version));
5462306a36Sopenharmony_ci	memset(authorization, 0, sizeof(authorization));
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	sysinfo = fopen(SYSINFO, "r");
5762306a36Sopenharmony_ci	if (sysinfo == NULL)
5862306a36Sopenharmony_ci		return errno;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	while ((read = getline(&line, &line_sz, sysinfo)) != -1) {
6162306a36Sopenharmony_ci		if (!strncmp(line, SYSINFO_MANU, strlen(SYSINFO_MANU))) {
6262306a36Sopenharmony_ci			line2 = line + strlen(SYSINFO_MANU);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci			while ((cp = strtok_r(line2, "\n ", &line2))) {
6562306a36Sopenharmony_ci				mfsize += scnprintf(manufacturer + mfsize,
6662306a36Sopenharmony_ci						    sizeof(manufacturer) - mfsize, "%s", cp);
6762306a36Sopenharmony_ci			}
6862306a36Sopenharmony_ci		}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci		if (!strncmp(line, SYSINFO_TYPE, strlen(SYSINFO_TYPE))) {
7162306a36Sopenharmony_ci			line2 = line + strlen(SYSINFO_TYPE);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci			while ((cp = strtok_r(line2, "\n ", &line2))) {
7462306a36Sopenharmony_ci				tpsize += scnprintf(type + tpsize,
7562306a36Sopenharmony_ci						    sizeof(type) - tpsize, "%s", cp);
7662306a36Sopenharmony_ci			}
7762306a36Sopenharmony_ci		}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		if (!strncmp(line, SYSINFO_MODEL, strlen(SYSINFO_MODEL))) {
8062306a36Sopenharmony_ci			line2 = line + strlen(SYSINFO_MODEL);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci			while ((cp = strtok_r(line2, "\n ", &line2))) {
8362306a36Sopenharmony_ci				mdsize += scnprintf(model + mdsize, sizeof(model) - mdsize,
8462306a36Sopenharmony_ci						    "%s%s", model[0] ? "," : "", cp);
8562306a36Sopenharmony_ci			}
8662306a36Sopenharmony_ci			break;
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	fclose(sysinfo);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* Missing manufacturer, type or model information should not happen */
9262306a36Sopenharmony_ci	if (!manufacturer[0] || !type[0] || !model[0])
9362306a36Sopenharmony_ci		return EINVAL;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/*
9662306a36Sopenharmony_ci	 * Scan /proc/service_levels and return the CPU-MF counter facility
9762306a36Sopenharmony_ci	 * version number and authorization level.
9862306a36Sopenharmony_ci	 * Optional, does not exist on z/VM guests.
9962306a36Sopenharmony_ci	 */
10062306a36Sopenharmony_ci	sysinfo = fopen(SRVLVL, "r");
10162306a36Sopenharmony_ci	if (sysinfo == NULL)
10262306a36Sopenharmony_ci		goto skip_sysinfo;
10362306a36Sopenharmony_ci	while ((read = getline(&line, &line_sz, sysinfo)) != -1) {
10462306a36Sopenharmony_ci		if (strncmp(line, SRVLVL_CPUMF, strlen(SRVLVL_CPUMF)))
10562306a36Sopenharmony_ci			continue;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		line2 = line + strlen(SRVLVL_CPUMF);
10862306a36Sopenharmony_ci		while ((cp = strtok_r(line2, "\n ", &line2))) {
10962306a36Sopenharmony_ci			if (!strncmp(cp, SRVLVL_VERSION,
11062306a36Sopenharmony_ci				     strlen(SRVLVL_VERSION))) {
11162306a36Sopenharmony_ci				char *sep = strchr(cp, '=');
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci				vssize += scnprintf(version + vssize,
11462306a36Sopenharmony_ci						    sizeof(version) - vssize, "%s", sep + 1);
11562306a36Sopenharmony_ci			}
11662306a36Sopenharmony_ci			if (!strncmp(cp, SRVLVL_AUTHORIZATION,
11762306a36Sopenharmony_ci				     strlen(SRVLVL_AUTHORIZATION))) {
11862306a36Sopenharmony_ci				char *sep = strchr(cp, '=');
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci				atsize += scnprintf(authorization + atsize,
12162306a36Sopenharmony_ci						    sizeof(authorization) - atsize, "%s", sep + 1);
12262306a36Sopenharmony_ci			}
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	fclose(sysinfo);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ciskip_sysinfo:
12862306a36Sopenharmony_ci	free(line);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (version[0] && authorization[0] )
13162306a36Sopenharmony_ci		nbytes = snprintf(buffer, sz, "%s,%s,%s,%s,%s",
13262306a36Sopenharmony_ci				  manufacturer, type, model, version,
13362306a36Sopenharmony_ci				  authorization);
13462306a36Sopenharmony_ci	else
13562306a36Sopenharmony_ci		nbytes = snprintf(buffer, sz, "%s,%s,%s", manufacturer, type,
13662306a36Sopenharmony_ci				  model);
13762306a36Sopenharmony_ci	return (nbytes >= sz) ? ENOBUFS : 0;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cichar *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	char *buf = malloc(128);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (buf && get_cpuid(buf, 128))
14562306a36Sopenharmony_ci		zfree(&buf);
14662306a36Sopenharmony_ci	return buf;
14762306a36Sopenharmony_ci}
148