From cbe6e7622d0180ca53ab0ce92d38e4704d466d1a Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 27 Oct 2023 21:55:38 +0400 Subject: [PATCH] fix: generate images for SBCs using imager See https://github.com/siderolabs/image-factory/issues/43 Two fixes: * pass path to the dtb, uboot and rpi-firmware explicitly * include dtb, uboot and rpi-firmware into arm64 installer image when generated via imager (regular arm64 installer was fine) (The generation of SBC images was not broken for Talos itself, but only when used via Image Factory). Signed-off-by: Andrey Smirnov --- cmd/installer/pkg/install/install.go | 8 +++- internal/app/machined/pkg/runtime/board.go | 11 ++++- .../board/bananapi_m64/bananapi_m64.go | 20 ++++----- .../v1alpha1/board/jetson_nano/jetson_nano.go | 10 ++--- .../libretech_all_h3_cc_h5.go | 20 ++++----- .../v1alpha1/board/nanopi_r4s/nanopi_r4s.go | 20 ++++----- .../runtime/v1alpha1/board/pine64/pine64.go | 20 ++++----- .../runtime/v1alpha1/board/rock64/rock64.go | 20 ++++----- .../runtime/v1alpha1/board/rockpi4/rockpi4.go | 20 ++++----- .../v1alpha1/board/rockpi4c/rockpi4c.go | 20 ++++----- .../v1alpha1/board/rpi_generic/rpi_generic.go | 7 ++-- .../v1alpha1/bootloader/options/options.go | 20 ++++++++- pkg/imager/filemap/filemap.go | 30 +++++++++++++ pkg/imager/out.go | 42 +++++++++++++++++-- pkg/imager/profile/input.go | 27 +++++++++++- pkg/imager/profile/profile.go | 4 +- pkg/machinery/constants/constants.go | 9 ++++ 17 files changed, 213 insertions(+), 95 deletions(-) diff --git a/cmd/installer/pkg/install/install.go b/cmd/installer/pkg/install/install.go index 1d87a2452..e6f8cb6ff 100644 --- a/cmd/installer/pkg/install/install.go +++ b/cmd/installer/pkg/install/install.go @@ -285,7 +285,13 @@ func (i *Installer) Install(ctx context.Context, mode Mode) (err error) { i.options.Printf("installing U-Boot for %q", b.Name()) - if err = b.Install(i.options.Disk); err != nil { + if err = b.Install(runtime.BoardInstallOptions{ + InstallDisk: i.options.Disk, + UBootPath: i.options.BootAssets.UBootPath, + DTBPath: i.options.BootAssets.DTBPath, + RPiFirmwarePath: i.options.BootAssets.RPiFirmwarePath, + Printf: i.options.Printf, + }); err != nil { return err } } diff --git a/internal/app/machined/pkg/runtime/board.go b/internal/app/machined/pkg/runtime/board.go index 55c7ea76b..b40c82e4a 100644 --- a/internal/app/machined/pkg/runtime/board.go +++ b/internal/app/machined/pkg/runtime/board.go @@ -12,10 +12,19 @@ type PartitionOptions struct { PartitionsOffset uint64 } +// BoardInstallOptions are the board specific options for installation of various boot assets. +type BoardInstallOptions struct { + InstallDisk string + DTBPath string + UBootPath string + RPiFirmwarePath string + Printf func(string, ...any) +} + // Board defines the requirements for a SBC. type Board interface { Name() string - Install(string) error + Install(options BoardInstallOptions) error KernelArgs() procfs.Parameters PartitionOptions() *PartitionOptions } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/board/bananapi_m64/bananapi_m64.go b/internal/app/machined/pkg/runtime/v1alpha1/board/bananapi_m64/bananapi_m64.go index 32d5fe982..a10f5260b 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/board/bananapi_m64/bananapi_m64.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/board/bananapi_m64/bananapi_m64.go @@ -6,8 +6,6 @@ package bananapim64 import ( - "fmt" - "log" "os" "path/filepath" @@ -20,9 +18,9 @@ import ( ) var ( - bin = fmt.Sprintf("/usr/install/arm64/u-boot/%s/u-boot-sunxi-with-spl.bin", constants.BoardBananaPiM64) + bin = constants.BoardBananaPiM64 + "/u-boot-sunxi-with-spl.bin" off int64 = 1024 * 8 - dtb = "/dtb/allwinner/sun50i-a64-bananapi-m64.dtb" + dtb = "allwinner/sun50i-a64-bananapi-m64.dtb" ) // BananaPiM64 represents the Banana Pi M64. @@ -39,10 +37,10 @@ func (b *BananaPiM64) Name() string { } // Install implements the runtime.Board. -func (b *BananaPiM64) Install(disk string) (err error) { +func (b *BananaPiM64) Install(options runtime.BoardInstallOptions) (err error) { var f *os.File - if f, err = os.OpenFile(disk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { + if f, err = os.OpenFile(options.InstallDisk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { return err } //nolint:errcheck @@ -50,12 +48,12 @@ func (b *BananaPiM64) Install(disk string) (err error) { var uboot []byte - uboot, err = os.ReadFile(bin) + uboot, err = os.ReadFile(filepath.Join(options.UBootPath, bin)) if err != nil { return err } - log.Printf("writing %s at offset %d", bin, off) + options.Printf("writing %s at offset %d", bin, off) var n int @@ -64,7 +62,7 @@ func (b *BananaPiM64) Install(disk string) (err error) { return err } - log.Printf("wrote %d bytes", n) + options.Printf("wrote %d bytes", n) // NB: In the case that the block device is a loopback device, we sync here // to esure that the file is written before the loopback device is @@ -74,8 +72,8 @@ func (b *BananaPiM64) Install(disk string) (err error) { return err } - src := "/usr/install/arm64" + dtb - dst := "/boot/EFI" + dtb + src := filepath.Join(options.DTBPath, dtb) + dst := filepath.Join("/boot/EFI/dtb", dtb) err = os.MkdirAll(filepath.Dir(dst), 0o600) if err != nil { diff --git a/internal/app/machined/pkg/runtime/v1alpha1/board/jetson_nano/jetson_nano.go b/internal/app/machined/pkg/runtime/v1alpha1/board/jetson_nano/jetson_nano.go index ef3a2853a..2b3585a60 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/board/jetson_nano/jetson_nano.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/board/jetson_nano/jetson_nano.go @@ -21,7 +21,7 @@ import ( // - https://github.com/u-boot/u-boot/blob/v2021.10/configs/p3450-0000_defconfig#L8 // - https://github.com/u-boot/u-boot/blob/v2021.10/include/configs/tegra-common.h#L53 // - https://github.com/u-boot/u-boot/blob/v2021.10/include/configs/tegra210-common.h#L49 -var dtb = "/dtb/nvidia/tegra210-p3450-0000.dtb" +var dtb = "nvidia/tegra210-p3450-0000.dtb" // JetsonNano represents the JetsonNano board // @@ -35,10 +35,10 @@ func (b *JetsonNano) Name() string { } // Install implements the runtime.Board. -func (b JetsonNano) Install(disk string) (err error) { +func (b JetsonNano) Install(options runtime.BoardInstallOptions) (err error) { var f *os.File - if f, err = os.OpenFile(disk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { + if f, err = os.OpenFile(options.InstallDisk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { return err } //nolint:errcheck @@ -52,8 +52,8 @@ func (b JetsonNano) Install(disk string) (err error) { return err } - src := "/usr/install/arm64" + dtb - dst := "/boot/EFI" + dtb + src := filepath.Join(options.DTBPath, dtb) + dst := filepath.Join("/boot/EFI/dtb", dtb) err = os.MkdirAll(filepath.Dir(dst), 0o600) if err != nil { diff --git a/internal/app/machined/pkg/runtime/v1alpha1/board/libretech_all_h3_cc_h5/libretech_all_h3_cc_h5.go b/internal/app/machined/pkg/runtime/v1alpha1/board/libretech_all_h3_cc_h5/libretech_all_h3_cc_h5.go index 910cccbd5..70c1ab225 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/board/libretech_all_h3_cc_h5/libretech_all_h3_cc_h5.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/board/libretech_all_h3_cc_h5/libretech_all_h3_cc_h5.go @@ -6,8 +6,6 @@ package libretechallh3cch5 import ( - "fmt" - "log" "os" "path/filepath" @@ -20,9 +18,9 @@ import ( ) var ( - bin = fmt.Sprintf("/usr/install/arm64/u-boot/%s/u-boot-sunxi-with-spl.bin", constants.BoardLibretechAllH3CCH5) + bin = constants.BoardLibretechAllH3CCH5 + "/u-boot-sunxi-with-spl.bin" off int64 = 1024 * 8 - dtb = "/dtb/allwinner/sun50i-h5-libretech-all-h3-cc.dtb" + dtb = "allwinner/sun50i-h5-libretech-all-h3-cc.dtb" ) // LibretechAllH3CCH5 represents the Libre Computer ALL-H3-CC (Tritium). @@ -36,10 +34,10 @@ func (l *LibretechAllH3CCH5) Name() string { } // Install implements the runtime.Board. -func (l *LibretechAllH3CCH5) Install(disk string) (err error) { +func (l *LibretechAllH3CCH5) Install(options runtime.BoardInstallOptions) (err error) { var f *os.File - if f, err = os.OpenFile(disk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { + if f, err = os.OpenFile(options.InstallDisk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { return err } //nolint:errcheck @@ -47,12 +45,12 @@ func (l *LibretechAllH3CCH5) Install(disk string) (err error) { var uboot []byte - uboot, err = os.ReadFile(bin) + uboot, err = os.ReadFile(filepath.Join(options.UBootPath, bin)) if err != nil { return err } - log.Printf("writing %s at offset %d", bin, off) + options.Printf("writing %s at offset %d", bin, off) var n int @@ -61,7 +59,7 @@ func (l *LibretechAllH3CCH5) Install(disk string) (err error) { return err } - log.Printf("wrote %d bytes", n) + options.Printf("wrote %d bytes", n) // NB: In the case that the block device is a loopback device, we sync here // to esure that the file is written before the loopback device is @@ -71,8 +69,8 @@ func (l *LibretechAllH3CCH5) Install(disk string) (err error) { return err } - src := "/usr/install/arm64" + dtb - dst := "/boot/EFI" + dtb + src := filepath.Join(options.DTBPath, dtb) + dst := filepath.Join("/boot/EFI/dtb", dtb) err = os.MkdirAll(filepath.Dir(dst), 0o600) if err != nil { diff --git a/internal/app/machined/pkg/runtime/v1alpha1/board/nanopi_r4s/nanopi_r4s.go b/internal/app/machined/pkg/runtime/v1alpha1/board/nanopi_r4s/nanopi_r4s.go index b46d6788e..54d40c612 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/board/nanopi_r4s/nanopi_r4s.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/board/nanopi_r4s/nanopi_r4s.go @@ -6,8 +6,6 @@ package nanopir4s import ( - "fmt" - "log" "os" "path/filepath" @@ -20,9 +18,9 @@ import ( ) var ( - bin = fmt.Sprintf("/usr/install/arm64/u-boot/%s/u-boot-rockchip.bin", constants.BoardNanoPiR4S) + bin = constants.BoardNanoPiR4S + "u-boot-rockchip.bin" off int64 = 512 * 64 - dtb = "/dtb/rockchip/rk3399-nanopi-r4s.dtb" + dtb = "rockchip/rk3399-nanopi-r4s.dtb" ) // NanoPiR4S represents the Friendlyelec Nano Pi R4S board. @@ -36,27 +34,27 @@ func (n *NanoPiR4S) Name() string { } // Install implements the runtime.Board. -func (n *NanoPiR4S) Install(disk string) (err error) { - file, err := os.OpenFile(disk, os.O_RDWR|unix.O_CLOEXEC, 0o666) +func (n *NanoPiR4S) Install(options runtime.BoardInstallOptions) (err error) { + file, err := os.OpenFile(options.InstallDisk, os.O_RDWR|unix.O_CLOEXEC, 0o666) if err != nil { return err } defer file.Close() //nolint:errcheck - uboot, err := os.ReadFile(bin) + uboot, err := os.ReadFile(filepath.Join(options.UBootPath, bin)) if err != nil { return err } - log.Printf("writing %s at offset %d", bin, off) + options.Printf("writing %s at offset %d", bin, off) amount, err := file.WriteAt(uboot, off) if err != nil { return err } - log.Printf("wrote %d bytes", amount) + options.Printf("wrote %d bytes", amount) // NB: In the case that the block device is a loopback device, we sync here // to esure that the file is written before the loopback device is @@ -65,8 +63,8 @@ func (n *NanoPiR4S) Install(disk string) (err error) { return err } - src := "/usr/install/arm64" + dtb - dst := "/boot/EFI" + dtb + src := filepath.Join(options.DTBPath, dtb) + dst := filepath.Join("/boot/EFI/dtb", dtb) if err := os.MkdirAll(filepath.Dir(dst), 0o600); err != nil { return err diff --git a/internal/app/machined/pkg/runtime/v1alpha1/board/pine64/pine64.go b/internal/app/machined/pkg/runtime/v1alpha1/board/pine64/pine64.go index 26d395ba1..4f680a9b3 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/board/pine64/pine64.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/board/pine64/pine64.go @@ -6,8 +6,6 @@ package pine64 import ( - "fmt" - "log" "os" "path/filepath" @@ -20,9 +18,9 @@ import ( ) var ( - bin = fmt.Sprintf("usr/install/arm64/u-boot/%s/u-boot-sunxi-with-spl.bin", constants.BoardPine64) + bin = constants.BoardPine64 + "/u-boot-sunxi-with-spl.bin" off int64 = 1024 * 8 - dtb = "/dtb/allwinner/sun50i-a64-pine64-plus.dtb" + dtb = "allwinner/sun50i-a64-pine64-plus.dtb" ) // Pine64 represents the Pine64 board @@ -37,10 +35,10 @@ func (b *Pine64) Name() string { } // Install implements the runtime.Board. -func (b Pine64) Install(disk string) (err error) { +func (b Pine64) Install(options runtime.BoardInstallOptions) (err error) { var f *os.File - if f, err = os.OpenFile(disk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { + if f, err = os.OpenFile(options.InstallDisk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { return err } //nolint:errcheck @@ -48,12 +46,12 @@ func (b Pine64) Install(disk string) (err error) { var uboot []byte - uboot, err = os.ReadFile(bin) + uboot, err = os.ReadFile(filepath.Join(options.UBootPath, bin)) if err != nil { return err } - log.Printf("writing %s at offset %d", bin, off) + options.Printf("writing %s at offset %d", bin, off) var n int @@ -62,7 +60,7 @@ func (b Pine64) Install(disk string) (err error) { return err } - log.Printf("wrote %d bytes", n) + options.Printf("wrote %d bytes", n) // NB: In the case that the block device is a loopback device, we sync here // to esure that the file is written before the loopback device is @@ -72,8 +70,8 @@ func (b Pine64) Install(disk string) (err error) { return err } - src := "/usr/install/arm64" + dtb - dst := "/boot/EFI" + dtb + src := filepath.Join(options.DTBPath, dtb) + dst := filepath.Join("/boot/EFI/dtb", dtb) err = os.MkdirAll(filepath.Dir(dst), 0o600) if err != nil { diff --git a/internal/app/machined/pkg/runtime/v1alpha1/board/rock64/rock64.go b/internal/app/machined/pkg/runtime/v1alpha1/board/rock64/rock64.go index 57ce8626a..578f05277 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/board/rock64/rock64.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/board/rock64/rock64.go @@ -6,8 +6,6 @@ package rock64 import ( - "fmt" - "log" "os" "path/filepath" @@ -20,9 +18,9 @@ import ( ) var ( - bin = fmt.Sprintf("/usr/install/arm64/u-boot/%s/u-boot-rockchip.bin", constants.BoardRock64) + bin = constants.BoardRock64 + "/u-boot-rockchip.bin" off int64 = 512 * 64 - dtb = "/dtb/rockchip/rk3328-rock64.dtb" + dtb = "rockchip/rk3328-rock64.dtb" ) // Rock64 represents the Pine64 Rock64 board. @@ -36,10 +34,10 @@ func (r *Rock64) Name() string { } // Install implements the runtime.Board. -func (r *Rock64) Install(disk string) (err error) { +func (r *Rock64) Install(options runtime.BoardInstallOptions) (err error) { var f *os.File - if f, err = os.OpenFile(disk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { + if f, err = os.OpenFile(options.InstallDisk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { return err } //nolint:errcheck @@ -47,12 +45,12 @@ func (r *Rock64) Install(disk string) (err error) { var uboot []byte - uboot, err = os.ReadFile(bin) + uboot, err = os.ReadFile(filepath.Join(options.UBootPath, bin)) if err != nil { return err } - log.Printf("writing %s at offset %d", bin, off) + options.Printf("writing %s at offset %d", bin, off) var n int @@ -61,7 +59,7 @@ func (r *Rock64) Install(disk string) (err error) { return err } - log.Printf("wrote %d bytes", n) + options.Printf("wrote %d bytes", n) // NB: In the case that the block device is a loopback device, we sync here // to esure that the file is written before the loopback device is @@ -71,8 +69,8 @@ func (r *Rock64) Install(disk string) (err error) { return err } - src := "/usr/install/arm64" + dtb - dst := "/boot/EFI" + dtb + src := filepath.Join(options.DTBPath, dtb) + dst := filepath.Join("/boot/EFI/dtb", dtb) err = os.MkdirAll(filepath.Dir(dst), 0o600) if err != nil { diff --git a/internal/app/machined/pkg/runtime/v1alpha1/board/rockpi4/rockpi4.go b/internal/app/machined/pkg/runtime/v1alpha1/board/rockpi4/rockpi4.go index 224060303..47e374c2c 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/board/rockpi4/rockpi4.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/board/rockpi4/rockpi4.go @@ -6,8 +6,6 @@ package rockpi4 import ( - "fmt" - "log" "os" "path/filepath" @@ -20,11 +18,11 @@ import ( ) var ( - bin = fmt.Sprintf("/usr/install/arm64/u-boot/%s/u-boot-rockchip.bin", constants.BoardRockpi4) + bin = constants.BoardRockpi4 + "/u-boot-rockchip.bin" off int64 = 512 * 64 // https://github.com/u-boot/u-boot/blob/4de720e98d552dfda9278516bf788c4a73b3e56f/configs/rock-pi-4-rk3399_defconfig#L7= // 4a and 4b uses the same overlay. - dtb = "/dtb/rockchip/rk3399-rock-pi-4b.dtb" + dtb = "rockchip/rk3399-rock-pi-4b.dtb" ) // Rockpi4 represents the Radxa rock pi board. @@ -38,21 +36,21 @@ func (r *Rockpi4) Name() string { } // Install implements the runtime.Board. -func (r *Rockpi4) Install(disk string) (err error) { +func (r *Rockpi4) Install(options runtime.BoardInstallOptions) (err error) { var f *os.File - if f, err = os.OpenFile(disk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { + if f, err = os.OpenFile(options.InstallDisk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { return err } defer f.Close() //nolint:errcheck - uboot, err := os.ReadFile(bin) + uboot, err := os.ReadFile(filepath.Join(options.UBootPath, bin)) if err != nil { return err } - log.Printf("writing %s at offset %d", bin, off) + options.Printf("writing %s at offset %d", bin, off) var n int @@ -61,7 +59,7 @@ func (r *Rockpi4) Install(disk string) (err error) { return err } - log.Printf("wrote %d bytes", n) + options.Printf("wrote %d bytes", n) // NB: In the case that the block device is a loopback device, we sync here // to esure that the file is written before the loopback device is @@ -71,8 +69,8 @@ func (r *Rockpi4) Install(disk string) (err error) { return err } - src := "/usr/install/arm64" + dtb - dst := "/boot/EFI" + dtb + src := filepath.Join(options.DTBPath, dtb) + dst := filepath.Join("/boot/EFI/dtb", dtb) err = os.MkdirAll(filepath.Dir(dst), 0o600) if err != nil { diff --git a/internal/app/machined/pkg/runtime/v1alpha1/board/rockpi4c/rockpi4c.go b/internal/app/machined/pkg/runtime/v1alpha1/board/rockpi4c/rockpi4c.go index 1a1361620..7ade79e6f 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/board/rockpi4c/rockpi4c.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/board/rockpi4c/rockpi4c.go @@ -6,8 +6,6 @@ package rockpi4c import ( - "fmt" - "log" "os" "path/filepath" @@ -20,10 +18,10 @@ import ( ) var ( - bin = fmt.Sprintf("/usr/install/arm64/u-boot/%s/u-boot-rockchip.bin", constants.BoardRockpi4) + bin = constants.BoardRockpi4 + "/u-boot-rockchip.bin" off int64 = 512 * 64 // https://github.com/u-boot/u-boot/blob/4de720e98d552dfda9278516bf788c4a73b3e56f/configs/rock-pi-4c-rk3399_defconfig#L7= - dtb = "/dtb/rockchip/rk3399-rock-pi-4c.dtb" + dtb = "rockchip/rk3399-rock-pi-4c.dtb" ) // Rockpi4c represents the Radxa rock pi board. @@ -37,21 +35,21 @@ func (r *Rockpi4c) Name() string { } // Install implements the runtime.Board. -func (r *Rockpi4c) Install(disk string) (err error) { +func (r *Rockpi4c) Install(options runtime.BoardInstallOptions) (err error) { var f *os.File - if f, err = os.OpenFile(disk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { + if f, err = os.OpenFile(options.InstallDisk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil { return err } defer f.Close() //nolint:errcheck - uboot, err := os.ReadFile(bin) + uboot, err := os.ReadFile(filepath.Join(options.UBootPath, bin)) if err != nil { return err } - log.Printf("writing %s at offset %d", bin, off) + options.Printf("writing %s at offset %d", bin, off) var n int @@ -60,7 +58,7 @@ func (r *Rockpi4c) Install(disk string) (err error) { return err } - log.Printf("wrote %d bytes", n) + options.Printf("wrote %d bytes", n) // NB: In the case that the block device is a loopback device, we sync here // to esure that the file is written before the loopback device is @@ -70,8 +68,8 @@ func (r *Rockpi4c) Install(disk string) (err error) { return err } - src := "/usr/install/arm64" + dtb - dst := "/boot/EFI" + dtb + src := filepath.Join(options.DTBPath, dtb) + dst := filepath.Join("/boot/EFI/dtb", dtb) err = os.MkdirAll(filepath.Dir(dst), 0o600) if err != nil { diff --git a/internal/app/machined/pkg/runtime/v1alpha1/board/rpi_generic/rpi_generic.go b/internal/app/machined/pkg/runtime/v1alpha1/board/rpi_generic/rpi_generic.go index 107bd624b..4fba67364 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/board/rpi_generic/rpi_generic.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/board/rpi_generic/rpi_generic.go @@ -8,6 +8,7 @@ package rpigeneric import ( _ "embed" "os" + "path/filepath" "github.com/siderolabs/go-procfs/procfs" @@ -30,13 +31,13 @@ func (r *RPiGeneric) Name() string { } // Install implements the runtime.Board. -func (r *RPiGeneric) Install(disk string) (err error) { - err = copy.Dir("/usr/install/arm64/raspberrypi-firmware/boot", "/boot/EFI") +func (r *RPiGeneric) Install(options runtime.BoardInstallOptions) (err error) { + err = copy.Dir(filepath.Join(options.RPiFirmwarePath, "boot"), "/boot/EFI") if err != nil { return err } - err = copy.File("/usr/install/arm64/u-boot/rpi_generic/u-boot.bin", "/boot/EFI/u-boot.bin") + err = copy.File(filepath.Join(options.UBootPath, "rpi_generic/u-boot.bin"), "/boot/EFI/u-boot.bin") if err != nil { return err } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go index 8719161c7..4ff9719d8 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go @@ -35,13 +35,17 @@ type InstallOptions struct { Printf func(format string, v ...any) } -// BootAssets describes the assets to be installed by the booloader. +// BootAssets describes the assets to be installed by the bootloader. type BootAssets struct { KernelPath string InitramfsPath string UKIPath string SDBootPath string + + DTBPath string + UBootPath string + RPiFirmwarePath string } // FillDefaults fills in default paths to be used when in the context of the installer. @@ -61,4 +65,18 @@ func (assets *BootAssets) FillDefaults(arch string) { if assets.SDBootPath == "" { assets.SDBootPath = fmt.Sprintf(constants.SDBootAssetPath, arch) } + + if arch == "arm64" { + if assets.DTBPath == "" { + assets.DTBPath = fmt.Sprintf(constants.DTBAssetPath, arch) + } + + if assets.UBootPath == "" { + assets.UBootPath = fmt.Sprintf(constants.UBootAssetPath, arch) + } + + if assets.RPiFirmwarePath == "" { + assets.RPiFirmwarePath = fmt.Sprintf(constants.RPiFirmwareAssetPath, arch) + } + } } diff --git a/pkg/imager/filemap/filemap.go b/pkg/imager/filemap/filemap.go index 718a5d836..e90468aaf 100644 --- a/pkg/imager/filemap/filemap.go +++ b/pkg/imager/filemap/filemap.go @@ -10,6 +10,7 @@ import ( "bytes" "io" "os" + "path/filepath" "sort" v1 "github.com/google/go-containerregistry/pkg/v1" @@ -22,6 +23,35 @@ type File struct { SourcePath string } +// Walk the filesystem generating a filemap. +func Walk(sourceBasePath, imageBasePath string) ([]File, error) { + var filemap []File + + err := filepath.WalkDir(sourceBasePath, func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + + rel, err := filepath.Rel(sourceBasePath, path) + if err != nil { + return err + } + + filemap = append(filemap, File{ + ImagePath: filepath.Join(imageBasePath, rel), + SourcePath: path, + }) + + return nil + }) + + return filemap, err +} + // Layer creates a layer from a single file map. // // These layers are reproducible and consistent. diff --git a/pkg/imager/out.go b/pkg/imager/out.go index 64137a058..a35d5eca8 100644 --- a/pkg/imager/out.go +++ b/pkg/imager/out.go @@ -188,10 +188,13 @@ func (i *Imager) buildImage(ctx context.Context, path string, printf func(string ImageSecureboot: i.prof.SecureBootEnabled(), Version: i.prof.Version, BootAssets: options.BootAssets{ - KernelPath: i.prof.Input.Kernel.Path, - InitramfsPath: i.initramfsPath, - UKIPath: i.ukiPath, - SDBootPath: i.sdBootPath, + KernelPath: i.prof.Input.Kernel.Path, + InitramfsPath: i.initramfsPath, + UKIPath: i.ukiPath, + SDBootPath: i.sdBootPath, + DTBPath: i.prof.Input.DTB.Path, + UBootPath: i.prof.Input.UBoot.Path, + RPiFirmwarePath: i.prof.Input.RPiFirmware.Path, }, MountPrefix: scratchSpace, Printf: printf, @@ -282,6 +285,37 @@ func (i *Imager) outInstaller(ctx context.Context, path string, report *reporter ) } + for _, extraArtifact := range []struct { + sourcePath string + imagePath string + }{ + { + sourcePath: i.prof.Input.DTB.Path, + imagePath: strings.TrimLeft(fmt.Sprintf(constants.DTBAssetPath, i.prof.Arch), "/"), + }, + { + sourcePath: i.prof.Input.UBoot.Path, + imagePath: strings.TrimLeft(fmt.Sprintf(constants.UBootAssetPath, i.prof.Arch), "/"), + }, + { + sourcePath: i.prof.Input.RPiFirmware.Path, + imagePath: strings.TrimLeft(fmt.Sprintf(constants.RPiFirmwareAssetPath, i.prof.Arch), "/"), + }, + } { + if extraArtifact.sourcePath == "" { + continue + } + + var extraFiles []filemap.File + + extraFiles, err = filemap.Walk(extraArtifact.sourcePath, extraArtifact.imagePath) + if err != nil { + return fmt.Errorf("failed to walk extra artifact %s: %w", extraArtifact.sourcePath, err) + } + + artifacts = append(artifacts, extraFiles...) + } + artifactsLayer, err := filemap.Layer(artifacts) if err != nil { return fmt.Errorf("failed to create artifacts layer: %w", err) diff --git a/pkg/imager/profile/input.go b/pkg/imager/profile/input.go index 500a9670a..9ab968fb1 100644 --- a/pkg/imager/profile/input.go +++ b/pkg/imager/profile/input.go @@ -21,6 +21,11 @@ import ( "github.com/siderolabs/talos/pkg/machinery/constants" ) +const ( + arm64 = "arm64" + amd64 = "amd64" +) + // Input describes inputs for image generation. type Input struct { // Kernel is a vmlinuz file. @@ -31,6 +36,12 @@ type Input struct { SDStub FileAsset `yaml:"sdStub,omitempty"` // SDBoot is a sd-boot file (only for SecureBoot). SDBoot FileAsset `yaml:"sdBoot,omitempty"` + // DTB is a path to the device tree blobs (arm64 only). + DTB FileAsset `yaml:"dtb,omitempty"` + // UBoot is a path to the u-boot binary (arm64 only). + UBoot FileAsset `yaml:"uBoot,omitempty"` + // RPiFirmware is a path to the Raspberry Pi firmware (arm64 only). + RPiFirmware FileAsset `yaml:"rpiFirmware,omitempty"` // Base installer image to mutate. BaseInstaller ContainerAsset `yaml:"baseInstaller,omitempty"` // SecureBoot is a section with secureboot keys, only for SecureBoot enabled builds. @@ -79,7 +90,7 @@ const defaultSecureBootPrefix = "/secureboot" // FillDefaults fills default values for the input. // -//nolint:gocyclo +//nolint:gocyclo,cyclop func (i *Input) FillDefaults(arch, version string, secureboot bool) { var ( zeroFileAsset FileAsset @@ -94,6 +105,20 @@ func (i *Input) FillDefaults(arch, version string, secureboot bool) { i.Initramfs.Path = fmt.Sprintf(constants.InitramfsAssetPath, arch) } + if arch == arm64 { + if i.DTB == zeroFileAsset { + i.DTB.Path = fmt.Sprintf(constants.DTBAssetPath, arch) + } + + if i.UBoot == zeroFileAsset { + i.UBoot.Path = fmt.Sprintf(constants.UBootAssetPath, arch) + } + + if i.RPiFirmware == zeroFileAsset { + i.RPiFirmware.Path = fmt.Sprintf(constants.RPiFirmwareAssetPath, arch) + } + } + if i.BaseInstaller == zeroContainerAsset { i.BaseInstaller.ImageRef = fmt.Sprintf("%s:%s", images.DefaultInstallerImageRepository, version) } diff --git a/pkg/imager/profile/profile.go b/pkg/imager/profile/profile.go index 7d435b284..1fa2194b1 100644 --- a/pkg/imager/profile/profile.go +++ b/pkg/imager/profile/profile.go @@ -57,7 +57,7 @@ func (p *Profile) SecureBootEnabled() bool { // //nolint:gocyclo,cyclop func (p *Profile) Validate() error { - if p.Arch != "amd64" && p.Arch != "arm64" { + if p.Arch != amd64 && p.Arch != arm64 { return fmt.Errorf("invalid arch %q", p.Arch) } @@ -66,7 +66,7 @@ func (p *Profile) Validate() error { } if p.Board != "" { - if !(p.Arch == "arm64" && p.Platform == "metal") { + if !(p.Arch == arm64 && p.Platform == "metal") { return fmt.Errorf("board is only supported for metal arm64") } } diff --git a/pkg/machinery/constants/constants.go b/pkg/machinery/constants/constants.go index 36bf51cc4..2a3139223 100644 --- a/pkg/machinery/constants/constants.go +++ b/pkg/machinery/constants/constants.go @@ -529,6 +529,15 @@ const ( // SDBootAssetPath is the path to the SDBoot in the installer. SDBootAssetPath = "/usr/install/%s/" + SDBootAsset + // DTBAssetPath is the path to the device tree blobs in the installer. + DTBAssetPath = "/usr/install/%s/dtb" + + // UBootAssetPath is the path to the u-boot in the installer. + UBootAssetPath = "/usr/install/%s/u-boot" + + // RPiFirmwareAssetPath is the path to the raspberrypi firmware in the installer. + RPiFirmwareAssetPath = "/usr/install/%s/raspberrypi-firmware" + // PlatformKeyAsset defines a well known name for the platform key filename used for auto-enrolling. PlatformKeyAsset = "PK.auth"