VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/fdt.cpp@ 103342

Last change on this file since 103342 was 103342, checked in by vboxsync, 10 months ago

IPRT/fdt.cpp: Missing va_end. Parfait bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 63.4 KB
Line 
1/* $Id: fdt.cpp 103342 2024-02-14 01:50:42Z vboxsync $ */
2/** @file
3 * IPRT - Flattened Devicetree parser and generator API.
4 */
5
6/*
7 * Copyright (C) 2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_FDT
42#include <iprt/fdt.h>
43
44#include <iprt/asm.h>
45#include <iprt/assert.h>
46#include <iprt/err.h>
47#include <iprt/file.h>
48#include <iprt/log.h>
49#include <iprt/mem.h>
50#include <iprt/sg.h>
51#include <iprt/string.h>
52#include <iprt/vfs.h>
53#include <iprt/zero.h>
54#include <iprt/formats/dtb.h>
55
56
57/*********************************************************************************************************************************
58* Defined Constants And Macros *
59*********************************************************************************************************************************/
60
61/** Special error token to denote that the end of the structs block was reached trying to query the next token. */
62#define RTFDT_TOKEN_ERROR UINT32_MAX
63
64
65/*********************************************************************************************************************************
66* Structures and Typedefs *
67*********************************************************************************************************************************/
68
69/**
70 * Internal Flattened Devicetree instance.
71 */
72typedef struct RTFDTINT
73{
74 /** Pointer to the string block. */
75 char *paszStrings;
76 /** Pointer to the raw structs block. */
77 uint8_t *pbStruct;
78 /** Pointer to the array of memory reservation entries. */
79 PDTBFDTRSVENTRY paMemRsv;
80 /** Number of memory reservation entries. */
81 uint32_t cMemRsv;
82 /** Maximum number of memory reservation entries allocated. */
83 uint32_t cMemRsvMax;
84 /** Size of the strings block. */
85 uint32_t cbStrings;
86 /** Allocated size of the string block. */
87 uint32_t cbStringsMax;
88 /** Size of the struct block. */
89 uint32_t cbStruct;
90 /** Allocated size of the struct block. */
91 uint32_t cbStructMax;
92 /** The physical boot CPU ID. */
93 uint32_t u32CpuIdPhysBoot;
94 /** Current tree depth in the structure block. */
95 uint32_t cTreeDepth;
96 /** The next free phandle value. */
97 uint32_t idPHandle;
98} RTFDTINT;
99/** Pointer to the internal Flattened Devicetree instance. */
100typedef RTFDTINT *PRTFDTINT;
101
102
103/**
104 * DTB property dump callback.
105 *
106 * @returns IPRT status code.
107 * @param pThis Pointer to the FDT instance.
108 * @param hVfsIos The VFS I/O stream handle to load the DTB from.
109 * @param pszProperty Property name.
110 * @param pvProperty Pointer to the raw property data.
111 * @param cbProperty Size of the property in bytes.
112 * @param pErrInfo Where to return additional error information.
113 */
114typedef DECLCALLBACKTYPE(int, FNRTFDTDTBPROPERTYDUMP,(PRTFDTINT pThis, RTVFSIOSTREAM hVfsIos, const char *pszProperty,
115 const void *pvProperty, size_t cbProperty, PRTERRINFO pErrInfo));
116/** Pointer to a DTB property dump callback. */
117typedef FNRTFDTDTBPROPERTYDUMP *PFNRTFDTDTBPROPERTYDUMP;
118
119
120/**
121 * DTB property dump descriptor.
122 */
123typedef struct RTFDTDTBPROPDUMPDESC
124{
125 /** Name of the property. */
126 const char *pszProperty;
127 /** The dump callback. */
128 PFNRTFDTDTBPROPERTYDUMP pfnDump;
129} RTFDTDTBPROPDUMPDESC;
130/** Pointer to a property dump descriptor. */
131typedef RTFDTDTBPROPDUMPDESC *PRTFDTDTBPROPDUMPDESC;
132/** Pointer to a const property dump descriptor. */
133typedef const RTFDTDTBPROPDUMPDESC *PCRTFDTDTBPROPDUMPDESC;
134
135
136/**
137 * DTB struct dump state.
138 */
139typedef struct RTFDTDTBDUMP
140{
141 /** Number of bytes left in the structs block to dump. */
142 size_t cbLeft;
143 /** Pointer to the next item in the structs block. */
144 const uint8_t *pbStructs;
145} RTFDTDTBDUMP;
146/** Pointer to a DTB struct dump state. */
147typedef RTFDTDTBDUMP *PRTFDTDTBDUMP;
148/** Pointer to a constant DTB struct dump state. */
149typedef const RTFDTDTBDUMP *PCRTFDTDTBDUMP;
150
151
152/*********************************************************************************************************************************
153* Global Variables *
154*********************************************************************************************************************************/
155
156
157/*********************************************************************************************************************************
158* Internal Functions *
159*********************************************************************************************************************************/
160#ifdef LOG_ENABLED
161
162/**
163 * Logs a FDT DTB header.
164 *
165 * @param pDtbHdr The FDT DTB header.
166 */
167static void rtFdtDtbHdr_Log(PCDTBFDTHDR pDtbHdr)
168{
169 if (LogIs2Enabled())
170 {
171 Log2(("RTFdt: Header:\n"));
172 Log2(("RTFdt: u32Magic %#RX32\n", RT_BE2H_U32(pDtbHdr->u32Magic)));
173 Log2(("RTFdt: cbFdt %#RX32\n", RT_BE2H_U32(pDtbHdr->cbFdt)));
174 Log2(("RTFdt: offDtStruct %#RX32\n", RT_BE2H_U32(pDtbHdr->offDtStruct)));
175 Log2(("RTFdt: offDtStrings %#RX32\n", RT_BE2H_U32(pDtbHdr->offDtStrings)));
176 Log2(("RTFdt: offMemRsvMap %#RX32\n", RT_BE2H_U32(pDtbHdr->offMemRsvMap)));
177 Log2(("RTFdt: u32Version %#RX32\n", RT_BE2H_U32(pDtbHdr->u32Version)));
178 Log2(("RTFdt: u32VersionLastCompatible %#RX32\n", RT_BE2H_U32(pDtbHdr->u32VersionLastCompatible)));
179 Log2(("RTFdt: u32CpuIdPhysBoot %#RX32\n", RT_BE2H_U32(pDtbHdr->u32CpuIdPhysBoot)));
180 Log2(("RTFdt: cbDtStrings %#RX32\n", RT_BE2H_U32(pDtbHdr->cbDtStrings)));
181 Log2(("RTFdt: cbDtStruct %#RX32\n", RT_BE2H_U32(pDtbHdr->cbDtStruct)));
182 }
183}
184
185#endif /* LOG_ENABLED */
186
187
188/**
189 * Validates the given header.
190 *
191 * @returns IPRT status code.
192 * @param pDtbHdr The DTB header to validate.
193 * @param cbDtb Size of the whole DTB in bytes.
194 * @param pErrInfo Where to return additional error information.
195 */
196static int rtFdtDtbHdr_Validate(PDTBFDTHDR pDtbHdr, uint64_t cbDtb, PRTERRINFO pErrInfo)
197{
198 /* Convert to host endianess first. */
199 pDtbHdr->u32Magic = RT_BE2H_U32(pDtbHdr->u32Magic);
200 pDtbHdr->cbFdt = RT_BE2H_U32(pDtbHdr->cbFdt);
201 pDtbHdr->offDtStruct = RT_BE2H_U32(pDtbHdr->offDtStruct);
202 pDtbHdr->offDtStrings = RT_BE2H_U32(pDtbHdr->offDtStrings);
203 pDtbHdr->offMemRsvMap = RT_BE2H_U32(pDtbHdr->offMemRsvMap);
204 pDtbHdr->u32Version = RT_BE2H_U32(pDtbHdr->u32Version);
205 pDtbHdr->u32VersionLastCompatible = RT_BE2H_U32(pDtbHdr->u32VersionLastCompatible);
206 pDtbHdr->u32CpuIdPhysBoot = RT_BE2H_U32(pDtbHdr->u32CpuIdPhysBoot);
207 pDtbHdr->cbDtStrings = RT_BE2H_U32(pDtbHdr->cbDtStrings);
208 pDtbHdr->cbDtStruct = RT_BE2H_U32(pDtbHdr->cbDtStruct);
209
210 if (pDtbHdr->u32Magic != DTBFDTHDR_MAGIC)
211 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_HDR_MAGIC_INVALID, "The magic of the DTB header is invalid (expected %#RX32, got %#RX32)",
212 DTBFDTHDR_MAGIC, pDtbHdr->u32Magic);
213 if (pDtbHdr->u32Version != DTBFDTHDR_VERSION)
214 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_HDR_VERSION_NOT_SUPPORTED, "Version %u of the DTB is not supported (supported is %u)",
215 pDtbHdr->u32Version, DTBFDTHDR_VERSION);
216 if (pDtbHdr->u32VersionLastCompatible != DTBFDTHDR_VERSION_LAST_COMP)
217 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_HDR_LAST_COMPAT_VERSION_INVALID, "Last compatible version %u of the DTB is invalid (expected %u)",
218 pDtbHdr->u32VersionLastCompatible, DTBFDTHDR_VERSION_LAST_COMP);
219 if (pDtbHdr->cbFdt != cbDtb)
220 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_HDR_SIZE_INVALID, "The size of the FDT is invalid (expected %RU32, got %RU32)",
221 cbDtb, pDtbHdr->cbFdt);
222
223 /*
224 * Check that any of the offsets is inside the bounds of the FDT and that the memory reservation block comes first,
225 * then the structs block and strings last.
226 */
227 if ( pDtbHdr->offMemRsvMap >= pDtbHdr->cbFdt
228 || pDtbHdr->offMemRsvMap < sizeof(*pDtbHdr))
229 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_HDR_MEM_RSV_BLOCK_OFF_INVALID, "Memory reservation block offset is out of bounds (offMemRsvMap=%#RX32 vs. cbFdt=%#%RX32)",
230 pDtbHdr->offMemRsvMap, pDtbHdr->cbFdt);
231 if ( pDtbHdr->offDtStruct >= pDtbHdr->cbFdt
232 || (pDtbHdr->cbFdt - pDtbHdr->offDtStruct < pDtbHdr->cbDtStruct)
233 || pDtbHdr->offDtStruct < pDtbHdr->offMemRsvMap + sizeof(DTBFDTRSVENTRY))
234 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_HDR_STRUCT_BLOCK_OFF_INVALID, "Structs block offset/size is out of bounds (offDtStruct=%#RX32 cbDtStruct=%#RX32 vs. cbFdt=%#RX32 offMemRsvMap=%#RX32)",
235 pDtbHdr->offDtStruct, pDtbHdr->cbDtStruct, pDtbHdr->cbFdt, pDtbHdr->offMemRsvMap);
236 if ( pDtbHdr->offDtStrings >= pDtbHdr->cbFdt
237 || (pDtbHdr->cbFdt - pDtbHdr->offDtStrings < pDtbHdr->cbDtStrings)
238 || pDtbHdr->offDtStrings < pDtbHdr->offDtStruct + pDtbHdr->cbDtStruct)
239 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_HDR_STRINGS_BLOCK_OFF_INVALID, "Strings block offset/size is out of bounds (offDtStrings=%#RX32 cbDtStrings=%#RX32 vs. cbFdt=%#%RX32 offDtStruct=%#RX32)",
240 pDtbHdr->offDtStrings, pDtbHdr->cbDtStrings, pDtbHdr->cbFdt, pDtbHdr->offDtStruct);
241
242 return VINF_SUCCESS;
243}
244
245
246/**
247 * Fres all resources allocated for the given FDT.
248 *
249 * @param pThis Pointer to the FDT instance to destroy.
250 */
251static void rtFdtDestroy(PRTFDTINT pThis)
252{
253 if (pThis->paszStrings)
254 RTMemFree(pThis->paszStrings);
255 if (pThis->pbStruct)
256 RTMemFree(pThis->pbStruct);
257 if (pThis->paMemRsv)
258 RTMemFree(pThis->paMemRsv);
259
260 pThis->paszStrings = NULL;
261 pThis->pbStruct = NULL;
262 pThis->paMemRsv = NULL;
263 pThis->cMemRsv = 0;
264 pThis->cMemRsvMax = 0;
265 pThis->cbStrings = 0;
266 pThis->cbStringsMax = 0;
267 pThis->cbStruct = 0;
268 pThis->cbStructMax = 0;
269 pThis->u32CpuIdPhysBoot = 0;
270 pThis->cTreeDepth = 0;
271 pThis->idPHandle = UINT32_MAX;
272 RTMemFree(pThis);
273}
274
275
276/**
277 * Loads the memory reservation block from the underlying VFS I/O stream.
278 *
279 * @returns IPRT status code.
280 * @param pThis Pointer to the FDT instance.
281 * @param pDtbHdr The DTB header.
282 * @param hVfsIos The VFS I/O stream handle to load the DTB from.
283 * @param pErrInfo Where to return additional error information.
284 */
285static int rtFdtDtbMemRsvLoad(PRTFDTINT pThis, PCDTBFDTHDR pDtbHdr, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
286{
287 AssertReturn(pDtbHdr->offMemRsvMap < pDtbHdr->offDtStruct, VERR_INTERNAL_ERROR);
288
289 uint32_t cMemRsvMax = (pDtbHdr->offDtStruct - pDtbHdr->offMemRsvMap) / sizeof(*pThis->paMemRsv);
290 Assert(cMemRsvMax);
291
292 pThis->paMemRsv = (PDTBFDTRSVENTRY)RTMemAllocZ(cMemRsvMax * sizeof(*pThis->paMemRsv));
293 if (!pThis->paMemRsv)
294 return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Failed to allocate %u bytes of memory for the memory reservation block",
295 cMemRsvMax * sizeof(*pThis->paMemRsv));
296
297 pThis->cMemRsvMax = cMemRsvMax;
298
299 /* Read the entries one after another until the terminator is reached. */
300 uint32_t cMemRsv = 0;
301 for (;;)
302 {
303 DTBFDTRSVENTRY MemRsv;
304 int rc = RTVfsIoStrmRead(hVfsIos, &MemRsv, sizeof(MemRsv), true /*fBlocking*/, NULL /*pcbRead*/);
305 if (RT_FAILURE(rc))
306 return RTErrInfoSetF(pErrInfo, rc, "Failed to read memory reservation entry %u from I/O stream",
307 cMemRsv);
308
309 /* Check whether the terminator is reached (no need to convert endianness here). */
310 if ( MemRsv.PhysAddrStart == 0
311 && MemRsv.cbArea == 0)
312 break;
313
314 cMemRsv++;
315
316 /*
317 * The terminator must be included in the maximum entry count, if not
318 * the DTB is malformed and lacks a terminating entry before the start of the structs block.
319 */
320 if (cMemRsv == cMemRsvMax)
321 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_MEM_RSV_BLOCK_TERMINATOR_MISSING,
322 "The memory reservation block lacks a terminating entry");
323
324 pThis->paMemRsv[cMemRsv - 1].PhysAddrStart = RT_BE2H_U64(MemRsv.PhysAddrStart);
325 pThis->paMemRsv[cMemRsv - 1].cbArea = RT_BE2H_U64(MemRsv.cbArea);
326 }
327
328 pThis->cMemRsv = cMemRsv;
329 return VINF_SUCCESS;
330}
331
332
333/**
334 * Loads the structs block of the given FDT.
335 *
336 * @returns IPRT status code.
337 * @param pThis Pointer to the FDT instance.
338 * @param pDtbHdr The DTB header.
339 * @param hVfsIos The VFS I/O stream handle to load the DTB from.
340 * @param pErrInfo Where to return additional error information.
341 */
342static int rtFdtDtbStructsLoad(PRTFDTINT pThis, PCDTBFDTHDR pDtbHdr, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
343{
344 pThis->pbStruct = (uint8_t *)RTMemAllocZ(pDtbHdr->cbDtStruct);
345 if (!pThis->pbStruct)
346 return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Failed to allocate %u bytes of memory for the structs block",
347 pDtbHdr->cbDtStruct);
348
349 int rc = RTVfsIoStrmReadAt(hVfsIos, pDtbHdr->offDtStruct, pThis->pbStruct, pDtbHdr->cbDtStruct,
350 true /*fBlocking*/, NULL /*pcbRead*/);
351 if (RT_FAILURE(rc))
352 return RTErrInfoSetF(pErrInfo, rc, "Failed to read structs block from I/O stream");
353
354 return VINF_SUCCESS;
355}
356
357
358/**
359 * Loads the strings block of the given FDT.
360 *
361 * @returns IPRT status code.
362 * @param pThis Pointer to the FDT instance.
363 * @param pDtbHdr The DTB header.
364 * @param hVfsIos The VFS I/O stream handle to load the DTB from.
365 * @param pErrInfo Where to return additional error information.
366 */
367static int rtFdtDtbStringsLoad(PRTFDTINT pThis, PCDTBFDTHDR pDtbHdr, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
368{
369 pThis->paszStrings = (char *)RTMemAllocZ(pDtbHdr->cbDtStrings);
370 if (!pThis->paszStrings)
371 return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Failed to allocate %u bytes of memory for the strings block",
372 pDtbHdr->cbDtStrings);
373
374 int rc = RTVfsIoStrmReadAt(hVfsIos, pDtbHdr->offDtStrings, pThis->paszStrings, pDtbHdr->cbDtStrings,
375 true /*fBlocking*/, NULL /*pcbRead*/);
376 if (RT_FAILURE(rc))
377 return RTErrInfoSetF(pErrInfo, rc, "Failed to read strings block from I/O stream");
378
379 /* Verify that the strings block is terminated. */
380 if (pThis->paszStrings[pDtbHdr->cbDtStrings - 1] != '\0')
381 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_STRINGS_BLOCK_NOT_TERMINATED, "The strings block is not zero terminated");
382
383 return VINF_SUCCESS;
384}
385
386
387/**
388 * Loads the DTB from the given VFS file.
389 *
390 * @returns IPRT status code.
391 * @param phFdt Where to return the FDT handle on success.
392 * @param hVfsIos The VFS I/O stream handle to load the DTB from.
393 * @param pErrInfo Where to return additional error information.
394 */
395static int rtFdtLoadDtb(PRTFDT phFdt, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
396{
397 DTBFDTHDR DtbHdr;
398 RTFSOBJINFO ObjInfo;
399 int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);
400 if (RT_SUCCESS(rc))
401 {
402 if ((uint64_t)ObjInfo.cbObject >= sizeof(DtbHdr) + sizeof(DTBFDTRSVENTRY))
403 {
404 rc = RTVfsIoStrmRead(hVfsIos, &DtbHdr, sizeof(DtbHdr), true /*fBlocking*/, NULL /*pcbRead*/);
405 if (RT_SUCCESS(rc))
406 {
407#ifdef LOG_ENABLED
408 rtFdtDtbHdr_Log(&DtbHdr);
409#endif
410
411 /* Validate the header. */
412 rc = rtFdtDtbHdr_Validate(&DtbHdr, ObjInfo.cbObject, pErrInfo);
413 if (RT_SUCCESS(rc))
414 {
415 PRTFDTINT pThis = (PRTFDTINT)RTMemAllocZ(sizeof(*pThis));
416 if (RT_LIKELY(pThis))
417 {
418 pThis->cbStrings = DtbHdr.cbDtStrings;
419 pThis->cbStruct = DtbHdr.cbDtStruct;
420 pThis->u32CpuIdPhysBoot = DtbHdr.u32CpuIdPhysBoot;
421 pThis->idPHandle = UINT32_MAX; /** @todo Would need to parse the whole tree to find the next free handle. */
422
423 /* Load the memory reservation block. */
424 rc = rtFdtDtbMemRsvLoad(pThis, &DtbHdr, hVfsIos, pErrInfo);
425 if (RT_SUCCESS(rc))
426 {
427 rc = rtFdtDtbStructsLoad(pThis, &DtbHdr, hVfsIos, pErrInfo);
428 if (RT_SUCCESS(rc))
429 {
430 rc = rtFdtDtbStringsLoad(pThis, &DtbHdr, hVfsIos, pErrInfo);
431 if (RT_SUCCESS(rc))
432 {
433 pThis->cbStringsMax = pThis->cbStrings;
434 pThis->cbStructMax = pThis->cbStruct;
435
436 *phFdt = pThis;
437 return VINF_SUCCESS;
438 }
439 }
440 }
441
442 rtFdtDestroy(pThis);
443 }
444 else
445 RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Failed to allocate memory for the FDT");
446 }
447 }
448 else
449 RTErrInfoSetF(pErrInfo, rc, "Failed to read %u bytes for the DTB header -> %Rrc",
450 sizeof(DtbHdr), rc);
451 }
452 else
453 RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_MALFORMED, "DTB is too small, needs at least %u bytes, only %u available",
454 sizeof(DtbHdr) + sizeof(DTBFDTRSVENTRY), ObjInfo.cbObject);
455 }
456 else
457 RTErrInfoSetF(pErrInfo, rc, "Failed to query size of the DTB -> %Rrc", rc);
458
459 return rc;
460}
461
462
463/**
464 * Returns the next token in the structs block from the given start returning an error
465 * if beyond the structs block.
466 *
467 * @returns Next token or RTFDT_TOKEN_ERROR if the end of the structs block is reached.
468 * @param pDump Pointer to the dump state.
469 */
470DECLINLINE(uint32_t) rtFdtStructsGetToken(PRTFDTDTBDUMP pDump)
471{
472 if (pDump->cbLeft < sizeof(uint32_t))
473 return RTFDT_TOKEN_ERROR;
474
475 uint32_t u32Token = *(const uint32_t *)pDump->pbStructs;
476 pDump->pbStructs += sizeof(uint32_t);
477 pDump->cbLeft -= sizeof(uint32_t);
478 return RT_H2BE_U32(u32Token);
479}
480
481
482#ifdef LOG_ENABLED
483/**
484 * Gets the offset inside the structs block given from the current pointer.
485 *
486 * @returns Offset in bytes from the start of the structs block.
487 * @param pThis Pointer to the FDT instance.
488 * @param pDump Pointer to the dump state.
489 */
490DECLINLINE(size_t) rtFdtStructsGetOffset(PRTFDTINT pThis, PCRTFDTDTBDUMP pDump)
491{
492 return pThis->cbStruct - pDump->cbLeft - sizeof(uint32_t);
493}
494#endif
495
496
497/**
498 * Advances the pointer inside the dump state by the given amount of bytes taking care of the alignment.
499 *
500 * @returns IPRT status code.
501 * @param pDump Pointer to the dump state.
502 * @param cbAdv How many bytes to advance.
503 * @param rcOob The status code to set if advancing goes beyond the end of the structs block.
504 */
505DECLINLINE(int) rtFdtStructsDumpAdvance(PRTFDTDTBDUMP pDump, uint32_t cbAdv, int rcOob)
506{
507 /* Align the pointer to the next 32-bit boundary. */
508 const uint8_t *pbStructsNew = RT_ALIGN_PT(pDump->pbStructs + cbAdv, sizeof(uint32_t), uint8_t *);
509 if (((uintptr_t)pbStructsNew - (uintptr_t)pDump->pbStructs) > pDump->cbLeft)
510 return rcOob;
511
512 pDump->pbStructs = pbStructsNew;
513 pDump->cbLeft -= (uintptr_t)pbStructsNew - (uintptr_t)pDump->pbStructs;
514 return VINF_SUCCESS;
515}
516
517
518/**
519 * Adds the proper indentation before a new line.
520 *
521 * @returns IPRT status code.
522 * @param hVfsIos The VFS I/O stream handle to dump the DTS to.
523 * @param uIndentLvl The level of indentation.
524 */
525static int rtFdtStructsDumpDtsIndent(RTVFSIOSTREAM hVfsIos, uint32_t uIndentLvl)
526{
527 while (uIndentLvl--)
528 {
529 ssize_t cch = RTVfsIoStrmPrintf(hVfsIos, " ");
530 if (cch != 4)
531 return cch < 0 ? -cch : VERR_BUFFER_UNDERFLOW;
532 }
533
534 return VINF_SUCCESS;
535}
536
537
538/**
539 * Queries a zero terminated ASCII string from the current location inside the structs block.
540 *
541 * @returns IPRT status code.
542 * @param pDump The dump state.
543 * @param pszString The string buffer to copy the string to.
544 * @param cchStringMax Maximum size of the string buffer in characters (including the terminator).
545 * @param pErrInfo Where to return additional error information.
546 */
547static int rtFdtStructsQueryString(PRTFDTDTBDUMP pDump, char *pszString, size_t cchStringMax, PRTERRINFO pErrInfo)
548{
549 const char *pszStrSrc = (const char *)pDump->pbStructs;
550 size_t cbLeft = pDump->cbLeft;
551
552 AssertReturn(cchStringMax, VERR_INTERNAL_ERROR);
553
554 for (;;)
555 {
556 *pszString++ = *pszStrSrc;
557 cchStringMax--;
558 cbLeft--;
559
560 if (*pszStrSrc == '\0')
561 {
562 pszStrSrc++;
563
564 int rc = rtFdtStructsDumpAdvance(pDump, (uintptr_t)pszStrSrc - (uintptr_t)pDump->pbStructs, VERR_FDT_DTB_STRUCTS_BLOCK_MALFORMED_PADDING);
565 if (RT_FAILURE(rc))
566 return RTErrInfoSetF(pErrInfo, rc, "String end + padding exceeds structs block");
567
568 return VINF_SUCCESS;
569 }
570
571 if (!cchStringMax)
572 return RTErrInfoSetF(pErrInfo, VERR_BUFFER_OVERFLOW, "Structs string too long to fit into target buffer");
573
574 *pszStrSrc++;
575 if (!cbLeft)
576 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_STRUCTS_BLOCK_STRING_NOT_TERMINATED, "Structs block contains an unterminated string");
577 }
578
579 /* Not reached */
580}
581
582
583/**
584 * Dumps a string list property.
585 *
586 * @returns IPRT status code.
587 * @param pThis Pointer to the FDT instance.
588 * @param hVfsIos The VFS I/O stream handle to dump the DTS to.
589 * @param pszProperty Name of the property being dumped.
590 * @param pvProperty Raw property payload.
591 * @param cbProperty Size of the property payload in bytes.
592 * @param pErrInfo Where to return additional error information.
593 */
594static DECLCALLBACK(int) rtFdtDtbPropDumpStringList(PRTFDTINT pThis, RTVFSIOSTREAM hVfsIos, const char *pszProperty,
595 const void *pvProperty, size_t cbProperty, PRTERRINFO pErrInfo)
596{
597 RT_NOREF(pThis);
598
599 const char *pszProp = (const char *)pvProperty;
600 if (pszProp[cbProperty - 1] != '\0')
601 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_PROP_STRING_NOT_TERMINATED,
602 "The string payload of property '%s' is not terminated", pszProperty);
603
604 ssize_t cch = RTVfsIoStrmPrintf(hVfsIos, "\"%s\"", pszProp);
605 if (cch <= 0)
606 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write property data");
607
608 cch = strlen(pszProp) + 1;
609 cbProperty -= cch;
610 while (cbProperty)
611 {
612 pszProp += cch;
613
614 cch = RTVfsIoStrmPrintf(hVfsIos, ", \"%s\"", pszProp);
615 if (cch <= 0)
616 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write property data");
617
618 cch = strlen(pszProp) + 1;
619 cbProperty -= cch;
620 }
621
622 return VINF_SUCCESS;
623}
624
625
626/**
627 * Dumps a string property.
628 *
629 * @returns IPRT status code.
630 * @param pThis Pointer to the FDT instance.
631 * @param hVfsIos The VFS I/O stream handle to dump the DTS to.
632 * @param pszProperty Name of the property being dumped.
633 * @param pvProperty Raw property payload.
634 * @param cbProperty Size of the property payload in bytes.
635 * @param pErrInfo Where to return additional error information.
636 */
637static DECLCALLBACK(int) rtFdtDtbPropDumpString(PRTFDTINT pThis, RTVFSIOSTREAM hVfsIos, const char *pszProperty,
638 const void *pvProperty, size_t cbProperty, PRTERRINFO pErrInfo)
639{
640 RT_NOREF(pThis);
641
642 const char *pszProp = (const char *)pvProperty;
643 if (pszProp[cbProperty - 1] != '\0')
644 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_PROP_STRING_NOT_TERMINATED,
645 "The string payload of property '%s' is not terminated", pszProperty);
646
647 ssize_t cch = RTVfsIoStrmPrintf(hVfsIos, "\"%s\"", pszProp);
648 if (cch <= 0)
649 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write property data");
650
651 return VINF_SUCCESS;
652}
653
654
655/**
656 * Dumps a u32 cell property.
657 *
658 * @returns IPRT status code.
659 * @param pThis Pointer to the FDT instance.
660 * @param hVfsIos The VFS I/O stream handle to dump the DTS to.
661 * @param pszProperty Name of the property being dumped.
662 * @param pvProperty Raw property payload.
663 * @param cbProperty Size of the property payload in bytes.
664 * @param pErrInfo Where to return additional error information.
665 */
666static DECLCALLBACK(int) rtFdtDtbPropDumpCellsU32(PRTFDTINT pThis, RTVFSIOSTREAM hVfsIos, const char *pszProperty,
667 const void *pvProperty, size_t cbProperty, PRTERRINFO pErrInfo)
668{
669 RT_NOREF(pThis);
670
671 if (cbProperty % sizeof(uint32_t))
672 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_PROP_SIZE_MALFORMED,
673 "Property '%s' payload is not a multiple of 32-bit", pszProperty);
674
675 const uint32_t *pu32Prop = (const uint32_t *)pvProperty;
676
677 ssize_t cch = RTVfsIoStrmPrintf(hVfsIos, "<");
678 if (cch == 1)
679 {
680 cch = RTVfsIoStrmPrintf(hVfsIos, "%#RX32", RT_BE2H_U32(*pu32Prop));
681 pu32Prop++;
682 if (cch > 0)
683 {
684 for (uint32_t i = 1; i < cbProperty / sizeof(uint32_t); i++)
685 {
686 cch = RTVfsIoStrmPrintf(hVfsIos, " %#RX32", RT_BE2H_U32(*pu32Prop));
687 pu32Prop++;
688 if (cch <= 0)
689 break;
690 }
691 }
692
693 if (cch > 0)
694 cch = RTVfsIoStrmPrintf(hVfsIos, ">");
695 }
696
697 if (cch <= 0)
698 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write property data");
699
700 return VINF_SUCCESS;
701}
702
703
704/**
705 * The known properties to dump.
706 */
707static const RTFDTDTBPROPDUMPDESC g_aPropDump[] =
708{
709 { "compatible", rtFdtDtbPropDumpStringList }, /** @todo stringlist */
710 { "model", rtFdtDtbPropDumpString },
711 { "status", rtFdtDtbPropDumpString },
712 { "phandle", rtFdtDtbPropDumpCellsU32 },
713 { "linux,phandle", rtFdtDtbPropDumpCellsU32 },
714 { "#address-cells", rtFdtDtbPropDumpCellsU32 },
715 { "#size-cells", rtFdtDtbPropDumpCellsU32 },
716 { "reg", rtFdtDtbPropDumpCellsU32 },
717 { "virtual-reg", rtFdtDtbPropDumpCellsU32 },
718 { "ranges", rtFdtDtbPropDumpCellsU32 },
719 { "dma-ranges", rtFdtDtbPropDumpCellsU32 },
720 { "name", rtFdtDtbPropDumpString },
721 { "device_type", rtFdtDtbPropDumpString },
722 { "interrupts", rtFdtDtbPropDumpCellsU32 },
723 { "interrupt-parent", rtFdtDtbPropDumpCellsU32 },
724 { "interrupts-extended", rtFdtDtbPropDumpCellsU32 },
725 { "#interrupt-cells", rtFdtDtbPropDumpCellsU32 },
726 { "interrupt-map", rtFdtDtbPropDumpCellsU32 },
727 { "interrupt-map-mask", rtFdtDtbPropDumpCellsU32 },
728 { "serial-number", rtFdtDtbPropDumpString },
729 { "chassis-type", rtFdtDtbPropDumpString },
730 { "clock-frequency", rtFdtDtbPropDumpCellsU32 },
731 { "reg-shift", rtFdtDtbPropDumpCellsU32 },
732 { "label", rtFdtDtbPropDumpString },
733 { "clock-names", rtFdtDtbPropDumpStringList },
734 { "clock-output-names", rtFdtDtbPropDumpStringList },
735 { "stdout-path", rtFdtDtbPropDumpString },
736 { "method", rtFdtDtbPropDumpString },
737};
738
739
740/**
741 * Dumps the property as a DTS source starting at the given location inside the structs block.
742 *
743 * @returns IPRT status code.
744 * @param pThis Pointer to the FDT instance.
745 * @param hVfsIos The VFS I/O stream handle to dump the DTS to.
746 * @param pDump The dump state.
747 * @param uIndentLvl The level of indentation.
748 * @param pErrInfo Where to return additional error information.
749 */
750static int rtFdtStructsDumpPropertyAsDts(PRTFDTINT pThis, RTVFSIOSTREAM hVfsIos, PRTFDTDTBDUMP pDump, uint32_t uIndentLvl, PRTERRINFO pErrInfo)
751{
752 DTBFDTPROP Prop;
753
754 if (pDump->cbLeft < sizeof(Prop))
755 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_STRUCTS_BLOCK_PREMATURE_END,
756 "Not enough space left in the structs block to read a property entry");
757
758 memcpy(&Prop, pDump->pbStructs, sizeof(Prop));
759 pDump->pbStructs += sizeof(Prop);
760 pDump->cbLeft -= sizeof(Prop);
761 Prop.offPropertyName = RT_BE2H_U32(Prop.offPropertyName);
762 Prop.cbProperty = RT_BE2H_U32(Prop.cbProperty);
763
764 if (Prop.offPropertyName >= pThis->cbStrings)
765 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_PROP_NAME_OFF_TOO_LARGE, "Property name offset points past the string block");
766
767 int rc = rtFdtStructsDumpDtsIndent(hVfsIos, uIndentLvl);
768 if (RT_FAILURE(rc))
769 return rc;
770
771 ssize_t cch = RTVfsIoStrmPrintf(hVfsIos, "%s", &pThis->paszStrings[Prop.offPropertyName]);
772 if (cch <= 0)
773 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write property name: '%s'",
774 &pThis->paszStrings[Prop.offPropertyName]);
775
776 const uint8_t *pbProp = pDump->pbStructs;
777 if (Prop.cbProperty)
778 {
779 if (Prop.cbProperty > pDump->cbLeft)
780 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_STRUCTS_BLOCK_PREMATURE_END, "Property '%s' data exceeds struct blocks",
781 &pThis->paszStrings[Prop.offPropertyName]);
782
783 cch = RTVfsIoStrmPrintf(hVfsIos, " = ");
784 if (cch <= 0)
785 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to output property");
786
787 rc = VERR_NOT_FOUND;
788 for (uint32_t i = 0; i < RT_ELEMENTS(g_aPropDump); i++)
789 {
790 if (!strcmp(g_aPropDump[i].pszProperty, &pThis->paszStrings[Prop.offPropertyName]))
791 {
792 rc = g_aPropDump[i].pfnDump(pThis, hVfsIos, &pThis->paszStrings[Prop.offPropertyName],
793 pbProp, Prop.cbProperty, pErrInfo);
794 break;
795 }
796 }
797
798 /* If not found use the standard U32 cells dumper. */
799 if (rc == VERR_NOT_FOUND)
800 rc = rtFdtDtbPropDumpCellsU32(pThis, hVfsIos, &pThis->paszStrings[Prop.offPropertyName],
801 pbProp, Prop.cbProperty, pErrInfo);
802 if (RT_FAILURE(rc))
803 return rc;
804
805 cch = RTVfsIoStrmPrintf(hVfsIos, ";\n");
806 if (cch <= 0)
807 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to output property");
808
809 rc = rtFdtStructsDumpAdvance(pDump, Prop.cbProperty, VERR_FDT_DTB_STRUCTS_BLOCK_PREMATURE_END);
810 }
811 else
812 {
813 cch = RTVfsIoStrmPrintf(hVfsIos, ";\n");
814 if (cch <= 0)
815 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write newline");
816 }
817
818 return rc;
819}
820
821
822/**
823 * Dumps the node name as a DTS source starting at the given location inside the structs block.
824 *
825 * @returns IPRT status code.
826 * @param hVfsIos The VFS I/O stream handle to dump the DTS to.
827 * @param pDump The dump state.
828 * @param uIndentLvl The level of indentation.
829 * @param pErrInfo Where to return additional error information.
830 */
831static int rtFdtStructsDumpNodeAsDts(RTVFSIOSTREAM hVfsIos, PRTFDTDTBDUMP pDump, uint32_t uIndentLvl, PRTERRINFO pErrInfo)
832{
833 char szNdName[512]; /* Should be plenty. */
834
835 int rc = rtFdtStructsQueryString(pDump, &szNdName[0], sizeof(szNdName), pErrInfo);
836 if (RT_FAILURE(rc))
837 return rc;
838
839 ssize_t cch = RTVfsIoStrmPrintf(hVfsIos, "\n", szNdName);
840 if (cch <= 0)
841 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write DTS node name");
842
843 rc = rtFdtStructsDumpDtsIndent(hVfsIos, uIndentLvl);
844 if (RT_FAILURE(rc))
845 return rc;
846
847 cch = RTVfsIoStrmPrintf(hVfsIos, "%s {\n", szNdName);
848 if (cch <= 0)
849 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write DTS node name");
850
851 return VINF_SUCCESS;
852}
853
854
855static int rtFdtStructsDumpEndNodeAsDts(RTVFSIOSTREAM hVfsIos, uint32_t uIndentLvl, PRTERRINFO pErrInfo)
856{
857 int rc = rtFdtStructsDumpDtsIndent(hVfsIos, uIndentLvl);
858 if (RT_FAILURE(rc))
859 return rc;
860
861 ssize_t cch = RTVfsIoStrmPrintf(hVfsIos, "};\n");
862 if (cch <= 0)
863 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write DTS node closing");
864
865 return VINF_SUCCESS;
866}
867
868
869/**
870 * Dumps the given FDT as DTS v1 sources from the root node.
871 *
872 * @returns IPRT status code.
873 * @param pThis Pointer to the FDT instance.
874 * @param hVfsIos The VFS I/O stream handle to dump the DTS to.
875 * @param pErrInfo Where to return additional error information.
876 */
877static int rtFdtDumpRootAsDts(PRTFDTINT pThis, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
878{
879 RTFDTDTBDUMP Dump;
880
881 Dump.cbLeft = pThis->cbStruct;
882 Dump.pbStructs = pThis->pbStruct;
883
884 /* Skip any NOP tokens. */
885 uint32_t u32Token = rtFdtStructsGetToken(&Dump);
886 while (u32Token == DTB_FDT_TOKEN_NOP)
887 u32Token = rtFdtStructsGetToken(&Dump);
888
889 /* The root node starts with a BEGIN_NODE token. */
890 if (u32Token != DTB_FDT_TOKEN_BEGIN_NODE)
891 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_STRUCTS_BLOCK_TOKEN_INVALID, "The structs block doesn't start with the BEGIN_NODE token for the root node: %#RX32",
892 RT_BE2H_U32(u32Token));
893
894 /* Load the name for the root node (should be an empty string). */
895 char chNdRootName;
896 int rc = rtFdtStructsQueryString(&Dump, &chNdRootName, sizeof(chNdRootName), pErrInfo);
897 if (RT_FAILURE(rc))
898 return rc;
899
900 if (chNdRootName != '\0')
901 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_STRUCTS_BLOCK_NODE_NAME_INVALID, "The root node name isn't zero terminated: %c",
902 chNdRootName);
903
904 ssize_t cch = RTVfsIoStrmPrintf(hVfsIos, "/ {\n");
905 if (cch <= 0)
906 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write DTS root node");
907
908 uint32_t uNdLvl = 1;
909 u32Token = rtFdtStructsGetToken(&Dump);
910 while (u32Token != DTB_FDT_TOKEN_END)
911 {
912 Log4(("rtFdtDumpAsDtsRoot: Token %#RX32 at offset %#zx\n", RT_BE2H_U32(u32Token), rtFdtStructsGetOffset(pThis, &Dump)));
913
914 switch (u32Token)
915 {
916 case DTB_FDT_TOKEN_BEGIN_NODE:
917 Log3(("rtFdtDumpAsDtsRoot: BEGIN_NODE token at offset %#zx\n", rtFdtStructsGetOffset(pThis, &Dump)));
918 rc = rtFdtStructsDumpNodeAsDts(hVfsIos, &Dump, uNdLvl, pErrInfo);
919 if (RT_FAILURE(rc))
920 return rc;
921
922 uNdLvl++;
923 break;
924 case DTB_FDT_TOKEN_PROPERTY:
925 Log3(("rtFdtDumpAsDtsRoot: PROP token at offset %#zx\n", rtFdtStructsGetOffset(pThis, &Dump)));
926 rc = rtFdtStructsDumpPropertyAsDts(pThis, hVfsIos, &Dump, uNdLvl, pErrInfo);
927 if (RT_FAILURE(rc))
928 return rc;
929 break;
930 case DTB_FDT_TOKEN_NOP:
931 Log3(("rtFdtDumpAsDtsRoot: NOP token at offset %#zx\n", rtFdtStructsGetOffset(pThis, &Dump)));
932 break;
933 case DTB_FDT_TOKEN_END_NODE:
934 Log3(("rtFdtDumpAsDtsRoot: END_NODE token at offset %#zx\n", rtFdtStructsGetOffset(pThis, &Dump)));
935 if (!uNdLvl)
936 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_STRUCTS_BLOCK_PREMATURE_END,
937 "END_NODE token encountered at the root node");
938
939 uNdLvl--;
940 rc = rtFdtStructsDumpEndNodeAsDts(hVfsIos, uNdLvl, pErrInfo);
941 if (RT_FAILURE(rc))
942 return rc;
943 break;
944 case RTFDT_TOKEN_ERROR:
945 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_STRUCTS_BLOCK_TOKEN_INVALID, "The structs block is malformed");
946 default:
947 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_STRUCTS_BLOCK_TOKEN_INVALID, "The structs block contains an invalid/unknown token: %#RX32",
948 RT_BE2H_U32(u32Token));
949 }
950
951 u32Token = rtFdtStructsGetToken(&Dump);
952 if (u32Token == DTB_FDT_TOKEN_END)
953 Log3(("rtFdtDumpAsDtsRoot: END token at offset %#zx\n", rtFdtStructsGetOffset(pThis, &Dump)));
954 }
955
956 /* Need to end on an END token. */
957 if (u32Token != DTB_FDT_TOKEN_END)
958 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_STRUCTS_BLOCK_TOKEN_INVALID, "The structs block doesn't end with an END token (got %#RX32, expected %#RX32)",
959 RT_BE2H_U32(u32Token), DTB_FDT_TOKEN_END);
960
961 return VINF_SUCCESS;
962}
963
964
965/**
966 * Dumps the given FDT instance as DTS source.
967 *
968 * @returns IPRT status code.
969 * @param pThis Pointer to the FDT instance.
970 * @param hVfsIos The VFS I/O stream handle to dump the DTS to.
971 * @param pErrInfo Where to return additional error information.
972 */
973static int rtFdtDumpAsDts(PRTFDTINT pThis, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
974{
975 ssize_t cch = RTVfsIoStrmPrintf(hVfsIos, "/dts-v1/;\n");
976 if (cch <= 0)
977 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write DTS header");
978
979 /* Write memory reservations. */
980 for (uint32_t i = 0; i < pThis->cMemRsv; i++)
981 {
982 cch = RTVfsIoStrmPrintf(hVfsIos, "/memreserve/ %#RX64 %#RX64;\n", pThis->paMemRsv[i].PhysAddrStart, pThis->paMemRsv[i].cbArea);
983 if (cch <= 0)
984 return RTErrInfoSetF(pErrInfo, cch == 0 ? VERR_NO_MEMORY : -cch, "Failed to write memory reservation block %u", i);
985 }
986
987 /* Dump the tree. */
988 return rtFdtDumpRootAsDts(pThis, hVfsIos, pErrInfo);
989}
990
991
992/**
993 * Adds the zero padding to align the next block properly.
994 *
995 * @returns IPRT status code.
996 * @param hVfsIos The VFS I/O stream handle to dump the DTB to.
997 * @param cbPad How many bytes to pad.
998 */
999static int rtFdtDumpAsDtbPad(RTVFSIOSTREAM hVfsIos, uint32_t cbPad)
1000{
1001 if (!cbPad)
1002 return VINF_SUCCESS;
1003
1004 while (cbPad)
1005 {
1006 uint32_t cbThisPad = RT_MIN(cbPad, _4K);
1007 int rc = RTVfsIoStrmWrite(hVfsIos, &g_abRTZero4K[0], cbThisPad, true /*fBlocking*/, NULL /*pcbWritten*/);
1008 if (RT_FAILURE(rc))
1009 return rc;
1010
1011 cbPad -= cbThisPad;
1012 }
1013
1014 return VINF_SUCCESS;
1015}
1016
1017
1018/**
1019 * Dumps the given FDT instance as a DTB (Devicetree blob).
1020 *
1021 * @returns IPRT status code.
1022 * @param pThis Pointer to the FDT instance.
1023 * @param hVfsIos The VFS I/O stream handle to dump the DTB to.
1024 * @param pErrInfo Where to return additional error information.
1025 */
1026static int rtFdtDumpAsDtb(PRTFDTINT pThis, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
1027{
1028 DTBFDTHDR Hdr;
1029
1030 /* ensure the FDT is finalized. */
1031 int rc = RTFdtFinalize(pThis);
1032 if (RT_FAILURE(rc))
1033 return rc;
1034
1035 uint32_t offMemRsvMap = RT_ALIGN_32(sizeof(Hdr), sizeof(uint64_t));
1036 uint32_t offDtStruct = RT_ALIGN_32(offMemRsvMap + (pThis->cMemRsv + 1) * sizeof(DTBFDTRSVENTRY), sizeof(uint32_t));
1037 uint32_t offDtStrings = offDtStruct + pThis->cbStruct;
1038 uint32_t cbFdt = offDtStrings + pThis->cbStrings;
1039 uint32_t cbCur = 0;
1040
1041 Hdr.u32Magic = RT_H2BE_U32(DTBFDTHDR_MAGIC);
1042 Hdr.cbFdt = RT_H2BE_U32(cbFdt);
1043 Hdr.offDtStruct = RT_H2BE_U32(offDtStruct);
1044 Hdr.offDtStrings = RT_H2BE_U32(offDtStrings);
1045 Hdr.offMemRsvMap = RT_H2BE_U32(offMemRsvMap);
1046 Hdr.u32Version = RT_H2BE_U32(DTBFDTHDR_VERSION);
1047 Hdr.u32VersionLastCompatible = RT_H2BE_U32(DTBFDTHDR_VERSION_LAST_COMP);
1048 Hdr.u32CpuIdPhysBoot = RT_H2BE_U32(pThis->u32CpuIdPhysBoot);
1049 Hdr.cbDtStrings = RT_H2BE_U32(pThis->cbStrings);
1050 Hdr.cbDtStruct = RT_H2BE_U32(pThis->cbStruct);
1051
1052 /* Write out header, memory reservation block, struct block and strings block all with appropriate padding. */
1053 rc = RTVfsIoStrmWrite(hVfsIos, &Hdr, sizeof(Hdr), true /*fBlocking*/, NULL /*pcbWritten*/);
1054 if (RT_FAILURE(rc))
1055 return RTErrInfoSetF(pErrInfo, rc, "Failed to write DTB header (%u bytes) to I/O stream", sizeof(Hdr));
1056
1057 cbCur += sizeof(Hdr);
1058 rc = rtFdtDumpAsDtbPad(hVfsIos, offMemRsvMap - cbCur);
1059 if (RT_FAILURE(rc))
1060 return RTErrInfoSetF(pErrInfo, rc, "Failed to write padding after DTB header (%u bytes) to I/O stream",
1061 offMemRsvMap - cbCur);
1062 cbCur += offMemRsvMap - cbCur;
1063
1064 /* Write memory reservation blocks. */
1065 for (uint32_t i = 0; i < pThis->cMemRsv; i++)
1066 {
1067 DTBFDTRSVENTRY MemRsv;
1068
1069 MemRsv.PhysAddrStart = RT_H2BE_U64(pThis->paMemRsv[i].PhysAddrStart);
1070 MemRsv.cbArea = RT_H2BE_U64(pThis->paMemRsv[i].cbArea);
1071 rc = RTVfsIoStrmWrite(hVfsIos, &MemRsv, sizeof(MemRsv), true /*fBlocking*/, NULL /*pcbWritten*/);
1072 if (RT_FAILURE(rc))
1073 return RTErrInfoSetF(pErrInfo, rc, "Failed to write memory reservation entry %u (%u bytes) to I/O stream",
1074 i, sizeof(MemRsv));
1075 cbCur += sizeof(MemRsv);
1076 }
1077
1078 /* Always write terminating entry. */
1079 DTBFDTRSVENTRY RsvTerm;
1080 RsvTerm.PhysAddrStart = RT_H2BE_U32(0); /* Yeah, pretty useful endianess conversion I know */
1081 RsvTerm.cbArea = RT_H2BE_U32(0);
1082 rc = RTVfsIoStrmWrite(hVfsIos, &RsvTerm, sizeof(RsvTerm), true /*fBlocking*/, NULL /*pcbWritten*/);
1083 if (RT_FAILURE(rc))
1084 return RTErrInfoSetF(pErrInfo, rc, "Failed to write terminating memory reservation entry (%u bytes) to I/O stream",
1085 sizeof(RsvTerm));
1086 cbCur += sizeof(RsvTerm);
1087
1088 rc = rtFdtDumpAsDtbPad(hVfsIos, offDtStruct - cbCur);
1089 if (RT_FAILURE(rc))
1090 return RTErrInfoSetF(pErrInfo, rc, "Failed to write padding after memory reservation block (%u bytes) to I/O stream",
1091 offDtStruct - cbCur);
1092 cbCur += offDtStruct - cbCur;
1093
1094 /* Write struct block. */
1095 rc = RTVfsIoStrmWrite(hVfsIos, pThis->pbStruct, pThis->cbStruct, true /*fBlocking*/, NULL /*pcbWritten*/);
1096 if (RT_FAILURE(rc))
1097 return RTErrInfoSetF(pErrInfo, rc, "Failed to write struct block (%u bytes) to I/O stream",
1098 pThis->cbStruct);
1099 cbCur += pThis->cbStruct;
1100
1101 rc = rtFdtDumpAsDtbPad(hVfsIos, offDtStrings - cbCur);
1102 if (RT_FAILURE(rc))
1103 return RTErrInfoSetF(pErrInfo, rc, "Failed to write padding after structs block (%u bytes) to I/O stream",
1104 offDtStrings - cbCur);
1105 cbCur += offDtStrings - cbCur;
1106
1107 /* Write strings block. */
1108 rc = RTVfsIoStrmWrite(hVfsIos, pThis->paszStrings, pThis->cbStrings, true /*fBlocking*/, NULL /*pcbWritten*/);
1109 if (RT_FAILURE(rc))
1110 return RTErrInfoSetF(pErrInfo, rc, "Failed to write strings block (%u bytes) to I/O stream",
1111 pThis->cbStrings);
1112 cbCur += pThis->cbStrings;
1113
1114 rc = rtFdtDumpAsDtbPad(hVfsIos, cbFdt - cbCur);
1115 if (RT_FAILURE(rc))
1116 return RTErrInfoSetF(pErrInfo, rc, "Failed to write padding after strings block (%u bytes) to I/O stream",
1117 cbFdt - cbCur);
1118 cbCur += cbFdt - cbCur;
1119
1120 Assert(cbCur == cbFdt);
1121 return VINF_SUCCESS;
1122}
1123
1124
1125/**
1126 * Ensures there is enough space in the structure block, allocating more if required.
1127 *
1128 * @returns IPRT status code.
1129 * @param pThis Pointer to the FDT instance.
1130 * @param cch Number of characters required, including the zero terminator.
1131 */
1132static int rtFdtStringsEnsureSpace(PRTFDTINT pThis, uint32_t cch)
1133{
1134 if (pThis->cbStringsMax - pThis->cbStrings >= cch)
1135 return VINF_SUCCESS;
1136
1137 uint32_t cbNew = RT_ALIGN_32(pThis->cbStrings + cch, 256);
1138 void *pvStringsNew = RTMemReallocZ(pThis->paszStrings, pThis->cbStringsMax, cbNew);
1139 if (!pvStringsNew)
1140 return VERR_NO_MEMORY;
1141
1142 Assert(cbNew - pThis->cbStrings >= cch);
1143 pThis->paszStrings = (char *)pvStringsNew;
1144 pThis->cbStringsMax = cbNew;
1145 return VINF_SUCCESS;
1146}
1147
1148
1149/**
1150 * Looks for the given string in the strings block appending it if not found, returning
1151 * the offset of the occurrence.
1152 *
1153 * @returns IPRT status code.
1154 * @param pThis Pointer to the FDT instance.
1155 * @param psz The string to insert.
1156 * @param poffStr Where to store the offset of the start of string from the beginning
1157 * of the strings block on success.
1158 */
1159static int rtFdtStringsInsertString(PRTFDTINT pThis, const char *psz, uint32_t *poffStr)
1160{
1161 uint32_t off = 0;
1162 while (off < pThis->cbStrings)
1163 {
1164 if (!RTStrCmp(psz, &pThis->paszStrings[off]))
1165 {
1166 *poffStr = off;
1167 return VINF_SUCCESS;
1168 }
1169
1170 /** @todo Optimize? The strings block is not very large though so probably not worth the effort. */
1171 off += (uint32_t)strlen(&pThis->paszStrings[off]) + 1;
1172 }
1173
1174 /* Not found, need to insert. */
1175 uint32_t cch = (uint32_t)strlen(psz) + 1;
1176 int rc = rtFdtStringsEnsureSpace(pThis, cch);
1177 if (RT_FAILURE(rc))
1178 return rc;
1179
1180 memcpy(&pThis->paszStrings[off], psz, cch);
1181 pThis->cbStrings += cch;
1182 *poffStr = off;
1183 return VINF_SUCCESS;
1184}
1185
1186
1187/**
1188 * Ensures there is enough space in the structure block, allocating more if required.
1189 *
1190 * @returns IPRT status code.
1191 * @param pThis Pointer to the FDT instance.
1192 * @param cbSpaceRequired Number of bytes required.
1193 */
1194static int rtFdtStructEnsureSpace(PRTFDTINT pThis, uint32_t cbSpaceRequired)
1195{
1196 if (pThis->cbStructMax - pThis->cbStruct >= cbSpaceRequired)
1197 return VINF_SUCCESS;
1198
1199 uint32_t cbNew = RT_ALIGN_32(pThis->cbStruct + cbSpaceRequired, _1K);
1200 Assert(cbNew > pThis->cbStructMax);
1201 void *pvStructNew = RTMemReallocZ(pThis->pbStruct, pThis->cbStructMax, cbNew);
1202 if (!pvStructNew)
1203 return VERR_NO_MEMORY;
1204
1205 Assert(cbNew - pThis->cbStruct >= cbSpaceRequired);
1206 pThis->pbStruct = (uint8_t *)pvStructNew;
1207 pThis->cbStructMax = cbNew;
1208 return VINF_SUCCESS;
1209}
1210
1211
1212/**
1213 * Appends the given token and payload data to the structure block taking care of aligning the data.
1214 *
1215 * @returns IPRT status code.
1216 * @param pThis Pointer to the FDT instance.
1217 * @param pSgBuf The S/G buffer to append.
1218 * @param cbAppend How many bytes to append.
1219 */
1220static int rtFdtStructAppendSg(PRTFDTINT pThis, PRTSGBUF pSgBuf, uint32_t cbAppend)
1221{
1222 uint32_t cbAppendAligned = RT_ALIGN_32(cbAppend, sizeof(uint32_t));
1223
1224 /* Ensure enough space for the token and the payload + padding. */
1225 int rc = rtFdtStructEnsureSpace(pThis, cbAppendAligned);
1226 if (RT_FAILURE(rc))
1227 return rc;
1228
1229 size_t cbCopied = RTSgBufCopyToBuf(pSgBuf, pThis->pbStruct + pThis->cbStruct, cbAppend);
1230 AssertReturn(cbCopied == cbAppend, VERR_INTERNAL_ERROR);
1231
1232 pThis->cbStruct += cbAppendAligned;
1233 return VINF_SUCCESS;
1234}
1235
1236
1237/**
1238 * Appends a token and payload data.
1239 *
1240 * @returns IPRT status code.
1241 * @param pThis Pointer to the FDT instance.
1242 * @param u32Token The token to append.
1243 * @param pvPayload Pointer to the payload data to append.
1244 * @param cbPayload Size of the payload data in bytes.
1245 */
1246static int rtFdtStructAppendTokenAndPayload(PRTFDTINT pThis, uint32_t u32Token, const void *pvPayload, size_t cbPayload)
1247{
1248 RTSGBUF SgBuf;
1249 RTSGSEG aSegs[2];
1250
1251 aSegs[0].pvSeg = &u32Token;
1252 aSegs[0].cbSeg = sizeof(u32Token);
1253 aSegs[1].pvSeg = (void *)pvPayload;
1254 aSegs[1].cbSeg = cbPayload;
1255 RTSgBufInit(&SgBuf, &aSegs[0], RT_ELEMENTS(aSegs));
1256
1257 return rtFdtStructAppendSg(pThis, &SgBuf, sizeof(u32Token) + (uint32_t)cbPayload);
1258}
1259
1260
1261/**
1262 * Appends a property.
1263 *
1264 * @returns IPRT status code.
1265 * @param pThis Pointer to the FDT instance.
1266 * @param pszProperty Name of the property.
1267 * @param pvProperty Pointer to the property data.
1268 * @param cbProperty Size of the property data in bytes.
1269 */
1270static int rtFdtStructAppendProperty(PRTFDTINT pThis, const char *pszProperty, const void *pvProperty, uint32_t cbProperty)
1271{
1272 /* Insert the property name into the strings block. */
1273 uint32_t offStr;
1274 int rc = rtFdtStringsInsertString(pThis, pszProperty, &offStr);
1275 if (RT_FAILURE(rc))
1276 return rc;
1277
1278 uint32_t u32Token = DTB_FDT_TOKEN_PROPERTY_BE;
1279 DTBFDTPROP Prop;
1280
1281 Prop.cbProperty = RT_H2BE_U32(cbProperty);
1282 Prop.offPropertyName = RT_H2BE_U32(offStr);
1283
1284 RTSGBUF SgBuf;
1285 RTSGSEG aSegs[3];
1286 aSegs[0].pvSeg = &u32Token;
1287 aSegs[0].cbSeg = sizeof(u32Token);
1288 aSegs[1].pvSeg = &Prop;
1289 aSegs[1].cbSeg = sizeof(Prop);
1290 if (cbProperty)
1291 {
1292 aSegs[2].pvSeg = (void *)pvProperty;
1293 aSegs[2].cbSeg = cbProperty;
1294 }
1295 RTSgBufInit(&SgBuf, &aSegs[0], cbProperty ? RT_ELEMENTS(aSegs) : 2);
1296
1297 return rtFdtStructAppendSg(pThis, &SgBuf, sizeof(u32Token) + sizeof(Prop) + cbProperty);
1298}
1299
1300
1301RTDECL(int) RTFdtCreateEmpty(PRTFDT phFdt)
1302{
1303 AssertPtrReturn(phFdt, VERR_INVALID_POINTER);
1304
1305 int rc = VINF_SUCCESS;
1306 PRTFDTINT pThis = (PRTFDTINT)RTMemAllocZ(sizeof(*pThis));
1307 if (pThis)
1308 {
1309 pThis->idPHandle = 1; /* 0 is invalid */
1310
1311 /* Add the root node. */
1312 rc = RTFdtNodeAdd(pThis, "");
1313 if (RT_SUCCESS(rc))
1314 {
1315 *phFdt = pThis;
1316 return VINF_SUCCESS;
1317 }
1318
1319 RTMemFree(pThis);
1320 }
1321 else
1322 rc = VERR_NO_MEMORY;
1323
1324 return rc;
1325}
1326
1327
1328RTDECL(int) RTFdtCreateFromVfsIoStrm(PRTFDT phFdt, RTVFSIOSTREAM hVfsIos, RTFDTTYPE enmInType, PRTERRINFO pErrInfo)
1329{
1330 AssertPtrReturn(phFdt, VERR_INVALID_POINTER);
1331 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_INVALID_HANDLE);
1332
1333 if (enmInType == RTFDTTYPE_DTB)
1334 return rtFdtLoadDtb(phFdt, hVfsIos, pErrInfo);
1335
1336 return VERR_NOT_IMPLEMENTED;
1337}
1338
1339
1340RTDECL(int) RTFdtCreateFromFile(PRTFDT phFdt, const char *pszFilename, RTFDTTYPE enmInType, PRTERRINFO pErrInfo)
1341{
1342 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
1343 int rc = RTVfsChainOpenIoStream(pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
1344 &hVfsIos, NULL /*poffError*/, pErrInfo);
1345 if (RT_FAILURE(rc))
1346 return rc;
1347
1348 rc = RTFdtCreateFromVfsIoStrm(phFdt, hVfsIos, enmInType, pErrInfo);
1349 RTVfsIoStrmRelease(hVfsIos);
1350 return rc;
1351}
1352
1353
1354RTDECL(void) RTFdtDestroy(RTFDT hFdt)
1355{
1356 PRTFDTINT pThis = hFdt;
1357 AssertPtrReturnVoid(pThis);
1358
1359 rtFdtDestroy(pThis);
1360}
1361
1362
1363RTDECL(int) RTFdtFinalize(RTFDT hFdt)
1364{
1365 PRTFDTINT pThis = hFdt;
1366 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1367
1368 /* Already finalized? */
1369 if (!pThis->cTreeDepth)
1370 return VINF_SUCCESS;
1371
1372 uint32_t cbStructSpace = pThis->cTreeDepth * sizeof(uint32_t) + sizeof(uint32_t); /* One extra for the END token. */
1373 int rc = rtFdtStructEnsureSpace(pThis, cbStructSpace);
1374 if (RT_FAILURE(rc))
1375 return rc;
1376
1377 uint32_t *pu32 = (uint32_t *)(pThis->pbStruct + pThis->cbStruct);
1378 while (pThis->cTreeDepth)
1379 {
1380 *pu32++ = DTB_FDT_TOKEN_END_NODE_BE;
1381 pThis->cTreeDepth--;
1382 }
1383
1384 *pu32 = DTB_FDT_TOKEN_END_BE;
1385 pThis->cbStruct += cbStructSpace;
1386 return VINF_SUCCESS;
1387}
1388
1389
1390RTDECL(uint32_t) RTFdtPHandleAllocate(RTFDT hFdt)
1391{
1392 PRTFDTINT pThis = hFdt;
1393 AssertPtrReturn(pThis, UINT32_MAX);
1394
1395 return pThis->idPHandle++;
1396}
1397
1398
1399RTDECL(int) RTFdtDumpToVfsIoStrm(RTFDT hFdt, RTFDTTYPE enmOutType, uint32_t fFlags, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
1400{
1401 PRTFDTINT pThis = hFdt;
1402 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1403 AssertReturn(enmOutType == RTFDTTYPE_DTS || enmOutType == RTFDTTYPE_DTB, VERR_INVALID_PARAMETER);
1404 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
1405
1406 if (enmOutType == RTFDTTYPE_DTS)
1407 return rtFdtDumpAsDts(pThis, hVfsIos, pErrInfo);
1408 else if (enmOutType == RTFDTTYPE_DTB)
1409 return rtFdtDumpAsDtb(pThis, hVfsIos, pErrInfo);
1410
1411 return VERR_NOT_SUPPORTED;
1412}
1413
1414
1415RTDECL(int) RTFdtDumpToFile(RTFDT hFdt, RTFDTTYPE enmOutType, uint32_t fFlags, const char *pszFilename, PRTERRINFO pErrInfo)
1416{
1417 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
1418 int rc = RTVfsChainOpenIoStream(pszFilename, RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_NONE,
1419 &hVfsIos, NULL /*poffError*/, pErrInfo);
1420 if (RT_FAILURE(rc))
1421 return rc;
1422
1423 rc = RTFdtDumpToVfsIoStrm(hFdt, enmOutType, fFlags, hVfsIos, pErrInfo);
1424 RTVfsIoStrmRelease(hVfsIos);
1425 return rc;
1426}
1427
1428
1429RTDECL(int) RTFdtAddMemoryReservation(RTFDT hFdt, uint64_t PhysAddrStart, uint64_t cbArea)
1430{
1431 PRTFDTINT pThis = hFdt;
1432 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1433 AssertReturn(PhysAddrStart > 0 || cbArea > 0, VERR_INVALID_PARAMETER);
1434
1435 if (pThis->cMemRsv == pThis->cMemRsvMax)
1436 {
1437 uint32_t cMemRsvMaxNew = pThis->cMemRsvMax + 10;
1438 PDTBFDTRSVENTRY paMemRsvNew = (PDTBFDTRSVENTRY)RTMemRealloc(pThis->paMemRsv, cMemRsvMaxNew * sizeof(*paMemRsvNew));
1439 if (!paMemRsvNew)
1440 return VERR_NO_MEMORY;
1441
1442 pThis->paMemRsv = paMemRsvNew;
1443 pThis->cMemRsvMax = cMemRsvMaxNew;
1444 }
1445
1446 pThis->paMemRsv[pThis->cMemRsv].PhysAddrStart = PhysAddrStart;
1447 pThis->paMemRsv[pThis->cMemRsv].cbArea = cbArea;
1448 pThis->cMemRsv++;
1449 return VINF_SUCCESS;
1450}
1451
1452
1453RTDECL(int) RTFdtSetPhysBootCpuId(RTFDT hFdt, uint32_t idPhysBootCpu)
1454{
1455 PRTFDTINT pThis = hFdt;
1456 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1457
1458 pThis->u32CpuIdPhysBoot = idPhysBootCpu;
1459 return VINF_SUCCESS;
1460}
1461
1462
1463RTDECL(int) RTFdtNodeAdd(RTFDT hFdt, const char *pszName)
1464{
1465 PRTFDTINT pThis = hFdt;
1466 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1467
1468 /** @todo Validate node name against allowed character set. */
1469 size_t cchName = strlen(pszName) + 1;
1470 int rc = rtFdtStructAppendTokenAndPayload(pThis, DTB_FDT_TOKEN_BEGIN_NODE_BE, pszName, cchName);
1471 if (RT_FAILURE(rc))
1472 return rc;
1473
1474 pThis->cTreeDepth++;
1475 return VINF_SUCCESS;
1476}
1477
1478
1479RTDECL(int) RTFdtNodeAddF(RTFDT hFdt, const char *pszNameFmt, ...)
1480{
1481 va_list va;
1482 va_start(va, pszNameFmt);
1483 int rc = RTFdtNodeAddV(hFdt, pszNameFmt, va);
1484 va_end(va);
1485 return rc;
1486}
1487
1488
1489RTDECL(int) RTFdtNodeAddV(RTFDT hFdt, const char *pszNameFmt, va_list va)
1490{
1491 char szName[512]; /* lazy developer */
1492 ssize_t cch = RTStrPrintf2V(&szName[0], sizeof(szName), pszNameFmt, va);
1493 if (cch <= 0)
1494 return VERR_BUFFER_OVERFLOW;
1495
1496 return RTFdtNodeAdd(hFdt, &szName[0]);
1497}
1498
1499
1500RTDECL(int) RTFdtNodeFinalize(RTFDT hFdt)
1501{
1502 PRTFDTINT pThis = hFdt;
1503 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1504 AssertReturn(pThis->cTreeDepth > 1, VERR_FDT_AT_ROOT_LEVEL);
1505
1506 int rc = rtFdtStructAppendTokenAndPayload(pThis, DTB_FDT_TOKEN_END_NODE_BE, NULL /*pvPayload*/, 0 /*cbPayload*/);
1507 if (RT_FAILURE(rc))
1508 return rc;
1509
1510 pThis->cTreeDepth--;
1511 return VINF_SUCCESS;
1512}
1513
1514
1515RTDECL(int) RTFdtNodePropertyAddEmpty(RTFDT hFdt, const char *pszProperty)
1516{
1517 PRTFDTINT pThis = hFdt;
1518 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1519
1520 return rtFdtStructAppendProperty(pThis, pszProperty, NULL /*pvProperty*/, 0 /*cbProperty*/);
1521}
1522
1523
1524RTDECL(int) RTFdtNodePropertyAddU32(RTFDT hFdt, const char *pszProperty, uint32_t u32)
1525{
1526 PRTFDTINT pThis = hFdt;
1527 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1528
1529 u32 = RT_H2BE_U32(u32);
1530 return rtFdtStructAppendProperty(pThis, pszProperty, &u32, sizeof(u32));
1531}
1532
1533
1534RTDECL(int) RTFdtNodePropertyAddU64(RTFDT hFdt, const char *pszProperty, uint64_t u64)
1535{
1536 PRTFDTINT pThis = hFdt;
1537 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1538
1539 uint32_t u32Low = RT_H2BE_U32((uint32_t)u64);
1540 uint32_t u32High = RT_H2BE_U32((uint32_t)(u64 >> 32));
1541 return RTFdtNodePropertyAddCellsU32(pThis, pszProperty, 2, u32High, u32Low);
1542}
1543
1544
1545RTDECL(int) RTFdtNodePropertyAddString(RTFDT hFdt, const char *pszProperty, const char *pszVal)
1546{
1547 PRTFDTINT pThis = hFdt;
1548 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1549
1550 uint32_t cchVal = (uint32_t)strlen(pszVal) + 1;
1551 return rtFdtStructAppendProperty(pThis, pszProperty, pszVal, cchVal);
1552}
1553
1554
1555RTDECL(int) RTFdtNodePropertyAddCellsU32(RTFDT hFdt, const char *pszProperty, uint32_t cCells, ...)
1556{
1557 va_list va;
1558 va_start(va, cCells);
1559 int rc = RTFdtNodePropertyAddCellsU32V(hFdt, pszProperty, cCells, va);
1560 va_end(va);
1561 return rc;
1562}
1563
1564
1565RTDECL(int) RTFdtNodePropertyAddCellsU32V(RTFDT hFdt, const char *pszProperty, uint32_t cCells, va_list va)
1566{
1567 PRTFDTINT pThis = hFdt;
1568 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1569
1570 /* Insert the property name into the strings block. */
1571 uint32_t offStr;
1572 int rc = rtFdtStringsInsertString(pThis, pszProperty, &offStr);
1573 if (RT_FAILURE(rc))
1574 return rc;
1575
1576 uint32_t cbProp = cCells * sizeof(uint32_t) + 3 * sizeof(uint32_t);
1577
1578 rc = rtFdtStructEnsureSpace(pThis, cbProp);
1579 if (RT_FAILURE(rc))
1580 return rc;
1581
1582 uint32_t *pu32 = (uint32_t *)(pThis->pbStruct + pThis->cbStruct);
1583 *pu32++ = DTB_FDT_TOKEN_PROPERTY_BE;
1584 *pu32++ = RT_H2BE_U32(cCells * sizeof(uint32_t));
1585 *pu32++ = RT_H2BE_U32(offStr);
1586 for (uint32_t i = 0; i < cCells; i++)
1587 {
1588 uint32_t u32 = va_arg(va, uint32_t);
1589 *pu32++ = RT_H2BE_U32(u32);
1590 }
1591
1592 pThis->cbStruct += cbProp;
1593 return VINF_SUCCESS;
1594}
1595
1596
1597RTDECL(int) RTFdtNodePropertyAddCellsU32AsArray(RTFDT hFdt, const char *pszProperty, uint32_t cCells, uint32_t *pau32Cells)
1598{
1599 PRTFDTINT pThis = hFdt;
1600 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1601
1602 /* Insert the property name into the strings block. */
1603 uint32_t offStr;
1604 int rc = rtFdtStringsInsertString(pThis, pszProperty, &offStr);
1605 if (RT_FAILURE(rc))
1606 return rc;
1607
1608 uint32_t cbProp = cCells * sizeof(uint32_t) + 3 * sizeof(uint32_t);
1609
1610 rc = rtFdtStructEnsureSpace(pThis, cbProp);
1611 if (RT_FAILURE(rc))
1612 return rc;
1613
1614 uint32_t *pu32 = (uint32_t *)(pThis->pbStruct + pThis->cbStruct);
1615 *pu32++ = DTB_FDT_TOKEN_PROPERTY_BE;
1616 *pu32++ = RT_H2BE_U32(cCells * sizeof(uint32_t));
1617 *pu32++ = RT_H2BE_U32(offStr);
1618 for (uint32_t i = 0; i < cCells; i++)
1619 *pu32++ = RT_H2BE_U32(pau32Cells[i]);
1620
1621 pThis->cbStruct += cbProp;
1622 return VINF_SUCCESS;
1623}
1624
1625
1626RTDECL(int) RTFdtNodePropertyAddCellsU64(RTFDT hFdt, const char *pszProperty, uint32_t cCells, ...)
1627{
1628 va_list va;
1629 va_start(va, cCells);
1630 int rc = RTFdtNodePropertyAddCellsU64V(hFdt, pszProperty, cCells, va);
1631 va_end(va);
1632 return rc;
1633}
1634
1635
1636RTDECL(int) RTFdtNodePropertyAddCellsU64V(RTFDT hFdt, const char *pszProperty, uint32_t cCells, va_list va)
1637{
1638 PRTFDTINT pThis = hFdt;
1639 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1640
1641 /* Insert the property name into the strings block. */
1642 uint32_t offStr;
1643 int rc = rtFdtStringsInsertString(pThis, pszProperty, &offStr);
1644 if (RT_FAILURE(rc))
1645 return rc;
1646
1647 uint32_t cbProp = cCells * 2 * sizeof(uint32_t) + 3 * sizeof(uint32_t);
1648
1649 rc = rtFdtStructEnsureSpace(pThis, cbProp);
1650 if (RT_FAILURE(rc))
1651 return rc;
1652
1653 uint32_t *pu32 = (uint32_t *)(pThis->pbStruct + pThis->cbStruct);
1654 *pu32++ = DTB_FDT_TOKEN_PROPERTY_BE;
1655 *pu32++ = RT_H2BE_U32(cCells * 2 * sizeof(uint32_t));
1656 *pu32++ = RT_H2BE_U32(offStr);
1657 for (uint32_t i = 0; i < cCells; i++)
1658 {
1659 /* First the high 32-bits of the 64-bit value are stored, then the lower ones. */
1660 uint64_t u64 = va_arg(va, uint64_t);
1661 *pu32++ = RT_H2BE_U32((uint32_t)(u64 >> 32));
1662 *pu32++ = RT_H2BE_U32((uint32_t)u64);
1663 }
1664
1665 pThis->cbStruct += cbProp;
1666 return VINF_SUCCESS;
1667}
1668
1669
1670RTDECL(int) RTFdtNodePropertyAddStringList(RTFDT hFdt, const char *pszProperty, uint32_t cStrings, ...)
1671{
1672 va_list va;
1673 va_start(va, cStrings);
1674 int rc = RTFdtNodePropertyAddStringListV(hFdt, pszProperty, cStrings, va);
1675 va_end(va);
1676 return rc;
1677}
1678
1679
1680RTDECL(int) RTFdtNodePropertyAddStringListV(RTFDT hFdt, const char *pszProperty, uint32_t cStrings, va_list va)
1681{
1682 PRTFDTINT pThis = hFdt;
1683 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1684
1685 /* Insert the property name into the strings block. */
1686 uint32_t offStr;
1687 int rc = rtFdtStringsInsertString(pThis, pszProperty, &offStr);
1688 if (RT_FAILURE(rc))
1689 return rc;
1690
1691 /* First pass, go over all strings and find out how much we have to add in total. */
1692 uint32_t cbStrings = 0;
1693 va_list vaCopy;
1694 va_copy(vaCopy, va);
1695 for (uint32_t i = 0; i < cStrings; i++)
1696 cbStrings += (uint32_t)strlen(va_arg(vaCopy, const char *)) + 1; /* Include terminator. */
1697 va_end(vaCopy);
1698
1699 uint32_t cbProp = RT_ALIGN_32(cbStrings + 3 * sizeof(uint32_t), sizeof(uint32_t)); /* Account for property token and the property data. */
1700 rc = rtFdtStructEnsureSpace(pThis, cbProp);
1701 if (RT_FAILURE(rc))
1702 return rc;
1703
1704 /* Add the data. */
1705 uint32_t *pu32 = (uint32_t *)(pThis->pbStruct + pThis->cbStruct);
1706 *pu32++ = DTB_FDT_TOKEN_PROPERTY_BE;
1707 *pu32++ = RT_H2BE_U32(cbStrings);
1708 *pu32++ = RT_H2BE_U32(offStr);
1709
1710 char *pb = (char *)pu32;
1711 for (uint32_t i = 0; i < cStrings; i++)
1712 {
1713 const char *psz = va_arg(va, const char *);
1714 /* MSVC doesn't know about stpcpy(), so we have to query the string length again... */
1715 uint32_t cbStr = (uint32_t)strlen(psz);
1716 strcpy(pb, psz);
1717 pb += cbStr + 1;
1718 }
1719
1720 pThis->cbStruct += cbProp;
1721 return VINF_SUCCESS;
1722}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette