diff options
Diffstat (limited to '')
-rw-r--r-- | src/runtime/os_plan9.go | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/src/runtime/os_plan9.go b/src/runtime/os_plan9.go new file mode 100644 index 0000000..2bb6ec0 --- /dev/null +++ b/src/runtime/os_plan9.go @@ -0,0 +1,541 @@ +// 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 + +//go:linkname os_sigpipe os.sigpipe +func os_sigpipe() { + throw("too many writes on closed pipe") +} + +func sigpanic() { + g := getg() + if !canpanic(g) { + throw("unexpected signal during runtime execution") + } + + note := gostringnocopy((*byte)(unsafe.Pointer(g.m.notesig))) + switch g.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:] + g.sigcode1 = uintptr(atolwhex(addr)) + if g.sigcode1 < 0x1000 { + panicmem() + } + if g.paniconfault { + panicmemAddr(g.sigcode1) + } + print("unexpected fault address ", hex(g.sigcode1), "\n") + throw("fault") + case _SIGTRAP: + if g.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() { + initBloc() + ncpu = getproccount() + physPageSize = getPageSize() + getg().m.procid = getpid() +} + +//go:nosplit +func crash() { + notify(nil) + *(*int)(nil) = 0 +} + +//go:nosplit +func getRandomData(r []byte) { + // inspired by wyrand see hash32.go for detail + t := nanotime() + v := getg().m.procid ^ uint64(t) + + for len(r) > 0 { + v ^= 0xa0761d6478bd642f + v *= 0xe7037ed1a0b428db + size := 8 + if len(r) < 8 { + size = len(r) + } + for i := 0; i < size; i++ { + r[i] = byte(v >> (8 * i)) + } + r = r[size:] + v = v>>32 | v<<32 + } +} + +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 + status = append(itoa(tmp[:len(tmp)-1], uint64(e)), 0) + } + 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 { + _g_ := getg() + if ns >= 0 { + ms := timediv(ns, 1000000, nil) + if ms == 0 { + ms = 1 + } + ret := plan9_tsemacquire(&_g_.m.waitsemacount, ms) + if ret == 1 { + return 0 // success + } + return -1 // timeout or interrupted + } + for plan9_semacquire(&_g_.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 +} |