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:
parent
f67795748b
commit
99aa3cdba5
@ -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
|
||||
|
2
Makefile
2
Makefile
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
87
pkg/copy/copy.go
Normal 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
|
||||
}
|
@ -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"
|
||||
|
71
website/content/docs/v0.8/Single Board Computers/rpi_4.md
Normal file
71
website/content/docs/v0.8/Single Board Computers/rpi_4.md
Normal 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 |
|
Loading…
x
Reference in New Issue
Block a user