Source file src/runtime/os_plan9.go

     1  // Copyright 2010 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  	"internal/abi"
     9  	"internal/byteorder"
    10  	"internal/runtime/atomic"
    11  	"internal/stringslite"
    12  	"unsafe"
    13  )
    14  
    15  type mOS struct {
    16  	waitsemacount uint32
    17  	notesig       *int8
    18  	errstr        *byte
    19  	ignoreHangup  bool
    20  }
    21  
    22  func dupfd(old, new int32) int32
    23  func closefd(fd int32) int32
    24  
    25  //go:noescape
    26  func open(name *byte, mode, perm int32) int32
    27  
    28  //go:noescape
    29  func pread(fd int32, buf unsafe.Pointer, nbytes int32, offset int64) int32
    30  
    31  //go:noescape
    32  func pwrite(fd int32, buf unsafe.Pointer, nbytes int32, offset int64) int32
    33  
    34  func seek(fd int32, offset int64, whence int32) int64
    35  
    36  //go:noescape
    37  func exits(msg *byte)
    38  
    39  //go:noescape
    40  func brk_(addr unsafe.Pointer) int32
    41  
    42  func sleep(ms int32) int32
    43  
    44  func rfork(flags int32) int32
    45  
    46  //go:noescape
    47  func plan9_semacquire(addr *uint32, block int32) int32
    48  
    49  //go:noescape
    50  func plan9_tsemacquire(addr *uint32, ms int32) int32
    51  
    52  //go:noescape
    53  func plan9_semrelease(addr *uint32, count int32) int32
    54  
    55  //go:noescape
    56  func notify(fn unsafe.Pointer) int32
    57  
    58  func noted(mode int32) int32
    59  
    60  //go:noescape
    61  func nsec(*int64) int64
    62  
    63  //go:noescape
    64  func sigtramp(ureg, note unsafe.Pointer)
    65  
    66  func setfpmasks()
    67  
    68  //go:noescape
    69  func tstart_plan9(newm *m)
    70  
    71  func errstr() string
    72  
    73  type _Plink uintptr
    74  
    75  func sigpanic() {
    76  	gp := getg()
    77  	note := gostringnocopy((*byte)(unsafe.Pointer(gp.m.notesig)))
    78  
    79  	haveAddr := false
    80  	if gp.sig == _SIGRFAULT || gp.sig == _SIGWFAULT {
    81  		i := indexNoFloat(note, "addr=")
    82  		if i >= 0 {
    83  			i += 5
    84  		} else if i = indexNoFloat(note, "va="); i >= 0 {
    85  			i += 3
    86  		}
    87  		if i >= 0 {
    88  			gp.sigcode1 = uintptr(atolwhex(note[i:]))
    89  			haveAddr = true
    90  		}
    91  	}
    92  
    93  	if !canpanic() {
    94  		throw("unexpected signal during runtime execution")
    95  	}
    96  
    97  	switch gp.sig {
    98  	case _SIGRFAULT, _SIGWFAULT:
    99  		if !haveAddr || gp.sigcode1 < 0x1000 {
   100  			panicmem()
   101  		}
   102  		if gp.paniconfault {
   103  			panicmemAddr(gp.sigcode1)
   104  		}
   105  		if inUserArenaChunk(gp.sigcode1) {
   106  			// We could check that the arena chunk is explicitly set to fault,
   107  			// but the fact that we faulted on accessing it is enough to prove
   108  			// that it is.
   109  			print("accessed data from freed user arena ", hex(gp.sigcode1), "\n")
   110  		} else {
   111  			print("unexpected fault address ", hex(gp.sigcode1), "\n")
   112  		}
   113  		throw("fault")
   114  	case _SIGTRAP:
   115  		if gp.paniconfault {
   116  			panicmem()
   117  		}
   118  		throw(note)
   119  	case _SIGINTDIV:
   120  		panicdivide()
   121  	case _SIGFLOAT:
   122  		panicfloat()
   123  	default:
   124  		panic(errorString(note))
   125  	}
   126  }
   127  
   128  // indexNoFloat is bytealg.IndexString but safe to use in a note
   129  // handler.
   130  func indexNoFloat(s, t string) int {
   131  	if len(t) == 0 {
   132  		return 0
   133  	}
   134  	for i := 0; i < len(s); i++ {
   135  		if s[i] == t[0] && stringslite.HasPrefix(s[i:], t) {
   136  			return i
   137  		}
   138  	}
   139  	return -1
   140  }
   141  
   142  func atolwhex(p string) int64 {
   143  	for stringslite.HasPrefix(p, " ") || stringslite.HasPrefix(p, "\t") {
   144  		p = p[1:]
   145  	}
   146  	neg := false
   147  	if stringslite.HasPrefix(p, "-") || stringslite.HasPrefix(p, "+") {
   148  		neg = p[0] == '-'
   149  		p = p[1:]
   150  		for stringslite.HasPrefix(p, " ") || stringslite.HasPrefix(p, "\t") {
   151  			p = p[1:]
   152  		}
   153  	}
   154  	var n int64
   155  	switch {
   156  	case stringslite.HasPrefix(p, "0x"), stringslite.HasPrefix(p, "0X"):
   157  		p = p[2:]
   158  		for ; len(p) > 0; p = p[1:] {
   159  			if '0' <= p[0] && p[0] <= '9' {
   160  				n = n*16 + int64(p[0]-'0')
   161  			} else if 'a' <= p[0] && p[0] <= 'f' {
   162  				n = n*16 + int64(p[0]-'a'+10)
   163  			} else if 'A' <= p[0] && p[0] <= 'F' {
   164  				n = n*16 + int64(p[0]-'A'+10)
   165  			} else {
   166  				break
   167  			}
   168  		}
   169  	case stringslite.HasPrefix(p, "0"):
   170  		for ; len(p) > 0 && '0' <= p[0] && p[0] <= '7'; p = p[1:] {
   171  			n = n*8 + int64(p[0]-'0')
   172  		}
   173  	default:
   174  		for ; len(p) > 0 && '0' <= p[0] && p[0] <= '9'; p = p[1:] {
   175  			n = n*10 + int64(p[0]-'0')
   176  		}
   177  	}
   178  	if neg {
   179  		n = -n
   180  	}
   181  	return n
   182  }
   183  
   184  type sigset struct{}
   185  
   186  // Called to initialize a new m (including the bootstrap m).
   187  // Called on the parent thread (main thread in case of bootstrap), can allocate memory.
   188  func mpreinit(mp *m) {
   189  	// Initialize stack and goroutine for note handling.
   190  	mp.gsignal = malg(32 * 1024)
   191  	mp.gsignal.m = mp
   192  	mp.notesig = (*int8)(mallocgc(_ERRMAX, nil, true))
   193  	// Initialize stack for handling strings from the
   194  	// errstr system call, as used in package syscall.
   195  	mp.errstr = (*byte)(mallocgc(_ERRMAX, nil, true))
   196  }
   197  
   198  func sigsave(p *sigset) {
   199  }
   200  
   201  func msigrestore(sigmask sigset) {
   202  }
   203  
   204  //go:nosplit
   205  //go:nowritebarrierrec
   206  func clearSignalHandlers() {
   207  }
   208  
   209  func sigblock(exiting bool) {
   210  }
   211  
   212  // Called to initialize a new m (including the bootstrap m).
   213  // Called on the new thread, cannot allocate memory.
   214  func minit() {
   215  	if atomic.Load(&exiting) != 0 {
   216  		exits(&emptystatus[0])
   217  	}
   218  	// Mask all SSE floating-point exceptions
   219  	// when running on the 64-bit kernel.
   220  	setfpmasks()
   221  }
   222  
   223  // Called from dropm to undo the effect of an minit.
   224  func unminit() {
   225  }
   226  
   227  // Called from mexit, but not from dropm, to undo the effect of thread-owned
   228  // resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
   229  //
   230  // This always runs without a P, so //go:nowritebarrierrec is required.
   231  //
   232  //go:nowritebarrierrec
   233  func mdestroy(mp *m) {
   234  }
   235  
   236  var sysstat = []byte("/dev/sysstat\x00")
   237  
   238  func getCPUCount() int32 {
   239  	var buf [2048]byte
   240  	fd := open(&sysstat[0], _OREAD|_OCEXEC, 0)
   241  	if fd < 0 {
   242  		return 1
   243  	}
   244  	ncpu := int32(0)
   245  	for {
   246  		n := read(fd, unsafe.Pointer(&buf), int32(len(buf)))
   247  		if n <= 0 {
   248  			break
   249  		}
   250  		for i := int32(0); i < n; i++ {
   251  			if buf[i] == '\n' {
   252  				ncpu++
   253  			}
   254  		}
   255  	}
   256  	closefd(fd)
   257  	if ncpu == 0 {
   258  		ncpu = 1
   259  	}
   260  	return ncpu
   261  }
   262  
   263  var devswap = []byte("/dev/swap\x00")
   264  var pagesize = []byte(" pagesize\n")
   265  
   266  func getPageSize() uintptr {
   267  	var buf [2048]byte
   268  	var pos int
   269  	fd := open(&devswap[0], _OREAD|_OCEXEC, 0)
   270  	if fd < 0 {
   271  		// There's not much we can do if /dev/swap doesn't
   272  		// exist. However, nothing in the memory manager uses
   273  		// this on Plan 9, so it also doesn't really matter.
   274  		return minPhysPageSize
   275  	}
   276  	for pos < len(buf) {
   277  		n := read(fd, unsafe.Pointer(&buf[pos]), int32(len(buf)-pos))
   278  		if n <= 0 {
   279  			break
   280  		}
   281  		pos += int(n)
   282  	}
   283  	closefd(fd)
   284  	text := buf[:pos]
   285  	// Find "<n> pagesize" line.
   286  	bol := 0
   287  	for i, c := range text {
   288  		if c == '\n' {
   289  			bol = i + 1
   290  		}
   291  		if bytesHasPrefix(text[i:], pagesize) {
   292  			// Parse number at the beginning of this line.
   293  			return uintptr(_atoi(text[bol:]))
   294  		}
   295  	}
   296  	// Again, the page size doesn't really matter, so use a fallback.
   297  	return minPhysPageSize
   298  }
   299  
   300  func bytesHasPrefix(s, prefix []byte) bool {
   301  	if len(s) < len(prefix) {
   302  		return false
   303  	}
   304  	for i, p := range prefix {
   305  		if s[i] != p {
   306  			return false
   307  		}
   308  	}
   309  	return true
   310  }
   311  
   312  var pid = []byte("#c/pid\x00")
   313  
   314  func getpid() uint64 {
   315  	var b [20]byte
   316  	fd := open(&pid[0], 0, 0)
   317  	if fd >= 0 {
   318  		read(fd, unsafe.Pointer(&b), int32(len(b)))
   319  		closefd(fd)
   320  	}
   321  	c := b[:]
   322  	for c[0] == ' ' || c[0] == '\t' {
   323  		c = c[1:]
   324  	}
   325  	return uint64(_atoi(c))
   326  }
   327  
   328  var (
   329  	bintimeFD int32 = -1
   330  
   331  	bintimeDev = []byte("/dev/bintime\x00")
   332  	randomDev  = []byte("/dev/random\x00")
   333  )
   334  
   335  func osinit() {
   336  	physPageSize = getPageSize()
   337  	initBloc()
   338  	numCPUStartup = getCPUCount()
   339  	getg().m.procid = getpid()
   340  
   341  	fd := open(&bintimeDev[0], _OREAD|_OCEXEC, 0)
   342  	if fd < 0 {
   343  		fatal("cannot open /dev/bintime")
   344  	}
   345  	bintimeFD = fd
   346  
   347  	// Move fd high up, to avoid conflicts with smaller ones
   348  	// that programs might hard code, and to make exec's job easier.
   349  	// Plan 9 allocates chunks of DELTAFD=20 fds in a row,
   350  	// so 18 is near the top of what's possible.
   351  	if bintimeFD < 18 {
   352  		if dupfd(bintimeFD, 18) < 0 {
   353  			fatal("cannot dup /dev/bintime onto 18")
   354  		}
   355  		closefd(bintimeFD)
   356  		bintimeFD = 18
   357  	}
   358  }
   359  
   360  //go:nosplit
   361  func crash() {
   362  	notify(nil)
   363  	*(*int)(nil) = 0
   364  }
   365  
   366  // Don't read from /dev/random, since this device can only
   367  // return a few hundred bits a second and would slow creation
   368  // of Go processes down significantly.
   369  //
   370  //go:nosplit
   371  func readRandom(r []byte) int {
   372  	return 0
   373  }
   374  
   375  func initsig(preinit bool) {
   376  	if !preinit {
   377  		notify(unsafe.Pointer(abi.FuncPCABI0(sigtramp)))
   378  	}
   379  }
   380  
   381  //go:nosplit
   382  func osyield() {
   383  	sleep(0)
   384  }
   385  
   386  //go:nosplit
   387  func osyield_no_g() {
   388  	osyield()
   389  }
   390  
   391  //go:nosplit
   392  func usleep(µs uint32) {
   393  	ms := int32(µs / 1000)
   394  	if ms == 0 {
   395  		ms = 1
   396  	}
   397  	sleep(ms)
   398  }
   399  
   400  //go:nosplit
   401  func usleep_no_g(usec uint32) {
   402  	usleep(usec)
   403  }
   404  
   405  var goexits = []byte("go: exit ")
   406  var emptystatus = []byte("\x00")
   407  var exiting uint32
   408  
   409  func goexitsall(status *byte) {
   410  	var buf [_ERRMAX]byte
   411  	if !atomic.Cas(&exiting, 0, 1) {
   412  		return
   413  	}
   414  	getg().m.locks++
   415  	n := copy(buf[:], goexits)
   416  	n = copy(buf[n:], gostringnocopy(status))
   417  	pid := getpid()
   418  	for mp := (*m)(atomic.Loadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink {
   419  		if mp.procid != 0 && mp.procid != pid {
   420  			postnote(mp.procid, buf[:])
   421  		}
   422  	}
   423  	getg().m.locks--
   424  }
   425  
   426  var procdir = []byte("/proc/")
   427  var notefile = []byte("/note\x00")
   428  
   429  func postnote(pid uint64, msg []byte) int {
   430  	var buf [128]byte
   431  	var tmp [32]byte
   432  	n := copy(buf[:], procdir)
   433  	n += copy(buf[n:], itoa(tmp[:], pid))
   434  	copy(buf[n:], notefile)
   435  	fd := open(&buf[0], _OWRITE, 0)
   436  	if fd < 0 {
   437  		return -1
   438  	}
   439  	len := findnull(&msg[0])
   440  	if write1(uintptr(fd), unsafe.Pointer(&msg[0]), int32(len)) != int32(len) {
   441  		closefd(fd)
   442  		return -1
   443  	}
   444  	closefd(fd)
   445  	return 0
   446  }
   447  
   448  //go:nosplit
   449  func exit(e int32) {
   450  	var status []byte
   451  	if e == 0 {
   452  		status = emptystatus
   453  	} else {
   454  		// build error string
   455  		var tmp [32]byte
   456  		sl := itoa(tmp[:len(tmp)-1], uint64(e))
   457  		// Don't append, rely on the existing data being zero.
   458  		status = sl[:len(sl)+1]
   459  	}
   460  	goexitsall(&status[0])
   461  	exits(&status[0])
   462  }
   463  
   464  // May run with m.p==nil, so write barriers are not allowed.
   465  //
   466  //go:nowritebarrier
   467  func newosproc(mp *m) {
   468  	if false {
   469  		print("newosproc mp=", mp, " ostk=", &mp, "\n")
   470  	}
   471  	pid := rfork(_RFPROC | _RFMEM | _RFNOWAIT)
   472  	if pid < 0 {
   473  		throw("newosproc: rfork failed")
   474  	}
   475  	if pid == 0 {
   476  		tstart_plan9(mp)
   477  	}
   478  }
   479  
   480  func exitThread(wait *atomic.Uint32) {
   481  	// We should never reach exitThread on Plan 9 because we let
   482  	// the OS clean up threads.
   483  	throw("exitThread")
   484  }
   485  
   486  //go:nosplit
   487  func semacreate(mp *m) {
   488  }
   489  
   490  //go:nosplit
   491  func semasleep(ns int64) int {
   492  	gp := getg()
   493  	if ns >= 0 {
   494  		ms := int32(ns / 1000000)
   495  		if ms == 0 {
   496  			ms = 1
   497  		}
   498  		ret := plan9_tsemacquire(&gp.m.waitsemacount, ms)
   499  		if ret == 1 {
   500  			return 0 // success
   501  		}
   502  		return -1 // timeout or interrupted
   503  	}
   504  	for plan9_semacquire(&gp.m.waitsemacount, 1) < 0 {
   505  		// interrupted; try again (c.f. lock_sema.go)
   506  	}
   507  	return 0 // success
   508  }
   509  
   510  //go:nosplit
   511  func semawakeup(mp *m) {
   512  	plan9_semrelease(&mp.waitsemacount, 1)
   513  }
   514  
   515  //go:nosplit
   516  func read(fd int32, buf unsafe.Pointer, n int32) int32 {
   517  	return pread(fd, buf, n, -1)
   518  }
   519  
   520  //go:nosplit
   521  func write1(fd uintptr, buf unsafe.Pointer, n int32) int32 {
   522  	return pwrite(int32(fd), buf, n, -1)
   523  }
   524  
   525  var _badsignal = []byte("runtime: signal received on thread not created by Go.\n")
   526  
   527  // This runs on a foreign stack, without an m or a g. No stack split.
   528  //
   529  //go:nosplit
   530  func badsignal2() {
   531  	pwrite(2, unsafe.Pointer(&_badsignal[0]), int32(len(_badsignal)), -1)
   532  	exits(&_badsignal[0])
   533  }
   534  
   535  func raisebadsignal(sig uint32) {
   536  	badsignal2()
   537  }
   538  
   539  func _atoi(b []byte) int {
   540  	n := 0
   541  	for len(b) > 0 && '0' <= b[0] && b[0] <= '9' {
   542  		n = n*10 + int(b[0]) - '0'
   543  		b = b[1:]
   544  	}
   545  	return n
   546  }
   547  
   548  func signame(sig uint32) string {
   549  	if sig >= uint32(len(sigtable)) {
   550  		return ""
   551  	}
   552  	return sigtable[sig].name
   553  }
   554  
   555  const preemptMSupported = false
   556  
   557  func preemptM(gp *g) {
   558  	casfrom_Gscanstatus(gp, _Gscanrunning, _Grunning)
   559  
   560  	// Not currently supported.
   561  	//
   562  	// TODO: Use a note like we use signals on POSIX OSes
   563  }
   564  
   565  //go:nosplit
   566  func readtime(t *uint64, min, n int) int {
   567  	if bintimeFD < 0 {
   568  		fatal("/dev/bintime not opened")
   569  	}
   570  	const uint64size = 8
   571  	r := pread(bintimeFD, unsafe.Pointer(t), int32(n*uint64size), 0)
   572  	if int(r) < min*uint64size {
   573  		fatal("cannot read /dev/bintime")
   574  	}
   575  	return int(r) / uint64size
   576  }
   577  
   578  // timesplit returns u/1e9, u%1e9
   579  func timesplit(u uint64) (sec int64, nsec int32)
   580  
   581  func frombe(u uint64) uint64 {
   582  	b := (*[8]byte)(unsafe.Pointer(&u))
   583  	return byteorder.BEUint64(b[:])
   584  }
   585  
   586  //go:nosplit
   587  func nanotime1() int64 {
   588  	var t [4]uint64
   589  	if readtime(&t[0], 1, 4) == 4 {
   590  		// long read indicates new kernel sending monotonic time
   591  		// (https://github.com/rsc/plan9/commit/baf076425).
   592  		return int64(frombe(t[3]))
   593  	}
   594  	// fall back to unix time
   595  	return int64(frombe(t[0]))
   596  }
   597  
   598  //go:nosplit
   599  func walltime() (sec int64, nsec int32) {
   600  	var t [1]uint64
   601  	readtime(&t[0], 1, 1)
   602  	return timesplit(frombe(t[0]))
   603  }
   604  
   605  //go:nowritebarrierrec
   606  //go:nosplit
   607  func libpreinit() {}
   608  
   609  //go:nowritebarrierrec
   610  //go:nosplit
   611  func newosproc0(stacksize uintptr, fn unsafe.Pointer) {
   612  	throw("bad newosproc0")
   613  }
   614  

View as plain text