iPXE
devtree.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2025 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 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 /** @file
27  *
28  * Devicetree bus
29  *
30  */
31 
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <ipxe/device.h>
37 #include <ipxe/fdt.h>
38 #include <ipxe/iomap.h>
39 #include <ipxe/devtree.h>
40 
41 static struct dt_driver dt_node_driver __dt_driver;
42 
43 /**
44  * Map devicetree range
45  *
46  * @v dt Devicetree device
47  * @v offset Starting node offset
48  * @v index Region index
49  * @v len Length to map, or 0 to map whole region
50  * @ret io_addr I/O address, or NULL on error
51  */
52 void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
53  unsigned int index, size_t len ) {
54  struct fdt_reg_cells regs;
56  uint64_t size;
57  void *io_addr;
58  int rc;
59 
60  /* Get parent region cell size specification */
61  if ( ( rc = fdt_parent_reg_cells ( &sysfdt, offset, &regs ) ) != 0 ) {
62  DBGC ( dt, "DT %s could not get region cell sizes: %s\n",
63  dt->name, strerror ( rc ) );
64  return NULL;
65  }
66 
67  /* Read address */
68  if ( ( rc = fdt_reg_address ( &sysfdt, offset, &regs, index,
69  &address ) ) != 0 ) {
70  DBGC ( dt, "DT %s could not read region %d address: %s\n",
71  dt->name, index, strerror ( rc ) );
72  return NULL;
73  }
74 
75  /* Read size (or assume sufficient, if tree specifies no sizes) */
76  size = len;
77  if ( regs.size_cells &&
78  ( ( rc = fdt_reg_size ( &sysfdt, offset, &regs, index,
79  &size ) ) != 0 ) ) {
80  DBGC ( dt, "DT %s could not read region %d size: %s\n",
81  dt->name, index, strerror ( rc ) );
82  return NULL;
83  }
84 
85  /* Use region size as length if not specified */
86  if ( ! len )
87  len = size;
88  DBGC ( dt, "DT %s region %d at %#08llx+%#04llx\n",
89  dt->name, index, ( ( unsigned long long ) address ),
90  ( ( unsigned long long ) size ) );
91 
92  /* Verify size */
93  if ( len > size ) {
94  DBGC ( dt, "DT %s region %d is too small (%#llx/%#zx bytes)\n",
95  dt->name, index, ( ( unsigned long long ) size ), len );
96  return NULL;
97  }
98 
99  /* Map region */
100  io_addr = ioremap ( address, len );
101  if ( ! io_addr ) {
102  DBGC ( dt, "DT %s could not map region %d\n",
103  dt->name, index );
104  return NULL;
105  }
106 
107  return io_addr;
108 }
109 
110 /**
111  * Find devicetree driver
112  *
113  * @v dt Devicetree device
114  * @v offset Starting node offset
115  * @ret driver Driver
116  */
117 static struct dt_driver * dt_find_driver ( struct dt_device *dt,
118  unsigned int offset ) {
119  struct dt_driver *driver;
120  const char *ids;
121  const char *id;
122  unsigned int count;
123  unsigned int i;
124 
125  /* Read compatible programming model identifiers */
126  ids = fdt_strings ( &sysfdt, offset, "compatible", &count );
127 
128  /* Look for a compatible driver */
129  for ( id = ids ; count-- ; id += ( strlen ( id ) + 1 ) ) {
130  DBGC2 ( &sysfdt, "DT %s is compatible with %s\n",
131  dt->name, id );
132  for_each_table_entry ( driver, DT_DRIVERS ) {
133  for ( i = 0 ; i < driver->id_count ; i++ ) {
134  if ( strcmp ( id, driver->ids[i] ) == 0 ) {
135  DBGC ( dt, "DT %s has %s driver %s\n",
136  dt->name, id, driver->name );
137  return driver;
138  }
139  }
140  }
141  }
142 
143  /* Use generic node driver if no other driver matches */
144  return &dt_node_driver;
145 }
146 
147 /**
148  * Probe devicetree device
149  *
150  * @v dt Devicetree device
151  * @v offset Starting node offset
152  * @ret rc Return status code
153  */
154 static int dt_probe ( struct dt_device *dt, unsigned int offset ) {
155  struct dt_driver *driver;
156  int rc;
157 
158  /* Identify driver */
159  driver = dt_find_driver ( dt, offset );
160  dt->driver = driver;
161  dt->dev.driver_name = driver->name;
162 
163  /* Probe device */
164  if ( ( rc = driver->probe ( dt, offset ) ) != 0 ) {
165  if ( driver != &dt_node_driver ) {
166  DBGC ( dt, "DT %s could not probe: %s\n",
167  dt->name, strerror ( rc ) );
168  }
169  return rc;
170  }
171 
172  return 0;
173 }
174 
175 /**
176  * Remove devicetree device
177  *
178  * @v dt Devicetree device
179  */
180 static void dt_remove ( struct dt_device *dt ) {
181  struct dt_driver *driver = dt->driver;
182 
183  /* Remove device */
184  driver->remove ( dt );
185  if ( driver != &dt_node_driver )
186  DBGC ( dt, "DT %s removed\n", dt->name );
187 }
188 
189 /**
190  * Probe devicetree node
191  *
192  * @v parent Parent generic device
193  * @v offset Starting node offset
194  * @ret rc Return status code
195  */
196 int dt_probe_node ( struct device *parent, unsigned int offset ) {
197  struct fdt_descriptor desc;
198  struct dt_device *dt;
199  const char *name;
200  int rc;
201 
202  /* Describe token */
203  if ( ( rc = fdt_describe ( &sysfdt, offset, &desc ) ) != 0 )
204  goto err_describe;
205 
206  /* Allocate and initialise device */
207  dt = zalloc ( sizeof ( *dt ) );
208  if ( ! dt ) {
209  rc = -ENOMEM;
210  goto err_alloc;
211  }
212  name = ( offset ? desc.name : "root node" );
213  dt->name = dt->dev.name;
214  snprintf ( dt->dev.name, sizeof ( dt->dev.name ), "%s", name );
217  dt->dev.parent = parent;
218  INIT_LIST_HEAD ( &dt->dev.children );
219  list_add_tail ( &dt->dev.siblings, &parent->children );
220 
221  /* Probe device */
222  if ( ( rc = dt_probe ( dt, offset ) ) != 0 )
223  goto err_probe;
224 
225  return 0;
226 
227  dt_remove ( dt );
228  err_probe:
229  list_del ( &dt->dev.siblings );
230  free ( dt );
231  err_alloc:
232  err_describe:
233  return rc;
234 }
235 
236 /**
237  * Remove devicetree node
238  *
239  * @v parent Parent generic device
240  */
241 void dt_remove_node ( struct device *parent ) {
242  struct dt_device *dt;
243 
244  /* Identify most recently added child */
245  dt = list_last_entry ( &parent->children, struct dt_device,
246  dev.siblings );
247  assert ( dt != NULL );
248 
249  /* Remove driver */
250  dt_remove ( dt );
251 
252  /* Delete and free device */
253  list_del ( &dt->dev.siblings );
254  free ( dt );
255 }
256 
257 /**
258  * Probe devicetree children
259  *
260  * @v parent Parent device
261  * @v offset Starting node offset
262  * @ret rc Return status code
263  */
264 int dt_probe_children ( struct dt_device *parent, unsigned int offset ) {
265  struct fdt_descriptor desc;
266  int depth;
267  int rc;
268 
269  /* Probe any immediate child nodes */
270  for ( depth = -1 ; ; depth += desc.depth, offset = desc.next ) {
271 
272  /* Describe token */
273  if ( ( rc = fdt_describe ( &sysfdt, offset, &desc ) ) != 0 ) {
274  DBGC ( &sysfdt, "DT %s has malformed node: %s\n",
275  parent->name, strerror ( rc ) );
276  goto err_describe;
277  }
278 
279  /* Terminate when we exit this node */
280  if ( ( depth == 0 ) && ( desc.depth < 0 ) )
281  break;
282 
283  /* Probe child node, if applicable */
284  if ( ( depth == 0 ) && desc.name && ( ! desc.data ) ) {
285  DBGC2 ( &sysfdt, "DT %s is child of %s\n",
286  desc.name, parent->name );
287  dt_probe_node ( &parent->dev, desc.offset );
288  }
289  }
290 
291  /* Fail if we have no children (so that this device will be freed) */
292  if ( list_empty ( &parent->dev.children ) ) {
293  rc = -ENODEV;
294  goto err_no_children;
295  }
296 
297  return 0;
298 
299  err_no_children:
300  err_describe:
301  dt_remove_children ( parent );
302  return rc;
303 }
304 
305 /**
306  * Remove devicetree children
307  *
308  * @v parent Parent device
309  */
310 void dt_remove_children ( struct dt_device *parent ) {
311 
312  /* Remove all child nodes */
313  while ( ! list_empty ( &parent->dev.children ) )
314  dt_remove_node ( &parent->dev );
315 }
316 
317 /** Generic node driver */
318 static struct dt_driver dt_node_driver __dt_driver = {
319  .name = "node",
320  .probe = dt_probe_children,
321  .remove = dt_remove_children,
322 };
323 
324 /**
325  * Probe devicetree bus
326  *
327  * @v rootdev Devicetree root device
328  * @ret rc Return status code
329  */
330 static int dt_probe_all ( struct root_device *rootdev ) {
331 
332  /* Probe root node */
333  return dt_probe_node ( &rootdev->dev, 0 );
334 }
335 
336 /**
337  * Remove devicetree bus
338  *
339  * @v rootdev Devicetree root device
340  */
341 static void dt_remove_all ( struct root_device *rootdev ) {
342 
343  /* Remove root node */
344  dt_remove_node ( &rootdev->dev );
345 }
346 
347 /** Devicetree bus root device driver */
348 static struct root_driver dt_root_driver = {
349  .probe = dt_probe_all,
350  .remove = dt_remove_all,
351 };
352 
353 /** Devicetree bus root device */
354 struct root_device dt_root_device __root_device = {
355  .dev = { .name = "DT" },
356  .driver = &dt_root_driver,
357 };
static void dt_remove(struct dt_device *dt)
Remove devicetree device.
Definition: devtree.c:180
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
const char * name
Definition: ath9k_hw.c:1984
int depth
Depth change.
Definition: fdt.h:132
static unsigned int unsigned int size_t uint8_t * ids
Definition: ena.h:770
void(* remove)(struct dt_device *dt)
Remove device.
Definition: devtree.h:51
void dt_remove_children(struct dt_device *parent)
Remove devicetree children.
Definition: devtree.c:310
Error codes.
const char ** ids
Compatible programming model identifiers.
Definition: devtree.h:35
A devicetree driver.
Definition: devtree.h:31
uint64_t address
Base address.
Definition: ena.h:24
uint16_t size
Buffer size.
Definition: dwmac.h:14
int fdt_describe(struct fdt *fdt, unsigned int offset, struct fdt_descriptor *desc)
Describe device tree token.
Definition: fdt.c:89
#define DBGC(...)
Definition: compiler.h:505
A device tree region cell size specification.
Definition: fdt.h:136
const char * fdt_strings(struct fdt *fdt, unsigned int offset, const char *name, unsigned int *count)
Find strings property.
Definition: fdt.c:539
char name[40]
Name.
Definition: device.h:78
long index
Definition: bigint.h:62
A root device.
Definition: device.h:97
unsigned long long uint64_t
Definition: stdint.h:13
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
struct device dev
Device chain.
Definition: device.h:102
#define list_last_entry(list, type, member)
Get the container of the last entry in a list.
Definition: list.h:346
struct device * parent
Bus device.
Definition: device.h:88
struct device dev
Generic device.
Definition: devtree.h:21
#define list_empty(list)
Test whether a list is empty.
Definition: list.h:136
#define list_del(list)
Delete an entry from a list.
Definition: list.h:119
struct ena_llq_option desc
Descriptor counts.
Definition: ena.h:20
int(* probe)(struct dt_device *dt, unsigned int offset)
Probe device.
Definition: devtree.h:45
#define ENOMEM
Not enough space.
Definition: errno.h:534
A hardware device.
Definition: device.h:76
uint32_t fdt_phandle(struct fdt *fdt, unsigned int offset)
Get package handle (phandle) property.
Definition: fdt.c:689
void dt_remove_node(struct device *parent)
Remove devicetree node.
Definition: devtree.c:241
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
Devicetree bus.
#define list_add_tail(new, head)
Add a new entry to the tail of a list.
Definition: list.h:93
ring len
Length.
Definition: dwmac.h:231
struct dt_driver * driver
Driver for this device.
Definition: devtree.h:25
A devicetree device.
Definition: devtree.h:17
static unsigned int count
Number of entries.
Definition: dwmac.h:225
static void dt_remove_all(struct root_device *rootdev)
Remove devicetree bus.
Definition: devtree.c:341
const char * driver_name
Driver name.
Definition: device.h:80
unsigned int location
Location.
Definition: device.h:29
uint8_t id
Request identifier.
Definition: ena.h:12
#define DT_DRIVERS
Devicetree driver table.
Definition: devtree.h:55
unsigned int id_count
Number of compatible programming model identifiers.
Definition: devtree.h:37
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
static void(* free)(struct refcnt *refcnt))
Definition: refcnt.h:54
void * zalloc(size_t size)
Allocate cleared memory.
Definition: malloc.c:661
struct list_head siblings
Devices on the same bus.
Definition: device.h:84
static struct dt_driver * dt_find_driver(struct dt_device *dt, unsigned int offset)
Find devicetree driver.
Definition: devtree.c:117
#define for_each_table_entry(pointer, table)
Iterate through all entries within a linker table.
Definition: tables.h:385
A root device driver.
Definition: device.h:110
#define ENODEV
No such device.
Definition: errno.h:509
size_t strlen(const char *src)
Get length of string.
Definition: string.c:243
int fdt_reg_address(struct fdt *fdt, unsigned int offset, struct fdt_reg_cells *regs, unsigned int index, uint64_t *address)
Get region address.
Definition: fdt.c:792
static int dt_probe(struct dt_device *dt, unsigned int offset)
Probe devicetree device.
Definition: devtree.c:154
int dt_probe_children(struct dt_device *parent, unsigned int offset)
Probe devicetree children.
Definition: devtree.c:264
int dt_probe_node(struct device *parent, unsigned int offset)
Probe devicetree node.
Definition: devtree.c:196
#define BUS_TYPE_DT
Devicetree bus type.
Definition: device.h:73
int(* probe)(struct root_device *rootdev)
Add root device.
Definition: device.h:119
static struct root_driver dt_root_driver
Devicetree bus root device driver.
Definition: devtree.c:348
struct i386_regs regs
Definition: registers.h:15
const char * name
Device name.
Definition: devtree.h:19
#define INIT_LIST_HEAD(list)
Initialise a list head.
Definition: list.h:45
unsigned int bus_type
Bus type.
Definition: device.h:24
const char * name
Driver name.
Definition: devtree.h:33
struct root_device dt_root_device __root_device
Devicetree bus root device.
Definition: devtree.c:354
#define DBGC2(...)
Definition: compiler.h:522
int strcmp(const char *first, const char *second)
Compare strings.
Definition: string.c:173
struct list_head children
Devices attached to this device.
Definition: device.h:86
Flattened Device Tree.
void * dt_ioremap(struct dt_device *dt, unsigned int offset, unsigned int index, size_t len)
Map devicetree range.
Definition: devtree.c:52
struct device_description desc
Device description.
Definition: device.h:82
Device model.
int snprintf(char *buf, size_t size, const char *fmt,...)
Write a formatted string to a buffer.
Definition: vsprintf.c:382
int fdt_parent_reg_cells(struct fdt *fdt, unsigned int offset, struct fdt_reg_cells *regs)
Get parent region cell size specification.
Definition: fdt.c:743
A device tree token descriptor.
Definition: fdt.h:120
uint16_t offset
Offset to command line.
Definition: bzimage.h:8
void * ioremap(unsigned long bus_addr, size_t len)
Map bus address as an I/O address.
static int dt_probe_all(struct root_device *rootdev)
Probe devicetree bus.
Definition: devtree.c:330
int fdt_reg_size(struct fdt *fdt, unsigned int offset, struct fdt_reg_cells *regs, unsigned int index, uint64_t *size)
Get region size.
Definition: fdt.c:817
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
String functions.
iPXE I/O mapping API
static struct dt_driver dt_node_driver __dt_driver
Generic node driver.
Definition: devtree.c:41
struct fdt sysfdt
The system flattened device tree (if present)
Definition: fdt.c:44