Some checks failed
Test / ShareFS (push) Failing after 49s
Test / Create distribution (push) Failing after 51s
Test / Hakurei (race detector) (push) Failing after 59s
Test / Sandbox (push) Failing after 1m0s
Test / Hakurei (push) Failing after 1m11s
Test / Sandbox (race detector) (push) Failing after 1m7s
Test / Flake checks (push) Has been skipped
This currently relies on a trusted bootloader to determine the boot device. Signed-off-by: Ophestra <cat@gensokyo.uk>
242 lines
5.2 KiB
Go
242 lines
5.2 KiB
Go
// The earlyinit is part of the Rosa OS initramfs and serves as the system init.
|
|
//
|
|
// This program is an internal detail of Rosa OS and is not usable on its own.
|
|
// It is not covered by the compatibility promise.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"os/signal"
|
|
"runtime"
|
|
"runtime/pprof"
|
|
"slices"
|
|
"strings"
|
|
. "syscall"
|
|
|
|
"hakurei.app/internal/kobject"
|
|
"hakurei.app/internal/report"
|
|
"hakurei.app/internal/uevent"
|
|
)
|
|
|
|
var r report.Reporter
|
|
|
|
func init() {
|
|
log.SetFlags(0)
|
|
log.SetPrefix("earlyinit: ")
|
|
r.SetOutput(log.Default())
|
|
|
|
// this handles SIGQUIT to provide useful debugging information without
|
|
// terminating, and prevents the runtime from throwing on the must family
|
|
// of early error reporting functions, DO NOT REMOVE
|
|
c := make(chan os.Signal, 1)
|
|
signal.Notify(c, SIGQUIT)
|
|
go func() {
|
|
for {
|
|
<-c
|
|
if p := pprof.Lookup("goroutine"); p == nil {
|
|
log.Println("initial built-in goroutine profile does not exist")
|
|
} else if err := p.WriteTo(os.Stderr, 2); err != nil {
|
|
log.Println(err)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// fatal calls [log.Println] with v and blocks forever. Must be called from
|
|
// main. Must not be used after error reporting is set up.
|
|
func fatal(v ...any) {
|
|
log.Println(v...)
|
|
log.Println("unable to continue, please reboot and resolve the problem manually")
|
|
select {}
|
|
}
|
|
|
|
// must calls fatal with err if it is non-nil.
|
|
func must(err error) {
|
|
if err != nil {
|
|
log.Println(err)
|
|
select {}
|
|
}
|
|
}
|
|
|
|
// mustSyscall is like must, but with an additional action name.
|
|
func mustSyscall(action string, err error) {
|
|
if err != nil {
|
|
fatal("cannot "+action+":", err)
|
|
select {}
|
|
}
|
|
}
|
|
|
|
// must1 is like must, but with an additional passed through value.
|
|
func must1[T any](v T, err error) T {
|
|
must(err)
|
|
return v
|
|
}
|
|
|
|
const (
|
|
// optionSystem specifies devpath of the system device.
|
|
optionSystem = "system"
|
|
|
|
// flagStrict sets [report.DStrict] on r.
|
|
flagStrict = "strict"
|
|
// flagNoRecover sets [report.DNoRecover] on r.
|
|
flagNoRecover = "no_recover"
|
|
)
|
|
|
|
func main() {
|
|
runtime.LockOSThread()
|
|
|
|
var (
|
|
option map[string]string
|
|
flags []string
|
|
)
|
|
if len(os.Args) > 1 {
|
|
option = make(map[string]string)
|
|
for _, s := range os.Args[1:] {
|
|
key, value, ok := strings.Cut(s, "=")
|
|
if !ok {
|
|
flags = append(flags, s)
|
|
continue
|
|
}
|
|
option[key] = value
|
|
}
|
|
}
|
|
|
|
{
|
|
var flag uint64
|
|
if slices.Contains(flags, flagStrict) {
|
|
flag |= report.DStrict
|
|
}
|
|
if slices.Contains(flags, flagNoRecover) {
|
|
flag |= report.DNoRecover
|
|
}
|
|
log.Printf("reporting flags %x", flag)
|
|
r.SetFlags(flag)
|
|
}
|
|
|
|
mustSyscall("mount devtmpfs", Mount(
|
|
"devtmpfs",
|
|
"/dev/",
|
|
"devtmpfs",
|
|
MS_NOSUID|MS_NOEXEC,
|
|
"",
|
|
))
|
|
must(os.Mkdir("/dev/pts/", 0))
|
|
mustSyscall("mount devpts", Mount(
|
|
"devpts",
|
|
"/dev/pts/",
|
|
"devpts",
|
|
MS_NOSUID|MS_NOEXEC,
|
|
"mode=620,ptmxmode=666",
|
|
))
|
|
|
|
// The kernel might be unable to set up the console. When that happens,
|
|
// printk is called with "Warning: unable to open an initial console."
|
|
// and the init runs with no files. The checkfds runtime function
|
|
// populates 0-2 by opening /dev/null for them.
|
|
//
|
|
// This check replaces 1 and 2 with /dev/kmsg to improve the chance
|
|
// of output being visible to the user.
|
|
if fi, err := os.Stdout.Stat(); err == nil {
|
|
if stat, ok := fi.Sys().(*Stat_t); ok {
|
|
if stat.Rdev == 0x103 {
|
|
var fd int
|
|
if fd, err = Open(
|
|
"/dev/kmsg",
|
|
O_WRONLY|O_CLOEXEC,
|
|
0,
|
|
); err != nil {
|
|
log.Fatalf("cannot open kmsg: %v", err)
|
|
}
|
|
|
|
if err = Dup3(fd, Stdout, 0); err != nil {
|
|
log.Fatalf("cannot open stdout: %v", err)
|
|
}
|
|
if err = Dup3(fd, Stderr, 0); err != nil {
|
|
log.Fatalf("cannot open stderr: %v", err)
|
|
}
|
|
|
|
if err = Close(fd); err != nil {
|
|
log.Printf("cannot close kmsg: %v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// staying in rootfs, these are no longer used
|
|
must(os.Remove("/root"))
|
|
must(os.Remove("/init"))
|
|
|
|
must(os.Mkdir("/proc", 0))
|
|
mustSyscall("mount proc", Mount(
|
|
"proc",
|
|
"/proc",
|
|
"proc",
|
|
MS_NOSUID|MS_NOEXEC|MS_NODEV,
|
|
"hidepid=1",
|
|
))
|
|
|
|
must(os.Mkdir("/sys", 0))
|
|
mustSyscall("mount sysfs", Mount(
|
|
"sysfs",
|
|
"/sys",
|
|
"sysfs",
|
|
0,
|
|
"",
|
|
))
|
|
|
|
conn := must1(uevent.Dial(-128 * 1024 * 1024))
|
|
events := make(chan *uevent.Message, 1<<10)
|
|
var uuid uevent.UUID
|
|
must1(rand.Read(uuid[:]))
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go consume(ctx, &r, conn, uuid, events)
|
|
s := kobject.New(uuid, func(o *kobject.Object, env map[string]string) {
|
|
log.Printf("change %s: %q", o.DevPath, env)
|
|
}, func(err error) {
|
|
r.Dispatch(
|
|
report.Inconsistent,
|
|
"processed inconsistent uevent",
|
|
err,
|
|
)
|
|
})
|
|
go func() {
|
|
s.Consume(ctx, events)
|
|
|
|
log.Println("closing NETLINK_KOBJECT_UEVENT socket")
|
|
cancel()
|
|
if err := conn.Close(); err != nil {
|
|
log.Fatal(err) // not reached
|
|
}
|
|
}()
|
|
|
|
must(os.Mkdir("/system", 0))
|
|
if devpath := option[optionSystem]; devpath == "" {
|
|
fatal("system must be nonempty")
|
|
} else {
|
|
log.Printf("waiting for devpath pattern %q", devpath)
|
|
mustMountSystem(ctx, s, devpath)
|
|
}
|
|
|
|
// after top level has been set up
|
|
mustSyscall("remount root", Mount(
|
|
"",
|
|
"/",
|
|
"",
|
|
MS_REMOUNT|MS_BIND|
|
|
MS_RDONLY|MS_NODEV|MS_NOSUID|MS_NOEXEC,
|
|
"",
|
|
))
|
|
|
|
must(os.WriteFile(
|
|
"/sys/module/firmware_class/parameters/path",
|
|
[]byte("/system/lib/firmware"),
|
|
0,
|
|
))
|
|
|
|
}
|