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