VirtualBox

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

Last change on this file since 100009 was 100009, checked in by vboxsync, 2 years ago

Runtime/common/misc/fdt.cpp: Implement loading the blocks from the I/O stream, bugref:10401

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.8 KB
Line 
1/* $Id: fdt.cpp 100009 2023-05-30 09:55:31Z 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.215389.xyz.
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/log.h>
48#include <iprt/mem.h>
49#include <iprt/strcache.h>
50#include <iprt/string.h>
51#include <iprt/vfs.h>
52#include <iprt/formats/dtb.h>
53
54
55/*********************************************************************************************************************************
56* Defined Constants And Macros *
57*********************************************************************************************************************************/
58
59
60/*********************************************************************************************************************************
61* Structures and Typedefs *
62*********************************************************************************************************************************/
63
64/**
65 * Internal Flattened Devicetree instance.
66 */
67typedef struct RTFDTINT
68{
69 /** Pointer to the string block. */
70 char *paszStrings;
71 /** Pointer to the raw structs block. */
72 uint32_t *pu32Structs;
73 /** Pointer to the array of memory reservation entries. */
74 PDTBFDTRSVENTRY paMemRsv;
75 /** Number of memory reservation entries. */
76 uint32_t cMemRsv;
77 /** The DTB header (converted to host endianess). */
78 DTBFDTHDR DtbHdr;
79} RTFDTINT;
80/** Pointer to the internal Flattened Devicetree instance. */
81typedef RTFDTINT *PRTFDTINT;
82
83
84/*********************************************************************************************************************************
85* Global Variables *
86*********************************************************************************************************************************/
87
88
89/*********************************************************************************************************************************
90* Internal Functions *
91*********************************************************************************************************************************/
92#ifdef LOG_ENABLED
93
94/**
95 * Logs a FDT DTB header.
96 *
97 * @param pDtbHdr The FDT DTB header.
98 */
99static void rtFdtDtbHdr_Log(PCDTBFDTHDR pDtbHdr)
100{
101 if (LogIs2Enabled())
102 {
103 Log2(("RTFdt: Header:\n"));
104 Log2(("RTFdt: u32Magic %#RX32\n", RT_BE2H_U32(pDtbHdr->u32Magic)));
105 Log2(("RTFdt: cbFdt %#RX32\n", RT_BE2H_U32(pDtbHdr->cbFdt)));
106 Log2(("RTFdt: offDtStruct %#RX32\n", RT_BE2H_U32(pDtbHdr->offDtStruct)));
107 Log2(("RTFdt: offDtStrings %#RX32\n", RT_BE2H_U32(pDtbHdr->offDtStrings)));
108 Log2(("RTFdt: offMemRsvMap %#RX32\n", RT_BE2H_U32(pDtbHdr->offMemRsvMap)));
109 Log2(("RTFdt: u32Version %#RX32\n", RT_BE2H_U32(pDtbHdr->u32Version)));
110 Log2(("RTFdt: u32VersionLastCompatible %#RX32\n", RT_BE2H_U32(pDtbHdr->u32VersionLastCompatible)));
111 Log2(("RTFdt: u32CpuIdPhysBoot %#RX32\n", RT_BE2H_U32(pDtbHdr->u32CpuIdPhysBoot)));
112 Log2(("RTFdt: cbDtStrings %#RX32\n", RT_BE2H_U32(pDtbHdr->cbDtStrings)));
113 Log2(("RTFdt: cbDtStruct %#RX32\n", RT_BE2H_U32(pDtbHdr->cbDtStruct)));
114 }
115}
116
117#endif /* LOG_ENABLED */
118
119
120/**
121 * Validates the given header.
122 *
123 * @returns IPRT status code.
124 * @param pDtbHdr The DTB header to validate.
125 * @param cbDtb Size of the whole DTB in bytes.
126 * @param pErrInfo Where to return additional error information.
127 */
128static int rtFdtDtbHdr_Validate(PDTBFDTHDR pDtbHdr, uint64_t cbDtb, PRTERRINFO pErrInfo)
129{
130 /* Convert to host endianess first. */
131 pDtbHdr->u32Magic = RT_BE2H_U32(pDtbHdr->u32Magic);
132 pDtbHdr->cbFdt = RT_BE2H_U32(pDtbHdr->cbFdt);
133 pDtbHdr->offDtStruct = RT_BE2H_U32(pDtbHdr->offDtStruct);
134 pDtbHdr->offDtStrings = RT_BE2H_U32(pDtbHdr->offDtStrings);
135 pDtbHdr->offMemRsvMap = RT_BE2H_U32(pDtbHdr->offMemRsvMap);
136 pDtbHdr->u32Version = RT_BE2H_U32(pDtbHdr->u32Version);
137 pDtbHdr->u32VersionLastCompatible = RT_BE2H_U32(pDtbHdr->u32VersionLastCompatible);
138 pDtbHdr->u32CpuIdPhysBoot = RT_BE2H_U32(pDtbHdr->u32CpuIdPhysBoot);
139 pDtbHdr->cbDtStrings = RT_BE2H_U32(pDtbHdr->cbDtStrings);
140 pDtbHdr->cbDtStruct = RT_BE2H_U32(pDtbHdr->cbDtStruct);
141
142 if (pDtbHdr->u32Magic != DTBFDTHDR_MAGIC)
143 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_HDR_MAGIC_INVALID, "The magic of the DTB header is invalid (expected %#RX32, got %#RX32)",
144 DTBFDTHDR_MAGIC, pDtbHdr->u32Magic);
145 if (pDtbHdr->u32Version != DTBFDTHDR_VERSION)
146 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_HDR_VERSION_NOT_SUPPORTED, "Version %u of the DTB is not supported (supported is %u)",
147 pDtbHdr->u32Version, DTBFDTHDR_VERSION);
148 if (pDtbHdr->u32VersionLastCompatible != DTBFDTHDR_VERSION_LAST_COMP)
149 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_HDR_LAST_COMPAT_VERSION_INVALID, "Last compatible version %u of the DTB is invalid (expected %u)",
150 pDtbHdr->u32VersionLastCompatible, DTBFDTHDR_VERSION_LAST_COMP);
151 if (pDtbHdr->cbFdt != cbDtb)
152 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_HDR_SIZE_INVALID, "The size of the FDT is invalid (expected %RU32, got %RU32)",
153 cbDtb, pDtbHdr->cbFdt);
154
155 /*
156 * Check that any of the offsets is inside the bounds of the FDT and that the memory reservation block comes first,
157 * then the structs block and strings last.
158 */
159 if ( pDtbHdr->offMemRsvMap >= pDtbHdr->cbFdt
160 || pDtbHdr->offMemRsvMap < sizeof(*pDtbHdr))
161 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)",
162 pDtbHdr->offMemRsvMap, pDtbHdr->cbFdt);
163 if ( pDtbHdr->offDtStruct >= pDtbHdr->cbFdt
164 || (pDtbHdr->cbFdt - pDtbHdr->offDtStruct < pDtbHdr->cbDtStruct)
165 || pDtbHdr->offDtStruct <= pDtbHdr->offMemRsvMap + sizeof(DTBFDTRSVENTRY))
166 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)",
167 pDtbHdr->offDtStruct, pDtbHdr->cbDtStruct, pDtbHdr->cbFdt, pDtbHdr->offMemRsvMap);
168 if ( pDtbHdr->offDtStrings >= pDtbHdr->cbFdt
169 || (pDtbHdr->cbFdt - pDtbHdr->offDtStrings < pDtbHdr->cbDtStrings)
170 || pDtbHdr->offDtStrings < pDtbHdr->offDtStruct + pDtbHdr->cbDtStruct)
171 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)",
172 pDtbHdr->offDtStrings, pDtbHdr->cbDtStrings, pDtbHdr->cbFdt, pDtbHdr->offDtStruct);
173
174 return VINF_SUCCESS;
175}
176
177
178/**
179 * Fres all resources allocated for the given FDT.
180 *
181 * @param pThis The FDT instance to destroy.
182 */
183static void rtFdtDestroy(PRTFDTINT pThis)
184{
185 if (pThis->paszStrings)
186 RTMemFree(pThis->paszStrings);
187 if (pThis->pu32Structs)
188 RTMemFree(pThis->pu32Structs);
189 if (pThis->paMemRsv)
190 RTMemFree(pThis->paMemRsv);
191
192 pThis->paszStrings = NULL;
193 pThis->pu32Structs = NULL;
194 pThis->paMemRsv = NULL;
195 pThis->cMemRsv = 0;
196 RT_ZERO(pThis->DtbHdr);
197 RTMemFree(pThis);
198}
199
200
201/**
202 * Loads the memory reservation block from the underlying VFS I/O stream.
203 *
204 * @returns IPRT status code.
205 * @param pThis The FDT instance.
206 * @param hVfsIos The VFS I/O stream handle to load the DTB from.
207 * @param pErrInfo Where to return additional error information.
208 */
209static int rtFdtDtbMemRsvLoad(PRTFDTINT pThis, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
210{
211 AssertReturn(pThis->DtbHdr.offMemRsvMap < pThis->DtbHdr.offDtStruct, VERR_INTERNAL_ERROR);
212
213 uint32_t cMemRsvMax = (pThis->DtbHdr.offDtStruct - pThis->DtbHdr.offMemRsvMap) / sizeof(*pThis->paMemRsv);
214 Assert(cMemRsvMax);
215
216 pThis->paMemRsv = (PDTBFDTRSVENTRY)RTMemAllocZ(cMemRsvMax * sizeof(*pThis->paMemRsv));
217 if (!pThis->paMemRsv)
218 return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Failed to allocate %u bytes of memory for the memory reservation block",
219 cMemRsvMax * sizeof(*pThis->paMemRsv));
220
221 /* Read the entries one after another until the terminator is reached. */
222 uint32_t cMemRsv = 0;
223 for (;;)
224 {
225 DTBFDTRSVENTRY MemRsv;
226 int rc = RTVfsIoStrmRead(hVfsIos, &MemRsv, sizeof(MemRsv), true /*fBlocking*/, NULL /*pcbRead*/);
227 if (RT_FAILURE(rc))
228 return RTErrInfoSetF(pErrInfo, rc, "Failed to read memory reservation entry %u from I/O stream",
229 cMemRsv);
230
231 /* Check whether the terminator is reached (no need to convert endianness here). */
232 if ( MemRsv.PhysAddrStart == 0
233 && MemRsv.cbArea == 0)
234 break;
235
236 /*
237 * The terminator must be included in the maximum entry count, if not
238 * the DTB is malformed and lacks a terminating entry before the start of the structs block.
239 */
240 if (cMemRsv + 1 == cMemRsvMax)
241 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_MEM_RSV_BLOCK_TERMINATOR_MISSING,
242 "The memory reservation block lacks a terminating entry");
243
244 pThis->paMemRsv[cMemRsv].PhysAddrStart = RT_BE2H_U64(MemRsv.PhysAddrStart);
245 pThis->paMemRsv[cMemRsv].cbArea = RT_BE2H_U64(MemRsv.cbArea);
246 cMemRsv++;
247 }
248
249 pThis->cMemRsv = cMemRsv + 1;
250 return VINF_SUCCESS;
251}
252
253
254/**
255 * Loads the structs block of the given FDT.
256 *
257 * @returns IPRT status code.
258 * @param pThis The FDT instance.
259 * @param hVfsIos The VFS I/O stream handle to load the DTB from.
260 * @param pErrInfo Where to return additional error information.
261 */
262static int rtFdtDtbStructsLoad(PRTFDTINT pThis, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
263{
264 pThis->pu32Structs = (uint32_t *)RTMemAllocZ(pThis->DtbHdr.cbDtStruct);
265 if (!pThis->pu32Structs)
266 return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Failed to allocate %u bytes of memory for the structs block",
267 pThis->DtbHdr.cbDtStruct);
268
269 int rc = RTVfsIoStrmRead(hVfsIos, pThis->pu32Structs, pThis->DtbHdr.cbDtStruct, true /*fBlocking*/, NULL /*pcbRead*/);
270 if (RT_FAILURE(rc))
271 return RTErrInfoSetF(pErrInfo, rc, "Failed to read structs block from I/O stream");
272
273 return VINF_SUCCESS;
274}
275
276
277/**
278 * Loads the strings block of the given FDT.
279 *
280 * @returns IPRT status code.
281 * @param pThis The FDT instance.
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 rtFdtDtbStringsLoad(PRTFDTINT pThis, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
286{
287 pThis->paszStrings = (char *)RTMemAllocZ(pThis->DtbHdr.cbDtStrings);
288 if (!pThis->paszStrings)
289 return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Failed to allocate %u bytes of memory for the strings block",
290 pThis->DtbHdr.cbDtStrings);
291
292 int rc = RTVfsIoStrmRead(hVfsIos, pThis->paszStrings, pThis->DtbHdr.cbDtStrings, true /*fBlocking*/, NULL /*pcbRead*/);
293 if (RT_FAILURE(rc))
294 return RTErrInfoSetF(pErrInfo, rc, "Failed to read strings block from I/O stream");
295
296 /* Verify that the strings block is terminated. */
297 if (pThis->paszStrings[pThis->DtbHdr.cbDtStrings - 1] != '\0')
298 return RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_STRINGS_BLOCK_NOT_TERMINATED, "The strings block is not zero temrinated");
299
300 return VINF_SUCCESS;
301}
302
303
304/**
305 * Loads the DTB from the given VFS file.
306 *
307 * @returns IPRT status code.
308 * @param phFdt Where to return the FDT handle on success.
309 * @param hVfsIos The VFS I/O stream handle to load the DTB from.
310 * @param pErrInfo Where to return additional error information.
311 */
312static int rtFdtLoadDtb(PRTFDT phFdt, RTVFSIOSTREAM hVfsIos, PRTERRINFO pErrInfo)
313{
314 DTBFDTHDR DtbHdr;
315 RTFSOBJINFO ObjInfo;
316 int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);
317 if (RT_SUCCESS(rc))
318 {
319 if ((uint64_t)ObjInfo.cbObject >= sizeof(DtbHdr) + sizeof(DTBFDTRSVENTRY))
320 {
321 rc = RTVfsIoStrmRead(hVfsIos, &DtbHdr, sizeof(DtbHdr), true /*fBlocking*/, NULL /*pcbRead*/);
322 if (RT_SUCCESS(rc))
323 {
324#ifdef LOG_ENABLED
325 rtFdtDtbHdr_Log(&DtbHdr);
326#endif
327
328 /* Validate the header. */
329 rc = rtFdtDtbHdr_Validate(&DtbHdr, ObjInfo.cbObject, pErrInfo);
330 if (RT_SUCCESS(rc))
331 {
332 PRTFDTINT pThis = (PRTFDTINT)RTMemAllocZ(sizeof(*pThis));
333 if (RT_LIKELY(pThis))
334 {
335 memcpy(&pThis->DtbHdr, &DtbHdr, sizeof(DtbHdr));
336
337 /* Load the memory reservation block. */
338 rc = rtFdtDtbMemRsvLoad(pThis, hVfsIos, pErrInfo);
339 if (RT_SUCCESS(rc))
340 {
341 rc = rtFdtDtbStructsLoad(pThis, hVfsIos, pErrInfo);
342 if (RT_SUCCESS(rc))
343 {
344 rc = rtFdtDtbStringsLoad(pThis, hVfsIos, pErrInfo);
345 if (RT_SUCCESS(rc))
346 {
347 *phFdt = pThis;
348 return VINF_SUCCESS;
349 }
350 }
351 }
352
353 rtFdtDestroy(pThis);
354 }
355 else
356 RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Failed to allocate memory for the FDT");
357 }
358 }
359 else
360 RTErrInfoSetF(pErrInfo, rc, "Failed to read %u bytes for the DTB header -> %Rrc",
361 sizeof(DtbHdr), rc);
362 }
363 else
364 RTErrInfoSetF(pErrInfo, VERR_FDT_DTB_MALFORMED, "DTB is too small, needs at least %u bytes, only %u available",
365 sizeof(DtbHdr) + sizeof(DTBFDTRSVENTRY), ObjInfo.cbObject);
366 }
367 else
368 RTErrInfoSetF(pErrInfo, rc, "Failed to query size of the DTB -> %Rrc", rc);
369
370 return rc;
371}
372
373
374RTDECL(int) RTFdtCreateEmpty(PRTFDT phFdt)
375{
376 RT_NOREF(phFdt);
377 return VERR_NOT_IMPLEMENTED;
378}
379
380
381RTDECL(int) RTFdtCreateFromVfsIoStrm(PRTFDT phFdt, RTVFSIOSTREAM hVfsIos, RTFDTTYPE enmInType, PRTERRINFO pErrInfo)
382{
383 AssertPtrReturn(phFdt, VERR_INVALID_POINTER);
384 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_INVALID_HANDLE);
385
386 if (enmInType == RTFDTTYPE_DTB)
387 return rtFdtLoadDtb(phFdt, hVfsIos, pErrInfo);
388
389 return VERR_NOT_IMPLEMENTED;
390}
391
392
393RTDECL(int) RTFdtCreateFromFile(PRTFDT phFdt, const char *pszFilename, RTFDTTYPE enmInType, PRTERRINFO pErrInfo)
394{
395 RT_NOREF(phFdt, pszFilename, enmInType, pErrInfo);
396 return VERR_NOT_IMPLEMENTED;
397}
398
399
400RTDECL(void) RTFdtDestroy(RTFDT hFdt)
401{
402 PRTFDTINT pThis = hFdt;
403 AssertPtrReturnVoid(pThis);
404
405 rtFdtDestroy(pThis);
406}
407
408
409RTDECL(int) RTFdtDumpToVfsIoStrm(RTFDT hFdt, RTFDTTYPE enmOutType, uint32_t fFlags, RTVFSIOSTREAM hVfsIos)
410{
411 RT_NOREF(hFdt, enmOutType, fFlags, hVfsIos);
412 return VERR_NOT_IMPLEMENTED;
413}
414
415
416RTDECL(int) RTFdtDumpToFile(RTFDT hFdt, RTFDTTYPE enmOutType, uint32_t fFlags, const char *pszFilename)
417{
418 RT_NOREF(hFdt, enmOutType, fFlags, pszFilename);
419 return VERR_NOT_IMPLEMENTED;
420}
Note: See TracBrowser for help on using the repository browser.

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