package main import ( "archive/tar" "bytes" "debug/pe" "fmt" "io" "log" "os" "path/filepath" "time" ) func rmTempFile(f *os.File) { f.Close() if err := os.Remove(f.Name()); err != nil { log.Print("failed to remove ", f.Name(), ": ", err) } } func buildUki(out io.Writer, ctx *renderContext, uki bool, cmdline string) error { if !uki { return fmt.Errorf("buildUki only builds UKI") } const efiStubPath = "/usr/lib/systemd/boot/efi/linuxx64.efi.stub" stub, err := pe.Open(efiStubPath) if err != nil { return fmt.Errorf("efi stub open: %w", err) } hdr := stub.OptionalHeader.(*pe.OptionalHeader64) align := uint64(hdr.SectionAlignment) var offset uint64 for _, s := range stub.Sections { end := uint64(s.VirtualAddress) + uint64(s.VirtualSize) if end > offset { offset = end } } offset += hdr.ImageBase stub.Close() // kernel kernelPath, err := distFetch("kernels", ctx.Host.Kernel) if err != nil { return fmt.Errorf("fetch kernel: %w", err) } // temp dir tempDir, err := os.MkdirTemp(os.TempDir(), "uki-") if err != nil { return fmt.Errorf("mkdir temp: %w", err) } defer os.RemoveAll(tempDir) // osrel osrelPath := filepath.Join(tempDir, "osrel") if err := os.WriteFile(osrelPath, fmt.Appendf(nil, "ID=direktil\nPRETTY_NAME='Direktil %s %s+0'\n", ctx.Host.Name, time.Now().UTC().Format(time.DateTime)), 0o600); err != nil { return fmt.Errorf("create osrel: %w", err) } // cmdline cmdlinePath := filepath.Join(tempDir, "cmdline") if err := os.WriteFile(cmdlinePath, append([]byte(cmdline), 0), 0o600); err != nil { return fmt.Errorf("create cmdline: %w", err) } // initrd initrdPath := filepath.Join(tempDir, "initrd") if err := func() (err error) { initrd, err := os.Create(initrdPath) if err != nil { return } defer initrd.Close() return buildInitrd(initrd, ctx) }(); err != nil { return fmt.Errorf("create initrd: %w", err) } // assemble args := make([]string, 0) for _, i := range []struct { section string path string }{ {"osrel", osrelPath}, {"cmdline", cmdlinePath}, {"initrd", initrdPath}, {"linux", kernelPath}, } { offset += align - offset%align args = append(args, "--add-section", "."+i.section+"="+i.path, "--change-section-vma", fmt.Sprintf(".%s=0x%x", i.section, offset)) stat, err := os.Stat(i.path) if err != nil { return fmt.Errorf("stat %s: %w", i.section, err) } offset += uint64(stat.Size()) } ukiPath := filepath.Join(tempDir, "uki") args = append(args, efiStubPath, ukiPath) if err := run("objcopy", args...); err != nil { return fmt.Errorf("objcopy: %w", err) } // read ukiBytes, err := os.ReadFile(ukiPath) if err != nil { return fmt.Errorf("read uki: %w", err) } io.Copy(out, bytes.NewBuffer(ukiBytes)) // done return nil } func buildBootTar(out io.Writer, ctx *renderContext, uki bool, cmdline string) (err error) { if uki { return buildUkiBootTar(out, ctx, cmdline) } else { return buildGrubBootTar(out, ctx) } } func buildGrubBootTar(out io.Writer, ctx *renderContext) (err error) { arch := tar.NewWriter(out) defer arch.Close() archAdd := func(path string, ba []byte) (err error) { err = arch.WriteHeader(&tar.Header{Name: path, Mode: 0640, Size: int64(len(ba))}) if err != nil { return } _, err = arch.Write(ba) return } // kernel kernelPath, err := distFetch("kernels", ctx.Host.Kernel) if err != nil { return } kernelBytes, err := os.ReadFile(kernelPath) if err != nil { return } err = archAdd("current/vmlinuz", kernelBytes) if err != nil { return } // initrd initrd := new(bytes.Buffer) err = buildInitrd(initrd, ctx) if err != nil { return } err = archAdd("current/initrd", initrd.Bytes()) if err != nil { return } // done return nil } func buildUkiBootTar(out io.Writer, ctx *renderContext, cmdline string) (err error) { arch := tar.NewWriter(out) defer arch.Close() archAdd := func(path string, ba []byte) (err error) { err = arch.WriteHeader(&tar.Header{Name: path, Mode: 0640, Size: int64(len(ba))}) if err != nil { return } _, err = arch.Write(ba) return } // UKI uki := new(bytes.Buffer) err = buildUki(uki, ctx, true, cmdline) if err != nil { return } err = archAdd("EFI/BOOT/BOOTX64.EFI", uki.Bytes()) if err != nil { return } // done return nil }