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