xref: /kvmtool/hw/rtc.c (revision 809f088b1d9fc4223c19a2f500e33adee5739ccc)
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 
44 	time(&ti);
45 
46 	tm = gmtime(&ti);
47 
48 	switch (rtc.cmos_idx) {
49 	case RTC_SECONDS:
50 		ioport__write8(data, bin2bcd(tm->tm_sec));
51 		break;
52 	case RTC_MINUTES:
53 		ioport__write8(data, bin2bcd(tm->tm_min));
54 		break;
55 	case RTC_HOURS:
56 		ioport__write8(data, bin2bcd(tm->tm_hour));
57 		break;
58 	case RTC_DAY_OF_WEEK:
59 		ioport__write8(data, bin2bcd(tm->tm_wday + 1));
60 		break;
61 	case RTC_DAY_OF_MONTH:
62 		ioport__write8(data, bin2bcd(tm->tm_mday));
63 		break;
64 	case RTC_MONTH:
65 		ioport__write8(data, bin2bcd(tm->tm_mon + 1));
66 		break;
67 	case RTC_YEAR:
68 		ioport__write8(data, bin2bcd(tm->tm_year));
69 		break;
70 	default:
71 		ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]);
72 		break;
73 	}
74 
75 	return true;
76 }
77 
78 static bool cmos_ram_data_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
79 {
80 	switch (rtc.cmos_idx) {
81 	case RTC_REG_C:
82 	case RTC_REG_D:
83 		/* Read-only */
84 		break;
85 	default:
86 		rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data);
87 		break;
88 	}
89 
90 	return true;
91 }
92 
93 static struct ioport_operations cmos_ram_data_ioport_ops = {
94 	.io_out		= cmos_ram_data_out,
95 	.io_in		= cmos_ram_data_in,
96 };
97 
98 static bool cmos_ram_index_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
99 {
100 	u8 value = ioport__read8(data);
101 
102 	kvm->nmi_disabled	= value & (1UL << 7);
103 	rtc.cmos_idx		= value & ~(1UL << 7);
104 
105 	return true;
106 }
107 
108 static struct ioport_operations cmos_ram_index_ioport_ops = {
109 	.io_out		= cmos_ram_index_out,
110 };
111 
112 int rtc__init(struct kvm *kvm)
113 {
114 	int r = 0;
115 
116 	/* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */
117 	r = ioport__register(0x0070, &cmos_ram_index_ioport_ops, 1, NULL);
118 	if (r < 0)
119 		return r;
120 
121 	r = ioport__register(0x0071, &cmos_ram_data_ioport_ops, 1, NULL);
122 	if (r < 0) {
123 		ioport__unregister(0x0071);
124 		return r;
125 	}
126 
127 	return r;
128 }
129 
130 int rtc__exit(struct kvm *kvm)
131 {
132 	/* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */
133 	ioport__unregister(0x0070);
134 	ioport__unregister(0x0071);
135 
136 	return 0;
137 }
138