1// Copyright 2014 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
5package runtime
6
7import (
8	"runtime/internal/atomic"
9	"runtime/internal/sys"
10	"unsafe"
11)
12
13type mOS struct {
14	waitsemacount uint32
15}
16
17//go:noescape
18//extern lwp_park
19func lwp_park(ts int32, rel int32, abstime *timespec, unpark int32, hint, unparkhint unsafe.Pointer) int32
20
21//go:noescape
22//extern lwp_unpark
23func lwp_unpark(lwp int32, hint unsafe.Pointer) int32
24
25//go:noescape
26//extern sysctl
27func sysctl(*uint32, uint32, *byte, *uintptr, *byte, uintptr) int32
28
29// From NetBSD's <sys/sysctl.h>
30const (
31	_CTL_HW      = 6
32	_HW_NCPU     = 3
33	_HW_PAGESIZE = 7
34)
35
36func getncpu() int32 {
37	mib := [2]uint32{_CTL_HW, _HW_NCPU}
38	out := uint32(0)
39	nout := unsafe.Sizeof(out)
40	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
41	if ret >= 0 {
42		return int32(out)
43	}
44	return 1
45}
46
47func getPageSize() uintptr {
48	mib := [2]uint32{_CTL_HW, _HW_PAGESIZE}
49	out := uint32(0)
50	nout := unsafe.Sizeof(out)
51	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
52	if ret >= 0 {
53		return uintptr(out)
54	}
55	return 0
56}
57
58//go:nosplit
59func semacreate(mp *m) {
60}
61
62//go:nosplit
63func semasleep(ns int64) int32 {
64	_g_ := getg()
65	var deadline int64
66	if ns >= 0 {
67		deadline = nanotime() + ns
68	}
69
70	for {
71		v := atomic.Load(&_g_.m.waitsemacount)
72		if v > 0 {
73			if atomic.Cas(&_g_.m.waitsemacount, v, v-1) {
74				return 0 // semaphore acquired
75			}
76			continue
77		}
78
79		// Sleep until unparked by semawakeup or timeout.
80		var tsp *timespec
81		var ts timespec
82		if ns >= 0 {
83			wait := deadline - nanotime()
84			if wait <= 0 {
85				return -1
86			}
87			ts.setNsec(wait)
88			tsp = &ts
89		}
90		ret := lwp_park(_CLOCK_MONOTONIC, _TIMER_RELTIME, tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil)
91		if ret == _ETIMEDOUT {
92			return -1
93		}
94	}
95}
96
97//go:nosplit
98func semawakeup(mp *m) {
99	atomic.Xadd(&mp.waitsemacount, 1)
100	// From NetBSD's _lwp_unpark(2) manual:
101	// "If the target LWP is not currently waiting, it will return
102	// immediately upon the next call to _lwp_park()."
103	ret := lwp_unpark(int32(mp.procid), unsafe.Pointer(&mp.waitsemacount))
104	if ret != 0 && ret != _ESRCH {
105		// semawakeup can be called on signal stack.
106		systemstack(func() {
107			print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " ret=", ret, "\n")
108		})
109	}
110}
111
112func osinit() {
113	ncpu = getncpu()
114	if physPageSize == 0 {
115		physPageSize = getPageSize()
116	}
117}
118
119func sysargs(argc int32, argv **byte) {
120	n := argc + 1
121
122	// skip over argv, envp to get to auxv
123	for argv_index(argv, n) != nil {
124		n++
125	}
126
127	// skip NULL separator
128	n++
129
130	// now argv+n is auxv
131	auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize))
132	sysauxv(auxv[:])
133}
134
135const (
136	_AT_NULL   = 0 // Terminates the vector
137	_AT_PAGESZ = 6 // Page size in bytes
138)
139
140func sysauxv(auxv []uintptr) {
141	for i := 0; auxv[i] != _AT_NULL; i += 2 {
142		tag, val := auxv[i], auxv[i+1]
143		switch tag {
144		case _AT_PAGESZ:
145			physPageSize = val
146		}
147	}
148}
149