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