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 }