github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/dt/node.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
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"errors"
    11  	"fmt"
    12  	"unicode"
    13  )
    14  
    15  // Empty represents an empty Device Tree value.
    16  type Empty struct{}
    17  
    18  // PHandle represents a pointer to another Node.
    19  type PHandle uint32
    20  
    21  // PropertyType is an enum of possible property types.
    22  type PropertyType int
    23  
    24  // These are the possible values for PropertyType.
    25  const (
    26  	EmptyType PropertyType = iota
    27  	U32Type
    28  	U64Type
    29  	StringType
    30  	PropEncodedArrayType
    31  	PHandleType
    32  	StringListType
    33  )
    34  
    35  // StandardPropertyTypes maps properties to values as defined by the spec.
    36  var StandardPropertyTypes = map[string]PropertyType{
    37  	"compatible":     StringListType,
    38  	"model":          StringType,
    39  	"phandle":        PHandleType,
    40  	"status":         StringType,
    41  	"#address-cells": U32Type,
    42  	"#size-cells":    U32Type,
    43  	"reg":            PropEncodedArrayType, // TODO: support cells
    44  	"virtual-reg":    U32Type,
    45  	"ranges":         PropEncodedArrayType, // TODO: or EmptyType
    46  	"dma-ranges":     PropEncodedArrayType, // TODO: or EmptyType
    47  	"name":           StringType,           // deprecated
    48  	"device_tree":    StringType,           // deprecated
    49  }
    50  
    51  var (
    52  	errPropertyRegionInvalid = errors.New("property value is not <u64x2>")
    53  )
    54  
    55  // Node is one Node in the Device Tree.
    56  type Node struct {
    57  	Name       string
    58  	Properties []Property `json:",omitempty"`
    59  	Children   []*Node    `json:",omitempty"`
    60  }
    61  
    62  // Walk calls f on a Node and alls its descendents.
    63  func (n *Node) Walk(f func(*Node) error) error {
    64  	if err := f(n); err != nil {
    65  		return err
    66  	}
    67  	for idx := range n.Children {
    68  		if err := n.Children[idx].Walk(f); err != nil {
    69  			return err
    70  		}
    71  	}
    72  	return nil
    73  }
    74  
    75  // Find finds a Node starting at a node, given a matching function.
    76  func (n *Node) Find(f func(*Node) bool) (*Node, bool) {
    77  	if ok := f(n); ok {
    78  		return n, ok
    79  	}
    80  	for idx := range n.Children {
    81  		if nn, ok := n.Children[idx].Find(f); ok {
    82  			return nn, ok
    83  		}
    84  	}
    85  	return nil, false
    86  }
    87  
    88  // FindAll returns all Node starting at a node, given a matching function.
    89  func (n *Node) FindAll(f func(*Node) bool) ([]*Node, bool) {
    90  	var nodes []*Node
    91  	if ok := f(n); ok {
    92  		nodes = append(nodes, n)
    93  	}
    94  
    95  	for idx := range n.Children {
    96  		if matching, ok := n.Children[idx].FindAll(f); ok {
    97  			nodes = append(nodes, matching...)
    98  		}
    99  	}
   100  	if len(nodes) == 0 {
   101  		return nil, false
   102  	}
   103  	return nodes, true
   104  }
   105  
   106  // NodeByName uses Find to find a node by name.
   107  func (n *Node) NodeByName(name string) (*Node, bool) {
   108  	return n.Find(func(n *Node) bool {
   109  		return n.Name == name
   110  	})
   111  }
   112  
   113  // LookProperty finds a property by name.
   114  func (n *Node) LookProperty(name string) (*Property, bool) {
   115  	for idx := range n.Properties {
   116  		if n.Properties[idx].Name == name {
   117  			return &n.Properties[idx], true
   118  		}
   119  	}
   120  	return nil, false
   121  }
   122  
   123  // RemoveProperty deletes a property by name.
   124  func (n *Node) RemoveProperty(name string) bool {
   125  	for idx := range n.Properties {
   126  		if n.Properties[idx].Name == name {
   127  			lastIdx := len(n.Properties) - 1
   128  			if idx != lastIdx {
   129  				n.Properties[idx] = n.Properties[lastIdx]
   130  			}
   131  			n.Properties = n.Properties[:lastIdx]
   132  			return true
   133  		}
   134  	}
   135  	return false
   136  }
   137  
   138  // UpdateProperty updates a property in the node, adding it if it does not exist.
   139  //
   140  // Returning boolean to indicate if the property was found.
   141  func (n *Node) UpdateProperty(name string, value []byte) bool {
   142  	p, found := n.LookProperty(name)
   143  	if found {
   144  		p.Value = value
   145  		return true
   146  	}
   147  
   148  	prop := Property{Name: name, Value: value}
   149  	n.Properties = append(n.Properties, prop)
   150  	return false
   151  }
   152  
   153  // Property is a name-value pair. Note the PropertyType of Value is not
   154  // encoded.
   155  type Property struct {
   156  	Name  string
   157  	Value []byte
   158  }
   159  
   160  // PredictType makes a prediction on what value the property contains based on
   161  // its name and data. The data types are not encoded in the data structure, so
   162  // some heuristics are used.
   163  func (p *Property) PredictType() PropertyType {
   164  	// Standard properties
   165  	if value, ok := StandardPropertyTypes[p.Name]; ok {
   166  		if _, err := p.AsType(value); err == nil {
   167  			return value
   168  		}
   169  	}
   170  
   171  	// Heuristic match
   172  	if _, err := p.AsEmpty(); err == nil {
   173  		return EmptyType
   174  	}
   175  	if _, err := p.AsString(); err == nil {
   176  		return StringType
   177  	}
   178  	if _, err := p.AsStringList(); err == nil {
   179  		return StringListType
   180  	}
   181  	if _, err := p.AsU32(); err == nil {
   182  		return U32Type
   183  	}
   184  	if _, err := p.AsU64(); err == nil {
   185  		return U64Type
   186  	}
   187  	return PropEncodedArrayType
   188  }
   189  
   190  // AsType converts a Property to a Go type using one of the AsXYX() functions.
   191  // The resulting Go type is as follows:
   192  //
   193  //	AsType(fdt.EmptyType)            -> fdt.Empty
   194  //	AsType(fdt.U32Type)              -> uint32
   195  //	AsType(fdt.U64Type)              -> uint64
   196  //	AsType(fdt.StringType)           -> string
   197  //	AsType(fdt.PropEncodedArrayType) -> []byte
   198  //	AsType(fdt.PHandleType)          -> fdt.PHandle
   199  //	AsType(fdt.StringListType)       -> []string
   200  func (p *Property) AsType(val PropertyType) (interface{}, error) {
   201  	switch val {
   202  	case EmptyType:
   203  		return p.AsEmpty()
   204  	case U32Type:
   205  		return p.AsU32()
   206  	case U64Type:
   207  		return p.AsU64()
   208  	case StringType:
   209  		return p.AsString()
   210  	case PropEncodedArrayType:
   211  		return p.AsPropEncodedArray()
   212  	case PHandleType:
   213  		return p.AsPHandle()
   214  	case StringListType:
   215  		return p.AsStringList()
   216  	}
   217  	return nil, fmt.Errorf("%d not in the PropertyType enum", val)
   218  }
   219  
   220  // AsEmpty converts the property to the Go fdt.Empty type.
   221  func (p *Property) AsEmpty() (Empty, error) {
   222  	if len(p.Value) != 0 {
   223  		return Empty{}, fmt.Errorf("property %q is not <empty>", p.Name)
   224  	}
   225  	return Empty{}, nil
   226  }
   227  
   228  // AsU32 converts the property to the Go uint32 type.
   229  func (p *Property) AsU32() (uint32, error) {
   230  	if len(p.Value) != 4 {
   231  		return 0, fmt.Errorf("property %q is not <u32>", p.Name)
   232  	}
   233  	var val uint32
   234  	err := binary.Read(bytes.NewBuffer(p.Value), binary.BigEndian, &val)
   235  	return val, err
   236  }
   237  
   238  // AsU64 converts the property to the Go uint64 type.
   239  func (p *Property) AsU64() (uint64, error) {
   240  	if len(p.Value) != 8 {
   241  		return 0, fmt.Errorf("property %q is not <u64>", p.Name)
   242  	}
   243  	var val uint64
   244  	err := binary.Read(bytes.NewBuffer(p.Value), binary.BigEndian, &val)
   245  	return val, err
   246  }
   247  
   248  // Region represents a memory range.
   249  type Region struct {
   250  	Start uint64
   251  	Size  uint64
   252  }
   253  
   254  // AsRegion converts the property to a Region.
   255  func (p *Property) AsRegion() (*Region, error) {
   256  	if len(p.Value) != 16 {
   257  		return nil, errPropertyRegionInvalid
   258  	}
   259  	var start, size uint64
   260  	b := bytes.NewBuffer(p.Value)
   261  
   262  	err := binary.Read(b, binary.BigEndian, &start)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	err = binary.Read(b, binary.BigEndian, &size)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  	return &Region{Start: start, Size: size}, nil
   271  }
   272  
   273  // AsString converts the property to the Go string type. The trailing null
   274  // character is stripped.
   275  func (p *Property) AsString() (string, error) {
   276  	if len(p.Value) == 0 || p.Value[len(p.Value)-1] != 0 {
   277  		return "", fmt.Errorf("property %q is not <string> (0 length or no null)", p.Name)
   278  	}
   279  	str := p.Value[:len(p.Value)-1]
   280  	if !isPrintableASCII(str) {
   281  		return "", fmt.Errorf("property %q is not <string>", p.Name)
   282  	}
   283  	return string(str), nil
   284  }
   285  
   286  // AsPropEncodedArray converts the property to the Go []byte type.
   287  func (p *Property) AsPropEncodedArray() ([]byte, error) {
   288  	return p.Value, nil
   289  }
   290  
   291  // AsPHandle converts the property to the Go fdt.PHandle type.
   292  func (p *Property) AsPHandle() (PHandle, error) {
   293  	val, err := p.AsU32()
   294  	return PHandle(val), err
   295  }
   296  
   297  // AsStringList converts the property to the Go []string type. The trailing
   298  // null character of each string is stripped.
   299  func (p *Property) AsStringList() ([]string, error) {
   300  	if len(p.Value) == 0 || p.Value[len(p.Value)-1] != 0 {
   301  		return nil, fmt.Errorf("property %q is not <stringlist>", p.Name)
   302  	}
   303  	value := p.Value
   304  	strs := []string{}
   305  	for len(p.Value) > 0 {
   306  		nextNull := bytes.IndexByte(value, 0) // cannot be -1
   307  		var str []byte
   308  		str, value = value[:nextNull], value[nextNull+1:]
   309  		if !isPrintableASCII(str) {
   310  			return nil, fmt.Errorf("property %q is not <stringlist>", p.Name)
   311  		}
   312  		strs = append(strs, string(str))
   313  	}
   314  	return strs, nil
   315  }
   316  
   317  func isPrintableASCII(s []byte) bool {
   318  	for _, v := range s {
   319  		if v > unicode.MaxASCII || !unicode.IsPrint(rune(v)) {
   320  			return false
   321  		}
   322  	}
   323  	return true
   324  }