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/malloc.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 static MALLOC_DEFINE(M_VPMTMR, "vpmtmr", "bhyve virtual acpi timer"); 71 72 struct vpmtmr * 73 vpmtmr_init(struct vm *vm) 74 { 75 struct vpmtmr *vpmtmr; 76 77 vpmtmr = malloc(sizeof (struct vpmtmr), M_VPMTMR, M_WAITOK | M_ZERO); 78 vpmtmr->vm = vm; 79 vpmtmr->base_time = gethrtime(); 80 81 return (vpmtmr); 82 } 83 84 static int 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 107 vpmtmr_cleanup(struct vpmtmr *vpmtmr) 108 { 109 int err; 110 111 err = vpmtmr_detach_ioport(vpmtmr); 112 VERIFY3P(err, ==, 0); 113 114 free(vpmtmr, M_VPMTMR); 115 } 116 117 int 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 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