blob: f733e78f7b9c6493e8d3a80400b6480f4322f12d [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
25 "github.com/diskfs/go-diskfs"
26 "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 (
34 efiPayloadPath = flag.String("efi", "", "UEFI payload")
35 outputPath = flag.String("out", "", "Output disk image")
36 initramfsPath = flag.String("initramfs", "", "External initramfs [optional]")
37)
38
Lorenz Brunae0d90d2019-09-05 17:53:56 +020039func mibToSectors(size uint64) uint64 {
40 return (size * 1024 * 1024) / 512
41}
42
Lorenz Brunae0d90d2019-09-05 17:53:56 +020043func main() {
Leopold Schabel65493072019-11-06 13:40:44 +000044 flag.Parse()
45 if *efiPayloadPath == "" || *outputPath == "" {
46 flag.PrintDefaults()
Lorenz Brunf95909d2019-09-11 19:48:26 +020047 os.Exit(2)
48 }
Leopold Schabel65493072019-11-06 13:40:44 +000049
50 _ = os.Remove(*outputPath)
51 diskImg, err := diskfs.Create(*outputPath, 3*1024*1024*1024, diskfs.Raw)
Lorenz Brunae0d90d2019-09-05 17:53:56 +020052 if err != nil {
53 fmt.Printf("Failed to create disk: %v", err)
54 os.Exit(1)
55 }
56
57 table := &gpt.Table{
58 // This is appropriate at least for virtio disks. Might need to be adjusted for real ones.
59 LogicalSectorSize: 512,
60 PhysicalSectorSize: 512,
61 ProtectiveMBR: true,
62 Partitions: []*gpt.Partition{
63 {
64 Type: gpt.EFISystemPartition,
65 Name: "ESP",
66 Start: mibToSectors(1),
Lorenz Brun0bcaaee2019-11-06 12:42:39 +010067 End: mibToSectors(256) - 1,
Lorenz Brunae0d90d2019-09-05 17:53:56 +020068 },
69 {
70 Type: SmalltownDataPartition,
71 Name: "SIGNOS-DATA",
Lorenz Brun0bcaaee2019-11-06 12:42:39 +010072 Start: mibToSectors(256),
Lorenz Brunae0d90d2019-09-05 17:53:56 +020073 End: mibToSectors(2560) - 1,
74 },
75 },
76 }
77 if err := diskImg.Partition(table); err != nil {
78 fmt.Printf("Failed to apply partition table: %v", err)
79 os.Exit(1)
80 }
81
82 fs, err := diskImg.CreateFilesystem(disk.FilesystemSpec{Partition: 1, FSType: filesystem.TypeFat32, VolumeLabel: "ESP"})
83 if err != nil {
84 fmt.Printf("Failed to create filesystem: %v", err)
85 os.Exit(1)
86 }
87 if err := fs.Mkdir("/EFI"); err != nil {
88 panic(err)
89 }
90 if err := fs.Mkdir("/EFI/BOOT"); err != nil {
91 panic(err)
92 }
93 if err := fs.Mkdir("/EFI/smalltown"); err != nil {
94 panic(err)
95 }
96 efiPayload, err := fs.OpenFile("/EFI/BOOT/BOOTX64.EFI", os.O_CREATE|os.O_RDWR)
97 if err != nil {
98 fmt.Printf("Failed to open EFI payload for writing: %v", err)
99 os.Exit(1)
100 }
Leopold Schabel65493072019-11-06 13:40:44 +0000101 efiPayloadSrc, err := os.Open(*efiPayloadPath)
Lorenz Brunae0d90d2019-09-05 17:53:56 +0200102 if err != nil {
103 fmt.Printf("Failed to open EFI payload for reading: %v", err)
104 os.Exit(1)
105 }
106 defer efiPayloadSrc.Close()
107 // If this is streamed (e.g. using io.Copy) it exposes a bug in diskfs, so do it in one go.
108 efiPayloadFull, err := ioutil.ReadAll(efiPayloadSrc)
109 if err != nil {
110 panic(err)
111 }
112 if _, err := efiPayload.Write(efiPayloadFull); err != nil {
113 fmt.Printf("Failed to write EFI payload: %v", err)
114 os.Exit(1)
115 }
Lorenz Brun0bcaaee2019-11-06 12:42:39 +0100116 initramfs, err := fs.OpenFile("/EFI/smalltown/initramfs.cpio.lz4", os.O_CREATE|os.O_RDWR)
117 if err != nil {
118 fmt.Printf("Failed to open initramfs for writing: %v", err)
119 os.Exit(1)
120 }
121 // If we have more than two arguments, the second one is the initramfs
Leopold Schabel65493072019-11-06 13:40:44 +0000122 if *initramfsPath != "" {
123 initramfsSrc, err := os.Open(*initramfsPath)
Lorenz Brun0bcaaee2019-11-06 12:42:39 +0100124 if err != nil {
125 fmt.Printf("Failed to open initramfs for reading: %v", err)
126 os.Exit(1)
127 }
128 initramfsFull, err := ioutil.ReadAll(initramfsSrc)
129 if err != nil {
130 fmt.Printf("Failed to read initramfs: %v", err)
131 os.Exit(1)
132 }
133 if _, err := initramfs.Write(initramfsFull); err != nil {
134 fmt.Printf("Failed to write initramfs: %v", err)
135 os.Exit(1)
136 }
137 }
Lorenz Brunae0d90d2019-09-05 17:53:56 +0200138 if err := diskImg.File.Close(); err != nil {
139 fmt.Printf("Failed to write image: %v", err)
140 os.Exit(1)
141 }
142 fmt.Println("Success! You can now boot smalltown.img")
143}