blob: 9b6c3e61e0abcdd611894de9c9db911deb1c1dfa [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Tim Windelschmidt83da4222024-12-16 02:49:50 +01004package bringup
5
6// This uses the unstable overrideWrite interface to also emit all runtime
7// writes to a dedicated runtime file descriptor to catch and debug crash dumps.
8// See https://go-review.googlesource.com/c/go/+/278792 for details about the
9// interface. This interface is relatively special, refrain from using most Go
10// features in here as it might cause unexpected behavior. Especially yielding
11// is a bad idea as the scheduler might be in an inconsistent state. But using
12// this interface was judged to be vastly more maintenance-friendly than
13// attempting to parse out this information from a combined stderr.
14
15import (
16 "os"
17 "syscall"
18 "unsafe"
19
20 "golang.org/x/sys/unix"
21
22 "source.monogon.dev/osbase/logtree"
23)
24
25// This hooks into a global variable which is checked by runtime.write and used
26// instead of runtime.write1 if populated.
27//
28//go:linkname overrideWrite runtime.overrideWrite
29var overrideWrite func(fd uintptr, p unsafe.Pointer, n int32) int32
30
31// Contains the files into which runtime logs and crashes are written.
32var runtimeFds []uintptr
33
34// This is essentially a reimplementation of the assembly function
35// runtime.write1, just with a hardcoded file descriptor and using the assembly
36// function syscall.RawSyscall to not get a dependency on Go's calling
37// convention and needing an implementation for every architecture.
38//
39// This has to use the syscall package as the x/sys/unix implementation
40// has a broken trampoline that calls the abi0 variant which is instrumented
41// for some unknown reason.
42//
43//go:norace
44//go:nosplit
45func runtimeWrite(fd uintptr, p unsafe.Pointer, n int32) int32 {
46 // Only redirect writes to stderr.
47 if fd != 2 {
48 a, _, err := syscall.RawSyscall(syscall.SYS_WRITE, fd, uintptr(p), uintptr(n))
49 if err == 0 {
50 return int32(a)
51 }
52 return int32(err)
53 }
54
55 // Write to the runtime panic FDs.
56 for _, f := range runtimeFds {
57 _, _, _ = syscall.RawSyscall(syscall.SYS_WRITE, f, uintptr(p), uintptr(n))
58 }
59
60 // Finally, write to original FD
61 a, _, err := syscall.RawSyscall(syscall.SYS_WRITE, fd, uintptr(p), uintptr(n))
62 if err == 0 {
63 return int32(a)
64 }
65 return int32(err)
66}
67
68func initPanicHandler(lt *logtree.LogTree, consoles []*console) {
69 l := lt.MustLeveledFor("panichandler")
70
71 // Setup pstore userspace message buffer
72 fd, err := unix.Open("/dev/pmsg0", os.O_WRONLY|os.O_SYNC, 0)
73 if err != nil {
74 l.Errorf("Failed to open pstore userspace device (pstore probably unavailable): %v", err)
75 l.Warningf("Continuing without persistent panic storage.")
76 } else {
77 runtimeFds = append(runtimeFds, uintptr(fd))
78 }
79
80 for _, c := range consoles {
81 fd, err := unix.Open(c.path, os.O_WRONLY, 0)
82 if err != nil {
83 l.Errorf("Failed opening panic console: %s", err)
84 continue
85 }
86
87 runtimeFds = append(runtimeFds, uintptr(fd))
88 l.Infof("Panic console: %s", c.path)
89 }
90
91 // This could cause a data race if the runtime crashed while we're
92 // initializing the crash handler, but there is no locking infrastructure
93 // for this so we have to take that risk.
94 overrideWrite = runtimeWrite
95}