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
24FILE_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
41static 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 */
52void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
53 unsigned int index, size_t len ) {
54 struct fdt_reg_cells regs;
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 &&
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 */
117static 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 */
154static 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 */
180static 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 */
196int 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 */
241void 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 */
264int 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 */
310void 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 */
318static 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 */
330static 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 */
341static 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 */
349 .probe = dt_probe_all,
350 .remove = dt_remove_all,
351};
352
353/** Devicetree bus root device */
354struct root_device dt_root_device __root_device = {
355 .dev = { .name = "DT" },
356 .driver = &dt_root_driver,
357};
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
unsigned long long uint64_t
Definition stdint.h:13
long index
Definition bigint.h:65
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
const char * name
Definition ath9k_hw.c:1986
uint16_t offset
Offset to command line.
Definition bzimage.h:3
Device model.
#define __root_device
Declare a root device.
Definition device.h:136
#define BUS_TYPE_DT
Devicetree bus type.
Definition device.h:74
static int dt_probe_all(struct root_device *rootdev)
Probe devicetree bus.
Definition devtree.c:330
static void dt_remove(struct dt_device *dt)
Remove devicetree device.
Definition devtree.c:180
void * dt_ioremap(struct dt_device *dt, unsigned int offset, unsigned int index, size_t len)
Map devicetree range.
Definition devtree.c:52
int dt_probe_node(struct device *parent, unsigned int offset)
Probe devicetree node.
Definition devtree.c:196
static struct dt_driver * dt_find_driver(struct dt_device *dt, unsigned int offset)
Find devicetree driver.
Definition devtree.c:117
void dt_remove_node(struct device *parent)
Remove devicetree node.
Definition devtree.c:241
static struct root_driver dt_root_driver
Devicetree bus root device driver.
Definition devtree.c:348
static void dt_remove_all(struct root_device *rootdev)
Remove devicetree bus.
Definition devtree.c:341
int dt_probe_children(struct dt_device *parent, unsigned int offset)
Probe devicetree children.
Definition devtree.c:264
static int dt_probe(struct dt_device *dt, unsigned int offset)
Probe devicetree device.
Definition devtree.c:154
void dt_remove_children(struct dt_device *parent)
Remove devicetree children.
Definition devtree.c:310
Devicetree bus.
#define DT_DRIVERS
Devicetree driver table.
Definition devtree.h:61
#define __dt_driver
Declare a devicetree driver.
Definition devtree.h:64
ring len
Length.
Definition dwmac.h:226
uint8_t id
Request identifier.
Definition ena.h:1
static unsigned int unsigned int size_t uint8_t * ids
Definition ena.h:770
uint64_t address
Base address.
Definition ena.h:13
struct ena_llq_option desc
Descriptor counts.
Definition ena.h:9
Error codes.
int fdt_describe(struct fdt *fdt, unsigned int offset, struct fdt_descriptor *desc)
Describe device tree token.
Definition fdt.c:90
uint32_t fdt_phandle(struct fdt *fdt, unsigned int offset)
Get package handle (phandle) property.
Definition fdt.c:690
const char * fdt_strings(struct fdt *fdt, unsigned int offset, const char *name, unsigned int *count)
Find strings property.
Definition fdt.c:540
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:818
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:793
struct fdt sysfdt
The system flattened device tree (if present)
Definition fdt.c:45
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:744
#define DBGC2(...)
Definition compiler.h:522
#define DBGC(...)
Definition compiler.h:505
uint16_t size
Buffer size.
Definition dwmac.h:3
static unsigned int count
Number of entries.
Definition dwmac.h:220
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define ENOMEM
Not enough space.
Definition errno.h:535
#define ENODEV
No such device.
Definition errno.h:510
Flattened Device Tree.
iPXE I/O mapping API
void * ioremap(unsigned long bus_addr, size_t len)
Map bus address as an I/O address.
String functions.
#define list_last_entry(list, type, member)
Get the container of the last entry in a list.
Definition list.h:347
#define list_add_tail(new, head)
Add a new entry to the tail of a list.
Definition list.h:94
#define list_del(list)
Delete an entry from a list.
Definition list.h:120
#define INIT_LIST_HEAD(list)
Initialise a list head.
Definition list.h:46
#define list_empty(list)
Test whether a list is empty.
Definition list.h:137
void * zalloc(size_t size)
Allocate cleared memory.
Definition malloc.c:662
static void(* free)(struct refcnt *refcnt))
Definition refcnt.h:55
struct i386_regs regs
Definition registers.h:1
char * strerror(int errno)
Retrieve string representation of error number.
Definition strerror.c:79
int strcmp(const char *first, const char *second)
Compare strings.
Definition string.c:174
size_t strlen(const char *src)
Get length of string.
Definition string.c:244
unsigned int bus_type
Bus type.
Definition device.h:25
unsigned int location
Location.
Definition device.h:30
A hardware device.
Definition device.h:77
struct device_description desc
Device description.
Definition device.h:83
struct device * parent
Bus device.
Definition device.h:89
struct list_head children
Devices attached to this device.
Definition device.h:87
struct list_head siblings
Devices on the same bus.
Definition device.h:85
const char * driver_name
Driver name.
Definition device.h:81
char name[40]
Name.
Definition device.h:79
A devicetree device.
Definition devtree.h:17
struct dt_driver * driver
Driver for this device.
Definition devtree.h:25
struct device dev
Generic device.
Definition devtree.h:21
const char * name
Device name.
Definition devtree.h:19
A devicetree driver.
Definition devtree.h:37
int(* probe)(struct dt_device *dt, unsigned int offset)
Probe device.
Definition devtree.h:51
const char * name
Driver name.
Definition devtree.h:39
void(* remove)(struct dt_device *dt)
Remove device.
Definition devtree.h:57
unsigned int id_count
Number of compatible programming model identifiers.
Definition devtree.h:43
const char ** ids
Compatible programming model identifiers.
Definition devtree.h:41
A device tree token descriptor.
Definition fdt.h:121
int depth
Depth change.
Definition fdt.h:133
A device tree region cell size specification.
Definition fdt.h:137
A root device.
Definition device.h:98
struct device dev
Device chain.
Definition device.h:103
A root device driver.
Definition device.h:111
#define for_each_table_entry(pointer, table)
Iterate through all entries within a linker table.
Definition tables.h:386
int snprintf(char *buf, size_t size, const char *fmt,...)
Write a formatted string to a buffer.
Definition vsprintf.c:383