blob: b0c56d034984bff4e56054d4abc2bea461cb11e1 [file] [log] [blame]
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +01001package main
2
3import (
4 "archive/zip"
5 "bytes"
6 _ "embed"
7 "fmt"
8 "io/fs"
9 "os"
10 "path/filepath"
11
12 "source.monogon.dev/go/logging"
13 "source.monogon.dev/osbase/blockdev"
14 "source.monogon.dev/osbase/build/mkimage/osimage"
15 "source.monogon.dev/osbase/efivarfs"
16)
17
Tim Windelschmidt1f51cf42024-10-01 17:04:28 +020018//go:embed metropolis/node/core/abloader/abloader.efi
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +010019var abloader []byte
20
21// FileSizedReader is a small adapter from fs.File to fs.SizedReader
22// Panics on Stat() failure, so should only be used with sources where Stat()
23// cannot fail.
24type FileSizedReader struct {
25 fs.File
26}
27
28func (f FileSizedReader) Size() int64 {
29 stat, err := f.Stat()
30 if err != nil {
31 panic(err)
32 }
33 return stat.Size()
34}
35
36// EnvInstallTarget environment variable which tells the takeover binary where
37// to install to
38const EnvInstallTarget = "TAKEOVER_INSTALL_TARGET"
39
40func installMetropolis(l logging.Leveled) error {
41 // Validate we are running via EFI.
42 if _, err := os.Stat("/sys/firmware/efi"); os.IsNotExist(err) {
Tim Windelschmidt1f51cf42024-10-01 17:04:28 +020043 // nolint:ST1005
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +010044 return fmt.Errorf("Monogon OS can only be installed on EFI-booted machines, this one is not")
45 }
46
47 metropolisSpecRaw, err := os.ReadFile("/params.pb")
48 if err != nil {
49 return err
50 }
51
52 bundleRaw, err := os.Open("/bundle.zip")
53 if err != nil {
54 return err
55 }
56
57 bundleStat, err := bundleRaw.Stat()
58 if err != nil {
59 return err
60 }
61
62 bundle, err := zip.NewReader(bundleRaw, bundleStat.Size())
63 if err != nil {
64 return fmt.Errorf("failed to open node bundle: %w", err)
65 }
66
67 installParams, err := setupOSImageParams(bundle, metropolisSpecRaw, os.Getenv(EnvInstallTarget))
68 if err != nil {
69 return err
70 }
71
72 be, err := osimage.Write(installParams)
73 if err != nil {
74 return fmt.Errorf("failed to apply installation: %w", err)
75 }
76 bootEntryIdx, err := efivarfs.AddBootEntry(be)
77 if err != nil {
78 return fmt.Errorf("error creating EFI boot entry: %w", err)
79 }
80 if err := efivarfs.SetBootOrder(efivarfs.BootOrder{uint16(bootEntryIdx)}); err != nil {
81 return fmt.Errorf("error setting EFI boot order: %w", err)
82 }
83 l.Info("Metropolis installation completed")
84 return nil
85}
86
87func setupOSImageParams(bundle *zip.Reader, metropolisSpecRaw []byte, installTarget string) (*osimage.Params, error) {
88 rootDev, err := blockdev.Open(filepath.Join("/dev", installTarget))
89 if err != nil {
90 return nil, fmt.Errorf("failed to open root device: %w", err)
91 }
92
93 efiPayload, err := bundle.Open("kernel_efi.efi")
94 if err != nil {
95 return nil, fmt.Errorf("invalid bundle: %w", err)
96 }
97
98 systemImage, err := bundle.Open("verity_rootfs.img")
99 if err != nil {
100 return nil, fmt.Errorf("invalid bundle: %w", err)
101 }
102
103 return &osimage.Params{
104 PartitionSize: osimage.PartitionSizeInfo{
105 ESP: 384,
106 System: 4096,
107 Data: 128,
108 },
109 SystemImage: systemImage,
110 EFIPayload: FileSizedReader{efiPayload},
111 ABLoader: bytes.NewReader(abloader),
112 NodeParameters: bytes.NewReader(metropolisSpecRaw),
113 Output: rootDev,
114 }, nil
115}