xref: /titanic_50/usr/src/uts/intel/io/acpica/tables/tbxface.c (revision b97d6ca7333c353b6ca20c20c99fb1be8d32a8de)
1 /******************************************************************************
2  *
3  * Module Name: tbxface - Public interfaces to the ACPI subsystem
4  *                         ACPI table oriented interfaces
5  *
6  *****************************************************************************/
7 
8 /*
9  * Copyright (C) 2000 - 2011, Intel Corp.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions, and the following disclaimer,
17  *    without modification.
18  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
19  *    substantially similar to the "NO WARRANTY" disclaimer below
20  *    ("Disclaimer") and any redistribution must be conditioned upon
21  *    including a substantially similar Disclaimer requirement for further
22  *    binary redistribution.
23  * 3. Neither the names of the above-listed copyright holders nor the names
24  *    of any contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * Alternatively, this software may be distributed under the terms of the
28  * GNU General Public License ("GPL") version 2 as published by the Free
29  * Software Foundation.
30  *
31  * NO WARRANTY
32  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
35  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
41  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42  * POSSIBILITY OF SUCH DAMAGES.
43  */
44 
45 #define __TBXFACE_C__
46 
47 #include "acpi.h"
48 #include "accommon.h"
49 #include "acnamesp.h"
50 #include "actables.h"
51 
52 #define _COMPONENT          ACPI_TABLES
53         ACPI_MODULE_NAME    ("tbxface")
54 
55 /* Local prototypes */
56 
57 static ACPI_STATUS
58 AcpiTbLoadNamespace (
59     void);
60 
61 
62 /*******************************************************************************
63  *
64  * FUNCTION:    AcpiAllocateRootTable
65  *
66  * PARAMETERS:  InitialTableCount   - Size of InitialTableArray, in number of
67  *                                    ACPI_TABLE_DESC structures
68  *
69  * RETURN:      Status
70  *
71  * DESCRIPTION: Allocate a root table array. Used by iASL compiler and
72  *              AcpiInitializeTables.
73  *
74  ******************************************************************************/
75 
76 ACPI_STATUS
77 AcpiAllocateRootTable (
78     UINT32                  InitialTableCount)
79 {
80 
81     AcpiGbl_RootTableList.MaxTableCount = InitialTableCount;
82     AcpiGbl_RootTableList.Flags = ACPI_ROOT_ALLOW_RESIZE;
83 
84     return (AcpiTbResizeRootTableList ());
85 }
86 
87 
88 /*******************************************************************************
89  *
90  * FUNCTION:    AcpiInitializeTables
91  *
92  * PARAMETERS:  InitialTableArray   - Pointer to an array of pre-allocated
93  *                                    ACPI_TABLE_DESC structures. If NULL, the
94  *                                    array is dynamically allocated.
95  *              InitialTableCount   - Size of InitialTableArray, in number of
96  *                                    ACPI_TABLE_DESC structures
97  *              AllowRealloc        - Flag to tell Table Manager if resize of
98  *                                    pre-allocated array is allowed. Ignored
99  *                                    if InitialTableArray is NULL.
100  *
101  * RETURN:      Status
102  *
103  * DESCRIPTION: Initialize the table manager, get the RSDP and RSDT/XSDT.
104  *
105  * NOTE:        Allows static allocation of the initial table array in order
106  *              to avoid the use of dynamic memory in confined environments
107  *              such as the kernel boot sequence where it may not be available.
108  *
109  *              If the host OS memory managers are initialized, use NULL for
110  *              InitialTableArray, and the table will be dynamically allocated.
111  *
112  ******************************************************************************/
113 
114 ACPI_STATUS
115 AcpiInitializeTables (
116     ACPI_TABLE_DESC         *InitialTableArray,
117     UINT32                  InitialTableCount,
118     BOOLEAN                 AllowResize)
119 {
120     ACPI_PHYSICAL_ADDRESS   RsdpAddress;
121     ACPI_STATUS             Status;
122 
123 
124     ACPI_FUNCTION_TRACE (AcpiInitializeTables);
125 
126 
127     /*
128      * Set up the Root Table Array
129      * Allocate the table array if requested
130      */
131     if (!InitialTableArray)
132     {
133         Status = AcpiAllocateRootTable (InitialTableCount);
134         if (ACPI_FAILURE (Status))
135         {
136             return_ACPI_STATUS (Status);
137         }
138     }
139     else
140     {
141         /* Root Table Array has been statically allocated by the host */
142 
143         ACPI_MEMSET (InitialTableArray, 0,
144             (ACPI_SIZE) InitialTableCount * sizeof (ACPI_TABLE_DESC));
145 
146         AcpiGbl_RootTableList.Tables = InitialTableArray;
147         AcpiGbl_RootTableList.MaxTableCount = InitialTableCount;
148         AcpiGbl_RootTableList.Flags = ACPI_ROOT_ORIGIN_UNKNOWN;
149         if (AllowResize)
150         {
151             AcpiGbl_RootTableList.Flags |= ACPI_ROOT_ALLOW_RESIZE;
152         }
153     }
154 
155     /* Get the address of the RSDP */
156 
157     RsdpAddress = AcpiOsGetRootPointer ();
158     if (!RsdpAddress)
159     {
160         return_ACPI_STATUS (AE_NOT_FOUND);
161     }
162 
163     /*
164      * Get the root table (RSDT or XSDT) and extract all entries to the local
165      * Root Table Array. This array contains the information of the RSDT/XSDT
166      * in a common, more useable format.
167      */
168     Status = AcpiTbParseRootTable (RsdpAddress);
169     return_ACPI_STATUS (Status);
170 }
171 
172 ACPI_EXPORT_SYMBOL (AcpiInitializeTables)
173 
174 
175 /*******************************************************************************
176  *
177  * FUNCTION:    AcpiReallocateRootTable
178  *
179  * PARAMETERS:  None
180  *
181  * RETURN:      Status
182  *
183  * DESCRIPTION: Reallocate Root Table List into dynamic memory. Copies the
184  *              root list from the previously provided scratch area. Should
185  *              be called once dynamic memory allocation is available in the
186  *              kernel
187  *
188  ******************************************************************************/
189 
190 ACPI_STATUS
191 AcpiReallocateRootTable (
192     void)
193 {
194     ACPI_TABLE_DESC         *Tables;
195     ACPI_SIZE               NewSize;
196     ACPI_SIZE               CurrentSize;
197 
198 
199     ACPI_FUNCTION_TRACE (AcpiReallocateRootTable);
200 
201 
202     /*
203      * Only reallocate the root table if the host provided a static buffer
204      * for the table array in the call to AcpiInitializeTables.
205      */
206     if (AcpiGbl_RootTableList.Flags & ACPI_ROOT_ORIGIN_ALLOCATED)
207     {
208         return_ACPI_STATUS (AE_SUPPORT);
209     }
210 
211     /*
212      * Get the current size of the root table and add the default
213      * increment to create the new table size.
214      */
215     CurrentSize = (ACPI_SIZE)
216         AcpiGbl_RootTableList.CurrentTableCount * sizeof (ACPI_TABLE_DESC);
217 
218     NewSize = CurrentSize +
219         (ACPI_ROOT_TABLE_SIZE_INCREMENT * sizeof (ACPI_TABLE_DESC));
220 
221     /* Create new array and copy the old array */
222 
223     Tables = ACPI_ALLOCATE_ZEROED (NewSize);
224     if (!Tables)
225     {
226         return_ACPI_STATUS (AE_NO_MEMORY);
227     }
228 
229     ACPI_MEMCPY (Tables, AcpiGbl_RootTableList.Tables, CurrentSize);
230 
231     /*
232      * Update the root table descriptor. The new size will be the current
233      * number of tables plus the increment, independent of the reserved
234      * size of the original table list.
235      */
236     AcpiGbl_RootTableList.Tables = Tables;
237     AcpiGbl_RootTableList.MaxTableCount =
238         AcpiGbl_RootTableList.CurrentTableCount + ACPI_ROOT_TABLE_SIZE_INCREMENT;
239     AcpiGbl_RootTableList.Flags =
240         ACPI_ROOT_ORIGIN_ALLOCATED | ACPI_ROOT_ALLOW_RESIZE;
241 
242     return_ACPI_STATUS (AE_OK);
243 }
244 
245 ACPI_EXPORT_SYMBOL (AcpiReallocateRootTable)
246 
247 
248 /*******************************************************************************
249  *
250  * FUNCTION:    AcpiGetTableHeader
251  *
252  * PARAMETERS:  Signature           - ACPI signature of needed table
253  *              Instance            - Which instance (for SSDTs)
254  *              OutTableHeader      - The pointer to the table header to fill
255  *
256  * RETURN:      Status and pointer to mapped table header
257  *
258  * DESCRIPTION: Finds an ACPI table header.
259  *
260  * NOTE:        Caller is responsible in unmapping the header with
261  *              AcpiOsUnmapMemory
262  *
263  ******************************************************************************/
264 
265 ACPI_STATUS
266 AcpiGetTableHeader (
267     char                    *Signature,
268     UINT32                  Instance,
269     ACPI_TABLE_HEADER       *OutTableHeader)
270 {
271     UINT32                  i;
272     UINT32                  j;
273     ACPI_TABLE_HEADER       *Header;
274 
275 
276     /* Parameter validation */
277 
278     if (!Signature || !OutTableHeader)
279     {
280         return (AE_BAD_PARAMETER);
281     }
282 
283     /* Walk the root table list */
284 
285     for (i = 0, j = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++)
286     {
287         if (!ACPI_COMPARE_NAME (&(AcpiGbl_RootTableList.Tables[i].Signature),
288                     Signature))
289         {
290             continue;
291         }
292 
293         if (++j < Instance)
294         {
295             continue;
296         }
297 
298         if (!AcpiGbl_RootTableList.Tables[i].Pointer)
299         {
300             if ((AcpiGbl_RootTableList.Tables[i].Flags &
301                     ACPI_TABLE_ORIGIN_MASK) ==
302                 ACPI_TABLE_ORIGIN_MAPPED)
303             {
304                 Header = AcpiOsMapMemory (
305                             AcpiGbl_RootTableList.Tables[i].Address,
306                             sizeof (ACPI_TABLE_HEADER));
307                 if (!Header)
308                 {
309                     return AE_NO_MEMORY;
310                 }
311 
312                 ACPI_MEMCPY (OutTableHeader, Header, sizeof(ACPI_TABLE_HEADER));
313                 AcpiOsUnmapMemory (Header, sizeof(ACPI_TABLE_HEADER));
314             }
315             else
316             {
317                 return AE_NOT_FOUND;
318             }
319         }
320         else
321         {
322             ACPI_MEMCPY (OutTableHeader,
323                 AcpiGbl_RootTableList.Tables[i].Pointer,
324                 sizeof(ACPI_TABLE_HEADER));
325         }
326 
327         return (AE_OK);
328     }
329 
330     return (AE_NOT_FOUND);
331 }
332 
333 ACPI_EXPORT_SYMBOL (AcpiGetTableHeader)
334 
335 
336 /*******************************************************************************
337  *
338  * FUNCTION:    AcpiGetTable
339  *
340  * PARAMETERS:  Signature           - ACPI signature of needed table
341  *              Instance            - Which instance (for SSDTs)
342  *              OutTable            - Where the pointer to the table is returned
343  *
344  * RETURN:      Status and pointer to table
345  *
346  * DESCRIPTION: Finds and verifies an ACPI table.
347  *
348  ******************************************************************************/
349 
350 ACPI_STATUS
351 AcpiGetTable (
352     char                    *Signature,
353     UINT32                  Instance,
354     ACPI_TABLE_HEADER       **OutTable)
355 {
356     UINT32                  i;
357     UINT32                  j;
358     ACPI_STATUS             Status;
359 
360 
361     /* Parameter validation */
362 
363     if (!Signature || !OutTable)
364     {
365         return (AE_BAD_PARAMETER);
366     }
367 
368     /* Walk the root table list */
369 
370     for (i = 0, j = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++)
371     {
372         if (!ACPI_COMPARE_NAME (&(AcpiGbl_RootTableList.Tables[i].Signature),
373                 Signature))
374         {
375             continue;
376         }
377 
378         if (++j < Instance)
379         {
380             continue;
381         }
382 
383         Status = AcpiTbVerifyTable (&AcpiGbl_RootTableList.Tables[i]);
384         if (ACPI_SUCCESS (Status))
385         {
386             *OutTable = AcpiGbl_RootTableList.Tables[i].Pointer;
387         }
388 
389         return (Status);
390     }
391 
392     return (AE_NOT_FOUND);
393 }
394 
395 ACPI_EXPORT_SYMBOL (AcpiGetTable)
396 
397 
398 /*******************************************************************************
399  *
400  * FUNCTION:    AcpiGetTableByIndex
401  *
402  * PARAMETERS:  TableIndex          - Table index
403  *              Table               - Where the pointer to the table is returned
404  *
405  * RETURN:      Status and pointer to the table
406  *
407  * DESCRIPTION: Obtain a table by an index into the global table list.
408  *
409  ******************************************************************************/
410 
411 ACPI_STATUS
412 AcpiGetTableByIndex (
413     UINT32                  TableIndex,
414     ACPI_TABLE_HEADER       **Table)
415 {
416     ACPI_STATUS             Status;
417 
418 
419     ACPI_FUNCTION_TRACE (AcpiGetTableByIndex);
420 
421 
422     /* Parameter validation */
423 
424     if (!Table)
425     {
426         return_ACPI_STATUS (AE_BAD_PARAMETER);
427     }
428 
429     (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
430 
431     /* Validate index */
432 
433     if (TableIndex >= AcpiGbl_RootTableList.CurrentTableCount)
434     {
435         (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
436         return_ACPI_STATUS (AE_BAD_PARAMETER);
437     }
438 
439     if (!AcpiGbl_RootTableList.Tables[TableIndex].Pointer)
440     {
441         /* Table is not mapped, map it */
442 
443         Status = AcpiTbVerifyTable (&AcpiGbl_RootTableList.Tables[TableIndex]);
444         if (ACPI_FAILURE (Status))
445         {
446             (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
447             return_ACPI_STATUS (Status);
448         }
449     }
450 
451     *Table = AcpiGbl_RootTableList.Tables[TableIndex].Pointer;
452     (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
453     return_ACPI_STATUS (AE_OK);
454 }
455 
456 ACPI_EXPORT_SYMBOL (AcpiGetTableByIndex)
457 
458 
459 /*******************************************************************************
460  *
461  * FUNCTION:    AcpiTbLoadNamespace
462  *
463  * PARAMETERS:  None
464  *
465  * RETURN:      Status
466  *
467  * DESCRIPTION: Load the namespace from the DSDT and all SSDTs/PSDTs found in
468  *              the RSDT/XSDT.
469  *
470  ******************************************************************************/
471 
472 static ACPI_STATUS
473 AcpiTbLoadNamespace (
474     void)
475 {
476     ACPI_STATUS             Status;
477     UINT32                  i;
478     ACPI_TABLE_HEADER       *NewDsdt;
479 
480 
481     ACPI_FUNCTION_TRACE (TbLoadNamespace);
482 
483 
484     (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
485 
486     /*
487      * Load the namespace. The DSDT is required, but any SSDT and
488      * PSDT tables are optional. Verify the DSDT.
489      */
490     if (!AcpiGbl_RootTableList.CurrentTableCount ||
491         !ACPI_COMPARE_NAME (
492             &(AcpiGbl_RootTableList.Tables[ACPI_TABLE_INDEX_DSDT].Signature),
493             ACPI_SIG_DSDT) ||
494          ACPI_FAILURE (AcpiTbVerifyTable (
495             &AcpiGbl_RootTableList.Tables[ACPI_TABLE_INDEX_DSDT])))
496     {
497         Status = AE_NO_ACPI_TABLES;
498         goto UnlockAndExit;
499     }
500 
501     /*
502      * Save the DSDT pointer for simple access. This is the mapped memory
503      * address. We must take care here because the address of the .Tables
504      * array can change dynamically as tables are loaded at run-time. Note:
505      * .Pointer field is not validated until after call to AcpiTbVerifyTable.
506      */
507     AcpiGbl_DSDT = AcpiGbl_RootTableList.Tables[ACPI_TABLE_INDEX_DSDT].Pointer;
508 
509     /*
510      * Optionally copy the entire DSDT to local memory (instead of simply
511      * mapping it.) There are some BIOSs that corrupt or replace the original
512      * DSDT, creating the need for this option. Default is FALSE, do not copy
513      * the DSDT.
514      */
515     if (AcpiGbl_CopyDsdtLocally)
516     {
517         NewDsdt = AcpiTbCopyDsdt (ACPI_TABLE_INDEX_DSDT);
518         if (NewDsdt)
519         {
520             AcpiGbl_DSDT = NewDsdt;
521         }
522     }
523 
524     /*
525      * Save the original DSDT header for detection of table corruption
526      * and/or replacement of the DSDT from outside the OS.
527      */
528     ACPI_MEMCPY (&AcpiGbl_OriginalDsdtHeader, AcpiGbl_DSDT,
529         sizeof (ACPI_TABLE_HEADER));
530 
531     (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
532 
533     /* Load and parse tables */
534 
535     Status = AcpiNsLoadTable (ACPI_TABLE_INDEX_DSDT, AcpiGbl_RootNode);
536     if (ACPI_FAILURE (Status))
537     {
538         return_ACPI_STATUS (Status);
539     }
540 
541     /* Load any SSDT or PSDT tables. Note: Loop leaves tables locked */
542 
543     (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
544     for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; ++i)
545     {
546         if ((!ACPI_COMPARE_NAME (&(AcpiGbl_RootTableList.Tables[i].Signature),
547                     ACPI_SIG_SSDT) &&
548              !ACPI_COMPARE_NAME (&(AcpiGbl_RootTableList.Tables[i].Signature),
549                     ACPI_SIG_PSDT)) ||
550              ACPI_FAILURE (AcpiTbVerifyTable (
551                 &AcpiGbl_RootTableList.Tables[i])))
552         {
553             continue;
554         }
555 
556         /* Ignore errors while loading tables, get as many as possible */
557 
558         (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
559         (void) AcpiNsLoadTable (i, AcpiGbl_RootNode);
560         (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
561     }
562 
563     ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "ACPI Tables successfully acquired\n"));
564 
565 UnlockAndExit:
566     (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
567     return_ACPI_STATUS (Status);
568 }
569 
570 
571 /*******************************************************************************
572  *
573  * FUNCTION:    AcpiLoadTables
574  *
575  * PARAMETERS:  None
576  *
577  * RETURN:      Status
578  *
579  * DESCRIPTION: Load the ACPI tables from the RSDT/XSDT
580  *
581  ******************************************************************************/
582 
583 ACPI_STATUS
584 AcpiLoadTables (
585     void)
586 {
587     ACPI_STATUS             Status;
588 
589 
590     ACPI_FUNCTION_TRACE (AcpiLoadTables);
591 
592 
593     /* Load the namespace from the tables */
594 
595     Status = AcpiTbLoadNamespace ();
596     if (ACPI_FAILURE (Status))
597     {
598         ACPI_EXCEPTION ((AE_INFO, Status,
599             "While loading namespace from ACPI tables"));
600     }
601 
602     return_ACPI_STATUS (Status);
603 }
604 
605 ACPI_EXPORT_SYMBOL (AcpiLoadTables)
606 
607 
608 /*******************************************************************************
609  *
610  * FUNCTION:    AcpiInstallTableHandler
611  *
612  * PARAMETERS:  Handler         - Table event handler
613  *              Context         - Value passed to the handler on each event
614  *
615  * RETURN:      Status
616  *
617  * DESCRIPTION: Install table event handler
618  *
619  ******************************************************************************/
620 
621 ACPI_STATUS
622 AcpiInstallTableHandler (
623     ACPI_TABLE_HANDLER      Handler,
624     void                    *Context)
625 {
626     ACPI_STATUS             Status;
627 
628 
629     ACPI_FUNCTION_TRACE (AcpiInstallTableHandler);
630 
631 
632     if (!Handler)
633     {
634         return_ACPI_STATUS (AE_BAD_PARAMETER);
635     }
636 
637     Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
638     if (ACPI_FAILURE (Status))
639     {
640         return_ACPI_STATUS (Status);
641     }
642 
643     /* Don't allow more than one handler */
644 
645     if (AcpiGbl_TableHandler)
646     {
647         Status = AE_ALREADY_EXISTS;
648         goto Cleanup;
649     }
650 
651     /* Install the handler */
652 
653     AcpiGbl_TableHandler = Handler;
654     AcpiGbl_TableHandlerContext = Context;
655 
656 Cleanup:
657     (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
658     return_ACPI_STATUS (Status);
659 }
660 
661 ACPI_EXPORT_SYMBOL (AcpiInstallTableHandler)
662 
663 
664 /*******************************************************************************
665  *
666  * FUNCTION:    AcpiRemoveTableHandler
667  *
668  * PARAMETERS:  Handler         - Table event handler that was installed
669  *                                previously.
670  *
671  * RETURN:      Status
672  *
673  * DESCRIPTION: Remove table event handler
674  *
675  ******************************************************************************/
676 
677 ACPI_STATUS
678 AcpiRemoveTableHandler (
679     ACPI_TABLE_HANDLER      Handler)
680 {
681     ACPI_STATUS             Status;
682 
683 
684     ACPI_FUNCTION_TRACE (AcpiRemoveTableHandler);
685 
686 
687     Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
688     if (ACPI_FAILURE (Status))
689     {
690         return_ACPI_STATUS (Status);
691     }
692 
693     /* Make sure that the installed handler is the same */
694 
695     if (!Handler ||
696         Handler != AcpiGbl_TableHandler)
697     {
698         Status = AE_BAD_PARAMETER;
699         goto Cleanup;
700     }
701 
702     /* Remove the handler */
703 
704     AcpiGbl_TableHandler = NULL;
705 
706 Cleanup:
707     (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
708     return_ACPI_STATUS (Status);
709 }
710 
711 ACPI_EXPORT_SYMBOL (AcpiRemoveTableHandler)
712 
713