VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/acpi/acpi-ns.cpp@ 108221

Last change on this file since 108221 was 108221, checked in by vboxsync, 3 months ago

Runtime/RTAcpi*: Support generating AML for more statements found in vbox.dsl, bugref:10733

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.4 KB
Line 
1/* $Id: acpi-ns.cpp 108221 2025-02-14 12:16:40Z vboxsync $ */
2/** @file
3 * IPRT - Advanced Configuration and Power Interface (ACPI) namespace handling.
4 */
5
6/*
7 * Copyright (C) 2025 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_ACPI
42#include <iprt/assert.h>
43#include <iprt/errcore.h>
44#include <iprt/list.h>
45#include <iprt/mem.h>
46#include <iprt/string.h>
47
48#include "internal/acpi.h"
49
50
51/*********************************************************************************************************************************
52* Defined Constants And Macros *
53*********************************************************************************************************************************/
54
55
56/*********************************************************************************************************************************
57* Structures and Typedefs *
58*********************************************************************************************************************************/
59
60
61/*********************************************************************************************************************************
62* Global Variables *
63*********************************************************************************************************************************/
64
65
66/*********************************************************************************************************************************
67* Internal Functions *
68*********************************************************************************************************************************/
69
70static void rtAcpiNsEntryDestroy(PRTACPINSENTRY pNsEntry)
71{
72 PRTACPINSENTRY pIt, pItNext;
73 RTListForEachSafe(&pNsEntry->LstNsEntries, pIt, pItNext, RTACPINSENTRY, NdNs)
74 {
75 RTListNodeRemove(&pIt->NdNs);
76 rtAcpiNsEntryDestroy(pIt);
77 }
78 RTMemFree(pNsEntry);
79}
80
81
82/**
83 * Worker for looking up the entry in the given namespace for a given namestring.
84 *
85 * @returns Pointer to the namespace entry if found or NULL if not.
86 * @param pNsRoot The namespace to search in.
87 * @param pszNameString The namestring to search.
88 * @param fExcludeLast Flag whether to exclude the last name segment from the search and return
89 * the pointer to the second to last entry.
90 * @param ppszNameSegLast Where to store the pointe to the last name segment on success (if fExcludeLast is true).
91 */
92static PRTACPINSENTRY rtAcpiNsLookupWorker(PRTACPINSROOT pNsRoot, const char *pszNameString, bool fExcludeLast,
93 const char **ppszNameSegLast)
94{
95 AssertReturn(*pszNameString != '\0', NULL);
96
97 /* Find the starting position. */
98 PRTACPINSENTRY pNsEntry;
99 const char *pszCur = pszNameString;
100 if (*pszCur == '\\')
101 {
102 /* Search from the root of the namespace. */
103 pNsEntry = &pNsRoot->RootEntry;
104 pszCur++;
105 }
106 else if (*pszCur == '^')
107 {
108 /* Go up the tree. */
109 pNsEntry = pNsRoot->aNsStack[pNsRoot->idxNsStack];
110 while (*pszCur == '^')
111 {
112 if (!pNsEntry->pParent) /* Too many levels up. */
113 return NULL;
114 pNsEntry = pNsEntry->pParent;
115 pszCur++;
116 }
117 }
118 else
119 pNsEntry = pNsRoot->aNsStack[pNsRoot->idxNsStack];
120
121 /* This ASSUMES the namestring has always full 4 character name segments and is well formed. */
122 do
123 {
124 Assert(pszCur[0] != '\0' && pszCur[1] != '\0' && pszCur[2] != '\0' && pszCur[3] != '\0');
125
126 if (fExcludeLast && pszCur[4] == '\0')
127 break;
128
129 PRTACPINSENTRY pIt;
130 bool fFound = false;
131 RTListForEach(&pNsEntry->LstNsEntries, pIt, RTACPINSENTRY, NdNs)
132 {
133 if (!memcmp(&pIt->achNameSeg[0], pszCur, sizeof(pIt->achNameSeg)))
134 {
135 pNsEntry = pIt;
136 fFound = true;
137 break;
138 }
139 }
140
141 /* The name path is invalid. */
142 if (!fFound)
143 return NULL;
144
145 pszCur += 4;
146 } while (*pszCur++ == '.');
147 AssertReturn( *pszCur == '\0'
148 || ( fExcludeLast
149 && pszCur[4] == '\0'),
150 NULL);
151
152 if (ppszNameSegLast)
153 *ppszNameSegLast = pszCur;
154 return pNsEntry;
155}
156
157
158/**
159 * Adds a new entry in the given namespace under the given path.
160 *
161 * @returns IPRT status code.
162 * @param pNsRoot The namespace to add the new entry to.
163 * @param pszNameString The namestring to add.
164 * @param fSwitchTo Flag whether to switch to the new entry.
165 * @param ppNsEntry Where to store the pointer to the created entry on success.
166 */
167static int rtAcpiNsAddEntryWorker(PRTACPINSROOT pNsRoot, const char *pszNameString, bool fSwitchTo, PRTACPINSENTRY *ppNsEntry)
168{
169 AssertReturn( !fSwitchTo
170 || pNsRoot->idxNsStack < RT_ELEMENTS(pNsRoot->aNsStack),
171 VERR_INVALID_STATE);
172
173 int rc;
174 const char *pszNameSegLast = NULL;
175 PRTACPINSENTRY pNsEntryParent = rtAcpiNsLookupWorker(pNsRoot, pszNameString, true /*fExcludeLast*/, &pszNameSegLast);
176 if (pNsEntryParent)
177 {
178 PRTACPINSENTRY pNsEntry = (PRTACPINSENTRY)RTMemAllocZ(sizeof(*pNsEntry));
179 if (pNsEntry)
180 {
181 pNsEntry->pParent = pNsEntryParent;
182 RTListInit(&pNsEntry->LstNsEntries);
183
184 memcpy(&pNsEntry->achNameSeg[0], pszNameSegLast, sizeof(pNsEntry->achNameSeg));
185
186 RTListAppend(&pNsEntryParent->LstNsEntries, &pNsEntry->NdNs);
187 *ppNsEntry = pNsEntry;
188 if (fSwitchTo)
189 pNsRoot->aNsStack[++pNsRoot->idxNsStack] = pNsEntry;
190 return VINF_SUCCESS;
191 }
192 else
193 rc = VERR_NO_MEMORY;
194 }
195 else
196 rc = VERR_NOT_FOUND;
197
198 return rc;
199}
200
201
202DECLHIDDEN(PRTACPINSROOT) rtAcpiNsCreate(void)
203{
204 PRTACPINSROOT pNsRoot = (PRTACPINSROOT)RTMemAllocZ(sizeof(*pNsRoot));
205 if (pNsRoot)
206 {
207 RTListInit(&pNsRoot->RootEntry.LstNsEntries);
208 pNsRoot->RootEntry.pParent = NULL;
209 pNsRoot->idxNsStack = 0;
210 pNsRoot->aNsStack[pNsRoot->idxNsStack] = &pNsRoot->RootEntry;
211 }
212 return pNsRoot;
213}
214
215
216DECLHIDDEN(void) rtAcpiNsDestroy(PRTACPINSROOT pNsRoot)
217{
218 PRTACPINSENTRY pIt, pItNext;
219 RTListForEachSafe(&pNsRoot->RootEntry.LstNsEntries, pIt, pItNext, RTACPINSENTRY, NdNs)
220 {
221 RTListNodeRemove(&pIt->NdNs);
222 rtAcpiNsEntryDestroy(pIt);
223 }
224 RTMemFree(pNsRoot);
225}
226
227
228DECLHIDDEN(int) rtAcpiNsAddEntryAstNode(PRTACPINSROOT pNsRoot, const char *pszNameString, PCRTACPIASTNODE pAstNd, bool fSwitchTo)
229{
230 PRTACPINSENTRY pNsEntry = NULL;
231 int rc = rtAcpiNsAddEntryWorker(pNsRoot, pszNameString, fSwitchTo, &pNsEntry);
232 if (RT_SUCCESS(rc))
233 {
234 pNsEntry->fAstNd = true;
235 pNsEntry->pAstNd = pAstNd;
236 }
237
238 return rc;
239}
240
241
242DECLHIDDEN(int) rtAcpiNsAddEntryRsrcField(PRTACPINSROOT pNsRoot, const char *pszNameString, uint32_t offBits, uint32_t cBits)
243{
244 PRTACPINSENTRY pNsEntry = NULL;
245 int rc = rtAcpiNsAddEntryWorker(pNsRoot, pszNameString, false /*fSwitchTo*/, &pNsEntry);
246 if (RT_SUCCESS(rc))
247 {
248 pNsEntry->fAstNd = false;
249 pNsEntry->pAstNd = NULL;
250 pNsEntry->offBits = offBits;
251 pNsEntry->cBits = cBits;
252 }
253
254 return rc;
255}
256
257
258DECLHIDDEN(int) rtAcpiNsAddEntryExternal(PRTACPINSROOT pNsRoot, const char *pszNameString, PCRTACPIASLEXTERNAL pExternal)
259{
260 PRTACPINSENTRY pNsEntry = NULL;
261 int rc = rtAcpiNsAddEntryWorker(pNsRoot, pszNameString, false /*fSwitchTo*/, &pNsEntry);
262 if (RT_SUCCESS(rc))
263 {
264 pNsEntry->fAstNd = false;
265 pNsEntry->pExternal = pExternal;
266 }
267
268 return rc;
269}
270
271
272DECLHIDDEN(int) rtAcpiNsQueryNamePathForNameString(PRTACPINSROOT pNsRoot, const char *pszNameString, char *pachNamePath, size_t *pcchNamePath)
273{
274 AssertReturn(!pachNamePath || *pcchNamePath >= 6, VERR_INVALID_PARAMETER); /* Needs to support at least \XXXX and the zero terminator. */
275
276 const char *pszNameSegLast = NULL;
277 PCRTACPINSENTRY pNsEntry = rtAcpiNsLookupWorker(pNsRoot, pszNameString, true /*fExcludeLast*/, &pszNameSegLast);
278 if (pNsEntry)
279 {
280 int rc = VERR_BUFFER_OVERFLOW;
281 size_t cchNamePath = 1; /* For the root prefix. */
282
283 if (!pachNamePath)
284 {
285 /* Calculate the name path size based on the number of segments. */
286 uint32_t cEntries = 0;
287 do
288 {
289 cEntries++;
290 pNsEntry = pNsEntry->pParent;
291 } while (pNsEntry);
292
293 cchNamePath += cEntries * (4 + 1) - 1; /* XXXX., except for the last one. */
294 }
295 else
296 {
297 uint32_t idxEntry = 0;
298 PCRTACPINSENTRY aNsEntries[255]; /* Maximum amount of name segments possible. */
299 do
300 {
301 aNsEntries[idxEntry++] = pNsEntry;
302 pNsEntry = pNsEntry->pParent;
303 } while (pNsEntry);
304
305 char *pch = pachNamePath;
306 *pch++ = '\\';
307 *pch = '\0';
308
309 /* The last entry must be the root entry. */
310 idxEntry--;
311 Assert(!aNsEntries[idxEntry]->pParent);
312
313 while (idxEntry)
314 {
315 pNsEntry = aNsEntries[--idxEntry];
316 if (cchNamePath + 5 < *pcchNamePath)
317 {
318 pch[0] = pNsEntry->achNameSeg[0];
319 pch[1] = pNsEntry->achNameSeg[1];
320 pch[2] = pNsEntry->achNameSeg[2];
321 pch[3] = pNsEntry->achNameSeg[3];
322 pch[4] = '.';
323 pch += 5;
324 }
325 cchNamePath += 5;
326 }
327
328 /* Append the last name segment. */
329 if (cchNamePath + 5 < *pcchNamePath)
330 {
331 pch[0] = pszNameSegLast[0];
332 pch[1] = pszNameSegLast[1];
333 pch[2] = pszNameSegLast[2];
334 pch[3] = pszNameSegLast[3];
335 pch[4] = '\0';
336 cchNamePath += 4;
337 }
338
339 if (cchNamePath <= *pcchNamePath)
340 rc = VINF_SUCCESS;
341 }
342
343 *pcchNamePath = cchNamePath;
344 return rc;
345 }
346
347 *pcchNamePath = 0;
348 return VERR_NOT_FOUND;
349}
350
351
352DECLHIDDEN(int) rtAcpiNsPop(PRTACPINSROOT pNsRoot)
353{
354 AssertReturn(pNsRoot->idxNsStack, VERR_INVALID_STATE); /* The root can't be popped from the stack. */
355 pNsRoot->idxNsStack--;
356 return VINF_SUCCESS;
357}
358
359
360DECLHIDDEN(PCRTACPINSENTRY) rtAcpiNsLookup(PRTACPINSROOT pNsRoot, const char *pszNameString)
361{
362 return rtAcpiNsLookupWorker(pNsRoot, pszNameString, false /*fExcludeLast*/, NULL /*ppszNameSegLast*/);
363}
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