feat: add support for the Raspberry Pi 4 Model B

This adds support for the Raspberry Pi 4 Model B.

Signed-off-by: Andrew Rynhard <andrew@rynhard.io>
This commit is contained in:
Andrew Rynhard 2020-11-28 16:52:25 -08:00 committed by talos-bot
parent f67795748b
commit 99aa3cdba5
10 changed files with 265 additions and 42 deletions

View File

@ -30,6 +30,7 @@ FROM ghcr.io/talos-systems/util-linux:${PKGS} AS pkg-util-linux
FROM ghcr.io/talos-systems/kmod:${PKGS} AS pkg-kmod
FROM ghcr.io/talos-systems/kernel:${PKGS} AS pkg-kernel
FROM ghcr.io/talos-systems/u-boot:${PKGS} AS pkg-u-boot
FROM ghcr.io/talos-systems/raspberrypi-firmware:${PKGS} AS pkg-raspberrypi-firmware
# Resolve package images using ${EXTRAS} to be used later in COPY --from=.
@ -472,6 +473,7 @@ ARG TARGETARCH
COPY --from=kernel /vmlinuz-${TARGETARCH} /usr/install/vmlinuz
COPY --from=initramfs /initramfs-${TARGETARCH}.xz /usr/install/initramfs.xz
COPY --from=pkg-u-boot / /usr/install/u-boot
COPY --from=pkg-raspberrypi-firmware / /usr/install/raspberrypi-firmware
COPY --from=installer-build /installer /bin/installer
RUN ln -s /bin/installer /bin/talosctl
ARG TAG

View File

@ -9,7 +9,7 @@ DOCKER_LOGIN_ENABLED ?= true
ARTIFACTS := _out
TOOLS ?= ghcr.io/talos-systems/tools:v0.3.0-11-g84c834e
PKGS ?= v0.3.0-38-gaad8171
PKGS ?= v0.3.0-41-g58ce509
EXTRAS ?= v0.1.0-5-gcc2df81
GO_VERSION ?= 1.15
GOFUMPT_VERSION ?= abc0db2c416aca0f60ea33c23c76665f6e7ba0b6

View File

