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