blob: 2b760956a2a7e9c889be985893cae20da39bcd23 [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 Windelschmidt7a1b27d2024-02-22 23:54:58 +01004package main
5
6import (
7 "archive/zip"
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +01008 _ "embed"
9 "fmt"
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +010010 "os"
11 "path/filepath"
12
13 "source.monogon.dev/go/logging"
14 "source.monogon.dev/osbase/blockdev"
15 "source.monogon.dev/osbase/build/mkimage/osimage"
16 "source.monogon.dev/osbase/efivarfs"
Jan Schärc1b6df42025-03-20 08:52:18 +000017 "source.monogon.dev/osbase/structfs"
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +010018)
19
Tim Windelschmidt1f51cf42024-10-01 17:04:28 +020020//go:embed metropolis/node/core/abloader/abloader.efi
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +010021var abloader []byte
22
Jan Schärc1b6df42025-03-20 08:52:18 +000023// zipBlob looks up a file in a [zip.Reader] and adapts it to [structfs.Blob].
24func zipBlob(reader *zip.Reader, name string) (zipFileBlob, error) {
25 for _, file := range reader.File {
26 if file.Name == name {
27 return zipFileBlob{file}, nil
28 }
29 }
30 return zipFileBlob{}, fmt.Errorf("file %q not found", name)
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +010031}
32
Jan Schärc1b6df42025-03-20 08:52:18 +000033type zipFileBlob struct {
34 *zip.File
35}
36
37func (f zipFileBlob) Size() int64 {
38 return int64(f.File.UncompressedSize64)
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +010039}
40
41// EnvInstallTarget environment variable which tells the takeover binary where
42// to install to
43const EnvInstallTarget = "TAKEOVER_INSTALL_TARGET"
44
45func installMetropolis(l logging.Leveled) error {
46 // Validate we are running via EFI.
47 if _, err := os.Stat("/sys/firmware/efi"); os.IsNotExist(err) {
Tim Windelschmidt1f51cf42024-10-01 17:04:28 +020048 // nolint:ST1005
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +010049 return fmt.Errorf("Monogon OS can only be installed on EFI-booted machines, this one is not")
50 }
51
52 metropolisSpecRaw, err := os.ReadFile("/params.pb")
53 if err != nil {
54 return err
55 }
56
57 bundleRaw, err := os.Open("/bundle.zip")
58 if err != nil {
59 return err
60 }
61
62 bundleStat, err := bundleRaw.Stat()
63 if err != nil {
64 return err
65 }
66
67 bundle, err := zip.NewReader(bundleRaw, bundleStat.Size())
68 if err != nil {
69 return fmt.Errorf("failed to open node bundle: %w", err)
70 }
71
72 installParams, err := setupOSImageParams(bundle, metropolisSpecRaw, os.Getenv(EnvInstallTarget))
73 if err != nil {
74 return err
75 }
76
77 be, err := osimage.Write(installParams)
78 if err != nil {
79 return fmt.Errorf("failed to apply installation: %w", err)
80 }
81 bootEntryIdx, err := efivarfs.AddBootEntry(be)
82 if err != nil {
83 return fmt.Errorf("error creating EFI boot entry: %w", err)
84 }
85 if err := efivarfs.SetBootOrder(efivarfs.BootOrder{uint16(bootEntryIdx)}); err != nil {
86 return fmt.Errorf("error setting EFI boot order: %w", err)
87 }
88 l.Info("Metropolis installation completed")
89 return nil
90}
91
92func setupOSImageParams(bundle *zip.Reader, metropolisSpecRaw []byte, installTarget string) (*osimage.Params, error) {
93 rootDev, err := blockdev.Open(filepath.Join("/dev", installTarget))
94 if err != nil {
95 return nil, fmt.Errorf("failed to open root device: %w", err)
96 }
97
Jan Schärc1b6df42025-03-20 08:52:18 +000098 efiPayload, err := zipBlob(bundle, "kernel_efi.efi")
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +010099 if err != nil {
100 return nil, fmt.Errorf("invalid bundle: %w", err)
101 }
102
Jan Schärc1b6df42025-03-20 08:52:18 +0000103 systemImage, err := zipBlob(bundle, "verity_rootfs.img")
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +0100104 if err != nil {
105 return nil, fmt.Errorf("invalid bundle: %w", err)
106 }
107
108 return &osimage.Params{
109 PartitionSize: osimage.PartitionSizeInfo{
110 ESP: 384,
111 System: 4096,
112 Data: 128,
113 },
114 SystemImage: systemImage,
Jan Schärc1b6df42025-03-20 08:52:18 +0000115 EFIPayload: efiPayload,
116 ABLoader: structfs.Bytes(abloader),
117 NodeParameters: structfs.Bytes(metropolisSpecRaw),
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +0100118 Output: rootDev,
119 }, nil
120}