10dafa5cdSNeel Natu /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3ebc3c37cSMarcelo Araujo * 40dafa5cdSNeel Natu * Copyright (c) 2014, Neel Natu (neel@freebsd.org) 50dafa5cdSNeel Natu * All rights reserved. 60dafa5cdSNeel Natu * 70dafa5cdSNeel Natu * Redistribution and use in source and binary forms, with or without 80dafa5cdSNeel Natu * modification, are permitted provided that the following conditions 90dafa5cdSNeel Natu * are met: 100dafa5cdSNeel Natu * 1. Redistributions of source code must retain the above copyright 110dafa5cdSNeel Natu * notice unmodified, this list of conditions, and the following 120dafa5cdSNeel Natu * disclaimer. 130dafa5cdSNeel Natu * 2. Redistributions in binary form must reproduce the above copyright 140dafa5cdSNeel Natu * notice, this list of conditions and the following disclaimer in the 150dafa5cdSNeel Natu * documentation and/or other materials provided with the distribution. 160dafa5cdSNeel Natu * 170dafa5cdSNeel Natu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 180dafa5cdSNeel Natu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 190dafa5cdSNeel Natu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 200dafa5cdSNeel Natu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 210dafa5cdSNeel Natu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 220dafa5cdSNeel Natu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 230dafa5cdSNeel Natu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 240dafa5cdSNeel Natu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 250dafa5cdSNeel Natu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 260dafa5cdSNeel Natu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 270dafa5cdSNeel Natu */ 280dafa5cdSNeel Natu 290dafa5cdSNeel Natu #include <sys/cdefs.h> 30483d953aSJohn Baldwin #include "opt_bhyve_snapshot.h" 31483d953aSJohn Baldwin 320dafa5cdSNeel Natu #include <sys/param.h> 330dafa5cdSNeel Natu #include <sys/systm.h> 340dafa5cdSNeel Natu #include <sys/queue.h> 350dafa5cdSNeel Natu #include <sys/kernel.h> 360dafa5cdSNeel Natu #include <sys/malloc.h> 370dafa5cdSNeel Natu #include <sys/lock.h> 380dafa5cdSNeel Natu #include <sys/mutex.h> 390dafa5cdSNeel Natu #include <sys/clock.h> 400dafa5cdSNeel Natu #include <sys/sysctl.h> 410dafa5cdSNeel Natu 420dafa5cdSNeel Natu #include <machine/vmm.h> 43483d953aSJohn Baldwin #include <machine/vmm_snapshot.h> 440dafa5cdSNeel Natu 450dafa5cdSNeel Natu #include <isa/rtc.h> 460dafa5cdSNeel Natu 47*3ccb0233SMark Johnston #include <dev/vmm/vmm_ktr.h> 48*3ccb0233SMark Johnston 490dafa5cdSNeel Natu #include "vatpic.h" 500dafa5cdSNeel Natu #include "vioapic.h" 510dafa5cdSNeel Natu #include "vrtc.h" 520dafa5cdSNeel Natu 530dafa5cdSNeel Natu /* Register layout of the RTC */ 540dafa5cdSNeel Natu struct rtcdev { 550dafa5cdSNeel Natu uint8_t sec; 560dafa5cdSNeel Natu uint8_t alarm_sec; 570dafa5cdSNeel Natu uint8_t min; 580dafa5cdSNeel Natu uint8_t alarm_min; 590dafa5cdSNeel Natu uint8_t hour; 600dafa5cdSNeel Natu uint8_t alarm_hour; 610dafa5cdSNeel Natu uint8_t day_of_week; 620dafa5cdSNeel Natu uint8_t day_of_month; 630dafa5cdSNeel Natu uint8_t month; 640dafa5cdSNeel Natu uint8_t year; 650dafa5cdSNeel Natu uint8_t reg_a; 660dafa5cdSNeel Natu uint8_t reg_b; 670dafa5cdSNeel Natu uint8_t reg_c; 680dafa5cdSNeel Natu uint8_t reg_d; 69f39630c2SNeel Natu uint8_t nvram[36]; 70f39630c2SNeel Natu uint8_t century; 71f39630c2SNeel Natu uint8_t nvram2[128 - 51]; 720dafa5cdSNeel Natu } __packed; 730dafa5cdSNeel Natu CTASSERT(sizeof(struct rtcdev) == 128); 74f39630c2SNeel Natu CTASSERT(offsetof(struct rtcdev, century) == RTC_CENTURY); 750dafa5cdSNeel Natu 760dafa5cdSNeel Natu struct vrtc { 770dafa5cdSNeel Natu struct vm *vm; 780dafa5cdSNeel Natu struct mtx mtx; 790dafa5cdSNeel Natu struct callout callout; 800dafa5cdSNeel Natu u_int addr; /* RTC register to read or write */ 810dafa5cdSNeel Natu sbintime_t base_uptime; 820dafa5cdSNeel Natu time_t base_rtctime; 830dafa5cdSNeel Natu struct rtcdev rtcdev; 840dafa5cdSNeel Natu }; 850dafa5cdSNeel Natu 860dafa5cdSNeel Natu #define VRTC_LOCK(vrtc) mtx_lock(&((vrtc)->mtx)) 870dafa5cdSNeel Natu #define VRTC_UNLOCK(vrtc) mtx_unlock(&((vrtc)->mtx)) 880dafa5cdSNeel Natu #define VRTC_LOCKED(vrtc) mtx_owned(&((vrtc)->mtx)) 890dafa5cdSNeel Natu 900dafa5cdSNeel Natu /* 910dafa5cdSNeel Natu * RTC time is considered "broken" if: 920dafa5cdSNeel Natu * - RTC updates are halted by the guest 930dafa5cdSNeel Natu * - RTC date/time fields have invalid values 940dafa5cdSNeel Natu */ 950dafa5cdSNeel Natu #define VRTC_BROKEN_TIME ((time_t)-1) 960dafa5cdSNeel Natu 970dafa5cdSNeel Natu #define RTC_IRQ 8 980dafa5cdSNeel Natu #define RTCSB_BIN 0x04 990dafa5cdSNeel Natu #define RTCSB_ALL_INTRS (RTCSB_UINTR | RTCSB_AINTR | RTCSB_PINTR) 1000dafa5cdSNeel Natu #define rtc_halted(vrtc) ((vrtc->rtcdev.reg_b & RTCSB_HALT) != 0) 1010dafa5cdSNeel Natu #define aintr_enabled(vrtc) (((vrtc)->rtcdev.reg_b & RTCSB_AINTR) != 0) 1020dafa5cdSNeel Natu #define pintr_enabled(vrtc) (((vrtc)->rtcdev.reg_b & RTCSB_PINTR) != 0) 1030dafa5cdSNeel Natu #define uintr_enabled(vrtc) (((vrtc)->rtcdev.reg_b & RTCSB_UINTR) != 0) 1040dafa5cdSNeel Natu 1050dafa5cdSNeel Natu static void vrtc_callout_handler(void *arg); 1060dafa5cdSNeel Natu static void vrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval); 1070dafa5cdSNeel Natu 1080dafa5cdSNeel Natu static MALLOC_DEFINE(M_VRTC, "vrtc", "bhyve virtual rtc"); 1090dafa5cdSNeel Natu 1100dafa5cdSNeel Natu SYSCTL_DECL(_hw_vmm); 111b40598c5SPawel Biernacki SYSCTL_NODE(_hw_vmm, OID_AUTO, vrtc, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 112b40598c5SPawel Biernacki NULL); 1130dafa5cdSNeel Natu 1140dafa5cdSNeel Natu static int rtc_flag_broken_time = 1; 1150dafa5cdSNeel Natu SYSCTL_INT(_hw_vmm_vrtc, OID_AUTO, flag_broken_time, CTLFLAG_RDTUN, 1160dafa5cdSNeel Natu &rtc_flag_broken_time, 0, "Stop guest when invalid RTC time is detected"); 1170dafa5cdSNeel Natu 1180dafa5cdSNeel Natu static __inline bool 1190dafa5cdSNeel Natu divider_enabled(int reg_a) 1200dafa5cdSNeel Natu { 1210dafa5cdSNeel Natu /* 1220dafa5cdSNeel Natu * The RTC is counting only when dividers are not held in reset. 1230dafa5cdSNeel Natu */ 1240dafa5cdSNeel Natu return ((reg_a & 0x70) == 0x20); 1250dafa5cdSNeel Natu } 1260dafa5cdSNeel Natu 1270dafa5cdSNeel Natu static __inline bool 1280dafa5cdSNeel Natu update_enabled(struct vrtc *vrtc) 1290dafa5cdSNeel Natu { 1300dafa5cdSNeel Natu /* 1310dafa5cdSNeel Natu * RTC date/time can be updated only if: 1320dafa5cdSNeel Natu * - divider is not held in reset 1330dafa5cdSNeel Natu * - guest has not disabled updates 1340dafa5cdSNeel Natu * - the date/time fields have valid contents 1350dafa5cdSNeel Natu */ 1360dafa5cdSNeel Natu if (!divider_enabled(vrtc->rtcdev.reg_a)) 1370dafa5cdSNeel Natu return (false); 1380dafa5cdSNeel Natu 1390dafa5cdSNeel Natu if (rtc_halted(vrtc)) 1400dafa5cdSNeel Natu return (false); 1410dafa5cdSNeel Natu 1420dafa5cdSNeel Natu if (vrtc->base_rtctime == VRTC_BROKEN_TIME) 1430dafa5cdSNeel Natu return (false); 1440dafa5cdSNeel Natu 1450dafa5cdSNeel Natu return (true); 1460dafa5cdSNeel Natu } 1470dafa5cdSNeel Natu 1480dafa5cdSNeel Natu static time_t 149787fb3d0SNeel Natu vrtc_curtime(struct vrtc *vrtc, sbintime_t *basetime) 1500dafa5cdSNeel Natu { 1510dafa5cdSNeel Natu sbintime_t now, delta; 152787fb3d0SNeel Natu time_t t, secs; 1530dafa5cdSNeel Natu 1540dafa5cdSNeel Natu KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 1550dafa5cdSNeel Natu 1560dafa5cdSNeel Natu t = vrtc->base_rtctime; 157787fb3d0SNeel Natu *basetime = vrtc->base_uptime; 1580dafa5cdSNeel Natu if (update_enabled(vrtc)) { 1590dafa5cdSNeel Natu now = sbinuptime(); 1600dafa5cdSNeel Natu delta = now - vrtc->base_uptime; 1610dafa5cdSNeel Natu KASSERT(delta >= 0, ("vrtc_curtime: uptime went backwards: " 1620dafa5cdSNeel Natu "%#lx to %#lx", vrtc->base_uptime, now)); 163787fb3d0SNeel Natu secs = delta / SBT_1S; 164787fb3d0SNeel Natu t += secs; 165787fb3d0SNeel Natu *basetime += secs * SBT_1S; 1660dafa5cdSNeel Natu } 1670dafa5cdSNeel Natu return (t); 1680dafa5cdSNeel Natu } 1690dafa5cdSNeel Natu 1700dafa5cdSNeel Natu static __inline uint8_t 1710dafa5cdSNeel Natu rtcset(struct rtcdev *rtc, int val) 1720dafa5cdSNeel Natu { 1730dafa5cdSNeel Natu 1740dafa5cdSNeel Natu KASSERT(val >= 0 && val < 100, ("%s: invalid bin2bcd index %d", 1750dafa5cdSNeel Natu __func__, val)); 1760dafa5cdSNeel Natu 1770dafa5cdSNeel Natu return ((rtc->reg_b & RTCSB_BIN) ? val : bin2bcd_data[val]); 1780dafa5cdSNeel Natu } 1790dafa5cdSNeel Natu 1800dafa5cdSNeel Natu static void 1810dafa5cdSNeel Natu secs_to_rtc(time_t rtctime, struct vrtc *vrtc, int force_update) 1820dafa5cdSNeel Natu { 1830dafa5cdSNeel Natu struct clocktime ct; 1840dafa5cdSNeel Natu struct timespec ts; 1850dafa5cdSNeel Natu struct rtcdev *rtc; 1860dafa5cdSNeel Natu int hour; 1870dafa5cdSNeel Natu 1880dafa5cdSNeel Natu KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 1890dafa5cdSNeel Natu 1900dafa5cdSNeel Natu if (rtctime < 0) { 1910dafa5cdSNeel Natu KASSERT(rtctime == VRTC_BROKEN_TIME, 1920dafa5cdSNeel Natu ("%s: invalid vrtc time %#lx", __func__, rtctime)); 1930dafa5cdSNeel Natu return; 1940dafa5cdSNeel Natu } 1950dafa5cdSNeel Natu 1960dafa5cdSNeel Natu /* 1970dafa5cdSNeel Natu * If the RTC is halted then the guest has "ownership" of the 1980dafa5cdSNeel Natu * date/time fields. Don't update the RTC date/time fields in 1990dafa5cdSNeel Natu * this case (unless forced). 2000dafa5cdSNeel Natu */ 2010dafa5cdSNeel Natu if (rtc_halted(vrtc) && !force_update) 2020dafa5cdSNeel Natu return; 2030dafa5cdSNeel Natu 2040dafa5cdSNeel Natu ts.tv_sec = rtctime; 2050dafa5cdSNeel Natu ts.tv_nsec = 0; 2060dafa5cdSNeel Natu clock_ts_to_ct(&ts, &ct); 2070dafa5cdSNeel Natu 2080dafa5cdSNeel Natu KASSERT(ct.sec >= 0 && ct.sec <= 59, ("invalid clocktime sec %d", 2090dafa5cdSNeel Natu ct.sec)); 2100dafa5cdSNeel Natu KASSERT(ct.min >= 0 && ct.min <= 59, ("invalid clocktime min %d", 2110dafa5cdSNeel Natu ct.min)); 2120dafa5cdSNeel Natu KASSERT(ct.hour >= 0 && ct.hour <= 23, ("invalid clocktime hour %d", 2130dafa5cdSNeel Natu ct.hour)); 2140dafa5cdSNeel Natu KASSERT(ct.dow >= 0 && ct.dow <= 6, ("invalid clocktime wday %d", 2150dafa5cdSNeel Natu ct.dow)); 2160dafa5cdSNeel Natu KASSERT(ct.day >= 1 && ct.day <= 31, ("invalid clocktime mday %d", 2170dafa5cdSNeel Natu ct.day)); 2180dafa5cdSNeel Natu KASSERT(ct.mon >= 1 && ct.mon <= 12, ("invalid clocktime month %d", 2190dafa5cdSNeel Natu ct.mon)); 2200dafa5cdSNeel Natu KASSERT(ct.year >= POSIX_BASE_YEAR, ("invalid clocktime year %d", 2210dafa5cdSNeel Natu ct.year)); 2220dafa5cdSNeel Natu 2230dafa5cdSNeel Natu rtc = &vrtc->rtcdev; 2240dafa5cdSNeel Natu rtc->sec = rtcset(rtc, ct.sec); 2250dafa5cdSNeel Natu rtc->min = rtcset(rtc, ct.min); 2260dafa5cdSNeel Natu 227f213ae0bSNeel Natu if (rtc->reg_b & RTCSB_24HR) { 2280dafa5cdSNeel Natu hour = ct.hour; 229f213ae0bSNeel Natu } else { 230f213ae0bSNeel Natu /* 231f213ae0bSNeel Natu * Convert to the 12-hour format. 232f213ae0bSNeel Natu */ 233f213ae0bSNeel Natu switch (ct.hour) { 234f213ae0bSNeel Natu case 0: /* 12 AM */ 235f213ae0bSNeel Natu case 12: /* 12 PM */ 236f213ae0bSNeel Natu hour = 12; 237f213ae0bSNeel Natu break; 238f213ae0bSNeel Natu default: 239f213ae0bSNeel Natu /* 240f213ae0bSNeel Natu * The remaining 'ct.hour' values are interpreted as: 241f213ae0bSNeel Natu * [1 - 11] -> 1 - 11 AM 242f213ae0bSNeel Natu * [13 - 23] -> 1 - 11 PM 243f213ae0bSNeel Natu */ 244f213ae0bSNeel Natu hour = ct.hour % 12; 245f213ae0bSNeel Natu break; 246f213ae0bSNeel Natu } 247f213ae0bSNeel Natu } 2480dafa5cdSNeel Natu 2490dafa5cdSNeel Natu rtc->hour = rtcset(rtc, hour); 2500dafa5cdSNeel Natu 2510dafa5cdSNeel Natu if ((rtc->reg_b & RTCSB_24HR) == 0 && ct.hour >= 12) 2520dafa5cdSNeel Natu rtc->hour |= 0x80; /* set MSB to indicate PM */ 2530dafa5cdSNeel Natu 2540dafa5cdSNeel Natu rtc->day_of_week = rtcset(rtc, ct.dow + 1); 2550dafa5cdSNeel Natu rtc->day_of_month = rtcset(rtc, ct.day); 2560dafa5cdSNeel Natu rtc->month = rtcset(rtc, ct.mon); 2570dafa5cdSNeel Natu rtc->year = rtcset(rtc, ct.year % 100); 258f39630c2SNeel Natu rtc->century = rtcset(rtc, ct.year / 100); 2590dafa5cdSNeel Natu } 2600dafa5cdSNeel Natu 2610dafa5cdSNeel Natu static int 2620dafa5cdSNeel Natu rtcget(struct rtcdev *rtc, int val, int *retval) 2630dafa5cdSNeel Natu { 2640dafa5cdSNeel Natu uint8_t upper, lower; 2650dafa5cdSNeel Natu 2660dafa5cdSNeel Natu if (rtc->reg_b & RTCSB_BIN) { 2670dafa5cdSNeel Natu *retval = val; 2680dafa5cdSNeel Natu return (0); 2690dafa5cdSNeel Natu } 2700dafa5cdSNeel Natu 2710dafa5cdSNeel Natu lower = val & 0xf; 2720dafa5cdSNeel Natu upper = (val >> 4) & 0xf; 2730dafa5cdSNeel Natu 2740dafa5cdSNeel Natu if (lower > 9 || upper > 9) 2750dafa5cdSNeel Natu return (-1); 2760dafa5cdSNeel Natu 2770dafa5cdSNeel Natu *retval = upper * 10 + lower; 2780dafa5cdSNeel Natu return (0); 2790dafa5cdSNeel Natu } 2800dafa5cdSNeel Natu 2810dafa5cdSNeel Natu static time_t 2820dafa5cdSNeel Natu rtc_to_secs(struct vrtc *vrtc) 2830dafa5cdSNeel Natu { 2840dafa5cdSNeel Natu struct clocktime ct; 2850dafa5cdSNeel Natu struct timespec ts; 2860dafa5cdSNeel Natu struct rtcdev *rtc; 287572edd3dSJohn Baldwin #ifdef KTR 2886a33ecdcSJohn Baldwin struct vm *vm = vrtc->vm; 289572edd3dSJohn Baldwin #endif 290f39630c2SNeel Natu int century, error, hour, pm, year; 2910dafa5cdSNeel Natu 2920dafa5cdSNeel Natu KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 2930dafa5cdSNeel Natu 2940dafa5cdSNeel Natu rtc = &vrtc->rtcdev; 2950dafa5cdSNeel Natu 2960dafa5cdSNeel Natu bzero(&ct, sizeof(struct clocktime)); 2970dafa5cdSNeel Natu 2980dafa5cdSNeel Natu error = rtcget(rtc, rtc->sec, &ct.sec); 2990dafa5cdSNeel Natu if (error || ct.sec < 0 || ct.sec > 59) { 3000dafa5cdSNeel Natu VM_CTR2(vm, "Invalid RTC sec %#x/%d", rtc->sec, ct.sec); 3010dafa5cdSNeel Natu goto fail; 3020dafa5cdSNeel Natu } 3030dafa5cdSNeel Natu 3040dafa5cdSNeel Natu error = rtcget(rtc, rtc->min, &ct.min); 3050dafa5cdSNeel Natu if (error || ct.min < 0 || ct.min > 59) { 3060dafa5cdSNeel Natu VM_CTR2(vm, "Invalid RTC min %#x/%d", rtc->min, ct.min); 3070dafa5cdSNeel Natu goto fail; 3080dafa5cdSNeel Natu } 3090dafa5cdSNeel Natu 3100dafa5cdSNeel Natu pm = 0; 3110dafa5cdSNeel Natu hour = rtc->hour; 3120dafa5cdSNeel Natu if ((rtc->reg_b & RTCSB_24HR) == 0) { 3130dafa5cdSNeel Natu if (hour & 0x80) { 3140dafa5cdSNeel Natu hour &= ~0x80; 3150dafa5cdSNeel Natu pm = 1; 3160dafa5cdSNeel Natu } 3170dafa5cdSNeel Natu } 3180dafa5cdSNeel Natu error = rtcget(rtc, hour, &ct.hour); 3190dafa5cdSNeel Natu if ((rtc->reg_b & RTCSB_24HR) == 0) { 320f213ae0bSNeel Natu if (ct.hour >= 1 && ct.hour <= 12) { 321f213ae0bSNeel Natu /* 322f213ae0bSNeel Natu * Convert from 12-hour format to internal 24-hour 323f213ae0bSNeel Natu * representation as follows: 324f213ae0bSNeel Natu * 325f213ae0bSNeel Natu * 12-hour format ct.hour 326f213ae0bSNeel Natu * 12 AM 0 327f213ae0bSNeel Natu * 1 - 11 AM 1 - 11 328f213ae0bSNeel Natu * 12 PM 12 329f213ae0bSNeel Natu * 1 - 11 PM 13 - 23 330f213ae0bSNeel Natu */ 331f213ae0bSNeel Natu if (ct.hour == 12) 332f213ae0bSNeel Natu ct.hour = 0; 3330dafa5cdSNeel Natu if (pm) 3340dafa5cdSNeel Natu ct.hour += 12; 335f213ae0bSNeel Natu } else { 336f213ae0bSNeel Natu VM_CTR2(vm, "Invalid RTC 12-hour format %#x/%d", 337f213ae0bSNeel Natu rtc->hour, ct.hour); 338f213ae0bSNeel Natu goto fail; 339f213ae0bSNeel Natu } 3400dafa5cdSNeel Natu } 3410dafa5cdSNeel Natu 3420dafa5cdSNeel Natu if (error || ct.hour < 0 || ct.hour > 23) { 3430dafa5cdSNeel Natu VM_CTR2(vm, "Invalid RTC hour %#x/%d", rtc->hour, ct.hour); 3440dafa5cdSNeel Natu goto fail; 3450dafa5cdSNeel Natu } 3460dafa5cdSNeel Natu 3470dafa5cdSNeel Natu /* 3480dafa5cdSNeel Natu * Ignore 'rtc->dow' because some guests like Linux don't bother 3490dafa5cdSNeel Natu * setting it at all while others like OpenBSD/i386 set it incorrectly. 3500dafa5cdSNeel Natu * 3510dafa5cdSNeel Natu * clock_ct_to_ts() does not depend on 'ct.dow' anyways so ignore it. 3520dafa5cdSNeel Natu */ 3530dafa5cdSNeel Natu ct.dow = -1; 3540dafa5cdSNeel Natu 3550dafa5cdSNeel Natu error = rtcget(rtc, rtc->day_of_month, &ct.day); 3560dafa5cdSNeel Natu if (error || ct.day < 1 || ct.day > 31) { 3570dafa5cdSNeel Natu VM_CTR2(vm, "Invalid RTC mday %#x/%d", rtc->day_of_month, 3580dafa5cdSNeel Natu ct.day); 3590dafa5cdSNeel Natu goto fail; 3600dafa5cdSNeel Natu } 3610dafa5cdSNeel Natu 3620dafa5cdSNeel Natu error = rtcget(rtc, rtc->month, &ct.mon); 3630dafa5cdSNeel Natu if (error || ct.mon < 1 || ct.mon > 12) { 3640dafa5cdSNeel Natu VM_CTR2(vm, "Invalid RTC month %#x/%d", rtc->month, ct.mon); 3650dafa5cdSNeel Natu goto fail; 3660dafa5cdSNeel Natu } 3670dafa5cdSNeel Natu 3680dafa5cdSNeel Natu error = rtcget(rtc, rtc->year, &year); 3690dafa5cdSNeel Natu if (error || year < 0 || year > 99) { 3700dafa5cdSNeel Natu VM_CTR2(vm, "Invalid RTC year %#x/%d", rtc->year, year); 3710dafa5cdSNeel Natu goto fail; 3720dafa5cdSNeel Natu } 373f39630c2SNeel Natu 374f39630c2SNeel Natu error = rtcget(rtc, rtc->century, ¢ury); 375f39630c2SNeel Natu ct.year = century * 100 + year; 376f39630c2SNeel Natu if (error || ct.year < POSIX_BASE_YEAR) { 377f39630c2SNeel Natu VM_CTR2(vm, "Invalid RTC century %#x/%d", rtc->century, 378f39630c2SNeel Natu ct.year); 379f39630c2SNeel Natu goto fail; 380f39630c2SNeel Natu } 3810dafa5cdSNeel Natu 3820dafa5cdSNeel Natu error = clock_ct_to_ts(&ct, &ts); 3830dafa5cdSNeel Natu if (error || ts.tv_sec < 0) { 3840dafa5cdSNeel Natu VM_CTR3(vm, "Invalid RTC clocktime.date %04d-%02d-%02d", 3850dafa5cdSNeel Natu ct.year, ct.mon, ct.day); 3860dafa5cdSNeel Natu VM_CTR3(vm, "Invalid RTC clocktime.time %02d:%02d:%02d", 3870dafa5cdSNeel Natu ct.hour, ct.min, ct.sec); 3880dafa5cdSNeel Natu goto fail; 3890dafa5cdSNeel Natu } 3900dafa5cdSNeel Natu return (ts.tv_sec); /* success */ 3910dafa5cdSNeel Natu fail: 392f39630c2SNeel Natu /* 393f39630c2SNeel Natu * Stop updating the RTC if the date/time fields programmed by 394f39630c2SNeel Natu * the guest are invalid. 395f39630c2SNeel Natu */ 396f39630c2SNeel Natu VM_CTR0(vrtc->vm, "Invalid RTC date/time programming detected"); 397f39630c2SNeel Natu return (VRTC_BROKEN_TIME); 3980dafa5cdSNeel Natu } 3990dafa5cdSNeel Natu 4000dafa5cdSNeel Natu static int 401787fb3d0SNeel Natu vrtc_time_update(struct vrtc *vrtc, time_t newtime, sbintime_t newbase) 4020dafa5cdSNeel Natu { 4030dafa5cdSNeel Natu struct rtcdev *rtc; 4040dafa5cdSNeel Natu time_t oldtime; 4050dafa5cdSNeel Natu uint8_t alarm_sec, alarm_min, alarm_hour; 4060dafa5cdSNeel Natu 4070dafa5cdSNeel Natu KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 4080dafa5cdSNeel Natu 4090dafa5cdSNeel Natu rtc = &vrtc->rtcdev; 4100dafa5cdSNeel Natu alarm_sec = rtc->alarm_sec; 4110dafa5cdSNeel Natu alarm_min = rtc->alarm_min; 4120dafa5cdSNeel Natu alarm_hour = rtc->alarm_hour; 4130dafa5cdSNeel Natu 4140dafa5cdSNeel Natu oldtime = vrtc->base_rtctime; 415787fb3d0SNeel Natu VM_CTR2(vrtc->vm, "Updating RTC secs from %#lx to %#lx", 4160dafa5cdSNeel Natu oldtime, newtime); 4170dafa5cdSNeel Natu 418787fb3d0SNeel Natu VM_CTR2(vrtc->vm, "Updating RTC base uptime from %#lx to %#lx", 419572edd3dSJohn Baldwin vrtc->base_uptime, newbase); 420787fb3d0SNeel Natu vrtc->base_uptime = newbase; 421787fb3d0SNeel Natu 4220dafa5cdSNeel Natu if (newtime == oldtime) 4230dafa5cdSNeel Natu return (0); 4240dafa5cdSNeel Natu 4250dafa5cdSNeel Natu /* 4260dafa5cdSNeel Natu * If 'newtime' indicates that RTC updates are disabled then just 4270dafa5cdSNeel Natu * record that and return. There is no need to do alarm interrupt 428787fb3d0SNeel Natu * processing in this case. 4290dafa5cdSNeel Natu */ 4300dafa5cdSNeel Natu if (newtime == VRTC_BROKEN_TIME) { 4310dafa5cdSNeel Natu vrtc->base_rtctime = VRTC_BROKEN_TIME; 4320dafa5cdSNeel Natu return (0); 4330dafa5cdSNeel Natu } 4340dafa5cdSNeel Natu 4350dafa5cdSNeel Natu /* 4360dafa5cdSNeel Natu * Return an error if RTC updates are halted by the guest. 4370dafa5cdSNeel Natu */ 4380dafa5cdSNeel Natu if (rtc_halted(vrtc)) { 4390dafa5cdSNeel Natu VM_CTR0(vrtc->vm, "RTC update halted by guest"); 4400dafa5cdSNeel Natu return (EBUSY); 4410dafa5cdSNeel Natu } 4420dafa5cdSNeel Natu 4430dafa5cdSNeel Natu do { 4440dafa5cdSNeel Natu /* 4450dafa5cdSNeel Natu * If the alarm interrupt is enabled and 'oldtime' is valid 4460dafa5cdSNeel Natu * then visit all the seconds between 'oldtime' and 'newtime' 4470dafa5cdSNeel Natu * to check for the alarm condition. 4480dafa5cdSNeel Natu * 4490dafa5cdSNeel Natu * Otherwise move the RTC time forward directly to 'newtime'. 4500dafa5cdSNeel Natu */ 4510dafa5cdSNeel Natu if (aintr_enabled(vrtc) && oldtime != VRTC_BROKEN_TIME) 4520dafa5cdSNeel Natu vrtc->base_rtctime++; 4530dafa5cdSNeel Natu else 4540dafa5cdSNeel Natu vrtc->base_rtctime = newtime; 4550dafa5cdSNeel Natu 4560dafa5cdSNeel Natu if (aintr_enabled(vrtc)) { 4570dafa5cdSNeel Natu /* 4580dafa5cdSNeel Natu * Update the RTC date/time fields before checking 4590dafa5cdSNeel Natu * if the alarm conditions are satisfied. 4600dafa5cdSNeel Natu */ 4610dafa5cdSNeel Natu secs_to_rtc(vrtc->base_rtctime, vrtc, 0); 4620dafa5cdSNeel Natu 4630dafa5cdSNeel Natu if ((alarm_sec >= 0xC0 || alarm_sec == rtc->sec) && 4640dafa5cdSNeel Natu (alarm_min >= 0xC0 || alarm_min == rtc->min) && 4650dafa5cdSNeel Natu (alarm_hour >= 0xC0 || alarm_hour == rtc->hour)) { 4660dafa5cdSNeel Natu vrtc_set_reg_c(vrtc, rtc->reg_c | RTCIR_ALARM); 4670dafa5cdSNeel Natu } 4680dafa5cdSNeel Natu } 4690dafa5cdSNeel Natu } while (vrtc->base_rtctime != newtime); 4700dafa5cdSNeel Natu 4710dafa5cdSNeel Natu if (uintr_enabled(vrtc)) 4720dafa5cdSNeel Natu vrtc_set_reg_c(vrtc, rtc->reg_c | RTCIR_UPDATE); 4730dafa5cdSNeel Natu 4740dafa5cdSNeel Natu return (0); 4750dafa5cdSNeel Natu } 4760dafa5cdSNeel Natu 4770dafa5cdSNeel Natu static sbintime_t 4780dafa5cdSNeel Natu vrtc_freq(struct vrtc *vrtc) 4790dafa5cdSNeel Natu { 4800dafa5cdSNeel Natu int ratesel; 4810dafa5cdSNeel Natu 4820dafa5cdSNeel Natu static sbintime_t pf[16] = { 4830dafa5cdSNeel Natu 0, 4840dafa5cdSNeel Natu SBT_1S / 256, 4850dafa5cdSNeel Natu SBT_1S / 128, 4860dafa5cdSNeel Natu SBT_1S / 8192, 4870dafa5cdSNeel Natu SBT_1S / 4096, 4880dafa5cdSNeel Natu SBT_1S / 2048, 4890dafa5cdSNeel Natu SBT_1S / 1024, 4900dafa5cdSNeel Natu SBT_1S / 512, 4910dafa5cdSNeel Natu SBT_1S / 256, 4920dafa5cdSNeel Natu SBT_1S / 128, 4930dafa5cdSNeel Natu SBT_1S / 64, 4940dafa5cdSNeel Natu SBT_1S / 32, 4950dafa5cdSNeel Natu SBT_1S / 16, 4960dafa5cdSNeel Natu SBT_1S / 8, 4970dafa5cdSNeel Natu SBT_1S / 4, 4980dafa5cdSNeel Natu SBT_1S / 2, 4990dafa5cdSNeel Natu }; 5000dafa5cdSNeel Natu 5010dafa5cdSNeel Natu KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 5020dafa5cdSNeel Natu 5030dafa5cdSNeel Natu /* 5040dafa5cdSNeel Natu * If both periodic and alarm interrupts are enabled then use the 5050dafa5cdSNeel Natu * periodic frequency to drive the callout. The minimum periodic 5060dafa5cdSNeel Natu * frequency (2 Hz) is higher than the alarm frequency (1 Hz) so 5070dafa5cdSNeel Natu * piggyback the alarm on top of it. The same argument applies to 5080dafa5cdSNeel Natu * the update interrupt. 5090dafa5cdSNeel Natu */ 5100dafa5cdSNeel Natu if (pintr_enabled(vrtc) && divider_enabled(vrtc->rtcdev.reg_a)) { 5110dafa5cdSNeel Natu ratesel = vrtc->rtcdev.reg_a & 0xf; 5120dafa5cdSNeel Natu return (pf[ratesel]); 5130dafa5cdSNeel Natu } else if (aintr_enabled(vrtc) && update_enabled(vrtc)) { 5140dafa5cdSNeel Natu return (SBT_1S); 5150dafa5cdSNeel Natu } else if (uintr_enabled(vrtc) && update_enabled(vrtc)) { 5160dafa5cdSNeel Natu return (SBT_1S); 5170dafa5cdSNeel Natu } else { 5180dafa5cdSNeel Natu return (0); 5190dafa5cdSNeel Natu } 5200dafa5cdSNeel Natu } 5210dafa5cdSNeel Natu 5220dafa5cdSNeel Natu static void 5230dafa5cdSNeel Natu vrtc_callout_reset(struct vrtc *vrtc, sbintime_t freqsbt) 5240dafa5cdSNeel Natu { 5250dafa5cdSNeel Natu 5260dafa5cdSNeel Natu KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 5270dafa5cdSNeel Natu 5280dafa5cdSNeel Natu if (freqsbt == 0) { 5290dafa5cdSNeel Natu if (callout_active(&vrtc->callout)) { 5300dafa5cdSNeel Natu VM_CTR0(vrtc->vm, "RTC callout stopped"); 5310dafa5cdSNeel Natu callout_stop(&vrtc->callout); 5320dafa5cdSNeel Natu } 5330dafa5cdSNeel Natu return; 5340dafa5cdSNeel Natu } 5350dafa5cdSNeel Natu VM_CTR1(vrtc->vm, "RTC callout frequency %d hz", SBT_1S / freqsbt); 5360dafa5cdSNeel Natu callout_reset_sbt(&vrtc->callout, freqsbt, 0, vrtc_callout_handler, 5370dafa5cdSNeel Natu vrtc, 0); 5380dafa5cdSNeel Natu } 5390dafa5cdSNeel Natu 5400dafa5cdSNeel Natu static void 5410dafa5cdSNeel Natu vrtc_callout_handler(void *arg) 5420dafa5cdSNeel Natu { 5430dafa5cdSNeel Natu struct vrtc *vrtc = arg; 544787fb3d0SNeel Natu sbintime_t freqsbt, basetime; 5450dafa5cdSNeel Natu time_t rtctime; 5462062ce99SRobert Wing int error __diagused; 5470dafa5cdSNeel Natu 5480dafa5cdSNeel Natu VM_CTR0(vrtc->vm, "vrtc callout fired"); 5490dafa5cdSNeel Natu 5500dafa5cdSNeel Natu VRTC_LOCK(vrtc); 5510dafa5cdSNeel Natu if (callout_pending(&vrtc->callout)) /* callout was reset */ 5520dafa5cdSNeel Natu goto done; 5530dafa5cdSNeel Natu 5540dafa5cdSNeel Natu if (!callout_active(&vrtc->callout)) /* callout was stopped */ 5550dafa5cdSNeel Natu goto done; 5560dafa5cdSNeel Natu 5570dafa5cdSNeel Natu callout_deactivate(&vrtc->callout); 5580dafa5cdSNeel Natu 5590dafa5cdSNeel Natu KASSERT((vrtc->rtcdev.reg_b & RTCSB_ALL_INTRS) != 0, 5600dafa5cdSNeel Natu ("gratuitous vrtc callout")); 5610dafa5cdSNeel Natu 5620dafa5cdSNeel Natu if (pintr_enabled(vrtc)) 5630dafa5cdSNeel Natu vrtc_set_reg_c(vrtc, vrtc->rtcdev.reg_c | RTCIR_PERIOD); 5640dafa5cdSNeel Natu 5650dafa5cdSNeel Natu if (aintr_enabled(vrtc) || uintr_enabled(vrtc)) { 566787fb3d0SNeel Natu rtctime = vrtc_curtime(vrtc, &basetime); 567787fb3d0SNeel Natu error = vrtc_time_update(vrtc, rtctime, basetime); 5680dafa5cdSNeel Natu KASSERT(error == 0, ("%s: vrtc_time_update error %d", 5690dafa5cdSNeel Natu __func__, error)); 5700dafa5cdSNeel Natu } 5710dafa5cdSNeel Natu 5720dafa5cdSNeel Natu freqsbt = vrtc_freq(vrtc); 5730dafa5cdSNeel Natu KASSERT(freqsbt != 0, ("%s: vrtc frequency cannot be zero", __func__)); 5740dafa5cdSNeel Natu vrtc_callout_reset(vrtc, freqsbt); 5750dafa5cdSNeel Natu done: 5760dafa5cdSNeel Natu VRTC_UNLOCK(vrtc); 5770dafa5cdSNeel Natu } 5780dafa5cdSNeel Natu 5790dafa5cdSNeel Natu static __inline void 5800dafa5cdSNeel Natu vrtc_callout_check(struct vrtc *vrtc, sbintime_t freq) 5810dafa5cdSNeel Natu { 5822062ce99SRobert Wing int active __diagused; 5830dafa5cdSNeel Natu 5840dafa5cdSNeel Natu active = callout_active(&vrtc->callout) ? 1 : 0; 5850dafa5cdSNeel Natu KASSERT((freq == 0 && !active) || (freq != 0 && active), 5860dafa5cdSNeel Natu ("vrtc callout %s with frequency %#lx", 5870dafa5cdSNeel Natu active ? "active" : "inactive", freq)); 5880dafa5cdSNeel Natu } 5890dafa5cdSNeel Natu 5900dafa5cdSNeel Natu static void 5910dafa5cdSNeel Natu vrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval) 5920dafa5cdSNeel Natu { 5930dafa5cdSNeel Natu struct rtcdev *rtc; 5940dafa5cdSNeel Natu int oldirqf, newirqf; 5950dafa5cdSNeel Natu uint8_t oldval, changed; 5960dafa5cdSNeel Natu 5970dafa5cdSNeel Natu KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 5980dafa5cdSNeel Natu 5990dafa5cdSNeel Natu rtc = &vrtc->rtcdev; 6000dafa5cdSNeel Natu newval &= RTCIR_ALARM | RTCIR_PERIOD | RTCIR_UPDATE; 6010dafa5cdSNeel Natu 6020dafa5cdSNeel Natu oldirqf = rtc->reg_c & RTCIR_INT; 6030dafa5cdSNeel Natu if ((aintr_enabled(vrtc) && (newval & RTCIR_ALARM) != 0) || 6040dafa5cdSNeel Natu (pintr_enabled(vrtc) && (newval & RTCIR_PERIOD) != 0) || 6050dafa5cdSNeel Natu (uintr_enabled(vrtc) && (newval & RTCIR_UPDATE) != 0)) { 6060dafa5cdSNeel Natu newirqf = RTCIR_INT; 6070dafa5cdSNeel Natu } else { 6080dafa5cdSNeel Natu newirqf = 0; 6090dafa5cdSNeel Natu } 6100dafa5cdSNeel Natu 6110dafa5cdSNeel Natu oldval = rtc->reg_c; 6120dafa5cdSNeel Natu rtc->reg_c = newirqf | newval; 6130dafa5cdSNeel Natu changed = oldval ^ rtc->reg_c; 6140dafa5cdSNeel Natu if (changed) { 6150dafa5cdSNeel Natu VM_CTR2(vrtc->vm, "RTC reg_c changed from %#x to %#x", 6160dafa5cdSNeel Natu oldval, rtc->reg_c); 6170dafa5cdSNeel Natu } 6180dafa5cdSNeel Natu 6190dafa5cdSNeel Natu if (!oldirqf && newirqf) { 6200dafa5cdSNeel Natu VM_CTR1(vrtc->vm, "RTC irq %d asserted", RTC_IRQ); 6210dafa5cdSNeel Natu vatpic_pulse_irq(vrtc->vm, RTC_IRQ); 6220dafa5cdSNeel Natu vioapic_pulse_irq(vrtc->vm, RTC_IRQ); 6230dafa5cdSNeel Natu } else if (oldirqf && !newirqf) { 6240dafa5cdSNeel Natu VM_CTR1(vrtc->vm, "RTC irq %d deasserted", RTC_IRQ); 6250dafa5cdSNeel Natu } 6260dafa5cdSNeel Natu } 6270dafa5cdSNeel Natu 6280dafa5cdSNeel Natu static int 6290dafa5cdSNeel Natu vrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval) 6300dafa5cdSNeel Natu { 6310dafa5cdSNeel Natu struct rtcdev *rtc; 632787fb3d0SNeel Natu sbintime_t oldfreq, newfreq, basetime; 6330dafa5cdSNeel Natu time_t curtime, rtctime; 6342062ce99SRobert Wing int error __diagused; 6350dafa5cdSNeel Natu uint8_t oldval, changed; 6360dafa5cdSNeel Natu 6370dafa5cdSNeel Natu KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 6380dafa5cdSNeel Natu 6390dafa5cdSNeel Natu rtc = &vrtc->rtcdev; 6400dafa5cdSNeel Natu oldval = rtc->reg_b; 6410dafa5cdSNeel Natu oldfreq = vrtc_freq(vrtc); 6420dafa5cdSNeel Natu 6430dafa5cdSNeel Natu rtc->reg_b = newval; 6440dafa5cdSNeel Natu changed = oldval ^ newval; 6450dafa5cdSNeel Natu if (changed) { 6460dafa5cdSNeel Natu VM_CTR2(vrtc->vm, "RTC reg_b changed from %#x to %#x", 6470dafa5cdSNeel Natu oldval, newval); 6480dafa5cdSNeel Natu } 6490dafa5cdSNeel Natu 6500dafa5cdSNeel Natu if (changed & RTCSB_HALT) { 6510dafa5cdSNeel Natu if ((newval & RTCSB_HALT) == 0) { 6520dafa5cdSNeel Natu rtctime = rtc_to_secs(vrtc); 653787fb3d0SNeel Natu basetime = sbinuptime(); 6540dafa5cdSNeel Natu if (rtctime == VRTC_BROKEN_TIME) { 6550dafa5cdSNeel Natu if (rtc_flag_broken_time) 6560dafa5cdSNeel Natu return (-1); 6570dafa5cdSNeel Natu } 6580dafa5cdSNeel Natu } else { 659787fb3d0SNeel Natu curtime = vrtc_curtime(vrtc, &basetime); 6600dafa5cdSNeel Natu KASSERT(curtime == vrtc->base_rtctime, ("%s: mismatch " 6610dafa5cdSNeel Natu "between vrtc basetime (%#lx) and curtime (%#lx)", 6620dafa5cdSNeel Natu __func__, vrtc->base_rtctime, curtime)); 6630dafa5cdSNeel Natu 6640dafa5cdSNeel Natu /* 6650dafa5cdSNeel Natu * Force a refresh of the RTC date/time fields so 6660dafa5cdSNeel Natu * they reflect the time right before the guest set 6670dafa5cdSNeel Natu * the HALT bit. 6680dafa5cdSNeel Natu */ 6690dafa5cdSNeel Natu secs_to_rtc(curtime, vrtc, 1); 6700dafa5cdSNeel Natu 6710dafa5cdSNeel Natu /* 6720dafa5cdSNeel Natu * Updates are halted so mark 'base_rtctime' to denote 6730dafa5cdSNeel Natu * that the RTC date/time is in flux. 6740dafa5cdSNeel Natu */ 6750dafa5cdSNeel Natu rtctime = VRTC_BROKEN_TIME; 6760dafa5cdSNeel Natu rtc->reg_b &= ~RTCSB_UINTR; 6770dafa5cdSNeel Natu } 678787fb3d0SNeel Natu error = vrtc_time_update(vrtc, rtctime, basetime); 6790dafa5cdSNeel Natu KASSERT(error == 0, ("vrtc_time_update error %d", error)); 6800dafa5cdSNeel Natu } 6810dafa5cdSNeel Natu 6820dafa5cdSNeel Natu /* 6830dafa5cdSNeel Natu * Side effect of changes to the interrupt enable bits. 6840dafa5cdSNeel Natu */ 6850dafa5cdSNeel Natu if (changed & RTCSB_ALL_INTRS) 6860dafa5cdSNeel Natu vrtc_set_reg_c(vrtc, vrtc->rtcdev.reg_c); 6870dafa5cdSNeel Natu 6880dafa5cdSNeel Natu /* 6890dafa5cdSNeel Natu * Change the callout frequency if it has changed. 6900dafa5cdSNeel Natu */ 6910dafa5cdSNeel Natu newfreq = vrtc_freq(vrtc); 6920dafa5cdSNeel Natu if (newfreq != oldfreq) 6930dafa5cdSNeel Natu vrtc_callout_reset(vrtc, newfreq); 6940dafa5cdSNeel Natu else 6950dafa5cdSNeel Natu vrtc_callout_check(vrtc, newfreq); 6960dafa5cdSNeel Natu 6970dafa5cdSNeel Natu /* 6980dafa5cdSNeel Natu * The side effect of bits that control the RTC date/time format 6990dafa5cdSNeel Natu * is handled lazily when those fields are actually read. 7000dafa5cdSNeel Natu */ 7010dafa5cdSNeel Natu return (0); 7020dafa5cdSNeel Natu } 7030dafa5cdSNeel Natu 7040dafa5cdSNeel Natu static void 7050dafa5cdSNeel Natu vrtc_set_reg_a(struct vrtc *vrtc, uint8_t newval) 7060dafa5cdSNeel Natu { 7070dafa5cdSNeel Natu sbintime_t oldfreq, newfreq; 7080dafa5cdSNeel Natu uint8_t oldval, changed; 7090dafa5cdSNeel Natu 7100dafa5cdSNeel Natu KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__)); 7110dafa5cdSNeel Natu 7120dafa5cdSNeel Natu newval &= ~RTCSA_TUP; 7130dafa5cdSNeel Natu oldval = vrtc->rtcdev.reg_a; 7140dafa5cdSNeel Natu oldfreq = vrtc_freq(vrtc); 7150dafa5cdSNeel Natu 7160dafa5cdSNeel Natu if (divider_enabled(oldval) && !divider_enabled(newval)) { 7170dafa5cdSNeel Natu VM_CTR2(vrtc->vm, "RTC divider held in reset at %#lx/%#lx", 7180dafa5cdSNeel Natu vrtc->base_rtctime, vrtc->base_uptime); 7190dafa5cdSNeel Natu } else if (!divider_enabled(oldval) && divider_enabled(newval)) { 7200dafa5cdSNeel Natu /* 7210dafa5cdSNeel Natu * If the dividers are coming out of reset then update 7220dafa5cdSNeel Natu * 'base_uptime' before this happens. This is done to 7230dafa5cdSNeel Natu * maintain the illusion that the RTC date/time was frozen 7240dafa5cdSNeel Natu * while the dividers were disabled. 7250dafa5cdSNeel Natu */ 7260dafa5cdSNeel Natu vrtc->base_uptime = sbinuptime(); 7270dafa5cdSNeel Natu VM_CTR2(vrtc->vm, "RTC divider out of reset at %#lx/%#lx", 7280dafa5cdSNeel Natu vrtc->base_rtctime, vrtc->base_uptime); 7290dafa5cdSNeel Natu } else { 7300dafa5cdSNeel Natu /* NOTHING */ 7310dafa5cdSNeel Natu } 7320dafa5cdSNeel Natu 7330dafa5cdSNeel Natu vrtc->rtcdev.reg_a = newval; 7340dafa5cdSNeel Natu changed = oldval ^ newval; 7350dafa5cdSNeel Natu if (changed) { 7360dafa5cdSNeel Natu VM_CTR2(vrtc->vm, "RTC reg_a changed from %#x to %#x", 7370dafa5cdSNeel Natu oldval, newval); 7380dafa5cdSNeel Natu } 7390dafa5cdSNeel Natu 7400dafa5cdSNeel Natu /* 7410dafa5cdSNeel Natu * Side effect of changes to rate select and divider enable bits. 7420dafa5cdSNeel Natu */ 7430dafa5cdSNeel Natu newfreq = vrtc_freq(vrtc); 7440dafa5cdSNeel Natu if (newfreq != oldfreq) 7450dafa5cdSNeel Natu vrtc_callout_reset(vrtc, newfreq); 7460dafa5cdSNeel Natu else 7470dafa5cdSNeel Natu vrtc_callout_check(vrtc, newfreq); 7480dafa5cdSNeel Natu } 7490dafa5cdSNeel Natu 7500dafa5cdSNeel Natu int 7510dafa5cdSNeel Natu vrtc_set_time(struct vm *vm, time_t secs) 7520dafa5cdSNeel Natu { 7530dafa5cdSNeel Natu struct vrtc *vrtc; 7540dafa5cdSNeel Natu int error; 7550dafa5cdSNeel Natu 7560dafa5cdSNeel Natu vrtc = vm_rtc(vm); 7570dafa5cdSNeel Natu VRTC_LOCK(vrtc); 758787fb3d0SNeel Natu error = vrtc_time_update(vrtc, secs, sbinuptime()); 7590dafa5cdSNeel Natu VRTC_UNLOCK(vrtc); 7600dafa5cdSNeel Natu 7610dafa5cdSNeel Natu if (error) { 7620dafa5cdSNeel Natu VM_CTR2(vrtc->vm, "Error %d setting RTC time to %#lx", error, 7630dafa5cdSNeel Natu secs); 7640dafa5cdSNeel Natu } else { 7650dafa5cdSNeel Natu VM_CTR1(vrtc->vm, "RTC time set to %#lx", secs); 7660dafa5cdSNeel Natu } 7670dafa5cdSNeel Natu 7680dafa5cdSNeel Natu return (error); 7690dafa5cdSNeel Natu } 7700dafa5cdSNeel Natu 7710dafa5cdSNeel Natu time_t 7720dafa5cdSNeel Natu vrtc_get_time(struct vm *vm) 7730dafa5cdSNeel Natu { 7740dafa5cdSNeel Natu struct vrtc *vrtc; 775787fb3d0SNeel Natu sbintime_t basetime; 7760dafa5cdSNeel Natu time_t t; 7770dafa5cdSNeel Natu 7780dafa5cdSNeel Natu vrtc = vm_rtc(vm); 7790dafa5cdSNeel Natu VRTC_LOCK(vrtc); 780787fb3d0SNeel Natu t = vrtc_curtime(vrtc, &basetime); 7810dafa5cdSNeel Natu VRTC_UNLOCK(vrtc); 7820dafa5cdSNeel Natu 7830dafa5cdSNeel Natu return (t); 7840dafa5cdSNeel Natu } 7850dafa5cdSNeel Natu 7860dafa5cdSNeel Natu int 7870dafa5cdSNeel Natu vrtc_nvram_write(struct vm *vm, int offset, uint8_t value) 7880dafa5cdSNeel Natu { 7890dafa5cdSNeel Natu struct vrtc *vrtc; 7900dafa5cdSNeel Natu uint8_t *ptr; 7910dafa5cdSNeel Natu 7920dafa5cdSNeel Natu vrtc = vm_rtc(vm); 7930dafa5cdSNeel Natu 7940dafa5cdSNeel Natu /* 7950dafa5cdSNeel Natu * Don't allow writes to RTC control registers or the date/time fields. 7960dafa5cdSNeel Natu */ 7970dafa5cdSNeel Natu if (offset < offsetof(struct rtcdev, nvram[0]) || 798f39630c2SNeel Natu offset == RTC_CENTURY || offset >= sizeof(struct rtcdev)) { 7990dafa5cdSNeel Natu VM_CTR1(vrtc->vm, "RTC nvram write to invalid offset %d", 8000dafa5cdSNeel Natu offset); 8010dafa5cdSNeel Natu return (EINVAL); 8020dafa5cdSNeel Natu } 8030dafa5cdSNeel Natu 8040dafa5cdSNeel Natu VRTC_LOCK(vrtc); 8050dafa5cdSNeel Natu ptr = (uint8_t *)(&vrtc->rtcdev); 8060dafa5cdSNeel Natu ptr[offset] = value; 8070dafa5cdSNeel Natu VM_CTR2(vrtc->vm, "RTC nvram write %#x to offset %#x", value, offset); 8080dafa5cdSNeel Natu VRTC_UNLOCK(vrtc); 8090dafa5cdSNeel Natu 8100dafa5cdSNeel Natu return (0); 8110dafa5cdSNeel Natu } 8120dafa5cdSNeel Natu 8130dafa5cdSNeel Natu int 8140dafa5cdSNeel Natu vrtc_nvram_read(struct vm *vm, int offset, uint8_t *retval) 8150dafa5cdSNeel Natu { 8160dafa5cdSNeel Natu struct vrtc *vrtc; 817787fb3d0SNeel Natu sbintime_t basetime; 8180dafa5cdSNeel Natu time_t curtime; 8190dafa5cdSNeel Natu uint8_t *ptr; 8200dafa5cdSNeel Natu 8210dafa5cdSNeel Natu /* 8220dafa5cdSNeel Natu * Allow all offsets in the RTC to be read. 8230dafa5cdSNeel Natu */ 8240dafa5cdSNeel Natu if (offset < 0 || offset >= sizeof(struct rtcdev)) 8250dafa5cdSNeel Natu return (EINVAL); 8260dafa5cdSNeel Natu 8270dafa5cdSNeel Natu vrtc = vm_rtc(vm); 8280dafa5cdSNeel Natu VRTC_LOCK(vrtc); 8290dafa5cdSNeel Natu 8300dafa5cdSNeel Natu /* 8310dafa5cdSNeel Natu * Update RTC date/time fields if necessary. 8320dafa5cdSNeel Natu */ 833f39630c2SNeel Natu if (offset < 10 || offset == RTC_CENTURY) { 834787fb3d0SNeel Natu curtime = vrtc_curtime(vrtc, &basetime); 8350dafa5cdSNeel Natu secs_to_rtc(curtime, vrtc, 0); 8360dafa5cdSNeel Natu } 8370dafa5cdSNeel Natu 8380dafa5cdSNeel Natu ptr = (uint8_t *)(&vrtc->rtcdev); 8390dafa5cdSNeel Natu *retval = ptr[offset]; 8400dafa5cdSNeel Natu 8410dafa5cdSNeel Natu VRTC_UNLOCK(vrtc); 8420dafa5cdSNeel Natu return (0); 8430dafa5cdSNeel Natu } 8440dafa5cdSNeel Natu 8450dafa5cdSNeel Natu int 8469388bc1eSJohn Baldwin vrtc_addr_handler(struct vm *vm, bool in, int port, int bytes, uint32_t *val) 8470dafa5cdSNeel Natu { 8480dafa5cdSNeel Natu struct vrtc *vrtc; 8490dafa5cdSNeel Natu 8500dafa5cdSNeel Natu vrtc = vm_rtc(vm); 8510dafa5cdSNeel Natu 8520dafa5cdSNeel Natu if (bytes != 1) 8530dafa5cdSNeel Natu return (-1); 8540dafa5cdSNeel Natu 8550dafa5cdSNeel Natu if (in) { 8560dafa5cdSNeel Natu *val = 0xff; 8570dafa5cdSNeel Natu return (0); 8580dafa5cdSNeel Natu } 8590dafa5cdSNeel Natu 8600dafa5cdSNeel Natu VRTC_LOCK(vrtc); 8610dafa5cdSNeel Natu vrtc->addr = *val & 0x7f; 8620dafa5cdSNeel Natu VRTC_UNLOCK(vrtc); 8630dafa5cdSNeel Natu 8640dafa5cdSNeel Natu return (0); 8650dafa5cdSNeel Natu } 8660dafa5cdSNeel Natu 8670dafa5cdSNeel Natu int 8689388bc1eSJohn Baldwin vrtc_data_handler(struct vm *vm, bool in, int port, int bytes, uint32_t *val) 8690dafa5cdSNeel Natu { 8700dafa5cdSNeel Natu struct vrtc *vrtc; 8710dafa5cdSNeel Natu struct rtcdev *rtc; 872787fb3d0SNeel Natu sbintime_t basetime; 8730dafa5cdSNeel Natu time_t curtime; 8740dafa5cdSNeel Natu int error, offset; 8750dafa5cdSNeel Natu 8760dafa5cdSNeel Natu vrtc = vm_rtc(vm); 8770dafa5cdSNeel Natu rtc = &vrtc->rtcdev; 8780dafa5cdSNeel Natu 8790dafa5cdSNeel Natu if (bytes != 1) 8800dafa5cdSNeel Natu return (-1); 8810dafa5cdSNeel Natu 8820dafa5cdSNeel Natu VRTC_LOCK(vrtc); 8830dafa5cdSNeel Natu offset = vrtc->addr; 8840dafa5cdSNeel Natu if (offset >= sizeof(struct rtcdev)) { 8850dafa5cdSNeel Natu VRTC_UNLOCK(vrtc); 8860dafa5cdSNeel Natu return (-1); 8870dafa5cdSNeel Natu } 8880dafa5cdSNeel Natu 8890dafa5cdSNeel Natu error = 0; 890787fb3d0SNeel Natu curtime = vrtc_curtime(vrtc, &basetime); 891787fb3d0SNeel Natu vrtc_time_update(vrtc, curtime, basetime); 8920dafa5cdSNeel Natu 8930dafa5cdSNeel Natu /* 8940dafa5cdSNeel Natu * Update RTC date/time fields if necessary. 895f39630c2SNeel Natu * 896f39630c2SNeel Natu * This is not just for reads of the RTC. The side-effect of writing 897f39630c2SNeel Natu * the century byte requires other RTC date/time fields (e.g. sec) 898f39630c2SNeel Natu * to be updated here. 8990dafa5cdSNeel Natu */ 900f39630c2SNeel Natu if (offset < 10 || offset == RTC_CENTURY) 9010dafa5cdSNeel Natu secs_to_rtc(curtime, vrtc, 0); 9020dafa5cdSNeel Natu 903f39630c2SNeel Natu if (in) { 9040dafa5cdSNeel Natu if (offset == 12) { 9050dafa5cdSNeel Natu /* 9060dafa5cdSNeel Natu * XXX 9070dafa5cdSNeel Natu * reg_c interrupt flags are updated only if the 9080dafa5cdSNeel Natu * corresponding interrupt enable bit in reg_b is set. 9090dafa5cdSNeel Natu */ 9100dafa5cdSNeel Natu *val = vrtc->rtcdev.reg_c; 9110dafa5cdSNeel Natu vrtc_set_reg_c(vrtc, 0); 9120dafa5cdSNeel Natu } else { 9130dafa5cdSNeel Natu *val = *((uint8_t *)rtc + offset); 9140dafa5cdSNeel Natu } 9159388bc1eSJohn Baldwin VM_CTR2(vm, "Read value %#x from RTC offset %#x", 9160dafa5cdSNeel Natu *val, offset); 9170dafa5cdSNeel Natu } else { 9180dafa5cdSNeel Natu switch (offset) { 9190dafa5cdSNeel Natu case 10: 9209388bc1eSJohn Baldwin VM_CTR1(vm, "RTC reg_a set to %#x", *val); 9210dafa5cdSNeel Natu vrtc_set_reg_a(vrtc, *val); 9220dafa5cdSNeel Natu break; 9230dafa5cdSNeel Natu case 11: 9249388bc1eSJohn Baldwin VM_CTR1(vm, "RTC reg_b set to %#x", *val); 9250dafa5cdSNeel Natu error = vrtc_set_reg_b(vrtc, *val); 9260dafa5cdSNeel Natu break; 9270dafa5cdSNeel Natu case 12: 9289388bc1eSJohn Baldwin VM_CTR1(vm, "RTC reg_c set to %#x (ignored)", 9290dafa5cdSNeel Natu *val); 9300dafa5cdSNeel Natu break; 9310dafa5cdSNeel Natu case 13: 9329388bc1eSJohn Baldwin VM_CTR1(vm, "RTC reg_d set to %#x (ignored)", 9330dafa5cdSNeel Natu *val); 9340dafa5cdSNeel Natu break; 9350dafa5cdSNeel Natu case 0: 9360dafa5cdSNeel Natu /* 9370dafa5cdSNeel Natu * High order bit of 'seconds' is readonly. 9380dafa5cdSNeel Natu */ 9390dafa5cdSNeel Natu *val &= 0x7f; 9400dafa5cdSNeel Natu /* FALLTHRU */ 9410dafa5cdSNeel Natu default: 9429388bc1eSJohn Baldwin VM_CTR2(vm, "RTC offset %#x set to %#x", 9430dafa5cdSNeel Natu offset, *val); 9440dafa5cdSNeel Natu *((uint8_t *)rtc + offset) = *val; 9450dafa5cdSNeel Natu break; 9460dafa5cdSNeel Natu } 947f39630c2SNeel Natu 948f39630c2SNeel Natu /* 949f39630c2SNeel Natu * XXX some guests (e.g. OpenBSD) write the century byte 950f39630c2SNeel Natu * outside of RTCSB_HALT so re-calculate the RTC date/time. 951f39630c2SNeel Natu */ 952f39630c2SNeel Natu if (offset == RTC_CENTURY && !rtc_halted(vrtc)) { 953f39630c2SNeel Natu curtime = rtc_to_secs(vrtc); 954787fb3d0SNeel Natu error = vrtc_time_update(vrtc, curtime, sbinuptime()); 955f39630c2SNeel Natu KASSERT(!error, ("vrtc_time_update error %d", error)); 956f39630c2SNeel Natu if (curtime == VRTC_BROKEN_TIME && rtc_flag_broken_time) 957f39630c2SNeel Natu error = -1; 958f39630c2SNeel Natu } 9590dafa5cdSNeel Natu } 9600dafa5cdSNeel Natu VRTC_UNLOCK(vrtc); 9610dafa5cdSNeel Natu return (error); 9620dafa5cdSNeel Natu } 9630dafa5cdSNeel Natu 9640dafa5cdSNeel Natu void 9650dafa5cdSNeel Natu vrtc_reset(struct vrtc *vrtc) 9660dafa5cdSNeel Natu { 9670dafa5cdSNeel Natu struct rtcdev *rtc; 9680dafa5cdSNeel Natu 9690dafa5cdSNeel Natu VRTC_LOCK(vrtc); 9700dafa5cdSNeel Natu 9710dafa5cdSNeel Natu rtc = &vrtc->rtcdev; 9720dafa5cdSNeel Natu vrtc_set_reg_b(vrtc, rtc->reg_b & ~(RTCSB_ALL_INTRS | RTCSB_SQWE)); 9730dafa5cdSNeel Natu vrtc_set_reg_c(vrtc, 0); 9740dafa5cdSNeel Natu KASSERT(!callout_active(&vrtc->callout), ("rtc callout still active")); 9750dafa5cdSNeel Natu 9760dafa5cdSNeel Natu VRTC_UNLOCK(vrtc); 9770dafa5cdSNeel Natu } 9780dafa5cdSNeel Natu 9790dafa5cdSNeel Natu struct vrtc * 9800dafa5cdSNeel Natu vrtc_init(struct vm *vm) 9810dafa5cdSNeel Natu { 9820dafa5cdSNeel Natu struct vrtc *vrtc; 9830dafa5cdSNeel Natu struct rtcdev *rtc; 9840dafa5cdSNeel Natu time_t curtime; 9850dafa5cdSNeel Natu 9860dafa5cdSNeel Natu vrtc = malloc(sizeof(struct vrtc), M_VRTC, M_WAITOK | M_ZERO); 9870dafa5cdSNeel Natu vrtc->vm = vm; 9880dafa5cdSNeel Natu mtx_init(&vrtc->mtx, "vrtc lock", NULL, MTX_DEF); 9890dafa5cdSNeel Natu callout_init(&vrtc->callout, 1); 9900dafa5cdSNeel Natu 9910dafa5cdSNeel Natu /* Allow dividers to keep time but disable everything else */ 9920dafa5cdSNeel Natu rtc = &vrtc->rtcdev; 9930dafa5cdSNeel Natu rtc->reg_a = 0x20; 9940dafa5cdSNeel Natu rtc->reg_b = RTCSB_24HR; 9950dafa5cdSNeel Natu rtc->reg_c = 0; 9960dafa5cdSNeel Natu rtc->reg_d = RTCSD_PWR; 9970dafa5cdSNeel Natu 9980dafa5cdSNeel Natu /* Reset the index register to a safe value. */ 9990dafa5cdSNeel Natu vrtc->addr = RTC_STATUSD; 10000dafa5cdSNeel Natu 10010dafa5cdSNeel Natu /* 10020dafa5cdSNeel Natu * Initialize RTC time to 00:00:00 Jan 1, 1970. 10030dafa5cdSNeel Natu */ 10040dafa5cdSNeel Natu curtime = 0; 10050dafa5cdSNeel Natu 10060dafa5cdSNeel Natu VRTC_LOCK(vrtc); 10070dafa5cdSNeel Natu vrtc->base_rtctime = VRTC_BROKEN_TIME; 1008787fb3d0SNeel Natu vrtc_time_update(vrtc, curtime, sbinuptime()); 10090dafa5cdSNeel Natu secs_to_rtc(curtime, vrtc, 0); 10100dafa5cdSNeel Natu VRTC_UNLOCK(vrtc); 10110dafa5cdSNeel Natu 10120dafa5cdSNeel Natu return (vrtc); 10130dafa5cdSNeel Natu } 10140dafa5cdSNeel Natu 10150dafa5cdSNeel Natu void 10160dafa5cdSNeel Natu vrtc_cleanup(struct vrtc *vrtc) 10170dafa5cdSNeel Natu { 10180dafa5cdSNeel Natu 10190dafa5cdSNeel Natu callout_drain(&vrtc->callout); 102008ebb360SJohn Baldwin mtx_destroy(&vrtc->mtx); 10210dafa5cdSNeel Natu free(vrtc, M_VRTC); 10220dafa5cdSNeel Natu } 1023483d953aSJohn Baldwin 1024483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 1025483d953aSJohn Baldwin int 1026483d953aSJohn Baldwin vrtc_snapshot(struct vrtc *vrtc, struct vm_snapshot_meta *meta) 1027483d953aSJohn Baldwin { 1028483d953aSJohn Baldwin int ret; 1029483d953aSJohn Baldwin 1030483d953aSJohn Baldwin VRTC_LOCK(vrtc); 1031483d953aSJohn Baldwin 1032483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->addr, meta, ret, done); 1033483d953aSJohn Baldwin if (meta->op == VM_SNAPSHOT_RESTORE) 1034483d953aSJohn Baldwin vrtc->base_uptime = sbinuptime(); 1035483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->base_rtctime, meta, ret, done); 1036483d953aSJohn Baldwin 1037483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.sec, meta, ret, done); 1038483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.alarm_sec, meta, ret, done); 1039483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.min, meta, ret, done); 1040483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.alarm_min, meta, ret, done); 1041483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.hour, meta, ret, done); 1042483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.alarm_hour, meta, ret, done); 1043483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.day_of_week, meta, ret, done); 1044483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.day_of_month, meta, ret, done); 1045483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.month, meta, ret, done); 1046483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.year, meta, ret, done); 1047483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.reg_a, meta, ret, done); 1048483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.reg_b, meta, ret, done); 1049483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.reg_c, meta, ret, done); 1050483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.reg_d, meta, ret, done); 1051483d953aSJohn Baldwin SNAPSHOT_BUF_OR_LEAVE(vrtc->rtcdev.nvram, sizeof(vrtc->rtcdev.nvram), 1052483d953aSJohn Baldwin meta, ret, done); 1053483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.century, meta, ret, done); 1054483d953aSJohn Baldwin SNAPSHOT_BUF_OR_LEAVE(vrtc->rtcdev.nvram2, sizeof(vrtc->rtcdev.nvram2), 1055483d953aSJohn Baldwin meta, ret, done); 1056483d953aSJohn Baldwin 1057483d953aSJohn Baldwin vrtc_callout_reset(vrtc, vrtc_freq(vrtc)); 1058483d953aSJohn Baldwin 1059483d953aSJohn Baldwin VRTC_UNLOCK(vrtc); 1060483d953aSJohn Baldwin 1061483d953aSJohn Baldwin done: 1062483d953aSJohn Baldwin return (ret); 1063483d953aSJohn Baldwin } 1064483d953aSJohn Baldwin #endif 1065