1 /* $NetBSD: qemu.c,v 1.6 2023/08/01 20:09:12 andvar Exp $ */ 2 3 /*- 4 * Copyright (c) 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 33 34 __KERNEL_RCSID(0, "$NetBSD: qemu.c,v 1.6 2023/08/01 20:09:12 andvar Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/device.h> 39 #include <sys/time.h> 40 #include <sys/timetc.h> 41 #include <sys/kernel.h> 42 #include <sys/cpu.h> 43 44 #include <machine/autoconf.h> 45 #include <machine/cpuconf.h> 46 #include <machine/rpb.h> 47 #include <machine/alpha.h> 48 49 #include <alpha/alpha/clockvar.h> 50 51 extern struct cfdriver qemu_cd; 52 53 struct qemu_softc { 54 device_t sc_dev; 55 56 struct timecounter sc_tc; 57 }; 58 59 static unsigned long qemu_nsec_per_tick __read_mostly = (unsigned long)-1; 60 61 static inline unsigned long 62 qemu_get_vmtime(void) 63 { 64 register unsigned long v0 __asm("$0"); 65 register unsigned long a0 __asm("$16") = 7; /* Qemu get-time */ 66 67 __asm volatile ("call_pal %2" 68 : "=r"(v0), "+r"(a0) 69 : "i"(PAL_cserve) 70 : "$17", "$18", "$19", "$20", "$21"); 71 72 return v0; 73 } 74 75 static void 76 qemu_delay(unsigned long usec) 77 { 78 /* Get starting point. */ 79 const unsigned long base = qemu_get_vmtime(); 80 81 /* convert request from usec to nsec */ 82 const unsigned long nsec = usec * 1000; 83 KASSERT(nsec > usec); 84 85 /* Figure out finish line. */ 86 const unsigned long finished = base + nsec; 87 KASSERT(finished > base); 88 89 unsigned long now; 90 91 /* Spin until we're finished. */ 92 while ((now = qemu_get_vmtime()) < finished) { 93 /* 94 * If we have more than one clock tick worth of spinning 95 * to do, when use WTINT to wait at a low power state. 96 */ 97 if (finished - now > qemu_nsec_per_tick) { 98 alpha_pal_wtint(0); 99 } 100 } 101 } 102 103 static u_int 104 qemu_get_timecount(struct timecounter * const tc __unused) 105 { 106 return (u_int)qemu_get_vmtime(); 107 } 108 109 static inline void 110 qemu_set_alarm_relative(unsigned long nsec) 111 { 112 register unsigned long a0 __asm("$16") = 5; /* Qemu set-alarm-rel */ 113 register unsigned long a1 __asm("$17") = nsec; 114 115 __asm volatile ("call_pal %2" 116 : "+r"(a0), "+r"(a1) 117 : "i"(PAL_cserve) 118 : "$0", "$18", "$19", "$20", "$21"); 119 } 120 121 static void 122 qemu_hardclock(struct clockframe * const framep) 123 { 124 if (__predict_false(qemu_nsec_per_tick == (unsigned long)-1)) { 125 /* Spurious; qemu_clock_init() hasn't been called yet. */ 126 return; 127 } 128 129 /* Schedule the next tick before we process the current one. */ 130 qemu_set_alarm_relative(qemu_nsec_per_tick); 131 132 hardclock(framep); 133 } 134 135 static void 136 qemu_clock_init(void * const v __unused) 137 { 138 /* First-time initialization... */ 139 if (qemu_nsec_per_tick == (unsigned long)-1) { 140 KASSERT(CPU_IS_PRIMARY(curcpu())); 141 142 /* 143 * Override the clockintr routine; the Qemu alarm is 144 * one-shot, so we have to restart it for the next one. 145 */ 146 platform.clockintr = qemu_hardclock; 147 148 /* 149 * hz=1024 is a little bananas for an emulated 150 * virtual machine. Let MI code drive schedhz. 151 */ 152 hz = 50; 153 schedhz = 0; 154 155 qemu_nsec_per_tick = 1000000000UL / hz; 156 157 printf("Using the Qemu CPU alarm for %d Hz hardclock.\n", hz); 158 } 159 160 /* 161 * Note: We need to do this on each CPU, as the Qemu 162 * alarm is implemented as a per-CPU register. 163 */ 164 qemu_set_alarm_relative(qemu_nsec_per_tick); 165 } 166 167 static int 168 qemu_match(device_t parent, cfdata_t cfdata, void *aux) 169 { 170 struct mainbus_attach_args *ma = aux; 171 172 if (strcmp(ma->ma_name, qemu_cd.cd_name) != 0) 173 return (0); 174 175 return (1); 176 } 177 178 static void 179 qemu_attach(device_t parent, device_t self, void *aux) 180 { 181 struct qemu_softc * const sc = device_private(self); 182 struct timecounter * const tc = &sc->sc_tc; 183 184 sc->sc_dev = self; 185 186 aprint_normal(": Qemu virtual machine services\n"); 187 aprint_naive("\n"); 188 189 /* 190 * Use the Qemu "VM time" hypercall as the system timecounter. 191 */ 192 tc->tc_name = "Qemu"; 193 tc->tc_get_timecount = qemu_get_timecount; 194 tc->tc_quality = 3000; 195 tc->tc_counter_mask = __BITS(0,31); 196 tc->tc_frequency = 1000000000UL; /* nanosecond granularity */ 197 tc->tc_priv = sc; 198 tc_init(tc); 199 200 /* 201 * Use the Qemu alarm as the system clock. 202 */ 203 clockattach(qemu_clock_init, sc); 204 205 /* 206 * Qemu's PALcode implements WTINT; use it to save host cycles. 207 */ 208 cpu_idle_fn = cpu_idle_wtint; 209 210 /* 211 * Use Qemu's "VM time" hypercall to implement delay(). 212 */ 213 alpha_delay_fn = qemu_delay; 214 } 215 216 CFATTACH_DECL_NEW(qemu, sizeof(struct qemu_softc), 217 qemu_match, qemu_attach, NULL, NULL); 218