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