xref: /kvmtool/hw/rtc.c (revision fcb1d91662503bff8436bf69835034004863d546)
1 #include "kvm/rtc.h"
2 
3 #include "kvm/ioport.h"
4 #include "kvm/kvm.h"
5 
6 #include <time.h>
7 
8 /*
9  * MC146818 RTC registers
10  */
11 #define RTC_SECONDS			0x00
12 #define RTC_SECONDS_ALARM		0x01
13 #define RTC_MINUTES			0x02
14 #define RTC_MINUTES_ALARM		0x03
15 #define RTC_HOURS			0x04
16 #define RTC_HOURS_ALARM			0x05
17 #define RTC_DAY_OF_WEEK			0x06
18 #define RTC_DAY_OF_MONTH		0x07
19 #define RTC_MONTH			0x08
20 #define RTC_YEAR			0x09
21 
22 #define RTC_REG_A			0x0A
23 #define RTC_REG_B			0x0B
24 #define RTC_REG_C			0x0C
25 #define RTC_REG_D			0x0D
26 
27 struct rtc_device {
28 	u8			cmos_idx;
29 	u8			cmos_data[128];
30 };
31 
32 static struct rtc_device	rtc;
33 
34 static inline unsigned char bin2bcd(unsigned val)
35 {
36 	return ((val / 10) << 4) + val % 10;
37 }
38 
39 static bool cmos_ram_data_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
40 {
41 	struct tm *tm;
42 	time_t ti;
43 	int year;
44 
45 	time(&ti);
46 
47 	tm = gmtime(&ti);
48 
49 	switch (rtc.cmos_idx) {
50 	case RTC_SECONDS:
51 		ioport__write8(data, bin2bcd(tm->tm_sec));
52 		break;
53 	case RTC_MINUTES:
54 		ioport__write8(data, bin2bcd(tm->tm_min));
55 		break;
56 	case RTC_HOURS:
57 		ioport__write8(data, bin2bcd(tm->tm_hour));
58 		break;
59 	case RTC_DAY_OF_WEEK:
60 		ioport__write8(data, bin2bcd(tm->tm_wday + 1));
61 		break;
62 	case RTC_DAY_OF_MONTH:
63 		ioport__write8(data, bin2bcd(tm->tm_mday));
64 		break;
65 	case RTC_MONTH:
66 		ioport__write8(data, bin2bcd(tm->tm_mon + 1));
67 		break;
68 	case RTC_YEAR:
69 		/*
70 		 * The gmtime() function returns time since 1900. The CMOS
71 		 * standard is time since 2000. If the year is < 100, do
72 		 * nothing; if it is > 100, subtract 100; this is the best fit
73 		 * with the twisted CMOS logic.
74 		 */
75 		year = tm->tm_year;
76 		if (year > 99)
77 			year -= 100;
78 		ioport__write8(data, bin2bcd(year));
79 		break;
80 	default:
81 		ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]);
82 		break;
83 	}
84 
85 	return true;
86 }
87 
88 static bool cmos_ram_data_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
89 {
90 	switch (rtc.cmos_idx) {
91 	case RTC_REG_C:
92 	case RTC_REG_D:
93 		/* Read-only */
94 		break;
95 	default:
96 		rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data);
97 		break;
98 	}
99 
100 	return true;
101 }
102 
103 static struct ioport_operations cmos_ram_data_ioport_ops = {
104 	.io_out		= cmos_ram_data_out,
105 	.io_in		= cmos_ram_data_in,
106 };
107 
108 static bool cmos_ram_index_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
109 {
110 	u8 value = ioport__read8(data);
111 
112 	kvm->nmi_disabled	= value & (1UL << 7);
113 	rtc.cmos_idx		= value & ~(1UL << 7);
114 
115 	return true;
116 }
117 
118 static struct ioport_operations cmos_ram_index_ioport_ops = {
119 	.io_out		= cmos_ram_index_out,
120 };
121 
122 int rtc__init(struct kvm *kvm)
123 {
124 	int r = 0;
125 
126 	/* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */
127 	r = ioport__register(kvm, 0x0070, &cmos_ram_index_ioport_ops, 1, NULL);
128 	if (r < 0)
129 		return r;
130 
131 	r = ioport__register(kvm, 0x0071, &cmos_ram_data_ioport_ops, 1, NULL);
132 	if (r < 0) {
133 		ioport__unregister(kvm, 0x0071);
134 		return r;
135 	}
136 
137 	return r;
138 }
139 dev_init(rtc__init);
140 
141 int rtc__exit(struct kvm *kvm)
142 {
143 	/* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */
144 	ioport__unregister(kvm, 0x0070);
145 	ioport__unregister(kvm, 0x0071);
146 
147 	return 0;
148 }
149 dev_exit(rtc__exit);
150