posix: multi process: add support for times()

Add support for the `times()` function, which can be used to get the
number of ticks spent in "system" and "user" mode, and which returns
the "real time" that has expired, since an arbitrary point in the past.

The following invariant should always hold:

rtime <= stime + utime

The `times()` function measures time for the calling process and all
of it's child processes. In Zephyr, we don't support separate processes
(yet), so the time spent in child processes is zero.

Additionally, in Zephyr we do not differentiate between "system" time
(i.e. time spent executing kernel code) and "user" time (time spent
executing application code). We only have information on "total time"
(time spent executing code) and "execution time" (time spent executing
code plus idle time).

For now, only report utime, since it is not clear how to obtain other
values.

Signed-off-by: Chris Friedt <cfriedt@tenstorrent.com>
diff --git a/include/zephyr/posix/sys/times.h b/include/zephyr/posix/sys/times.h
new file mode 100644
index 0000000..9faea50
--- /dev/null
+++ b/include/zephyr/posix/sys/times.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2025 Tenstorrent AI ULC
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef ZEPHYR_INCLUDE_POSIX_SYS_TIMES_H_
+#define ZEPHYR_INCLUDE_POSIX_SYS_TIMES_H_
+
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(_POSIX_MULTI_PROCESS) || defined(__DOXYGEN__)
+
+#if !defined(_TMS_DECLARED) && !defined(__tms_defined)
+struct tms {
+	clock_t tms_utime;
+	clock_t tms_stime;
+	clock_t tms_cutime;
+	clock_t tms_cstime;
+};
+#define _TMS_DECLARED
+#define __tms_defined
+#endif
+
+clock_t times(struct tms *buf);
+
+#endif /* _POSIX_MULTI_PROCESS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZEPHYR_INCLUDE_POSIX_SYS_TIMES_H_ */
diff --git a/lib/posix/options/CMakeLists.txt b/lib/posix/options/CMakeLists.txt
index 429f1d3..f6b7f92 100644
--- a/lib/posix/options/CMakeLists.txt
+++ b/lib/posix/options/CMakeLists.txt
@@ -95,6 +95,9 @@
     multi_process.c
   )
 endif()
+if (CONFIG_POSIX_MULTI_PROCESS)
+  zephyr_compile_definitions(-D_POSIX_MULTI_PROCESS=${POSIX_VERSION})
+endif()
 
 if (NOT CONFIG_TC_PROVIDES_POSIX_NETWORKING)
   zephyr_library_sources_ifdef(CONFIG_POSIX_NETWORKING net.c)
diff --git a/lib/posix/options/Kconfig.procN b/lib/posix/options/Kconfig.procN
index 6a6b030..4f05f3b 100644
--- a/lib/posix/options/Kconfig.procN
+++ b/lib/posix/options/Kconfig.procN
@@ -4,6 +4,8 @@
 
 menuconfig POSIX_MULTI_PROCESS
 	bool "POSIX multi-process support"
+	select SCHED_THREAD_USAGE # times()
+	select THREAD_RUNTIME_STATS
 	help
 	  Support for multi-processing.
 
diff --git a/lib/posix/options/multi_process.c b/lib/posix/options/multi_process.c
index ab697a6..42ed26d 100644
--- a/lib/posix/options/multi_process.c
+++ b/lib/posix/options/multi_process.c
@@ -4,7 +4,15 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
+#include <errno.h>
+#include <time.h>
+
+#include <zephyr/kernel.h>
+#include <zephyr/posix/sys/times.h>
 #include <zephyr/posix/unistd.h>
+#include <zephyr/sys/clock.h>
+#include <zephyr/sys/time_units.h>
+#include <zephyr/sys/util.h>
 
 pid_t getpid(void)
 {
@@ -24,3 +32,29 @@
 #ifdef CONFIG_POSIX_MULTI_PROCESS_ALIAS_GETPID
 FUNC_ALIAS(getpid, _getpid, pid_t);
 #endif /* CONFIG_POSIX_MULTI_PROCESS_ALIAS_GETPID */
+
+clock_t times(struct tms *buffer)
+{
+	int ret;
+	clock_t utime; /* user time */
+	k_thread_runtime_stats_t stats;
+
+	ret = k_thread_runtime_stats_all_get(&stats);
+	if (ret < 0) {
+		errno = -ret;
+		return (clock_t)-1;
+	}
+
+	utime = z_tmcvt(stats.total_cycles, sys_clock_hw_cycles_per_sec(), USEC_PER_SEC,
+			IS_ENABLED(CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME) ? false : true,
+			sizeof(clock_t) == sizeof(uint32_t), false, false);
+
+	*buffer = (struct tms){
+		.tms_utime = utime,
+		.tms_stime = 0,
+		.tms_cutime = 0,
+		.tms_cstime = 0,
+	};
+
+	return utime;
+}