lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260114114638.2290765-18-cristian.marussi@arm.com>
Date: Wed, 14 Jan 2026 11:46:21 +0000
From: Cristian Marussi <cristian.marussi@....com>
To: linux-kernel@...r.kernel.org,
	linux-arm-kernel@...ts.infradead.org,
	arm-scmi@...r.kernel.org,
	linux-fsdevel@...r.kernel.org
Cc: sudeep.holla@....com,
	james.quinlan@...adcom.com,
	f.fainelli@...il.com,
	vincent.guittot@...aro.org,
	etienne.carriere@...com,
	peng.fan@....nxp.com,
	michal.simek@....com,
	dan.carpenter@...aro.org,
	d-gole@...com,
	jonathan.cameron@...wei.com,
	elif.topuz@....com,
	lukasz.luba@....com,
	philip.radford@....com,
	souvik.chakravarty@....com,
	Cristian Marussi <cristian.marussi@....com>
Subject: [PATCH v2 17/17] [RFC] tools/scmi: Add SCMI Telemetry testing tool

Add a testing tool that exercises the SCMI ioctls UAPI interface: as of
now the tool simply queries the initial state of the SCMI Telemetry
subsystem, tries to enable all the existent Data Events and dumps all
the Telemetry data.

Signed-off-by: Cristian Marussi <cristian.marussi@....com>
---
Basic implementation just to exercise a few IOCTls: to be refined and
extended to support a more interactive usage.
---
 tools/testing/scmi/Makefile |  25 +++
 tools/testing/scmi/stlm.c   | 385 ++++++++++++++++++++++++++++++++++++
 2 files changed, 410 insertions(+)
 create mode 100644 tools/testing/scmi/Makefile
 create mode 100644 tools/testing/scmi/stlm.c

