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