Home | History | Annotate | Line # | Download | only in alpha
      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