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 }