// 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 }