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

View as plain text