blob: 77446d09d3b51e9661774fea7e6d538949d0db0a [file] [log] [blame]
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import (
"internal/abi"
"runtime/internal/atomic"
"unsafe"
)
type mOS struct {
waitsemacount uint32
notesig *int8
errstr *byte
ignoreHangup bool
}
func closefd(fd int32) int32
//go:noescape
func open(name *byte, mode, perm int32) int32
//go:noescape
func pread(fd int32, buf unsafe.Pointer, nbytes int32, offset int64) int32
//go:noescape
func pwrite(fd int32, buf unsafe.Pointer, nbytes int32, offset int64) int32
func seek(fd int32, offset int64, whence int32) int64
//go:noescape
func exits(msg *byte)
//go:noescape
func brk_(addr unsafe.Pointer) int32
func sleep(ms int32) int32
func rfork(flags int32) int32
//go:noescape
func plan9_semacquire(addr *uint32, block int32) int32
//go:noescape
func plan9_tsemacquire(addr *uint32, ms int32) int32
//go:noescape
func plan9_semrelease(addr *uint32, count int32) int32
//go:noescape
func notify(fn unsafe.Pointer) int32
func noted(mode int32) int32
//go:noescape
func nsec(*int64) int64
//go:noescape
func sigtramp(ureg, note unsafe.Pointer)
func setfpmasks()
//go:noescape
func tstart_plan9(newm *m)
func errstr() string
type _Plink uintptr
func sigpanic() {
gp := getg()
if !canpanic() {
throw("unexpected signal during runtime execution")
}
note := gostringnocopy((*byte)(unsafe.Pointer(gp.m.notesig)))
switch gp.sig {
case _SIGRFAULT, _SIGWFAULT:
i := indexNoFloat(note, "addr=")
if i >= 0 {
i += 5
} else if i = indexNoFloat(note, "va="); i >= 0 {
i += 3
} else {
panicmem()
}
addr := note[i:]
gp.sigcode1 = uintptr(atolwhex(addr))
if gp.sigcode1 < 0x1000 {
panicmem()
}
if gp.paniconfault {
panicmemAddr(gp.sigcode1)
}
if inUserArenaChunk(gp.sigcode1) {
// We could check that the arena chunk is explicitly set to fault,
// but the fact that we faulted on accessing it is enough to prove
// that it is.
print("accessed data from freed user arena ", hex(gp.sigcode1), "\n")
} else {
print("unexpected fault address ", hex(gp.sigcode1), "\n")
}
throw("fault")
case _SIGTRAP:
if gp.paniconfault {
panicmem()
}
throw(note)
case _SIGINTDIV:
panicdivide()
case _SIGFLOAT:
panicfloat()
default:
panic(errorString(note))
}
}
// indexNoFloat is bytealg.IndexString but safe to use in a note
// handler.
func indexNoFloat(s, t string) int {
if len(t) == 0 {
return 0
}
for i := 0; i < len(s); i++ {
if s[i] == t[0] && hasPrefix(s[i:], t) {
return i
}
}
return -1
}
func atolwhex(p string) int64 {
for hasPrefix(p, " ") || hasPrefix(p, "\t") {
p = p[1:]
}
neg := false
if hasPrefix(p, "-") || hasPrefix(p, "+") {
neg = p[0] == '-'
p = p[1:]
for hasPrefix(p, " ") || hasPrefix(p, "\t") {
p = p[1:]
}
}
var n int64
switch {
case hasPrefix(p, "0x"), hasPrefix(p, "0X"):
p = p[2:]
for ; len(p) > 0; p = p[1:] {
if '0' <= p[0] && p[0] <= '9' {
n = n*16 + int64(p[0]-'0')
} else if 'a' <= p[0] && p[0] <= 'f' {
n = n*16 + int64(p[0]-'a'+10)
} else if 'A' <= p[0] && p[0] <= 'F' {
n = n*16 + int64(p[0]-'A'+10)
} else {
break
}
}
case hasPrefix(p, "0"):
for ; len(p) > 0 && '0' <= p[0] && p[0] <= '7'; p = p[1:] {
n = n*8 + int64(p[0]-'0')
}
default:
for ; len(p) > 0 && '0' <= p[0] && p[0] <= '9'; p = p[1:] {
n = n*10 + int64(p[0]-'0')
}
}
if neg {
n = -n
}
return n
}
type sigset struct{}
// Called to initialize a new m (including the bootstrap m).
// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
func mpreinit(mp *m) {
// Initialize stack and goroutine for note handling.
mp.gsignal = malg(32 * 1024)
mp.gsignal.m = mp
mp.notesig = (*int8)(mallocgc(_ERRMAX, nil, true))
// Initialize stack for handling strings from the
// errstr system call, as used in package syscall.
mp.errstr = (*byte)(mallocgc(_ERRMAX, nil, true))
}
func sigsave(p *sigset) {
}
func msigrestore(sigmask sigset) {
}
//go:nosplit
//go:nowritebarrierrec
func clearSignalHandlers() {
}
func sigblock(exiting bool) {
}
// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, cannot allocate memory.
func minit() {
if atomic.Load(&exiting) != 0 {
exits(&emptystatus[0])
}
// Mask all SSE floating-point exceptions
// when running on the 64-bit kernel.
setfpmasks()
}
// Called from dropm to undo the effect of an minit.
func unminit() {
}
// Called from exitm, but not from drop, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
func mdestroy(mp *m) {
}
var sysstat = []byte("/dev/sysstat\x00")
func getproccount() int32 {
var buf [2048]byte
fd := open(&sysstat[0], _OREAD, 0)
if fd < 0 {
return 1
}
ncpu := int32(0)
for {
n := read(fd, unsafe.Pointer(&buf), int32(len(buf)))
if n <= 0 {
break
}
for i := int32(0); i < n; i++ {
if buf[i] == '\n' {
ncpu++
}
}
}
closefd(fd)
if ncpu == 0 {
ncpu = 1
}
return ncpu
}
var devswap = []byte("/dev/swap\x00")
var pagesize = []byte(" pagesize\n")
func getPageSize() uintptr {
var buf [2048]byte
var pos int
fd := open(&devswap[0], _OREAD, 0)
if fd < 0 {
// There's not much we can do if /dev/swap doesn't
// exist. However, nothing in the memory manager uses
// this on Plan 9, so it also doesn't really matter.
return minPhysPageSize
}
for pos < len(buf) {
n := read(fd, unsafe.Pointer(&buf[pos]), int32(len(buf)-pos))
if n <= 0 {
break
}
pos += int(n)
}
closefd(fd)
text := buf[:pos]
// Find "<n> pagesize" line.
bol := 0
for i, c := range text {
if c == '\n' {
bol = i + 1
}
if bytesHasPrefix(text[i:], pagesize) {
// Parse number at the beginning of this line.
return uintptr(_atoi(text[bol:]))
}
}
// Again, the page size doesn't really matter, so use a fallback.
return minPhysPageSize
}
func bytesHasPrefix(s, prefix []byte) bool {
if len(s) < len(prefix) {
return false
}
for i, p := range prefix {
if s[i] != p {
return false
}
}
return true
}
var pid = []byte("#c/pid\x00")
func getpid() uint64 {
var b [20]byte
fd := open(&pid[0], 0, 0)
if fd >= 0 {
read(fd, unsafe.Pointer(&b), int32(len(b)))
closefd(fd)
}
c := b[:]
for c[0] == ' ' || c[0] == '\t' {
c = c[1:]
}
return uint64(_atoi(c))
}
func osinit() {
physPageSize = getPageSize()
initBloc()
ncpu = getproccount()
getg().m.procid = getpid()
}
//go:nosplit
func crash() {
notify(nil)
*(*int)(nil) = 0
}
//go:nosplit
func readRandom(r []byte) int {
return 0
}
func initsig(preinit bool) {
if !preinit {
notify(unsafe.Pointer(abi.FuncPCABI0(sigtramp)))
}
}
//go:nosplit
func osyield() {
sleep(0)
}
//go:nosplit
func osyield_no_g() {
osyield()
}
//go:nosplit
func usleep(µs uint32) {
ms := int32(µs / 1000)
if ms == 0 {
ms = 1
}
sleep(ms)
}
//go:nosplit
func usleep_no_g(usec uint32) {
usleep(usec)
}
//go:nosplit
func nanotime1() int64 {
var scratch int64
ns := nsec(&scratch)
// TODO(aram): remove hack after I fix _nsec in the pc64 kernel.
if ns == 0 {
return scratch
}
return ns
}
var goexits = []byte("go: exit ")
var emptystatus = []byte("\x00")
var exiting uint32
func goexitsall(status *byte) {
var buf [_ERRMAX]byte
if !atomic.Cas(&exiting, 0, 1) {
return
}
getg().m.locks++
n := copy(buf[:], goexits)
n = copy(buf[n:], gostringnocopy(status))
pid := getpid()
for mp := (*m)(atomic.Loadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink {
if mp.procid != 0 && mp.procid != pid {
postnote(mp.procid, buf[:])
}
}
getg().m.locks--
}
var procdir = []byte("/proc/")
var notefile = []byte("/note\x00")
func postnote(pid uint64, msg []byte) int {
var buf [128]byte
var tmp [32]byte
n := copy(buf[:], procdir)
n += copy(buf[n:], itoa(tmp[:], pid))
copy(buf[n:], notefile)
fd := open(&buf[0], _OWRITE, 0)
if fd < 0 {
return -1
}
len := findnull(&msg[0])
if write1(uintptr(fd), unsafe.Pointer(&msg[0]), int32(len)) != int32(len) {
closefd(fd)
return -1
}
closefd(fd)
return 0
}
//go:nosplit
func exit(e int32) {
var status []byte
if e == 0 {
status = emptystatus
} else {
// build error string
var tmp [32]byte
sl := itoa(tmp[:len(tmp)-1], uint64(e))
// Don't append, rely on the existing data being zero.
status = sl[:len(sl)+1]
}
goexitsall(&status[0])
exits(&status[0])
}
// May run with m.p==nil, so write barriers are not allowed.
//
//go:nowritebarrier
func newosproc(mp *m) {
if false {
print("newosproc mp=", mp, " ostk=", &mp, "\n")
}
pid := rfork(_RFPROC | _RFMEM | _RFNOWAIT)
if pid < 0 {
throw("newosproc: rfork failed")
}
if pid == 0 {
tstart_plan9(mp)
}
}
func exitThread(wait *atomic.Uint32) {
// We should never reach exitThread on Plan 9 because we let
// the OS clean up threads.
throw("exitThread")
}
//go:nosplit
func semacreate(mp *m) {
}
//go:nosplit
func semasleep(ns int64) int {
gp := getg()
if ns >= 0 {
ms := timediv(ns, 1000000, nil)
if ms == 0 {
ms = 1
}
ret := plan9_tsemacquire(&gp.m.waitsemacount, ms)
if ret == 1 {
return 0 // success
}
return -1 // timeout or interrupted
}
for plan9_semacquire(&gp.m.waitsemacount, 1) < 0 {
// interrupted; try again (c.f. lock_sema.go)
}
return 0 // success
}
//go:nosplit
func semawakeup(mp *m) {
plan9_semrelease(&mp.waitsemacount, 1)
}
//go:nosplit
func read(fd int32, buf unsafe.Pointer, n int32) int32 {
return pread(fd, buf, n, -1)
}
//go:nosplit
func write1(fd uintptr, buf unsafe.Pointer, n int32) int32 {
return pwrite(int32(fd), buf, n, -1)
}
var _badsignal = []byte("runtime: signal received on thread not created by Go.\n")
// This runs on a foreign stack, without an m or a g. No stack split.
//
//go:nosplit
func badsignal2() {
pwrite(2, unsafe.Pointer(&_badsignal[0]), int32(len(_badsignal)), -1)
exits(&_badsignal[0])
}
func raisebadsignal(sig uint32) {
badsignal2()
}
func _atoi(b []byte) int {
n := 0
for len(b) > 0 && '0' <= b[0] && b[0] <= '9' {
n = n*10 + int(b[0]) - '0'
b = b[1:]
}
return n
}
func signame(sig uint32) string {
if sig >= uint32(len(sigtable)) {
return ""
}
return sigtable[sig].name
}
const preemptMSupported = false
func preemptM(mp *m) {
// Not currently supported.
//
// TODO: Use a note like we use signals on POSIX OSes
}