cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rtctest.c (9008B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Real Time Clock Driver Test Program
      4 *
      5 * Copyright (c) 2018 Alexandre Belloni <alexandre.belloni@bootlin.com>
      6 */
      7
      8#include <errno.h>
      9#include <fcntl.h>
     10#include <linux/rtc.h>
     11#include <stdio.h>
     12#include <stdlib.h>
     13#include <sys/ioctl.h>
     14#include <sys/time.h>
     15#include <sys/types.h>
     16#include <time.h>
     17#include <unistd.h>
     18
     19#include "../kselftest_harness.h"
     20
     21#define NUM_UIE 3
     22#define ALARM_DELTA 3
     23#define READ_LOOP_DURATION_SEC 30
     24#define READ_LOOP_SLEEP_MS 11
     25
     26static char *rtc_file = "/dev/rtc0";
     27
     28FIXTURE(rtc) {
     29	int fd;
     30};
     31
     32FIXTURE_SETUP(rtc) {
     33	self->fd = open(rtc_file, O_RDONLY);
     34	ASSERT_NE(-1, self->fd);
     35}
     36
     37FIXTURE_TEARDOWN(rtc) {
     38	close(self->fd);
     39}
     40
     41TEST_F(rtc, date_read) {
     42	int rc;
     43	struct rtc_time rtc_tm;
     44
     45	/* Read the RTC time/date */
     46	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
     47	ASSERT_NE(-1, rc);
     48
     49	TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
     50	       rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
     51	       rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
     52}
     53
     54static time_t rtc_time_to_timestamp(struct rtc_time *rtc_time)
     55{
     56	struct tm tm_time = {
     57	       .tm_sec = rtc_time->tm_sec,
     58	       .tm_min = rtc_time->tm_min,
     59	       .tm_hour = rtc_time->tm_hour,
     60	       .tm_mday = rtc_time->tm_mday,
     61	       .tm_mon = rtc_time->tm_mon,
     62	       .tm_year = rtc_time->tm_year,
     63	};
     64
     65	return mktime(&tm_time);
     66}
     67
     68static void nanosleep_with_retries(long ns)
     69{
     70	struct timespec req = {
     71		.tv_sec = 0,
     72		.tv_nsec = ns,
     73	};
     74	struct timespec rem;
     75
     76	while (nanosleep(&req, &rem) != 0) {
     77		req.tv_sec = rem.tv_sec;
     78		req.tv_nsec = rem.tv_nsec;
     79	}
     80}
     81
     82TEST_F_TIMEOUT(rtc, date_read_loop, READ_LOOP_DURATION_SEC + 2) {
     83	int rc;
     84	long iter_count = 0;
     85	struct rtc_time rtc_tm;
     86	time_t start_rtc_read, prev_rtc_read;
     87
     88	TH_LOG("Continuously reading RTC time for %ds (with %dms breaks after every read).",
     89	       READ_LOOP_DURATION_SEC, READ_LOOP_SLEEP_MS);
     90
     91	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
     92	ASSERT_NE(-1, rc);
     93	start_rtc_read = rtc_time_to_timestamp(&rtc_tm);
     94	prev_rtc_read = start_rtc_read;
     95
     96	do  {
     97		time_t rtc_read;
     98
     99		rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
    100		ASSERT_NE(-1, rc);
    101
    102		rtc_read = rtc_time_to_timestamp(&rtc_tm);
    103		/* Time should not go backwards */
    104		ASSERT_LE(prev_rtc_read, rtc_read);
    105		/* Time should not increase more then 1s at a time */
    106		ASSERT_GE(prev_rtc_read + 1, rtc_read);
    107
    108		/* Sleep 11ms to avoid killing / overheating the RTC */
    109		nanosleep_with_retries(READ_LOOP_SLEEP_MS * 1000000);
    110
    111		prev_rtc_read = rtc_read;
    112		iter_count++;
    113	} while (prev_rtc_read <= start_rtc_read + READ_LOOP_DURATION_SEC);
    114
    115	TH_LOG("Performed %ld RTC time reads.", iter_count);
    116}
    117
    118TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
    119	int i, rc, irq = 0;
    120	unsigned long data;
    121
    122	/* Turn on update interrupts */
    123	rc = ioctl(self->fd, RTC_UIE_ON, 0);
    124	if (rc == -1) {
    125		ASSERT_EQ(EINVAL, errno);
    126		TH_LOG("skip update IRQs not supported.");
    127		return;
    128	}
    129
    130	for (i = 0; i < NUM_UIE; i++) {
    131		/* This read will block */
    132		rc = read(self->fd, &data, sizeof(data));
    133		ASSERT_NE(-1, rc);
    134		irq++;
    135	}
    136
    137	EXPECT_EQ(NUM_UIE, irq);
    138
    139	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
    140	ASSERT_NE(-1, rc);
    141}
    142
    143TEST_F(rtc, uie_select) {
    144	int i, rc, irq = 0;
    145	unsigned long data;
    146
    147	/* Turn on update interrupts */
    148	rc = ioctl(self->fd, RTC_UIE_ON, 0);
    149	if (rc == -1) {
    150		ASSERT_EQ(EINVAL, errno);
    151		TH_LOG("skip update IRQs not supported.");
    152		return;
    153	}
    154
    155	for (i = 0; i < NUM_UIE; i++) {
    156		struct timeval tv = { .tv_sec = 2 };
    157		fd_set readfds;
    158
    159		FD_ZERO(&readfds);
    160		FD_SET(self->fd, &readfds);
    161		/* The select will wait until an RTC interrupt happens. */
    162		rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
    163		ASSERT_NE(-1, rc);
    164		ASSERT_NE(0, rc);
    165
    166		/* This read won't block */
    167		rc = read(self->fd, &data, sizeof(unsigned long));
    168		ASSERT_NE(-1, rc);
    169		irq++;
    170	}
    171
    172	EXPECT_EQ(NUM_UIE, irq);
    173
    174	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
    175	ASSERT_NE(-1, rc);
    176}
    177
    178TEST_F(rtc, alarm_alm_set) {
    179	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
    180	unsigned long data;
    181	struct rtc_time tm;
    182	fd_set readfds;
    183	time_t secs, new;
    184	int rc;
    185
    186	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
    187	ASSERT_NE(-1, rc);
    188
    189	secs = timegm((struct tm *)&tm) + ALARM_DELTA;
    190	gmtime_r(&secs, (struct tm *)&tm);
    191
    192	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
    193	if (rc == -1) {
    194		ASSERT_EQ(EINVAL, errno);
    195		TH_LOG("skip alarms are not supported.");
    196		return;
    197	}
    198
    199	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
    200	ASSERT_NE(-1, rc);
    201
    202	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
    203	       tm.tm_hour, tm.tm_min, tm.tm_sec);
    204
    205	/* Enable alarm interrupts */
    206	rc = ioctl(self->fd, RTC_AIE_ON, 0);
    207	ASSERT_NE(-1, rc);
    208
    209	FD_ZERO(&readfds);
    210	FD_SET(self->fd, &readfds);
    211
    212	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
    213	ASSERT_NE(-1, rc);
    214	ASSERT_NE(0, rc);
    215
    216	/* Disable alarm interrupts */
    217	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
    218	ASSERT_NE(-1, rc);
    219
    220	rc = read(self->fd, &data, sizeof(unsigned long));
    221	ASSERT_NE(-1, rc);
    222	TH_LOG("data: %lx", data);
    223
    224	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
    225	ASSERT_NE(-1, rc);
    226
    227	new = timegm((struct tm *)&tm);
    228	ASSERT_EQ(new, secs);
    229}
    230
    231TEST_F(rtc, alarm_wkalm_set) {
    232	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
    233	struct rtc_wkalrm alarm = { 0 };
    234	struct rtc_time tm;
    235	unsigned long data;
    236	fd_set readfds;
    237	time_t secs, new;
    238	int rc;
    239
    240	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
    241	ASSERT_NE(-1, rc);
    242
    243	secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
    244	gmtime_r(&secs, (struct tm *)&alarm.time);
    245
    246	alarm.enabled = 1;
    247
    248	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
    249	if (rc == -1) {
    250		ASSERT_EQ(EINVAL, errno);
    251		TH_LOG("skip alarms are not supported.");
    252		return;
    253	}
    254
    255	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
    256	ASSERT_NE(-1, rc);
    257
    258	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
    259	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
    260	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
    261	       alarm.time.tm_min, alarm.time.tm_sec);
    262
    263	FD_ZERO(&readfds);
    264	FD_SET(self->fd, &readfds);
    265
    266	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
    267	ASSERT_NE(-1, rc);
    268	ASSERT_NE(0, rc);
    269
    270	rc = read(self->fd, &data, sizeof(unsigned long));
    271	ASSERT_NE(-1, rc);
    272
    273	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
    274	ASSERT_NE(-1, rc);
    275
    276	new = timegm((struct tm *)&tm);
    277	ASSERT_EQ(new, secs);
    278}
    279
    280TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
    281	struct timeval tv = { .tv_sec = 62 };
    282	unsigned long data;
    283	struct rtc_time tm;
    284	fd_set readfds;
    285	time_t secs, new;
    286	int rc;
    287
    288	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
    289	ASSERT_NE(-1, rc);
    290
    291	secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
    292	gmtime_r(&secs, (struct tm *)&tm);
    293
    294	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
    295	if (rc == -1) {
    296		ASSERT_EQ(EINVAL, errno);
    297		TH_LOG("skip alarms are not supported.");
    298		return;
    299	}
    300
    301	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
    302	ASSERT_NE(-1, rc);
    303
    304	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
    305	       tm.tm_hour, tm.tm_min, tm.tm_sec);
    306
    307	/* Enable alarm interrupts */
    308	rc = ioctl(self->fd, RTC_AIE_ON, 0);
    309	ASSERT_NE(-1, rc);
    310
    311	FD_ZERO(&readfds);
    312	FD_SET(self->fd, &readfds);
    313
    314	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
    315	ASSERT_NE(-1, rc);
    316	ASSERT_NE(0, rc);
    317
    318	/* Disable alarm interrupts */
    319	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
    320	ASSERT_NE(-1, rc);
    321
    322	rc = read(self->fd, &data, sizeof(unsigned long));
    323	ASSERT_NE(-1, rc);
    324	TH_LOG("data: %lx", data);
    325
    326	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
    327	ASSERT_NE(-1, rc);
    328
    329	new = timegm((struct tm *)&tm);
    330	ASSERT_EQ(new, secs);
    331}
    332
    333TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
    334	struct timeval tv = { .tv_sec = 62 };
    335	struct rtc_wkalrm alarm = { 0 };
    336	struct rtc_time tm;
    337	unsigned long data;
    338	fd_set readfds;
    339	time_t secs, new;
    340	int rc;
    341
    342	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
    343	ASSERT_NE(-1, rc);
    344
    345	secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
    346	gmtime_r(&secs, (struct tm *)&alarm.time);
    347
    348	alarm.enabled = 1;
    349
    350	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
    351	if (rc == -1) {
    352		ASSERT_EQ(EINVAL, errno);
    353		TH_LOG("skip alarms are not supported.");
    354		return;
    355	}
    356
    357	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
    358	ASSERT_NE(-1, rc);
    359
    360	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
    361	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
    362	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
    363	       alarm.time.tm_min, alarm.time.tm_sec);
    364
    365	FD_ZERO(&readfds);
    366	FD_SET(self->fd, &readfds);
    367
    368	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
    369	ASSERT_NE(-1, rc);
    370	ASSERT_NE(0, rc);
    371
    372	rc = read(self->fd, &data, sizeof(unsigned long));
    373	ASSERT_NE(-1, rc);
    374
    375	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
    376	ASSERT_NE(-1, rc);
    377
    378	new = timegm((struct tm *)&tm);
    379	ASSERT_EQ(new, secs);
    380}
    381
    382static void __attribute__((constructor))
    383__constructor_order_last(void)
    384{
    385	if (!__constructor_order)
    386		__constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
    387}
    388
    389int main(int argc, char **argv)
    390{
    391	switch (argc) {
    392	case 2:
    393		rtc_file = argv[1];
    394		/* FALLTHROUGH */
    395	case 1:
    396		break;
    397	default:
    398		fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
    399		return 1;
    400	}
    401
    402	return test_harness_run(argc, argv);
    403}