xref: /kvmtool/hw/rtc.c (revision 5a3a5c07dd878d6f5dfb7ca49a257d9c245d8d05)
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 #define RTC_CENTURY			0x32
22 
23 #define RTC_REG_A			0x0A
24 #define RTC_REG_B			0x0B
25 #define RTC_REG_C			0x0C
26 #define RTC_REG_D			0x0D
27 
28 /*
29  * Register D Bits
30  */
31 #define RTC_REG_D_VRT			(1 << 7)
32 
33 struct rtc_device {
34 	u8			cmos_idx;
35 	u8			cmos_data[128];
36 };
37 
38 static struct rtc_device	rtc;
39 
40 static inline unsigned char bin2bcd(unsigned val)
41 {
42 	return ((val / 10) << 4) + val % 10;
43 }
44 
45 static bool cmos_ram_data_in(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size)
46 {
47 	struct tm *tm;
48 	time_t ti;
49 
50 	time(&ti);
51 
52 	tm = gmtime(&ti);
53 
54 	switch (rtc.cmos_idx) {
55 	case RTC_SECONDS:
56 		ioport__write8(data, bin2bcd(tm->tm_sec));
57 		break;
58 	case RTC_MINUTES:
59 		ioport__write8(data, bin2bcd(tm->tm_min));
60 		break;
61 	case RTC_HOURS:
62 		ioport__write8(data, bin2bcd(tm->tm_hour));
63 		break;
64 	case RTC_DAY_OF_WEEK:
65 		ioport__write8(data, bin2bcd(tm->tm_wday + 1));
66 		break;
67 	case RTC_DAY_OF_MONTH:
68 		ioport__write8(data, bin2bcd(tm->tm_mday));
69 		break;
70 	case RTC_MONTH:
71 		ioport__write8(data, bin2bcd(tm->tm_mon + 1));
72 		break;
73 	case RTC_YEAR: {
74 		int year;
75 
76 		year = tm->tm_year + 1900;
77 
78 		ioport__write8(data, bin2bcd(year % 100));
79 
80 		break;
81 	}
82 	case RTC_CENTURY: {
83 		int year;
84 
85 		year = tm->tm_year + 1900;
86 
87 		ioport__write8(data, bin2bcd(year / 100));
88 
89 		break;
90 	}
91 	default:
92 		ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]);
93 		break;
94 	}
95 
96 	return true;
97 }
98 
99 static bool cmos_ram_data_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size)
100 {
101 	switch (rtc.cmos_idx) {
102 	case RTC_REG_C:
103 	case RTC_REG_D:
104 		/* Read-only */
105 		break;
106 	default:
107 		rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data);
108 		break;
109 	}
110 
111 	return true;
112 }
113 
114 static struct ioport_operations cmos_ram_data_ioport_ops = {
115 	.io_out		= cmos_ram_data_out,
116 	.io_in		= cmos_ram_data_in,
117 };
118 
119 static bool cmos_ram_index_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size)
120 {
121 	u8 value = ioport__read8(data);
122 
123 	vcpu->kvm->nmi_disabled	= value & (1UL << 7);
124 	rtc.cmos_idx		= value & ~(1UL << 7);
125 
126 	return true;
127 }
128 
129 static struct ioport_operations cmos_ram_index_ioport_ops = {
130 	.io_out		= cmos_ram_index_out,
131 };
132 
133 #ifdef CONFIG_HAS_LIBFDT
134 static void generate_rtc_fdt_node(void *fdt,
135 				  struct device_header *dev_hdr,
136 				  void (*generate_irq_prop)(void *fdt,
137 							    u8 irq,
138 							    enum irq_type))
139 {
140 	u64 reg_prop[2] = { cpu_to_fdt64(0x70), cpu_to_fdt64(2) };
141 
142 	_FDT(fdt_begin_node(fdt, "rtc"));
143 	_FDT(fdt_property_string(fdt, "compatible", "motorola,mc146818"));
144 	_FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop)));
145 	_FDT(fdt_end_node(fdt));
146 }
147 #else
148 #define generate_rtc_fdt_node NULL
149 #endif
150 
151 struct device_header rtc_dev_hdr = {
152 	.bus_type = DEVICE_BUS_IOPORT,
153 	.data = generate_rtc_fdt_node,
154 };
155 
156 int rtc__init(struct kvm *kvm)
157 {
158 	int r;
159 
160 	r = device__register(&rtc_dev_hdr);
161 	if (r < 0)
162 		return r;
163 
164 	/* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */
165 	r = ioport__register(kvm, 0x0070, &cmos_ram_index_ioport_ops, 1, NULL);
166 	if (r < 0)
167 		goto out_device;
168 
169 	r = ioport__register(kvm, 0x0071, &cmos_ram_data_ioport_ops, 1, NULL);
170 	if (r < 0)
171 		goto out_ioport;
172 
173 	/* Set the VRT bit in Register D to indicate valid RAM and time */
174 	rtc.cmos_data[RTC_REG_D] = RTC_REG_D_VRT;
175 
176 	return r;
177 
178 out_ioport:
179 	ioport__unregister(kvm, 0x0070);
180 out_device:
181 	device__unregister(&rtc_dev_hdr);
182 
183 	return r;
184 }
185 dev_init(rtc__init);
186 
187 int rtc__exit(struct kvm *kvm)
188 {
189 	/* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */
190 	ioport__unregister(kvm, 0x0070);
191 	ioport__unregister(kvm, 0x0071);
192 
193 	return 0;
194 }
195 dev_exit(rtc__exit);
196