| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build freebsd && (386 || amd64) |
| |
| package runtime |
| |
| import ( |
| "runtime/internal/atomic" |
| "unsafe" |
| ) |
| |
| const ( |
| _VDSO_TH_ALGO_X86_TSC = 1 |
| _VDSO_TH_ALGO_X86_HPET = 2 |
| ) |
| |
| const ( |
| _HPET_DEV_MAP_MAX = 10 |
| _HPET_MAIN_COUNTER = 0xf0 /* Main counter register */ |
| |
| hpetDevPath = "/dev/hpetX\x00" |
| ) |
| |
| var hpetDevMap [_HPET_DEV_MAP_MAX]uintptr |
| |
| //go:nosplit |
| func (th *vdsoTimehands) getTSCTimecounter() uint32 { |
| tsc := cputicks() |
| if th.x86_shift > 0 { |
| tsc >>= th.x86_shift |
| } |
| return uint32(tsc) |
| } |
| |
| //go:nosplit |
| func (th *vdsoTimehands) getHPETTimecounter() (uint32, bool) { |
| idx := int(th.x86_hpet_idx) |
| if idx >= len(hpetDevMap) { |
| return 0, false |
| } |
| |
| p := atomic.Loaduintptr(&hpetDevMap[idx]) |
| if p == 0 { |
| systemstack(func() { initHPETTimecounter(idx) }) |
| p = atomic.Loaduintptr(&hpetDevMap[idx]) |
| } |
| if p == ^uintptr(0) { |
| return 0, false |
| } |
| return *(*uint32)(unsafe.Pointer(p + _HPET_MAIN_COUNTER)), true |
| } |
| |
| //go:systemstack |
| func initHPETTimecounter(idx int) { |
| const digits = "0123456789" |
| |
| var devPath [len(hpetDevPath)]byte |
| copy(devPath[:], hpetDevPath) |
| devPath[9] = digits[idx] |
| |
| fd := open(&devPath[0], 0 /* O_RDONLY */ |_O_CLOEXEC, 0) |
| if fd < 0 { |
| atomic.Casuintptr(&hpetDevMap[idx], 0, ^uintptr(0)) |
| return |
| } |
| |
| addr, mmapErr := mmap(nil, physPageSize, _PROT_READ, _MAP_SHARED, fd, 0) |
| closefd(fd) |
| newP := uintptr(addr) |
| if mmapErr != 0 { |
| newP = ^uintptr(0) |
| } |
| if !atomic.Casuintptr(&hpetDevMap[idx], 0, newP) && mmapErr == 0 { |
| munmap(addr, physPageSize) |
| } |
| } |
| |
| //go:nosplit |
| func (th *vdsoTimehands) getTimecounter() (uint32, bool) { |
| switch th.algo { |
| case _VDSO_TH_ALGO_X86_TSC: |
| return th.getTSCTimecounter(), true |
| case _VDSO_TH_ALGO_X86_HPET: |
| return th.getHPETTimecounter() |
| default: |
| return 0, false |
| } |
| } |