utobject.c revision 1.1.1.2.8.2 1 /******************************************************************************
2 *
3 * Module Name: utobject - ACPI object create/delete/size/cache routines
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 #define __UTOBJECT_C__
45
46 #include "acpi.h"
47 #include "accommon.h"
48 #include "acnamesp.h"
49
50
51 #define _COMPONENT ACPI_UTILITIES
52 ACPI_MODULE_NAME ("utobject")
53
54 /* Local prototypes */
55
56 static ACPI_STATUS
57 AcpiUtGetSimpleObjectSize (
58 ACPI_OPERAND_OBJECT *Obj,
59 ACPI_SIZE *ObjLength);
60
61 static ACPI_STATUS
62 AcpiUtGetPackageObjectSize (
63 ACPI_OPERAND_OBJECT *Obj,
64 ACPI_SIZE *ObjLength);
65
66 static ACPI_STATUS
67 AcpiUtGetElementLength (
68 UINT8 ObjectType,
69 ACPI_OPERAND_OBJECT *SourceObject,
70 ACPI_GENERIC_STATE *State,
71 void *Context);
72
73
74 /*******************************************************************************
75 *
76 * FUNCTION: AcpiUtCreateInternalObjectDbg
77 *
78 * PARAMETERS: ModuleName - Source file name of caller
79 * LineNumber - Line number of caller
80 * ComponentId - Component type of caller
81 * Type - ACPI Type of the new object
82 *
83 * RETURN: A new internal object, null on failure
84 *
85 * DESCRIPTION: Create and initialize a new internal object.
86 *
87 * NOTE: We always allocate the worst-case object descriptor because
88 * these objects are cached, and we want them to be
89 * one-size-satisifies-any-request. This in itself may not be
90 * the most memory efficient, but the efficiency of the object
91 * cache should more than make up for this!
92 *
93 ******************************************************************************/
94
95 ACPI_OPERAND_OBJECT *
96 AcpiUtCreateInternalObjectDbg (
97 const char *ModuleName,
98 UINT32 LineNumber,
99 UINT32 ComponentId,
100 ACPI_OBJECT_TYPE Type)
101 {
102 ACPI_OPERAND_OBJECT *Object;
103 ACPI_OPERAND_OBJECT *SecondObject;
104
105
106 ACPI_FUNCTION_TRACE_STR (UtCreateInternalObjectDbg,
107 AcpiUtGetTypeName (Type));
108
109
110 /* Allocate the raw object descriptor */
111
112 Object = AcpiUtAllocateObjectDescDbg (ModuleName, LineNumber, ComponentId);
113 if (!Object)
114 {
115 return_PTR (NULL);
116 }
117
118 switch (Type)
119 {
120 case ACPI_TYPE_REGION:
121 case ACPI_TYPE_BUFFER_FIELD:
122 case ACPI_TYPE_LOCAL_BANK_FIELD:
123
124 /* These types require a secondary object */
125
126 SecondObject = AcpiUtAllocateObjectDescDbg (ModuleName,
127 LineNumber, ComponentId);
128 if (!SecondObject)
129 {
130 AcpiUtDeleteObjectDesc (Object);
131 return_PTR (NULL);
132 }
133
134 SecondObject->Common.Type = ACPI_TYPE_LOCAL_EXTRA;
135 SecondObject->Common.ReferenceCount = 1;
136
137 /* Link the second object to the first */
138
139 Object->Common.NextObject = SecondObject;
140 break;
141
142 default:
143 /* All others have no secondary object */
144 break;
145 }
146
147 /* Save the object type in the object descriptor */
148
149 Object->Common.Type = (UINT8) Type;
150
151 /* Init the reference count */
152
153 Object->Common.ReferenceCount = 1;
154
155 /* Any per-type initialization should go here */
156
157 return_PTR (Object);
158 }
159
160
161 /*******************************************************************************
162 *
163 * FUNCTION: AcpiUtCreatePackageObject
164 *
165 * PARAMETERS: Count - Number of package elements
166 *
167 * RETURN: Pointer to a new Package object, null on failure
168 *
169 * DESCRIPTION: Create a fully initialized package object
170 *
171 ******************************************************************************/
172
173 ACPI_OPERAND_OBJECT *
174 AcpiUtCreatePackageObject (
175 UINT32 Count)
176 {
177 ACPI_OPERAND_OBJECT *PackageDesc;
178 ACPI_OPERAND_OBJECT **PackageElements;
179
180
181 ACPI_FUNCTION_TRACE_U32 (UtCreatePackageObject, Count);
182
183
184 /* Create a new Package object */
185
186 PackageDesc = AcpiUtCreateInternalObject (ACPI_TYPE_PACKAGE);
187 if (!PackageDesc)
188 {
189 return_PTR (NULL);
190 }
191
192 /*
193 * Create the element array. Count+1 allows the array to be null
194 * terminated.
195 */
196 PackageElements = ACPI_ALLOCATE_ZEROED (
197 ((ACPI_SIZE) Count + 1) * sizeof (void *));
198 if (!PackageElements)
199 {
200 ACPI_FREE (PackageDesc);
201 return_PTR (NULL);
202 }
203
204 PackageDesc->Package.Count = Count;
205 PackageDesc->Package.Elements = PackageElements;
206 return_PTR (PackageDesc);
207 }
208
209
210 /*******************************************************************************
211 *
212 * FUNCTION: AcpiUtCreateIntegerObject
213 *
214 * PARAMETERS: InitialValue - Initial value for the integer
215 *
216 * RETURN: Pointer to a new Integer object, null on failure
217 *
218 * DESCRIPTION: Create an initialized integer object
219 *
220 ******************************************************************************/
221
222 ACPI_OPERAND_OBJECT *
223 AcpiUtCreateIntegerObject (
224 UINT64 InitialValue)
225 {
226 ACPI_OPERAND_OBJECT *IntegerDesc;
227
228
229 ACPI_FUNCTION_TRACE (UtCreateIntegerObject);
230
231
232 /* Create and initialize a new integer object */
233
234 IntegerDesc = AcpiUtCreateInternalObject (ACPI_TYPE_INTEGER);
235 if (!IntegerDesc)
236 {
237 return_PTR (NULL);
238 }
239
240 IntegerDesc->Integer.Value = InitialValue;
241 return_PTR (IntegerDesc);
242 }
243
244
245 /*******************************************************************************
246 *
247 * FUNCTION: AcpiUtCreateBufferObject
248 *
249 * PARAMETERS: BufferSize - Size of buffer to be created
250 *
251 * RETURN: Pointer to a new Buffer object, null on failure
252 *
253 * DESCRIPTION: Create a fully initialized buffer object
254 *
255 ******************************************************************************/
256
257 ACPI_OPERAND_OBJECT *
258 AcpiUtCreateBufferObject (
259 ACPI_SIZE BufferSize)
260 {
261 ACPI_OPERAND_OBJECT *BufferDesc;
262 UINT8 *Buffer = NULL;
263
264
265 ACPI_FUNCTION_TRACE_U32 (UtCreateBufferObject, BufferSize);
266
267
268 /* Create a new Buffer object */
269
270 BufferDesc = AcpiUtCreateInternalObject (ACPI_TYPE_BUFFER);
271 if (!BufferDesc)
272 {
273 return_PTR (NULL);
274 }
275
276 /* Create an actual buffer only if size > 0 */
277
278 if (BufferSize > 0)
279 {
280 /* Allocate the actual buffer */
281
282 Buffer = ACPI_ALLOCATE_ZEROED (BufferSize);
283 if (!Buffer)
284 {
285 ACPI_ERROR ((AE_INFO, "Could not allocate size %u",
286 (UINT32) BufferSize));
287 AcpiUtRemoveReference (BufferDesc);
288 return_PTR (NULL);
289 }
290 }
291
292 /* Complete buffer object initialization */
293
294 BufferDesc->Buffer.Flags |= AOPOBJ_DATA_VALID;
295 BufferDesc->Buffer.Pointer = Buffer;
296 BufferDesc->Buffer.Length = (UINT32) BufferSize;
297
298 /* Return the new buffer descriptor */
299
300 return_PTR (BufferDesc);
301 }
302
303
304 /*******************************************************************************
305 *
306 * FUNCTION: AcpiUtCreateStringObject
307 *
308 * PARAMETERS: StringSize - Size of string to be created. Does not
309 * include NULL terminator, this is added
310 * automatically.
311 *
312 * RETURN: Pointer to a new String object
313 *
314 * DESCRIPTION: Create a fully initialized string object
315 *
316 ******************************************************************************/
317
318 ACPI_OPERAND_OBJECT *
319 AcpiUtCreateStringObject (
320 ACPI_SIZE StringSize)
321 {
322 ACPI_OPERAND_OBJECT *StringDesc;
323 char *String;
324
325
326 ACPI_FUNCTION_TRACE_U32 (UtCreateStringObject, StringSize);
327
328
329 /* Create a new String object */
330
331 StringDesc = AcpiUtCreateInternalObject (ACPI_TYPE_STRING);
332 if (!StringDesc)
333 {
334 return_PTR (NULL);
335 }
336
337 /*
338 * Allocate the actual string buffer -- (Size + 1) for NULL terminator.
339 * NOTE: Zero-length strings are NULL terminated
340 */
341 String = ACPI_ALLOCATE_ZEROED (StringSize + 1);
342 if (!String)
343 {
344 ACPI_ERROR ((AE_INFO, "Could not allocate size %u",
345 (UINT32) StringSize));
346 AcpiUtRemoveReference (StringDesc);
347 return_PTR (NULL);
348 }
349
350 /* Complete string object initialization */
351
352 StringDesc->String.Pointer = String;
353 StringDesc->String.Length = (UINT32) StringSize;
354
355 /* Return the new string descriptor */
356
357 return_PTR (StringDesc);
358 }
359
360
361 /*******************************************************************************
362 *
363 * FUNCTION: AcpiUtValidInternalObject
364 *
365 * PARAMETERS: Object - Object to be validated
366 *
367 * RETURN: TRUE if object is valid, FALSE otherwise
368 *
369 * DESCRIPTION: Validate a pointer to be an ACPI_OPERAND_OBJECT
370 *
371 ******************************************************************************/
372
373 BOOLEAN
374 AcpiUtValidInternalObject (
375 void *Object)
376 {
377
378 ACPI_FUNCTION_NAME (UtValidInternalObject);
379
380
381 /* Check for a null pointer */
382
383 if (!Object)
384 {
385 ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "**** Null Object Ptr\n"));
386 return (FALSE);
387 }
388
389 /* Check the descriptor type field */
390
391 switch (ACPI_GET_DESCRIPTOR_TYPE (Object))
392 {
393 case ACPI_DESC_TYPE_OPERAND:
394
395 /* The object appears to be a valid ACPI_OPERAND_OBJECT */
396
397 return (TRUE);
398
399 default:
400 ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
401 "%p is not not an ACPI operand obj [%s]\n",
402 Object, AcpiUtGetDescriptorName (Object)));
403 break;
404 }
405
406 return (FALSE);
407 }
408
409
410 /*******************************************************************************
411 *
412 * FUNCTION: AcpiUtAllocateObjectDescDbg
413 *
414 * PARAMETERS: ModuleName - Caller's module name (for error output)
415 * LineNumber - Caller's line number (for error output)
416 * ComponentId - Caller's component ID (for error output)
417 *
418 * RETURN: Pointer to newly allocated object descriptor. Null on error
419 *
420 * DESCRIPTION: Allocate a new object descriptor. Gracefully handle
421 * error conditions.
422 *
423 ******************************************************************************/
424
425 void *
426 AcpiUtAllocateObjectDescDbg (
427 const char *ModuleName,
428 UINT32 LineNumber,
429 UINT32 ComponentId)
430 {
431 ACPI_OPERAND_OBJECT *Object;
432
433
434 ACPI_FUNCTION_TRACE (UtAllocateObjectDescDbg);
435
436
437 Object = AcpiOsAcquireObject (AcpiGbl_OperandCache);
438 if (!Object)
439 {
440 ACPI_ERROR ((ModuleName, LineNumber,
441 "Could not allocate an object descriptor"));
442
443 return_PTR (NULL);
444 }
445
446 /* Mark the descriptor type */
447
448 ACPI_SET_DESCRIPTOR_TYPE (Object, ACPI_DESC_TYPE_OPERAND);
449
450 ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "%p Size %X\n",
451 Object, (UINT32) sizeof (ACPI_OPERAND_OBJECT)));
452
453 return_PTR (Object);
454 }
455
456
457 /*******************************************************************************
458 *
459 * FUNCTION: AcpiUtDeleteObjectDesc
460 *
461 * PARAMETERS: Object - An Acpi internal object to be deleted
462 *
463 * RETURN: None.
464 *
465 * DESCRIPTION: Free an ACPI object descriptor or add it to the object cache
466 *
467 ******************************************************************************/
468
469 void
470 AcpiUtDeleteObjectDesc (
471 ACPI_OPERAND_OBJECT *Object)
472 {
473 ACPI_FUNCTION_TRACE_PTR (UtDeleteObjectDesc, Object);
474
475
476 /* Object must be an ACPI_OPERAND_OBJECT */
477
478 if (ACPI_GET_DESCRIPTOR_TYPE (Object) != ACPI_DESC_TYPE_OPERAND)
479 {
480 ACPI_ERROR ((AE_INFO,
481 "%p is not an ACPI Operand object [%s]", Object,
482 AcpiUtGetDescriptorName (Object)));
483 return_VOID;
484 }
485
486 (void) AcpiOsReleaseObject (AcpiGbl_OperandCache, Object);
487 return_VOID;
488 }
489
490
491 /*******************************************************************************
492 *
493 * FUNCTION: AcpiUtGetSimpleObjectSize
494 *
495 * PARAMETERS: InternalObject - An ACPI operand object
496 * ObjLength - Where the length is returned
497 *
498 * RETURN: Status
499 *
500 * DESCRIPTION: This function is called to determine the space required to
501 * contain a simple object for return to an external user.
502 *
503 * The length includes the object structure plus any additional
504 * needed space.
505 *
506 ******************************************************************************/
507
508 static ACPI_STATUS
509 AcpiUtGetSimpleObjectSize (
510 ACPI_OPERAND_OBJECT *InternalObject,
511 ACPI_SIZE *ObjLength)
512 {
513 ACPI_SIZE Length;
514 ACPI_SIZE Size;
515 ACPI_STATUS Status = AE_OK;
516
517
518 ACPI_FUNCTION_TRACE_PTR (UtGetSimpleObjectSize, InternalObject);
519
520
521 /* Start with the length of the (external) Acpi object */
522
523 Length = sizeof (ACPI_OBJECT);
524
525 /* A NULL object is allowed, can be a legal uninitialized package element */
526
527 if (!InternalObject)
528 {
529 /*
530 * Object is NULL, just return the length of ACPI_OBJECT
531 * (A NULL ACPI_OBJECT is an object of all zeroes.)
532 */
533 *ObjLength = ACPI_ROUND_UP_TO_NATIVE_WORD (Length);
534 return_ACPI_STATUS (AE_OK);
535 }
536
537 /* A Namespace Node should never appear here */
538
539 if (ACPI_GET_DESCRIPTOR_TYPE (InternalObject) == ACPI_DESC_TYPE_NAMED)
540 {
541 /* A namespace node should never get here */
542
543 return_ACPI_STATUS (AE_AML_INTERNAL);
544 }
545
546 /*
547 * The final length depends on the object type
548 * Strings and Buffers are packed right up against the parent object and
549 * must be accessed bytewise or there may be alignment problems on
550 * certain processors
551 */
552 switch (InternalObject->Common.Type)
553 {
554 case ACPI_TYPE_STRING:
555
556 Length += (ACPI_SIZE) InternalObject->String.Length + 1;
557 break;
558
559
560 case ACPI_TYPE_BUFFER:
561
562 Length += (ACPI_SIZE) InternalObject->Buffer.Length;
563 break;
564
565
566 case ACPI_TYPE_INTEGER:
567 case ACPI_TYPE_PROCESSOR:
568 case ACPI_TYPE_POWER:
569
570 /* No extra data for these types */
571
572 break;
573
574
575 case ACPI_TYPE_LOCAL_REFERENCE:
576
577 switch (InternalObject->Reference.Class)
578 {
579 case ACPI_REFCLASS_NAME:
580
581 /*
582 * Get the actual length of the full pathname to this object.
583 * The reference will be converted to the pathname to the object
584 */
585 Size = AcpiNsGetPathnameLength (InternalObject->Reference.Node);
586 if (!Size)
587 {
588 return_ACPI_STATUS (AE_BAD_PARAMETER);
589 }
590
591 Length += ACPI_ROUND_UP_TO_NATIVE_WORD (Size);
592 break;
593
594 default:
595
596 /*
597 * No other reference opcodes are supported.
598 * Notably, Locals and Args are not supported, but this may be
599 * required eventually.
600 */
601 ACPI_ERROR ((AE_INFO, "Cannot convert to external object - "
602 "unsupported Reference Class [%s] 0x%X in object %p",
603 AcpiUtGetReferenceName (InternalObject),
604 InternalObject->Reference.Class, InternalObject));
605 Status = AE_TYPE;
606 break;
607 }
608 break;
609
610
611 default:
612
613 ACPI_ERROR ((AE_INFO, "Cannot convert to external object - "
614 "unsupported type [%s] 0x%X in object %p",
615 AcpiUtGetObjectTypeName (InternalObject),
616 InternalObject->Common.Type, InternalObject));
617 Status = AE_TYPE;
618 break;
619 }
620
621 /*
622 * Account for the space required by the object rounded up to the next
623 * multiple of the machine word size. This keeps each object aligned
624 * on a machine word boundary. (preventing alignment faults on some
625 * machines.)
626 */
627 *ObjLength = ACPI_ROUND_UP_TO_NATIVE_WORD (Length);
628 return_ACPI_STATUS (Status);
629 }
630
631
632 /*******************************************************************************
633 *
634 * FUNCTION: AcpiUtGetElementLength
635 *
636 * PARAMETERS: ACPI_PKG_CALLBACK
637 *
638 * RETURN: Status
639 *
640 * DESCRIPTION: Get the length of one package element.
641 *
642 ******************************************************************************/
643
644 static ACPI_STATUS
645 AcpiUtGetElementLength (
646 UINT8 ObjectType,
647 ACPI_OPERAND_OBJECT *SourceObject,
648 ACPI_GENERIC_STATE *State,
649 void *Context)
650 {
651 ACPI_STATUS Status = AE_OK;
652 ACPI_PKG_INFO *Info = (ACPI_PKG_INFO *) Context;
653 ACPI_SIZE ObjectSpace;
654
655
656 switch (ObjectType)
657 {
658 case ACPI_COPY_TYPE_SIMPLE:
659
660 /*
661 * Simple object - just get the size (Null object/entry is handled
662 * here also) and sum it into the running package length
663 */
664 Status = AcpiUtGetSimpleObjectSize (SourceObject, &ObjectSpace);
665 if (ACPI_FAILURE (Status))
666 {
667 return (Status);
668 }
669
670 Info->Length += ObjectSpace;
671 break;
672
673
674 case ACPI_COPY_TYPE_PACKAGE:
675
676 /* Package object - nothing much to do here, let the walk handle it */
677
678 Info->NumPackages++;
679 State->Pkg.ThisTargetObj = NULL;
680 break;
681
682
683 default:
684
685 /* No other types allowed */
686
687 return (AE_BAD_PARAMETER);
688 }
689
690 return (Status);
691 }
692
693
694 /*******************************************************************************
695 *
696 * FUNCTION: AcpiUtGetPackageObjectSize
697 *
698 * PARAMETERS: InternalObject - An ACPI internal object
699 * ObjLength - Where the length is returned
700 *
701 * RETURN: Status
702 *
703 * DESCRIPTION: This function is called to determine the space required to
704 * contain a package object for return to an external user.
705 *
706 * This is moderately complex since a package contains other
707 * objects including packages.
708 *
709 ******************************************************************************/
710
711 static ACPI_STATUS
712 AcpiUtGetPackageObjectSize (
713 ACPI_OPERAND_OBJECT *InternalObject,
714 ACPI_SIZE *ObjLength)
715 {
716 ACPI_STATUS Status;
717 ACPI_PKG_INFO Info;
718
719
720 ACPI_FUNCTION_TRACE_PTR (UtGetPackageObjectSize, InternalObject);
721
722
723 Info.Length = 0;
724 Info.ObjectSpace = 0;
725 Info.NumPackages = 1;
726
727 Status = AcpiUtWalkPackageTree (InternalObject, NULL,
728 AcpiUtGetElementLength, &Info);
729 if (ACPI_FAILURE (Status))
730 {
731 return_ACPI_STATUS (Status);
732 }
733
734 /*
735 * We have handled all of the objects in all levels of the package.
736 * just add the length of the package objects themselves.
737 * Round up to the next machine word.
738 */
739 Info.Length += ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT)) *
740 (ACPI_SIZE) Info.NumPackages;
741
742 /* Return the total package length */
743
744 *ObjLength = Info.Length;
745 return_ACPI_STATUS (Status);
746 }
747
748
749 /*******************************************************************************
750 *
751 * FUNCTION: AcpiUtGetObjectSize
752 *
753 * PARAMETERS: InternalObject - An ACPI internal object
754 * ObjLength - Where the length will be returned
755 *
756 * RETURN: Status
757 *
758 * DESCRIPTION: This function is called to determine the space required to
759 * contain an object for return to an API user.
760 *
761 ******************************************************************************/
762
763 ACPI_STATUS
764 AcpiUtGetObjectSize (
765 ACPI_OPERAND_OBJECT *InternalObject,
766 ACPI_SIZE *ObjLength)
767 {
768 ACPI_STATUS Status;
769
770
771 ACPI_FUNCTION_ENTRY ();
772
773
774 if ((ACPI_GET_DESCRIPTOR_TYPE (InternalObject) == ACPI_DESC_TYPE_OPERAND) &&
775 (InternalObject->Common.Type == ACPI_TYPE_PACKAGE))
776 {
777 Status = AcpiUtGetPackageObjectSize (InternalObject, ObjLength);
778 }
779 else
780 {
781 Status = AcpiUtGetSimpleObjectSize (InternalObject, ObjLength);
782 }
783
784 return (Status);
785 }
786
787
788