The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/hyperv/vmbus/amd64/hyperv_machdep.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2016-2017 Microsoft Corp.
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice unmodified, this list of conditions, and the following
   10  *    disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 #include <sys/param.h>
   31 #include <sys/conf.h>
   32 #include <sys/fcntl.h>
   33 #include <sys/kernel.h>
   34 #include <sys/systm.h>
   35 #include <sys/timetc.h>
   36 #include <sys/vdso.h>
   37 
   38 #include <machine/cpufunc.h>
   39 #include <machine/cputypes.h>
   40 #include <machine/md_var.h>
   41 #include <machine/specialreg.h>
   42 
   43 #include <vm/vm.h>
   44 
   45 #include <dev/hyperv/include/hyperv.h>
   46 #include <dev/hyperv/include/hyperv_busdma.h>
   47 #include <dev/hyperv/vmbus/x86/hyperv_machdep.h>
   48 #include <dev/hyperv/vmbus/x86/hyperv_reg.h>
   49 #include <dev/hyperv/vmbus/hyperv_var.h>
   50 #include <dev/hyperv/vmbus/hyperv_common_reg.h>
   51 
   52 struct hyperv_reftsc_ctx {
   53         struct hyperv_reftsc    *tsc_ref;
   54         struct hyperv_dma       tsc_ref_dma;
   55 };
   56 
   57 static uint32_t                 hyperv_tsc_vdso_timehands(
   58                                     struct vdso_timehands *,
   59                                     struct timecounter *);
   60 
   61 static d_open_t                 hyperv_tsc_open;
   62 static d_mmap_t                 hyperv_tsc_mmap;
   63 
   64 static struct timecounter       hyperv_tsc_timecounter = {
   65         .tc_get_timecount       = NULL, /* based on CPU vendor. */
   66         .tc_counter_mask        = 0xffffffff,
   67         .tc_frequency           = HYPERV_TIMER_FREQ,
   68         .tc_name                = "Hyper-V-TSC",
   69         .tc_quality             = 3000,
   70         .tc_fill_vdso_timehands = hyperv_tsc_vdso_timehands,
   71 };
   72 
   73 static struct cdevsw            hyperv_tsc_cdevsw = {
   74         .d_version              = D_VERSION,
   75         .d_open                 = hyperv_tsc_open,
   76         .d_mmap                 = hyperv_tsc_mmap,
   77         .d_name                 = HYPERV_REFTSC_DEVNAME
   78 };
   79 
   80 static struct hyperv_reftsc_ctx hyperv_ref_tsc;
   81 
   82 uint64_t
   83 hypercall_md(volatile void *hc_addr, uint64_t in_val,
   84     uint64_t in_paddr, uint64_t out_paddr)
   85 {
   86         uint64_t status;
   87 
   88         __asm__ __volatile__ ("mov %0, %%r8" : : "r" (out_paddr): "r8");
   89         __asm__ __volatile__ ("call *%3" : "=a" (status) :
   90             "c" (in_val), "d" (in_paddr), "m" (hc_addr));
   91         return (status);
   92 }
   93 
   94 static int
   95 hyperv_tsc_open(struct cdev *dev __unused, int oflags, int devtype __unused,
   96     struct thread *td __unused)
   97 {
   98 
   99         if (oflags & FWRITE)
  100                 return (EPERM);
  101         return (0);
  102 }
  103 
  104 static int
  105 hyperv_tsc_mmap(struct cdev *dev __unused, vm_ooffset_t offset,
  106     vm_paddr_t *paddr, int nprot __unused, vm_memattr_t *memattr __unused)
  107 {
  108 
  109         KASSERT(hyperv_ref_tsc.tsc_ref != NULL, ("reftsc has not been setup"));
  110 
  111         /*
  112          * NOTE:
  113          * 'nprot' does not contain information interested to us;
  114          * WR-open is blocked by d_open.
  115          */
  116 
  117         if (offset != 0)
  118                 return (EOPNOTSUPP);
  119 
  120         *paddr = hyperv_ref_tsc.tsc_ref_dma.hv_paddr;
  121         return (0);
  122 }
  123 
  124 static uint32_t
  125 hyperv_tsc_vdso_timehands(struct vdso_timehands *vdso_th,
  126     struct timecounter *tc __unused)
  127 {
  128 
  129         vdso_th->th_algo = VDSO_TH_ALGO_X86_HVTSC;
  130         vdso_th->th_x86_shift = 0;
  131         vdso_th->th_x86_hpet_idx = 0;
  132         vdso_th->th_x86_pvc_last_systime = 0;
  133         vdso_th->th_x86_pvc_stable_mask = 0;
  134         bzero(vdso_th->th_res, sizeof(vdso_th->th_res));
  135         return (1);
  136 }
  137 
  138 #define HYPERV_TSC_TIMECOUNT(fence)                                     \
  139 static uint64_t                                                         \
  140 hyperv_tc64_tsc_##fence(void)                                           \
  141 {                                                                       \
  142         struct hyperv_reftsc *tsc_ref = hyperv_ref_tsc.tsc_ref;         \
  143         uint32_t seq;                                                   \
  144                                                                         \
  145         while ((seq = atomic_load_acq_int(&tsc_ref->tsc_seq)) != 0) {   \
  146                 uint64_t disc, ret, tsc;                                \
  147                 uint64_t scale = tsc_ref->tsc_scale;                    \
  148                 int64_t ofs = tsc_ref->tsc_ofs;                         \
  149                                                                         \
  150                 fence();                                                \
  151                 tsc = rdtsc();                                          \
  152                                                                         \
  153                 /* ret = ((tsc * scale) >> 64) + ofs */                 \
  154                 __asm__ __volatile__ ("mulq %3" :                       \
  155                     "=d" (ret), "=a" (disc) :                           \
  156                     "a" (tsc), "r" (scale));                            \
  157                 ret += ofs;                                             \
  158                                                                         \
  159                 atomic_thread_fence_acq();                              \
  160                 if (tsc_ref->tsc_seq == seq)                            \
  161                         return (ret);                                   \
  162                                                                         \
  163                 /* Sequence changed; re-sync. */                        \
  164         }                                                               \
  165         /* Fallback to the generic timecounter, i.e. rdmsr. */          \
  166         return (rdmsr(MSR_HV_TIME_REF_COUNT));                          \
  167 }                                                                       \
  168                                                                         \
  169 static u_int                                                            \
  170 hyperv_tsc_timecount_##fence(struct timecounter *tc __unused)           \
  171 {                                                                       \
  172                                                                         \
  173         return (hyperv_tc64_tsc_##fence());                             \
  174 }                                                                       \
  175 struct __hack
  176 
  177 HYPERV_TSC_TIMECOUNT(lfence);
  178 HYPERV_TSC_TIMECOUNT(mfence);
  179 
  180 static void
  181 hyperv_tsc_tcinit(void *dummy __unused)
  182 {
  183         hyperv_tc64_t tc64 = NULL;
  184         uint64_t val, orig;
  185 
  186         if ((hyperv_features &
  187              (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC)) !=
  188             (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC) ||
  189             (cpu_feature & CPUID_SSE2) == 0)    /* SSE2 for mfence/lfence */
  190                 return;
  191 
  192         switch (cpu_vendor_id) {
  193         case CPU_VENDOR_AMD:
  194         case CPU_VENDOR_HYGON:
  195                 hyperv_tsc_timecounter.tc_get_timecount =
  196                     hyperv_tsc_timecount_mfence;
  197                 tc64 = hyperv_tc64_tsc_mfence;
  198                 break;
  199 
  200         case CPU_VENDOR_INTEL:
  201                 hyperv_tsc_timecounter.tc_get_timecount =
  202                     hyperv_tsc_timecount_lfence;
  203                 tc64 = hyperv_tc64_tsc_lfence;
  204                 break;
  205 
  206         default:
  207                 /* Unsupported CPU vendors. */
  208                 return;
  209         }
  210 
  211         hyperv_ref_tsc.tsc_ref = hyperv_dmamem_alloc(NULL, PAGE_SIZE, 0,
  212             sizeof(struct hyperv_reftsc), &hyperv_ref_tsc.tsc_ref_dma,
  213             BUS_DMA_WAITOK | BUS_DMA_ZERO);
  214         if (hyperv_ref_tsc.tsc_ref == NULL) {
  215                 printf("hyperv: reftsc page allocation failed\n");
  216                 return;
  217         }
  218 
  219         orig = rdmsr(MSR_HV_REFERENCE_TSC);
  220         val = MSR_HV_REFTSC_ENABLE | (orig & MSR_HV_REFTSC_RSVD_MASK) |
  221             ((hyperv_ref_tsc.tsc_ref_dma.hv_paddr >> PAGE_SHIFT) <<
  222              MSR_HV_REFTSC_PGSHIFT);
  223         wrmsr(MSR_HV_REFERENCE_TSC, val);
  224 
  225         /* Register "enlightened" timecounter. */
  226         tc_init(&hyperv_tsc_timecounter);
  227 
  228         /* Install 64 bits timecounter method for other modules to use. */
  229         KASSERT(tc64 != NULL, ("tc64 is not set"));
  230         hyperv_tc64 = tc64;
  231 
  232         /* Add device for mmap(2). */
  233         make_dev(&hyperv_tsc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0444,
  234             HYPERV_REFTSC_DEVNAME);
  235 }
  236 SYSINIT(hyperv_tsc_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, hyperv_tsc_tcinit,
  237     NULL);

Cache object: deb77871a459315cdfdb385073da1abb


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.