blob: 574842f64467c8d422d666ef77812385442847df [file] [log] [blame]
Lorenz Brunae0d90d2019-09-05 17:53:56 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package main
18
19import (
Leopold Schabel65493072019-11-06 13:40:44 +000020 "flag"
Lorenz Brunae0d90d2019-09-05 17:53:56 +020021 "fmt"
22 "io/ioutil"
23 "os"
Lorenz Brunae0d90d2019-09-05 17:53:56 +020024
Hendrik Hofstadt8efe51e2020-02-28 12:53:41 +010025 diskfs "github.com/diskfs/go-diskfs"
Lorenz Brunae0d90d2019-09-05 17:53:56 +020026 "github.com/diskfs/go-diskfs/disk"
27 "github.com/diskfs/go-diskfs/filesystem"
28 "github.com/diskfs/go-diskfs/partition/gpt"
Lorenz Brunae0d90d2019-09-05 17:53:56 +020029)
30
31var SmalltownDataPartition gpt.Type = gpt.Type("9eeec464-6885-414a-b278-4305c51f7966")
32
Leopold Schabel65493072019-11-06 13:40:44 +000033var (
Lorenz Brunaa6b7342019-12-12 02:55:02 +010034 efiPayloadPath = flag.String("efi", "", "UEFI payload")
35 outputPath = flag.String("out", "", "Output disk image")
36 initramfsPath = flag.String("initramfs", "", "External initramfs [optional]")
37 enrolmentCredentialsPath = flag.String("enrolment-credentials", "", "Enrolment credentials [optional]")
Lorenz Brun878f5f92020-05-12 16:15:39 +020038 dataPartitionSizeMiB = flag.Uint64("data-partition-size", 2048, "Override the data partition size (default 2048 MiB)")
Serge Bazanskic3ae7582020-06-08 17:15:26 +020039 espPartitionSizeMiB = flag.Uint64("esp-partition-size", 512, "Override the ESP partition size (default: 512MiB)")
Leopold Schabel65493072019-11-06 13:40:44 +000040)
41
Lorenz Brunae0d90d2019-09-05 17:53:56 +020042func mibToSectors(size uint64) uint64 {
43 return (size * 1024 * 1024) / 512
44}
45
Lorenz Brunae0d90d2019-09-05 17:53:56 +020046func main() {
Leopold Schabel65493072019-11-06 13:40:44 +000047 flag.Parse()
48 if *efiPayloadPath == "" || *outputPath == "" {
49 flag.PrintDefaults()
Lorenz Brunf95909d2019-09-11 19:48:26 +020050 os.Exit(2)
51 }
Leopold Schabel65493072019-11-06 13:40:44 +000052
53 _ = os.Remove(*outputPath)
54 diskImg, err := diskfs.Create(*outputPath, 3*1024*1024*1024, diskfs.Raw)
Lorenz Brunae0d90d2019-09-05 17:53:56 +020055 if err != nil {
56 fmt.Printf("Failed to create disk: %v", err)
57 os.Exit(1)
58 }
59
60 table := &gpt.Table{
61 // This is appropriate at least for virtio disks. Might need to be adjusted for real ones.
62 LogicalSectorSize: 512,
63 PhysicalSectorSize: 512,
64 ProtectiveMBR: true,
65 Partitions: []*gpt.Partition{
66 {
67 Type: gpt.EFISystemPartition,
68 Name: "ESP",
69 Start: mibToSectors(1),
Serge Bazanskic3ae7582020-06-08 17:15:26 +020070 End: mibToSectors(*espPartitionSizeMiB) - 1,
Lorenz Brunae0d90d2019-09-05 17:53:56 +020071 },
72 {
73 Type: SmalltownDataPartition,
74 Name: "SIGNOS-DATA",
Serge Bazanskic3ae7582020-06-08 17:15:26 +020075 Start: mibToSectors(*espPartitionSizeMiB),
76 End: mibToSectors(*espPartitionSizeMiB+*dataPartitionSizeMiB) - 1,
Lorenz Brunae0d90d2019-09-05 17:53:56 +020077 },
78 },
79 }
80 if err := diskImg.Partition(table); err != nil {
81 fmt.Printf("Failed to apply partition table: %v", err)
82 os.Exit(1)
83 }
84
85 fs, err := diskImg.CreateFilesystem(disk.FilesystemSpec{Partition: 1, FSType: filesystem.TypeFat32, VolumeLabel: "ESP"})
86 if err != nil {
87 fmt.Printf("Failed to create filesystem: %v", err)
88 os.Exit(1)
89 }
90 if err := fs.Mkdir("/EFI"); err != nil {
91 panic(err)
92 }
93 if err := fs.Mkdir("/EFI/BOOT"); err != nil {
94 panic(err)
95 }
96 if err := fs.Mkdir("/EFI/smalltown"); err != nil {
97 panic(err)
98 }
99 efiPayload, err := fs.OpenFile("/EFI/BOOT/BOOTX64.EFI", os.O_CREATE|os.O_RDWR)
100 if err != nil {
101 fmt.Printf("Failed to open EFI payload for writing: %v", err)
102 os.Exit(1)
103 }
Leopold Schabel65493072019-11-06 13:40:44 +0000104 efiPayloadSrc, err := os.Open(*efiPayloadPath)
Lorenz Brunae0d90d2019-09-05 17:53:56 +0200105 if err != nil {
106 fmt.Printf("Failed to open EFI payload for reading: %v", err)
107 os.Exit(1)
108 }
109 defer efiPayloadSrc.Close()
110 // If this is streamed (e.g. using io.Copy) it exposes a bug in diskfs, so do it in one go.
111 efiPayloadFull, err := ioutil.ReadAll(efiPayloadSrc)
112 if err != nil {
113 panic(err)
114 }
115 if _, err := efiPayload.Write(efiPayloadFull); err != nil {
116 fmt.Printf("Failed to write EFI payload: %v", err)
117 os.Exit(1)
118 }
Lorenz Brun0bcaaee2019-11-06 12:42:39 +0100119 initramfs, err := fs.OpenFile("/EFI/smalltown/initramfs.cpio.lz4", os.O_CREATE|os.O_RDWR)
120 if err != nil {
121 fmt.Printf("Failed to open initramfs for writing: %v", err)
122 os.Exit(1)
123 }
124 // If we have more than two arguments, the second one is the initramfs
Leopold Schabel65493072019-11-06 13:40:44 +0000125 if *initramfsPath != "" {
126 initramfsSrc, err := os.Open(*initramfsPath)
Lorenz Brun0bcaaee2019-11-06 12:42:39 +0100127 if err != nil {
128 fmt.Printf("Failed to open initramfs for reading: %v", err)
129 os.Exit(1)
130 }
131 initramfsFull, err := ioutil.ReadAll(initramfsSrc)
132 if err != nil {
133 fmt.Printf("Failed to read initramfs: %v", err)
134 os.Exit(1)
135 }
136 if _, err := initramfs.Write(initramfsFull); err != nil {
137 fmt.Printf("Failed to write initramfs: %v", err)
138 os.Exit(1)
139 }
140 }
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100141 if *enrolmentCredentialsPath != "" {
142 enrolmentCredentials, err := fs.OpenFile("/EFI/smalltown/enrolment.pb", os.O_CREATE|os.O_RDWR)
143 enrolmentCredentialsSrc, err := os.Open(*enrolmentCredentialsPath)
144 if err != nil {
145 fmt.Printf("Failed to open enrolment credentials for reading: %v", err)
146 os.Exit(1)
147 }
148 enrolmentCredentialsFull, err := ioutil.ReadAll(enrolmentCredentialsSrc)
149 if err != nil {
150 fmt.Printf("Failed to read enrolment credentials: %v", err)
151 os.Exit(1)
152 }
153 if _, err := enrolmentCredentials.Write(enrolmentCredentialsFull); err != nil {
154 fmt.Printf("Failed to write enrolment credentials")
155 os.Exit(1)
156 }
157 }
Lorenz Brunae0d90d2019-09-05 17:53:56 +0200158 if err := diskImg.File.Close(); err != nil {
159 fmt.Printf("Failed to write image: %v", err)
160 os.Exit(1)
161 }
162 fmt.Println("Success! You can now boot smalltown.img")
163}