1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2014, Neel Natu (neel@freebsd.org)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
12 * disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 /*
29 * This file and its contents are supplied under the terms of the
30 * Common Development and Distribution License ("CDDL"), version 1.0.
31 * You may only use this file in accordance with the terms of version
32 * 1.0 of the CDDL.
33 *
34 * A full copy of the text of the CDDL should have accompanied this
35 * source. A copy of the CDDL is also available via the Internet at
36 * http://www.illumos.org/license/CDDL.
37 */
38 /* This file is dual-licensed; see usr/src/contrib/bhyve/LICENSE */
39
40 /*
41 * Copyright 2020 Oxide Computer Company
42 */
43
44 #include <sys/cdefs.h>
45
46 #include <sys/param.h>
47 #include <sys/queue.h>
48 #include <sys/kernel.h>
49 #include <sys/kmem.h>
50 #include <sys/systm.h>
51
52 #include <machine/vmm.h>
53
54 #include "vpmtmr.h"
55
56 /*
57 * The ACPI Power Management timer is a free-running 24- or 32-bit
58 * timer with a frequency of 3.579545MHz
59 *
60 * This implementation will be 32-bits
61 */
62
63 #define PMTMR_FREQ 3579545 /* 3.579545MHz */
64
65 struct vpmtmr {
66 struct vm *vm;
67 void *io_cookie;
68 uint16_t io_port;
69 hrtime_t base_time;
70 };
71
72 struct vpmtmr *
vpmtmr_init(struct vm * vm)73 vpmtmr_init(struct vm *vm)
74 {
75 struct vpmtmr *vpmtmr;
76
77 vpmtmr = kmem_zalloc(sizeof (struct vpmtmr), KM_SLEEP);
78 vpmtmr->vm = vm;
79 vpmtmr->base_time = gethrtime();
80
81 return (vpmtmr);
82 }
83
84 static int
vpmtmr_detach_ioport(struct vpmtmr * vpmtmr)85 vpmtmr_detach_ioport(struct vpmtmr *vpmtmr)
86 {
87 if (vpmtmr->io_cookie != NULL) {
88 ioport_handler_t old_func;
89 void *old_arg;
90 int err;
91
92 err = vm_ioport_detach(vpmtmr->vm, &vpmtmr->io_cookie,
93 &old_func, &old_arg);
94 if (err != 0) {
95 return (err);
96 }
97
98 ASSERT3P(old_func, ==, vpmtmr_handler);
99 ASSERT3P(old_arg, ==, vpmtmr);
100 ASSERT3P(vpmtmr->io_cookie, ==, NULL);
101 vpmtmr->io_port = 0;
102 }
103 return (0);
104 }
105
106 void
vpmtmr_cleanup(struct vpmtmr * vpmtmr)107 vpmtmr_cleanup(struct vpmtmr *vpmtmr)
108 {
109 int err;
110
111 err = vpmtmr_detach_ioport(vpmtmr);
112 VERIFY3P(err, ==, 0);
113
114 kmem_free(vpmtmr, sizeof (*vpmtmr));
115 }
116
117 int
vpmtmr_set_location(struct vm * vm,uint16_t ioport)118 vpmtmr_set_location(struct vm *vm, uint16_t ioport)
119 {
120 struct vpmtmr *vpmtmr = vm_pmtmr(vm);
121 int err;
122
123 if (vpmtmr->io_cookie != NULL) {
124 if (vpmtmr->io_port == ioport) {
125 /* already attached in the right place */
126 return (0);
127 }
128
129 err = vpmtmr_detach_ioport(vpmtmr);
130 VERIFY3P(err, ==, 0);
131 }
132 err = vm_ioport_attach(vm, ioport, vpmtmr_handler, vpmtmr,
133 &vpmtmr->io_cookie);
134 if (err == 0) {
135 vpmtmr->io_port = ioport;
136 }
137
138 return (err);
139 }
140
141 int
vpmtmr_handler(void * arg,bool in,uint16_t port,uint8_t bytes,uint32_t * val)142 vpmtmr_handler(void *arg, bool in, uint16_t port, uint8_t bytes, uint32_t *val)
143 {
144 struct vpmtmr *vpmtmr = arg;
145
146 if (!in || bytes != 4)
147 return (-1);
148
149 /*
150 * No locking needed because 'base_time' is written only during
151 * initialization.
152 */
153 const hrtime_t delta = gethrtime() - vpmtmr->base_time;
154 ASSERT3S(delta, >=, 0);
155
156 *val = hrt_freq_count(delta, PMTMR_FREQ);
157
158 return (0);
159 }
160
161 static int
vpmtmr_data_read(void * datap,const vmm_data_req_t * req)162 vpmtmr_data_read(void *datap, const vmm_data_req_t *req)
163 {
164 VERIFY3U(req->vdr_class, ==, VDC_PM_TIMER);
165 VERIFY3U(req->vdr_version, ==, 1);
166 VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_pm_timer_v1));
167
168 struct vpmtmr *vpmtmr = datap;
169 struct vdi_pm_timer_v1 *out = req->vdr_data;
170
171 out->vpt_time_base = vm_normalize_hrtime(vpmtmr->vm, vpmtmr->base_time);
172 out->vpt_ioport = vpmtmr->io_port;
173
174 return (0);
175 }
176
177 static int
vpmtmr_data_write(void * datap,const vmm_data_req_t * req)178 vpmtmr_data_write(void *datap, const vmm_data_req_t *req)
179 {
180 VERIFY3U(req->vdr_class, ==, VDC_PM_TIMER);
181 VERIFY3U(req->vdr_version, ==, 1);
182 VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_pm_timer_v1));
183
184 struct vpmtmr *vpmtmr = datap;
185 const struct vdi_pm_timer_v1 *src = req->vdr_data;
186
187 vpmtmr->base_time =
188 vm_denormalize_hrtime(vpmtmr->vm, src->vpt_time_base);
189
190 return (0);
191 }
192
193 static const vmm_data_version_entry_t pm_timer_v1 = {
194 .vdve_class = VDC_PM_TIMER,
195 .vdve_version = 1,
196 .vdve_len_expect = sizeof (struct vdi_pm_timer_v1),
197 .vdve_readf = vpmtmr_data_read,
198 .vdve_writef = vpmtmr_data_write,
199 };
200 VMM_DATA_VERSION(pm_timer_v1);
201