1276428Sneel/*- 2336190Saraujo * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3336190Saraujo * 4276428Sneel * Copyright (c) 2014, Neel Natu (neel@freebsd.org) 5276428Sneel * All rights reserved. 6276428Sneel * 7276428Sneel * Redistribution and use in source and binary forms, with or without 8276428Sneel * modification, are permitted provided that the following conditions 9276428Sneel * are met: 10276428Sneel * 1. Redistributions of source code must retain the above copyright 11276428Sneel * notice unmodified, this list of conditions, and the following 12276428Sneel * disclaimer. 13276428Sneel * 2. Redistributions in binary form must reproduce the above copyright 14276428Sneel * notice, this list of conditions and the following disclaimer in the 15276428Sneel * documentation and/or other materials provided with the distribution. 16276428Sneel * 17276428Sneel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18276428Sneel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19276428Sneel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20276428Sneel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21276428Sneel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22276428Sneel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23276428Sneel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24276428Sneel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25276428Sneel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26276428Sneel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27276428Sneel */ 28276428Sneel 29276428Sneel#include <sys/cdefs.h> 30276428Sneel__FBSDID("$FreeBSD: stable/11/sys/amd64/vmm/io/vrtc.c 336190 2018-07-11 07:19:42Z araujo $"); 31276428Sneel 32276428Sneel#include <sys/param.h> 33276428Sneel#include <sys/systm.h> 34276428Sneel#include <sys/queue.h> 35276428Sneel#include <sys/kernel.h> 36276428Sneel#include <sys/malloc.h> 37276428Sneel#include <sys/lock.h> 38276428Sneel#include <sys/mutex.h> 39276428Sneel#include <sys/clock.h> 40276428Sneel#include <sys/sysctl.h> 41276428Sneel 42276428Sneel#include <machine/vmm.h> 43276428Sneel 44276428Sneel#include <isa/rtc.h> 45276428Sneel 46276428Sneel#include "vmm_ktr.h" 47276428Sneel#include "vatpic.h" 48276428Sneel#include "vioapic.h" 49276428Sneel#include "vrtc.h" 50276428Sneel 51276428Sneel/* Register layout of the RTC */ 52276428Sneelstruct rtcdev { 53276428Sneel uint8_t sec; 54276428Sneel uint8_t alarm_sec; 55276428Sneel uint8_t min; 56276428Sneel uint8_t alarm_min; 57276428Sneel uint8_t hour; 58276428Sneel uint8_t alarm_hour; 59276428Sneel uint8_t day_of_week; 60276428Sneel uint8_t day_of_month; 61276428Sneel uint8_t month; 62276428Sneel uint8_t year; 63276428Sneel uint8_t reg_a; 64276428Sneel uint8_t reg_b; 65276428Sneel uint8_t reg_c; 66276428Sneel uint8_t reg_d; 67282206Sneel uint8_t nvram[36]; 68282206Sneel uint8_t century; 69282206Sneel uint8_t nvram2[128 - 51]; 70276428Sneel} __packed; 71276428SneelCTASSERT(sizeof(struct rtcdev) == 128); 72282206SneelCTASSERT(offsetof(struct rtcdev, century) == RTC_CENTURY); 73276428Sneel 74276428Sneelstruct vrtc { 75276428Sneel struct vm *vm; 76276428Sneel struct mtx mtx; 77276428Sneel struct callout callout; 78276428Sneel u_int addr; /* RTC register to read or write */ 79276428Sneel sbintime_t base_uptime; 80276428Sneel time_t base_rtctime; 81276428Sneel struct rtcdev rtcdev; 82276428Sneel}; 83276428Sneel 84276428Sneel#define VRTC_LOCK(vrtc) mtx_lock(&((vrtc)->mtx)) 85276428Sneel#define VRTC_UNLOCK(vrtc) mtx_unlock(&((vrtc)->mtx)) 86276428Sneel#define VRTC_LOCKED(vrtc) mtx_owned(&((vrtc)->mtx)) 87276428Sneel 88276428Sneel/* 89276428Sneel * RTC time is considered "broken" if: 90276428Sneel * - RTC updates are halted by the guest 91276428Sneel * - RTC date/time fields have invalid values 92276428Sneel */ 93276428Sneel#define VRTC_BROKEN_TIME ((time_t)-1) 94276428Sneel 95276428Sneel#define RTC_IRQ 8 96276428Sneel#define RTCSB_BIN 0x04 97276428Sneel#define RTCSB_ALL_INTRS (RTCSB_UINTR | RTCSB_AINTR | RTCSB_PINTR) 98276428Sneel#define rtc_halted(vrtc) ((vrtc->rtcdev.reg_b & RTCSB_HALT) != 0) 99276428Sneel#define aintr_enabled(vrtc) (((vrtc)->rtcdev.reg_b & RTCSB_AINTR) != 0) 100276428Sneel#define pintr_enabled(vrtc) (((vrtc)->rtcdev.reg_b & RTCSB_PINTR) != 0) 101276428Sneel#define uintr_enabled(vrtc) (((vrtc)->rtcdev.reg_b & RTCSB_UINTR) != 0) 102276428Sneel 103276428Sneelstatic void vrtc_callout_handler(void *arg); 104276428Sneelstatic void vrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval); 105276428Sneel 106276428Sneelstatic MALLOC_DEFINE(M_VRTC, "vrtc", "bhyve virtual rtc"); 107276428Sneel 108276428SneelSYSCTL_DECL(_hw_vmm); 109276428SneelSYSCTL_NODE(_hw_vmm, OID_AUTO, vrtc, CTLFLAG_RW, NULL, NULL); 110276428Sneel 111276428Sneelstatic int rtc_flag_broken_time = 1; 112276428SneelSYSCTL_INT(_hw_vmm_vrtc, OID_AUTO, flag_broken_time, CTLFLAG_RDTUN, 113276428Sneel &rtc_flag_broken_time, 0, "Stop guest when invalid RTC time is detected"); 114276428Sneel 115276428Sneelstatic __inline bool 116276428Sneeldivider_enabled(int reg_a) 117276428Sneel{ 118276428Sneel /* 119276428Sneel * The RTC is counting only when dividers are not held in reset. 120276428Sneel */ 121276428Sneel return ((reg_a & 0x70) == 0x20); 122276428Sneel} 123276428Sneel 124276428Sneelstatic __inline bool 125276428Sneelupdate_enabled(struct vrtc *vrtc) 126276428Sneel{ 127276428Sneel /* 128276428Sneel * RTC date/time can be updated only if: 129276428Sneel * - divider is not held in reset 130276428Sneel * - guest has not disabled updates 131276428Sneel * - the date/time fields have valid contents 132276428Sneel */ 133276428Sneel if (!divider_enabled(vrtc->rtcdev.reg_a)) 134276428Sneel return (false); 135276428Sneel 136276428Sneel if (rtc_halted(vrtc)) 137276428Sneel return (false); 138276428Sneel 139276428Sneel if (vrtc->base_rtctime == VRTC_BROKEN_TIME) 140276428Sneel return (false); 141276428Sneel 142276428Sneel return (true); 143276428Sneel} 144276428Sneel 145276428Sneelstatic time_t 146282259Sneelvrtc_curtime(struct vrtc *vrtc, sbintime_t *basetime) 147276428Sneel{ 148276428Sneel sbintime_t now, delta; 149282259Sneel time_t t, secs; 150276428Sneel 151276428Sneel KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 152276428Sneel 153276428Sneel t = vrtc->base_rtctime; 154282259Sneel *basetime = vrtc->base_uptime; 155276428Sneel if (update_enabled(vrtc)) { 156276428Sneel now = sbinuptime(); 157276428Sneel delta = now - vrtc->base_uptime; 158276428Sneel KASSERT(delta >= 0, ("vrtc_curtime: uptime went backwards: " 159276428Sneel "%#lx to %#lx", vrtc->base_uptime, now)); 160282259Sneel secs = delta / SBT_1S; 161282259Sneel t += secs; 162282259Sneel *basetime += secs * SBT_1S; 163276428Sneel } 164276428Sneel return (t); 165276428Sneel} 166276428Sneel 167276428Sneelstatic __inline uint8_t 168276428Sneelrtcset(struct rtcdev *rtc, int val) 169276428Sneel{ 170276428Sneel 171276428Sneel KASSERT(val >= 0 && val < 100, ("%s: invalid bin2bcd index %d", 172276428Sneel __func__, val)); 173276428Sneel 174276428Sneel return ((rtc->reg_b & RTCSB_BIN) ? val : bin2bcd_data[val]); 175276428Sneel} 176276428Sneel 177276428Sneelstatic void 178276428Sneelsecs_to_rtc(time_t rtctime, struct vrtc *vrtc, int force_update) 179276428Sneel{ 180276428Sneel struct clocktime ct; 181276428Sneel struct timespec ts; 182276428Sneel struct rtcdev *rtc; 183276428Sneel int hour; 184276428Sneel 185276428Sneel KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 186276428Sneel 187276428Sneel if (rtctime < 0) { 188276428Sneel KASSERT(rtctime == VRTC_BROKEN_TIME, 189276428Sneel ("%s: invalid vrtc time %#lx", __func__, rtctime)); 190276428Sneel return; 191276428Sneel } 192276428Sneel 193276428Sneel /* 194276428Sneel * If the RTC is halted then the guest has "ownership" of the 195276428Sneel * date/time fields. Don't update the RTC date/time fields in 196276428Sneel * this case (unless forced). 197276428Sneel */ 198276428Sneel if (rtc_halted(vrtc) && !force_update) 199276428Sneel return; 200276428Sneel 201276428Sneel ts.tv_sec = rtctime; 202276428Sneel ts.tv_nsec = 0; 203276428Sneel clock_ts_to_ct(&ts, &ct); 204276428Sneel 205276428Sneel KASSERT(ct.sec >= 0 && ct.sec <= 59, ("invalid clocktime sec %d", 206276428Sneel ct.sec)); 207276428Sneel KASSERT(ct.min >= 0 && ct.min <= 59, ("invalid clocktime min %d", 208276428Sneel ct.min)); 209276428Sneel KASSERT(ct.hour >= 0 && ct.hour <= 23, ("invalid clocktime hour %d", 210276428Sneel ct.hour)); 211276428Sneel KASSERT(ct.dow >= 0 && ct.dow <= 6, ("invalid clocktime wday %d", 212276428Sneel ct.dow)); 213276428Sneel KASSERT(ct.day >= 1 && ct.day <= 31, ("invalid clocktime mday %d", 214276428Sneel ct.day)); 215276428Sneel KASSERT(ct.mon >= 1 && ct.mon <= 12, ("invalid clocktime month %d", 216276428Sneel ct.mon)); 217276428Sneel KASSERT(ct.year >= POSIX_BASE_YEAR, ("invalid clocktime year %d", 218276428Sneel ct.year)); 219276428Sneel 220276428Sneel rtc = &vrtc->rtcdev; 221276428Sneel rtc->sec = rtcset(rtc, ct.sec); 222276428Sneel rtc->min = rtcset(rtc, ct.min); 223276428Sneel 224280775Sneel if (rtc->reg_b & RTCSB_24HR) { 225280775Sneel hour = ct.hour; 226280775Sneel } else { 227280775Sneel /* 228280775Sneel * Convert to the 12-hour format. 229280775Sneel */ 230280775Sneel switch (ct.hour) { 231280775Sneel case 0: /* 12 AM */ 232280775Sneel case 12: /* 12 PM */ 233280775Sneel hour = 12; 234280775Sneel break; 235280775Sneel default: 236280775Sneel /* 237280775Sneel * The remaining 'ct.hour' values are interpreted as: 238280775Sneel * [1 - 11] -> 1 - 11 AM 239280775Sneel * [13 - 23] -> 1 - 11 PM 240280775Sneel */ 241280775Sneel hour = ct.hour % 12; 242280775Sneel break; 243280775Sneel } 244280775Sneel } 245276428Sneel 246276428Sneel rtc->hour = rtcset(rtc, hour); 247276428Sneel 248276428Sneel if ((rtc->reg_b & RTCSB_24HR) == 0 && ct.hour >= 12) 249276428Sneel rtc->hour |= 0x80; /* set MSB to indicate PM */ 250276428Sneel 251276428Sneel rtc->day_of_week = rtcset(rtc, ct.dow + 1); 252276428Sneel rtc->day_of_month = rtcset(rtc, ct.day); 253276428Sneel rtc->month = rtcset(rtc, ct.mon); 254276428Sneel rtc->year = rtcset(rtc, ct.year % 100); 255282206Sneel rtc->century = rtcset(rtc, ct.year / 100); 256276428Sneel} 257276428Sneel 258276428Sneelstatic int 259276428Sneelrtcget(struct rtcdev *rtc, int val, int *retval) 260276428Sneel{ 261276428Sneel uint8_t upper, lower; 262276428Sneel 263276428Sneel if (rtc->reg_b & RTCSB_BIN) { 264276428Sneel *retval = val; 265276428Sneel return (0); 266276428Sneel } 267276428Sneel 268276428Sneel lower = val & 0xf; 269276428Sneel upper = (val >> 4) & 0xf; 270276428Sneel 271276428Sneel if (lower > 9 || upper > 9) 272276428Sneel return (-1); 273276428Sneel 274276428Sneel *retval = upper * 10 + lower; 275276428Sneel return (0); 276276428Sneel} 277276428Sneel 278276428Sneelstatic time_t 279276428Sneelrtc_to_secs(struct vrtc *vrtc) 280276428Sneel{ 281276428Sneel struct clocktime ct; 282276428Sneel struct timespec ts; 283276428Sneel struct rtcdev *rtc; 284276428Sneel struct vm *vm; 285282206Sneel int century, error, hour, pm, year; 286276428Sneel 287276428Sneel KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 288276428Sneel 289276428Sneel vm = vrtc->vm; 290276428Sneel rtc = &vrtc->rtcdev; 291276428Sneel 292276428Sneel bzero(&ct, sizeof(struct clocktime)); 293276428Sneel 294276428Sneel error = rtcget(rtc, rtc->sec, &ct.sec); 295276428Sneel if (error || ct.sec < 0 || ct.sec > 59) { 296276428Sneel VM_CTR2(vm, "Invalid RTC sec %#x/%d", rtc->sec, ct.sec); 297276428Sneel goto fail; 298276428Sneel } 299276428Sneel 300276428Sneel error = rtcget(rtc, rtc->min, &ct.min); 301276428Sneel if (error || ct.min < 0 || ct.min > 59) { 302276428Sneel VM_CTR2(vm, "Invalid RTC min %#x/%d", rtc->min, ct.min); 303276428Sneel goto fail; 304276428Sneel } 305276428Sneel 306276428Sneel pm = 0; 307276428Sneel hour = rtc->hour; 308276428Sneel if ((rtc->reg_b & RTCSB_24HR) == 0) { 309276428Sneel if (hour & 0x80) { 310276428Sneel hour &= ~0x80; 311276428Sneel pm = 1; 312276428Sneel } 313276428Sneel } 314276428Sneel error = rtcget(rtc, hour, &ct.hour); 315276428Sneel if ((rtc->reg_b & RTCSB_24HR) == 0) { 316280775Sneel if (ct.hour >= 1 && ct.hour <= 12) { 317280775Sneel /* 318280775Sneel * Convert from 12-hour format to internal 24-hour 319280775Sneel * representation as follows: 320280775Sneel * 321280775Sneel * 12-hour format ct.hour 322280775Sneel * 12 AM 0 323280775Sneel * 1 - 11 AM 1 - 11 324280775Sneel * 12 PM 12 325280775Sneel * 1 - 11 PM 13 - 23 326280775Sneel */ 327280775Sneel if (ct.hour == 12) 328280775Sneel ct.hour = 0; 329280775Sneel if (pm) 330280775Sneel ct.hour += 12; 331280775Sneel } else { 332280775Sneel VM_CTR2(vm, "Invalid RTC 12-hour format %#x/%d", 333280775Sneel rtc->hour, ct.hour); 334280775Sneel goto fail; 335280775Sneel } 336276428Sneel } 337276428Sneel 338276428Sneel if (error || ct.hour < 0 || ct.hour > 23) { 339276428Sneel VM_CTR2(vm, "Invalid RTC hour %#x/%d", rtc->hour, ct.hour); 340276428Sneel goto fail; 341276428Sneel } 342276428Sneel 343276428Sneel /* 344276428Sneel * Ignore 'rtc->dow' because some guests like Linux don't bother 345276428Sneel * setting it at all while others like OpenBSD/i386 set it incorrectly. 346276428Sneel * 347276428Sneel * clock_ct_to_ts() does not depend on 'ct.dow' anyways so ignore it. 348276428Sneel */ 349276428Sneel ct.dow = -1; 350276428Sneel 351276428Sneel error = rtcget(rtc, rtc->day_of_month, &ct.day); 352276428Sneel if (error || ct.day < 1 || ct.day > 31) { 353276428Sneel VM_CTR2(vm, "Invalid RTC mday %#x/%d", rtc->day_of_month, 354276428Sneel ct.day); 355276428Sneel goto fail; 356276428Sneel } 357276428Sneel 358276428Sneel error = rtcget(rtc, rtc->month, &ct.mon); 359276428Sneel if (error || ct.mon < 1 || ct.mon > 12) { 360276428Sneel VM_CTR2(vm, "Invalid RTC month %#x/%d", rtc->month, ct.mon); 361276428Sneel goto fail; 362276428Sneel } 363276428Sneel 364276428Sneel error = rtcget(rtc, rtc->year, &year); 365276428Sneel if (error || year < 0 || year > 99) { 366276428Sneel VM_CTR2(vm, "Invalid RTC year %#x/%d", rtc->year, year); 367276428Sneel goto fail; 368276428Sneel } 369276428Sneel 370282206Sneel error = rtcget(rtc, rtc->century, ¢ury); 371282206Sneel ct.year = century * 100 + year; 372282206Sneel if (error || ct.year < POSIX_BASE_YEAR) { 373282206Sneel VM_CTR2(vm, "Invalid RTC century %#x/%d", rtc->century, 374282206Sneel ct.year); 375282206Sneel goto fail; 376282206Sneel } 377282206Sneel 378276428Sneel error = clock_ct_to_ts(&ct, &ts); 379276428Sneel if (error || ts.tv_sec < 0) { 380276428Sneel VM_CTR3(vm, "Invalid RTC clocktime.date %04d-%02d-%02d", 381276428Sneel ct.year, ct.mon, ct.day); 382276428Sneel VM_CTR3(vm, "Invalid RTC clocktime.time %02d:%02d:%02d", 383276428Sneel ct.hour, ct.min, ct.sec); 384276428Sneel goto fail; 385276428Sneel } 386276428Sneel return (ts.tv_sec); /* success */ 387276428Sneelfail: 388282206Sneel /* 389282206Sneel * Stop updating the RTC if the date/time fields programmed by 390282206Sneel * the guest are invalid. 391282206Sneel */ 392282206Sneel VM_CTR0(vrtc->vm, "Invalid RTC date/time programming detected"); 393282206Sneel return (VRTC_BROKEN_TIME); 394276428Sneel} 395276428Sneel 396276428Sneelstatic int 397282259Sneelvrtc_time_update(struct vrtc *vrtc, time_t newtime, sbintime_t newbase) 398276428Sneel{ 399276428Sneel struct rtcdev *rtc; 400282259Sneel sbintime_t oldbase; 401276428Sneel time_t oldtime; 402276428Sneel uint8_t alarm_sec, alarm_min, alarm_hour; 403276428Sneel 404276428Sneel KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 405276428Sneel 406276428Sneel rtc = &vrtc->rtcdev; 407276428Sneel alarm_sec = rtc->alarm_sec; 408276428Sneel alarm_min = rtc->alarm_min; 409276428Sneel alarm_hour = rtc->alarm_hour; 410276428Sneel 411276428Sneel oldtime = vrtc->base_rtctime; 412282259Sneel VM_CTR2(vrtc->vm, "Updating RTC secs from %#lx to %#lx", 413276428Sneel oldtime, newtime); 414276428Sneel 415282259Sneel oldbase = vrtc->base_uptime; 416282259Sneel VM_CTR2(vrtc->vm, "Updating RTC base uptime from %#lx to %#lx", 417282259Sneel oldbase, newbase); 418282259Sneel vrtc->base_uptime = newbase; 419282259Sneel 420276428Sneel if (newtime == oldtime) 421276428Sneel return (0); 422276428Sneel 423276428Sneel /* 424276428Sneel * If 'newtime' indicates that RTC updates are disabled then just 425276428Sneel * record that and return. There is no need to do alarm interrupt 426282259Sneel * processing in this case. 427276428Sneel */ 428276428Sneel if (newtime == VRTC_BROKEN_TIME) { 429276428Sneel vrtc->base_rtctime = VRTC_BROKEN_TIME; 430276428Sneel return (0); 431276428Sneel } 432276428Sneel 433276428Sneel /* 434276428Sneel * Return an error if RTC updates are halted by the guest. 435276428Sneel */ 436276428Sneel if (rtc_halted(vrtc)) { 437276428Sneel VM_CTR0(vrtc->vm, "RTC update halted by guest"); 438276428Sneel return (EBUSY); 439276428Sneel } 440276428Sneel 441276428Sneel do { 442276428Sneel /* 443276428Sneel * If the alarm interrupt is enabled and 'oldtime' is valid 444276428Sneel * then visit all the seconds between 'oldtime' and 'newtime' 445276428Sneel * to check for the alarm condition. 446276428Sneel * 447276428Sneel * Otherwise move the RTC time forward directly to 'newtime'. 448276428Sneel */ 449276428Sneel if (aintr_enabled(vrtc) && oldtime != VRTC_BROKEN_TIME) 450276428Sneel vrtc->base_rtctime++; 451276428Sneel else 452276428Sneel vrtc->base_rtctime = newtime; 453276428Sneel 454276428Sneel if (aintr_enabled(vrtc)) { 455276428Sneel /* 456276428Sneel * Update the RTC date/time fields before checking 457276428Sneel * if the alarm conditions are satisfied. 458276428Sneel */ 459276428Sneel secs_to_rtc(vrtc->base_rtctime, vrtc, 0); 460276428Sneel 461276428Sneel if ((alarm_sec >= 0xC0 || alarm_sec == rtc->sec) && 462276428Sneel (alarm_min >= 0xC0 || alarm_min == rtc->min) && 463276428Sneel (alarm_hour >= 0xC0 || alarm_hour == rtc->hour)) { 464276428Sneel vrtc_set_reg_c(vrtc, rtc->reg_c | RTCIR_ALARM); 465276428Sneel } 466276428Sneel } 467276428Sneel } while (vrtc->base_rtctime != newtime); 468276428Sneel 469276428Sneel if (uintr_enabled(vrtc)) 470276428Sneel vrtc_set_reg_c(vrtc, rtc->reg_c | RTCIR_UPDATE); 471276428Sneel 472276428Sneel return (0); 473276428Sneel} 474276428Sneel 475276428Sneelstatic sbintime_t 476276428Sneelvrtc_freq(struct vrtc *vrtc) 477276428Sneel{ 478276428Sneel int ratesel; 479276428Sneel 480276428Sneel static sbintime_t pf[16] = { 481276428Sneel 0, 482276428Sneel SBT_1S / 256, 483276428Sneel SBT_1S / 128, 484276428Sneel SBT_1S / 8192, 485276428Sneel SBT_1S / 4096, 486276428Sneel SBT_1S / 2048, 487276428Sneel SBT_1S / 1024, 488276428Sneel SBT_1S / 512, 489276428Sneel SBT_1S / 256, 490276428Sneel SBT_1S / 128, 491276428Sneel SBT_1S / 64, 492276428Sneel SBT_1S / 32, 493276428Sneel SBT_1S / 16, 494276428Sneel SBT_1S / 8, 495276428Sneel SBT_1S / 4, 496276428Sneel SBT_1S / 2, 497276428Sneel }; 498276428Sneel 499276428Sneel KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 500276428Sneel 501276428Sneel /* 502276428Sneel * If both periodic and alarm interrupts are enabled then use the 503276428Sneel * periodic frequency to drive the callout. The minimum periodic 504276428Sneel * frequency (2 Hz) is higher than the alarm frequency (1 Hz) so 505276428Sneel * piggyback the alarm on top of it. The same argument applies to 506276428Sneel * the update interrupt. 507276428Sneel */ 508276428Sneel if (pintr_enabled(vrtc) && divider_enabled(vrtc->rtcdev.reg_a)) { 509276428Sneel ratesel = vrtc->rtcdev.reg_a & 0xf; 510276428Sneel return (pf[ratesel]); 511276428Sneel } else if (aintr_enabled(vrtc) && update_enabled(vrtc)) { 512276428Sneel return (SBT_1S); 513276428Sneel } else if (uintr_enabled(vrtc) && update_enabled(vrtc)) { 514276428Sneel return (SBT_1S); 515276428Sneel } else { 516276428Sneel return (0); 517276428Sneel } 518276428Sneel} 519276428Sneel 520276428Sneelstatic void 521276428Sneelvrtc_callout_reset(struct vrtc *vrtc, sbintime_t freqsbt) 522276428Sneel{ 523276428Sneel 524276428Sneel KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 525276428Sneel 526276428Sneel if (freqsbt == 0) { 527276428Sneel if (callout_active(&vrtc->callout)) { 528276428Sneel VM_CTR0(vrtc->vm, "RTC callout stopped"); 529276428Sneel callout_stop(&vrtc->callout); 530276428Sneel } 531276428Sneel return; 532276428Sneel } 533276428Sneel VM_CTR1(vrtc->vm, "RTC callout frequency %d hz", SBT_1S / freqsbt); 534276428Sneel callout_reset_sbt(&vrtc->callout, freqsbt, 0, vrtc_callout_handler, 535276428Sneel vrtc, 0); 536276428Sneel} 537276428Sneel 538276428Sneelstatic void 539276428Sneelvrtc_callout_handler(void *arg) 540276428Sneel{ 541276428Sneel struct vrtc *vrtc = arg; 542282259Sneel sbintime_t freqsbt, basetime; 543276428Sneel time_t rtctime; 544276428Sneel int error; 545276428Sneel 546276428Sneel VM_CTR0(vrtc->vm, "vrtc callout fired"); 547276428Sneel 548276428Sneel VRTC_LOCK(vrtc); 549276428Sneel if (callout_pending(&vrtc->callout)) /* callout was reset */ 550276428Sneel goto done; 551276428Sneel 552276428Sneel if (!callout_active(&vrtc->callout)) /* callout was stopped */ 553276428Sneel goto done; 554276428Sneel 555276428Sneel callout_deactivate(&vrtc->callout); 556276428Sneel 557276428Sneel KASSERT((vrtc->rtcdev.reg_b & RTCSB_ALL_INTRS) != 0, 558276428Sneel ("gratuitous vrtc callout")); 559276428Sneel 560276428Sneel if (pintr_enabled(vrtc)) 561276428Sneel vrtc_set_reg_c(vrtc, vrtc->rtcdev.reg_c | RTCIR_PERIOD); 562276428Sneel 563276428Sneel if (aintr_enabled(vrtc) || uintr_enabled(vrtc)) { 564282259Sneel rtctime = vrtc_curtime(vrtc, &basetime); 565282259Sneel error = vrtc_time_update(vrtc, rtctime, basetime); 566276428Sneel KASSERT(error == 0, ("%s: vrtc_time_update error %d", 567276428Sneel __func__, error)); 568276428Sneel } 569276428Sneel 570276428Sneel freqsbt = vrtc_freq(vrtc); 571276428Sneel KASSERT(freqsbt != 0, ("%s: vrtc frequency cannot be zero", __func__)); 572276428Sneel vrtc_callout_reset(vrtc, freqsbt); 573276428Sneeldone: 574276428Sneel VRTC_UNLOCK(vrtc); 575276428Sneel} 576276428Sneel 577276428Sneelstatic __inline void 578276428Sneelvrtc_callout_check(struct vrtc *vrtc, sbintime_t freq) 579276428Sneel{ 580276428Sneel int active; 581276428Sneel 582276428Sneel active = callout_active(&vrtc->callout) ? 1 : 0; 583276428Sneel KASSERT((freq == 0 && !active) || (freq != 0 && active), 584276428Sneel ("vrtc callout %s with frequency %#lx", 585276428Sneel active ? "active" : "inactive", freq)); 586276428Sneel} 587276428Sneel 588276428Sneelstatic void 589276428Sneelvrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval) 590276428Sneel{ 591276428Sneel struct rtcdev *rtc; 592276428Sneel int oldirqf, newirqf; 593276428Sneel uint8_t oldval, changed; 594276428Sneel 595276428Sneel KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 596276428Sneel 597276428Sneel rtc = &vrtc->rtcdev; 598276428Sneel newval &= RTCIR_ALARM | RTCIR_PERIOD | RTCIR_UPDATE; 599276428Sneel 600276428Sneel oldirqf = rtc->reg_c & RTCIR_INT; 601276428Sneel if ((aintr_enabled(vrtc) && (newval & RTCIR_ALARM) != 0) || 602276428Sneel (pintr_enabled(vrtc) && (newval & RTCIR_PERIOD) != 0) || 603276428Sneel (uintr_enabled(vrtc) && (newval & RTCIR_UPDATE) != 0)) { 604276428Sneel newirqf = RTCIR_INT; 605276428Sneel } else { 606276428Sneel newirqf = 0; 607276428Sneel } 608276428Sneel 609276428Sneel oldval = rtc->reg_c; 610276428Sneel rtc->reg_c = newirqf | newval; 611276428Sneel changed = oldval ^ rtc->reg_c; 612276428Sneel if (changed) { 613276428Sneel VM_CTR2(vrtc->vm, "RTC reg_c changed from %#x to %#x", 614276428Sneel oldval, rtc->reg_c); 615276428Sneel } 616276428Sneel 617276428Sneel if (!oldirqf && newirqf) { 618276428Sneel VM_CTR1(vrtc->vm, "RTC irq %d asserted", RTC_IRQ); 619276428Sneel vatpic_pulse_irq(vrtc->vm, RTC_IRQ); 620276428Sneel vioapic_pulse_irq(vrtc->vm, RTC_IRQ); 621276428Sneel } else if (oldirqf && !newirqf) { 622276428Sneel VM_CTR1(vrtc->vm, "RTC irq %d deasserted", RTC_IRQ); 623276428Sneel } 624276428Sneel} 625276428Sneel 626276428Sneelstatic int 627276428Sneelvrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval) 628276428Sneel{ 629276428Sneel struct rtcdev *rtc; 630282259Sneel sbintime_t oldfreq, newfreq, basetime; 631276428Sneel time_t curtime, rtctime; 632276428Sneel int error; 633276428Sneel uint8_t oldval, changed; 634276428Sneel 635276428Sneel KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 636276428Sneel 637276428Sneel rtc = &vrtc->rtcdev; 638276428Sneel oldval = rtc->reg_b; 639276428Sneel oldfreq = vrtc_freq(vrtc); 640276428Sneel 641276428Sneel rtc->reg_b = newval; 642276428Sneel changed = oldval ^ newval; 643276428Sneel if (changed) { 644276428Sneel VM_CTR2(vrtc->vm, "RTC reg_b changed from %#x to %#x", 645276428Sneel oldval, newval); 646276428Sneel } 647276428Sneel 648276428Sneel if (changed & RTCSB_HALT) { 649276428Sneel if ((newval & RTCSB_HALT) == 0) { 650276428Sneel rtctime = rtc_to_secs(vrtc); 651282259Sneel basetime = sbinuptime(); 652276428Sneel if (rtctime == VRTC_BROKEN_TIME) { 653276428Sneel if (rtc_flag_broken_time) 654276428Sneel return (-1); 655276428Sneel } 656276428Sneel } else { 657282259Sneel curtime = vrtc_curtime(vrtc, &basetime); 658276428Sneel KASSERT(curtime == vrtc->base_rtctime, ("%s: mismatch " 659276428Sneel "between vrtc basetime (%#lx) and curtime (%#lx)", 660276428Sneel __func__, vrtc->base_rtctime, curtime)); 661276428Sneel 662276428Sneel /* 663276428Sneel * Force a refresh of the RTC date/time fields so 664276428Sneel * they reflect the time right before the guest set 665276428Sneel * the HALT bit. 666276428Sneel */ 667276428Sneel secs_to_rtc(curtime, vrtc, 1); 668276428Sneel 669276428Sneel /* 670276428Sneel * Updates are halted so mark 'base_rtctime' to denote 671276428Sneel * that the RTC date/time is in flux. 672276428Sneel */ 673276428Sneel rtctime = VRTC_BROKEN_TIME; 674276428Sneel rtc->reg_b &= ~RTCSB_UINTR; 675276428Sneel } 676282259Sneel error = vrtc_time_update(vrtc, rtctime, basetime); 677276428Sneel KASSERT(error == 0, ("vrtc_time_update error %d", error)); 678276428Sneel } 679276428Sneel 680276428Sneel /* 681276428Sneel * Side effect of changes to the interrupt enable bits. 682276428Sneel */ 683276428Sneel if (changed & RTCSB_ALL_INTRS) 684276428Sneel vrtc_set_reg_c(vrtc, vrtc->rtcdev.reg_c); 685276428Sneel 686276428Sneel /* 687276428Sneel * Change the callout frequency if it has changed. 688276428Sneel */ 689276428Sneel newfreq = vrtc_freq(vrtc); 690276428Sneel if (newfreq != oldfreq) 691276428Sneel vrtc_callout_reset(vrtc, newfreq); 692276428Sneel else 693276428Sneel vrtc_callout_check(vrtc, newfreq); 694276428Sneel 695276428Sneel /* 696276428Sneel * The side effect of bits that control the RTC date/time format 697276428Sneel * is handled lazily when those fields are actually read. 698276428Sneel */ 699276428Sneel return (0); 700276428Sneel} 701276428Sneel 702276428Sneelstatic void 703276428Sneelvrtc_set_reg_a(struct vrtc *vrtc, uint8_t newval) 704276428Sneel{ 705276428Sneel sbintime_t oldfreq, newfreq; 706276428Sneel uint8_t oldval, changed; 707276428Sneel 708276428Sneel KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 709276428Sneel 710276428Sneel newval &= ~RTCSA_TUP; 711276428Sneel oldval = vrtc->rtcdev.reg_a; 712276428Sneel oldfreq = vrtc_freq(vrtc); 713276428Sneel 714276428Sneel if (divider_enabled(oldval) && !divider_enabled(newval)) { 715276428Sneel VM_CTR2(vrtc->vm, "RTC divider held in reset at %#lx/%#lx", 716276428Sneel vrtc->base_rtctime, vrtc->base_uptime); 717276428Sneel } else if (!divider_enabled(oldval) && divider_enabled(newval)) { 718276428Sneel /* 719276428Sneel * If the dividers are coming out of reset then update 720276428Sneel * 'base_uptime' before this happens. This is done to 721276428Sneel * maintain the illusion that the RTC date/time was frozen 722276428Sneel * while the dividers were disabled. 723276428Sneel */ 724276428Sneel vrtc->base_uptime = sbinuptime(); 725276428Sneel VM_CTR2(vrtc->vm, "RTC divider out of reset at %#lx/%#lx", 726276428Sneel vrtc->base_rtctime, vrtc->base_uptime); 727276428Sneel } else { 728276428Sneel /* NOTHING */ 729276428Sneel } 730276428Sneel 731276428Sneel vrtc->rtcdev.reg_a = newval; 732276428Sneel changed = oldval ^ newval; 733276428Sneel if (changed) { 734276428Sneel VM_CTR2(vrtc->vm, "RTC reg_a changed from %#x to %#x", 735276428Sneel oldval, newval); 736276428Sneel } 737276428Sneel 738276428Sneel /* 739276428Sneel * Side effect of changes to rate select and divider enable bits. 740276428Sneel */ 741276428Sneel newfreq = vrtc_freq(vrtc); 742276428Sneel if (newfreq != oldfreq) 743276428Sneel vrtc_callout_reset(vrtc, newfreq); 744276428Sneel else 745276428Sneel vrtc_callout_check(vrtc, newfreq); 746276428Sneel} 747276428Sneel 748276428Sneelint 749276428Sneelvrtc_set_time(struct vm *vm, time_t secs) 750276428Sneel{ 751276428Sneel struct vrtc *vrtc; 752276428Sneel int error; 753276428Sneel 754276428Sneel vrtc = vm_rtc(vm); 755276428Sneel VRTC_LOCK(vrtc); 756282259Sneel error = vrtc_time_update(vrtc, secs, sbinuptime()); 757276428Sneel VRTC_UNLOCK(vrtc); 758276428Sneel 759276428Sneel if (error) { 760276428Sneel VM_CTR2(vrtc->vm, "Error %d setting RTC time to %#lx", error, 761276428Sneel secs); 762276428Sneel } else { 763276428Sneel VM_CTR1(vrtc->vm, "RTC time set to %#lx", secs); 764276428Sneel } 765276428Sneel 766276428Sneel return (error); 767276428Sneel} 768276428Sneel 769276428Sneeltime_t 770276428Sneelvrtc_get_time(struct vm *vm) 771276428Sneel{ 772276428Sneel struct vrtc *vrtc; 773282259Sneel sbintime_t basetime; 774276428Sneel time_t t; 775276428Sneel 776276428Sneel vrtc = vm_rtc(vm); 777276428Sneel VRTC_LOCK(vrtc); 778282259Sneel t = vrtc_curtime(vrtc, &basetime); 779276428Sneel VRTC_UNLOCK(vrtc); 780276428Sneel 781276428Sneel return (t); 782276428Sneel} 783276428Sneel 784276428Sneelint 785276428Sneelvrtc_nvram_write(struct vm *vm, int offset, uint8_t value) 786276428Sneel{ 787276428Sneel struct vrtc *vrtc; 788276428Sneel uint8_t *ptr; 789276428Sneel 790276428Sneel vrtc = vm_rtc(vm); 791276428Sneel 792276428Sneel /* 793276428Sneel * Don't allow writes to RTC control registers or the date/time fields. 794276428Sneel */ 795276428Sneel if (offset < offsetof(struct rtcdev, nvram[0]) || 796282206Sneel offset == RTC_CENTURY || offset >= sizeof(struct rtcdev)) { 797276428Sneel VM_CTR1(vrtc->vm, "RTC nvram write to invalid offset %d", 798276428Sneel offset); 799276428Sneel return (EINVAL); 800276428Sneel } 801276428Sneel 802276428Sneel VRTC_LOCK(vrtc); 803276428Sneel ptr = (uint8_t *)(&vrtc->rtcdev); 804276428Sneel ptr[offset] = value; 805276428Sneel VM_CTR2(vrtc->vm, "RTC nvram write %#x to offset %#x", value, offset); 806276428Sneel VRTC_UNLOCK(vrtc); 807276428Sneel 808276428Sneel return (0); 809276428Sneel} 810276428Sneel 811276428Sneelint 812276428Sneelvrtc_nvram_read(struct vm *vm, int offset, uint8_t *retval) 813276428Sneel{ 814276428Sneel struct vrtc *vrtc; 815282259Sneel sbintime_t basetime; 816276428Sneel time_t curtime; 817276428Sneel uint8_t *ptr; 818276428Sneel 819276428Sneel /* 820276428Sneel * Allow all offsets in the RTC to be read. 821276428Sneel */ 822276428Sneel if (offset < 0 || offset >= sizeof(struct rtcdev)) 823276428Sneel return (EINVAL); 824276428Sneel 825276428Sneel vrtc = vm_rtc(vm); 826276428Sneel VRTC_LOCK(vrtc); 827276428Sneel 828276428Sneel /* 829276428Sneel * Update RTC date/time fields if necessary. 830276428Sneel */ 831282206Sneel if (offset < 10 || offset == RTC_CENTURY) { 832282259Sneel curtime = vrtc_curtime(vrtc, &basetime); 833276428Sneel secs_to_rtc(curtime, vrtc, 0); 834276428Sneel } 835276428Sneel 836276428Sneel ptr = (uint8_t *)(&vrtc->rtcdev); 837276428Sneel *retval = ptr[offset]; 838276428Sneel 839276428Sneel VRTC_UNLOCK(vrtc); 840276428Sneel return (0); 841276428Sneel} 842276428Sneel 843276428Sneelint 844276428Sneelvrtc_addr_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes, 845276428Sneel uint32_t *val) 846276428Sneel{ 847276428Sneel struct vrtc *vrtc; 848276428Sneel 849276428Sneel vrtc = vm_rtc(vm); 850276428Sneel 851276428Sneel if (bytes != 1) 852276428Sneel return (-1); 853276428Sneel 854276428Sneel if (in) { 855276428Sneel *val = 0xff; 856276428Sneel return (0); 857276428Sneel } 858276428Sneel 859276428Sneel VRTC_LOCK(vrtc); 860276428Sneel vrtc->addr = *val & 0x7f; 861276428Sneel VRTC_UNLOCK(vrtc); 862276428Sneel 863276428Sneel return (0); 864276428Sneel} 865276428Sneel 866276428Sneelint 867276428Sneelvrtc_data_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes, 868276428Sneel uint32_t *val) 869276428Sneel{ 870276428Sneel struct vrtc *vrtc; 871276428Sneel struct rtcdev *rtc; 872282259Sneel sbintime_t basetime; 873276428Sneel time_t curtime; 874276428Sneel int error, offset; 875276428Sneel 876276428Sneel vrtc = vm_rtc(vm); 877276428Sneel rtc = &vrtc->rtcdev; 878276428Sneel 879276428Sneel if (bytes != 1) 880276428Sneel return (-1); 881276428Sneel 882276428Sneel VRTC_LOCK(vrtc); 883276428Sneel offset = vrtc->addr; 884276428Sneel if (offset >= sizeof(struct rtcdev)) { 885276428Sneel VRTC_UNLOCK(vrtc); 886276428Sneel return (-1); 887276428Sneel } 888276428Sneel 889276428Sneel error = 0; 890282259Sneel curtime = vrtc_curtime(vrtc, &basetime); 891282259Sneel vrtc_time_update(vrtc, curtime, basetime); 892276428Sneel 893282206Sneel /* 894282206Sneel * Update RTC date/time fields if necessary. 895282206Sneel * 896282206Sneel * This is not just for reads of the RTC. The side-effect of writing 897282206Sneel * the century byte requires other RTC date/time fields (e.g. sec) 898282206Sneel * to be updated here. 899282206Sneel */ 900282206Sneel if (offset < 10 || offset == RTC_CENTURY) 901282206Sneel secs_to_rtc(curtime, vrtc, 0); 902282206Sneel 903276428Sneel if (in) { 904276428Sneel if (offset == 12) { 905276428Sneel /* 906276428Sneel * XXX 907276428Sneel * reg_c interrupt flags are updated only if the 908276428Sneel * corresponding interrupt enable bit in reg_b is set. 909276428Sneel */ 910276428Sneel *val = vrtc->rtcdev.reg_c; 911276428Sneel vrtc_set_reg_c(vrtc, 0); 912276428Sneel } else { 913276428Sneel *val = *((uint8_t *)rtc + offset); 914276428Sneel } 915276428Sneel VCPU_CTR2(vm, vcpuid, "Read value %#x from RTC offset %#x", 916276428Sneel *val, offset); 917276428Sneel } else { 918276428Sneel switch (offset) { 919276428Sneel case 10: 920276428Sneel VCPU_CTR1(vm, vcpuid, "RTC reg_a set to %#x", *val); 921276428Sneel vrtc_set_reg_a(vrtc, *val); 922276428Sneel break; 923276428Sneel case 11: 924276428Sneel VCPU_CTR1(vm, vcpuid, "RTC reg_b set to %#x", *val); 925276428Sneel error = vrtc_set_reg_b(vrtc, *val); 926276428Sneel break; 927276428Sneel case 12: 928276428Sneel VCPU_CTR1(vm, vcpuid, "RTC reg_c set to %#x (ignored)", 929276428Sneel *val); 930276428Sneel break; 931276428Sneel case 13: 932276428Sneel VCPU_CTR1(vm, vcpuid, "RTC reg_d set to %#x (ignored)", 933276428Sneel *val); 934276428Sneel break; 935276428Sneel case 0: 936276428Sneel /* 937276428Sneel * High order bit of 'seconds' is readonly. 938276428Sneel */ 939276428Sneel *val &= 0x7f; 940276428Sneel /* FALLTHRU */ 941276428Sneel default: 942276428Sneel VCPU_CTR2(vm, vcpuid, "RTC offset %#x set to %#x", 943276428Sneel offset, *val); 944276428Sneel *((uint8_t *)rtc + offset) = *val; 945276428Sneel break; 946276428Sneel } 947282206Sneel 948282206Sneel /* 949282206Sneel * XXX some guests (e.g. OpenBSD) write the century byte 950282206Sneel * outside of RTCSB_HALT so re-calculate the RTC date/time. 951282206Sneel */ 952282206Sneel if (offset == RTC_CENTURY && !rtc_halted(vrtc)) { 953282206Sneel curtime = rtc_to_secs(vrtc); 954282259Sneel error = vrtc_time_update(vrtc, curtime, sbinuptime()); 955282206Sneel KASSERT(!error, ("vrtc_time_update error %d", error)); 956282206Sneel if (curtime == VRTC_BROKEN_TIME && rtc_flag_broken_time) 957282206Sneel error = -1; 958282206Sneel } 959276428Sneel } 960276428Sneel VRTC_UNLOCK(vrtc); 961276428Sneel return (error); 962276428Sneel} 963276428Sneel 964276428Sneelvoid 965276428Sneelvrtc_reset(struct vrtc *vrtc) 966276428Sneel{ 967276428Sneel struct rtcdev *rtc; 968276428Sneel 969276428Sneel VRTC_LOCK(vrtc); 970276428Sneel 971276428Sneel rtc = &vrtc->rtcdev; 972276428Sneel vrtc_set_reg_b(vrtc, rtc->reg_b & ~(RTCSB_ALL_INTRS | RTCSB_SQWE)); 973276428Sneel vrtc_set_reg_c(vrtc, 0); 974276428Sneel KASSERT(!callout_active(&vrtc->callout), ("rtc callout still active")); 975276428Sneel 976276428Sneel VRTC_UNLOCK(vrtc); 977276428Sneel} 978276428Sneel 979276428Sneelstruct vrtc * 980276428Sneelvrtc_init(struct vm *vm) 981276428Sneel{ 982276428Sneel struct vrtc *vrtc; 983276428Sneel struct rtcdev *rtc; 984276428Sneel time_t curtime; 985276428Sneel 986276428Sneel vrtc = malloc(sizeof(struct vrtc), M_VRTC, M_WAITOK | M_ZERO); 987276428Sneel vrtc->vm = vm; 988276428Sneel mtx_init(&vrtc->mtx, "vrtc lock", NULL, MTX_DEF); 989276428Sneel callout_init(&vrtc->callout, 1); 990276428Sneel 991276428Sneel /* Allow dividers to keep time but disable everything else */ 992276428Sneel rtc = &vrtc->rtcdev; 993276428Sneel rtc->reg_a = 0x20; 994276428Sneel rtc->reg_b = RTCSB_24HR; 995276428Sneel rtc->reg_c = 0; 996276428Sneel rtc->reg_d = RTCSD_PWR; 997276428Sneel 998276428Sneel /* Reset the index register to a safe value. */ 999276428Sneel vrtc->addr = RTC_STATUSD; 1000276428Sneel 1001276428Sneel /* 1002276428Sneel * Initialize RTC time to 00:00:00 Jan 1, 1970. 1003276428Sneel */ 1004276428Sneel curtime = 0; 1005276428Sneel 1006276428Sneel VRTC_LOCK(vrtc); 1007276428Sneel vrtc->base_rtctime = VRTC_BROKEN_TIME; 1008282259Sneel vrtc_time_update(vrtc, curtime, sbinuptime()); 1009276428Sneel secs_to_rtc(curtime, vrtc, 0); 1010276428Sneel VRTC_UNLOCK(vrtc); 1011276428Sneel 1012276428Sneel return (vrtc); 1013276428Sneel} 1014276428Sneel 1015276428Sneelvoid 1016276428Sneelvrtc_cleanup(struct vrtc *vrtc) 1017276428Sneel{ 1018276428Sneel 1019276428Sneel callout_drain(&vrtc->callout); 1020276428Sneel free(vrtc, M_VRTC); 1021276428Sneel} 1022