github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/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  	"fmt"
    11  	"unicode"
    12  )
    13  
    14  // Empty represents an empty Device Tree value.
    15  type Empty struct{}
    16  
    17  // PHandle represents a pointer to another Node.
    18  type PHandle uint32
    19  
    20  // PropertyType is an enum of possible property types.
    21  type PropertyType int
    22  
    23  // These are the possible values for PropertyType.
    24  const (
    25  	EmptyType PropertyType = iota
    26  	U32Type
    27  	U64Type
    28  	StringType
    29  	PropEncodedArrayType
    30  	PHandleType
    31  	StringListType
    32  )
    33  
    34  // StandardPropertyTypes maps properties to values as defined by the spec.
    35  var StandardPropertyTypes = map[string]PropertyType{
    36  	"compatible":     StringListType,
    37  	"model":          StringType,
    38  	"phandle":        PHandleType,
    39  	"status":         StringType,
    40  	"#address-cells": U32Type,
    41  	"#size-cells":    U32Type,
    42  	"reg":            PropEncodedArrayType, // TODO: support cells
    43  	"virtual-reg":    U32Type,
    44  	"ranges":         PropEncodedArrayType, // TODO: or EmptyType
    45  	"dma-ranges":     PropEncodedArrayType, // TODO: or EmptyType
    46  	"name":           StringType,           // deprecated
    47  	"device_tree":    StringType,           // deprecated
    48  }
    49  
    50  // Node is one Node in the Device Tree.
    51  type Node struct {
    52  	Name       string
    53  	Properties []Property `json:",omitempty"`
    54  	Children   []*Node    `json:",omitempty"`
    55  }
    56  
    57  // Walk calls f on a Node and alls its descendents.
    58  func (n *Node) Walk(f func(*Node) error) error {
    59  	if err := f(n); err != nil {
    60  		return err
    61  	}
    62  	for _, child := range n.Children {
    63  		if err := child.Walk(f); err != nil {
    64  			return err
    65  		}
    66  	}
    67  	return nil
    68  }
    69  
    70  // Property is a name-value pair. Note the PropertyType of Value is not
    71  // encoded.
    72  type Property struct {
    73  	Name  string
    74  	Value []byte
    75  }
    76  
    77  // PredictType makes a prediction on what value the property contains based on
    78  // its name and data. The data types are not encoded in the data structure, so
    79  // some heuristics are used.
    80  func (p *Property) PredictType() PropertyType {
    81  	// Standard properties
    82  	if value, ok := StandardPropertyTypes[p.Name]; ok {
    83  		if _, err := p.AsType(value); err == nil {
    84  			return value
    85  		}
    86  	}
    87  
    88  	// Heuristic match
    89  	if _, err := p.AsEmpty(); err == nil {
    90  		return EmptyType
    91  	}
    92  	if _, err := p.AsString(); err == nil {
    93  		return StringType
    94  	}
    95  	if _, err := p.AsStringList(); err == nil {
    96  		return StringListType
    97  	}
    98  	if _, err := p.AsU32(); err == nil {
    99  		return U32Type
   100  	}
   101  	if _, err := p.AsU64(); err == nil {
   102  		return U64Type
   103  	}
   104  	return PropEncodedArrayType
   105  }
   106  
   107  // AsType converts a Property to a Go type using one of the AsXYX() functions.
   108  // The resulting Go type is as follows:
   109  //
   110  //     AsType(fdt.EmptyType)            -> fdt.Empty
   111  //     AsType(fdt.U32Type)              -> uint32
   112  //     AsType(fdt.U64Type)              -> uint64
   113  //     AsType(fdt.StringType)           -> string
   114  //     AsType(fdt.PropEncodedArrayType) -> []byte
   115  //     AsType(fdt.PHandleType)          -> fdt.PHandle
   116  //     AsType(fdt.StringListType)       -> []string
   117  func (p *Property) AsType(val PropertyType) (interface{}, error) {
   118  	switch val {
   119  	case EmptyType:
   120  		return p.AsEmpty()
   121  	case U32Type:
   122  		return p.AsU32()
   123  	case U64Type:
   124  		return p.AsU64()
   125  	case StringType:
   126  		return p.AsString()
   127  	case PropEncodedArrayType:
   128  		return p.AsPropEncodedArray()
   129  	case PHandleType:
   130  		return p.AsPHandle()
   131  	case StringListType:
   132  		return p.AsStringList()
   133  	}
   134  	return nil, fmt.Errorf("%d not in the PropertyType enum", val)
   135  }
   136  
   137  // AsEmpty converts the property to the Go fdt.Empty type.
   138  func (p *Property) AsEmpty() (Empty, error) {
   139  	if len(p.Value) != 0 {
   140  		return Empty{}, fmt.Errorf("property %q is not <empty>", p.Name)
   141  	}
   142  	return Empty{}, nil
   143  }
   144  
   145  // AsU32 converts the property to the Go uint32 type.
   146  func (p *Property) AsU32() (uint32, error) {
   147  	if len(p.Value) != 4 {
   148  		return 0, fmt.Errorf("property %q is not <u32>", p.Name)
   149  	}
   150  	var val uint32
   151  	err := binary.Read(bytes.NewBuffer(p.Value), binary.BigEndian, &val)
   152  	return val, err
   153  }
   154  
   155  // AsU64 converts the property to the Go uint64 type.
   156  func (p *Property) AsU64() (uint64, error) {
   157  	if len(p.Value) != 8 {
   158  		return 0, fmt.Errorf("property %q is not <u64>", p.Name)
   159  	}
   160  	var val uint64
   161  	err := binary.Read(bytes.NewBuffer(p.Value), binary.BigEndian, &val)
   162  	return val, err
   163  }
   164  
   165  // AsString converts the property to the Go string type. The trailing null
   166  // character is stripped.
   167  func (p *Property) AsString() (string, error) {
   168  	if len(p.Value) == 0 || p.Value[len(p.Value)-1] != 0 {
   169  		return "", fmt.Errorf("property %q is not <string>", p.Name)
   170  	}
   171  	str := p.Value[:len(p.Value)-1]
   172  	if !isPrintableASCII(str) {
   173  		return "", fmt.Errorf("property %q is not <string>", p.Name)
   174  	}
   175  	return string(str), nil
   176  }
   177  
   178  // AsPropEncodedArray converts the property to the Go []byte type.
   179  func (p *Property) AsPropEncodedArray() ([]byte, error) {
   180  	return p.Value, nil
   181  }
   182  
   183  // AsPHandle converts the property to the Go fdt.PHandle type.
   184  func (p *Property) AsPHandle() (PHandle, error) {
   185  	val, err := p.AsU32()
   186  	return PHandle(val), err
   187  }
   188  
   189  // AsStringList converts the property to the Go []string type. The trailing
   190  // null character of each string is stripped.
   191  func (p *Property) AsStringList() ([]string, error) {
   192  	if len(p.Value) == 0 || p.Value[len(p.Value)-1] != 0 {
   193  		return nil, fmt.Errorf("property %q is not <stringlist>", p.Name)
   194  	}
   195  	value := p.Value
   196  	strs := []string{}
   197  	for len(p.Value) > 0 {
   198  		nextNull := bytes.IndexByte(value, 0) // cannot be -1
   199  		var str []byte
   200  		str, value = value[:nextNull], value[nextNull+1:]
   201  		if !isPrintableASCII(str) {
   202  			return nil, fmt.Errorf("property %q is not <stringlist>", p.Name)
   203  		}
   204  		strs = append(strs, string(str))
   205  	}
   206  	return strs, nil
   207  }
   208  
   209  func isPrintableASCII(s []byte) bool {
   210  	for _, v := range s {
   211  		if v > unicode.MaxASCII || !unicode.IsPrint(rune(v)) {
   212  			return false
   213  		}
   214  	}
   215  	return true
   216  }