xref: /openbmc/linux/arch/um/drivers/rtc_user.c (revision 39f75da7)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020 Intel Corporation
4  * Author: Johannes Berg <johannes@sipsolutions.net>
5  */
6 #include <stdbool.h>
7 #include <os.h>
8 #include <errno.h>
9 #include <sched.h>
10 #include <unistd.h>
11 #include <kern_util.h>
12 #include <sys/select.h>
13 #include <stdio.h>
14 #include <sys/timerfd.h>
15 #include "rtc.h"
16 
17 static int uml_rtc_irq_fds[2];
18 
uml_rtc_send_timetravel_alarm(void)19 void uml_rtc_send_timetravel_alarm(void)
20 {
21 	unsigned long long c = 1;
22 
23 	CATCH_EINTR(write(uml_rtc_irq_fds[1], &c, sizeof(c)));
24 }
25 
uml_rtc_start(bool timetravel)26 int uml_rtc_start(bool timetravel)
27 {
28 	int err;
29 
30 	if (timetravel) {
31 		int err = os_pipe(uml_rtc_irq_fds, 1, 1);
32 		if (err)
33 			goto fail;
34 	} else {
35 		uml_rtc_irq_fds[0] = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
36 		if (uml_rtc_irq_fds[0] < 0) {
37 			err = -errno;
38 			goto fail;
39 		}
40 
41 		/* apparently timerfd won't send SIGIO, use workaround */
42 		sigio_broken(uml_rtc_irq_fds[0]);
43 		err = add_sigio_fd(uml_rtc_irq_fds[0]);
44 		if (err < 0) {
45 			close(uml_rtc_irq_fds[0]);
46 			goto fail;
47 		}
48 	}
49 
50 	return uml_rtc_irq_fds[0];
51 fail:
52 	uml_rtc_stop(timetravel);
53 	return err;
54 }
55 
uml_rtc_enable_alarm(unsigned long long delta_seconds)56 int uml_rtc_enable_alarm(unsigned long long delta_seconds)
57 {
58 	struct itimerspec it = {
59 		.it_value = {
60 			.tv_sec = delta_seconds,
61 		},
62 	};
63 
64 	if (timerfd_settime(uml_rtc_irq_fds[0], 0, &it, NULL))
65 		return -errno;
66 	return 0;
67 }
68 
uml_rtc_disable_alarm(void)69 void uml_rtc_disable_alarm(void)
70 {
71 	uml_rtc_enable_alarm(0);
72 }
73 
uml_rtc_stop(bool timetravel)74 void uml_rtc_stop(bool timetravel)
75 {
76 	if (timetravel)
77 		os_close_file(uml_rtc_irq_fds[1]);
78 	else
79 		ignore_sigio_fd(uml_rtc_irq_fds[0]);
80 	os_close_file(uml_rtc_irq_fds[0]);
81 }
82