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
qemu_get_vmtime(void)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
qemu_delay(unsigned long usec)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
qemu_get_timecount(struct timecounter * const tc __unused)104 qemu_get_timecount(struct timecounter * const tc __unused)
105 {
106 return (u_int)qemu_get_vmtime();
107 }
108
109 static inline void
qemu_set_alarm_relative(unsigned long nsec)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
qemu_hardclock(struct clockframe * const framep)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
qemu_clock_init(void * const v __unused)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
qemu_match(device_t parent,cfdata_t cfdata,void * aux)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
qemu_attach(device_t parent,device_t self,void * aux)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