// 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.
// +build aix
package runtime
import (
"internal/cpu"
"unsafe"
)
const (
threadStackSize = 0x100000 // size of a thread stack allocated by OS
)
// funcDescriptor is a structure representing a function descriptor
// A variable with this type is always created in assembler
type funcDescriptor struct {
fn uintptr
toc uintptr
envPointer uintptr // unused in Golang
}
type mOS struct {
waitsema uintptr // semaphore for parking on locks
perrno uintptr // pointer to tls errno
}
//go:nosplit
func semacreate(mp *m) {
if mp.waitsema != 0 {
return
}
var sem *semt
// Call libc's malloc rather than malloc. This will
// allocate space on the C heap. We can't call mallocgc
// here because it could cause a deadlock.
sem = (*semt)(malloc(unsafe.Sizeof(*sem)))
if sem_init(sem, 0, 0) != 0 {
throw("sem_init")
}
mp.waitsema = uintptr(unsafe.Pointer(sem))
}
//go:nosplit
func semasleep(ns int64) int32 {
_m_ := getg().m
if ns >= 0 {
var ts timespec
if clock_gettime(_CLOCK_REALTIME, &ts) != 0 {
throw("clock_gettime")
}
ts.tv_sec += ns / 1e9
ts.tv_nsec += ns % 1e9
if ts.tv_nsec >= 1e9 {
ts.tv_sec++
ts.tv_nsec -= 1e9
}
if r, err := sem_timedwait((*semt)(unsafe.Pointer(_m_.waitsema)), &ts); r != 0 {
if err == _ETIMEDOUT || err == _EAGAIN || err == _EINTR {
return -1
}
println("sem_timedwait err ", err, " ts.tv_sec ", ts.tv_sec, " ts.tv_nsec ", ts.tv_nsec, " ns ", ns, " id ", _m_.id)
throw("sem_timedwait")
}
return 0
}
for {
r1, err := sem_wait((*semt)(unsafe.Pointer(_m_.waitsema)))
if r1 == 0 {
break
}
if err == _EINTR {
continue
}
throw("sem_wait")
}
return 0
}
//go:nosplit
func semawakeup(mp *m) {
if sem_post((*semt)(unsafe.Pointer(mp.waitsema))) != 0 {
throw("sem_post")
}
}
func osinit() {
ncpu = int32(sysconf(__SC_NPROCESSORS_ONLN))
physPageSize = sysconf(__SC_PAGE_SIZE)
setupSystemConf()
}
// Ms related functions
func mpreinit(mp *m) {
mp.gsignal = malg(32 * 1024) // AIX wants >= 8K
mp.gsignal.m = mp
}
// errno address must be retrieved by calling _Errno libc function.
// This will return a pointer to errno
func miniterrno() {
mp := getg().m
r, _ := syscall0(&libc__Errno)
mp.perrno = r
}
func minit() {
miniterrno()
minitSignals()
}
func unminit() {
unminitSignals()
}
// tstart is a function descriptor to _tstart defined in assembly.
var tstart funcDescriptor
func newosproc(mp *m) {
var (
attr pthread_attr
oset sigset
tid pthread
)
if pthread_attr_init(&attr) != 0 {
throw("pthread_attr_init")
}
if pthread_attr_setstacksize(&attr, threadStackSize) != 0 {
throw("pthread_attr_getstacksize")
}
if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
throw("pthread_attr_setdetachstate")
}
// Disable signals during create, so that the new thread starts
// with signals disabled. It will enable them in minit.
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
var ret int32
for tries := 0; tries < 20; tries++ {
// pthread_create can fail with EAGAIN for no reasons
// but it will be ok if it retries.
ret = pthread_create(&tid, &attr, &tstart, unsafe.Pointer(mp))
if ret != _EAGAIN {
break
}
usleep(uint32(tries+1) * 1000) // Milliseconds.
}
sigprocmask(_SIG_SETMASK, &oset, nil)
if ret != 0 {
print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", ret, ")\n")
if ret == _EAGAIN {
println("runtime: may need to increase max user processes (ulimit -u)")
}
throw("newosproc")
}
}
func exitThread(wait *uint32) {
// We should never reach exitThread on AIX because we let
// libc clean up threads.
throw("exitThread")
}
var urandom_dev = []byte("/dev/urandom\x00")
//go:nosplit
func getRandomData(r []byte) {
fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
closefd(fd)
extendRandom(r, int(n))
}
func goenvs() {
goenvs_unix()
}
/* SIGNAL */
const (
_NSIG = 256
)
// sigtramp is a function descriptor to _sigtramp defined in assembly
var sigtramp funcDescriptor
//go:nosplit
//go:nowritebarrierrec
func setsig(i uint32, fn uintptr) {
var sa sigactiont
sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
sa.sa_mask = sigset_all
if fn == funcPC(sighandler) {
fn = uintptr(unsafe.Pointer(&sigtramp))
}
sa.sa_handler = fn
sigaction(uintptr(i), &sa, nil)
}
//go:nosplit
//go:nowritebarrierrec
func setsigstack(i uint32) {
throw("Not yet implemented\n")
}
//go:nosplit
//go:nowritebarrierrec
func getsig(i uint32) uintptr {
var sa sigactiont
sigaction(uintptr(i), nil, &sa)
return sa.sa_handler
}
// setSignaltstackSP sets the ss_sp field of a stackt.
//go:nosplit
func setSignalstackSP(s *stackt, sp uintptr) {
*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
}
func (c *sigctxt) fixsigcode(sig uint32) {
}
func sigaddset(mask *sigset, i int) {
(*mask)[(i-1)/64] |= 1 << ((uint32(i) - 1) & 63)
}
func sigdelset(mask *sigset, i int) {
(*mask)[(i-1)/64] &^= 1 << ((uint32(i) - 1) & 63)
}
const (
_CLOCK_REALTIME = 9
_CLOCK_MONOTONIC = 10
)
//go:nosplit
func nanotime() int64 {
tp := ×pec{}
if clock_gettime(_CLOCK_REALTIME, tp) != 0 {
throw("syscall clock_gettime failed")
}
return tp.tv_sec*1000000000 + tp.tv_nsec
}
func walltime() (sec int64, nsec int32) {
ts := ×pec{}
if clock_gettime(_CLOCK_REALTIME, ts) != 0 {
throw("syscall clock_gettime failed")
}
return ts.tv_sec, int32(ts.tv_nsec)
}
const (
// getsystemcfg constants
_SC_IMPL = 2
_IMPL_POWER8 = 0x10000
_IMPL_POWER9 = 0x20000
)
// setupSystemConf retrieves information about the CPU and updates
// cpu.HWCap variables.
func setupSystemConf() {
impl := getsystemcfg(_SC_IMPL)
if impl&_IMPL_POWER8 != 0 {
cpu.HWCap2 |= cpu.PPC_FEATURE2_ARCH_2_07
}
if impl&_IMPL_POWER9 != 0 {
cpu.HWCap2 |= cpu.PPC_FEATURE2_ARCH_3_00
}
}