github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/dt/fdt.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 contains utilities for device tree. 6 package dt 7 8 import ( 9 "bufio" 10 "bytes" 11 "encoding/binary" 12 "errors" 13 "fmt" 14 "io" 15 "unsafe" 16 17 "github.com/u-root/u-root/pkg/uio" 18 ) 19 20 const ( 21 // Magic value seen in the FDT Header. 22 Magic uint32 = 0xd00dfeed 23 24 // MaxTotalSize is a limitation imposed by this implementation. This 25 // prevents the integers from wrapping around. Typically, the total size is 26 // a few megabytes, so this is not restrictive. 27 MaxTotalSize = 1024 * 1024 * 1024 28 ) 29 30 type token uint32 31 32 const ( 33 tokenBeginNode token = 0x1 34 tokenEndNode token = 0x2 35 tokenProp token = 0x3 36 tokenNop token = 0x4 37 tokenEnd token = 0x9 38 ) 39 40 // FDT contains the parsed contents of a Flattend Device Tree (.dtb). 41 // 42 // The format is relatively simple and defined in chapter 5 of the Devicetree 43 // Specification Release 0.2. 44 // 45 // See: https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.2 46 // 47 // This package is compatible with version 16 and 17 of DTSpec. 48 type FDT struct { 49 Header Header 50 ReserveEntries []ReserveEntry 51 RootNode *Node 52 } 53 54 // Header appears at offset 0. 55 type Header struct { 56 Magic uint32 57 TotalSize uint32 58 OffDtStruct uint32 59 OffDtStrings uint32 60 OffMemRsvmap uint32 61 Version uint32 62 LastCompVersion uint32 63 BootCpuidPhys uint32 64 SizeDtStrings uint32 65 SizeDtStruct uint32 66 } 67 68 // ReserveEntry defines a memory region which is reserved. 69 type ReserveEntry struct { 70 Address uint64 71 Size uint64 72 } 73 74 // ReadFDT reads FDT from an io.ReadSeeker. 75 func ReadFDT(f io.ReadSeeker) (*FDT, error) { 76 fdt := &FDT{} 77 if err := fdt.readHeader(f); err != nil { 78 return nil, err 79 } 80 if err := fdt.readMemoryReservationBlock(f); err != nil { 81 return nil, err 82 } 83 if err := fdt.checkLayout(); err != nil { 84 return nil, err 85 } 86 strs, err := fdt.readStringsBlock(f) 87 if err != nil { 88 return nil, err 89 } 90 if err := fdt.readStructBlock(f, strs); err != nil { 91 return nil, err 92 } 93 return fdt, nil 94 } 95 96 func (fdt *FDT) readHeader(f io.ReadSeeker) error { 97 h := &fdt.Header 98 if _, err := f.Seek(0, io.SeekStart); err != nil { 99 return err 100 } 101 if err := binary.Read(f, binary.BigEndian, h); err != nil { 102 return err 103 } 104 if h.Magic != Magic { 105 return fmt.Errorf("invalid FDT magic, got %#08x, expected %#08x", 106 h.Magic, Magic) 107 } 108 if !(h.Version == 16 || h.Version == 17 || 109 (h.LastCompVersion <= 17 && h.Version > 17)) { 110 return fmt.Errorf( 111 "incompatible FDT version, must be compatible with 16/17,"+ 112 "but version is %d last compatible with %d", 113 h.Version, h.LastCompVersion) 114 } 115 if h.TotalSize > MaxTotalSize { 116 return fmt.Errorf("FDT too large, %d > %d", h.TotalSize, MaxTotalSize) 117 } 118 return nil 119 } 120 121 func (fdt *FDT) readMemoryReservationBlock(f io.ReadSeeker) error { 122 if fdt.Header.OffMemRsvmap < uint32(unsafe.Sizeof(fdt.Header)) { 123 return fmt.Errorf("memory reservation block may not overlap header, %#x < %#x", 124 fdt.Header.OffMemRsvmap, unsafe.Sizeof(fdt.Header)) 125 } 126 if fdt.Header.OffMemRsvmap%8 != 0 { 127 return fmt.Errorf( 128 "memory reservation offset must be aligned to 8 bytes, but is %#x", 129 fdt.Header.OffMemRsvmap) 130 } 131 if _, err := f.Seek(int64(fdt.Header.OffMemRsvmap), io.SeekStart); err != nil { 132 return err 133 } 134 fdt.ReserveEntries = []ReserveEntry{} 135 for { 136 entry := ReserveEntry{} 137 if err := binary.Read(f, binary.BigEndian, &entry); err != nil { 138 return err 139 } 140 if entry.Address == 0 && entry.Size == 0 { 141 break 142 } 143 fdt.ReserveEntries = append(fdt.ReserveEntries, entry) 144 } 145 return nil 146 } 147 148 // checkLayout returns any errors if any of the blocks overlap, are in the 149 // wrong order or stray past the end of the file. This function must be called 150 // after readHeader and readMemoryReservationBlock. 151 func (fdt *FDT) checkLayout() error { 152 memRscEnd := fdt.Header.OffMemRsvmap + 153 uint32(len(fdt.ReserveEntries)+1)*uint32(unsafe.Sizeof(ReserveEntry{})) 154 if fdt.Header.OffDtStruct < memRscEnd { 155 return fmt.Errorf( 156 "struct block must not overlap memory reservation block, %#x < %#x", 157 fdt.Header.OffDtStruct, memRscEnd) 158 } 159 // TODO: there are more checks which should be done 160 return nil 161 } 162 163 func (fdt *FDT) readStringsBlock(f io.ReadSeeker) (strs []byte, err error) { 164 if _, err = f.Seek(int64(fdt.Header.OffDtStrings), io.SeekStart); err != nil { 165 return 166 } 167 strs = make([]byte, fdt.Header.SizeDtStrings) 168 _, err = f.Read(strs) 169 return 170 } 171 172 // readStructBlock reads the nodes and properties of the device and creates the 173 // tree structure. strs contains the strings block. 174 func (fdt *FDT) readStructBlock(f io.ReadSeeker, strs []byte) error { 175 if fdt.Header.OffDtStruct%4 != 0 { 176 return fmt.Errorf( 177 "struct offset must be aligned to 4 bytes, but is %#v", 178 fdt.Header.OffDtStruct) 179 } 180 if _, err := f.Seek(int64(fdt.Header.OffDtStruct), io.SeekStart); err != nil { 181 return err 182 } 183 184 // Buffer file so we don't perform a bajillion syscalls when looking for 185 // null-terminating characters. 186 r := &uio.AlignReader{ 187 R: bufio.NewReader( 188 &io.LimitedReader{ 189 R: f, 190 N: int64(fdt.Header.SizeDtStruct), 191 }, 192 ), 193 } 194 195 stack := []*Node{} 196 for { 197 var t token 198 if err := binary.Read(r, binary.BigEndian, &t); err != nil { 199 return err 200 } 201 switch t { 202 case tokenBeginNode: 203 // Push new node onto the stack. 204 child := &Node{} 205 stack = append(stack, child) 206 if len(stack) == 1 { 207 // Root node 208 if fdt.RootNode != nil { 209 return errors.New("invalid multiple root nodes") 210 } 211 fdt.RootNode = child 212 } else if len(stack) > 1 { 213 // Non-root node 214 parent := stack[len(stack)-2] 215 parent.Children = append(parent.Children, child) 216 } 217 218 // The name is a null-terminating string. 219 for { 220 if b, err := r.ReadByte(); err != nil { 221 return err 222 } else if b == 0 { 223 break 224 } else { 225 child.Name += string(b) 226 } 227 } 228 229 case tokenEndNode: 230 if len(stack) == 0 { 231 return errors.New( 232 "unbalanced FDT_BEGIN_NODE and FDT_END_NODE tokens") 233 } 234 stack = stack[:len(stack)-1] 235 236 case tokenProp: 237 pHeader := struct { 238 Len, Nameoff uint32 239 }{} 240 if err := binary.Read(r, binary.BigEndian, &pHeader); err != nil { 241 return err 242 } 243 if pHeader.Nameoff >= uint32(len(strs)) { 244 return fmt.Errorf( 245 "name offset is larger than strings block: %#x >= %#x", 246 pHeader.Nameoff, len(strs)) 247 } 248 null := bytes.IndexByte(strs[pHeader.Nameoff:], 0) 249 if null == -1 { 250 return fmt.Errorf( 251 "property name does not having terminating null at %#x", 252 pHeader.Nameoff) 253 } 254 p := Property{ 255 Name: string(strs[pHeader.Nameoff : pHeader.Nameoff+uint32(null)]), 256 Value: make([]byte, pHeader.Len), 257 } 258 _, err := io.ReadFull(r, p.Value) 259 if err != nil { 260 return err 261 } 262 if len(stack) == 0 { 263 return fmt.Errorf("property %q appears outside a node", p.Name) 264 } 265 curNode := stack[len(stack)-1] 266 curNode.Properties = append(curNode.Properties, p) 267 268 case tokenNop: 269 270 case tokenEnd: 271 if uint32(r.N) < fdt.Header.SizeDtStruct { 272 return fmt.Errorf( 273 "extra data at end of structure block, %#x < %#x", 274 uint32(r.N), fdt.Header.SizeDtStruct) 275 } 276 if fdt.RootNode == nil { 277 return errors.New("no root node") 278 } 279 return nil 280 281 default: 282 return fmt.Errorf("undefined token %d", t) 283 } 284 285 // Align to four bytes. 286 // was: pad, err := 287 _, err := r.Align(4) 288 if err != nil { 289 return err 290 } 291 /*for _, v := range pad { 292 if v != 0 { 293 // TODO: Some of the padding is not zero. Is this a mistake? 294 return fmt.Errorf("padding is non-zero: %d", v) 295 } 296 }*/ 297 } 298 } 299 300 func align4(x int) uint32 { 301 return uint32((x + 0x3) &^ 0x3) 302 } 303 304 func align16(x int) uint32 { 305 return uint32((x + 0xf) &^ 0xf) 306 } 307 308 // Write marshals the FDT to an io.Writer and returns the size. 309 func (fdt *FDT) Write(f io.Writer) (int, error) { 310 // Create string block and offset map. 311 strs := []byte{} 312 strOff := map[string]uint32{} 313 fdt.RootNode.Walk(func(n *Node) error { 314 for _, p := range n.Properties { 315 if _, ok := strOff[p.Name]; !ok { // deduplicate 316 strOff[p.Name] = uint32(len(strs)) 317 strs = append(strs, []byte(p.Name)...) 318 strs = append(strs, 0) 319 } 320 } 321 return nil 322 }) 323 324 // Calculate block sizes and offsets. 325 fdt.Header.SizeDtStrings = uint32(len(strs)) 326 fdt.Header.SizeDtStruct = 4 327 fdt.RootNode.Walk(func(n *Node) error { 328 fdt.Header.SizeDtStruct += 8 + align4(len(n.Name)+1) 329 for _, p := range n.Properties { 330 fdt.Header.SizeDtStruct += 12 + align4(len(p.Value)) 331 } 332 return nil 333 }) 334 fdt.Header.OffMemRsvmap = align16(int(unsafe.Sizeof(fdt.Header))) 335 fdt.Header.OffDtStruct = fdt.Header.OffMemRsvmap + 336 align4((len(fdt.ReserveEntries)+1)*int(unsafe.Sizeof(ReserveEntry{}))) 337 fdt.Header.OffDtStrings = fdt.Header.OffDtStruct + fdt.Header.SizeDtStruct 338 fdt.Header.TotalSize = fdt.Header.OffDtStrings + fdt.Header.SizeDtStrings 339 340 // Setup AlignWriter. 341 w := &uio.AlignWriter{W: f} 342 343 // Write header. 344 if err := binary.Write(w, binary.BigEndian, fdt.Header); err != nil { 345 return w.N, err 346 } 347 348 // Write memreserve block. 349 if err := w.Align(16, 0x00); err != nil { 350 return w.N, err 351 } 352 if err := binary.Write(w, binary.BigEndian, &fdt.ReserveEntries); err != nil { 353 return w.N, err 354 } 355 if err := binary.Write(w, binary.BigEndian, &ReserveEntry{}); err != nil { 356 return w.N, err 357 } 358 359 // Write struct block. 360 if err := w.Align(4, 0x00); err != nil { 361 return w.N, err 362 } 363 var writeNode func(n *Node) error 364 writeNode = func(n *Node) error { 365 if err := binary.Write(w, binary.BigEndian, tokenBeginNode); err != nil { 366 return err 367 } 368 if _, err := w.Write([]byte(n.Name + "\000")); err != nil { 369 return err 370 } 371 if err := w.Align(4, 0x00); err != nil { 372 return err 373 } 374 for _, p := range n.Properties { 375 property := struct { 376 Token token 377 Len, Nameoff uint32 378 }{ 379 tokenProp, 380 uint32(len(p.Value)), 381 strOff[p.Name], 382 } 383 if err := binary.Write(w, binary.BigEndian, &property); err != nil { 384 return err 385 } 386 if _, err := w.Write(p.Value); err != nil { 387 return err 388 } 389 if err := w.Align(4, 0x00); err != nil { 390 return err 391 } 392 } 393 for _, child := range n.Children { 394 if err := writeNode(child); err != nil { 395 return err 396 } 397 } 398 if err := binary.Write(w, binary.BigEndian, tokenEndNode); err != nil { 399 return err 400 } 401 return nil 402 } 403 if err := writeNode(fdt.RootNode); err != nil { 404 return w.N, err 405 } 406 if err := binary.Write(w, binary.BigEndian, tokenEnd); err != nil { 407 return w.N, err 408 } 409 410 // Write strings block 411 _, err := w.Write(strs) 412 return w.N, err 413 }