blob: 9f49f0a9b1514ea24092477f58d393d0aade1040 [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
Serge Bazanski032ca182020-06-09 20:17:13 +020019// mkimage is a tool to generate a Smalltown disk image containing the given EFI payload, and optionally, a given external
20// initramfs image and enrolment credentials.
21
Lorenz Brunae0d90d2019-09-05 17:53:56 +020022import (
Leopold Schabel65493072019-11-06 13:40:44 +000023 "flag"
Lorenz Brunae0d90d2019-09-05 17:53:56 +020024 "fmt"
25 "io/ioutil"
Serge Bazanski032ca182020-06-09 20:17:13 +020026 "log"
Lorenz Brunae0d90d2019-09-05 17:53:56 +020027 "os"
Lorenz Brunae0d90d2019-09-05 17:53:56 +020028
Hendrik Hofstadt8efe51e2020-02-28 12:53:41 +010029 diskfs "github.com/diskfs/go-diskfs"
Lorenz Brunae0d90d2019-09-05 17:53:56 +020030 "github.com/diskfs/go-diskfs/disk"
31 "github.com/diskfs/go-diskfs/filesystem"
32 "github.com/diskfs/go-diskfs/partition/gpt"
Lorenz Brunae0d90d2019-09-05 17:53:56 +020033)
34
35var SmalltownDataPartition gpt.Type = gpt.Type("9eeec464-6885-414a-b278-4305c51f7966")
36
Leopold Schabel65493072019-11-06 13:40:44 +000037var (
Serge Bazanski032ca182020-06-09 20:17:13 +020038 flagEFI string
39 flagOut string
40 flagInitramfs string
41 flagEnrolmentCredentials string
42 flagDataPartitionSize uint64
43 flagESPPartitionSize uint64
Leopold Schabel65493072019-11-06 13:40:44 +000044)
45
Lorenz Brunae0d90d2019-09-05 17:53:56 +020046func mibToSectors(size uint64) uint64 {
47 return (size * 1024 * 1024) / 512
48}
49
Lorenz Brunae0d90d2019-09-05 17:53:56 +020050func main() {
Serge Bazanski032ca182020-06-09 20:17:13 +020051 flag.StringVar(&flagEFI, "efi", "", "UEFI payload")
52 flag.StringVar(&flagOut, "out", "", "Output disk image")
53 flag.StringVar(&flagInitramfs, "initramfs", "", "External initramfs [optional]")
54 flag.StringVar(&flagEnrolmentCredentials, "enrolment_credentials", "", "Enrolment credentials [optional]")
55 flag.Uint64Var(&flagDataPartitionSize, "data_partition_size", 2048, "Override the data partition size (default 2048 MiB)")
56 flag.Uint64Var(&flagESPPartitionSize, "esp_partition_size", 512, "Override the ESP partition size (default: 512MiB)")
Leopold Schabel65493072019-11-06 13:40:44 +000057 flag.Parse()
Serge Bazanski032ca182020-06-09 20:17:13 +020058
59 if flagEFI == "" || flagOut == "" {
60 log.Fatalf("efi and initramfs must be set")
Lorenz Brunf95909d2019-09-11 19:48:26 +020061 }
Leopold Schabel65493072019-11-06 13:40:44 +000062
Serge Bazanski032ca182020-06-09 20:17:13 +020063 _ = os.Remove(flagOut)
64 diskImg, err := diskfs.Create(flagOut, 3*1024*1024*1024, diskfs.Raw)
Lorenz Brunae0d90d2019-09-05 17:53:56 +020065 if err != nil {
Serge Bazanski032ca182020-06-09 20:17:13 +020066 log.Fatalf("diskfs.Create(%q): %v", flagOut, err)
Lorenz Brunae0d90d2019-09-05 17:53:56 +020067 }
68
69 table := &gpt.Table{
70 // This is appropriate at least for virtio disks. Might need to be adjusted for real ones.
71 LogicalSectorSize: 512,
72 PhysicalSectorSize: 512,
73 ProtectiveMBR: true,
74 Partitions: []*gpt.Partition{
75 {
76 Type: gpt.EFISystemPartition,
77 Name: "ESP",
78 Start: mibToSectors(1),
Serge Bazanski032ca182020-06-09 20:17:13 +020079 End: mibToSectors(flagESPPartitionSize) - 1,
Lorenz Brunae0d90d2019-09-05 17:53:56 +020080 },
81 {
82 Type: SmalltownDataPartition,
83 Name: "SIGNOS-DATA",
Serge Bazanski032ca182020-06-09 20:17:13 +020084 Start: mibToSectors(flagESPPartitionSize),
85 End: mibToSectors(flagESPPartitionSize+flagDataPartitionSize) - 1,
Lorenz Brunae0d90d2019-09-05 17:53:56 +020086 },
87 },
88 }
89 if err := diskImg.Partition(table); err != nil {
Serge Bazanski032ca182020-06-09 20:17:13 +020090 log.Fatalf("Failed to apply partition table: %v", err)
Lorenz Brunae0d90d2019-09-05 17:53:56 +020091 }
92
93 fs, err := diskImg.CreateFilesystem(disk.FilesystemSpec{Partition: 1, FSType: filesystem.TypeFat32, VolumeLabel: "ESP"})
94 if err != nil {
Serge Bazanski032ca182020-06-09 20:17:13 +020095 log.Fatalf("Failed to create filesystem: %v", err)
Lorenz Brunae0d90d2019-09-05 17:53:56 +020096 }
Serge Bazanski032ca182020-06-09 20:17:13 +020097
98 // Create EFI partition structure.
99 for _, dir := range []string{"/EFI", "/EFI/BOOT", "/EFI/smalltown"} {
100 if err := fs.Mkdir(dir); err != nil {
101 log.Fatalf("Mkdir(%q): %v", dir, err)
Lorenz Brun0bcaaee2019-11-06 12:42:39 +0100102 }
103 }
Serge Bazanski032ca182020-06-09 20:17:13 +0200104
105 put(fs, flagEFI, "/EFI/BOOT/BOOTX64.EFI")
106
107 if flagInitramfs != "" {
108 put(fs, flagInitramfs, "/EFI/smalltown/initramfs.cpio.lz4")
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100109 }
Serge Bazanski032ca182020-06-09 20:17:13 +0200110
111 if flagEnrolmentCredentials != "" {
112 put(fs, flagEnrolmentCredentials, "/EFI/smalltown/enrolment.pb")
113 }
114
Lorenz Brunae0d90d2019-09-05 17:53:56 +0200115 if err := diskImg.File.Close(); err != nil {
Serge Bazanski032ca182020-06-09 20:17:13 +0200116 log.Fatalf("Failed to finalize image: %v", err)
117 }
118 log.Printf("Success! You can now boot %v", flagOut)
119}
120
121// put copies a file from the host filesystem into the target image.
122func put(fs filesystem.FileSystem, src, dst string) {
123 target, err := fs.OpenFile(dst, os.O_CREATE|os.O_RDWR)
124 if err != nil {
125 log.Fatalf("fs.OpenFile(%q): %v", dst, err)
126 }
127 source, err := os.Open(src)
128 if err != nil {
129 log.Fatalf("os.Open(%q): %v", src, err)
130 }
131 defer source.Close()
132 // If this is streamed (e.g. using io.Copy) it exposes a bug in diskfs, so do it in one go.
133 data, err := ioutil.ReadAll(source)
134 if err != nil {
135 log.Fatalf("Reading %q: %v", src, err)
136 }
137 if _, err := target.Write(data); err != nil {
138 fmt.Printf("writing file %q: %v", dst, err)
Lorenz Brunae0d90d2019-09-05 17:53:56 +0200139 os.Exit(1)
140 }
Lorenz Brunae0d90d2019-09-05 17:53:56 +0200141}