VirtualBox

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

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

Runtime/RTAcpi*: Implement constant folding for ShiftLeft (used by vbox.dsl), bugref:10733

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.4 KB
Line 
1/* $Id: acpi-ns.cpp 108234 2025-02-16 14:20:28Z 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.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_ACPI
42#include <iprt/assert.h>
43#include <iprt/err.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
82static PRTACPINSENTRY rtAcpiNsLookupWorkerSingleNameSeg(PCRTACPINSENTRY pNsEntry, const char *pszNameSeg)
83{
84 do
85 {
86 PRTACPINSENTRY pIt;
87 RTListForEach(&pNsEntry->LstNsEntries, pIt, RTACPINSENTRY, NdNs)
88 {
89 if (!memcmp(&pIt->achNameSeg[0], pszNameSeg, sizeof(pIt->achNameSeg)))
90 return pIt;
91 }
92
93 pNsEntry = pNsEntry->pParent;
94 } while (pNsEntry);
95
96 return NULL;
97}
98
99
100/**
101 * Worker for looking up the entry in the given namespace for a given namestring.
102 *
103 * @returns Pointer to the namespace entry if found or NULL if not.
104 * @param pNsRoot The namespace to search in.
105 * @param pszNameString The namestring to search.
106 * @param fExcludeLast Flag whether to exclude the last name segment from the search and return
107 * the pointer to the second to last entry.
108 * @param ppszNameSegLast Where to store the pointe to the last name segment on success (if fExcludeLast is true).
109 */
110static PRTACPINSENTRY rtAcpiNsLookupWorker(PRTACPINSROOT pNsRoot, const char *pszNameString, bool fExcludeLast,
111 const char **ppszNameSegLast)
112{
113 AssertReturn(*pszNameString != '\0', NULL);
114
115 /* Find the starting position. */
116 PRTACPINSENTRY pNsEntry;
117 const char *pszCur = pszNameString;
118 if (*pszCur == '\\')
119 {
120 /* Search from the root of the namespace. */
121 pNsEntry = &pNsRoot->RootEntry;
122 pszCur++;
123 }
124 else if (*pszCur == '^')
125 {
126 /* Go up the tree. */
127 pNsEntry = pNsRoot->aNsStack[pNsRoot->idxNsStack];
128 while (*pszCur == '^')
129 {
130 if (!pNsEntry->pParent) /* Too many levels up. */
131 return NULL;
132 pNsEntry = pNsEntry->pParent;
133 pszCur++;
134 }
135 }
136 else
137 {
138 pNsEntry = pNsRoot->aNsStack[pNsRoot->idxNsStack];
139
140 /* For single name segments there is a special search rule which searches recursively upwards in the namespace. */
141 if (pszNameString[4] == '\0')
142 {
143 if (fExcludeLast)
144 {
145 AssertPtr(ppszNameSegLast);
146 *ppszNameSegLast = pszNameString;
147 return pNsEntry;
148 }
149 else
150 return rtAcpiNsLookupWorkerSingleNameSeg(pNsEntry, pszNameString);
151 }
152 }
153
154 /* This ASSUMES the namestring has always full 4 character name segments and is well formed. */
155 do
156 {
157 Assert(pszCur[0] != '\0' && pszCur[1] != '\0' && pszCur[2] != '\0' && pszCur[3] != '\0');
158
159 if (fExcludeLast && pszCur[4] == '\0')
160 break;
161
162 PRTACPINSENTRY pIt;
163 bool fFound = false;
164 RTListForEach(&pNsEntry->LstNsEntries, pIt, RTACPINSENTRY, NdNs)
165 {
166 if (!memcmp(&pIt->achNameSeg[0], pszCur, sizeof(pIt->achNameSeg)))
167 {
168 pNsEntry = pIt;
169 fFound = true;
170 break;
171 }
172 }
173
174 /* The name path is invalid. */
175 if (!fFound)
176 return NULL;
177
178 pszCur += 4;
179 } while (*pszCur++ == '.');
180 AssertReturn( *pszCur == '\0'
181 || ( fExcludeLast
182 && pszCur[4] == '\0'),
183 NULL);
184
185 if (ppszNameSegLast)
186 *ppszNameSegLast = pszCur;
187 return pNsEntry;
188}
189
190
191/**
192 * Looks up a name string under the specified entry.
193 *
194 * @returns Pointer to the namespace entry or NULL if not found.
195 * @param pNsEntry The namespace entry to start searching at.
196 * @param pszNameString The name string to look for.
197 */
198static PCRTACPINSENTRY rtAcpiNsLookupSubTree(PCRTACPINSENTRY pNsEntry, const char *pszNameString)
199{
200 /* This ASSUMES the namestring has always full 4 character name segments and is well formed. */
201 do
202 {
203 Assert(pszNameString[0] != '\0' && pszNameString[1] != '\0' && pszNameString[2] != '\0' && pszNameString[3] != '\0');
204
205 PCRTACPINSENTRY pIt;
206 bool fFound = false;
207 RTListForEach(&pNsEntry->LstNsEntries, pIt, RTACPINSENTRY, NdNs)
208 {
209 if (!memcmp(&pIt->achNameSeg[0], pszNameString, sizeof(pIt->achNameSeg)))
210 {
211 pNsEntry = pIt;
212 fFound = true;
213 break;
214 }
215 }
216
217 /* The name path is invalid. */
218 if (!fFound)
219 return NULL;
220
221 pszNameString += 4;
222 } while (*pszNameString++ == '.');
223
224 return pNsEntry;
225}
226
227
228/**
229 * Adds a new entry in the given namespace under the given path.
230 *
231 * @returns IPRT status code.
232 * @param pNsRoot The namespace to add the new entry to.
233 * @param enmType The type of the namespace entry.
234 * @param pszNameString The namestring to add.
235 * @param fSwitchTo Flag whether to switch to the new entry.
236 * @param fIgnoreExisting Flag whether to ignore any existing entry in the namespace parents.
237 * @param ppNsEntry Where to store the pointer to the created entry on success.
238 */
239static int rtAcpiNsAddEntryWorker(PRTACPINSROOT pNsRoot, RTACPINSENTRYTYPE enmType, const char *pszNameString, bool fSwitchTo, bool fIgnoreExisting, PRTACPINSENTRY *ppNsEntry)
240{
241 AssertReturn( !fSwitchTo
242 || pNsRoot->idxNsStack < RT_ELEMENTS(pNsRoot->aNsStack),
243 VERR_INVALID_STATE);
244
245 /* Does it exist already? */
246 if (!fIgnoreExisting)
247 {
248 PRTACPINSENTRY pNsEntry = rtAcpiNsLookupWorker(pNsRoot, pszNameString, false /*fExcludeLast*/, NULL);
249 if (pNsEntry)
250 {
251 *ppNsEntry = pNsEntry;
252 if (fSwitchTo)
253 pNsRoot->aNsStack[++pNsRoot->idxNsStack] = pNsEntry;
254 return VERR_ALREADY_EXISTS;
255 }
256 }
257
258 int rc;
259 const char *pszNameSegLast = NULL;
260 PRTACPINSENTRY pNsEntryParent = rtAcpiNsLookupWorker(pNsRoot, pszNameString, true /*fExcludeLast*/, &pszNameSegLast);
261 if (pNsEntryParent)
262 {
263 PRTACPINSENTRY pNsEntry = (PRTACPINSENTRY)RTMemAllocZ(sizeof(*pNsEntry));
264 if (pNsEntry)
265 {
266 pNsEntry->enmType = enmType;
267 pNsEntry->pParent = pNsEntryParent;
268 RTListInit(&pNsEntry->LstNsEntries);
269
270 memcpy(&pNsEntry->achNameSeg[0], pszNameSegLast, sizeof(pNsEntry->achNameSeg));
271
272 RTListAppend(&pNsEntryParent->LstNsEntries, &pNsEntry->NdNs);
273 *ppNsEntry = pNsEntry;
274 if (fSwitchTo)
275 pNsRoot->aNsStack[++pNsRoot->idxNsStack] = pNsEntry;
276 return VINF_SUCCESS;
277 }
278 else
279 rc = VERR_NO_MEMORY;
280 }
281 else
282 rc = VERR_NOT_FOUND;
283
284 return rc;
285}
286
287
288DECLHIDDEN(PRTACPINSROOT) rtAcpiNsCreate(void)
289{
290 PRTACPINSROOT pNsRoot = (PRTACPINSROOT)RTMemAllocZ(sizeof(*pNsRoot));
291 if (pNsRoot)
292 {
293 RTListInit(&pNsRoot->RootEntry.LstNsEntries);
294 pNsRoot->RootEntry.pParent = NULL;
295 pNsRoot->idxNsStack = 0;
296 pNsRoot->aNsStack[pNsRoot->idxNsStack] = &pNsRoot->RootEntry;
297 /* Create the default scopes. */
298 int rc = rtAcpiNsAddEntryAstNode(pNsRoot, "\\_SB_", NULL /*pAstNd*/, false /*fSwitchTo*/);
299 if (RT_SUCCESS(rc))
300 rc = rtAcpiNsAddEntryAstNode(pNsRoot, "\\_PR_", NULL /*pAstNd*/, false /*fSwitchTo*/);
301 if (RT_SUCCESS(rc))
302 rc = rtAcpiNsAddEntryAstNode(pNsRoot, "\\_GPE", NULL /*pAstNd*/, false /*fSwitchTo*/);
303 if (RT_SUCCESS(rc))
304 rc = rtAcpiNsAddEntryAstNode(pNsRoot, "\\_SI_", NULL /*pAstNd*/, false /*fSwitchTo*/);
305 if (RT_SUCCESS(rc))
306 rc = rtAcpiNsAddEntryAstNode(pNsRoot, "\\_TZ_", NULL /*pAstNd*/, false /*fSwitchTo*/);
307 Assert(RT_SUCCESS(rc) || rc == VERR_NO_MEMORY);
308 if (RT_FAILURE(rc))
309 {
310 RTMemFree(pNsRoot);
311 pNsRoot = NULL;
312 }
313 }
314 return pNsRoot;
315}
316
317
318DECLHIDDEN(void) rtAcpiNsDestroy(PRTACPINSROOT pNsRoot)
319{
320 PRTACPINSENTRY pIt, pItNext;
321 RTListForEachSafe(&pNsRoot->RootEntry.LstNsEntries, pIt, pItNext, RTACPINSENTRY, NdNs)
322 {
323 RTListNodeRemove(&pIt->NdNs);
324 rtAcpiNsEntryDestroy(pIt);
325 }
326 RTMemFree(pNsRoot);
327}
328
329
330DECLHIDDEN(int) rtAcpiNsSwitchTo(PRTACPINSROOT pNsRoot, const char *pszNameString)
331{
332 PRTACPINSENTRY pNsEntry = rtAcpiNsLookup(pNsRoot, pszNameString);
333 if (!pNsEntry)
334 return VERR_NOT_FOUND;
335
336 pNsRoot->aNsStack[++pNsRoot->idxNsStack] = pNsEntry;
337 return VINF_SUCCESS;
338}
339
340
341DECLHIDDEN(int) rtAcpiNsAddEntryAstNode(PRTACPINSROOT pNsRoot, const char *pszNameString, PCRTACPIASTNODE pAstNd, bool fSwitchTo)
342{
343 PRTACPINSENTRY pNsEntry = NULL;
344 int rc = rtAcpiNsAddEntryWorker(pNsRoot, kAcpiNsEntryType_AstNode, pszNameString, fSwitchTo, false /*fIgnoreExisting*/, &pNsEntry);
345 if (rc == VERR_ALREADY_EXISTS)
346 rc = VINF_SUCCESS;
347 if (RT_SUCCESS(rc))
348 pNsEntry->pAstNd = pAstNd;
349
350 return rc;
351}
352
353
354DECLHIDDEN(int) rtAcpiNsAddEntryRsrcField(PRTACPINSROOT pNsRoot, const char *pszNameString, uint32_t offBits, uint32_t cBits)
355{
356 PRTACPINSENTRY pNsEntry = NULL;
357 int rc = rtAcpiNsAddEntryWorker(pNsRoot, kAcpiNsEntryType_ResourceField, pszNameString, false /*fSwitchTo*/, true /*fIgnoreExisting*/, &pNsEntry);
358 if (RT_SUCCESS(rc))
359 {
360 pNsEntry->RsrcFld.offBits = offBits;
361 pNsEntry->RsrcFld.cBits = cBits;
362 }
363
364 return rc;
365}
366
367
368DECLHIDDEN(int) rtAcpiNsAddEntryExternal(PRTACPINSROOT pNsRoot, const char *pszNameString, PCRTACPIASLEXTERNAL pExternal)
369{
370 PRTACPINSENTRY pNsEntry = NULL;
371 int rc = rtAcpiNsAddEntryWorker(pNsRoot, kAcpiNsEntryType_External, pszNameString, false /*fSwitchTo*/, false /*fIgnoreExisting*/, &pNsEntry);
372 if (RT_SUCCESS(rc))
373 pNsEntry->pExternal = pExternal;
374
375 return rc;
376}
377
378
379DECLHIDDEN(int) rtAcpiNsQueryNamePathForNameString(PRTACPINSROOT pNsRoot, const char *pszNameString, char *pachNamePath, size_t *pcchNamePath)
380{
381 AssertReturn(!pachNamePath || *pcchNamePath >= 6, VERR_INVALID_PARAMETER); /* Needs to support at least \XXXX and the zero terminator. */
382
383 const char *pszNameSegLast = NULL;
384 PCRTACPINSENTRY pNsEntry = rtAcpiNsLookupWorker(pNsRoot, pszNameString, true /*fExcludeLast*/, &pszNameSegLast);
385 if (pNsEntry)
386 {
387 int rc = VERR_BUFFER_OVERFLOW;
388 size_t cchNamePath = 1; /* For the root prefix. */
389
390 if (!pachNamePath)
391 {
392 /* Calculate the name path size based on the number of segments. */
393 uint32_t cEntries = 0;
394 do
395 {
396 cEntries++;
397 pNsEntry = pNsEntry->pParent;
398 } while (pNsEntry);
399
400 cchNamePath += cEntries * (4 + 1) - 1; /* XXXX., except for the last one. */
401 }
402 else
403 {
404 uint32_t idxEntry = 0;
405 PCRTACPINSENTRY aNsEntries[255]; /* Maximum amount of name segments possible. */
406 do
407 {
408 aNsEntries[idxEntry++] = pNsEntry;
409 pNsEntry = pNsEntry->pParent;
410 } while (pNsEntry);
411
412 char *pch = pachNamePath;
413 *pch++ = '\\';
414 *pch = '\0';
415
416 /* The last entry must be the root entry. */
417 idxEntry--;
418 Assert(!aNsEntries[idxEntry]->pParent);
419
420 while (idxEntry)
421 {
422 pNsEntry = aNsEntries[--idxEntry];
423 if (cchNamePath + 5 < *pcchNamePath)
424 {
425 pch[0] = pNsEntry->achNameSeg[0];
426 pch[1] = pNsEntry->achNameSeg[1];
427 pch[2] = pNsEntry->achNameSeg[2];
428 pch[3] = pNsEntry->achNameSeg[3];
429 pch[4] = '.';
430 pch += 5;
431 }
432 cchNamePath += 5;
433 }
434
435 /* Append the last name segment. */
436 if (cchNamePath + 5 < *pcchNamePath)
437 {
438 pch[0] = pszNameSegLast[0];
439 pch[1] = pszNameSegLast[1];
440 pch[2] = pszNameSegLast[2];
441 pch[3] = pszNameSegLast[3];
442 pch[4] = '\0';
443 cchNamePath += 4;
444 }
445
446 if (cchNamePath <= *pcchNamePath)
447 rc = VINF_SUCCESS;
448 }
449
450 *pcchNamePath = cchNamePath;
451 return rc;
452 }
453
454 *pcchNamePath = 0;
455 return VERR_NOT_FOUND;
456}
457
458
459DECLHIDDEN(int) rtAcpiNsCompressNameString(PCRTACPINSROOT pNsRoot, PCRTACPINSENTRY pNsEntry, const char *pszNameString, char *pszNameStringComp, size_t cchNameStringComp)
460{
461 size_t cchNameString = strlen(pszNameString);
462 if (cchNameString > cchNameStringComp)
463 return VERR_BUFFER_OVERFLOW;
464
465 if ( *pszNameString != '\\'
466 || pNsEntry != rtAcpiNsGetCurrent(pNsRoot))
467 {
468 memcpy(pszNameStringComp, pszNameString, cchNameString + 1);
469 return VINF_SUCCESS;
470 }
471
472 /* Try to remove as many components as possible. */
473 uint32_t cEntries = 0;
474 PCRTACPINSENTRY aNsEntries[255]; /* Maximum amount of name segments possible. */
475 do
476 {
477 aNsEntries[cEntries++] = pNsEntry;
478 pNsEntry = pNsEntry->pParent;
479 } while (pNsEntry);
480
481 Assert(cEntries > 0); /* Should have at least the root entry. */
482
483 /* Remove the \ specifier. */
484 pszNameString++;
485 cchNameString--;
486 uint32_t idxEntry = 1;
487 while (idxEntry < cEntries)
488 {
489 pNsEntry = aNsEntries[idxEntry++];
490 if (memcmp(pszNameString, &pNsEntry->achNameSeg[0], sizeof(pNsEntry->achNameSeg)))
491 break;
492
493 Assert(pszNameString[4] == '.');
494 pszNameString += 5;
495 cchNameString -= 5;
496 }
497
498 /* The remaining string is what we end up with. */
499 memcpy(pszNameStringComp, pszNameString, cchNameString + 1);
500 return VINF_SUCCESS;
501}
502
503
504DECLHIDDEN(int) rtAcpiNsAbsoluteNameStringToRelative(PRTACPINSROOT pNsRoot, PCRTACPINSENTRY pNsEntrySrc, const char *pszNameStringDst, char *pszNameStringRel, size_t cchNameStringRel)
505{
506 size_t cchNameStringDst = strlen(pszNameStringDst);
507 if (cchNameStringDst > cchNameStringRel)
508 return VERR_BUFFER_OVERFLOW;
509
510 /* Init with the default. */
511 memcpy(pszNameStringRel, pszNameStringDst, cchNameStringDst + 1);
512 if (*pszNameStringDst != '\\')
513 return VINF_SUCCESS;
514
515 PCRTACPINSENTRY pNsDst = rtAcpiNsLookup(pNsRoot, pszNameStringDst);
516 AssertReturn(pNsDst, VERR_NOT_FOUND);
517
518 uint32_t cEntriesSrc = 0;
519 PCRTACPINSENTRY aNsEntriesSrc[255]; /* Maximum amount of name segments possible. */
520 do
521 {
522 aNsEntriesSrc[cEntriesSrc++] = pNsEntrySrc;
523 pNsEntrySrc = pNsEntrySrc->pParent;
524 } while (pNsEntrySrc);
525
526 uint32_t cEntriesDst = 0;
527 PCRTACPINSENTRY aNsEntriesDst[255]; /* Maximum amount of name segments possible. */
528 do
529 {
530 aNsEntriesDst[cEntriesDst++] = pNsDst;
531 pNsDst = pNsDst->pParent;
532 } while (pNsDst);
533
534 Assert(cEntriesSrc > 0 && cEntriesDst > 0); /* Should have at least the root entry. */
535 uint32_t idxEntrySrc = cEntriesSrc;
536 idxEntrySrc--;
537 cEntriesDst--;
538 Assert(aNsEntriesSrc[idxEntrySrc] == aNsEntriesDst[cEntriesDst]);
539
540 /* Remove the \ specifier. */
541 size_t cchNameStringNew = cchNameStringDst;
542 pszNameStringDst++;
543 cchNameStringNew--;
544
545 /* Find the first different path entry. */
546 while ( idxEntrySrc
547 && cEntriesDst)
548 {
549 if ( aNsEntriesSrc[--idxEntrySrc] != aNsEntriesDst[--cEntriesDst]
550 || pszNameStringDst[4] == '\0')
551 break;
552
553 Assert(pszNameStringDst[4] == '.');
554 pszNameStringDst += 5;
555 cchNameStringNew -= 5;
556 }
557
558 /*
559 * Calculate how many parent prefixes we need to add.
560 * If the remaining name path is just a segment it must be a
561 * direct parent of the source and can be written as a simple name segment
562 * due to the default search rules.
563 */
564 uint32_t cParentPrefixes = ( rtAcpiNsLookupSubTree(aNsEntriesSrc[idxEntrySrc], pszNameStringDst)
565 || pszNameStringDst[4] == '\0')
566 ? 0
567 : idxEntrySrc + 1;
568 /* Only overwrite with our result if it is shorter. */
569 if (cParentPrefixes + cchNameStringNew < cchNameStringDst)
570 {
571 for (uint32_t i = 0; i < cParentPrefixes; i++)
572 pszNameStringRel[i] = '^';
573 memcpy(&pszNameStringRel[cParentPrefixes], pszNameStringDst, cchNameStringNew + 1);
574 }
575 return VINF_SUCCESS;
576}
577
578
579DECLHIDDEN(int) rtAcpiNsPop(PRTACPINSROOT pNsRoot)
580{
581 AssertReturn(pNsRoot->idxNsStack, VERR_INVALID_STATE); /* The root can't be popped from the stack. */
582 pNsRoot->idxNsStack--;
583 return VINF_SUCCESS;
584}
585
586
587DECLHIDDEN(PRTACPINSENTRY) rtAcpiNsLookup(PRTACPINSROOT pNsRoot, const char *pszNameString)
588{
589 return rtAcpiNsLookupWorker(pNsRoot, pszNameString, false /*fExcludeLast*/, NULL /*ppszNameSegLast*/);
590}
591
592
593DECLHIDDEN(PCRTACPINSENTRY) rtAcpiNsGetCurrent(PCRTACPINSROOT pNsRoot)
594{
595 return pNsRoot->aNsStack[pNsRoot->idxNsStack];
596}
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