iPXE
fdt.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <string.h>
27 #include <errno.h>
28 #include <assert.h>
29 #include <byteswap.h>
30 #include <ipxe/netdevice.h>
31 #include <ipxe/fdt.h>
32 
33 /** @file
34  *
35  * Flattened Device Tree
36  *
37  */
38 
39 /** The system flattened device tree (if present) */
40 static struct fdt fdt;
41 
42 /** A position within a device tree */
43 struct fdt_cursor {
44  /** Offset within structure block */
45  unsigned int offset;
46  /** Tree depth */
47  int depth;
48 };
49 
50 /** A lexical descriptor */
52  /** Node or property name (if applicable) */
53  const char *name;
54  /** Property data (if applicable) */
55  const void *data;
56  /** Length of property data (if applicable) */
57  size_t len;
58 };
59 
60 /**
61  * Check if device tree exists
62  *
63  * @v has_fdt Device tree exists
64  */
65 static inline __attribute__ (( always_inline )) int fdt_exists ( void ) {
66 
67  return ( fdt.hdr != NULL );
68 }
69 
70 /**
71  * Traverse device tree
72  *
73  * @v pos Position within device tree
74  * @v desc Lexical descriptor to fill in
75  * @ret rc Return status code
76  */
77 static int fdt_traverse ( struct fdt_cursor *pos,
78  struct fdt_descriptor *desc ) {
79  const fdt_token_t *token;
80  const void *data;
81  const struct fdt_prop *prop;
82  unsigned int name_off;
83  size_t remaining;
84  size_t len;
85 
86  /* Sanity checks */
87  assert ( pos->offset < fdt.len );
88  assert ( ( pos->offset & ( FDT_STRUCTURE_ALIGN - 1 ) ) == 0 );
89 
90  /* Clear descriptor */
91  memset ( desc, 0, sizeof ( *desc ) );
92 
93  /* Locate token and calculate remaining space */
94  token = ( fdt.raw + fdt.structure + pos->offset );
95  remaining = ( fdt.len - pos->offset );
96  if ( remaining < sizeof ( *token ) ) {
97  DBGC ( &fdt, "FDT truncated tree at +%#04x\n", pos->offset );
98  return -EINVAL;
99  }
100  remaining -= sizeof ( *token );
101  data = ( ( ( const void * ) token ) + sizeof ( *token ) );
102  len = 0;
103 
104  /* Handle token */
105  switch ( *token ) {
106 
107  case cpu_to_be32 ( FDT_BEGIN_NODE ):
108 
109  /* Start of node */
110  desc->name = data;
111  len = ( strnlen ( desc->name, remaining ) + 1 /* NUL */ );
112  if ( remaining < len ) {
113  DBGC ( &fdt, "FDT unterminated node name at +%#04x\n",
114  pos->offset );
115  return -EINVAL;
116  }
117  pos->depth++;
118  break;
119 
120  case cpu_to_be32 ( FDT_END_NODE ):
121 
122  /* End of node */
123  if ( pos->depth < 0 ) {
124  DBGC ( &fdt, "FDT spurious node end at +%#04x\n",
125  pos->offset );
126  return -EINVAL;
127  }
128  pos->depth--;
129  if ( pos->depth < 0 ) {
130  /* End of (sub)tree */
131  return -ENOENT;
132  }
133  break;
134 
135  case cpu_to_be32 ( FDT_PROP ):
136 
137  /* Property */
138  prop = data;
139  if ( remaining < sizeof ( *prop ) ) {
140  DBGC ( &fdt, "FDT truncated property at +%#04x\n",
141  pos->offset );
142  return -EINVAL;
143  }
144  desc->data = ( ( ( const void * ) prop ) + sizeof ( *prop ) );
145  desc->len = be32_to_cpu ( prop->len );
146  len = ( sizeof ( *prop ) + desc->len );
147  if ( remaining < len ) {
148  DBGC ( &fdt, "FDT overlength property at +%#04x\n",
149  pos->offset );
150  return -EINVAL;
151  }
152  name_off = be32_to_cpu ( prop->name_off );
153  if ( name_off > fdt.strings_len ) {
154  DBGC ( &fdt, "FDT property name outside strings "
155  "block at +%#04x\n", pos->offset );
156  return -EINVAL;
157  }
158  desc->name = ( fdt.raw + fdt.strings + name_off );
159  break;
160 
161  case cpu_to_be32 ( FDT_NOP ):
162 
163  /* Do nothing */
164  break;
165 
166  default:
167 
168  /* Unrecognised or unexpected token */
169  DBGC ( &fdt, "FDT unexpected token %#08x at +%#04x\n",
170  be32_to_cpu ( *token ), pos->offset );
171  return -EINVAL;
172  }
173 
174  /* Update cursor */
175  len = ( ( len + FDT_STRUCTURE_ALIGN - 1 ) &
176  ~( FDT_STRUCTURE_ALIGN - 1 ) );
177  pos->offset += ( sizeof ( *token ) + len );
178 
179  /* Sanity checks */
180  assert ( pos->offset <= fdt.len );
181 
182  return 0;
183 }
184 
185 /**
186  * Find child node
187  *
188  * @v offset Starting node offset
189  * @v name Node name
190  * @v child Child node offset to fill in
191  * @ret rc Return status code
192  */
193 static int fdt_child ( unsigned int offset, const char *name,
194  unsigned int *child ) {
195  struct fdt_cursor pos;
196  struct fdt_descriptor desc;
197  unsigned int orig_offset;
198  int rc;
199 
200  /* Record original offset (for debugging) */
201  orig_offset = offset;
202 
203  /* Initialise cursor */
204  pos.offset = offset;
205  pos.depth = -1;
206 
207  /* Find child node */
208  while ( 1 ) {
209 
210  /* Record current offset */
211  *child = pos.offset;
212 
213  /* Traverse tree */
214  if ( ( rc = fdt_traverse ( &pos, &desc ) ) != 0 ) {
215  DBGC ( &fdt, "FDT +%#04x has no child node \"%s\": "
216  "%s\n", orig_offset, name, strerror ( rc ) );
217  return rc;
218  }
219 
220  /* Check for matching immediate child node */
221  if ( ( pos.depth == 1 ) && desc.name && ( ! desc.data ) ) {
222  DBGC2 ( &fdt, "FDT +%#04x has child node \"%s\"\n",
223  orig_offset, desc.name );
224  if ( strcmp ( name, desc.name ) == 0 ) {
225  DBGC2 ( &fdt, "FDT +%#04x found child node "
226  "\"%s\" at +%#04x\n", orig_offset,
227  desc.name, *child );
228  return 0;
229  }
230  }
231  }
232 }
233 
234 /**
235  * Find node by path
236  *
237  * @v path Node path
238  * @v offset Offset to fill in
239  * @ret rc Return status code
240  */
241 int fdt_path ( const char *path, unsigned int *offset ) {
242  char *tmp = ( ( char * ) path );
243  char *del;
244  int rc;
245 
246  /* Initialise offset */
247  *offset = 0;
248 
249  /* Traverse tree one path segment at a time */
250  while ( *tmp ) {
251 
252  /* Skip any leading '/' */
253  while ( *tmp == '/' )
254  tmp++;
255 
256  /* Find next '/' delimiter and convert to NUL */
257  del = strchr ( tmp, '/' );
258  if ( del )
259  *del = '\0';
260 
261  /* Find child and restore delimiter */
262  rc = fdt_child ( *offset, tmp, offset );
263  if ( del )
264  *del = '/';
265  if ( rc != 0 )
266  return rc;
267 
268  /* Move to next path component, if any */
269  while ( *tmp && ( *tmp != '/' ) )
270  tmp++;
271  }
272 
273  DBGC2 ( &fdt, "FDT found path \"%s\" at +%#04x\n", path, *offset );
274  return 0;
275 }
276 
277 /**
278  * Find node by alias
279  *
280  * @v name Alias name
281  * @v offset Offset to fill in
282  * @ret rc Return status code
283  */
284 int fdt_alias ( const char *name, unsigned int *offset ) {
285  const char *alias;
286  int rc;
287 
288  /* Locate "/aliases" node */
289  if ( ( rc = fdt_child ( 0, "aliases", offset ) ) != 0 )
290  return rc;
291 
292  /* Locate alias property */
293  if ( ( alias = fdt_string ( *offset, name ) ) == NULL )
294  return -ENOENT;
295  DBGC ( &fdt, "FDT alias \"%s\" is \"%s\"\n", name, alias );
296 
297  /* Locate aliased node */
298  if ( ( rc = fdt_path ( alias, offset ) ) != 0 )
299  return rc;
300 
301  return 0;
302 }
303 
304 /**
305  * Find property
306  *
307  * @v offset Starting node offset
308  * @v name Property name
309  * @v desc Lexical descriptor to fill in
310  * @ret rc Return status code
311  */
312 static int fdt_property ( unsigned int offset, const char *name,
313  struct fdt_descriptor *desc ) {
314  struct fdt_cursor pos;
315  int rc;
316 
317  /* Initialise cursor */
318  pos.offset = offset;
319  pos.depth = -1;
320 
321  /* Find property */
322  while ( 1 ) {
323 
324  /* Traverse tree */
325  if ( ( rc = fdt_traverse ( &pos, desc ) ) != 0 ) {
326  DBGC ( &fdt, "FDT +%#04x has no property \"%s\": %s\n",
327  offset, name, strerror ( rc ) );
328  return rc;
329  }
330 
331  /* Check for matching immediate child property */
332  if ( ( pos.depth == 0 ) && desc->data ) {
333  DBGC2 ( &fdt, "FDT +%#04x has property \"%s\" len "
334  "%#zx\n", offset, desc->name, desc->len );
335  if ( strcmp ( name, desc->name ) == 0 ) {
336  DBGC2 ( &fdt, "FDT +%#04x found property "
337  "\"%s\"\n", offset, desc->name );
338  DBGC2_HDA ( &fdt, 0, desc->data, desc->len );
339  return 0;
340  }
341  }
342  }
343 }
344 
345 /**
346  * Find string property
347  *
348  * @v offset Starting node offset
349  * @v name Property name
350  * @ret string String property, or NULL on error
351  */
352 const char * fdt_string ( unsigned int offset, const char *name ) {
353  struct fdt_descriptor desc;
354  int rc;
355 
356  /* Find property */
357  if ( ( rc = fdt_property ( offset, name, &desc ) ) != 0 )
358  return NULL;
359 
360  /* Check NUL termination */
361  if ( strnlen ( desc.data, desc.len ) == desc.len ) {
362  DBGC ( &fdt, "FDT unterminated string property \"%s\"\n",
363  name );
364  return NULL;
365  }
366 
367  return desc.data;
368 }
369 
370 /**
371  * Get MAC address from property
372  *
373  * @v offset Starting node offset
374  * @v netdev Network device
375  * @ret rc Return status code
376  */
377 int fdt_mac ( unsigned int offset, struct net_device *netdev ) {
378  struct fdt_descriptor desc;
379  size_t len;
380  int rc;
381 
382  /* Find applicable MAC address property */
383  if ( ( ( rc = fdt_property ( offset, "mac-address", &desc ) ) != 0 ) &&
384  ( ( rc = fdt_property ( offset, "local-mac-address",
385  &desc ) ) != 0 ) ) {
386  return rc;
387  }
388 
389  /* Check length */
391  if ( len != desc.len ) {
392  DBGC ( &fdt, "FDT malformed MAC address \"%s\":\n",
393  desc.name );
394  DBGC_HDA ( &fdt, 0, desc.data, desc.len );
395  return -ERANGE;
396  }
397 
398  /* Fill in MAC address */
399  memcpy ( netdev->hw_addr, desc.data, len );
400 
401  return 0;
402 }
403 
404 /**
405  * Register device tree
406  *
407  * @v fdt Device tree header
408  * @ret rc Return status code
409  */
410 int register_fdt ( const struct fdt_header *hdr ) {
411  const uint8_t *end;
412 
413  /* Record device tree location */
414  fdt.hdr = hdr;
415  fdt.len = be32_to_cpu ( hdr->totalsize );
416  DBGC ( &fdt, "FDT version %d at %p+%#04zx\n",
417  be32_to_cpu ( hdr->version ), fdt.hdr, fdt.len );
418 
419  /* Check signature */
420  if ( hdr->magic != cpu_to_be32 ( FDT_MAGIC ) ) {
421  DBGC ( &fdt, "FDT has invalid magic value %#08x\n",
422  be32_to_cpu ( hdr->magic ) );
423  goto err;
424  }
425 
426  /* Check version */
427  if ( hdr->last_comp_version != cpu_to_be32 ( FDT_VERSION ) ) {
428  DBGC ( &fdt, "FDT unsupported version %d\n",
429  be32_to_cpu ( hdr->last_comp_version ) );
430  goto err;
431  }
432 
433  /* Record structure block location */
434  fdt.structure = be32_to_cpu ( hdr->off_dt_struct );
435  fdt.structure_len = be32_to_cpu ( hdr->size_dt_struct );
436  DBGC ( &fdt, "FDT structure block at +[%#04x,%#04zx)\n",
438  if ( ( fdt.structure > fdt.len ) ||
439  ( fdt.structure_len > ( fdt.len - fdt.structure ) ) ) {
440  DBGC ( &fdt, "FDT structure block exceeds table\n" );
441  goto err;
442  }
443  if ( ( fdt.structure | fdt.structure_len ) &
444  ( FDT_STRUCTURE_ALIGN - 1 ) ) {
445  DBGC ( &fdt, "FDT structure block is misaligned\n" );
446  goto err;
447  }
448 
449  /* Record strings block location */
450  fdt.strings = be32_to_cpu ( hdr->off_dt_strings );
451  fdt.strings_len = be32_to_cpu ( hdr->size_dt_strings );
452  DBGC ( &fdt, "FDT strings block at +[%#04x,%#04zx)\n",
454  if ( ( fdt.strings > fdt.len ) ||
455  ( fdt.strings_len > ( fdt.len - fdt.strings ) ) ) {
456  DBGC ( &fdt, "FDT strings block exceeds table\n" );
457  goto err;
458  }
459 
460  /* Shrink strings block to ensure NUL termination safety */
461  end = ( fdt.raw + fdt.strings + fdt.strings_len );
462  for ( ; fdt.strings_len ; fdt.strings_len-- ) {
463  if ( *(--end) == '\0' )
464  break;
465  }
466  if ( fdt.strings_len != be32_to_cpu ( hdr->size_dt_strings ) ) {
467  DBGC ( &fdt, "FDT strings block shrunk to +[%#04x,%#04zx)\n",
469  }
470 
471  /* Print model name (for debugging) */
472  DBGC ( &fdt, "FDT model is \"%s\"\n", fdt_string ( 0, "model" ) );
473 
474  return 0;
475 
476  err:
477  DBGC_HDA ( &fdt, 0, hdr, sizeof ( *hdr ) );
478  fdt.hdr = NULL;
479  return -EINVAL;
480 }
481 
482 /* Drag in objects via register_fdt */
484 
485 /* Drag in device tree configuration */
486 REQUIRE_OBJECT ( config_fdt );
#define __attribute__(x)
Definition: compiler.h:10
#define EINVAL
Invalid argument.
Definition: errno.h:428
uint32_t name_off
Name offset.
Definition: fdt.h:51
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
const char * name
Definition: ath9k_hw.c:1984
const char * name
Node or property name (if applicable)
Definition: fdt.c:53
#define FDT_STRUCTURE_ALIGN
Alignment of structure block.
Definition: fdt.h:73
unsigned int offset
Offset within structure block.
Definition: fdt.c:45
#define FDT_NOP
NOP token.
Definition: fdt.h:67
Error codes.
size_t len
Length of tree.
Definition: fdt.h:85
Device tree header.
Definition: fdt.h:17
struct golan_inbox_hdr hdr
Message header.
Definition: CIB_PRM.h:28
int fdt_mac(unsigned int offset, struct net_device *netdev)
Get MAC address from property.
Definition: fdt.c:377
int fdt_path(const char *path, unsigned int *offset)
Find node by path.
Definition: fdt.c:241
uint64_t desc
Microcode descriptor list physical address.
Definition: ucode.h:12
#define DBGC(...)
Definition: compiler.h:505
#define ENOENT
No such file or directory.
Definition: errno.h:514
static int fdt_property(unsigned int offset, const char *name, struct fdt_descriptor *desc)
Find property.
Definition: fdt.c:312
const struct fdt_header * hdr
Tree header.
Definition: fdt.h:80
size_t strings_len
Length of strings block.
Definition: fdt.h:93
static int fdt_exists(void)
Check if device tree exists.
Definition: fdt.c:65
uint32_t fdt_token_t
Device tree token.
Definition: fdt.h:47
const char * fdt_string(unsigned int offset, const char *name)
Find string property.
Definition: fdt.c:352
uint32_t len
Data length.
Definition: fdt.h:61
unsigned long tmp
Definition: linux_pci.h:53
void * memcpy(void *dest, const void *src, size_t len) __nonnull
Assertions.
#define be32_to_cpu(value)
Definition: byteswap.h:116
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
uint8_t hw_addr_len
Hardware address length.
Definition: netdevice.h:196
#define FDT_MAGIC
Magic signature.
Definition: fdt.h:41
#define DBGC_HDA(...)
Definition: compiler.h:506
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
static struct net_device * netdev
Definition: gdbudp.c:52
size_t len
Length of property data (if applicable)
Definition: fdt.c:57
unsigned int structure
Offset to structure block.
Definition: fdt.h:87
#define FDT_VERSION
Expected device tree version.
Definition: fdt.h:44
#define DBGC2_HDA(...)
Definition: compiler.h:523
#define ERANGE
Result too large.
Definition: errno.h:639
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
char * strchr(const char *src, int character)
Find character within a string.
Definition: string.c:271
Property fragment.
Definition: fdt.h:59
A network device.
Definition: netdevice.h:352
unsigned char uint8_t
Definition: stdint.h:10
size_t strnlen(const char *src, size_t max)
Get length of string.
Definition: string.c:255
A position within a device tree.
Definition: fdt.c:43
static int fdt_child(unsigned int offset, const char *name, unsigned int *child)
Find child node.
Definition: fdt.c:193
u8 token
Definition: CIB_PRM.h:42
REQUIRE_OBJECT(config_fdt)
size_t structure_len
Length of structure block.
Definition: fdt.h:89
#define FDT_PROP
Property token.
Definition: fdt.h:56
Network device management.
int depth
Tree depth.
Definition: fdt.c:47
#define cpu_to_be32(value)
Definition: byteswap.h:110
static int fdt_traverse(struct fdt_cursor *pos, struct fdt_descriptor *desc)
Traverse device tree.
Definition: fdt.c:77
REQUIRING_SYMBOL(register_fdt)
A device tree.
Definition: fdt.h:76
uint32_t len
Length.
Definition: ena.h:14
#define DBGC2(...)
Definition: compiler.h:522
int strcmp(const char *first, const char *second)
Compare strings.
Definition: string.c:173
uint32_t name_off
Name offset.
Definition: fdt.h:63
const void * raw
Raw data.
Definition: fdt.h:82
#define FDT_END_NODE
End node token.
Definition: fdt.h:53
Flattened Device Tree.
uint32_t end
Ending offset.
Definition: netvsc.h:18
uint8_t data[48]
Additional event data.
Definition: ena.h:22
#define FDT_BEGIN_NODE
Begin node token.
Definition: fdt.h:50
A lexical descriptor.
Definition: fdt.c:51
uint8_t hw_addr[MAX_HW_ADDR_LEN]
Hardware address.
Definition: netdevice.h:381
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
String functions.
const void * data
Property data (if applicable)
Definition: fdt.c:55
int register_fdt(const struct fdt_header *hdr)
Register device tree.
Definition: fdt.c:410
struct ll_protocol * ll_protocol
Link-layer protocol.
Definition: netdevice.h:372
unsigned int strings
Offset to strings block.
Definition: fdt.h:91
void * memset(void *dest, int character, size_t len) __nonnull
int fdt_alias(const char *name, unsigned int *offset)
Find node by alias.
Definition: fdt.c:284