1243327Sgrehan/*- 2243327Sgrehan * Copyright (c) 2012 NetApp, Inc. 3243327Sgrehan * All rights reserved. 4243327Sgrehan * 5243327Sgrehan * Redistribution and use in source and binary forms, with or without 6243327Sgrehan * modification, are permitted provided that the following conditions 7243327Sgrehan * are met: 8243327Sgrehan * 1. Redistributions of source code must retain the above copyright 9243327Sgrehan * notice, this list of conditions and the following disclaimer. 10243327Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11243327Sgrehan * notice, this list of conditions and the following disclaimer in the 12243327Sgrehan * documentation and/or other materials provided with the distribution. 13243327Sgrehan * 14243327Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15243327Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16243327Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17243327Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18243327Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19243327Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20243327Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21243327Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22243327Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23243327Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24243327Sgrehan * SUCH DAMAGE. 25243327Sgrehan * 26243327Sgrehan * $FreeBSD$ 27243327Sgrehan */ 28243327Sgrehan 29243327Sgrehan#include <sys/cdefs.h> 30243327Sgrehan__FBSDID("$FreeBSD$"); 31243327Sgrehan 32243327Sgrehan#include <sys/types.h> 33243327Sgrehan#include <sys/sysctl.h> 34243327Sgrehan#include <sys/time.h> 35243327Sgrehan#include <machine/cpufunc.h> 36243327Sgrehan 37243327Sgrehan#include <stdio.h> 38249324Sneel#include <stdlib.h> 39243327Sgrehan#include <time.h> 40243327Sgrehan#include <assert.h> 41245123Sgrehan#include <pthread.h> 42243327Sgrehan 43261090Sjhb#include "acpi.h" 44243327Sgrehan#include "inout.h" 45243327Sgrehan 46243327Sgrehan/* 47243327Sgrehan * The ACPI Power Management timer is a free-running 24- or 32-bit 48243327Sgrehan * timer with a frequency of 3.579545MHz 49243327Sgrehan * 50243327Sgrehan * This implementation will be 32-bits 51243327Sgrehan */ 52243327Sgrehan 53243327Sgrehan#define PMTMR_FREQ 3579545 /* 3.579545MHz */ 54243327Sgrehan 55245123Sgrehanstatic pthread_mutex_t pmtmr_mtx; 56261090Sjhbstatic pthread_once_t pmtmr_once = PTHREAD_ONCE_INIT; 57249324Sneel 58249324Sneelstatic uint64_t pmtmr_old; 59249324Sneel 60245123Sgrehanstatic uint64_t pmtmr_tscf; 61243327Sgrehanstatic uint64_t pmtmr_tsc_old; 62243327Sgrehan 63249324Sneelstatic clockid_t clockid = CLOCK_UPTIME_FAST; 64249324Sneelstatic struct timespec pmtmr_uptime_old; 65249324Sneel 66249324Sneel#define timespecsub(vvp, uvp) \ 67249324Sneel do { \ 68249324Sneel (vvp)->tv_sec -= (uvp)->tv_sec; \ 69249324Sneel (vvp)->tv_nsec -= (uvp)->tv_nsec; \ 70249324Sneel if ((vvp)->tv_nsec < 0) { \ 71249324Sneel (vvp)->tv_sec--; \ 72249324Sneel (vvp)->tv_nsec += 1000000000; \ 73249324Sneel } \ 74249324Sneel } while (0) 75249324Sneel 76249324Sneelstatic uint64_t 77249324Sneeltimespec_to_pmtmr(const struct timespec *tsnew, const struct timespec *tsold) 78249324Sneel{ 79249324Sneel struct timespec tsdiff; 80249324Sneel int64_t nsecs; 81249324Sneel 82249324Sneel tsdiff = *tsnew; 83249324Sneel timespecsub(&tsdiff, tsold); 84249324Sneel nsecs = tsdiff.tv_sec * 1000000000 + tsdiff.tv_nsec; 85249324Sneel assert(nsecs >= 0); 86249324Sneel 87249324Sneel return (nsecs * PMTMR_FREQ / 1000000000 + pmtmr_old); 88249324Sneel} 89249324Sneel 90249324Sneelstatic uint64_t 91249324Sneeltsc_to_pmtmr(uint64_t tsc_new, uint64_t tsc_old) 92249324Sneel{ 93249324Sneel 94249324Sneel return ((tsc_new - tsc_old) * PMTMR_FREQ / pmtmr_tscf + pmtmr_old); 95249324Sneel} 96249324Sneel 97249324Sneelstatic void 98249324Sneelpmtmr_init(void) 99249324Sneel{ 100249324Sneel size_t len; 101249324Sneel int smp_tsc, err; 102249324Sneel struct timespec tsnew, tsold = { 0 }; 103249324Sneel 104249324Sneel len = sizeof(smp_tsc); 105249324Sneel err = sysctlbyname("kern.timecounter.smp_tsc", &smp_tsc, &len, NULL, 0); 106249324Sneel assert(err == 0); 107249324Sneel 108249324Sneel if (smp_tsc) { 109249324Sneel len = sizeof(pmtmr_tscf); 110249324Sneel err = sysctlbyname("machdep.tsc_freq", &pmtmr_tscf, &len, 111249324Sneel NULL, 0); 112249324Sneel assert(err == 0); 113249324Sneel 114249324Sneel pmtmr_tsc_old = rdtsc(); 115249324Sneel pmtmr_old = tsc_to_pmtmr(pmtmr_tsc_old, 0); 116249324Sneel } else { 117249324Sneel if (getenv("BHYVE_PMTMR_PRECISE") != NULL) 118249324Sneel clockid = CLOCK_UPTIME; 119249324Sneel 120249324Sneel err = clock_gettime(clockid, &tsnew); 121249324Sneel assert(err == 0); 122249324Sneel 123249324Sneel pmtmr_uptime_old = tsnew; 124249324Sneel pmtmr_old = timespec_to_pmtmr(&tsnew, &tsold); 125249324Sneel } 126261090Sjhb pthread_mutex_init(&pmtmr_mtx, NULL); 127249324Sneel} 128249324Sneel 129243327Sgrehanstatic uint32_t 130243327Sgrehanpmtmr_val(void) 131243327Sgrehan{ 132249324Sneel struct timespec tsnew; 133243327Sgrehan uint64_t pmtmr_tsc_new; 134245123Sgrehan uint64_t pmtmr_new; 135249324Sneel int error; 136249324Sneel 137261090Sjhb pthread_once(&pmtmr_once, pmtmr_init); 138243327Sgrehan 139245123Sgrehan pthread_mutex_lock(&pmtmr_mtx); 140249324Sneel 141249324Sneel if (pmtmr_tscf) { 142249324Sneel pmtmr_tsc_new = rdtsc(); 143249324Sneel pmtmr_new = tsc_to_pmtmr(pmtmr_tsc_new, pmtmr_tsc_old); 144249324Sneel pmtmr_tsc_old = pmtmr_tsc_new; 145249324Sneel } else { 146249324Sneel error = clock_gettime(clockid, &tsnew); 147249324Sneel assert(error == 0); 148249324Sneel 149249324Sneel pmtmr_new = timespec_to_pmtmr(&tsnew, &pmtmr_uptime_old); 150249324Sneel pmtmr_uptime_old = tsnew; 151249324Sneel } 152243327Sgrehan pmtmr_old = pmtmr_new; 153249324Sneel 154245123Sgrehan pthread_mutex_unlock(&pmtmr_mtx); 155243327Sgrehan 156245123Sgrehan return (pmtmr_new); 157243327Sgrehan} 158243327Sgrehan 159243327Sgrehanstatic int 160243327Sgrehanpmtmr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 161243327Sgrehan uint32_t *eax, void *arg) 162243327Sgrehan{ 163243327Sgrehan assert(in == 1); 164243327Sgrehan 165243327Sgrehan if (bytes != 4) 166243327Sgrehan return (-1); 167243327Sgrehan 168243327Sgrehan *eax = pmtmr_val(); 169243327Sgrehan 170243327Sgrehan return (0); 171243327Sgrehan} 172243327Sgrehan 173243327SgrehanINOUT_PORT(pmtmr, IO_PMTMR, IOPORT_F_IN, pmtmr_handler); 174