1 /* $OpenBSD: mc146818.c,v 1.24 2021/06/16 16:55:02 dv Exp $ */ 2 /* 3 * Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 20 #include <dev/ic/mc146818reg.h> 21 #include <dev/isa/isareg.h> 22 23 #include <machine/vmmvar.h> 24 25 #include <event.h> 26 #include <stddef.h> 27 #include <string.h> 28 #include <time.h> 29 #include <unistd.h> 30 31 #include "atomicio.h" 32 #include "mc146818.h" 33 #include "virtio.h" 34 #include "vmd.h" 35 #include "vmm.h" 36 37 #define MC_DIVIDER_MASK 0xe0 38 #define MC_RATE_MASK 0xf 39 40 #define NVRAM_CENTURY 0x32 41 #define NVRAM_MEMSIZE_LO 0x34 42 #define NVRAM_MEMSIZE_HI 0x35 43 #define NVRAM_HIMEMSIZE_LO 0x5B 44 #define NVRAM_HIMEMSIZE_MID 0x5C 45 #define NVRAM_HIMEMSIZE_HI 0x5D 46 #define NVRAM_SMP_COUNT 0x5F 47 48 #define NVRAM_SIZE 0x60 49 50 #define TOBCD(x) (((x) / 10 * 16) + ((x) % 10)) 51 52 struct mc146818 { 53 time_t now; 54 uint8_t idx; 55 uint8_t regs[NVRAM_SIZE]; 56 uint32_t vm_id; 57 struct event sec; 58 struct timeval sec_tv; 59 struct event per; 60 struct timeval per_tv; 61 }; 62 63 struct mc146818 rtc; 64 65 static struct vm_dev_pipe dev_pipe; 66 67 static void rtc_reschedule_per(void); 68 69 /* 70 * mc146818_pipe_dispatch 71 * 72 * Reads a message off the pipe, expecting a request to reschedule periodic 73 * interrupt rate. 74 */ 75 static void 76 mc146818_pipe_dispatch(int fd, short event, void *arg) 77 { 78 enum pipe_msg_type msg; 79 80 msg = vm_pipe_recv(&dev_pipe); 81 if (msg == MC146818_RESCHEDULE_PER) { 82 rtc_reschedule_per(); 83 } else { 84 fatalx("%s: unexpected pipe message %d", __func__, msg); 85 } 86 } 87 88 /* 89 * rtc_updateregs 90 * 91 * Updates the RTC TOD bytes, reflecting 'now'. 92 */ 93 static void 94 rtc_updateregs(void) 95 { 96 struct tm *gnow; 97 98 rtc.regs[MC_REGD] &= ~MC_REGD_VRT; 99 gnow = gmtime(&rtc.now); 100 101 rtc.regs[MC_SEC] = TOBCD(gnow->tm_sec); 102 rtc.regs[MC_MIN] = TOBCD(gnow->tm_min); 103 rtc.regs[MC_HOUR] = TOBCD(gnow->tm_hour); 104 rtc.regs[MC_DOW] = TOBCD(gnow->tm_wday + 1); 105 rtc.regs[MC_DOM] = TOBCD(gnow->tm_mday); 106 rtc.regs[MC_MONTH] = TOBCD(gnow->tm_mon + 1); 107 rtc.regs[MC_YEAR] = TOBCD((gnow->tm_year + 1900) % 100); 108 rtc.regs[NVRAM_CENTURY] = TOBCD((gnow->tm_year + 1900) / 100); 109 rtc.regs[MC_REGD] |= MC_REGD_VRT; 110 } 111 112 /* 113 * rtc_fire1 114 * 115 * Callback for the 1s periodic TOD refresh timer 116 * 117 * Parameters: 118 * fd: unused 119 * type: unused 120 * arg: unused 121 */ 122 static void 123 rtc_fire1(int fd, short type, void *arg) 124 { 125 time_t old = rtc.now; 126 127 time(&rtc.now); 128 129 rtc_updateregs(); 130 if (rtc.now - old > 5) { 131 log_debug("%s: RTC clock drift (%llds), requesting guest " 132 "resync", __func__, (rtc.now - old)); 133 vmmci_ctl(VMMCI_SYNCRTC); 134 } 135 evtimer_add(&rtc.sec, &rtc.sec_tv); 136 } 137 138 /* 139 * rtc_fireper 140 * 141 * Callback for the periodic interrupt timer 142 * 143 * Parameters: 144 * fd: unused 145 * type: unused 146 * arg: (as uint32_t), VM ID to which this RTC belongs 147 */ 148 static void 149 rtc_fireper(int fd, short type, void *arg) 150 { 151 rtc.regs[MC_REGC] |= MC_REGC_PF; 152 153 vcpu_assert_pic_irq((ptrdiff_t)arg, 0, 8); 154 vcpu_deassert_pic_irq((ptrdiff_t)arg, 0, 8); 155 156 evtimer_add(&rtc.per, &rtc.per_tv); 157 } 158 159 /* 160 * mc146818_init 161 * 162 * Initializes the emulated RTC/NVRAM 163 * 164 * Parameters: 165 * vm_id: VM ID to which this RTC belongs 166 * memlo: size of memory in bytes between 16MB .. 4GB 167 * memhi: size of memory in bytes after 4GB 168 */ 169 void 170 mc146818_init(uint32_t vm_id, uint64_t memlo, uint64_t memhi) 171 { 172 memset(&rtc, 0, sizeof(rtc)); 173 time(&rtc.now); 174 175 rtc.regs[MC_REGB] = MC_REGB_24HR; 176 177 memlo /= 65536; 178 memhi /= 65536; 179 180 rtc.regs[NVRAM_MEMSIZE_HI] = (memlo >> 8) & 0xFF; 181 rtc.regs[NVRAM_MEMSIZE_LO] = memlo & 0xFF; 182 rtc.regs[NVRAM_HIMEMSIZE_HI] = (memhi >> 16) & 0xFF; 183 rtc.regs[NVRAM_HIMEMSIZE_MID] = (memhi >> 8) & 0xFF; 184 rtc.regs[NVRAM_HIMEMSIZE_LO] = memhi & 0xFF; 185 186 rtc.regs[NVRAM_SMP_COUNT] = 0; 187 188 rtc_updateregs(); 189 rtc.vm_id = vm_id; 190 191 timerclear(&rtc.sec_tv); 192 rtc.sec_tv.tv_sec = 1; 193 194 timerclear(&rtc.per_tv); 195 196 evtimer_set(&rtc.sec, rtc_fire1, NULL); 197 evtimer_add(&rtc.sec, &rtc.sec_tv); 198 199 evtimer_set(&rtc.per, rtc_fireper, (void *)(intptr_t)rtc.vm_id); 200 201 vm_pipe_init(&dev_pipe, mc146818_pipe_dispatch); 202 event_add(&dev_pipe.read_ev, NULL); 203 } 204 205 /* 206 * rtc_reschedule_per 207 * 208 * Reschedule the periodic interrupt firing rate, based on the currently 209 * selected REGB values. 210 */ 211 static void 212 rtc_reschedule_per(void) 213 { 214 uint16_t rate; 215 uint64_t us; 216 217 if (rtc.regs[MC_REGB] & MC_REGB_PIE) { 218 rate = 32768 >> ((rtc.regs[MC_REGA] & MC_RATE_MASK) - 1); 219 us = (1.0 / rate) * 1000000; 220 rtc.per_tv.tv_usec = us; 221 if (evtimer_pending(&rtc.per, NULL)) 222 evtimer_del(&rtc.per); 223 224 evtimer_add(&rtc.per, &rtc.per_tv); 225 } 226 } 227 228 /* 229 * rtc_update_rega 230 * 231 * Updates the RTC's REGA register 232 * 233 * Parameters: 234 * data: REGA register data 235 */ 236 static void 237 rtc_update_rega(uint32_t data) 238 { 239 if ((data & MC_DIVIDER_MASK) != MC_BASE_32_KHz) 240 log_warnx("%s: set non-32KHz timebase not supported", 241 __func__); 242 243 rtc.regs[MC_REGA] = data; 244 if ((rtc.regs[MC_REGA] ^ data) & 0x0f) 245 vm_pipe_send(&dev_pipe, MC146818_RESCHEDULE_PER); 246 } 247 248 /* 249 * rtc_update_regb 250 * 251 * Updates the RTC's REGB register 252 * 253 * Parameters: 254 * data: REGB register data 255 */ 256 static void 257 rtc_update_regb(uint32_t data) 258 { 259 if (data & MC_REGB_DSE) 260 log_warnx("%s: DSE mode not supported", __func__); 261 262 if (!(data & MC_REGB_24HR)) 263 log_warnx("%s: 12 hour mode not supported", __func__); 264 265 rtc.regs[MC_REGB] = data; 266 267 if (data & MC_REGB_PIE) 268 vm_pipe_send(&dev_pipe, MC146818_RESCHEDULE_PER); 269 } 270 271 /* 272 * vcpu_exit_mc146818 273 * 274 * Handles emulated MC146818 RTC access (in/out instruction to RTC ports). 275 * 276 * Parameters: 277 * vrp: vm run parameters containing exit information for the I/O 278 * instruction being performed 279 * 280 * Return value: 281 * Interrupt to inject to the guest VM, or 0xFF if no interrupt should 282 * be injected. 283 */ 284 uint8_t 285 vcpu_exit_mc146818(struct vm_run_params *vrp) 286 { 287 struct vm_exit *vei = vrp->vrp_exit; 288 uint16_t port = vei->vei.vei_port; 289 uint8_t dir = vei->vei.vei_dir; 290 uint32_t data = 0; 291 292 get_input_data(vei, &data); 293 294 if (port == IO_RTC) { 295 /* Discard NMI bit */ 296 if (data & 0x80) 297 data &= ~0x80; 298 299 if (dir == 0) { 300 if (data < (NVRAM_SIZE)) 301 rtc.idx = data; 302 else 303 rtc.idx = MC_REGD; 304 } else 305 set_return_data(vei, rtc.idx); 306 } else if (port == IO_RTC + 1) { 307 if (dir == 0) { 308 switch (rtc.idx) { 309 case MC_SEC ... MC_YEAR: 310 case MC_NVRAM_START ... MC_NVRAM_START + MC_NVRAM_SIZE: 311 rtc.regs[rtc.idx] = data; 312 break; 313 case MC_REGA: 314 rtc_update_rega(data); 315 break; 316 case MC_REGB: 317 rtc_update_regb(data); 318 break; 319 case MC_REGC: 320 case MC_REGD: 321 log_warnx("%s: mc146818 illegal write " 322 "of reg 0x%x", __func__, rtc.idx); 323 break; 324 default: 325 log_warnx("%s: mc146818 illegal reg %x\n", 326 __func__, rtc.idx); 327 } 328 } else { 329 data = rtc.regs[rtc.idx]; 330 set_return_data(vei, data); 331 332 if (rtc.idx == MC_REGC) { 333 /* Reset IRQ state */ 334 rtc.regs[MC_REGC] &= ~MC_REGC_PF; 335 } 336 } 337 } else { 338 log_warnx("%s: mc146818 unknown port 0x%x", 339 __func__, vei->vei.vei_port); 340 } 341 342 return 0xFF; 343 } 344 345 int 346 mc146818_dump(int fd) 347 { 348 log_debug("%s: sending RTC", __func__); 349 if (atomicio(vwrite, fd, &rtc, sizeof(rtc)) != sizeof(rtc)) { 350 log_warnx("%s: error writing RTC to fd", __func__); 351 return (-1); 352 } 353 return (0); 354 } 355 356 int 357 mc146818_restore(int fd, uint32_t vm_id) 358 { 359 log_debug("%s: restoring RTC", __func__); 360 if (atomicio(read, fd, &rtc, sizeof(rtc)) != sizeof(rtc)) { 361 log_warnx("%s: error reading RTC from fd", __func__); 362 return (-1); 363 } 364 rtc.vm_id = vm_id; 365 366 memset(&rtc.sec, 0, sizeof(struct event)); 367 memset(&rtc.per, 0, sizeof(struct event)); 368 evtimer_set(&rtc.sec, rtc_fire1, NULL); 369 evtimer_set(&rtc.per, rtc_fireper, (void *)(intptr_t)rtc.vm_id); 370 371 vm_pipe_init(&dev_pipe, mc146818_pipe_dispatch); 372 373 return (0); 374 } 375 376 void 377 mc146818_stop() 378 { 379 evtimer_del(&rtc.per); 380 evtimer_del(&rtc.sec); 381 event_del(&dev_pipe.read_ev); 382 } 383 384 void 385 mc146818_start() 386 { 387 evtimer_add(&rtc.sec, &rtc.sec_tv); 388 event_add(&dev_pipe.read_ev, NULL); 389 rtc_reschedule_per(); 390 } 391