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

View as plain text