|  | // Copyright 2020 The Monogon Project Authors. | 
|  | // | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | package main | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "io/ioutil" | 
|  | "os" | 
|  |  | 
|  | "github.com/diskfs/go-diskfs" | 
|  | "github.com/diskfs/go-diskfs/disk" | 
|  | "github.com/diskfs/go-diskfs/filesystem" | 
|  | "github.com/diskfs/go-diskfs/partition/gpt" | 
|  | ) | 
|  |  | 
|  | var SmalltownDataPartition gpt.Type = gpt.Type("9eeec464-6885-414a-b278-4305c51f7966") | 
|  |  | 
|  | func mibToSectors(size uint64) uint64 { | 
|  | return (size * 1024 * 1024) / 512 | 
|  | } | 
|  |  | 
|  | func main() { | 
|  | if len(os.Args) < 3 { | 
|  | fmt.Println("Usage: mkimage <UEFI payload> <image path>") | 
|  | os.Exit(2) | 
|  | } | 
|  | _ = os.Remove(os.Args[2]) | 
|  | diskImg, err := diskfs.Create(os.Args[2], 3*1024*1024*1024, diskfs.Raw) | 
|  | if err != nil { | 
|  | fmt.Printf("Failed to create disk: %v", err) | 
|  | os.Exit(1) | 
|  | } | 
|  |  | 
|  | table := &gpt.Table{ | 
|  | // This is appropriate at least for virtio disks. Might need to be adjusted for real ones. | 
|  | LogicalSectorSize:  512, | 
|  | PhysicalSectorSize: 512, | 
|  | ProtectiveMBR:      true, | 
|  | Partitions: []*gpt.Partition{ | 
|  | { | 
|  | Type:  gpt.EFISystemPartition, | 
|  | Name:  "ESP", | 
|  | Start: mibToSectors(1), | 
|  | End:   mibToSectors(128) - 1, | 
|  | }, | 
|  | { | 
|  | Type:  SmalltownDataPartition, | 
|  | Name:  "SIGNOS-DATA", | 
|  | Start: mibToSectors(128), | 
|  | End:   mibToSectors(2560) - 1, | 
|  | }, | 
|  | }, | 
|  | } | 
|  | if err := diskImg.Partition(table); err != nil { | 
|  | fmt.Printf("Failed to apply partition table: %v", err) | 
|  | os.Exit(1) | 
|  | } | 
|  |  | 
|  | fs, err := diskImg.CreateFilesystem(disk.FilesystemSpec{Partition: 1, FSType: filesystem.TypeFat32, VolumeLabel: "ESP"}) | 
|  | if err != nil { | 
|  | fmt.Printf("Failed to create filesystem: %v", err) | 
|  | os.Exit(1) | 
|  | } | 
|  | if err := fs.Mkdir("/EFI"); err != nil { | 
|  | panic(err) | 
|  | } | 
|  | if err := fs.Mkdir("/EFI/BOOT"); err != nil { | 
|  | panic(err) | 
|  | } | 
|  | if err := fs.Mkdir("/EFI/smalltown"); err != nil { | 
|  | panic(err) | 
|  | } | 
|  | efiPayload, err := fs.OpenFile("/EFI/BOOT/BOOTX64.EFI", os.O_CREATE|os.O_RDWR) | 
|  | if err != nil { | 
|  | fmt.Printf("Failed to open EFI payload for writing: %v", err) | 
|  | os.Exit(1) | 
|  | } | 
|  | efiPayloadSrc, err := os.Open(os.Args[1]) | 
|  | if err != nil { | 
|  | fmt.Printf("Failed to open EFI payload for reading: %v", err) | 
|  | os.Exit(1) | 
|  | } | 
|  | defer efiPayloadSrc.Close() | 
|  | // If this is streamed (e.g. using io.Copy) it exposes a bug in diskfs, so do it in one go. | 
|  | efiPayloadFull, err := ioutil.ReadAll(efiPayloadSrc) | 
|  | if err != nil { | 
|  | panic(err) | 
|  | } | 
|  | if _, err := efiPayload.Write(efiPayloadFull); err != nil { | 
|  | fmt.Printf("Failed to write EFI payload: %v", err) | 
|  | os.Exit(1) | 
|  | } | 
|  | if err := diskImg.File.Close(); err != nil { | 
|  | fmt.Printf("Failed to write image: %v", err) | 
|  | os.Exit(1) | 
|  | } | 
|  | fmt.Println("Success! You can now boot smalltown.img") | 
|  | } |