xref: /kvmtool/hw/rtc.c (revision 4123ca555b1d466b08c630763a378b4d53de9ec5)
164136c1cSPekka Enberg #include "kvm/rtc.h"
264136c1cSPekka Enberg 
364136c1cSPekka Enberg #include "kvm/ioport.h"
464136c1cSPekka Enberg #include "kvm/kvm.h"
564136c1cSPekka Enberg 
664136c1cSPekka Enberg #include <time.h>
764136c1cSPekka Enberg 
8c8f6893bSPekka Enberg /*
9c8f6893bSPekka Enberg  * MC146818 RTC registers
10c8f6893bSPekka Enberg  */
11c8f6893bSPekka Enberg #define RTC_SECONDS			0x00
12c8f6893bSPekka Enberg #define RTC_SECONDS_ALARM		0x01
13c8f6893bSPekka Enberg #define RTC_MINUTES			0x02
14c8f6893bSPekka Enberg #define RTC_MINUTES_ALARM		0x03
15c8f6893bSPekka Enberg #define RTC_HOURS			0x04
16c8f6893bSPekka Enberg #define RTC_HOURS_ALARM			0x05
17c8f6893bSPekka Enberg #define RTC_DAY_OF_WEEK			0x06
18c8f6893bSPekka Enberg #define RTC_DAY_OF_MONTH		0x07
19c8f6893bSPekka Enberg #define RTC_MONTH			0x08
20c8f6893bSPekka Enberg #define RTC_YEAR			0x09
2197ed4838SPekka Enberg #define RTC_CENTURY			0x32
22c8f6893bSPekka Enberg 
23c8f6893bSPekka Enberg #define RTC_REG_A			0x0A
24c8f6893bSPekka Enberg #define RTC_REG_B			0x0B
25c8f6893bSPekka Enberg #define RTC_REG_C			0x0C
26c8f6893bSPekka Enberg #define RTC_REG_D			0x0D
2764136c1cSPekka Enberg 
282f062989SPekka Enberg struct rtc_device {
292f062989SPekka Enberg 	u8			cmos_idx;
30d951bae0SPekka Enberg 	u8			cmos_data[128];
312f062989SPekka Enberg };
322f062989SPekka Enberg 
332f062989SPekka Enberg static struct rtc_device	rtc;
342f062989SPekka Enberg 
3564136c1cSPekka Enberg static inline unsigned char bin2bcd(unsigned val)
3664136c1cSPekka Enberg {
3764136c1cSPekka Enberg 	return ((val / 10) << 4) + val % 10;
3864136c1cSPekka Enberg }
3964136c1cSPekka Enberg 
40*4123ca55SMarc Zyngier static bool cmos_ram_data_in(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size)
4164136c1cSPekka Enberg {
4264136c1cSPekka Enberg 	struct tm *tm;
4364136c1cSPekka Enberg 	time_t ti;
4464136c1cSPekka Enberg 
4564136c1cSPekka Enberg 	time(&ti);
4664136c1cSPekka Enberg 
4764136c1cSPekka Enberg 	tm = gmtime(&ti);
4864136c1cSPekka Enberg 
492f062989SPekka Enberg 	switch (rtc.cmos_idx) {
50c8f6893bSPekka Enberg 	case RTC_SECONDS:
5164136c1cSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_sec));
5264136c1cSPekka Enberg 		break;
53c8f6893bSPekka Enberg 	case RTC_MINUTES:
5464136c1cSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_min));
5564136c1cSPekka Enberg 		break;
56c8f6893bSPekka Enberg 	case RTC_HOURS:
5764136c1cSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_hour));
5864136c1cSPekka Enberg 		break;
59c717392fSPekka Enberg 	case RTC_DAY_OF_WEEK:
60c717392fSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_wday + 1));
61c717392fSPekka Enberg 		break;
62c8f6893bSPekka Enberg 	case RTC_DAY_OF_MONTH:
6364136c1cSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_mday));
6464136c1cSPekka Enberg 		break;
65c8f6893bSPekka Enberg 	case RTC_MONTH:
6664136c1cSPekka Enberg 		ioport__write8(data, bin2bcd(tm->tm_mon + 1));
6764136c1cSPekka Enberg 		break;
6897ed4838SPekka Enberg 	case RTC_YEAR: {
6997ed4838SPekka Enberg 		int year;
7097ed4838SPekka Enberg 
7197ed4838SPekka Enberg 		year = tm->tm_year + 1900;
7297ed4838SPekka Enberg 
7397ed4838SPekka Enberg 		ioport__write8(data, bin2bcd(year % 100));
7497ed4838SPekka Enberg 
7564136c1cSPekka Enberg 		break;
7697ed4838SPekka Enberg 	}
7797ed4838SPekka Enberg 	case RTC_CENTURY: {
7897ed4838SPekka Enberg 		int year;
7997ed4838SPekka Enberg 
8097ed4838SPekka Enberg 		year = tm->tm_year + 1900;
8197ed4838SPekka Enberg 
8297ed4838SPekka Enberg 		ioport__write8(data, bin2bcd(year / 100));
8397ed4838SPekka Enberg 
8497ed4838SPekka Enberg 		break;
8597ed4838SPekka Enberg 	}
86d951bae0SPekka Enberg 	default:
87d951bae0SPekka Enberg 		ioport__write8(data, rtc.cmos_data[rtc.cmos_idx]);
88d951bae0SPekka Enberg 		break;
8964136c1cSPekka Enberg 	}
9064136c1cSPekka Enberg 
9164136c1cSPekka Enberg 	return true;
9264136c1cSPekka Enberg }
9364136c1cSPekka Enberg 
94*4123ca55SMarc Zyngier static bool cmos_ram_data_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size)
9564136c1cSPekka Enberg {
96d951bae0SPekka Enberg 	switch (rtc.cmos_idx) {
97d951bae0SPekka Enberg 	case RTC_REG_C:
98d951bae0SPekka Enberg 	case RTC_REG_D:
99d951bae0SPekka Enberg 		/* Read-only */
100d951bae0SPekka Enberg 		break;
101d951bae0SPekka Enberg 	default:
102d951bae0SPekka Enberg 		rtc.cmos_data[rtc.cmos_idx] = ioport__read8(data);
103d951bae0SPekka Enberg 		break;
104d951bae0SPekka Enberg 	}
105d951bae0SPekka Enberg 
10664136c1cSPekka Enberg 	return true;
10764136c1cSPekka Enberg }
10864136c1cSPekka Enberg 
10964136c1cSPekka Enberg static struct ioport_operations cmos_ram_data_ioport_ops = {
11064136c1cSPekka Enberg 	.io_out		= cmos_ram_data_out,
11164136c1cSPekka Enberg 	.io_in		= cmos_ram_data_in,
11264136c1cSPekka Enberg };
11364136c1cSPekka Enberg 
114*4123ca55SMarc Zyngier static bool cmos_ram_index_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size)
11564136c1cSPekka Enberg {
1162f062989SPekka Enberg 	u8 value = ioport__read8(data);
11764136c1cSPekka Enberg 
118*4123ca55SMarc Zyngier 	vcpu->kvm->nmi_disabled	= value & (1UL << 7);
1192f062989SPekka Enberg 	rtc.cmos_idx		= value & ~(1UL << 7);
12064136c1cSPekka Enberg 
12164136c1cSPekka Enberg 	return true;
12264136c1cSPekka Enberg }
12364136c1cSPekka Enberg 
12464136c1cSPekka Enberg static struct ioport_operations cmos_ram_index_ioport_ops = {
12564136c1cSPekka Enberg 	.io_out		= cmos_ram_index_out,
12664136c1cSPekka Enberg };
12764136c1cSPekka Enberg 
12820c39545SSasha Levin int rtc__init(struct kvm *kvm)
12920c39545SSasha Levin {
13020c39545SSasha Levin 	int r = 0;
13120c39545SSasha Levin 
13220c39545SSasha Levin 	/* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */
1334346fd8fSSasha Levin 	r = ioport__register(kvm, 0x0070, &cmos_ram_index_ioport_ops, 1, NULL);
13420c39545SSasha Levin 	if (r < 0)
13520c39545SSasha Levin 		return r;
13620c39545SSasha Levin 
1374346fd8fSSasha Levin 	r = ioport__register(kvm, 0x0071, &cmos_ram_data_ioport_ops, 1, NULL);
13820c39545SSasha Levin 	if (r < 0) {
1394346fd8fSSasha Levin 		ioport__unregister(kvm, 0x0071);
14020c39545SSasha Levin 		return r;
14120c39545SSasha Levin 	}
14220c39545SSasha Levin 
14320c39545SSasha Levin 	return r;
14420c39545SSasha Levin }
14549a8afd1SSasha Levin dev_init(rtc__init);
14620c39545SSasha Levin 
14720c39545SSasha Levin int rtc__exit(struct kvm *kvm)
14864136c1cSPekka Enberg {
14964136c1cSPekka Enberg 	/* PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK) */
1504346fd8fSSasha Levin 	ioport__unregister(kvm, 0x0070);
1514346fd8fSSasha Levin 	ioport__unregister(kvm, 0x0071);
15220c39545SSasha Levin 
15320c39545SSasha Levin 	return 0;
15464136c1cSPekka Enberg }
15549a8afd1SSasha Levin dev_exit(rtc__exit);
156