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 usleeps uint32) {
353	ms := int32s / 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