18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Implementation of get_cpuid().
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2014, 2018
68c2ecf20Sopenharmony_ci * Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
78c2ecf20Sopenharmony_ci *	      Thomas Richter <tmricht@linux.vnet.ibm.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <sys/types.h>
118c2ecf20Sopenharmony_ci#include <errno.h>
128c2ecf20Sopenharmony_ci#include <unistd.h>
138c2ecf20Sopenharmony_ci#include <stdio.h>
148c2ecf20Sopenharmony_ci#include <string.h>
158c2ecf20Sopenharmony_ci#include <linux/ctype.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/zalloc.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "../../util/header.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define SYSINFO_MANU	"Manufacturer:"
228c2ecf20Sopenharmony_ci#define SYSINFO_TYPE	"Type:"
238c2ecf20Sopenharmony_ci#define SYSINFO_MODEL	"Model:"
248c2ecf20Sopenharmony_ci#define SRVLVL_CPUMF	"CPU-MF:"
258c2ecf20Sopenharmony_ci#define SRVLVL_VERSION	"version="
268c2ecf20Sopenharmony_ci#define SRVLVL_AUTHORIZATION	"authorization="
278c2ecf20Sopenharmony_ci#define SYSINFO		"/proc/sysinfo"
288c2ecf20Sopenharmony_ci#define SRVLVL		"/proc/service_levels"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciint get_cpuid(char *buffer, size_t sz)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	char *cp, *line = NULL, *line2;
338c2ecf20Sopenharmony_ci	char type[8], model[33], version[8], manufacturer[32], authorization[8];
348c2ecf20Sopenharmony_ci	int tpsize = 0, mdsize = 0, vssize = 0, mfsize = 0, atsize = 0;
358c2ecf20Sopenharmony_ci	int read;
368c2ecf20Sopenharmony_ci	unsigned long line_sz;
378c2ecf20Sopenharmony_ci	size_t nbytes;
388c2ecf20Sopenharmony_ci	FILE *sysinfo;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	/*
418c2ecf20Sopenharmony_ci	 * Scan /proc/sysinfo line by line and read out values for
428c2ecf20Sopenharmony_ci	 * Manufacturer:, Type: and Model:, for example:
438c2ecf20Sopenharmony_ci	 * Manufacturer:    IBM
448c2ecf20Sopenharmony_ci	 * Type:            2964
458c2ecf20Sopenharmony_ci	 * Model:           702              N96
468c2ecf20Sopenharmony_ci	 * The first word is the Model Capacity and the second word is
478c2ecf20Sopenharmony_ci	 * Model (can be omitted). Both words have a maximum size of 16
488c2ecf20Sopenharmony_ci	 * bytes.
498c2ecf20Sopenharmony_ci	 */
508c2ecf20Sopenharmony_ci	memset(manufacturer, 0, sizeof(manufacturer));
518c2ecf20Sopenharmony_ci	memset(type, 0, sizeof(type));
528c2ecf20Sopenharmony_ci	memset(model, 0, sizeof(model));
538c2ecf20Sopenharmony_ci	memset(version, 0, sizeof(version));
548c2ecf20Sopenharmony_ci	memset(authorization, 0, sizeof(authorization));
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	sysinfo = fopen(SYSINFO, "r");
578c2ecf20Sopenharmony_ci	if (sysinfo == NULL)
588c2ecf20Sopenharmony_ci		return errno;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	while ((read = getline(&line, &line_sz, sysinfo)) != -1) {
618c2ecf20Sopenharmony_ci		if (!strncmp(line, SYSINFO_MANU, strlen(SYSINFO_MANU))) {
628c2ecf20Sopenharmony_ci			line2 = line + strlen(SYSINFO_MANU);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci			while ((cp = strtok_r(line2, "\n ", &line2))) {
658c2ecf20Sopenharmony_ci				mfsize += scnprintf(manufacturer + mfsize,
668c2ecf20Sopenharmony_ci						    sizeof(manufacturer) - mfsize, "%s", cp);
678c2ecf20Sopenharmony_ci			}
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		if (!strncmp(line, SYSINFO_TYPE, strlen(SYSINFO_TYPE))) {
718c2ecf20Sopenharmony_ci			line2 = line + strlen(SYSINFO_TYPE);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci			while ((cp = strtok_r(line2, "\n ", &line2))) {
748c2ecf20Sopenharmony_ci				tpsize += scnprintf(type + tpsize,
758c2ecf20Sopenharmony_ci						    sizeof(type) - tpsize, "%s", cp);
768c2ecf20Sopenharmony_ci			}
778c2ecf20Sopenharmony_ci		}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		if (!strncmp(line, SYSINFO_MODEL, strlen(SYSINFO_MODEL))) {
808c2ecf20Sopenharmony_ci			line2 = line + strlen(SYSINFO_MODEL);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci			while ((cp = strtok_r(line2, "\n ", &line2))) {
838c2ecf20Sopenharmony_ci				mdsize += scnprintf(model + mdsize, sizeof(model) - mdsize,
848c2ecf20Sopenharmony_ci						    "%s%s", model[0] ? "," : "", cp);
858c2ecf20Sopenharmony_ci			}
868c2ecf20Sopenharmony_ci			break;
878c2ecf20Sopenharmony_ci		}
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	fclose(sysinfo);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* Missing manufacturer, type or model information should not happen */
928c2ecf20Sopenharmony_ci	if (!manufacturer[0] || !type[0] || !model[0])
938c2ecf20Sopenharmony_ci		return EINVAL;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/*
968c2ecf20Sopenharmony_ci	 * Scan /proc/service_levels and return the CPU-MF counter facility
978c2ecf20Sopenharmony_ci	 * version number and authorization level.
988c2ecf20Sopenharmony_ci	 * Optional, does not exist on z/VM guests.
998c2ecf20Sopenharmony_ci	 */
1008c2ecf20Sopenharmony_ci	sysinfo = fopen(SRVLVL, "r");
1018c2ecf20Sopenharmony_ci	if (sysinfo == NULL)
1028c2ecf20Sopenharmony_ci		goto skip_sysinfo;
1038c2ecf20Sopenharmony_ci	while ((read = getline(&line, &line_sz, sysinfo)) != -1) {
1048c2ecf20Sopenharmony_ci		if (strncmp(line, SRVLVL_CPUMF, strlen(SRVLVL_CPUMF)))
1058c2ecf20Sopenharmony_ci			continue;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		line2 = line + strlen(SRVLVL_CPUMF);
1088c2ecf20Sopenharmony_ci		while ((cp = strtok_r(line2, "\n ", &line2))) {
1098c2ecf20Sopenharmony_ci			if (!strncmp(cp, SRVLVL_VERSION,
1108c2ecf20Sopenharmony_ci				     strlen(SRVLVL_VERSION))) {
1118c2ecf20Sopenharmony_ci				char *sep = strchr(cp, '=');
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci				vssize += scnprintf(version + vssize,
1148c2ecf20Sopenharmony_ci						    sizeof(version) - vssize, "%s", sep + 1);
1158c2ecf20Sopenharmony_ci			}
1168c2ecf20Sopenharmony_ci			if (!strncmp(cp, SRVLVL_AUTHORIZATION,
1178c2ecf20Sopenharmony_ci				     strlen(SRVLVL_AUTHORIZATION))) {
1188c2ecf20Sopenharmony_ci				char *sep = strchr(cp, '=');
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci				atsize += scnprintf(authorization + atsize,
1218c2ecf20Sopenharmony_ci						    sizeof(authorization) - atsize, "%s", sep + 1);
1228c2ecf20Sopenharmony_ci			}
1238c2ecf20Sopenharmony_ci		}
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	fclose(sysinfo);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ciskip_sysinfo:
1288c2ecf20Sopenharmony_ci	free(line);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (version[0] && authorization[0] )
1318c2ecf20Sopenharmony_ci		nbytes = snprintf(buffer, sz, "%s,%s,%s,%s,%s",
1328c2ecf20Sopenharmony_ci				  manufacturer, type, model, version,
1338c2ecf20Sopenharmony_ci				  authorization);
1348c2ecf20Sopenharmony_ci	else
1358c2ecf20Sopenharmony_ci		nbytes = snprintf(buffer, sz, "%s,%s,%s", manufacturer, type,
1368c2ecf20Sopenharmony_ci				  model);
1378c2ecf20Sopenharmony_ci	return (nbytes >= sz) ? ENOBUFS : 0;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cichar *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	char *buf = malloc(128);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (buf && get_cpuid(buf, 128))
1458c2ecf20Sopenharmony_ci		zfree(&buf);
1468c2ecf20Sopenharmony_ci	return buf;
1478c2ecf20Sopenharmony_ci}
148