ia64/xen-unstable

view tools/ioemu/hw/mc146818rtc.c @ 10078:345464c2fd47

Allow to specify different time-of-day clock offsets for HVM guests.

There are some usage scenarios in which differing user domains want
to be using different base TOD clocks. This patch adds the ability
to specify the base TOD time difference. The patch also adds a
hook point to notify another entity when the domain changes this
offset. This might occur, for instance, on a Linux domain using
hwclock -w.

Signed-off-by: Ben Thomas <ben@virtualiron.com>
author kaf24@firebug.cl.cam.ac.uk
date Wed May 17 23:43:32 2006 +0100 (2006-05-17)
parents 8e5fc5fe636c
children
line source
1 /*
2 * QEMU MC146818 RTC emulation
3 *
4 * Copyright (c) 2003-2004 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 #include "vl.h"
26 //#define DEBUG_CMOS
28 #define RTC_SECONDS 0
29 #define RTC_SECONDS_ALARM 1
30 #define RTC_MINUTES 2
31 #define RTC_MINUTES_ALARM 3
32 #define RTC_HOURS 4
33 #define RTC_HOURS_ALARM 5
34 #define RTC_ALARM_DONT_CARE 0xC0
36 #define RTC_DAY_OF_WEEK 6
37 #define RTC_DAY_OF_MONTH 7
38 #define RTC_MONTH 8
39 #define RTC_YEAR 9
41 #define RTC_REG_A 10
42 #define RTC_REG_B 11
43 #define RTC_REG_C 12
44 #define RTC_REG_D 13
46 #define REG_A_UIP 0x80
48 #define REG_B_SET 0x80
49 #define REG_B_PIE 0x40
50 #define REG_B_AIE 0x20
51 #define REG_B_UIE 0x10
53 struct RTCState {
54 uint8_t cmos_data[128];
55 uint8_t cmos_index;
56 struct tm current_tm;
57 int irq;
58 /* periodic timer */
59 QEMUTimer *periodic_timer;
60 int64_t next_periodic_time;
61 /* second update */
62 int64_t next_second_time;
63 QEMUTimer *second_timer;
64 QEMUTimer *second_timer2;
65 };
67 static void rtc_set_time(RTCState *s);
68 static void rtc_copy_date(RTCState *s);
70 static void rtc_timer_update(RTCState *s, int64_t current_time)
71 {
72 int period_code, period;
73 int64_t cur_clock, next_irq_clock;
75 period_code = s->cmos_data[RTC_REG_A] & 0x0f;
76 if (period_code != 0 &&
77 (s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
78 if (period_code <= 2)
79 period_code += 7;
80 /* period in 32 Khz cycles */
81 period = 1 << (period_code - 1);
82 /* compute 32 khz clock */
83 cur_clock = muldiv64(current_time, 32768, ticks_per_sec);
84 next_irq_clock = (cur_clock & ~(period - 1)) + period;
85 s->next_periodic_time = muldiv64(next_irq_clock, ticks_per_sec, 32768) + 1;
86 qemu_mod_timer(s->periodic_timer, s->next_periodic_time);
87 } else {
88 qemu_del_timer(s->periodic_timer);
89 }
90 }
92 static void rtc_periodic_timer(void *opaque)
93 {
94 RTCState *s = opaque;
96 rtc_timer_update(s, s->next_periodic_time);
97 s->cmos_data[RTC_REG_C] |= 0xc0;
98 pic_set_irq(s->irq, 1);
99 }
101 static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
102 {
103 RTCState *s = opaque;
105 if ((addr & 1) == 0) {
106 s->cmos_index = data & 0x7f;
107 } else {
108 #ifdef DEBUG_CMOS
109 printf("cmos: write index=0x%02x val=0x%02x\n",
110 s->cmos_index, data);
111 #endif
112 switch(s->cmos_index) {
113 case RTC_SECONDS_ALARM:
114 case RTC_MINUTES_ALARM:
115 case RTC_HOURS_ALARM:
116 /* XXX: not supported */
117 s->cmos_data[s->cmos_index] = data;
118 break;
119 case RTC_SECONDS:
120 case RTC_MINUTES:
121 case RTC_HOURS:
122 case RTC_DAY_OF_WEEK:
123 case RTC_DAY_OF_MONTH:
124 case RTC_MONTH:
125 case RTC_YEAR:
126 s->cmos_data[s->cmos_index] = data;
127 /* if in set mode, do not update the time */
128 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
129 rtc_set_time(s);
130 }
131 break;
132 case RTC_REG_A:
133 /* UIP bit is read only */
134 s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
135 (s->cmos_data[RTC_REG_A] & REG_A_UIP);
136 rtc_timer_update(s, qemu_get_clock(vm_clock));
137 break;
138 case RTC_REG_B:
139 if (data & REG_B_SET) {
140 /* set mode: reset UIP mode */
141 s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
142 data &= ~REG_B_UIE;
143 } else {
144 /* if disabling set mode, update the time */
145 if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
146 rtc_set_time(s);
147 }
148 }
149 s->cmos_data[RTC_REG_B] = data;
150 rtc_timer_update(s, qemu_get_clock(vm_clock));
151 break;
152 case RTC_REG_C:
153 case RTC_REG_D:
154 /* cannot write to them */
155 break;
156 default:
157 s->cmos_data[s->cmos_index] = data;
158 break;
159 }
160 }
161 }
163 static inline int to_bcd(RTCState *s, int a)
164 {
165 if (s->cmos_data[RTC_REG_B] & 0x04) {
166 return a;
167 } else {
168 return ((a / 10) << 4) | (a % 10);
169 }
170 }
172 static inline int from_bcd(RTCState *s, int a)
173 {
174 if (s->cmos_data[RTC_REG_B] & 0x04) {
175 return a;
176 } else {
177 return ((a >> 4) * 10) + (a & 0x0f);
178 }
179 }
181 static void send_timeoffset_msg(time_t delta)
182 {
184 /* This routine is used to inform another entity that the
185 base time offset has changed. For instance, if you
186 were using xenstore, you might want to write to the store
187 at this point. Or, you might use some other method.
188 Whatever you might choose, here's a hook point to implement it.
190 One item of note is that this delta is in addition to
191 any existing offset you might be already using. */
193 return;
194 }
196 static void rtc_set_time(RTCState *s)
197 {
198 struct tm *tm = &s->current_tm;
199 time_t before, after;
201 before = mktime(tm);
202 tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
203 tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
204 tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
205 if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
206 (s->cmos_data[RTC_HOURS] & 0x80)) {
207 tm->tm_hour += 12;
208 }
209 tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
210 tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
211 tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
212 tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
214 /* Compute, and send, the additional time delta
215 We could compute the total time delta, but this is
216 sufficient, and simple. */
217 after = mktime(tm);
218 send_timeoffset_msg(after-before);
219 }
221 static void rtc_copy_date(RTCState *s)
222 {
223 const struct tm *tm = &s->current_tm;
225 s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
226 s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
227 if (s->cmos_data[RTC_REG_B] & 0x02) {
228 /* 24 hour format */
229 s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
230 } else {
231 /* 12 hour format */
232 s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
233 if (tm->tm_hour >= 12)
234 s->cmos_data[RTC_HOURS] |= 0x80;
235 }
236 s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
237 s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
238 s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
239 s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
240 }
242 /* month is between 0 and 11. */
243 static int get_days_in_month(int month, int year)
244 {
245 static const int days_tab[12] = {
246 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
247 };
248 int d;
249 if ((unsigned )month >= 12)
250 return 31;
251 d = days_tab[month];
252 if (month == 1) {
253 if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
254 d++;
255 }
256 return d;
257 }
259 /* update 'tm' to the next second */
260 static void rtc_next_second(struct tm *tm)
261 {
262 int days_in_month;
264 tm->tm_sec++;
265 if ((unsigned)tm->tm_sec >= 60) {
266 tm->tm_sec = 0;
267 tm->tm_min++;
268 if ((unsigned)tm->tm_min >= 60) {
269 tm->tm_min = 0;
270 tm->tm_hour++;
271 if ((unsigned)tm->tm_hour >= 24) {
272 tm->tm_hour = 0;
273 /* next day */
274 tm->tm_wday++;
275 if ((unsigned)tm->tm_wday >= 7)
276 tm->tm_wday = 0;
277 days_in_month = get_days_in_month(tm->tm_mon,
278 tm->tm_year + 1900);
279 tm->tm_mday++;
280 if (tm->tm_mday < 1) {
281 tm->tm_mday = 1;
282 } else if (tm->tm_mday > days_in_month) {
283 tm->tm_mday = 1;
284 tm->tm_mon++;
285 if (tm->tm_mon >= 12) {
286 tm->tm_mon = 0;
287 tm->tm_year++;
288 }
289 }
290 }
291 }
292 }
293 }
296 static void rtc_update_second(void *opaque)
297 {
298 RTCState *s = opaque;
299 int64_t delay;
301 /* if the oscillator is not in normal operation, we do not update */
302 if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
303 s->next_second_time += ticks_per_sec;
304 qemu_mod_timer(s->second_timer, s->next_second_time);
305 } else {
306 rtc_next_second(&s->current_tm);
308 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
309 /* update in progress bit */
310 s->cmos_data[RTC_REG_A] |= REG_A_UIP;
311 }
312 /* should be 244 us = 8 / 32768 seconds, but currently the
313 timers do not have the necessary resolution. */
314 delay = (ticks_per_sec * 1) / 100;
315 if (delay < 1)
316 delay = 1;
317 qemu_mod_timer(s->second_timer2,
318 s->next_second_time + delay);
319 }
320 }
322 static void rtc_update_second2(void *opaque)
323 {
324 RTCState *s = opaque;
326 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
327 rtc_copy_date(s);
328 }
330 /* check alarm */
331 if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
332 if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
333 s->cmos_data[RTC_SECONDS_ALARM] == s->current_tm.tm_sec) &&
334 ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
335 s->cmos_data[RTC_MINUTES_ALARM] == s->current_tm.tm_mon) &&
336 ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
337 s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) {
339 s->cmos_data[RTC_REG_C] |= 0xa0;
340 pic_set_irq(s->irq, 1);
341 }
342 }
344 /* update ended interrupt */
345 if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
346 s->cmos_data[RTC_REG_C] |= 0x90;
347 pic_set_irq(s->irq, 1);
348 }
350 /* clear update in progress bit */
351 s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
353 s->next_second_time += ticks_per_sec;
354 qemu_mod_timer(s->second_timer, s->next_second_time);
355 }
357 static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
358 {
359 RTCState *s = opaque;
360 int ret;
361 if ((addr & 1) == 0) {
362 return 0xff;
363 } else {
364 switch(s->cmos_index) {
365 case RTC_SECONDS:
366 case RTC_MINUTES:
367 case RTC_HOURS:
368 case RTC_DAY_OF_WEEK:
369 case RTC_DAY_OF_MONTH:
370 case RTC_MONTH:
371 case RTC_YEAR:
372 ret = s->cmos_data[s->cmos_index];
373 break;
374 case RTC_REG_A:
375 ret = s->cmos_data[s->cmos_index];
376 break;
377 case RTC_REG_C:
378 ret = s->cmos_data[s->cmos_index];
379 pic_set_irq(s->irq, 0);
380 s->cmos_data[RTC_REG_C] = 0x00;
381 break;
382 default:
383 ret = s->cmos_data[s->cmos_index];
384 break;
385 }
386 #ifdef DEBUG_CMOS
387 printf("cmos: read index=0x%02x val=0x%02x\n",
388 s->cmos_index, ret);
389 #endif
390 return ret;
391 }
392 }
394 void rtc_set_memory(RTCState *s, int addr, int val)
395 {
396 if (addr >= 0 && addr <= 127)
397 s->cmos_data[addr] = val;
398 }
400 void rtc_set_date(RTCState *s, const struct tm *tm)
401 {
402 s->current_tm = *tm;
403 rtc_copy_date(s);
404 }
406 static void rtc_save(QEMUFile *f, void *opaque)
407 {
408 RTCState *s = opaque;
410 qemu_put_buffer(f, s->cmos_data, 128);
411 qemu_put_8s(f, &s->cmos_index);
413 qemu_put_be32s(f, &s->current_tm.tm_sec);
414 qemu_put_be32s(f, &s->current_tm.tm_min);
415 qemu_put_be32s(f, &s->current_tm.tm_hour);
416 qemu_put_be32s(f, &s->current_tm.tm_wday);
417 qemu_put_be32s(f, &s->current_tm.tm_mday);
418 qemu_put_be32s(f, &s->current_tm.tm_mon);
419 qemu_put_be32s(f, &s->current_tm.tm_year);
421 qemu_put_timer(f, s->periodic_timer);
422 qemu_put_be64s(f, &s->next_periodic_time);
424 qemu_put_be64s(f, &s->next_second_time);
425 qemu_put_timer(f, s->second_timer);
426 qemu_put_timer(f, s->second_timer2);
427 }
429 static int rtc_load(QEMUFile *f, void *opaque, int version_id)
430 {
431 RTCState *s = opaque;
433 if (version_id != 1)
434 return -EINVAL;
436 qemu_get_buffer(f, s->cmos_data, 128);
437 qemu_get_8s(f, &s->cmos_index);
439 qemu_get_be32s(f, &s->current_tm.tm_sec);
440 qemu_get_be32s(f, &s->current_tm.tm_min);
441 qemu_get_be32s(f, &s->current_tm.tm_hour);
442 qemu_get_be32s(f, &s->current_tm.tm_wday);
443 qemu_get_be32s(f, &s->current_tm.tm_mday);
444 qemu_get_be32s(f, &s->current_tm.tm_mon);
445 qemu_get_be32s(f, &s->current_tm.tm_year);
447 qemu_get_timer(f, s->periodic_timer);
448 qemu_get_be64s(f, &s->next_periodic_time);
450 qemu_get_be64s(f, &s->next_second_time);
451 qemu_get_timer(f, s->second_timer);
452 qemu_get_timer(f, s->second_timer2);
453 return 0;
454 }
456 RTCState *rtc_init(int base, int irq)
457 {
458 RTCState *s;
460 s = qemu_mallocz(sizeof(RTCState));
461 if (!s)
462 return NULL;
464 s->irq = irq;
465 s->cmos_data[RTC_REG_A] = 0x26;
466 s->cmos_data[RTC_REG_B] = 0x02;
467 s->cmos_data[RTC_REG_C] = 0x00;
468 s->cmos_data[RTC_REG_D] = 0x80;
470 s->periodic_timer = qemu_new_timer(vm_clock,
471 rtc_periodic_timer, s);
472 s->second_timer = qemu_new_timer(vm_clock,
473 rtc_update_second, s);
474 s->second_timer2 = qemu_new_timer(vm_clock,
475 rtc_update_second2, s);
477 s->next_second_time = qemu_get_clock(vm_clock) + (ticks_per_sec * 99) / 100;
478 qemu_mod_timer(s->second_timer2, s->next_second_time);
480 register_ioport_write(base, 2, 1, cmos_ioport_write, s);
481 register_ioport_read(base, 2, 1, cmos_ioport_read, s);
483 register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
484 return s;
485 }