...

Source file src/runtime/os_freebsd.go

     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	
     5	package runtime
     6	
     7	import (
     8		"runtime/internal/sys"
     9		"unsafe"
    10	)
    11	
    12	type mOS struct{}
    13	
    14	//go:noescape
    15	func thr_new(param *thrparam, size int32) int32
    16	
    17	//go:noescape
    18	func sigaltstack(new, old *stackt)
    19	
    20	//go:noescape
    21	func sigprocmask(how int32, new, old *sigset)
    22	
    23	//go:noescape
    24	func setitimer(mode int32, new, old *itimerval)
    25	
    26	//go:noescape
    27	func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
    28	
    29	func raise(sig uint32)
    30	func raiseproc(sig uint32)
    31	
    32	//go:noescape
    33	func sys_umtx_op(addr *uint32, mode int32, val uint32, uaddr1 uintptr, ut *umtx_time) int32
    34	
    35	func osyield()
    36	
    37	func kqueue() int32
    38	
    39	//go:noescape
    40	func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32
    41	func closeonexec(fd int32)
    42	
    43	// From FreeBSD's <sys/sysctl.h>
    44	const (
    45		_CTL_HW      = 6
    46		_HW_PAGESIZE = 7
    47	)
    48	
    49	var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
    50	
    51	// Undocumented numbers from FreeBSD's lib/libc/gen/sysctlnametomib.c.
    52	const (
    53		_CTL_QUERY     = 0
    54		_CTL_QUERY_MIB = 3
    55	)
    56	
    57	// sysctlnametomib fill mib with dynamically assigned sysctl entries of name,
    58	// return count of effected mib slots, return 0 on error.
    59	func sysctlnametomib(name []byte, mib *[_CTL_MAXNAME]uint32) uint32 {
    60		oid := [2]uint32{_CTL_QUERY, _CTL_QUERY_MIB}
    61		miblen := uintptr(_CTL_MAXNAME)
    62		if sysctl(&oid[0], 2, (*byte)(unsafe.Pointer(mib)), &miblen, (*byte)(unsafe.Pointer(&name[0])), (uintptr)(len(name))) < 0 {
    63			return 0
    64		}
    65		miblen /= unsafe.Sizeof(uint32(0))
    66		if miblen <= 0 {
    67			return 0
    68		}
    69		return uint32(miblen)
    70	}
    71	
    72	const (
    73		_CPU_CURRENT_PID = -1 // Current process ID.
    74	)
    75	
    76	//go:noescape
    77	func cpuset_getaffinity(level int, which int, id int64, size int, mask *byte) int32
    78	
    79	//go:systemstack
    80	func getncpu() int32 {
    81		// Use a large buffer for the CPU mask. We're on the system
    82		// stack, so this is fine, and we can't allocate memory for a
    83		// dynamically-sized buffer at this point.
    84		const maxCPUs = 64 * 1024
    85		var mask [maxCPUs / 8]byte
    86		var mib [_CTL_MAXNAME]uint32
    87	
    88		// According to FreeBSD's /usr/src/sys/kern/kern_cpuset.c,
    89		// cpuset_getaffinity return ERANGE when provided buffer size exceed the limits in kernel.
    90		// Querying kern.smp.maxcpus to calculate maximum buffer size.
    91		// See https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=200802
    92	
    93		// Variable kern.smp.maxcpus introduced at Dec 23 2003, revision 123766,
    94		// with dynamically assigned sysctl entries.
    95		miblen := sysctlnametomib([]byte("kern.smp.maxcpus"), &mib)
    96		if miblen == 0 {
    97			return 1
    98		}
    99	
   100		// Query kern.smp.maxcpus.
   101		dstsize := uintptr(4)
   102		maxcpus := uint32(0)
   103		if sysctl(&mib[0], miblen, (*byte)(unsafe.Pointer(&maxcpus)), &dstsize, nil, 0) != 0 {
   104			return 1
   105		}
   106	
   107		maskSize := int(maxcpus+7) / 8
   108		if maskSize < sys.PtrSize {
   109			maskSize = sys.PtrSize
   110		}
   111		if maskSize > len(mask) {
   112			maskSize = len(mask)
   113		}
   114	
   115		if cpuset_getaffinity(_CPU_LEVEL_WHICH, _CPU_WHICH_PID, _CPU_CURRENT_PID,
   116			maskSize, (*byte)(unsafe.Pointer(&mask[0]))) != 0 {
   117			return 1
   118		}
   119		n := int32(0)
   120		for _, v := range mask[:maskSize] {
   121			for v != 0 {
   122				n += int32(v & 1)
   123				v >>= 1
   124			}
   125		}
   126		if n == 0 {
   127			return 1
   128		}
   129		return n
   130	}
   131	
   132	func getPageSize() uintptr {
   133		mib := [2]uint32{_CTL_HW, _HW_PAGESIZE}
   134		out := uint32(0)
   135		nout := unsafe.Sizeof(out)
   136		ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
   137		if ret >= 0 {
   138			return uintptr(out)
   139		}
   140		return 0
   141	}
   142	
   143	// FreeBSD's umtx_op syscall is effectively the same as Linux's futex, and
   144	// thus the code is largely similar. See Linux implementation
   145	// and lock_futex.go for comments.
   146	
   147	//go:nosplit
   148	func futexsleep(addr *uint32, val uint32, ns int64) {
   149		systemstack(func() {
   150			futexsleep1(addr, val, ns)
   151		})
   152	}
   153	
   154	func futexsleep1(addr *uint32, val uint32, ns int64) {
   155		var utp *umtx_time
   156		if ns >= 0 {
   157			var ut umtx_time
   158			ut._clockid = _CLOCK_MONOTONIC
   159			ut._timeout.setNsec(ns)
   160			utp = &ut
   161		}
   162		ret := sys_umtx_op(addr, _UMTX_OP_WAIT_UINT_PRIVATE, val, unsafe.Sizeof(*utp), utp)
   163		if ret >= 0 || ret == -_EINTR {
   164			return
   165		}
   166		print("umtx_wait addr=", addr, " val=", val, " ret=", ret, "\n")
   167		*(*int32)(unsafe.Pointer(uintptr(0x1005))) = 0x1005
   168	}
   169	
   170	//go:nosplit
   171	func futexwakeup(addr *uint32, cnt uint32) {
   172		ret := sys_umtx_op(addr, _UMTX_OP_WAKE_PRIVATE, cnt, 0, nil)
   173		if ret >= 0 {
   174			return
   175		}
   176	
   177		systemstack(func() {
   178			print("umtx_wake_addr=", addr, " ret=", ret, "\n")
   179		})
   180	}
   181	
   182	func thr_start()
   183	
   184	// May run with m.p==nil, so write barriers are not allowed.
   185	//go:nowritebarrier
   186	func newosproc(mp *m) {
   187		stk := unsafe.Pointer(mp.g0.stack.hi)
   188		if false {
   189			print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " thr_start=", funcPC(thr_start), " id=", mp.id, " ostk=", &mp, "\n")
   190		}
   191	
   192		param := thrparam{
   193			start_func: funcPC(thr_start),
   194			arg:        unsafe.Pointer(mp),
   195			stack_base: mp.g0.stack.lo,
   196			stack_size: uintptr(stk) - mp.g0.stack.lo,
   197			child_tid:  unsafe.Pointer(&mp.procid),
   198			parent_tid: nil,
   199			tls_base:   unsafe.Pointer(&mp.tls[0]),
   200			tls_size:   unsafe.Sizeof(mp.tls),
   201		}
   202	
   203		var oset sigset
   204		sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
   205		// TODO: Check for error.
   206		ret := thr_new(&param, int32(unsafe.Sizeof(param)))
   207		sigprocmask(_SIG_SETMASK, &oset, nil)
   208		if ret < 0 {
   209			print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n")
   210			throw("newosproc")
   211		}
   212	}
   213	
   214	// Version of newosproc that doesn't require a valid G.
   215	//go:nosplit
   216	func newosproc0(stacksize uintptr, fn unsafe.Pointer) {
   217		stack := sysAlloc(stacksize, &memstats.stacks_sys)
   218		if stack == nil {
   219			write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack)))
   220			exit(1)
   221		}
   222		// This code "knows" it's being called once from the library
   223		// initialization code, and so it's using the static m0 for the
   224		// tls and procid (thread) pointers. thr_new() requires the tls
   225		// pointers, though the tid pointers can be nil.
   226		// However, newosproc0 is currently unreachable because builds
   227		// utilizing c-shared/c-archive force external linking.
   228		param := thrparam{
   229			start_func: funcPC(fn),
   230			arg:        nil,
   231			stack_base: uintptr(stack), //+stacksize?
   232			stack_size: stacksize,
   233			child_tid:  unsafe.Pointer(&m0.procid),
   234			parent_tid: nil,
   235			tls_base:   unsafe.Pointer(&m0.tls[0]),
   236			tls_size:   unsafe.Sizeof(m0.tls),
   237		}
   238	
   239		var oset sigset
   240		sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
   241		ret := thr_new(&param, int32(unsafe.Sizeof(param)))
   242		sigprocmask(_SIG_SETMASK, &oset, nil)
   243		if ret < 0 {
   244			write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
   245			exit(1)
   246		}
   247	}
   248	
   249	var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n")
   250	var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
   251	
   252	// Called to do synchronous initialization of Go code built with
   253	// -buildmode=c-archive or -buildmode=c-shared.
   254	// None of the Go runtime is initialized.
   255	//go:nosplit
   256	//go:nowritebarrierrec
   257	func libpreinit() {
   258		initsig(true)
   259	}
   260	
   261	func osinit() {
   262		ncpu = getncpu()
   263		if physPageSize == 0 {
   264			physPageSize = getPageSize()
   265		}
   266	}
   267	
   268	var urandom_dev = []byte("/dev/urandom\x00")
   269	
   270	//go:nosplit
   271	func getRandomData(r []byte) {
   272		fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
   273		n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
   274		closefd(fd)
   275		extendRandom(r, int(n))
   276	}
   277	
   278	func goenvs() {
   279		goenvs_unix()
   280	}
   281	
   282	// Called to initialize a new m (including the bootstrap m).
   283	// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
   284	func mpreinit(mp *m) {
   285		mp.gsignal = malg(32 * 1024)
   286		mp.gsignal.m = mp
   287	}
   288	
   289	// Called to initialize a new m (including the bootstrap m).
   290	// Called on the new thread, cannot allocate memory.
   291	func minit() {
   292		// m.procid is a uint64, but thr_new writes a uint32 on 32-bit systems.
   293		// Fix it up. (Only matters on big-endian, but be clean anyway.)
   294		if sys.PtrSize == 4 {
   295			_g_ := getg()
   296			_g_.m.procid = uint64(*(*uint32)(unsafe.Pointer(&_g_.m.procid)))
   297		}
   298	
   299		// On FreeBSD before about April 2017 there was a bug such
   300		// that calling execve from a thread other than the main
   301		// thread did not reset the signal stack. That would confuse
   302		// minitSignals, which calls minitSignalStack, which checks
   303		// whether there is currently a signal stack and uses it if
   304		// present. To avoid this confusion, explicitly disable the
   305		// signal stack on the main thread when not running in a
   306		// library. This can be removed when we are confident that all
   307		// FreeBSD users are running a patched kernel. See issue #15658.
   308		if gp := getg(); !isarchive && !islibrary && gp.m == &m0 && gp == gp.m.g0 {
   309			st := stackt{ss_flags: _SS_DISABLE}
   310			sigaltstack(&st, nil)
   311		}
   312	
   313		minitSignals()
   314	}
   315	
   316	// Called from dropm to undo the effect of an minit.
   317	//go:nosplit
   318	func unminit() {
   319		unminitSignals()
   320	}
   321	
   322	func sigtramp()
   323	
   324	type sigactiont struct {
   325		sa_handler uintptr
   326		sa_flags   int32
   327		sa_mask    sigset
   328	}
   329	
   330	// See os_freebsd2.go, os_freebsd_amd64.go for setsig function
   331	
   332	//go:nosplit
   333	//go:nowritebarrierrec
   334	func setsigstack(i uint32) {
   335		var sa sigactiont
   336		sigaction(i, nil, &sa)
   337		if sa.sa_flags&_SA_ONSTACK != 0 {
   338			return
   339		}
   340		sa.sa_flags |= _SA_ONSTACK
   341		sigaction(i, &sa, nil)
   342	}
   343	
   344	//go:nosplit
   345	//go:nowritebarrierrec
   346	func getsig(i uint32) uintptr {
   347		var sa sigactiont
   348		sigaction(i, nil, &sa)
   349		return sa.sa_handler
   350	}
   351	
   352	// setSignaltstackSP sets the ss_sp field of a stackt.
   353	//go:nosplit
   354	func setSignalstackSP(s *stackt, sp uintptr) {
   355		s.ss_sp = sp
   356	}
   357	
   358	//go:nosplit
   359	//go:nowritebarrierrec
   360	func sigaddset(mask *sigset, i int) {
   361		mask.__bits[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
   362	}
   363	
   364	func sigdelset(mask *sigset, i int) {
   365		mask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
   366	}
   367	
   368	//go:nosplit
   369	func (c *sigctxt) fixsigcode(sig uint32) {
   370	}
   371	
   372	func sysargs(argc int32, argv **byte) {
   373		n := argc + 1
   374	
   375		// skip over argv, envp to get to auxv
   376		for argv_index(argv, n) != nil {
   377			n++
   378		}
   379	
   380		// skip NULL separator
   381		n++
   382	
   383		// now argv+n is auxv
   384		auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize))
   385		sysauxv(auxv[:])
   386	}
   387	
   388	const (
   389		_AT_NULL     = 0  // Terminates the vector
   390		_AT_PAGESZ   = 6  // Page size in bytes
   391		_AT_TIMEKEEP = 22 // Pointer to timehands.
   392		_AT_HWCAP    = 25 // CPU feature flags
   393		_AT_HWCAP2   = 26 // CPU feature flags 2
   394	)
   395	
   396	func sysauxv(auxv []uintptr) {
   397		for i := 0; auxv[i] != _AT_NULL; i += 2 {
   398			tag, val := auxv[i], auxv[i+1]
   399			switch tag {
   400			// _AT_NCPUS from auxv shouldn't be used due to golang.org/issue/15206
   401			case _AT_PAGESZ:
   402				physPageSize = val
   403			case _AT_TIMEKEEP:
   404				timekeepSharedPage = (*vdsoTimekeep)(unsafe.Pointer(val))
   405			}
   406	
   407			archauxv(tag, val)
   408		}
   409	}
   410	
   411	// sysSigaction calls the sigaction system call.
   412	//go:nosplit
   413	func sysSigaction(sig uint32, new, old *sigactiont) {
   414		// Use system stack to avoid split stack overflow on amd64
   415		if asmSigaction(uintptr(sig), new, old) != 0 {
   416			systemstack(func() {
   417				throw("sigaction failed")
   418			})
   419		}
   420	}
   421	
   422	// asmSigaction is implemented in assembly.
   423	//go:noescape
   424	func asmSigaction(sig uintptr, new, old *sigactiont) int32
   425	

View as plain text