1/*- 2 * Copyright (c) 2012 NetApp, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32#include <sys/types.h> 33#include <sys/sysctl.h> 34#include <sys/time.h> 35#include <machine/cpufunc.h> 36 37#include <stdio.h> 38#include <stdlib.h> 39#include <time.h> 40#include <assert.h> 41#include <pthread.h> 42 43#include "acpi.h" 44#include "inout.h" 45 46/* 47 * The ACPI Power Management timer is a free-running 24- or 32-bit 48 * timer with a frequency of 3.579545MHz 49 * 50 * This implementation will be 32-bits 51 */ 52 53#define PMTMR_FREQ 3579545 /* 3.579545MHz */ 54 55static pthread_mutex_t pmtmr_mtx; 56static pthread_once_t pmtmr_once = PTHREAD_ONCE_INIT; 57 58static uint64_t pmtmr_old; 59 60static uint64_t pmtmr_tscf; 61static uint64_t pmtmr_tsc_old; 62 63static clockid_t clockid = CLOCK_UPTIME_FAST; 64static struct timespec pmtmr_uptime_old; 65 66#define timespecsub(vvp, uvp) \ 67 do { \ 68 (vvp)->tv_sec -= (uvp)->tv_sec; \ 69 (vvp)->tv_nsec -= (uvp)->tv_nsec; \ 70 if ((vvp)->tv_nsec < 0) { \ 71 (vvp)->tv_sec--; \ 72 (vvp)->tv_nsec += 1000000000; \ 73 } \ 74 } while (0) 75 76static uint64_t 77timespec_to_pmtmr(const struct timespec *tsnew, const struct timespec *tsold) 78{ 79 struct timespec tsdiff; 80 int64_t nsecs; 81 82 tsdiff = *tsnew; 83 timespecsub(&tsdiff, tsold); 84 nsecs = tsdiff.tv_sec * 1000000000 + tsdiff.tv_nsec; 85 assert(nsecs >= 0); 86 87 return (nsecs * PMTMR_FREQ / 1000000000 + pmtmr_old); 88} 89 90static uint64_t 91tsc_to_pmtmr(uint64_t tsc_new, uint64_t tsc_old) 92{ 93 94 return ((tsc_new - tsc_old) * PMTMR_FREQ / pmtmr_tscf + pmtmr_old); 95} 96 97static void 98pmtmr_init(void) 99{ 100 size_t len; 101 int smp_tsc, err; 102 struct timespec tsnew, tsold = { 0 }; 103 104 len = sizeof(smp_tsc); 105 err = sysctlbyname("kern.timecounter.smp_tsc", &smp_tsc, &len, NULL, 0); 106 assert(err == 0); 107 108 if (smp_tsc) { 109 len = sizeof(pmtmr_tscf); 110 err = sysctlbyname("machdep.tsc_freq", &pmtmr_tscf, &len, 111 NULL, 0); 112 assert(err == 0); 113 114 pmtmr_tsc_old = rdtsc(); 115 pmtmr_old = tsc_to_pmtmr(pmtmr_tsc_old, 0); 116 } else { 117 if (getenv("BHYVE_PMTMR_PRECISE") != NULL) 118 clockid = CLOCK_UPTIME; 119 120 err = clock_gettime(clockid, &tsnew); 121 assert(err == 0); 122 123 pmtmr_uptime_old = tsnew; 124 pmtmr_old = timespec_to_pmtmr(&tsnew, &tsold); 125 } 126 pthread_mutex_init(&pmtmr_mtx, NULL); 127} 128 129static uint32_t 130pmtmr_val(void) 131{ 132 struct timespec tsnew; 133 uint64_t pmtmr_tsc_new; 134 uint64_t pmtmr_new; 135 int error; 136 137 pthread_once(&pmtmr_once, pmtmr_init); 138 139 pthread_mutex_lock(&pmtmr_mtx); 140 141 if (pmtmr_tscf) { 142 pmtmr_tsc_new = rdtsc(); 143 pmtmr_new = tsc_to_pmtmr(pmtmr_tsc_new, pmtmr_tsc_old); 144 pmtmr_tsc_old = pmtmr_tsc_new; 145 } else { 146 error = clock_gettime(clockid, &tsnew); 147 assert(error == 0); 148 149 pmtmr_new = timespec_to_pmtmr(&tsnew, &pmtmr_uptime_old); 150 pmtmr_uptime_old = tsnew; 151 } 152 pmtmr_old = pmtmr_new; 153 154 pthread_mutex_unlock(&pmtmr_mtx); 155 156 return (pmtmr_new); 157} 158 159static int 160pmtmr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 161 uint32_t *eax, void *arg) 162{ 163 assert(in == 1); 164 165 if (bytes != 4) 166 return (-1); 167 168 *eax = pmtmr_val(); 169 170 return (0); 171} 172 173INOUT_PORT(pmtmr, IO_PMTMR, IOPORT_F_IN, pmtmr_handler); 174