diff --git a/tools/testing/scmi/Makefile b/tools/testing/scmi/Makefile
new file mode 100644
index 000000000000..a6a101f8398b
--- /dev/null
+++ b/tools/testing/scmi/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+CC?=$(CROSS_COMPILE)gcc
+OBJS = stlm.o
+
+CFLAGS=-Wall -static -std=gnu11 -I ../../../include/uapi/
+ifneq ($(DEBUG), )
+	CFLAGS+=-O0 -g -ggdb
+else
+	CFLAGS+=-static
+endif
+
+all: stlm
+
+stlm: $(OBJS)
+	$(CC) $(CFLAGS) $^ -o $@
+
+%.o: %.c
+	$(CC) $(CFLAGS) -c $<
+
+clean:
+	rm -f *.o
+	rm -f stlm
+
+.PHONY: clean
diff --git a/tools/testing/scmi/stlm.c b/tools/testing/scmi/stlm.c
new file mode 100644
index 000000000000..137ef278d1dd
--- /dev/null
+++ b/tools/testing/scmi/stlm.c
@@ -0,0 +1,385 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <unistd.h>
+
+#include <linux/scmi.h>
+
+#define SLEEP_MS	3000
+#define DEF_TLM_ROOT	"/sys/fs/arm_telemetry/"
+
+#define IOCTL_ERR_STR(_ioctl)	"IOCTL:" #_ioctl
+
+struct tlm_de {
+	struct scmi_tlm_de_info *info;
+	struct scmi_tlm_de_config cfg;
+	struct scmi_tlm_de_sample sample;
+};
+
+struct tlm_group {
+	int fd;
+	struct scmi_tlm_grp_info *info;
+	struct scmi_tlm_grp_desc *desc;
+	struct scmi_tlm_intervals *ivs;
+};
+
+struct tlm_state {
+	int dfd;
+	int fd;
+	int g_dfd;
+	const char *path;
+	struct scmi_tlm_base_info info;
+	struct scmi_tlm_config cfg;
+	struct scmi_tlm_intervals *ivs;
+	unsigned int num_des;
+	struct tlm_de *des;
+	unsigned int num_groups;
+	struct tlm_group *grps;
+};
+
+static inline void dump_state(struct tlm_state *st)
+{
+	uint32_t *uuid32 = st->info.de_impl_version;
+	uint16_t *uuid16 = (uint16_t *)&st->info.de_impl_version[1];
+
+	fprintf(stdout, "- SYSTEM TELEMETRY @instance: %s\n\n", st->path);
+	fprintf(stdout, "+ Version: 0x%08X\n", st->info.version);
+	fprintf(stdout, "+ DEs#: %d\n", st->info.num_des);
+	fprintf(stdout, "+ GRPS#: %d\n", st->info.num_groups);
+	fprintf(stdout, "+ INTRV#: %d\n", st->info.num_intervals);
+
+	fprintf(stdout, "+ UUID: ");
+	fprintf(stdout, "%X-", uuid32[0]);
+	fprintf(stdout, "%X-", uuid16[0]);
+	fprintf(stdout, "%X-", uuid16[1]);
+	fprintf(stdout, "%X", uuid16[2]);
+	fprintf(stdout, "%X\n", uuid32[3]);
+
+	fprintf(stdout, "\n+ TLM_ENABLED: %d\n", st->cfg.enable);
+	fprintf(stdout, "+ CURRENT_UPDATE_INTERVAL: %d\n",
+		st->cfg.current_update_interval);
+
+	fprintf(stdout, "+ Found #%u Global Update Intervals\n",
+		st->info.num_intervals);
+	for (int i = 0; i < st->ivs->num; i++)
+		fprintf(stdout, "\t[%d]::%u\n", i, st->ivs->update_intervals[i]);
+
+	if (st->info.num_des != st->num_des) {
+		fprintf(stdout, "\n++++++ DES NOT FULLY_ENUMERATED ++++++\n");
+		fprintf(stdout, "+++ DECLARED:%u  ENUMERATED:%u +++\n",
+			st->info.num_des, st->num_des);
+	}
+
+	fprintf(stdout, "\n+ Found #%d DEs:\n", st->num_des);
+	for (int i = 0; i < st->num_des; i++)
+		fprintf(stdout, "\t0x%08X %s %s -- TS:%16llu %016llX\n",
+			st->des[i].info->id,
+			st->des[i].cfg.enable ? "ON" : "--",
+			st->des[i].cfg.t_enable ? "TS_ON" : "-----",
+			st->des[i].sample.tstamp, st->des[i].sample.val);
+	fprintf(stdout, "\n");
+
+	fprintf(stdout, "+ Found %d GRPs: ", st->num_groups);
+	for (int i = 0; i < st->num_groups; i++) {
+		fprintf(stdout, "\n\tGRP_ID:%d  DES#:%d  INTRVS#:%d\n",
+			st->grps[i].info->id, st->grps[i].info->num_des,
+			st->grps[i].info->num_intervals);
+
+		fprintf(stdout, "\tCOMPOSING_DES:");
+		for (int j = 0; j < st->grps[i].desc->num_des; j++)
+			fprintf(stdout, "0x%08X ",
+				st->grps[i].desc->composing_des[j]);
+		fprintf(stdout, "\n");
+	}
+}
+
+static int discover_base_info(int fd, struct scmi_tlm_base_info *info)
+{
+	int ret;
+
+	ret = ioctl(fd, SCMI_TLM_GET_INFO, info);
+	if (ret) {
+		perror(IOCTL_ERR_STR(SCMI_TLM_GET_INFO));
+		return ret;
+	}
+
+	return ret;
+}
+
+static struct scmi_tlm_des_list *scmi_get_des_list(int fd, int num_des)
+{
+	struct scmi_tlm_des_list *dsl;
+	size_t size = sizeof(*dsl) + num_des * sizeof(dsl->des[0]);
+	int ret;
+
+	dsl = malloc(size);
+	if (!dsl)
+		return NULL;
+
+	bzero(dsl, size);
+	dsl->num_des = num_des;
+	ret = ioctl(fd, SCMI_TLM_GET_DE_LIST, dsl);
+	if (ret) {
+		perror(IOCTL_ERR_STR(SCMI_TLM_GET_DE_LIST));
+		return NULL;
+	}
+
+	return dsl;
+}
+
+static struct tlm_de *enumerate_des(struct tlm_state *st)
+{
+	struct scmi_tlm_des_list *dsl;
+	struct tlm_de *des;
+
+	dsl = scmi_get_des_list(st->fd, st->info.num_des);
+	if (!dsl)
+		return NULL;
+
+	st->num_des = dsl->num_des;
+	des = malloc(sizeof(*des) * st->num_des);
+	if (!des)
+		return NULL;
+
+	bzero(des, sizeof(*des) * st->num_des);
+	for (int i = 0; i < st->num_des; i++) {
+		struct tlm_de *de = &des[i];
+		int ret;
+
+		de->info = &dsl->des[i];
+		de->cfg.id = de->info->id;
+		ret = ioctl(st->fd, SCMI_TLM_GET_DE_CFG, &de->cfg);
+		if (ret) {
+			perror(IOCTL_ERR_STR(SCMI_TLM_GET_DE_CFG));
+			continue;
+		}
+
+		if (!de->cfg.enable)
+			continue;
+
+		/* Collect initial sample */
+		de->sample.id = de->info->id;
+		ret = ioctl(st->fd, SCMI_TLM_GET_DE_VALUE, &de->sample);
+		if (ret) {
+			perror(IOCTL_ERR_STR(SCMI_TLM_GET_DE_VALUE));
+			continue;
+		}
+	}
+
+	return des;
+}
+
+static int get_current_config(int fd, struct scmi_tlm_config *cfg)
+{
+	int ret;
+
+	ret = ioctl(fd, SCMI_TLM_GET_CFG, cfg);
+	if (ret) {
+		perror(IOCTL_ERR_STR(SCMI_TLM_GET_CFG));
+		return ret;
+	}
+
+	return ret;
+}
+
+static struct scmi_tlm_grps_list *scmi_get_grps_list(int fd, int num_groups)
+{
+	struct scmi_tlm_grps_list *gsl;
+	size_t size = sizeof(*gsl) + num_groups * sizeof(gsl->grps[0]);
+	int ret;
+
+	gsl = malloc(size);
+	if (!gsl)
+		return NULL;
+
+	bzero(gsl, size);
+	gsl->num_grps = num_groups;
+	ret = ioctl(fd, SCMI_TLM_GET_GRP_LIST, gsl);
+	if (ret) {
+		perror(IOCTL_ERR_STR(SCMI_TLM_GET_GRP_LIST));
+		return NULL;
+	}
+
+	return gsl;
+}
+
+static struct scmi_tlm_intervals *enumerate_intervals(int fd, int num_intervals)
+{
+	struct scmi_tlm_intervals *ivs;
+	size_t sz;
+	int ret;
+
+	sz = sizeof(*ivs) + sizeof(*ivs->update_intervals) * num_intervals;
+	ivs = malloc(sz);
+	if (!ivs)
+		return NULL;
+
+	memset(ivs, 0, sz);
+
+	ivs->num = num_intervals;
+	ret = ioctl(fd, SCMI_TLM_GET_INTRVS, ivs);
+	if (ret) {
+		perror(IOCTL_ERR_STR(SCMI_TLM_GET_INTRVS));
+		free(ivs);
+		return NULL;
+	}
+
+	return ivs;
+}
+
+static struct tlm_group *enumerate_groups(struct tlm_state *st)
+{
+	struct scmi_tlm_grps_list *gsl;
+	struct tlm_group *grps;
+
+	gsl = scmi_get_grps_list(st->fd, st->info.num_groups);
+	if (!gsl)
+		return NULL;
+
+	st->g_dfd = openat(st->dfd, "groups", O_RDONLY);
+	if (st->g_dfd < 0)
+		return NULL;
+
+	st->num_groups = gsl->num_grps;
+	grps = malloc(sizeof(*grps) * st->num_groups);
+	if (!grps)
+		return NULL;
+
+	bzero(grps, sizeof(*grps) * st->num_groups);
+	for (int i = 0; i < st->num_groups; i++) {
+		struct tlm_group *grp = &grps[i];
+		char gctrl[32];
+		size_t size;
+		int ret;
+
+		snprintf(gctrl, 32, "%d/control", i);
+		grp->fd = openat(st->g_dfd, gctrl, O_RDWR);
+		if (grp->fd < 0)
+			return NULL;
+
+		grp->info = &gsl->grps[i];
+		size = sizeof(*grp->desc) + sizeof(uint32_t) * grp->info->num_des;
+		grp->desc = malloc(size);
+		if (!grp->desc)
+			return NULL;
+
+		bzero(grp->desc, size);
+		grp->desc->num_des = grp->info->num_des;
+		ret = ioctl(grp->fd, SCMI_TLM_GET_GRP_DESC, grp->desc);
+		if (ret) {
+			perror(IOCTL_ERR_STR(SCMI_TLM_GET_GRP_DESC));
+			continue;
+		}
+
+		grp->ivs = enumerate_intervals(grp->fd, grp->info->num_intervals);
+	}
+
+	return grps;
+}
+
+static int get_tlm_state(const char *path, struct tlm_state *st)
+{
+	int ret;
+
+	st->dfd = open(path, O_RDONLY);
+	if (st->dfd < 0) {
+		perror("open");
+		return st->dfd;
+	}
+
+	st->fd = openat(st->dfd, "control", O_RDWR);
+	if (st->fd < 0) {
+		perror("openat");
+		return st->fd;
+	}
+
+	ret = discover_base_info(st->fd, &st->info);
+	if (ret)
+		return ret;
+
+	st->ivs = enumerate_intervals(st->fd, st->info.num_intervals);
+	if (!st->ivs)
+		return -1;
+
+	ret = get_current_config(st->fd, &st->cfg);
+	if (ret)
+		return ret;
+
+	if (st->info.num_des)
+		st->des = enumerate_des(st);
+
+	if (st->info.num_groups)
+		st->grps = enumerate_groups(st);
+
+	st->path = path;
+
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	const char *tlm_root_instance = DEF_TLM_ROOT "tlm_0/";
+	struct scmi_tlm_data_read *bulk;
+	struct scmi_tlm_de_config de_cfg = {};
+	struct tlm_state st = {};
+	size_t bulk_sz;
+	int ret;
+
+	ret = get_tlm_state(tlm_root_instance, &st);
+	if (ret)
+		return ret;
+
+	dump_state(&st);
+
+	bulk_sz = sizeof(*bulk) + sizeof(bulk->samples[0]) * st.info.num_des;
+	bulk = malloc(bulk_sz);
+	if (!bulk)
+		return -1;
+
+	bzero(bulk, bulk_sz);
+	bulk->num_samples = st.info.num_des;
+	ret = ioctl(st.fd, SCMI_TLM_SINGLE_SAMPLE, bulk);
+	if (ret) {
+		perror(IOCTL_ERR_STR(SCMI_TLM_SINGLE_SAMPLE));
+		return -1;
+	}
+
+	fprintf(stdout, "\n--- Enabling ALL DEs with timestamp...\n");
+	de_cfg.enable = 1;
+	de_cfg.t_enable = 1;
+	ret = ioctl(st.fd, SCMI_TLM_SET_ALL_CFG, &de_cfg);
+	if (ret) {
+		perror(IOCTL_ERR_STR(SCMI_TLM_SET_ALL_CFG));
+		return ret;
+	}
+
+	fprintf(stdout, "\n- Single ASYNC read -\n-------------------\n");
+	for (int i = 0; i < bulk->num_samples; i++)
+		fprintf(stdout, "0x%08X %016llu %016llX\n",
+			bulk->samples[i].id, bulk->samples[i].tstamp,
+			bulk->samples[i].val);
+
+	bzero(bulk, bulk_sz);
+	bulk->num_samples = st.info.num_des;
+	ret = ioctl(st.fd, SCMI_TLM_BULK_READ, bulk);
+	if (ret) {
+		perror(IOCTL_ERR_STR(SCMI_TLM_BULK_READ));
+		return -1;
+	}
+
+	fprintf(stdout, "\n- BULK read -\n-------------------\n");
+	for (int i = 0; i < bulk->num_samples; i++)
+		fprintf(stdout, "0x%08X %016llu %016llX\n",
+			bulk->samples[i].id, bulk->samples[i].tstamp,
+			bulk->samples[i].val);
+
+	return 0;
+}
-- 
2.52.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