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 "unsafe" 9) 10 11type mOS struct{} 12 13//go:noescape 14//extern _umtx_op 15func sys_umtx_op(addr *uint32, mode int32, val uint32, uaddr1 uinptr, ts *umtx_time) int32 16 17//go:noescape 18//extern sysctl 19func sysctl(*uint32, uint32, *byte, *uintptr, *byte, uintptr) int32 20 21const ( 22 _CTL_MAXNAME = 24 23 _CPU_LEVEL_WHICH = 3 24 _CPU_WHICH_PID = 2 25) 26 27// From FreeBSD's <sys/sysctl.h> 28const ( 29 _CTL_HW = 6 30 _HW_PAGESIZE = 7 31) 32 33// Undocumented numbers from FreeBSD's lib/libc/gen/sysctlnametomib.c. 34const ( 35 _CTL_QUERY = 0 36 _CTL_QUERY_MIB = 3 37) 38 39// sysctlnametomib fill mib with dynamically assigned sysctl entries of name, 40// return count of effected mib slots, return 0 on error. 41func sysctlnametomib(name []byte, mib *[_CTL_MAXNAME]uint32) uint32 { 42 oid := [2]uint32{_CTL_QUERY, _CTL_QUERY_MIB} 43 miblen := uintptr(_CTL_MAXNAME) 44 if sysctl(&oid[0], 2, (*byte)(unsafe.Pointer(mib)), &miblen, (*byte)(unsafe.Pointer(&name[0])), (uintptr)(len(name))) < 0 { 45 return 0 46 } 47 miblen /= unsafe.Sizeof(uint32(0)) 48 if miblen <= 0 { 49 return 0 50 } 51 return uint32(miblen) 52} 53 54const ( 55 _CPU_CURRENT_PID = -1 // Current process ID. 56) 57 58//go:noescape 59//extern cpuset_getaffinity 60func cpuset_getaffinity(level int32, which int32, id int64, size uintptr, mask *byte) int32 61 62//go:systemstack 63func getncpu() int32 { 64 // Use a large buffer for the CPU mask. We're on the system 65 // stack, so this is fine, and we can't allocate memory for a 66 // dynamically-sized buffer at this point. 67 const maxCPUs = 64 * 1024 68 var mask [maxCPUs / 8]byte 69 var mib [_CTL_MAXNAME]uint32 70 71 // According to FreeBSD's /usr/src/sys/kern/kern_cpuset.c, 72 // cpuset_getaffinity return ERANGE when provided buffer size exceed the limits in kernel. 73 // Querying kern.smp.maxcpus to calculate maximum buffer size. 74 // See https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=200802 75 76 // Variable kern.smp.maxcpus introduced at Dec 23 2003, revision 123766, 77 // with dynamically assigned sysctl entries. 78 miblen := sysctlnametomib([]byte("kern.smp.maxcpus"), &mib) 79 if miblen == 0 { 80 return 1 81 } 82 83 // Query kern.smp.maxcpus. 84 dstsize := uintptr(4) 85 maxcpus := uint32(0) 86 if sysctl(&mib[0], miblen, (*byte)(unsafe.Pointer(&maxcpus)), &dstsize, nil, 0) != 0 { 87 return 1 88 } 89 90 maskSize := uintptr(int(maxcpus+7) / 8) 91 if maskSize < sys.PtrSize { 92 maskSize = sys.PtrSize 93 } 94 if maskSize > uintptr(len(mask)) { 95 maskSize = uintptr(len(mask)) 96 } 97 98 if cpuset_getaffinity(_CPU_LEVEL_WHICH, _CPU_WHICH_PID, _CPU_CURRENT_PID, 99 maskSize, (*byte)(unsafe.Pointer(&mask[0]))) != 0 { 100 return 1 101 } 102 n := int32(0) 103 for _, v := range mask[:maskSize] { 104 for v != 0 { 105 n += int32(v & 1) 106 v >>= 1 107 } 108 } 109 if n == 0 { 110 return 1 111 } 112 return n 113} 114 115func getPageSize() uintptr { 116 mib := [2]uint32{_CTL_HW, _HW_PAGESIZE} 117 out := uint32(0) 118 nout := unsafe.Sizeof(out) 119 ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) 120 if ret >= 0 { 121 return uintptr(out) 122 } 123 return 0 124} 125 126// FreeBSD's umtx_op syscall is effectively the same as Linux's futex, and 127// thus the code is largely similar. See Linux implementation 128// and lock_futex.go for comments. 129 130//go:nosplit 131func futexsleep(addr *uint32, val uint32, ns int64) { 132 systemstack(func() { 133 futexsleep1(addr, val, ns) 134 }) 135} 136 137func futexsleep1(addr *uint32, val uint32, ns int64) { 138 var utp *umtx_time 139 if ns >= 0 { 140 var ut umtx_time 141 ut._clockid = _CLOCK_MONOTONIC 142 ut._timeout.setNsec(ns) 143 utp = &ut 144 } 145 ret := sys_umtx_op(addr, _UMTX_OP_WAIT_UINT_PRIVATE, val, unsafe.Sizeof(*utp), utp) 146 if ret >= 0 || ret == -_EINTR { 147 return 148 } 149 print("umtx_wait addr=", addr, " val=", val, " ret=", ret, "\n") 150 *(*int32)(unsafe.Pointer(uintptr(0x1005))) = 0x1005 151} 152 153//go:nosplit 154func futexwakeup(addr *uint32, cnt uint32) { 155 ret := sys_umtx_op(addr, _UMTX_OP_WAKE_PRIVATE, cnt, 0, nil) 156 if ret >= 0 { 157 return 158 } 159 160 systemstack(func() { 161 print("umtx_wake_addr=", addr, " ret=", ret, "\n") 162 }) 163} 164 165func osinit() { 166 ncpu = getncpu() 167 if physPageSize == 0 { 168 physPageSize = getPageSize() 169 } 170} 171 172func sysargs(argc int32, argv **byte) { 173 n := argc + 1 174 175 // skip over argv, envp to get to auxv 176 for argv_index(argv, n) != nil { 177 n++ 178 } 179 180 // skip NULL separator 181 n++ 182 183 // now argv+n is auxv 184 auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) 185 sysauxv(auxv[:]) 186} 187 188const ( 189 _AT_NULL = 0 // Terminates the vector 190 _AT_PAGESZ = 6 // Page size in bytes 191 _AT_TIMEKEEP = 22 // Pointer to timehands. 192 _AT_HWCAP = 25 // CPU feature flags 193 _AT_HWCAP2 = 26 // CPU feature flags 2 194) 195 196func sysauxv(auxv []uintptr) { 197 for i := 0; auxv[i] != _AT_NULL; i += 2 { 198 tag, val := auxv[i], auxv[i+1] 199 switch tag { 200 // _AT_NCPUS from auxv shouldn't be used due to golang.org/issue/15206 201 case _AT_PAGESZ: 202 physPageSize = val 203 case _AT_TIMEKEEP: 204 timekeepSharedPage = (*vdsoTimekeep)(unsafe.Pointer(val)) 205 } 206 207 archauxv(tag, val) 208 } 209} 210