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

View as plain text