1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org> 5 * Copyright (c) 2016, 2017, 2019 The FreeBSD Foundation 6 * Copyright (c) 2021 Dmitry Chagin <dchagin@FreeBSD.org> 7 * 8 * Portions of this software were developed by Konstantin Belousov 9 * under sponsorship from the FreeBSD Foundation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD$"); 35 36#include <sys/param.h> 37 38#if defined(__i386__) || defined(COMPAT_LINUX32) 39#include <i386/include/atomic.h> 40#include <i386/include/cpufunc.h> 41#else 42#include <amd64/include/atomic.h> 43#include <amd64/include/cpufunc.h> 44#endif 45 46static inline u_int 47rdtsc_low(const struct vdso_timehands *th) 48{ 49 u_int rv; 50 51 __asm __volatile("rdtsc; shrd %%cl, %%edx, %0" 52 : "=a" (rv) : "c" (th->th_x86_shift) : "edx"); 53 return (rv); 54} 55 56static inline u_int 57rdtscp_low(const struct vdso_timehands *th) 58{ 59 u_int rv; 60 61 __asm __volatile("rdtscp; movl %%edi,%%ecx; shrd %%cl, %%edx, %0" 62 : "=a" (rv) : "D" (th->th_x86_shift) : "ecx", "edx"); 63 return (rv); 64} 65 66static u_int 67rdtsc_low_mb_lfence(const struct vdso_timehands *th) 68{ 69 lfence(); 70 return (rdtsc_low(th)); 71} 72 73static u_int 74rdtsc_low_mb_mfence(const struct vdso_timehands *th) 75{ 76 mfence(); 77 return (rdtsc_low(th)); 78} 79 80static u_int 81rdtsc_low_mb_none(const struct vdso_timehands *th) 82{ 83 return (rdtsc_low(th)); 84} 85 86static u_int 87rdtsc32_mb_lfence(void) 88{ 89 lfence(); 90 return (rdtsc32()); 91} 92 93static u_int 94rdtsc32_mb_mfence(void) 95{ 96 mfence(); 97 return (rdtsc32()); 98} 99 100static u_int 101rdtsc32_mb_none(void) 102{ 103 return (rdtsc32()); 104} 105 106static u_int 107rdtscp32_(void) 108{ 109 return (rdtscp32()); 110} 111 112struct tsc_selector_tag { 113 u_int (*ts_rdtsc32)(void); 114 u_int (*ts_rdtsc_low)(const struct vdso_timehands *); 115}; 116 117static const struct tsc_selector_tag tsc_selector[] = { 118 [0] = { /* Intel, LFENCE */ 119 .ts_rdtsc32 = rdtsc32_mb_lfence, 120 .ts_rdtsc_low = rdtsc_low_mb_lfence, 121 }, 122 [1] = { /* AMD, MFENCE */ 123 .ts_rdtsc32 = rdtsc32_mb_mfence, 124 .ts_rdtsc_low = rdtsc_low_mb_mfence, 125 }, 126 [2] = { /* No SSE2 */ 127 .ts_rdtsc32 = rdtsc32_mb_none, 128 .ts_rdtsc_low = rdtsc_low_mb_none, 129 }, 130 [3] = { /* RDTSCP */ 131 .ts_rdtsc32 = rdtscp32_, 132 .ts_rdtsc_low = rdtscp_low, 133 }, 134}; 135 136static u_int 137__vdso_gettc_rdtsc_low(const struct vdso_timehands *th) 138{ 139 140 return (tsc_selector[kern_tsc_selector].ts_rdtsc_low(th)); 141} 142 143static u_int 144__vdso_gettc_rdtsc32(void) 145{ 146 147 return (tsc_selector[kern_tsc_selector].ts_rdtsc32()); 148} 149 150int 151__vdso_gettc(const struct vdso_timehands *th, u_int *tc) 152{ 153 154 switch (th->th_algo) { 155 case VDSO_TH_ALGO_X86_TSC: 156 *tc = th->th_x86_shift > 0 ? __vdso_gettc_rdtsc_low(th) : 157 __vdso_gettc_rdtsc32(); 158 return (0); 159 case VDSO_TH_ALGO_X86_HPET: 160 /* TODO */ 161 default: 162 return (ENOSYS); 163 } 164} 165