xref: /netbsd-src/sys/external/bsd/acpica/dist/tables/tbinstal.c (revision daf6c4152fcddc27c445489775ed1f66ab4ea9a9)
1 /******************************************************************************
2  *
3  * Module Name: tbinstal - ACPI table installation and removal
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2011, 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 
45 #define __TBINSTAL_C__
46 
47 #include "acpi.h"
48 #include "accommon.h"
49 #include "acnamesp.h"
50 #include "actables.h"
51 
52 
53 #define _COMPONENT          ACPI_TABLES
54         ACPI_MODULE_NAME    ("tbinstal")
55 
56 
57 /******************************************************************************
58  *
59  * FUNCTION:    AcpiTbVerifyTable
60  *
61  * PARAMETERS:  TableDesc           - table
62  *
63  * RETURN:      Status
64  *
65  * DESCRIPTION: this function is called to verify and map table
66  *
67  *****************************************************************************/
68 
69 ACPI_STATUS
70 AcpiTbVerifyTable (
71     ACPI_TABLE_DESC         *TableDesc)
72 {
73     ACPI_STATUS             Status = AE_OK;
74 
75 
76     ACPI_FUNCTION_TRACE (TbVerifyTable);
77 
78 
79     /* Map the table if necessary */
80 
81     if (!TableDesc->Pointer)
82     {
83         if ((TableDesc->Flags & ACPI_TABLE_ORIGIN_MASK) ==
84             ACPI_TABLE_ORIGIN_MAPPED)
85         {
86             TableDesc->Pointer = AcpiOsMapMemory (
87                 TableDesc->Address, TableDesc->Length);
88         }
89 
90         if (!TableDesc->Pointer)
91         {
92             return_ACPI_STATUS (AE_NO_MEMORY);
93         }
94     }
95 
96     /* FACS is the odd table, has no standard ACPI header and no checksum */
97 
98     if (!ACPI_COMPARE_NAME (&TableDesc->Signature, ACPI_SIG_FACS))
99     {
100         /* Always calculate checksum, ignore bad checksum if requested */
101 
102         Status = AcpiTbVerifyChecksum (TableDesc->Pointer, TableDesc->Length);
103     }
104 
105     return_ACPI_STATUS (Status);
106 }
107 
108 
109 /*******************************************************************************
110  *
111  * FUNCTION:    AcpiTbAddTable
112  *
113  * PARAMETERS:  TableDesc           - Table descriptor
114  *              TableIndex          - Where the table index is returned
115  *
116  * RETURN:      Status
117  *
118  * DESCRIPTION: This function is called to add an ACPI table. It is used to
119  *              dynamically load tables via the Load and LoadTable AML
120  *              operators.
121  *
122  ******************************************************************************/
123 
124 ACPI_STATUS
125 AcpiTbAddTable (
126     ACPI_TABLE_DESC         *TableDesc,
127     UINT32                  *TableIndex)
128 {
129     UINT32                  i;
130     ACPI_STATUS             Status = AE_OK;
131     ACPI_TABLE_HEADER       *OverrideTable = NULL;
132 
133 
134     ACPI_FUNCTION_TRACE (TbAddTable);
135 
136 
137     if (!TableDesc->Pointer)
138     {
139         Status = AcpiTbVerifyTable (TableDesc);
140         if (ACPI_FAILURE (Status) || !TableDesc->Pointer)
141         {
142             return_ACPI_STATUS (Status);
143         }
144     }
145 
146     /*
147      * Originally, we checked the table signature for "SSDT" or "PSDT" here.
148      * Next, we added support for OEMx tables, signature "OEM".
149      * Valid tables were encountered with a null signature, so we've just
150      * given up on validating the signature, since it seems to be a waste
151      * of code. The original code was removed (05/2008).
152      */
153 
154     (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
155 
156     /* Check if table is already registered */
157 
158     for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; ++i)
159     {
160         if (!AcpiGbl_RootTableList.Tables[i].Pointer)
161         {
162             Status = AcpiTbVerifyTable (&AcpiGbl_RootTableList.Tables[i]);
163             if (ACPI_FAILURE (Status) ||
164                 !AcpiGbl_RootTableList.Tables[i].Pointer)
165             {
166                 continue;
167             }
168         }
169 
170         /*
171          * Check for a table match on the entire table length,
172          * not just the header.
173          */
174         if (TableDesc->Length != AcpiGbl_RootTableList.Tables[i].Length)
175         {
176             continue;
177         }
178 
179         if (ACPI_MEMCMP (TableDesc->Pointer,
180                 AcpiGbl_RootTableList.Tables[i].Pointer,
181                 AcpiGbl_RootTableList.Tables[i].Length))
182         {
183             continue;
184         }
185 
186         /*
187          * Note: the current mechanism does not unregister a table if it is
188          * dynamically unloaded. The related namespace entries are deleted,
189          * but the table remains in the root table list.
190          *
191          * The assumption here is that the number of different tables that
192          * will be loaded is actually small, and there is minimal overhead
193          * in just keeping the table in case it is needed again.
194          *
195          * If this assumption changes in the future (perhaps on large
196          * machines with many table load/unload operations), tables will
197          * need to be unregistered when they are unloaded, and slots in the
198          * root table list should be reused when empty.
199          */
200 
201         /*
202          * Table is already registered.
203          * We can delete the table that was passed as a parameter.
204          */
205         AcpiTbDeleteTable (TableDesc);
206         *TableIndex = i;
207 
208         if (AcpiGbl_RootTableList.Tables[i].Flags & ACPI_TABLE_IS_LOADED)
209         {
210             /* Table is still loaded, this is an error */
211 
212             Status = AE_ALREADY_EXISTS;
213             goto Release;
214         }
215         else
216         {
217             /* Table was unloaded, allow it to be reloaded */
218 
219             TableDesc->Pointer = AcpiGbl_RootTableList.Tables[i].Pointer;
220             TableDesc->Address = AcpiGbl_RootTableList.Tables[i].Address;
221             Status = AE_OK;
222             goto PrintHeader;
223         }
224     }
225 
226     /*
227      * ACPI Table Override:
228      * Allow the host to override dynamically loaded tables.
229      */
230     Status = AcpiOsTableOverride (TableDesc->Pointer, &OverrideTable);
231     if (ACPI_SUCCESS (Status) && OverrideTable)
232     {
233         ACPI_INFO ((AE_INFO,
234             "%4.4s @ 0x%p Table override, replaced with:",
235             TableDesc->Pointer->Signature,
236             ACPI_CAST_PTR (void, TableDesc->Address)));
237 
238         /* We can delete the table that was passed as a parameter */
239 
240         AcpiTbDeleteTable (TableDesc);
241 
242         /* Setup descriptor for the new table */
243 
244         TableDesc->Address = ACPI_PTR_TO_PHYSADDR (OverrideTable);
245         TableDesc->Pointer = OverrideTable;
246         TableDesc->Length = OverrideTable->Length;
247         TableDesc->Flags = ACPI_TABLE_ORIGIN_OVERRIDE;
248     }
249 
250     /* Add the table to the global root table list */
251 
252     Status = AcpiTbStoreTable (TableDesc->Address, TableDesc->Pointer,
253                 TableDesc->Length, TableDesc->Flags, TableIndex);
254     if (ACPI_FAILURE (Status))
255     {
256         goto Release;
257     }
258 
259 PrintHeader:
260     AcpiTbPrintTableHeader (TableDesc->Address, TableDesc->Pointer);
261 
262 Release:
263     (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
264     return_ACPI_STATUS (Status);
265 }
266 
267 
268 /*******************************************************************************
269  *
270  * FUNCTION:    AcpiTbResizeRootTableList
271  *
272  * PARAMETERS:  None
273  *
274  * RETURN:      Status
275  *
276  * DESCRIPTION: Expand the size of global table array
277  *
278  ******************************************************************************/
279 
280 ACPI_STATUS
281 AcpiTbResizeRootTableList (
282     void)
283 {
284     ACPI_TABLE_DESC         *Tables;
285 
286 
287     ACPI_FUNCTION_TRACE (TbResizeRootTableList);
288 
289 
290     /* AllowResize flag is a parameter to AcpiInitializeTables */
291 
292     if (!(AcpiGbl_RootTableList.Flags & ACPI_ROOT_ALLOW_RESIZE))
293     {
294         ACPI_ERROR ((AE_INFO, "Resize of Root Table Array is not allowed"));
295         return_ACPI_STATUS (AE_SUPPORT);
296     }
297 
298     /* Increase the Table Array size */
299 
300     Tables = ACPI_ALLOCATE_ZEROED (
301         ((ACPI_SIZE) AcpiGbl_RootTableList.MaxTableCount +
302             ACPI_ROOT_TABLE_SIZE_INCREMENT) *
303         sizeof (ACPI_TABLE_DESC));
304     if (!Tables)
305     {
306         ACPI_ERROR ((AE_INFO, "Could not allocate new root table array"));
307         return_ACPI_STATUS (AE_NO_MEMORY);
308     }
309 
310     /* Copy and free the previous table array */
311 
312     if (AcpiGbl_RootTableList.Tables)
313     {
314         ACPI_MEMCPY (Tables, AcpiGbl_RootTableList.Tables,
315             (ACPI_SIZE) AcpiGbl_RootTableList.MaxTableCount * sizeof (ACPI_TABLE_DESC));
316 
317         if (AcpiGbl_RootTableList.Flags & ACPI_ROOT_ORIGIN_ALLOCATED)
318         {
319             ACPI_FREE (AcpiGbl_RootTableList.Tables);
320         }
321     }
322 
323     AcpiGbl_RootTableList.Tables = Tables;
324     AcpiGbl_RootTableList.MaxTableCount += ACPI_ROOT_TABLE_SIZE_INCREMENT;
325     AcpiGbl_RootTableList.Flags |= (UINT8) ACPI_ROOT_ORIGIN_ALLOCATED;
326 
327     return_ACPI_STATUS (AE_OK);
328 }
329 
330 
331 /*******************************************************************************
332  *
333  * FUNCTION:    AcpiTbStoreTable
334  *
335  * PARAMETERS:  Address             - Table address
336  *              Table               - Table header
337  *              Length              - Table length
338  *              Flags               - flags
339  *
340  * RETURN:      Status and table index.
341  *
342  * DESCRIPTION: Add an ACPI table to the global table list
343  *
344  ******************************************************************************/
345 
346 ACPI_STATUS
347 AcpiTbStoreTable (
348     ACPI_PHYSICAL_ADDRESS   Address,
349     ACPI_TABLE_HEADER       *Table,
350     UINT32                  Length,
351     UINT8                   Flags,
352     UINT32                  *TableIndex)
353 {
354     ACPI_STATUS             Status;
355     ACPI_TABLE_DESC         *NewTable;
356 
357 
358     /* Ensure that there is room for the table in the Root Table List */
359 
360     if (AcpiGbl_RootTableList.CurrentTableCount >=
361         AcpiGbl_RootTableList.MaxTableCount)
362     {
363         Status = AcpiTbResizeRootTableList();
364         if (ACPI_FAILURE (Status))
365         {
366             return (Status);
367         }
368     }
369 
370     NewTable = &AcpiGbl_RootTableList.Tables[AcpiGbl_RootTableList.CurrentTableCount];
371 
372     /* Initialize added table */
373 
374     NewTable->Address = Address;
375     NewTable->Pointer = Table;
376     NewTable->Length = Length;
377     NewTable->OwnerId = 0;
378     NewTable->Flags = Flags;
379 
380     ACPI_MOVE_32_TO_32 (&NewTable->Signature, Table->Signature);
381 
382     *TableIndex = AcpiGbl_RootTableList.CurrentTableCount;
383     AcpiGbl_RootTableList.CurrentTableCount++;
384     return (AE_OK);
385 }
386 
387 
388 /*******************************************************************************
389  *
390  * FUNCTION:    AcpiTbDeleteTable
391  *
392  * PARAMETERS:  TableIndex          - Table index
393  *
394  * RETURN:      None
395  *
396  * DESCRIPTION: Delete one internal ACPI table
397  *
398  ******************************************************************************/
399 
400 void
401 AcpiTbDeleteTable (
402     ACPI_TABLE_DESC         *TableDesc)
403 {
404 
405     /* Table must be mapped or allocated */
406 
407     if (!TableDesc->Pointer)
408     {
409         return;
410     }
411 
412     switch (TableDesc->Flags & ACPI_TABLE_ORIGIN_MASK)
413     {
414     case ACPI_TABLE_ORIGIN_MAPPED:
415         AcpiOsUnmapMemory (TableDesc->Pointer, TableDesc->Length);
416         break;
417 
418     case ACPI_TABLE_ORIGIN_ALLOCATED:
419         ACPI_FREE (TableDesc->Pointer);
420         break;
421 
422     default:
423         break;
424     }
425 
426     TableDesc->Pointer = NULL;
427 }
428 
429 
430 /*******************************************************************************
431  *
432  * FUNCTION:    AcpiTbTerminate
433  *
434  * PARAMETERS:  None
435  *
436  * RETURN:      None
437  *
438  * DESCRIPTION: Delete all internal ACPI tables
439  *
440  ******************************************************************************/
441 
442 void
443 AcpiTbTerminate (
444     void)
445 {
446     UINT32                  i;
447 
448 
449     ACPI_FUNCTION_TRACE (TbTerminate);
450 
451 
452     (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
453 
454     /* Delete the individual tables */
455 
456     for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++)
457     {
458         AcpiTbDeleteTable (&AcpiGbl_RootTableList.Tables[i]);
459     }
460 
461     /*
462      * Delete the root table array if allocated locally. Array cannot be
463      * mapped, so we don't need to check for that flag.
464      */
465     if (AcpiGbl_RootTableList.Flags & ACPI_ROOT_ORIGIN_ALLOCATED)
466     {
467         ACPI_FREE (AcpiGbl_RootTableList.Tables);
468     }
469 
470     AcpiGbl_RootTableList.Tables = NULL;
471     AcpiGbl_RootTableList.Flags = 0;
472     AcpiGbl_RootTableList.CurrentTableCount = 0;
473 
474     ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "ACPI Tables freed\n"));
475     (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
476 }
477 
478 
479 /*******************************************************************************
480  *
481  * FUNCTION:    AcpiTbDeleteNamespaceByOwner
482  *
483  * PARAMETERS:  TableIndex          - Table index
484  *
485  * RETURN:      Status
486  *
487  * DESCRIPTION: Delete all namespace objects created when this table was loaded.
488  *
489  ******************************************************************************/
490 
491 ACPI_STATUS
492 AcpiTbDeleteNamespaceByOwner (
493     UINT32                  TableIndex)
494 {
495     ACPI_OWNER_ID           OwnerId;
496     ACPI_STATUS             Status;
497 
498 
499     ACPI_FUNCTION_TRACE (TbDeleteNamespaceByOwner);
500 
501 
502     Status = AcpiUtAcquireMutex (ACPI_MTX_TABLES);
503     if (ACPI_FAILURE (Status))
504     {
505         return_ACPI_STATUS (Status);
506     }
507 
508     if (TableIndex >= AcpiGbl_RootTableList.CurrentTableCount)
509     {
510         /* The table index does not exist */
511 
512         (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
513         return_ACPI_STATUS (AE_NOT_EXIST);
514     }
515 
516     /* Get the owner ID for this table, used to delete namespace nodes */
517 
518     OwnerId = AcpiGbl_RootTableList.Tables[TableIndex].OwnerId;
519     (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
520 
521     /*
522      * Need to acquire the namespace writer lock to prevent interference
523      * with any concurrent namespace walks. The interpreter must be
524      * released during the deletion since the acquisition of the deletion
525      * lock may block, and also since the execution of a namespace walk
526      * must be allowed to use the interpreter.
527      */
528     (void) AcpiUtReleaseMutex (ACPI_MTX_INTERPRETER);
529     Status = AcpiUtAcquireWriteLock (&AcpiGbl_NamespaceRwLock);
530 
531     AcpiNsDeleteNamespaceByOwner (OwnerId);
532     if (ACPI_FAILURE (Status))
533     {
534         return_ACPI_STATUS (Status);
535     }
536 
537     AcpiUtReleaseWriteLock (&AcpiGbl_NamespaceRwLock);
538 
539     Status = AcpiUtAcquireMutex (ACPI_MTX_INTERPRETER);
540     return_ACPI_STATUS (Status);
541 }
542 
543 
544 /*******************************************************************************
545  *
546  * FUNCTION:    AcpiTbAllocateOwnerId
547  *
548  * PARAMETERS:  TableIndex          - Table index
549  *
550  * RETURN:      Status
551  *
552  * DESCRIPTION: Allocates OwnerId in TableDesc
553  *
554  ******************************************************************************/
555 
556 ACPI_STATUS
557 AcpiTbAllocateOwnerId (
558     UINT32                  TableIndex)
559 {
560     ACPI_STATUS             Status = AE_BAD_PARAMETER;
561 
562 
563     ACPI_FUNCTION_TRACE (TbAllocateOwnerId);
564 
565 
566     (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
567     if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
568     {
569         Status = AcpiUtAllocateOwnerId
570                     (&(AcpiGbl_RootTableList.Tables[TableIndex].OwnerId));
571     }
572 
573     (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
574     return_ACPI_STATUS (Status);
575 }
576 
577 
578 /*******************************************************************************
579  *
580  * FUNCTION:    AcpiTbReleaseOwnerId
581  *
582  * PARAMETERS:  TableIndex          - Table index
583  *
584  * RETURN:      Status
585  *
586  * DESCRIPTION: Releases OwnerId in TableDesc
587  *
588  ******************************************************************************/
589 
590 ACPI_STATUS
591 AcpiTbReleaseOwnerId (
592     UINT32                  TableIndex)
593 {
594     ACPI_STATUS             Status = AE_BAD_PARAMETER;
595 
596 
597     ACPI_FUNCTION_TRACE (TbReleaseOwnerId);
598 
599 
600     (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
601     if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
602     {
603         AcpiUtReleaseOwnerId (
604             &(AcpiGbl_RootTableList.Tables[TableIndex].OwnerId));
605         Status = AE_OK;
606     }
607 
608     (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
609     return_ACPI_STATUS (Status);
610 }
611 
612 
613 /*******************************************************************************
614  *
615  * FUNCTION:    AcpiTbGetOwnerId
616  *
617  * PARAMETERS:  TableIndex          - Table index
618  *              OwnerId             - Where the table OwnerId is returned
619  *
620  * RETURN:      Status
621  *
622  * DESCRIPTION: returns OwnerId for the ACPI table
623  *
624  ******************************************************************************/
625 
626 ACPI_STATUS
627 AcpiTbGetOwnerId (
628     UINT32                  TableIndex,
629     ACPI_OWNER_ID           *OwnerId)
630 {
631     ACPI_STATUS             Status = AE_BAD_PARAMETER;
632 
633 
634     ACPI_FUNCTION_TRACE (TbGetOwnerId);
635 
636 
637     (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
638     if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
639     {
640         *OwnerId = AcpiGbl_RootTableList.Tables[TableIndex].OwnerId;
641         Status = AE_OK;
642     }
643 
644     (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
645     return_ACPI_STATUS (Status);
646 }
647 
648 
649 /*******************************************************************************
650  *
651  * FUNCTION:    AcpiTbIsTableLoaded
652  *
653  * PARAMETERS:  TableIndex          - Table index
654  *
655  * RETURN:      Table Loaded Flag
656  *
657  ******************************************************************************/
658 
659 BOOLEAN
660 AcpiTbIsTableLoaded (
661     UINT32                  TableIndex)
662 {
663     BOOLEAN                 IsLoaded = FALSE;
664 
665 
666     (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
667     if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
668     {
669         IsLoaded = (BOOLEAN)
670             (AcpiGbl_RootTableList.Tables[TableIndex].Flags &
671             ACPI_TABLE_IS_LOADED);
672     }
673 
674     (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
675     return (IsLoaded);
676 }
677 
678 
679 /*******************************************************************************
680  *
681  * FUNCTION:    AcpiTbSetTableLoadedFlag
682  *
683  * PARAMETERS:  TableIndex          - Table index
684  *              IsLoaded            - TRUE if table is loaded, FALSE otherwise
685  *
686  * RETURN:      None
687  *
688  * DESCRIPTION: Sets the table loaded flag to either TRUE or FALSE.
689  *
690  ******************************************************************************/
691 
692 void
693 AcpiTbSetTableLoadedFlag (
694     UINT32                  TableIndex,
695     BOOLEAN                 IsLoaded)
696 {
697 
698     (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
699     if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
700     {
701         if (IsLoaded)
702         {
703             AcpiGbl_RootTableList.Tables[TableIndex].Flags |=
704                 ACPI_TABLE_IS_LOADED;
705         }
706         else
707         {
708             AcpiGbl_RootTableList.Tables[TableIndex].Flags &=
709                 ~ACPI_TABLE_IS_LOADED;
710         }
711     }
712 
713     (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
714 }
715 
716