Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 51 additions & 5 deletions pkg/dt/fdt.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"errors"
"fmt"
"io"
"math"
"os"
"unsafe"

"github.com/u-root/u-root/pkg/align"
Expand All @@ -38,10 +40,6 @@ const (
tokenEnd token = 0x9
)

var (
errLoadFDT = errors.New("load fdt failed after trying all sources")
)

// FDT contains the parsed contents of a Flattend Device Tree (.dtb).
//
// The format is relatively simple and defined in chapter 5 of the Devicetree
Expand Down Expand Up @@ -76,7 +74,7 @@ type ReserveEntry struct {
Size uint64
}

// ReadFDT reads FDT from an io.ReadSeeker.
// ReadFDT reads an FDT from an io.ReadSeeker.
func ReadFDT(f io.ReadSeeker) (*FDT, error) {
fdt := &FDT{}
if err := fdt.readHeader(f); err != nil {
Expand Down Expand Up @@ -415,3 +413,51 @@ func (fdt *FDT) NodeByName(name string) (*Node, bool) {
return n.Name == name
})
}

// ReadFile accepts a file name and returns an *FDT or error.
func ReadFile(n string) (*FDT, error) {
f, err := os.Open(n)
if err != nil {
return nil, err
}

defer f.Close()
return ReadFDT(f)
}

// FDTReader is a function type with no args that returns
// a *FDT or an error.
type FDTReader func() (*FDT, error)

// WithReaderAt constructs an FDTReader with the provided io.ReaderAt.
func WithReaderAt(r io.ReaderAt) FDTReader {
return func() (*FDT, error) {
return ReadFDT(io.NewSectionReader(r, 0, math.MaxInt64))
}
}

// WithFileName constructs an FDTReader with the provided file name.
func WithFileName(n string) FDTReader {
return func() (*FDT, error) {
return ReadFile(n)
}
}

// ErrNoValidReaders indicates that no readers succeeded.
var ErrNoValidReaders = errors.New("No FDT readers succeeded")

// New returns a new FDT, trying each FDTReader in turn
// until it succeeds or all have failed. It will return
// the last error.
// TODO: once we move to go 1.20, use the new error tree
// support.
func New(readers ...FDTReader) (*FDT, error) {
for _, r := range readers {
f, err := r()
if err != nil {
continue
}
return f, nil
}
return nil, ErrNoValidReaders
}
38 changes: 17 additions & 21 deletions pkg/dt/fdt_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,31 @@ package dt

import (
"io"
"math"
"os"
)

var (
sysfsFDT = "/sys/firmware/fdt"
)
const sysfsFDT = "/sys/firmware/fdt"

// LoadFDT loads a flattened device tree from current running system.
//
// It first tries to load it from given io.ReaderAt, then from
// /sys/firmware/fdt.
func LoadFDT(dtb io.ReaderAt) (*FDT, error) {
// that passed-in file name. If there are not passed-in file names,
// it will try sysfsFDT.
//
// BUGS:
// It is a bit clunky due to its origins; in the original version it
// even had a race. hopefully we can deprecate it in a future u-root
// release.
func LoadFDT(dtb io.ReaderAt, names ...string) (*FDT, error) {
f := []FDTReader{}
if dtb != nil {
fdt, err := ReadFDT(io.NewSectionReader(dtb, 0, math.MaxInt64))
if err == nil {
return fdt, nil
}
f = append(f, WithReaderAt(dtb))
}

// Fallback to load fdt from sysfs.
sysFDTFile, err := os.Open(sysfsFDT)
if err == nil {
defer sysFDTFile.Close()
fdt, err := ReadFDT(sysFDTFile)
if err == nil {
return fdt, nil
if len(names) == 0 {
f = append(f, WithFileName(sysfsFDT))
} else {
for _, n := range names {
f = append(f, WithFileName(n))
}
}

return nil, errLoadFDT
return New(f...)
}
45 changes: 21 additions & 24 deletions pkg/dt/fdt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ package dt
import (
"bytes"
"encoding/json"
"fmt"
"errors"
"os"
"os/exec"
"path/filepath"
"reflect"
"strings"
"testing"
Expand Down Expand Up @@ -42,20 +43,15 @@ func TestLoadFDT(t *testing.T) {
t.Errorf(`Read("fdt.dtb") = %s \n, want %s`, got, jsonData)
}

nonexistDTB := fmt.Sprintf("testdata/fdt_2022.dtb")
if _, err := os.Stat(nonexistDTB); err == nil {
t.Fatalf("test file should not exist: %s", nonexistDTB)
}
dir := t.TempDir()
nonexistDTB := filepath.Join(dir, "xxx")
// 2. Fallback to read from sys fs, and sys fs reading also failed.
sysfsFDT = nonexistDTB
_, err = LoadFDT(nil)
if err != errLoadFDT {
t.Errorf("LoadFDT(%s) return error %v, want error %v", nonexistDTB, err, errLoadFDT)
if _, err = LoadFDT(nil, nonexistDTB); !errors.Is(err, ErrNoValidReaders) {
t.Errorf("LoadFDT(%s) got %v, want %v", nonexistDTB, err, ErrNoValidReaders)
}

// 3. Fallback to read from sys fs, and succeed.
sysfsFDT = "testdata/fdt.dtb"
fdt, err = LoadFDT(nil)
fdt, err = LoadFDT(nil, "testdata/fdt.dtb")
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -106,16 +102,13 @@ func TestParity(t *testing.T) {
t.Skip()

// Read and write the fdt.
f, err := os.Open("testdata/fdt.dtb")
if err != nil {
t.Fatal(err)
}
fdt, err := ReadFDT(f)
f.Close()
fdt, err := New(WithFileName("testdata/fdt.dtb"))
if err != nil {
t.Fatal(err)
}
f, err = os.Create("testdata/fdt2.dtb")
dir := t.TempDir()
dtb := filepath.Join(dir, "fdt2.dtb")
f, err := os.Create(dtb)
if err != nil {
t.Fatal(err)
}
Expand All @@ -126,22 +119,26 @@ func TestParity(t *testing.T) {
}

// Run your system's fdtdump command.
f, err = os.Create("testdata/fdt2.dts")
dts := filepath.Join(dir, "fdt2.dts")
f, err = os.Create(dts)
if err != nil {
t.Fatal(err)
}
cmd := exec.Command("fdtdump", "testdata/fdt2.dtb")
cmd := exec.Command("fdtdump", dtb)
cmd.Stdout = f
err = cmd.Run()
f.Close()
if err != nil {
t.Fatal(err) // TODO: skip if system does not have fdtdump
}

cmd = exec.Command("diff", "testdata/fdt.dts", "testdata/fdt2.dts")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
// This used to run diff, has to be done better. It's not even working now so.
if false {
cmd = exec.Command("diff", "testdata/fdt.dts", "testdata/fdt2.dts")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
}
}

func TestFindNode(t *testing.T) {
Expand Down