1// Copyright 2011 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
18func setitimer(mode int32, new, old *itimerval)
19
20//go:noescape
21func sigaction(sig uint32, new, old *sigactiont)
22
23//go:noescape
24func sigaltstack(new, old *stackt)
25
26//go:noescape
27func obsdsigprocmask(how int32, new sigset) sigset
28
29//go:nosplit
30//go:nowritebarrierrec
31func sigprocmask(how int32, new, old *sigset) {
32	n := sigset(0)
33	if new != nil {
34		n = *new
35	}
36	r := obsdsigprocmask(how, n)
37	if old != nil {
38		*old = r
39	}
40}
41
42//go:noescape
43func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
44
45func raiseproc(sig uint32)
46
47func getthrid() int32
48func thrkill(tid int32, sig int)
49
50//go:noescape
51func tfork(param *tforkt, psize uintptr, mm *m, gg *g, fn uintptr) int32
52
53//go:noescape
54func thrsleep(ident uintptr, clock_id int32, tsp *timespec, lock uintptr, abort *uint32) int32
55
56//go:noescape
57func thrwakeup(ident uintptr, n int32) int32
58
59func osyield()
60
61func kqueue() int32
62
63//go:noescape
64func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32
65
66func pipe() (r, w int32, errno int32)
67func pipe2(flags int32) (r, w int32, errno int32)
68func closeonexec(fd int32)
69func setNonblock(fd int32)
70
71const (
72	_ESRCH       = 3
73	_EWOULDBLOCK = _EAGAIN
74	_ENOTSUP     = 91
75
76	// From OpenBSD's sys/time.h
77	_CLOCK_REALTIME  = 0
78	_CLOCK_VIRTUAL   = 1
79	_CLOCK_PROF      = 2
80	_CLOCK_MONOTONIC = 3
81)
82
83type sigset uint32
84
85var sigset_all = ^sigset(0)
86
87// From OpenBSD's <sys/sysctl.h>
88const (
89	_CTL_KERN   = 1
90	_KERN_OSREV = 3
91
92	_CTL_HW        = 6
93	_HW_NCPU       = 3
94	_HW_PAGESIZE   = 7
95	_HW_NCPUONLINE = 25
96)
97
98func sysctlInt(mib []uint32) (int32, bool) {
99	var out int32
100	nout := unsafe.Sizeof(out)
101	ret := sysctl(&mib[0], uint32(len(mib)), (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
102	if ret < 0 {
103		return 0, false
104	}
105	return out, true
106}
107
108func getncpu() int32 {
109	// Try hw.ncpuonline first because hw.ncpu would report a number twice as
110	// high as the actual CPUs running on OpenBSD 6.4 with hyperthreading
111	// disabled (hw.smt=0). See https://golang.org/issue/30127
112	if n, ok := sysctlInt([]uint32{_CTL_HW, _HW_NCPUONLINE}); ok {
113		return int32(n)
114	}
115	if n, ok := sysctlInt([]uint32{_CTL_HW, _HW_NCPU}); ok {
116		return int32(n)
117	}
118	return 1
119}
120
121func getPageSize() uintptr {
122	if ps, ok := sysctlInt([]uint32{_CTL_HW, _HW_PAGESIZE}); ok {
123		return uintptr(ps)
124	}
125	return 0
126}
127
128func getOSRev() int {
129	if osrev, ok := sysctlInt([]uint32{_CTL_KERN, _KERN_OSREV}); ok {
130		return int(osrev)
131	}
132	return 0
133}
134
135//go:nosplit
136func semacreate(mp *m) {
137}
138
139//go:nosplit
140func semasleep(ns int64) int32 {
141	_g_ := getg()
142
143	// Compute sleep deadline.
144	var tsp *timespec
145	if ns >= 0 {
146		var ts timespec
147		ts.setNsec(ns + nanotime())
148		tsp = &ts
149	}
150
151	for {
152		v := atomic.Load(&_g_.m.waitsemacount)
153		if v > 0 {
154			if atomic.Cas(&_g_.m.waitsemacount, v, v-1) {
155				return 0 // semaphore acquired
156			}
157			continue
158		}
159
160		// Sleep until woken by semawakeup or timeout; or abort if waitsemacount != 0.
161		//
162		// From OpenBSD's __thrsleep(2) manual:
163		// "The abort argument, if not NULL, points to an int that will
164		// be examined [...] immediately before blocking. If that int
165		// is non-zero then __thrsleep() will immediately return EINTR
166		// without blocking."
167		ret := thrsleep(uintptr(unsafe.Pointer(&_g_.m.waitsemacount)), _CLOCK_MONOTONIC, tsp, 0, &_g_.m.waitsemacount)
168		if ret == _EWOULDBLOCK {
169			return -1
170		}
171	}
172}
173
174//go:nosplit
175func semawakeup(mp *m) {
176	atomic.Xadd(&mp.waitsemacount, 1)
177	ret := thrwakeup(uintptr(unsafe.Pointer(&mp.waitsemacount)), 1)
178	if ret != 0 && ret != _ESRCH {
179		// semawakeup can be called on signal stack.
180		systemstack(func() {
181			print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " ret=", ret, "\n")
182		})
183	}
184}
185
186// May run with m.p==nil, so write barriers are not allowed.
187//go:nowritebarrier
188func newosproc(mp *m) {
189	stk := unsafe.Pointer(mp.g0.stack.hi)
190	if false {
191		print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n")
192	}
193
194	// Stack pointer must point inside stack area (as marked with MAP_STACK),
195	// rather than at the top of it.
196	param := tforkt{
197		tf_tcb:   unsafe.Pointer(&mp.tls[0]),
198		tf_tid:   nil, // minit will record tid
199		tf_stack: uintptr(stk) - sys.PtrSize,
200	}
201
202	var oset sigset
203	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
204	ret := tfork(&param, unsafe.Sizeof(param), mp, mp.g0, funcPC(mstart))
205	sigprocmask(_SIG_SETMASK, &oset, nil)
206
207	if ret < 0 {
208		print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n")
209		if ret == -_EAGAIN {
210			println("runtime: may need to increase max user processes (ulimit -p)")
211		}
212		throw("runtime.newosproc")
213	}
214}
215
216func osinit() {
217	ncpu = getncpu()
218	physPageSize = getPageSize()
219	haveMapStack = getOSRev() >= 201805 // OpenBSD 6.3
220}
221
222var urandom_dev = []byte("/dev/urandom\x00")
223
224//go:nosplit
225func getRandomData(r []byte) {
226	fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
227	n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
228	closefd(fd)
229	extendRandom(r, int(n))
230}
231
232func goenvs() {
233	goenvs_unix()
234}
235
236// Called to initialize a new m (including the bootstrap m).
237// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
238func mpreinit(mp *m) {
239	mp.gsignal = malg(32 * 1024)
240	mp.gsignal.m = mp
241}
242
243// Called to initialize a new m (including the bootstrap m).
244// Called on the new thread, can not allocate memory.
245func minit() {
246	getg().m.procid = uint64(getthrid())
247	minitSignals()
248}
249
250// Called from dropm to undo the effect of an minit.
251//go:nosplit
252func unminit() {
253	unminitSignals()
254}
255
256func sigtramp()
257
258type sigactiont struct {
259	sa_sigaction uintptr
260	sa_mask      uint32
261	sa_flags     int32
262}
263
264//go:nosplit
265//go:nowritebarrierrec
266func setsig(i uint32, fn uintptr) {
267	var sa sigactiont
268	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
269	sa.sa_mask = uint32(sigset_all)
270	if fn == funcPC(sighandler) {
271		fn = funcPC(sigtramp)
272	}
273	sa.sa_sigaction = fn
274	sigaction(i, &sa, nil)
275}
276
277//go:nosplit
278//go:nowritebarrierrec
279func setsigstack(i uint32) {
280	throw("setsigstack")
281}
282
283//go:nosplit
284//go:nowritebarrierrec
285func getsig(i uint32) uintptr {
286	var sa sigactiont
287	sigaction(i, nil, &sa)
288	return sa.sa_sigaction
289}
290
291// setSignaltstackSP sets the ss_sp field of a stackt.
292//go:nosplit
293func setSignalstackSP(s *stackt, sp uintptr) {
294	s.ss_sp = sp
295}
296
297//go:nosplit
298//go:nowritebarrierrec
299func sigaddset(mask *sigset, i int) {
300	*mask |= 1 << (uint32(i) - 1)
301}
302
303func sigdelset(mask *sigset, i int) {
304	*mask &^= 1 << (uint32(i) - 1)
305}
306
307//go:nosplit
308func (c *sigctxt) fixsigcode(sig uint32) {
309}
310
311var haveMapStack = false
312
313func osStackAlloc(s *mspan) {
314	// OpenBSD 6.4+ requires that stacks be mapped with MAP_STACK.
315	// It will check this on entry to system calls, traps, and
316	// when switching to the alternate system stack.
317	//
318	// This function is called before s is used for any data, so
319	// it's safe to simply re-map it.
320	osStackRemap(s, _MAP_STACK)
321}
322
323func osStackFree(s *mspan) {
324	// Undo MAP_STACK.
325	osStackRemap(s, 0)
326}
327
328func osStackRemap(s *mspan, flags int32) {
329	if !haveMapStack {
330		// OpenBSD prior to 6.3 did not have MAP_STACK and so
331		// the following mmap will fail. But it also didn't
332		// require MAP_STACK (obviously), so there's no need
333		// to do the mmap.
334		return
335	}
336	a, err := mmap(unsafe.Pointer(s.base()), s.npages*pageSize, _PROT_READ|_PROT_WRITE, _MAP_PRIVATE|_MAP_ANON|_MAP_FIXED|flags, -1, 0)
337	if err != 0 || uintptr(a) != s.base() {
338		print("runtime: remapping stack memory ", hex(s.base()), " ", s.npages*pageSize, " a=", a, " err=", err, "\n")
339		throw("remapping stack memory failed")
340	}
341}
342
343func raise(sig uint32) {
344	thrkill(getthrid(), int(sig))
345}
346
347func signalM(mp *m, sig int) {
348	thrkill(int32(mp.procid), sig)
349}
350