@ -6,9 +6,7 @@ package install
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/talos-systems/go-blockdevice/blockdevice/probe"
@ -246,42 +244,9 @@ func (i *Installer) Install(seq runtime.Sequence) (err error) {
return err
}
log.Printf("installing U-Boot for %q board", b.Name())
log.Printf("installing U-Boot for %q", b.Name())
var f *os.File
if f, err = os.OpenFile(i.options.Disk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil {
return err
}
// nolint: errcheck
defer f.Close()
bin, off := b.UBoot()
var uboot []byte
uboot, err = ioutil.ReadFile(bin)
if err != nil {
return err
}
log.Printf("writing %s to %s at offset %d", bin, i.options.Disk, off)
var n int
n, err = f.WriteAt(uboot, off)
if err != nil {
return err
}
log.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
// unmounted.
err = f.Sync()
if err != nil {
if err = b.Install(i.options.Disk); err != nil {
return err
}
}

View File

@ -13,6 +13,6 @@ type PartitionOptions struct {
// Board defines the requirements for a SBC.
type Board interface {
Name() string
UBoot() (string, int64)
Install(string) error
PartitionOptions() *PartitionOptions
}

View File

@ -13,6 +13,7 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
libretechallh3cch5 "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/board/libretech_all_h3_cc_h5"
rpi4 "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/board/rpi_4"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
@ -46,6 +47,8 @@ func newBoard(board string) (b runtime.Board, err error) {
switch board {
case constants.BoardLibretechAllH3CCH5:
b = &libretechallh3cch5.LibretechAllH3CCH5{}
case constants.BoardRPi4:
b = &rpi4.RPi4{}
default:
return nil, fmt.Errorf("unsupported board: %q", board)
}

View File

@ -6,11 +6,21 @@ package libretechallh3cch5
import (
"fmt"
"io/ioutil"
"log"
"os"
"golang.org/x/sys/unix"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
var (
bin = fmt.Sprintf("/usr/install/u-boot/%s/u-boot-sunxi-with-spl.bin", constants.BoardLibretechAllH3CCH5)
off int64 = 1024 * 8
)
// LibretechAllH3CCH5 represents the Libre Computer ALL-H3-CC (Tritium).
//
// Reference: https://libre.computer/products/boards/all-h3-cc/
@ -21,9 +31,43 @@ func (l *LibretechAllH3CCH5) Name() string {
return constants.BoardLibretechAllH3CCH5
}
// UBoot implenents the runtime.Board.
func (l *LibretechAllH3CCH5) UBoot() (string, int64) {
return fmt.Sprintf("/usr/install/u-boot/%s/u-boot-sunxi-with-spl.bin", constants.BoardLibretechAllH3CCH5), 1024 * 8
// Install implenents the runtime.Board.
func (l *LibretechAllH3CCH5) Install(disk string) (err error) {
var f *os.File
if f, err = os.OpenFile(disk, os.O_RDWR|unix.O_CLOEXEC, 0o666); err != nil {
return err
}
// nolint: errcheck
defer f.Close()
var uboot []byte
uboot, err = ioutil.ReadFile(bin)
if err != nil {
return err
}
log.Printf("writing %s at offset %d", bin, off)
var n int
n, err = f.WriteAt(uboot, off)
if err != nil {
return err
}
log.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
// unmounted.
err = f.Sync()
if err != nil {
return err
}
return nil
}
// PartitionOptions implenents the runtime.Board.

View File

@ -0,0 +1,48 @@
// 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 rpi4
import (
"io/ioutil"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/pkg/copy"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
var configTxt = []byte(`arm_64bit=1
enable_uart=1
kernel=u-boot.bin
`)
// RPi4 represents the Raspberry Pi 4 Model B.
//
// Reference: https://www.raspberrypi.org/products/raspberry-pi-4-model-b/
type RPi4 struct{}
// Name implements the runtime.Board.
func (l *RPi4) Name() string {
return constants.BoardRPi4
}
// Install implements the runtime.Board.
func (l *RPi4) Install(disk string) (err error) {
err = copy.Dir("/usr/install/raspberrypi-firmware/boot", "/boot/EFI")
if err != nil {
return err
}
err = copy.File("/usr/install/u-boot/rpi_4/u-boot.bin", "/boot/EFI/u-boot.bin")
if err != nil {
return err
}
return ioutil.WriteFile("/boot/EFI/config.txt", configTxt, 0o600)
}
// PartitionOptions implements the runtime.Board.
func (l *RPi4) PartitionOptions() *runtime.PartitionOptions {
return nil
}

87
pkg/copy/copy.go Normal file
View File

@ -0,0 +1,87 @@
// 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 copy
import (
"io"
"io/ioutil"
"os"
"path"
)
// File copies the `src` file to the `dst` file.
func File(src, dst string) error {
var (
err error
s *os.File
d *os.File
info os.FileInfo
)
if s, err = os.Open(src); err != nil {
return err
}
// nolint: errcheck
defer s.Close()
if d, err = os.Create(dst); err != nil {
return err
}
// nolint: errcheck
defer d.Close()
// nolint: errcheck
defer d.Sync()
if _, err = io.Copy(d, s); err != nil {
return err
}
if info, err = os.Stat(src); err != nil {
return err
}
return os.Chmod(dst, info.Mode())
}
// Dir copies the `src` directory to the `dst` directory.
func Dir(src, dst string) error {
var (
err error
files []os.FileInfo
info os.FileInfo
)
if info, err = os.Stat(src); err != nil {
return err
}
if err = os.MkdirAll(dst, info.Mode()); err != nil {
return err
}
if files, err = ioutil.ReadDir(src); err != nil {
return err
}
for _, file := range files {
s := path.Join(src, file.Name())
d := path.Join(dst, file.Name())
if file.IsDir() {
if err = Dir(s, d); err != nil {
return err
}
} else {
if err = File(s, d); err != nil {
return err
}
}
}
return nil
}

View File

@ -37,6 +37,9 @@ const (
// BoardLibretechAllH3CCH5 is the name of the Libre Computer board ALL-H3-CC.
BoardLibretechAllH3CCH5 = "libretech_all_h3_cc_h5"
// BoardRPi4 is the name of the Raspberry Pi 4 Model B.
BoardRPi4 = "rpi_4"
// KernelParamHostname is the kernel parameter name for specifying the
// hostname.
KernelParamHostname = "talos.hostname"

View File

@ -0,0 +1,71 @@
---
title: "Raspberry Pi 4 Model B"
---
## Updating the Bootloader
A recent version of the `rpi-eeprom` is required.
To update the bootloader we will need an SD card.
Insert the SD card into your computer and run the following:
```bash
curl -LO https://github.com/raspberrypi/rpi-eeprom/releases/download/v2020.09.03-138a1/rpi-boot-eeprom-recovery-2020-09-03-vl805-000138a1.zip
sudo mkfs.fat -I /dev/mmcblk0
sudo mount /dev/mmcblk0 /mnt
sudo bsdtar rpi-boot-eeprom-recovery-2020-09-03-vl805-000138a1.zip -C /mnt
```
Insert the SD card into the Raspberry Pi, power it on, and wait at least 10 seconds.
If successful, the green LED light will blink rapidly (forever), otherwise an error pattern will be displayed.
If a HDMI display is attached then screen will display green for success or red if failure a failure occurs.
Power off the Raspberry Pi and remove the SD card.
## Generating the Image
Using the Talos installer container, we can generate an image for the Raspberry Pi 4 by running:
```bash
docker run \
--rm \
-v /dev:/dev \
--privileged \
ghcr.io/talos-systems/installer:latest image --platform metal --board rpi_4 --tar-to-stdout | tar xz
```
## Writing the Image
Once the image generation is done, extract the raw disk and `dd` it your SD card (be sure to update `x` in `mmcblkx`):
```bash
tar -xvf metal-rpi_4-arm64.tar.gz
sudo dd if=disk.raw of=/dev/mmcblkx
```
## Bootstrapping the Node
Insert the SD card, turn on the board, and wait for the console to show you the instructions for bootstrapping the node.
Following the instructions in the console output, generate the configuration files and apply the `init.yaml`:
```bash
talosctl gen config rpi4 https://<node IP or DNS name>:6443
talosctl apply-config --insecure --file init.yaml --nodes <node IP or DNS name>
```
The following table can be used to troubleshoot booting issues:
| Long Flashes | Short Flashes | Status |
| ------------ | :-----------: | ----------------------------------: |
| 0 | 3 | Generic failure to boot |
| 0 | 4 | start\*.elf not found |
| 0 | 7 | Kernel image not found |
| 0 | 8 | SDRAM failure |
| 0 | 9 | Insufficient SDRAM |
| 0 | 10 | In HALT state |
| 2 | 1 | Partition not FAT |
| 2 | 2 | Failed to read from partition |
| 2 | 3 | Extended partition not FAT |
| 2 | 4 | File signature/hash mismatch - Pi 4 |
| 4 | 4 | Unsupported board type |
| 4 | 5 | Fatal firmware error |
| 4 | 6 | Power failure type A |
| 4 | 7 | Power failure type B |