1 | /****************************************************************************** |
2 | * |
3 | * Module Name: exconfig - Namespace reconfiguration (Load/Unload opcodes) |
4 | * |
5 | *****************************************************************************/ |
6 | |
7 | /* |
8 | * Copyright (C) 2000 - 2016, Intel Corp. |
9 | * All rights reserved. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions, and the following disclaimer, |
16 | * without modification. |
17 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
18 | * substantially similar to the "NO WARRANTY" disclaimer below |
19 | * ("Disclaimer") and any redistribution must be conditioned upon |
20 | * including a substantially similar Disclaimer requirement for further |
21 | * binary redistribution. |
22 | * 3. Neither the names of the above-listed copyright holders nor the names |
23 | * of any contributors may be used to endorse or promote products derived |
24 | * from this software without specific prior written permission. |
25 | * |
26 | * Alternatively, this software may be distributed under the terms of the |
27 | * GNU General Public License ("GPL") version 2 as published by the Free |
28 | * Software Foundation. |
29 | * |
30 | * NO WARRANTY |
31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
32 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
33 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR |
34 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
35 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
36 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
37 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
38 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
39 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
40 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
41 | * POSSIBILITY OF SUCH DAMAGES. |
42 | */ |
43 | |
44 | #include "acpi.h" |
45 | #include "accommon.h" |
46 | #include "acinterp.h" |
47 | #include "acnamesp.h" |
48 | #include "actables.h" |
49 | #include "acdispat.h" |
50 | #include "acevents.h" |
51 | #include "amlcode.h" |
52 | |
53 | |
54 | #define _COMPONENT ACPI_EXECUTER |
55 | ACPI_MODULE_NAME ("exconfig" ) |
56 | |
57 | /* Local prototypes */ |
58 | |
59 | static ACPI_STATUS |
60 | AcpiExAddTable ( |
61 | UINT32 TableIndex, |
62 | ACPI_OPERAND_OBJECT **DdbHandle); |
63 | |
64 | static ACPI_STATUS |
65 | AcpiExRegionRead ( |
66 | ACPI_OPERAND_OBJECT *ObjDesc, |
67 | UINT32 Length, |
68 | UINT8 *Buffer); |
69 | |
70 | |
71 | /******************************************************************************* |
72 | * |
73 | * FUNCTION: AcpiExAddTable |
74 | * |
75 | * PARAMETERS: Table - Pointer to raw table |
76 | * ParentNode - Where to load the table (scope) |
77 | * DdbHandle - Where to return the table handle. |
78 | * |
79 | * RETURN: Status |
80 | * |
81 | * DESCRIPTION: Common function to Install and Load an ACPI table with a |
82 | * returned table handle. |
83 | * |
84 | ******************************************************************************/ |
85 | |
86 | static ACPI_STATUS |
87 | AcpiExAddTable ( |
88 | UINT32 TableIndex, |
89 | ACPI_OPERAND_OBJECT **DdbHandle) |
90 | { |
91 | ACPI_OPERAND_OBJECT *ObjDesc; |
92 | |
93 | |
94 | ACPI_FUNCTION_TRACE (ExAddTable); |
95 | |
96 | |
97 | /* Create an object to be the table handle */ |
98 | |
99 | ObjDesc = AcpiUtCreateInternalObject (ACPI_TYPE_LOCAL_REFERENCE); |
100 | if (!ObjDesc) |
101 | { |
102 | return_ACPI_STATUS (AE_NO_MEMORY); |
103 | } |
104 | |
105 | /* Init the table handle */ |
106 | |
107 | ObjDesc->Common.Flags |= AOPOBJ_DATA_VALID; |
108 | ObjDesc->Reference.Class = ACPI_REFCLASS_TABLE; |
109 | ObjDesc->Reference.Value = TableIndex; |
110 | *DdbHandle = ObjDesc; |
111 | return_ACPI_STATUS (AE_OK); |
112 | } |
113 | |
114 | |
115 | /******************************************************************************* |
116 | * |
117 | * FUNCTION: AcpiExLoadTableOp |
118 | * |
119 | * PARAMETERS: WalkState - Current state with operands |
120 | * ReturnDesc - Where to store the return object |
121 | * |
122 | * RETURN: Status |
123 | * |
124 | * DESCRIPTION: Load an ACPI table from the RSDT/XSDT |
125 | * |
126 | ******************************************************************************/ |
127 | |
128 | ACPI_STATUS |
129 | AcpiExLoadTableOp ( |
130 | ACPI_WALK_STATE *WalkState, |
131 | ACPI_OPERAND_OBJECT **ReturnDesc) |
132 | { |
133 | ACPI_STATUS Status; |
134 | ACPI_OPERAND_OBJECT **Operand = &WalkState->Operands[0]; |
135 | ACPI_NAMESPACE_NODE *ParentNode; |
136 | ACPI_NAMESPACE_NODE *StartNode; |
137 | ACPI_NAMESPACE_NODE *ParameterNode = NULL; |
138 | ACPI_OPERAND_OBJECT *DdbHandle; |
139 | UINT32 TableIndex; |
140 | |
141 | |
142 | ACPI_FUNCTION_TRACE (ExLoadTableOp); |
143 | |
144 | |
145 | /* Find the ACPI table in the RSDT/XSDT */ |
146 | |
147 | AcpiExExitInterpreter (); |
148 | Status = AcpiTbFindTable ( |
149 | Operand[0]->String.Pointer, |
150 | Operand[1]->String.Pointer, |
151 | Operand[2]->String.Pointer, &TableIndex); |
152 | AcpiExEnterInterpreter (); |
153 | if (ACPI_FAILURE (Status)) |
154 | { |
155 | if (Status != AE_NOT_FOUND) |
156 | { |
157 | return_ACPI_STATUS (Status); |
158 | } |
159 | |
160 | /* Table not found, return an Integer=0 and AE_OK */ |
161 | |
162 | DdbHandle = AcpiUtCreateIntegerObject ((UINT64) 0); |
163 | if (!DdbHandle) |
164 | { |
165 | return_ACPI_STATUS (AE_NO_MEMORY); |
166 | } |
167 | |
168 | *ReturnDesc = DdbHandle; |
169 | return_ACPI_STATUS (AE_OK); |
170 | } |
171 | |
172 | /* Default nodes */ |
173 | |
174 | StartNode = WalkState->ScopeInfo->Scope.Node; |
175 | ParentNode = AcpiGbl_RootNode; |
176 | |
177 | /* RootPath (optional parameter) */ |
178 | |
179 | if (Operand[3]->String.Length > 0) |
180 | { |
181 | /* |
182 | * Find the node referenced by the RootPathString. This is the |
183 | * location within the namespace where the table will be loaded. |
184 | */ |
185 | Status = AcpiNsGetNodeUnlocked (StartNode, |
186 | Operand[3]->String.Pointer, ACPI_NS_SEARCH_PARENT, |
187 | &ParentNode); |
188 | if (ACPI_FAILURE (Status)) |
189 | { |
190 | return_ACPI_STATUS (Status); |
191 | } |
192 | } |
193 | |
194 | /* ParameterPath (optional parameter) */ |
195 | |
196 | if (Operand[4]->String.Length > 0) |
197 | { |
198 | if ((Operand[4]->String.Pointer[0] != AML_ROOT_PREFIX) && |
199 | (Operand[4]->String.Pointer[0] != AML_PARENT_PREFIX)) |
200 | { |
201 | /* |
202 | * Path is not absolute, so it will be relative to the node |
203 | * referenced by the RootPathString (or the NS root if omitted) |
204 | */ |
205 | StartNode = ParentNode; |
206 | } |
207 | |
208 | /* Find the node referenced by the ParameterPathString */ |
209 | |
210 | Status = AcpiNsGetNodeUnlocked (StartNode, |
211 | Operand[4]->String.Pointer, ACPI_NS_SEARCH_PARENT, |
212 | &ParameterNode); |
213 | if (ACPI_FAILURE (Status)) |
214 | { |
215 | return_ACPI_STATUS (Status); |
216 | } |
217 | } |
218 | |
219 | /* Load the table into the namespace */ |
220 | |
221 | ACPI_INFO (("Dynamic OEM Table Load:" )); |
222 | AcpiExExitInterpreter (); |
223 | Status = AcpiTbLoadTable (TableIndex, ParentNode); |
224 | AcpiExEnterInterpreter (); |
225 | if (ACPI_FAILURE (Status)) |
226 | { |
227 | return_ACPI_STATUS (Status); |
228 | } |
229 | |
230 | Status = AcpiExAddTable (TableIndex, &DdbHandle); |
231 | if (ACPI_FAILURE (Status)) |
232 | { |
233 | return_ACPI_STATUS (Status); |
234 | } |
235 | |
236 | /* Parameter Data (optional) */ |
237 | |
238 | if (ParameterNode) |
239 | { |
240 | /* Store the parameter data into the optional parameter object */ |
241 | |
242 | Status = AcpiExStore (Operand[5], |
243 | ACPI_CAST_PTR (ACPI_OPERAND_OBJECT, ParameterNode), WalkState); |
244 | if (ACPI_FAILURE (Status)) |
245 | { |
246 | (void) AcpiExUnloadTable (DdbHandle); |
247 | |
248 | AcpiUtRemoveReference (DdbHandle); |
249 | return_ACPI_STATUS (Status); |
250 | } |
251 | } |
252 | |
253 | *ReturnDesc = DdbHandle; |
254 | return_ACPI_STATUS (Status); |
255 | } |
256 | |
257 | |
258 | /******************************************************************************* |
259 | * |
260 | * FUNCTION: AcpiExRegionRead |
261 | * |
262 | * PARAMETERS: ObjDesc - Region descriptor |
263 | * Length - Number of bytes to read |
264 | * Buffer - Pointer to where to put the data |
265 | * |
266 | * RETURN: Status |
267 | * |
268 | * DESCRIPTION: Read data from an operation region. The read starts from the |
269 | * beginning of the region. |
270 | * |
271 | ******************************************************************************/ |
272 | |
273 | static ACPI_STATUS |
274 | AcpiExRegionRead ( |
275 | ACPI_OPERAND_OBJECT *ObjDesc, |
276 | UINT32 Length, |
277 | UINT8 *Buffer) |
278 | { |
279 | ACPI_STATUS Status; |
280 | UINT64 Value; |
281 | UINT32 RegionOffset = 0; |
282 | UINT32 i; |
283 | |
284 | |
285 | /* Bytewise reads */ |
286 | |
287 | for (i = 0; i < Length; i++) |
288 | { |
289 | Status = AcpiEvAddressSpaceDispatch (ObjDesc, NULL, ACPI_READ, |
290 | RegionOffset, 8, &Value); |
291 | if (ACPI_FAILURE (Status)) |
292 | { |
293 | return (Status); |
294 | } |
295 | |
296 | *Buffer = (UINT8) Value; |
297 | Buffer++; |
298 | RegionOffset++; |
299 | } |
300 | |
301 | return (AE_OK); |
302 | } |
303 | |
304 | |
305 | /******************************************************************************* |
306 | * |
307 | * FUNCTION: AcpiExLoadOp |
308 | * |
309 | * PARAMETERS: ObjDesc - Region or Buffer/Field where the table will be |
310 | * obtained |
311 | * Target - Where a handle to the table will be stored |
312 | * WalkState - Current state |
313 | * |
314 | * RETURN: Status |
315 | * |
316 | * DESCRIPTION: Load an ACPI table from a field or operation region |
317 | * |
318 | * NOTE: Region Fields (Field, BankField, IndexFields) are resolved to buffer |
319 | * objects before this code is reached. |
320 | * |
321 | * If source is an operation region, it must refer to SystemMemory, as |
322 | * per the ACPI specification. |
323 | * |
324 | ******************************************************************************/ |
325 | |
326 | ACPI_STATUS |
327 | AcpiExLoadOp ( |
328 | ACPI_OPERAND_OBJECT *ObjDesc, |
329 | ACPI_OPERAND_OBJECT *Target, |
330 | ACPI_WALK_STATE *WalkState) |
331 | { |
332 | ACPI_OPERAND_OBJECT *DdbHandle; |
333 | ACPI_TABLE_HEADER *; |
334 | ACPI_TABLE_HEADER *Table; |
335 | UINT32 TableIndex; |
336 | ACPI_STATUS Status; |
337 | UINT32 Length; |
338 | |
339 | |
340 | ACPI_FUNCTION_TRACE (ExLoadOp); |
341 | |
342 | |
343 | /* Source Object can be either an OpRegion or a Buffer/Field */ |
344 | |
345 | switch (ObjDesc->Common.Type) |
346 | { |
347 | case ACPI_TYPE_REGION: |
348 | |
349 | ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, |
350 | "Load table from Region %p\n" , ObjDesc)); |
351 | |
352 | /* Region must be SystemMemory (from ACPI spec) */ |
353 | |
354 | if (ObjDesc->Region.SpaceId != ACPI_ADR_SPACE_SYSTEM_MEMORY) |
355 | { |
356 | return_ACPI_STATUS (AE_AML_OPERAND_TYPE); |
357 | } |
358 | |
359 | /* |
360 | * If the Region Address and Length have not been previously |
361 | * evaluated, evaluate them now and save the results. |
362 | */ |
363 | if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID)) |
364 | { |
365 | Status = AcpiDsGetRegionArguments (ObjDesc); |
366 | if (ACPI_FAILURE (Status)) |
367 | { |
368 | return_ACPI_STATUS (Status); |
369 | } |
370 | } |
371 | |
372 | /* Get the table header first so we can get the table length */ |
373 | |
374 | TableHeader = ACPI_ALLOCATE (sizeof (ACPI_TABLE_HEADER)); |
375 | if (!TableHeader) |
376 | { |
377 | return_ACPI_STATUS (AE_NO_MEMORY); |
378 | } |
379 | |
380 | Status = AcpiExRegionRead (ObjDesc, sizeof (ACPI_TABLE_HEADER), |
381 | ACPI_CAST_PTR (UINT8, TableHeader)); |
382 | Length = TableHeader->Length; |
383 | ACPI_FREE (TableHeader); |
384 | |
385 | if (ACPI_FAILURE (Status)) |
386 | { |
387 | return_ACPI_STATUS (Status); |
388 | } |
389 | |
390 | /* Must have at least an ACPI table header */ |
391 | |
392 | if (Length < sizeof (ACPI_TABLE_HEADER)) |
393 | { |
394 | return_ACPI_STATUS (AE_INVALID_TABLE_LENGTH); |
395 | } |
396 | |
397 | /* |
398 | * The original implementation simply mapped the table, with no copy. |
399 | * However, the memory region is not guaranteed to remain stable and |
400 | * we must copy the table to a local buffer. For example, the memory |
401 | * region is corrupted after suspend on some machines. Dynamically |
402 | * loaded tables are usually small, so this overhead is minimal. |
403 | * |
404 | * The latest implementation (5/2009) does not use a mapping at all. |
405 | * We use the low-level operation region interface to read the table |
406 | * instead of the obvious optimization of using a direct mapping. |
407 | * This maintains a consistent use of operation regions across the |
408 | * entire subsystem. This is important if additional processing must |
409 | * be performed in the (possibly user-installed) operation region |
410 | * handler. For example, AcpiExec and ASLTS depend on this. |
411 | */ |
412 | |
413 | /* Allocate a buffer for the table */ |
414 | |
415 | Table = ACPI_ALLOCATE (Length); |
416 | if (!Table) |
417 | { |
418 | return_ACPI_STATUS (AE_NO_MEMORY); |
419 | } |
420 | |
421 | /* Read the entire table */ |
422 | |
423 | Status = AcpiExRegionRead (ObjDesc, Length, |
424 | ACPI_CAST_PTR (UINT8, Table)); |
425 | if (ACPI_FAILURE (Status)) |
426 | { |
427 | ACPI_FREE (Table); |
428 | return_ACPI_STATUS (Status); |
429 | } |
430 | break; |
431 | |
432 | case ACPI_TYPE_BUFFER: /* Buffer or resolved RegionField */ |
433 | |
434 | ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, |
435 | "Load table from Buffer or Field %p\n" , ObjDesc)); |
436 | |
437 | /* Must have at least an ACPI table header */ |
438 | |
439 | if (ObjDesc->Buffer.Length < sizeof (ACPI_TABLE_HEADER)) |
440 | { |
441 | return_ACPI_STATUS (AE_INVALID_TABLE_LENGTH); |
442 | } |
443 | |
444 | /* Get the actual table length from the table header */ |
445 | |
446 | TableHeader = ACPI_CAST_PTR ( |
447 | ACPI_TABLE_HEADER, ObjDesc->Buffer.Pointer); |
448 | Length = TableHeader->Length; |
449 | |
450 | /* Table cannot extend beyond the buffer */ |
451 | |
452 | if (Length > ObjDesc->Buffer.Length) |
453 | { |
454 | return_ACPI_STATUS (AE_AML_BUFFER_LIMIT); |
455 | } |
456 | if (Length < sizeof (ACPI_TABLE_HEADER)) |
457 | { |
458 | return_ACPI_STATUS (AE_INVALID_TABLE_LENGTH); |
459 | } |
460 | |
461 | /* |
462 | * Copy the table from the buffer because the buffer could be |
463 | * modified or even deleted in the future |
464 | */ |
465 | Table = ACPI_ALLOCATE (Length); |
466 | if (!Table) |
467 | { |
468 | return_ACPI_STATUS (AE_NO_MEMORY); |
469 | } |
470 | |
471 | memcpy (Table, TableHeader, Length); |
472 | break; |
473 | |
474 | default: |
475 | |
476 | return_ACPI_STATUS (AE_AML_OPERAND_TYPE); |
477 | } |
478 | |
479 | /* Install the new table into the local data structures */ |
480 | |
481 | ACPI_INFO (("Dynamic OEM Table Load:" )); |
482 | AcpiExExitInterpreter (); |
483 | Status = AcpiTbInstallAndLoadTable (Table, ACPI_PTR_TO_PHYSADDR (Table), |
484 | ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL, TRUE, &TableIndex); |
485 | AcpiExEnterInterpreter (); |
486 | if (ACPI_FAILURE (Status)) |
487 | { |
488 | /* Delete allocated table buffer */ |
489 | |
490 | ACPI_FREE (Table); |
491 | return_ACPI_STATUS (Status); |
492 | } |
493 | |
494 | /* |
495 | * Add the table to the namespace. |
496 | * |
497 | * Note: Load the table objects relative to the root of the namespace. |
498 | * This appears to go against the ACPI specification, but we do it for |
499 | * compatibility with other ACPI implementations. |
500 | */ |
501 | Status = AcpiExAddTable (TableIndex, &DdbHandle); |
502 | if (ACPI_FAILURE (Status)) |
503 | { |
504 | /* On error, TablePtr was deallocated above */ |
505 | |
506 | return_ACPI_STATUS (Status); |
507 | } |
508 | |
509 | /* Store the DdbHandle into the Target operand */ |
510 | |
511 | Status = AcpiExStore (DdbHandle, Target, WalkState); |
512 | if (ACPI_FAILURE (Status)) |
513 | { |
514 | (void) AcpiExUnloadTable (DdbHandle); |
515 | |
516 | /* TablePtr was deallocated above */ |
517 | |
518 | AcpiUtRemoveReference (DdbHandle); |
519 | return_ACPI_STATUS (Status); |
520 | } |
521 | |
522 | /* Remove the reference by added by AcpiExStore above */ |
523 | |
524 | AcpiUtRemoveReference (DdbHandle); |
525 | return_ACPI_STATUS (Status); |
526 | } |
527 | |
528 | |
529 | /******************************************************************************* |
530 | * |
531 | * FUNCTION: AcpiExUnloadTable |
532 | * |
533 | * PARAMETERS: DdbHandle - Handle to a previously loaded table |
534 | * |
535 | * RETURN: Status |
536 | * |
537 | * DESCRIPTION: Unload an ACPI table |
538 | * |
539 | ******************************************************************************/ |
540 | |
541 | ACPI_STATUS |
542 | AcpiExUnloadTable ( |
543 | ACPI_OPERAND_OBJECT *DdbHandle) |
544 | { |
545 | ACPI_STATUS Status = AE_OK; |
546 | ACPI_OPERAND_OBJECT *TableDesc = DdbHandle; |
547 | UINT32 TableIndex; |
548 | ACPI_TABLE_HEADER *Table; |
549 | |
550 | |
551 | ACPI_FUNCTION_TRACE (ExUnloadTable); |
552 | |
553 | |
554 | /* |
555 | * Temporarily emit a warning so that the ASL for the machine can be |
556 | * hopefully obtained. This is to say that the Unload() operator is |
557 | * extremely rare if not completely unused. |
558 | */ |
559 | ACPI_WARNING ((AE_INFO, |
560 | "Received request to unload an ACPI table" )); |
561 | |
562 | /* |
563 | * Validate the handle |
564 | * Although the handle is partially validated in AcpiExReconfiguration() |
565 | * when it calls AcpiExResolveOperands(), the handle is more completely |
566 | * validated here. |
567 | * |
568 | * Handle must be a valid operand object of type reference. Also, the |
569 | * DdbHandle must still be marked valid (table has not been previously |
570 | * unloaded) |
571 | */ |
572 | if ((!DdbHandle) || |
573 | (ACPI_GET_DESCRIPTOR_TYPE (DdbHandle) != ACPI_DESC_TYPE_OPERAND) || |
574 | (DdbHandle->Common.Type != ACPI_TYPE_LOCAL_REFERENCE) || |
575 | (!(DdbHandle->Common.Flags & AOPOBJ_DATA_VALID))) |
576 | { |
577 | return_ACPI_STATUS (AE_AML_OPERAND_TYPE); |
578 | } |
579 | |
580 | /* Get the table index from the DdbHandle */ |
581 | |
582 | TableIndex = TableDesc->Reference.Value; |
583 | |
584 | /* |
585 | * Release the interpreter lock so that the table lock won't have |
586 | * strict order requirement against it. |
587 | */ |
588 | AcpiExExitInterpreter (); |
589 | |
590 | /* Ensure the table is still loaded */ |
591 | |
592 | if (!AcpiTbIsTableLoaded (TableIndex)) |
593 | { |
594 | Status = AE_NOT_EXIST; |
595 | goto LockAndExit; |
596 | } |
597 | |
598 | /* Invoke table handler if present */ |
599 | |
600 | if (AcpiGbl_TableHandler) |
601 | { |
602 | Status = AcpiGetTableByIndex (TableIndex, &Table); |
603 | if (ACPI_SUCCESS (Status)) |
604 | { |
605 | (void) AcpiGbl_TableHandler (ACPI_TABLE_EVENT_UNLOAD, Table, |
606 | AcpiGbl_TableHandlerContext); |
607 | } |
608 | } |
609 | |
610 | /* Delete the portion of the namespace owned by this table */ |
611 | |
612 | Status = AcpiTbDeleteNamespaceByOwner (TableIndex); |
613 | if (ACPI_FAILURE (Status)) |
614 | { |
615 | goto LockAndExit; |
616 | } |
617 | |
618 | (void) AcpiTbReleaseOwnerId (TableIndex); |
619 | AcpiTbSetTableLoadedFlag (TableIndex, FALSE); |
620 | |
621 | LockAndExit: |
622 | |
623 | /* Re-acquire the interpreter lock */ |
624 | |
625 | AcpiExEnterInterpreter (); |
626 | |
627 | /* |
628 | * Invalidate the handle. We do this because the handle may be stored |
629 | * in a named object and may not be actually deleted until much later. |
630 | */ |
631 | if (ACPI_SUCCESS (Status)) |
632 | { |
633 | DdbHandle->Common.Flags &= ~AOPOBJ_DATA_VALID; |
634 | } |
635 | return_ACPI_STATUS (Status); |
636 | } |
637 | |