talos/internal/pkg/zboot/zboot.go
Noel Georgi 88cf69b8c5
feat: multi profile UKIs
Support generating multi profile UKIs.
This PR adds the default wipe options.

Fixes: #10190

Supporting extra profiles via imager would be another PR.

Signed-off-by: Noel Georgi <git@frezbo.dev>
2025-02-11 15:41:55 +05:30

75 lines
2.3 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package zboot provides a function to extract the kernel from a Zboot image.
package zboot
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"os"
"github.com/klauspost/compress/zstd"
"golang.org/x/sys/unix"
)
// Extract extracts the kernel from a Zboot image and returns a file descriptor of the extracted kernel.
func Extract(kernel *os.File) (int, io.Closer, error) {
// https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/tree/include/kexec-pe-zboot.h
var peZbootheaderData [28]byte
if _, err := io.ReadFull(kernel, peZbootheaderData[:]); err != nil {
return 0, nil, err
}
// https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/firmware/efi/libstub/zboot-header.S
// https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/linux/pe.h#n42
if !bytes.Equal(peZbootheaderData[:2], []byte("MZ")) {
return 0, nil, fmt.Errorf("invalid PE Zboot header")
}
// not a Zboot image, return
if !bytes.Equal(peZbootheaderData[4:8], []byte("zimg")) {
return int(kernel.Fd()), nil, nil
}
payloadOffset := binary.LittleEndian.Uint32(peZbootheaderData[8:12])
payloadSize := binary.LittleEndian.Uint32(peZbootheaderData[12:16])
if _, err := kernel.Seek(int64(payloadOffset), io.SeekStart); err != nil {
return 0, nil, fmt.Errorf("failed to seek to kernel zstd data from vmlinuz.efi: %w", err)
}
z, err := zstd.NewReader(io.LimitReader(kernel, int64(payloadSize)))
if err != nil {
return 0, nil, fmt.Errorf("failed to create zstd reader: %w", err)
}
defer z.Close()
fd, err := unix.MemfdCreate("vmlinux", 0)
if err != nil {
return 0, nil, fmt.Errorf("memfdCreate: %v", err)
}
kernelMemfd := os.NewFile(uintptr(fd), "vmlinux")
if _, err := io.Copy(kernelMemfd, z); err != nil {
kernelMemfd.Close() //nolint:errcheck
return 0, nil, fmt.Errorf("failed to copy zstd data to memfd: %w", err)
}
if _, err := kernelMemfd.Seek(0, io.SeekStart); err != nil {
kernelMemfd.Close() //nolint:errcheck
return 0, nil, fmt.Errorf("failed to seek to start of memfd: %w", err)
}
return fd, kernelMemfd, nil
}