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// +build aix
6
7package runtime
8
9import (
10	"internal/cpu"
11	"unsafe"
12)
13
14const (
15	threadStackSize = 0x100000 // size of a thread stack allocated by OS
16)
17
18// funcDescriptor is a structure representing a function descriptor
19// A variable with this type is always created in assembler
20type funcDescriptor struct {
21	fn         uintptr
22	toc        uintptr
23	envPointer uintptr // unused in Golang
24}
25
26type mOS struct {
27	waitsema uintptr // semaphore for parking on locks
28	perrno   uintptr // pointer to tls errno
29}
30
31//go:nosplit
32func semacreate(mp *m) {
33	if mp.waitsema != 0 {
34		return
35	}
36
37	var sem *semt
38
39	// Call libc's malloc rather than malloc. This will
40	// allocate space on the C heap. We can't call mallocgc
41	// here because it could cause a deadlock.
42	sem = (*semt)(malloc(unsafe.Sizeof(*sem)))
43	if sem_init(sem, 0, 0) != 0 {
44		throw("sem_init")
45	}
46	mp.waitsema = uintptr(unsafe.Pointer(sem))
47}
48
49//go:nosplit
50func semasleep(ns int64) int32 {
51	_m_ := getg().m
52	if ns >= 0 {
53		var ts timespec
54
55		if clock_gettime(_CLOCK_REALTIME, &ts) != 0 {
56			throw("clock_gettime")
57		}
58		ts.tv_sec += ns / 1e9
59		ts.tv_nsec += ns % 1e9
60		if ts.tv_nsec >= 1e9 {
61			ts.tv_sec++
62			ts.tv_nsec -= 1e9
63		}
64
65		if r, err := sem_timedwait((*semt)(unsafe.Pointer(_m_.waitsema)), &ts); r != 0 {
66			if err == _ETIMEDOUT || err == _EAGAIN || err == _EINTR {
67				return -1
68			}
69			println("sem_timedwait err ", err, " ts.tv_sec ", ts.tv_sec, " ts.tv_nsec ", ts.tv_nsec, " ns ", ns, " id ", _m_.id)
70			throw("sem_timedwait")
71		}
72		return 0
73	}
74	for {
75		r1, err := sem_wait((*semt)(unsafe.Pointer(_m_.waitsema)))
76		if r1 == 0 {
77			break
78		}
79		if err == _EINTR {
80			continue
81		}
82		throw("sem_wait")
83	}
84	return 0
85}
86
87//go:nosplit
88func semawakeup(mp *m) {
89	if sem_post((*semt)(unsafe.Pointer(mp.waitsema))) != 0 {
90		throw("sem_post")
91	}
92}
93
94func osinit() {
95	ncpu = int32(sysconf(__SC_NPROCESSORS_ONLN))
96	physPageSize = sysconf(__SC_PAGE_SIZE)
97	setupSystemConf()
98}
99
100// newosproc0 is a version of newosproc that can be called before the runtime
101// is initialized.
102//
103// This function is not safe to use after initialization as it does not pass an M as fnarg.
104//
105//go:nosplit
106func newosproc0(stacksize uintptr, fn *funcDescriptor) {
107	var (
108		attr pthread_attr
109		oset sigset
110		tid  pthread
111	)
112
113	if pthread_attr_init(&attr) != 0 {
114		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
115		exit(1)
116	}
117
118	if pthread_attr_setstacksize(&attr, threadStackSize) != 0 {
119		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
120		exit(1)
121	}
122
123	if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
124		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
125		exit(1)
126	}
127
128	// Disable signals during create, so that the new thread starts
129	// with signals disabled. It will enable them in minit.
130	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
131	var ret int32
132	for tries := 0; tries < 20; tries++ {
133		// pthread_create can fail with EAGAIN for no reasons
134		// but it will be ok if it retries.
135		ret = pthread_create(&tid, &attr, fn, nil)
136		if ret != _EAGAIN {
137			break
138		}
139		usleep(uint32(tries+1) * 1000) // Milliseconds.
140	}
141	sigprocmask(_SIG_SETMASK, &oset, nil)
142	if ret != 0 {
143		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
144		exit(1)
145	}
146
147}
148
149var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
150
151// Called to do synchronous initialization of Go code built with
152// -buildmode=c-archive or -buildmode=c-shared.
153// None of the Go runtime is initialized.
154//go:nosplit
155//go:nowritebarrierrec
156func libpreinit() {
157	initsig(true)
158}
159
160// Ms related functions
161func mpreinit(mp *m) {
162	mp.gsignal = malg(32 * 1024) // AIX wants >= 8K
163	mp.gsignal.m = mp
164}
165
166// errno address must be retrieved by calling _Errno libc function.
167// This will return a pointer to errno
168func miniterrno() {
169	mp := getg().m
170	r, _ := syscall0(&libc__Errno)
171	mp.perrno = r
172
173}
174
175func minit() {
176	miniterrno()
177	minitSignals()
178	getg().m.procid = uint64(pthread_self())
179}
180
181func unminit() {
182	unminitSignals()
183}
184
185// tstart is a function descriptor to _tstart defined in assembly.
186var tstart funcDescriptor
187
188func newosproc(mp *m) {
189	var (
190		attr pthread_attr
191		oset sigset
192		tid  pthread
193	)
194
195	if pthread_attr_init(&attr) != 0 {
196		throw("pthread_attr_init")
197	}
198
199	if pthread_attr_setstacksize(&attr, threadStackSize) != 0 {
200		throw("pthread_attr_getstacksize")
201	}
202
203	if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
204		throw("pthread_attr_setdetachstate")
205	}
206
207	// Disable signals during create, so that the new thread starts
208	// with signals disabled. It will enable them in minit.
209	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
210	var ret int32
211	for tries := 0; tries < 20; tries++ {
212		// pthread_create can fail with EAGAIN for no reasons
213		// but it will be ok if it retries.
214		ret = pthread_create(&tid, &attr, &tstart, unsafe.Pointer(mp))
215		if ret != _EAGAIN {
216			break
217		}
218		usleep(uint32(tries+1) * 1000) // Milliseconds.
219	}
220	sigprocmask(_SIG_SETMASK, &oset, nil)
221	if ret != 0 {
222		print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", ret, ")\n")
223		if ret == _EAGAIN {
224			println("runtime: may need to increase max user processes (ulimit -u)")
225		}
226		throw("newosproc")
227	}
228
229}
230
231func exitThread(wait *uint32) {
232	// We should never reach exitThread on AIX because we let
233	// libc clean up threads.
234	throw("exitThread")
235}
236
237var urandom_dev = []byte("/dev/urandom\x00")
238
239//go:nosplit
240func getRandomData(r []byte) {
241	fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
242	n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
243	closefd(fd)
244	extendRandom(r, int(n))
245}
246
247func goenvs() {
248	goenvs_unix()
249}
250
251/* SIGNAL */
252
253const (
254	_NSIG = 256
255)
256
257// sigtramp is a function descriptor to _sigtramp defined in assembly
258var sigtramp funcDescriptor
259
260//go:nosplit
261//go:nowritebarrierrec
262func setsig(i uint32, fn uintptr) {
263	var sa sigactiont
264	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
265	sa.sa_mask = sigset_all
266	if fn == funcPC(sighandler) {
267		fn = uintptr(unsafe.Pointer(&sigtramp))
268	}
269	sa.sa_handler = fn
270	sigaction(uintptr(i), &sa, nil)
271
272}
273
274//go:nosplit
275//go:nowritebarrierrec
276func setsigstack(i uint32) {
277	var sa sigactiont
278	sigaction(uintptr(i), nil, &sa)
279	if sa.sa_flags&_SA_ONSTACK != 0 {
280		return
281	}
282	sa.sa_flags |= _SA_ONSTACK
283	sigaction(uintptr(i), &sa, nil)
284}
285
286//go:nosplit
287//go:nowritebarrierrec
288func getsig(i uint32) uintptr {
289	var sa sigactiont
290	sigaction(uintptr(i), nil, &sa)
291	return sa.sa_handler
292}
293
294// setSignaltstackSP sets the ss_sp field of a stackt.
295//go:nosplit
296func setSignalstackSP(s *stackt, sp uintptr) {
297	*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
298}
299
300//go:nosplit
301func (c *sigctxt) fixsigcode(sig uint32) {
302	switch sig {
303	case _SIGPIPE:
304		// For SIGPIPE, c.sigcode() isn't set to _SI_USER as on Linux.
305		// Therefore, raisebadsignal won't raise SIGPIPE again if
306		// it was deliver in a non-Go thread.
307		c.set_sigcode(_SI_USER)
308	}
309}
310
311//go:nosplit
312//go:nowritebarrierrec
313func sigaddset(mask *sigset, i int) {
314	(*mask)[(i-1)/64] |= 1 << ((uint32(i) - 1) & 63)
315}
316
317func sigdelset(mask *sigset, i int) {
318	(*mask)[(i-1)/64] &^= 1 << ((uint32(i) - 1) & 63)
319}
320
321const (
322	_CLOCK_REALTIME  = 9
323	_CLOCK_MONOTONIC = 10
324)
325
326//go:nosplit
327func nanotime1() int64 {
328	tp := &timespec{}
329	if clock_gettime(_CLOCK_REALTIME, tp) != 0 {
330		throw("syscall clock_gettime failed")
331	}
332	return tp.tv_sec*1000000000 + tp.tv_nsec
333}
334
335func walltime1() (sec int64, nsec int32) {
336	ts := &timespec{}
337	if clock_gettime(_CLOCK_REALTIME, ts) != 0 {
338		throw("syscall clock_gettime failed")
339	}
340	return ts.tv_sec, int32(ts.tv_nsec)
341}
342
343const (
344	// getsystemcfg constants
345	_SC_IMPL     = 2
346	_IMPL_POWER8 = 0x10000
347	_IMPL_POWER9 = 0x20000
348)
349
350// setupSystemConf retrieves information about the CPU and updates
351// cpu.HWCap variables.
352func setupSystemConf() {
353	impl := getsystemcfg(_SC_IMPL)
354	if impl&_IMPL_POWER8 != 0 {
355		cpu.HWCap2 |= cpu.PPC_FEATURE2_ARCH_2_07
356	}
357	if impl&_IMPL_POWER9 != 0 {
358		cpu.HWCap2 |= cpu.PPC_FEATURE2_ARCH_3_00
359	}
360}
361
362//go:nosplit
363func fcntl(fd, cmd, arg int32) int32 {
364	r, _ := syscall3(&libc_fcntl, uintptr(fd), uintptr(cmd), uintptr(arg))
365	return int32(r)
366}
367
368//go:nosplit
369func closeonexec(fd int32) {
370	fcntl(fd, _F_SETFD, _FD_CLOEXEC)
371}
372
373//go:nosplit
374func setNonblock(fd int32) {
375	flags := fcntl(fd, _F_GETFL, 0)
376	fcntl(fd, _F_SETFL, flags|_O_NONBLOCK)
377}
378