github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/dt/fdt.go (about)

     1  // Copyright 2019 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package dt contains utilities for device tree.
     6  package dt
     7  
     8  import (
     9  	"bufio"
    10  	"bytes"
    11  	"encoding/binary"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"math"
    16  	"os"
    17  	"unsafe"
    18  
    19  	"github.com/mvdan/u-root-coreutils/pkg/align"
    20  	"github.com/mvdan/u-root-coreutils/pkg/uio"
    21  )
    22  
    23  const (
    24  	// Magic value seen in the FDT Header.
    25  	Magic uint32 = 0xd00dfeed
    26  
    27  	// MaxTotalSize is a limitation imposed by this implementation. This
    28  	// prevents the integers from wrapping around. Typically, the total size is
    29  	// a few megabytes, so this is not restrictive.
    30  	MaxTotalSize = 1024 * 1024 * 1024
    31  )
    32  
    33  type token uint32
    34  
    35  const (
    36  	tokenBeginNode token = 0x1
    37  	tokenEndNode   token = 0x2
    38  	tokenProp      token = 0x3
    39  	tokenNop       token = 0x4
    40  	tokenEnd       token = 0x9
    41  )
    42  
    43  // FDT contains the parsed contents of a Flattend Device Tree (.dtb).
    44  //
    45  // The format is relatively simple and defined in chapter 5 of the Devicetree
    46  // Specification Release 0.2.
    47  //
    48  // See: https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.2
    49  //
    50  // This package is compatible with version 16 and 17 of DTSpec.
    51  type FDT struct {
    52  	Header         Header
    53  	ReserveEntries []ReserveEntry
    54  	RootNode       *Node
    55  }
    56  
    57  // Header appears at offset 0.
    58  type Header struct {
    59  	Magic           uint32
    60  	TotalSize       uint32
    61  	OffDtStruct     uint32
    62  	OffDtStrings    uint32
    63  	OffMemRsvmap    uint32
    64  	Version         uint32
    65  	LastCompVersion uint32
    66  	BootCpuidPhys   uint32
    67  	SizeDtStrings   uint32
    68  	SizeDtStruct    uint32
    69  }
    70  
    71  // ReserveEntry defines a memory region which is reserved.
    72  type ReserveEntry struct {
    73  	Address uint64
    74  	Size    uint64
    75  }
    76  
    77  // ReadFDT reads an FDT from an io.ReadSeeker.
    78  func ReadFDT(f io.ReadSeeker) (*FDT, error) {
    79  	fdt := &FDT{}
    80  	if err := fdt.readHeader(f); err != nil {
    81  		return nil, err
    82  	}
    83  	if err := fdt.readMemoryReservationBlock(f); err != nil {
    84  		return nil, err
    85  	}
    86  	if err := fdt.checkLayout(); err != nil {
    87  		return nil, err
    88  	}
    89  	strs, err := fdt.readStringsBlock(f)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	if err := fdt.readStructBlock(f, strs); err != nil {
    94  		return nil, err
    95  	}
    96  	return fdt, nil
    97  }
    98  
    99  func (fdt *FDT) readHeader(f io.ReadSeeker) error {
   100  	h := &fdt.Header
   101  	if _, err := f.Seek(0, io.SeekStart); err != nil {
   102  		return err
   103  	}
   104  	if err := binary.Read(f, binary.BigEndian, h); err != nil {
   105  		return err
   106  	}
   107  	if h.Magic != Magic {
   108  		return fmt.Errorf("invalid FDT magic, got %#08x, expected %#08x",
   109  			h.Magic, Magic)
   110  	}
   111  	if !(h.Version == 16 || h.Version == 17 ||
   112  		(h.LastCompVersion <= 17 && h.Version > 17)) {
   113  		return fmt.Errorf(
   114  			"incompatible FDT version, must be compatible with 16/17,"+
   115  				"but version is %d last compatible with %d",
   116  			h.Version, h.LastCompVersion)
   117  	}
   118  	if h.TotalSize > MaxTotalSize {
   119  		return fmt.Errorf("FDT too large, %d > %d", h.TotalSize, MaxTotalSize)
   120  	}
   121  	return nil
   122  }
   123  
   124  func (fdt *FDT) readMemoryReservationBlock(f io.ReadSeeker) error {
   125  	if fdt.Header.OffMemRsvmap < uint32(unsafe.Sizeof(fdt.Header)) {
   126  		return fmt.Errorf("memory reservation block may not overlap header, %#x < %#x",
   127  			fdt.Header.OffMemRsvmap, unsafe.Sizeof(fdt.Header))
   128  	}
   129  	if fdt.Header.OffMemRsvmap%8 != 0 {
   130  		return fmt.Errorf(
   131  			"memory reservation offset must be aligned to 8 bytes, but is %#x",
   132  			fdt.Header.OffMemRsvmap)
   133  	}
   134  	if _, err := f.Seek(int64(fdt.Header.OffMemRsvmap), io.SeekStart); err != nil {
   135  		return err
   136  	}
   137  	fdt.ReserveEntries = []ReserveEntry{}
   138  	for {
   139  		entry := ReserveEntry{}
   140  		if err := binary.Read(f, binary.BigEndian, &entry); err != nil {
   141  			return err
   142  		}
   143  		if entry.Address == 0 && entry.Size == 0 {
   144  			break
   145  		}
   146  		fdt.ReserveEntries = append(fdt.ReserveEntries, entry)
   147  	}
   148  	return nil
   149  }
   150  
   151  // checkLayout returns any errors if any of the blocks overlap, are in the
   152  // wrong order or stray past the end of the file. This function must be called
   153  // after readHeader and readMemoryReservationBlock.
   154  func (fdt *FDT) checkLayout() error {
   155  	memRscEnd := fdt.Header.OffMemRsvmap +
   156  		uint32(len(fdt.ReserveEntries)+1)*uint32(unsafe.Sizeof(ReserveEntry{}))
   157  	if fdt.Header.OffDtStruct < memRscEnd {
   158  		return fmt.Errorf(
   159  			"struct block must not overlap memory reservation block, %#x < %#x",
   160  			fdt.Header.OffDtStruct, memRscEnd)
   161  	}
   162  	// TODO: there are more checks which should be done
   163  	return nil
   164  }
   165  
   166  func (fdt *FDT) readStringsBlock(f io.ReadSeeker) (strs []byte, err error) {
   167  	if _, err = f.Seek(int64(fdt.Header.OffDtStrings), io.SeekStart); err != nil {
   168  		return
   169  	}
   170  	strs = make([]byte, fdt.Header.SizeDtStrings)
   171  	_, err = f.Read(strs)
   172  	return
   173  }
   174  
   175  // readStructBlock reads the nodes and properties of the device and creates the
   176  // tree structure. strs contains the strings block.
   177  func (fdt *FDT) readStructBlock(f io.ReadSeeker, strs []byte) error {
   178  	if fdt.Header.OffDtStruct%4 != 0 {
   179  		return fmt.Errorf(
   180  			"struct offset must be aligned to 4 bytes, but is %#v",
   181  			fdt.Header.OffDtStruct)
   182  	}
   183  	if _, err := f.Seek(int64(fdt.Header.OffDtStruct), io.SeekStart); err != nil {
   184  		return err
   185  	}
   186  
   187  	// Buffer file so we don't perform a bajillion syscalls when looking for
   188  	// null-terminating characters.
   189  	r := &uio.AlignReader{
   190  		R: bufio.NewReader(
   191  			&io.LimitedReader{
   192  				R: f,
   193  				N: int64(fdt.Header.SizeDtStruct),
   194  			},
   195  		),
   196  	}
   197  
   198  	stack := []*Node{}
   199  	for {
   200  		var t token
   201  		if err := binary.Read(r, binary.BigEndian, &t); err != nil {
   202  			return err
   203  		}
   204  		switch t {
   205  		case tokenBeginNode:
   206  			// Push new node onto the stack.
   207  			child := &Node{}
   208  			stack = append(stack, child)
   209  			if len(stack) == 1 {
   210  				// Root node
   211  				if fdt.RootNode != nil {
   212  					return errors.New("invalid multiple root nodes")
   213  				}
   214  				fdt.RootNode = child
   215  			} else if len(stack) > 1 {
   216  				// Non-root node
   217  				parent := stack[len(stack)-2]
   218  				parent.Children = append(parent.Children, child)
   219  			}
   220  
   221  			// The name is a null-terminating string.
   222  			for {
   223  				if b, err := r.ReadByte(); err != nil {
   224  					return err
   225  				} else if b == 0 {
   226  					break
   227  				} else {
   228  					child.Name += string(b)
   229  				}
   230  			}
   231  
   232  		case tokenEndNode:
   233  			if len(stack) == 0 {
   234  				return errors.New(
   235  					"unbalanced FDT_BEGIN_NODE and FDT_END_NODE tokens")
   236  			}
   237  			stack = stack[:len(stack)-1]
   238  
   239  		case tokenProp:
   240  			pHeader := struct {
   241  				Len, Nameoff uint32
   242  			}{}
   243  			if err := binary.Read(r, binary.BigEndian, &pHeader); err != nil {
   244  				return err
   245  			}
   246  			if pHeader.Nameoff >= uint32(len(strs)) {
   247  				return fmt.Errorf(
   248  					"name offset is larger than strings block: %#x >= %#x",
   249  					pHeader.Nameoff, len(strs))
   250  			}
   251  			null := bytes.IndexByte(strs[pHeader.Nameoff:], 0)
   252  			if null == -1 {
   253  				return fmt.Errorf(
   254  					"property name does not having terminating null at %#x",
   255  					pHeader.Nameoff)
   256  			}
   257  			p := Property{
   258  				Name:  string(strs[pHeader.Nameoff : pHeader.Nameoff+uint32(null)]),
   259  				Value: make([]byte, pHeader.Len),
   260  			}
   261  			_, err := io.ReadFull(r, p.Value)
   262  			if err != nil {
   263  				return err
   264  			}
   265  			if len(stack) == 0 {
   266  				return fmt.Errorf("property %q appears outside a node", p.Name)
   267  			}
   268  			curNode := stack[len(stack)-1]
   269  			curNode.Properties = append(curNode.Properties, p)
   270  
   271  		case tokenNop:
   272  
   273  		case tokenEnd:
   274  			if uint32(r.N) < fdt.Header.SizeDtStruct {
   275  				return fmt.Errorf(
   276  					"extra data at end of structure block, %#x < %#x",
   277  					uint32(r.N), fdt.Header.SizeDtStruct)
   278  			}
   279  			if fdt.RootNode == nil {
   280  				return errors.New("no root node")
   281  			}
   282  			return nil
   283  
   284  		default:
   285  			return fmt.Errorf("undefined token %d", t)
   286  		}
   287  
   288  		// Align to four bytes.
   289  		// was: pad, err :=
   290  		_, err := r.Align(4)
   291  		if err != nil {
   292  			return err
   293  		}
   294  		/*for _, v := range pad {
   295  			if v != 0 {
   296  				// TODO: Some of the padding is not zero. Is this a mistake?
   297  				return fmt.Errorf("padding is non-zero: %d", v)
   298  			}
   299  		}*/
   300  	}
   301  }
   302  
   303  // Write marshals the FDT to an io.Writer and returns the size.
   304  func (fdt *FDT) Write(f io.Writer) (int, error) {
   305  	// Create string block and offset map.
   306  	strs := []byte{}
   307  	strOff := map[string]uint32{}
   308  	fdt.RootNode.Walk(func(n *Node) error {
   309  		for _, p := range n.Properties {
   310  			if _, ok := strOff[p.Name]; !ok { // deduplicate
   311  				strOff[p.Name] = uint32(len(strs))
   312  				strs = append(strs, []byte(p.Name)...)
   313  				strs = append(strs, 0)
   314  			}
   315  		}
   316  		return nil
   317  	})
   318  
   319  	// Calculate block sizes and offsets.
   320  	fdt.Header.SizeDtStrings = uint32(len(strs))
   321  	fdt.Header.SizeDtStruct = 4
   322  	fdt.RootNode.Walk(func(n *Node) error {
   323  		fdt.Header.SizeDtStruct += 8 + uint32(align.Up(uint(len(n.Name)+1), 4))
   324  		for _, p := range n.Properties {
   325  			fdt.Header.SizeDtStruct += 12 + uint32(align.Up(uint(len(p.Value)), 4))
   326  		}
   327  		return nil
   328  	})
   329  	fdt.Header.OffMemRsvmap = uint32(align.Up(uint(unsafe.Sizeof(fdt.Header)), 16))
   330  	fdt.Header.OffDtStruct = fdt.Header.OffMemRsvmap +
   331  		uint32(align.Up((uint(len(fdt.ReserveEntries)+1))*uint(unsafe.Sizeof(ReserveEntry{})), 4))
   332  	fdt.Header.OffDtStrings = fdt.Header.OffDtStruct + fdt.Header.SizeDtStruct
   333  	fdt.Header.TotalSize = fdt.Header.OffDtStrings + fdt.Header.SizeDtStrings
   334  
   335  	// Setup AlignWriter.
   336  	w := &uio.AlignWriter{W: f}
   337  
   338  	// Write header.
   339  	if err := binary.Write(w, binary.BigEndian, fdt.Header); err != nil {
   340  		return w.N, err
   341  	}
   342  
   343  	// Write memreserve block.
   344  	if err := w.Align(16, 0x00); err != nil {
   345  		return w.N, err
   346  	}
   347  	if err := binary.Write(w, binary.BigEndian, &fdt.ReserveEntries); err != nil {
   348  		return w.N, err
   349  	}
   350  	if err := binary.Write(w, binary.BigEndian, &ReserveEntry{}); err != nil {
   351  		return w.N, err
   352  	}
   353  
   354  	// Write struct block.
   355  	if err := w.Align(4, 0x00); err != nil {
   356  		return w.N, err
   357  	}
   358  	var writeNode func(n *Node) error
   359  	writeNode = func(n *Node) error {
   360  		if err := binary.Write(w, binary.BigEndian, tokenBeginNode); err != nil {
   361  			return err
   362  		}
   363  		if _, err := w.Write([]byte(n.Name + "\000")); err != nil {
   364  			return err
   365  		}
   366  		if err := w.Align(4, 0x00); err != nil {
   367  			return err
   368  		}
   369  		for _, p := range n.Properties {
   370  			property := struct {
   371  				Token        token
   372  				Len, Nameoff uint32
   373  			}{
   374  				tokenProp,
   375  				uint32(len(p.Value)),
   376  				strOff[p.Name],
   377  			}
   378  			if err := binary.Write(w, binary.BigEndian, &property); err != nil {
   379  				return err
   380  			}
   381  			if _, err := w.Write(p.Value); err != nil {
   382  				return err
   383  			}
   384  			if err := w.Align(4, 0x00); err != nil {
   385  				return err
   386  			}
   387  		}
   388  		for _, child := range n.Children {
   389  			if err := writeNode(child); err != nil {
   390  				return err
   391  			}
   392  		}
   393  		if err := binary.Write(w, binary.BigEndian, tokenEndNode); err != nil {
   394  			return err
   395  		}
   396  		return nil
   397  	}
   398  	if err := writeNode(fdt.RootNode); err != nil {
   399  		return w.N, err
   400  	}
   401  	if err := binary.Write(w, binary.BigEndian, tokenEnd); err != nil {
   402  		return w.N, err
   403  	}
   404  
   405  	// Write strings block
   406  	_, err := w.Write(strs)
   407  	return w.N, err
   408  }
   409  
   410  // NodeByName finds a node by name.
   411  func (fdt *FDT) NodeByName(name string) (*Node, bool) {
   412  	return fdt.RootNode.Find(func(n *Node) bool {
   413  		return n.Name == name
   414  	})
   415  }
   416  
   417  // ReadFile accepts a file name and returns an *FDT or error.
   418  func ReadFile(n string) (*FDT, error) {
   419  	f, err := os.Open(n)
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  
   424  	defer f.Close()
   425  	return ReadFDT(f)
   426  }
   427  
   428  // FDTReader is a function type with no args that returns
   429  // a *FDT or an error.
   430  type FDTReader func() (*FDT, error)
   431  
   432  // WithReaderAt constructs an FDTReader with the provided io.ReaderAt.
   433  func WithReaderAt(r io.ReaderAt) FDTReader {
   434  	return func() (*FDT, error) {
   435  		return ReadFDT(io.NewSectionReader(r, 0, math.MaxInt64))
   436  	}
   437  }
   438  
   439  // WithFileName constructs an FDTReader with the provided file name.
   440  func WithFileName(n string) FDTReader {
   441  	return func() (*FDT, error) {
   442  		return ReadFile(n)
   443  	}
   444  }
   445  
   446  // ErrNoValidReaders indicates that no readers succeeded.
   447  var ErrNoValidReaders = errors.New("No FDT readers succeeded")
   448  
   449  // New returns a new FDT, trying each FDTReader in turn
   450  // until it succeeds or all have failed. It will return
   451  // the last error.
   452  // TODO: once we move to go 1.20, use the new error tree
   453  // support.
   454  func New(readers ...FDTReader) (*FDT, error) {
   455  	for _, r := range readers {
   456  		f, err := r()
   457  		if err != nil {
   458  			continue
   459  		}
   460  		return f, nil
   461  	}
   462  	return nil, ErrNoValidReaders
   463  }