diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index dc4b09c9c2e7..d52020ccb6bd 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -172,6 +172,8 @@ static caddr_t native_parse_preload_data(u_int64_t); /* Default init_ops implementation. */ struct init_ops init_ops = { .parse_preload_data = native_parse_preload_data, + .early_clock_source_init = i8254_init, + .early_delay = i8254_delay, }; /* @@ -1822,10 +1824,10 @@ hammer_time(u_int64_t modulep, u_int64_t physfree) lidt(&r_idt); /* - * Initialize the i8254 before the console so that console + * Initialize the clock before the console so that console * initialization can use DELAY(). */ - i8254_init(); + clock_init(); /* * Initialize the console before we print anything out. diff --git a/sys/amd64/include/clock.h b/sys/amd64/include/clock.h index d7f7d828b6c5..65bf8bd93bb4 100644 --- a/sys/amd64/include/clock.h +++ b/sys/amd64/include/clock.h @@ -25,6 +25,8 @@ extern int smp_tsc; #endif void i8254_init(void); +void i8254_delay(int); +void clock_init(void); /* * Driver to clock driver interface. diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index a6eb12acad6f..45c092730019 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -554,6 +554,7 @@ x86/x86/mptable_pci.c optional mptable pci x86/x86/msi.c optional pci x86/x86/nexus.c standard x86/x86/tsc.c standard +x86/x86/delay.c standard x86/xen/hvm.c optional xenhvm x86/xen/xen_intr.c optional xen | xenhvm x86/xen/pv.c optional xenhvm diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index 5408908c6e84..8d724ca95927 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -593,5 +593,6 @@ x86/x86/mptable_pci.c optional apic native pci x86/x86/msi.c optional apic pci x86/x86/nexus.c standard x86/x86/tsc.c standard +x86/x86/delay.c standard x86/xen/hvm.c optional xenhvm x86/xen/xen_intr.c optional xen | xenhvm diff --git a/sys/conf/files.pc98 b/sys/conf/files.pc98 index 0c315e4ddac1..92696ee9c08b 100644 --- a/sys/conf/files.pc98 +++ b/sys/conf/files.pc98 @@ -259,3 +259,5 @@ x86/x86/mptable_pci.c optional apic pci x86/x86/msi.c optional apic pci x86/x86/nexus.c standard x86/x86/tsc.c standard +x86/x86/tsc.c standard +x86/x86/delay.c standard diff --git a/sys/dev/xen/timer/timer.c b/sys/dev/xen/timer/timer.c index 6c0a140ec762..ed611d03e3a3 100644 --- a/sys/dev/xen/timer/timer.c +++ b/sys/dev/xen/timer/timer.c @@ -60,6 +60,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include + #include "clock_if.h" static devclass_t xentimer_devclass; @@ -592,6 +594,38 @@ xentimer_suspend(device_t dev) return (0); } +/* + * Xen early clock init + */ +void +xen_clock_init(void) +{ +} + +/* + * Xen PV DELAY function + * + * When running on PVH mode we don't have an emulated i8524, so + * make use of the Xen time info in order to code a simple DELAY + * function that can be used during early boot. + */ +void +xen_delay(int n) +{ + struct vcpu_info *vcpu = &HYPERVISOR_shared_info->vcpu_info[0]; + uint64_t end_ns; + uint64_t current; + + end_ns = xen_fetch_vcpu_time(vcpu); + end_ns += n * NSEC_IN_USEC; + + for (;;) { + current = xen_fetch_vcpu_time(vcpu); + if (current >= end_ns) + break; + } +} + static device_method_t xentimer_methods[] = { DEVMETHOD(device_identify, xentimer_identify), DEVMETHOD(device_probe, xentimer_probe), diff --git a/sys/dev/xen/timer/timer.h b/sys/dev/xen/timer/timer.h new file mode 100644 index 000000000000..38518cb394c6 --- /dev/null +++ b/sys/dev/xen/timer/timer.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2013 Roger Pau Monné + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _XEN_TIMER_H_ +#define _XEN_TIMER_H_ + +void xen_clock_init(void); +void xen_delay(int); + +#endif /* _XEN_TIMER_H_ */ diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 929a22518eb8..a4a680445ede 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -132,6 +132,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #ifdef PERFMON #include #endif @@ -253,6 +254,12 @@ struct mtx icu_lock; struct mem_range_softc mem_range_softc; + /* Default init_ops implementation. */ + struct init_ops init_ops = { + .early_clock_source_init = i8254_init, + .early_delay = i8254_delay, + }; + static void cpu_startup(dummy) void *dummy; @@ -2977,10 +2984,10 @@ init386(first) #endif /* XBOX */ /* - * Initialize the i8254 before the console so that console + * Initialize the clock before the console so that console * initialization can use DELAY(). */ - i8254_init(); + clock_init(); /* * Initialize the console before we print anything out. diff --git a/sys/i386/include/clock.h b/sys/i386/include/clock.h index d980ec7bd6d9..564d231dde0c 100644 --- a/sys/i386/include/clock.h +++ b/sys/i386/include/clock.h @@ -22,6 +22,8 @@ extern int tsc_is_invariant; extern int tsc_perf_stat; void i8254_init(void); +void i8254_delay(int); +void clock_init(void); /* * Driver to clock driver interface. diff --git a/sys/i386/xen/clock.c b/sys/i386/xen/clock.c index 075f5675cad1..5d03d2648028 100644 --- a/sys/i386/xen/clock.c +++ b/sys/i386/xen/clock.c @@ -192,7 +192,7 @@ i8254_init(void) * Note: timer had better have been programmed before this is first used! */ void -DELAY(int n) +i8254_delay(int n) { int delta, ticks_left; uint32_t tick, prev_tick; diff --git a/sys/pc98/pc98/machdep.c b/sys/pc98/pc98/machdep.c index b6cdcdcb8f7b..ab978deca8d1 100644 --- a/sys/pc98/pc98/machdep.c +++ b/sys/pc98/pc98/machdep.c @@ -129,6 +129,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #ifdef PERFMON #include #endif @@ -220,6 +221,12 @@ struct mtx icu_lock; struct mem_range_softc mem_range_softc; + /* Default init_ops implementation. */ + struct init_ops init_ops = { + .early_clock_source_init = i8254_init, + .early_delay = i8254_delay, + }; + static void cpu_startup(dummy) void *dummy; @@ -2267,7 +2274,7 @@ init386(first) * Initialize the i8254 before the console so that console * initialization can use DELAY(). */ - i8254_init(); + clock_init(); /* * Initialize the console before we print anything out. diff --git a/sys/x86/include/init.h b/sys/x86/include/init.h index 7e032df214fe..c72889f0209b 100644 --- a/sys/x86/include/init.h +++ b/sys/x86/include/init.h @@ -36,6 +36,8 @@ */ struct init_ops { caddr_t (*parse_preload_data)(u_int64_t); + void (*early_clock_source_init)(void); + void (*early_delay)(int); }; extern struct init_ops init_ops; diff --git a/sys/x86/isa/clock.c b/sys/x86/isa/clock.c index a12e17514a91..8ed68e0f97d6 100644 --- a/sys/x86/isa/clock.c +++ b/sys/x86/isa/clock.c @@ -65,6 +65,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #ifdef PC98 #include @@ -139,6 +140,15 @@ static u_char timer2_state; static unsigned i8254_get_timecount(struct timecounter *tc); static void set_i8254_freq(int mode, uint32_t period); +void +clock_init(void) +{ + /* Init the clock lock */ + mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_NOPROFILE); + /* Init the clock in order to use DELAY */ + init_ops.early_clock_source_init(); +} + static int clkintr(void *arg) { @@ -247,61 +257,13 @@ getit(void) return ((high << 8) | low); } -#ifndef DELAYDEBUG -static u_int -get_tsc(__unused struct timecounter *tc) -{ - - return (rdtsc32()); -} - -static __inline int -delay_tc(int n) -{ - struct timecounter *tc; - timecounter_get_t *func; - uint64_t end, freq, now; - u_int last, mask, u; - - tc = timecounter; - freq = atomic_load_acq_64(&tsc_freq); - if (tsc_is_invariant && freq != 0) { - func = get_tsc; - mask = ~0u; - } else { - if (tc->tc_quality <= 0) - return (0); - func = tc->tc_get_timecount; - mask = tc->tc_counter_mask; - freq = tc->tc_frequency; - } - now = 0; - end = freq * n / 1000000; - if (func == get_tsc) - sched_pin(); - last = func(tc) & mask; - do { - cpu_spinwait(); - u = func(tc) & mask; - if (u < last) - now += mask - last + u + 1; - else - now += u - last; - last = u; - } while (now < end); - if (func == get_tsc) - sched_unpin(); - return (1); -} -#endif - /* * Wait "n" microseconds. * Relies on timer 1 counting down from (i8254_freq / hz) * Note: timer had better have been programmed before this is first used! */ void -DELAY(int n) +i8254_delay(int n) { int delta, prev_tick, tick, ticks_left; #ifdef DELAYDEBUG @@ -317,9 +279,6 @@ DELAY(int n) } if (state == 1) printf("DELAY(%d)...", n); -#else - if (delay_tc(n)) - return; #endif /* * Read the counter first, so that the rest of the setup overhead is @@ -499,7 +458,6 @@ void i8254_init(void) { - mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_NOPROFILE); #ifdef PC98 if (pc98_machine_type & M_8M) i8254_freq = 1996800L; /* 1.9968 MHz */ diff --git a/sys/x86/x86/delay.c b/sys/x86/x86/delay.c new file mode 100644 index 000000000000..8cbe6012a96b --- /dev/null +++ b/sys/x86/x86/delay.c @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * Copyright (c) 2010 Alexander Motin + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz and Don Ahn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)clock.c 7.2 (Berkeley) 5/12/91 + */ + +#include +__FBSDID("$FreeBSD$"); + +/* Generic x86 routines to handle delay */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static u_int +get_tsc(__unused struct timecounter *tc) +{ + + return (rdtsc32()); +} + +static int +delay_tc(int n) +{ + struct timecounter *tc; + timecounter_get_t *func; + uint64_t end, freq, now; + u_int last, mask, u; + + tc = timecounter; + freq = atomic_load_acq_64(&tsc_freq); + if (tsc_is_invariant && freq != 0) { + func = get_tsc; + mask = ~0u; + } else { + if (tc->tc_quality <= 0) + return (0); + func = tc->tc_get_timecount; + mask = tc->tc_counter_mask; + freq = tc->tc_frequency; + } + now = 0; + end = freq * n / 1000000; + if (func == get_tsc) + sched_pin(); + last = func(tc) & mask; + do { + cpu_spinwait(); + u = func(tc) & mask; + if (u < last) + now += mask - last + u + 1; + else + now += u - last; + last = u; + } while (now < end); + if (func == get_tsc) + sched_unpin(); + return (1); +} + +void +DELAY(int n) +{ + + if (delay_tc(n)) + return; + + init_ops.early_delay(n); +} diff --git a/sys/x86/xen/pv.c b/sys/x86/xen/pv.c index 63dc39b92709..4bc7e86b23c0 100644 --- a/sys/x86/xen/pv.c +++ b/sys/x86/xen/pv.c @@ -53,6 +53,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include + /* Native initial function */ extern u_int64_t hammer_time(u_int64_t, u_int64_t); /* Xen initial function */ @@ -65,6 +67,8 @@ static caddr_t xen_pv_parse_preload_data(u_int64_t); /* Xen init_ops implementation. */ struct init_ops xen_init_ops = { .parse_preload_data = xen_pv_parse_preload_data, + .early_clock_source_init = xen_clock_init, + .early_delay = xen_delay, }; /*-------------------------------- Xen PV init -------------------------------*/