1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 * Copyright 2020 Oxide Computer Company 39 */ 40 41 #include <sys/cdefs.h> 42 __FBSDID("$FreeBSD$"); 43 44 #include <sys/param.h> 45 #include <sys/queue.h> 46 #include <sys/kernel.h> 47 #include <sys/kmem.h> 48 #include <sys/systm.h> 49 50 #include <machine/vmm.h> 51 52 #include "vpmtmr.h" 53 54 /* 55 * The ACPI Power Management timer is a free-running 24- or 32-bit 56 * timer with a frequency of 3.579545MHz 57 * 58 * This implementation will be 32-bits 59 */ 60 61 #define PMTMR_FREQ 3579545 /* 3.579545MHz */ 62 63 struct vpmtmr { 64 struct vm *vm; 65 void *io_cookie; 66 uint16_t io_port; 67 hrtime_t base_time; 68 }; 69 70 struct vpmtmr * 71 vpmtmr_init(struct vm *vm) 72 { 73 struct vpmtmr *vpmtmr; 74 75 vpmtmr = kmem_zalloc(sizeof (struct vpmtmr), KM_SLEEP); 76 vpmtmr->vm = vm; 77 vpmtmr->base_time = gethrtime(); 78 79 return (vpmtmr); 80 } 81 82 static int 83 vpmtmr_detach_ioport(struct vpmtmr *vpmtmr) 84 { 85 if (vpmtmr->io_cookie != NULL) { 86 ioport_handler_t old_func; 87 void *old_arg; 88 int err; 89 90 err = vm_ioport_detach(vpmtmr->vm, &vpmtmr->io_cookie, 91 &old_func, &old_arg); 92 if (err != 0) { 93 return (err); 94 } 95 96 ASSERT3P(old_func, ==, vpmtmr_handler); 97 ASSERT3P(old_arg, ==, vpmtmr); 98 ASSERT3P(vpmtmr->io_cookie, ==, NULL); 99 vpmtmr->io_port = 0; 100 } 101 return (0); 102 } 103 104 void 105 vpmtmr_cleanup(struct vpmtmr *vpmtmr) 106 { 107 int err; 108 109 err = vpmtmr_detach_ioport(vpmtmr); 110 VERIFY3P(err, ==, 0); 111 112 kmem_free(vpmtmr, sizeof (*vpmtmr)); 113 } 114 115 int 116 vpmtmr_set_location(struct vm *vm, uint16_t ioport) 117 { 118 struct vpmtmr *vpmtmr = vm_pmtmr(vm); 119 int err; 120 121 if (vpmtmr->io_cookie != NULL) { 122 if (vpmtmr->io_port == ioport) { 123 /* already attached in the right place */ 124 return (0); 125 } 126 127 err = vpmtmr_detach_ioport(vpmtmr); 128 VERIFY3P(err, ==, 0); 129 } 130 err = vm_ioport_attach(vm, ioport, vpmtmr_handler, vpmtmr, 131 &vpmtmr->io_cookie); 132 if (err == 0) { 133 vpmtmr->io_port = ioport; 134 } 135 136 return (err); 137 } 138 139 int 140 vpmtmr_handler(void *arg, bool in, uint16_t port, uint8_t bytes, uint32_t *val) 141 { 142 struct vpmtmr *vpmtmr = arg; 143 144 if (!in || bytes != 4) 145 return (-1); 146 147 /* 148 * No locking needed because 'base_time' is written only during 149 * initialization. 150 */ 151 const hrtime_t delta = gethrtime() - vpmtmr->base_time; 152 ASSERT3S(delta, >=, 0); 153 154 *val = hrt_freq_count(delta, PMTMR_FREQ); 155 156 return (0); 157 } 158 159 static int 160 vpmtmr_data_read(void *datap, const vmm_data_req_t *req) 161 { 162 VERIFY3U(req->vdr_class, ==, VDC_PM_TIMER); 163 VERIFY3U(req->vdr_version, ==, 1); 164 VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_pm_timer_v1)); 165 166 struct vpmtmr *vpmtmr = datap; 167 struct vdi_pm_timer_v1 *out = req->vdr_data; 168 169 out->vpt_time_base = vm_normalize_hrtime(vpmtmr->vm, vpmtmr->base_time); 170 out->vpt_ioport = vpmtmr->io_port; 171 172 return (0); 173 } 174 175 static int 176 vpmtmr_data_write(void *datap, const vmm_data_req_t *req) 177 { 178 VERIFY3U(req->vdr_class, ==, VDC_PM_TIMER); 179 VERIFY3U(req->vdr_version, ==, 1); 180 VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_pm_timer_v1)); 181 182 struct vpmtmr *vpmtmr = datap; 183 const struct vdi_pm_timer_v1 *src = req->vdr_data; 184 185 vpmtmr->base_time = 186 vm_denormalize_hrtime(vpmtmr->vm, src->vpt_time_base); 187 188 return (0); 189 } 190 191 static const vmm_data_version_entry_t pm_timer_v1 = { 192 .vdve_class = VDC_PM_TIMER, 193 .vdve_version = 1, 194 .vdve_len_expect = sizeof (struct vdi_pm_timer_v1), 195 .vdve_readf = vpmtmr_data_read, 196 .vdve_writef = vpmtmr_data_write, 197 }; 198 VMM_DATA_VERSION(pm_timer_v1); 199