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