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