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 5package runtime 6 7import ( 8 "internal/abi" 9 "internal/runtime/atomic" 10 "internal/stringslite" 11 "unsafe" 12) 13 14type mOS struct { 15 waitsemacount uint32 16 notesig *int8 17 errstr *byte 18 ignoreHangup bool 19} 20 21func closefd(fd int32) int32 22 23//go:noescape 24func open(name *byte, mode, perm int32) int32 25 26//go:noescape 27func pread(fd int32, buf unsafe.Pointer, nbytes int32, offset int64) int32 28 29//go:noescape 30func pwrite(fd int32, buf unsafe.Pointer, nbytes int32, offset int64) int32 31 32func seek(fd int32, offset int64, whence int32) int64 33 34//go:noescape 35func exits(msg *byte) 36 37//go:noescape 38func brk_(addr unsafe.Pointer) int32 39 40func sleep(ms int32) int32 41 42func rfork(flags int32) int32 43 44//go:noescape 45func plan9_semacquire(addr *uint32, block int32) int32 46 47//go:noescape 48func plan9_tsemacquire(addr *uint32, ms int32) int32 49 50//go:noescape 51func plan9_semrelease(addr *uint32, count int32) int32 52 53//go:noescape 54func notify(fn unsafe.Pointer) int32 55 56func noted(mode int32) int32 57 58//go:noescape 59func nsec(*int64) int64 60 61//go:noescape 62func sigtramp(ureg, note unsafe.Pointer) 63 64func setfpmasks() 65 66//go:noescape 67func tstart_plan9(newm *m) 68 69func errstr() string 70 71type _Plink uintptr 72 73func 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. 123func 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 135func 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 177type 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. 181func 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 191func sigsave(p *sigset) { 192} 193 194func msigrestore(sigmask sigset) { 195} 196 197//go:nosplit 198//go:nowritebarrierrec 199func clearSignalHandlers() { 200} 201 202func 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. 207func 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. 217func unminit() { 218} 219 220// Called from exitm, but not from drop, to undo the effect of thread-owned 221// resources in minit, semacreate, or elsewhere. Do not take locks after calling this. 222func mdestroy(mp *m) { 223} 224 225var sysstat = []byte("/dev/sysstat\x00") 226 227func getproccount() int32 { 228 var buf [2048]byte 229 fd := open(&sysstat[0], _OREAD, 0) 230 if fd < 0 { 231 return 1 232 } 233 ncpu := int32(0) 234 for { 235 n := read(fd, unsafe.Pointer(&buf), int32(len(buf))) 236 if n <= 0 { 237 break 238 } 239 for i := int32(0); i < n; i++ { 240 if buf[i] == '\n' { 241 ncpu++ 242 } 243 } 244 } 245 closefd(fd) 246 if ncpu == 0 { 247 ncpu = 1 248 } 249 return ncpu 250} 251 252var devswap = []byte("/dev/swap\x00") 253var pagesize = []byte(" pagesize\n") 254 255func getPageSize() uintptr { 256 var buf [2048]byte 257 var pos int 258 fd := open(&devswap[0], _OREAD, 0) 259 if fd < 0 { 260 // There's not much we can do if /dev/swap doesn't 261 // exist. However, nothing in the memory manager uses 262 // this on Plan 9, so it also doesn't really matter. 263 return minPhysPageSize 264 } 265 for pos < len(buf) { 266 n := read(fd, unsafe.Pointer(&buf[pos]), int32(len(buf)-pos)) 267 if n <= 0 { 268 break 269 } 270 pos += int(n) 271 } 272 closefd(fd) 273 text := buf[:pos] 274 // Find "<n> pagesize" line. 275 bol := 0 276 for i, c := range text { 277 if c == '\n' { 278 bol = i + 1 279 } 280 if bytesHasPrefix(text[i:], pagesize) { 281 // Parse number at the beginning of this line. 282 return uintptr(_atoi(text[bol:])) 283 } 284 } 285 // Again, the page size doesn't really matter, so use a fallback. 286 return minPhysPageSize 287} 288 289func bytesHasPrefix(s, prefix []byte) bool { 290 if len(s) < len(prefix) { 291 return false 292 } 293 for i, p := range prefix { 294 if s[i] != p { 295 return false 296 } 297 } 298 return true 299} 300 301var pid = []byte("#c/pid\x00") 302 303func getpid() uint64 { 304 var b [20]byte 305 fd := open(&pid[0], 0, 0) 306 if fd >= 0 { 307 read(fd, unsafe.Pointer(&b), int32(len(b))) 308 closefd(fd) 309 } 310 c := b[:] 311 for c[0] == ' ' || c[0] == '\t' { 312 c = c[1:] 313 } 314 return uint64(_atoi(c)) 315} 316 317func osinit() { 318 physPageSize = getPageSize() 319 initBloc() 320 ncpu = getproccount() 321 getg().m.procid = getpid() 322} 323 324//go:nosplit 325func crash() { 326 notify(nil) 327 *(*int)(nil) = 0 328} 329 330//go:nosplit 331func readRandom(r []byte) int { 332 return 0 333} 334 335func initsig(preinit bool) { 336 if !preinit { 337 notify(unsafe.Pointer(abi.FuncPCABI0(sigtramp))) 338 } 339} 340 341//go:nosplit 342func osyield() { 343 sleep(0) 344} 345 346//go:nosplit 347func osyield_no_g() { 348 osyield() 349} 350 351//go:nosplit 352func usleep(µs uint32) { 353 ms := int32(µs / 1000) 354 if ms == 0 { 355 ms = 1 356 } 357 sleep(ms) 358} 359 360//go:nosplit 361func usleep_no_g(usec uint32) { 362 usleep(usec) 363} 364 365//go:nosplit 366func nanotime1() int64 { 367 var scratch int64 368 ns := nsec(&scratch) 369 // TODO(aram): remove hack after I fix _nsec in the pc64 kernel. 370 if ns == 0 { 371 return scratch 372 } 373 return ns 374} 375 376var goexits = []byte("go: exit ") 377var emptystatus = []byte("\x00") 378var exiting uint32 379 380func goexitsall(status *byte) { 381 var buf [_ERRMAX]byte 382 if !atomic.Cas(&exiting, 0, 1) { 383 return 384 } 385 getg().m.locks++ 386 n := copy(buf[:], goexits) 387 n = copy(buf[n:], gostringnocopy(status)) 388 pid := getpid() 389 for mp := (*m)(atomic.Loadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink { 390 if mp.procid != 0 && mp.procid != pid { 391 postnote(mp.procid, buf[:]) 392 } 393 } 394 getg().m.locks-- 395} 396 397var procdir = []byte("/proc/") 398var notefile = []byte("/note\x00") 399 400func postnote(pid uint64, msg []byte) int { 401 var buf [128]byte 402 var tmp [32]byte 403 n := copy(buf[:], procdir) 404 n += copy(buf[n:], itoa(tmp[:], pid)) 405 copy(buf[n:], notefile) 406 fd := open(&buf[0], _OWRITE, 0) 407 if fd < 0 { 408 return -1 409 } 410 len := findnull(&msg[0]) 411 if write1(uintptr(fd), unsafe.Pointer(&msg[0]), int32(len)) != int32(len) { 412 closefd(fd) 413 return -1 414 } 415 closefd(fd) 416 return 0 417} 418 419//go:nosplit 420func exit(e int32) { 421 var status []byte 422 if e == 0 { 423 status = emptystatus 424 } else { 425 // build error string 426 var tmp [32]byte 427 sl := itoa(tmp[:len(tmp)-1], uint64(e)) 428 // Don't append, rely on the existing data being zero. 429 status = sl[:len(sl)+1] 430 } 431 goexitsall(&status[0]) 432 exits(&status[0]) 433} 434 435// May run with m.p==nil, so write barriers are not allowed. 436// 437//go:nowritebarrier 438func newosproc(mp *m) { 439 if false { 440 print("newosproc mp=", mp, " ostk=", &mp, "\n") 441 } 442 pid := rfork(_RFPROC | _RFMEM | _RFNOWAIT) 443 if pid < 0 { 444 throw("newosproc: rfork failed") 445 } 446 if pid == 0 { 447 tstart_plan9(mp) 448 } 449} 450 451func exitThread(wait *atomic.Uint32) { 452 // We should never reach exitThread on Plan 9 because we let 453 // the OS clean up threads. 454 throw("exitThread") 455} 456 457//go:nosplit 458func semacreate(mp *m) { 459} 460 461//go:nosplit 462func semasleep(ns int64) int { 463 gp := getg() 464 if ns >= 0 { 465 ms := timediv(ns, 1000000, nil) 466 if ms == 0 { 467 ms = 1 468 } 469 ret := plan9_tsemacquire(&gp.m.waitsemacount, ms) 470 if ret == 1 { 471 return 0 // success 472 } 473 return -1 // timeout or interrupted 474 } 475 for plan9_semacquire(&gp.m.waitsemacount, 1) < 0 { 476 // interrupted; try again (c.f. lock_sema.go) 477 } 478 return 0 // success 479} 480 481//go:nosplit 482func semawakeup(mp *m) { 483 plan9_semrelease(&mp.waitsemacount, 1) 484} 485 486//go:nosplit 487func read(fd int32, buf unsafe.Pointer, n int32) int32 { 488 return pread(fd, buf, n, -1) 489} 490 491//go:nosplit 492func write1(fd uintptr, buf unsafe.Pointer, n int32) int32 { 493 return pwrite(int32(fd), buf, n, -1) 494} 495 496var _badsignal = []byte("runtime: signal received on thread not created by Go.\n") 497 498// This runs on a foreign stack, without an m or a g. No stack split. 499// 500//go:nosplit 501func badsignal2() { 502 pwrite(2, unsafe.Pointer(&_badsignal[0]), int32(len(_badsignal)), -1) 503 exits(&_badsignal[0]) 504} 505 506func raisebadsignal(sig uint32) { 507 badsignal2() 508} 509 510func _atoi(b []byte) int { 511 n := 0 512 for len(b) > 0 && '0' <= b[0] && b[0] <= '9' { 513 n = n*10 + int(b[0]) - '0' 514 b = b[1:] 515 } 516 return n 517} 518 519func signame(sig uint32) string { 520 if sig >= uint32(len(sigtable)) { 521 return "" 522 } 523 return sigtable[sig].name 524} 525 526const preemptMSupported = false 527 528func preemptM(mp *m) { 529 // Not currently supported. 530 // 531 // TODO: Use a note like we use signals on POSIX OSes 532} 533