[PATCH 2/2] hwclock, rtcwake: handle EINVAL from RTC_RD_TIME as "time not set"

Thomas Perrot via busybox <[email protected]> Tue, 26 May 2026 11:21:48 +0200
Newsgroups gmane.linux.busybox
Message-ID <[email protected]>
Some RTC drivers (e.g. rtc-nxp-bbnsm) return EINVAL from RTC_RD_TIME
when the hardware counter is not yet enabled, which is the case on a
freshly manufactured board that has never had its time set. Previously
this caused hwclock and rtcwake to exit with the cryptic message:

  hwclock: RTC_RD_TIME: Invalid argument

Now that rtc_read_tm() returns -errno instead of dying, update both
callers to map EINVAL to an informative "RTC time is not set" error
message. Other ioctl failures are still reported via perror as before.

A local rtc_read_tm_or_die() helper in hwclock.c avoids duplicating
the error-handling logic across the two call sites in read_rtc().

Signed-off-by: Thomas Perrot <[email protected]>
Reviewed-by: Alexandre Belloni <[email protected]>
---
 util-linux/hwclock.c | 15 +++++++++++++--
 util-linux/rtcwake.c |  8 +++++++-
 2 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/util-linux/hwclock.c b/util-linux/hwclock.c
index c3fd0eb57568..da7b4e330418 100644
--- a/util-linux/hwclock.c
+++ b/util-linux/hwclock.c
@@ -66,6 +66,17 @@
 #if !SHOW_HWCLOCK_DIFF
 # define read_rtc(pp_rtcname, sys_tv, utc) read_rtc(pp_rtcname, utc)
 #endif
+static void rtc_read_tm_or_die(struct tm *ptm, int fd)
+{
+	int err = rtc_read_tm(ptm, fd);
+	if (err) {
+		if (err == -EINVAL)
+			bb_simple_error_msg_and_die("RTC time is not set");
+		errno = -err;
+		bb_simple_perror_msg_and_die("ioctl(RTC_RD_TIME)");
+	}
+}
+
 static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc)
 {
 	struct tm tm_time;
@@ -73,13 +84,13 @@ static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc)
 
 	fd = rtc_xopen(pp_rtcname, O_RDONLY);
 
-	rtc_read_tm(&tm_time, fd);
+	rtc_read_tm_or_die(&tm_time, fd);
 
 #if SHOW_HWCLOCK_DIFF
 	{
 		int before = tm_time.tm_sec;
 		while (1) {
-			rtc_read_tm(&tm_time, fd);
+			rtc_read_tm_or_die(&tm_time, fd);
 			xgettimeofday(sys_tv);
 			if (before != (int)tm_time.tm_sec)
 				break;
diff --git a/util-linux/rtcwake.c b/util-linux/rtcwake.c
index a8dfab064452..11ba92156f40 100644
--- a/util-linux/rtcwake.c
+++ b/util-linux/rtcwake.c
@@ -183,7 +183,13 @@ int rtcwake_main(int argc UNUSED_PARAM, char **argv)
 	sys_time = time(NULL);
 	{
 		struct tm tm_time;
-		rtc_read_tm(&tm_time, fd);
+		int err = rtc_read_tm(&tm_time, fd);
+		if (err) {
+			if (err == -EINVAL)
+				bb_simple_error_msg_and_die("RTC time is not set");
+			errno = -err;
+			bb_simple_perror_msg_and_die("ioctl(RTC_RD_TIME)");
+		}
 		rtc_time = rtc_tm2time(&tm_time, utc);
 	}
 

-- 
2.54.0