1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build freebsd && (386 || amd64)
6
7package runtime
8
9import (
10	"internal/runtime/atomic"
11	"unsafe"
12)
13
14const (
15	_VDSO_TH_ALGO_X86_TSC  = 1
16	_VDSO_TH_ALGO_X86_HPET = 2
17)
18
19const (
20	_HPET_DEV_MAP_MAX  = 10
21	_HPET_MAIN_COUNTER = 0xf0 /* Main counter register */
22
23	hpetDevPath = "/dev/hpetX\x00"
24)
25
26var hpetDevMap [_HPET_DEV_MAP_MAX]uintptr
27
28//go:nosplit
29func (th *vdsoTimehands) getTSCTimecounter() uint32 {
30	tsc := cputicks()
31	if th.x86_shift > 0 {
32		tsc >>= th.x86_shift
33	}
34	return uint32(tsc)
35}
36
37//go:nosplit
38func (th *vdsoTimehands) getHPETTimecounter() (uint32, bool) {
39	idx := int(th.x86_hpet_idx)
40	if idx >= len(hpetDevMap) {
41		return 0, false
42	}
43
44	p := atomic.Loaduintptr(&hpetDevMap[idx])
45	if p == 0 {
46		systemstack(func() { initHPETTimecounter(idx) })
47		p = atomic.Loaduintptr(&hpetDevMap[idx])
48	}
49	if p == ^uintptr(0) {
50		return 0, false
51	}
52	return *(*uint32)(unsafe.Pointer(p + _HPET_MAIN_COUNTER)), true
53}
54
55//go:systemstack
56func initHPETTimecounter(idx int) {
57	const digits = "0123456789"
58
59	var devPath [len(hpetDevPath)]byte
60	copy(devPath[:], hpetDevPath)
61	devPath[9] = digits[idx]
62
63	fd := open(&devPath[0], 0 /* O_RDONLY */ |_O_CLOEXEC, 0)
64	if fd < 0 {
65		atomic.Casuintptr(&hpetDevMap[idx], 0, ^uintptr(0))
66		return
67	}
68
69	addr, mmapErr := mmap(nil, physPageSize, _PROT_READ, _MAP_SHARED, fd, 0)
70	closefd(fd)
71	newP := uintptr(addr)
72	if mmapErr != 0 {
73		newP = ^uintptr(0)
74	}
75	if !atomic.Casuintptr(&hpetDevMap[idx], 0, newP) && mmapErr == 0 {
76		munmap(addr, physPageSize)
77	}
78}
79
80//go:nosplit
81func (th *vdsoTimehands) getTimecounter() (uint32, bool) {
82	switch th.algo {
83	case _VDSO_TH_ALGO_X86_TSC:
84		return th.getTSCTimecounter(), true
85	case _VDSO_TH_ALGO_X86_HPET:
86		return th.getHPETTimecounter()
87	default:
88		return 0, false
89	}
90}
91