github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+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 pad, err := r.Align(4) 287 if err != nil { 288 return err 289 } 290 for _, v := range pad { 291 if v != 0 { 292 // TODO: Some of the padding is not zero. Is this a mistake? 293 //return fmt.Errorf("padding is non-zero: %d", v) 294 } 295 } 296 } 297 } 298 299 func align4(x int) uint32 { 300 return uint32((x + 0x3) &^ 0x3) 301 } 302 303 func align16(x int) uint32 { 304 return uint32((x + 0xf) &^ 0xf) 305 } 306 307 // Write marshals the FDT to an io.Writer and returns the size. 308 func (fdt *FDT) Write(f io.Writer) (int, error) { 309 // Create string block and offset map. 310 strs := []byte{} 311 strOff := map[string]uint32{} 312 fdt.RootNode.Walk(func(n *Node) error { 313 for _, p := range n.Properties { 314 if _, ok := strOff[p.Name]; !ok { // deduplicate 315 strOff[p.Name] = uint32(len(strs)) 316 strs = append(strs, []byte(p.Name)...) 317 strs = append(strs, 0) 318 } 319 } 320 return nil 321 }) 322 323 // Calculate block sizes and offsets. 324 fdt.Header.SizeDtStrings = uint32(len(strs)) 325 fdt.Header.SizeDtStruct = 4 326 fdt.RootNode.Walk(func(n *Node) error { 327 fdt.Header.SizeDtStruct += 8 + align4(len(n.Name)+1) 328 for _, p := range n.Properties { 329 fdt.Header.SizeDtStruct += 12 + align4(len(p.Value)) 330 } 331 return nil 332 }) 333 fdt.Header.OffMemRsvmap = align16(int(unsafe.Sizeof(fdt.Header))) 334 fdt.Header.OffDtStruct = fdt.Header.OffMemRsvmap + 335 align4((len(fdt.ReserveEntries)+1)*int(unsafe.Sizeof(ReserveEntry{}))) 336 fdt.Header.OffDtStrings = fdt.Header.OffDtStruct + fdt.Header.SizeDtStruct 337 fdt.Header.TotalSize = fdt.Header.OffDtStrings + fdt.Header.SizeDtStrings 338 339 // Setup AlignWriter. 340 w := &uio.AlignWriter{W: f} 341 342 // Write header. 343 if err := binary.Write(w, binary.BigEndian, fdt.Header); err != nil { 344 return w.N, err 345 } 346 347 // Write memreserve block. 348 if err := w.Align(16, 0x00); err != nil { 349 return w.N, err 350 } 351 if err := binary.Write(w, binary.BigEndian, &fdt.ReserveEntries); err != nil { 352 return w.N, err 353 } 354 if err := binary.Write(w, binary.BigEndian, &ReserveEntry{}); err != nil { 355 return w.N, err 356 } 357 358 // Write struct block. 359 if err := w.Align(4, 0x00); err != nil { 360 return w.N, err 361 } 362 var writeNode func(n *Node) error 363 writeNode = func(n *Node) error { 364 if err := binary.Write(w, binary.BigEndian, tokenBeginNode); err != nil { 365 return err 366 } 367 if _, err := w.Write([]byte(n.Name + "\000")); err != nil { 368 return err 369 } 370 if err := w.Align(4, 0x00); err != nil { 371 return err 372 } 373 for _, p := range n.Properties { 374 property := struct { 375 Token token 376 Len, Nameoff uint32 377 }{ 378 tokenProp, 379 uint32(len(p.Value)), 380 strOff[p.Name], 381 } 382 if err := binary.Write(w, binary.BigEndian, &property); err != nil { 383 return err 384 } 385 if _, err := w.Write(p.Value); err != nil { 386 return err 387 } 388 if err := w.Align(4, 0x00); err != nil { 389 return err 390 } 391 } 392 for _, child := range n.Children { 393 if err := writeNode(child); err != nil { 394 return err 395 } 396 } 397 if err := binary.Write(w, binary.BigEndian, tokenEndNode); err != nil { 398 return err 399 } 400 return nil 401 } 402 if err := writeNode(fdt.RootNode); err != nil { 403 return w.N, err 404 } 405 if err := binary.Write(w, binary.BigEndian, tokenEnd); err != nil { 406 return w.N, err 407 } 408 409 // Write strings block 410 _, err := w.Write(strs) 411 return w.N, err 412 }