1. Sync the latest network stack. Add NetLibCreateIPv4DPathNode () in netlib library.
[mirror_edk2.git] / MdeModulePkg / Universal / PCD / Pei / Pcd.c
1 /** @file PCD PEIM
2
3 Copyright (c) 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12
13 Module Name: Pcd.c
14
15 **/
16
17 #include "Service.h"
18
19
20 PCD_PPI mPcdPpiInstance = {
21 PeiPcdSetSku,
22
23 PeiPcdGet8,
24 PeiPcdGet16,
25 PeiPcdGet32,
26 PeiPcdGet64,
27 PeiPcdGetPtr,
28 PeiPcdGetBool,
29 PeiPcdGetSize,
30
31 PeiPcdGet8Ex,
32 PeiPcdGet16Ex,
33 PeiPcdGet32Ex,
34 PeiPcdGet64Ex,
35 PeiPcdGetPtrEx,
36 PeiPcdGetBoolEx,
37 PeiPcdGetSizeEx,
38
39 PeiPcdSet8,
40 PeiPcdSet16,
41 PeiPcdSet32,
42 PeiPcdSet64,
43 PeiPcdSetPtr,
44 PeiPcdSetBool,
45
46 PeiPcdSet8Ex,
47 PeiPcdSet16Ex,
48 PeiPcdSet32Ex,
49 PeiPcdSet64Ex,
50 PeiPcdSetPtrEx,
51 PeiPcdSetBoolEx,
52
53 PeiRegisterCallBackOnSet,
54 PcdUnRegisterCallBackOnSet,
55 PeiPcdGetNextToken,
56 PeiPcdGetNextTokenSpace
57 };
58
59
60
61 STATIC EFI_PEI_PPI_DESCRIPTOR mPpiPCD = {
62 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
63 &gPcdPpiGuid,
64 &mPcdPpiInstance
65 };
66
67
68
69 EFI_STATUS
70 EFIAPI
71 PcdPeimInit (
72 IN EFI_FFS_FILE_HEADER *FfsHeader,
73 IN EFI_PEI_SERVICES **PeiServices
74 )
75 {
76 EFI_STATUS Status;
77
78 BuildPcdDatabase ();
79
80 Status = PeiServicesInstallPpi (&mPpiPCD);
81
82 ASSERT_EFI_ERROR (Status);
83
84 return EFI_SUCCESS;
85 }
86
87 VOID
88 EFIAPI
89 PeiPcdSetSku (
90 IN UINTN SkuId
91 )
92 {
93
94 GetPcdDatabase()->Init.SystemSkuId = (SKU_ID) SkuId;
95
96 return;
97 }
98
99
100
101 UINT8
102 EFIAPI
103 PeiPcdGet8 (
104 IN UINTN TokenNumber
105 )
106 {
107 return *((UINT8 *) GetWorker (TokenNumber, sizeof (UINT8)));
108 }
109
110
111
112 UINT16
113 EFIAPI
114 PeiPcdGet16 (
115 IN UINTN TokenNumber
116 )
117 {
118 return ReadUnaligned16 (GetWorker (TokenNumber, sizeof (UINT16)));
119 }
120
121
122
123 UINT32
124 EFIAPI
125 PeiPcdGet32 (
126 IN UINTN TokenNumber
127 )
128 {
129 return ReadUnaligned32 (GetWorker (TokenNumber, sizeof (UINT32)));
130 }
131
132
133
134 UINT64
135 EFIAPI
136 PeiPcdGet64 (
137 IN UINTN TokenNumber
138 )
139 {
140 return ReadUnaligned64 (GetWorker (TokenNumber, sizeof (UINT64)));
141 }
142
143
144
145 VOID *
146 EFIAPI
147 PeiPcdGetPtr (
148 IN UINTN TokenNumber
149 )
150 {
151 return GetWorker (TokenNumber, 0);
152 }
153
154
155
156 BOOLEAN
157 EFIAPI
158 PeiPcdGetBool (
159 IN UINTN TokenNumber
160 )
161 {
162 return *((BOOLEAN *) GetWorker (TokenNumber, sizeof (BOOLEAN)));
163 }
164
165
166
167 UINTN
168 EFIAPI
169 PeiPcdGetSize (
170 IN UINTN TokenNumber
171 )
172 {
173 PEI_PCD_DATABASE *PeiPcdDb;
174 UINTN Size;
175 UINTN MaxSize;
176
177 //
178 // If DebugAssertEnabled is TRUE, we still need to provide the GET size
179 // function as GetWorker and SetWoker need this function to do ASSERT.
180 //
181 if ((!FeaturePcdGet(PcdPeiPcdDatabaseGetSizeEnabled)) &&
182 (!DebugAssertEnabled ())) {
183 return 0;
184 }
185
186 PeiPcdDb = GetPcdDatabase ();
187 //
188 // TokenNumber Zero is reserved as PCD_INVALID_TOKEN_NUMBER.
189 // We have to decrement TokenNumber by 1 to make it usable
190 // as the array index.
191 //
192 TokenNumber--;
193
194 // EBC compiler is very choosy. It may report warning about comparison
195 // between UINTN and 0 . So we add 1 in each size of the
196 // comparison.
197 ASSERT (TokenNumber + 1 < PEI_LOCAL_TOKEN_NUMBER + 1);
198
199 Size = (PeiPcdDb->Init.LocalTokenNumberTable[TokenNumber] & PCD_DATUM_TYPE_ALL_SET) >> PCD_DATUM_TYPE_SHIFT;
200
201 if (Size == 0) {
202 //
203 // For pointer type, we need to scan the SIZE_TABLE to get the current size.
204 //
205 return GetPtrTypeSize (TokenNumber, &MaxSize, PeiPcdDb);
206 } else {
207 return Size;
208 }
209
210 }
211
212
213
214 UINT8
215 EFIAPI
216 PeiPcdGet8Ex (
217 IN CONST EFI_GUID *Guid,
218 IN UINTN ExTokenNumber
219 )
220 {
221 return *((UINT8 *) ExGetWorker (Guid, ExTokenNumber, sizeof (UINT8)));
222 }
223
224
225
226 UINT16
227 EFIAPI
228 PeiPcdGet16Ex (
229 IN CONST EFI_GUID *Guid,
230 IN UINTN ExTokenNumber
231 )
232 {
233 return ReadUnaligned16 (ExGetWorker (Guid, ExTokenNumber, sizeof (UINT16)));
234 }
235
236
237
238 UINT32
239 EFIAPI
240 PeiPcdGet32Ex (
241 IN CONST EFI_GUID *Guid,
242 IN UINTN ExTokenNumber
243 )
244 {
245 return ReadUnaligned32 (ExGetWorker (Guid, ExTokenNumber, sizeof (UINT32)));
246 }
247
248
249
250 UINT64
251 EFIAPI
252 PeiPcdGet64Ex (
253 IN CONST EFI_GUID *Guid,
254 IN UINTN ExTokenNumber
255 )
256 {
257 return ReadUnaligned64 (ExGetWorker (Guid, ExTokenNumber, sizeof (UINT64)));
258 }
259
260
261
262 VOID *
263 EFIAPI
264 PeiPcdGetPtrEx (
265 IN CONST EFI_GUID *Guid,
266 IN UINTN ExTokenNumber
267 )
268 {
269 return ExGetWorker (Guid, ExTokenNumber, 0);
270 }
271
272
273
274 BOOLEAN
275 EFIAPI
276 PeiPcdGetBoolEx (
277 IN CONST EFI_GUID *Guid,
278 IN UINTN ExTokenNumber
279 )
280 {
281 return *((BOOLEAN *) ExGetWorker (Guid, ExTokenNumber, sizeof (BOOLEAN)));
282 }
283
284
285
286 UINTN
287 EFIAPI
288 PeiPcdGetSizeEx (
289 IN CONST EFI_GUID *Guid,
290 IN UINTN ExTokenNumber
291 )
292 {
293 if ((!FeaturePcdGet (PcdPeiPcdDatabaseGetSizeEnabled)) || !FeaturePcdGet (PcdPeiPcdDatabaseExEnabled)) {
294 return 0;
295 }
296
297 return PeiPcdGetSize (GetExPcdTokenNumber (Guid, ExTokenNumber));
298 }
299
300
301
302 EFI_STATUS
303 EFIAPI
304 PeiPcdSet8 (
305 IN UINTN TokenNumber,
306 IN UINT8 Value
307 )
308 {
309 return SetValueWorker (TokenNumber, &Value, sizeof (Value));
310 }
311
312
313
314 EFI_STATUS
315 EFIAPI
316 PeiPcdSet16 (
317 IN UINTN TokenNumber,
318 IN UINT16 Value
319 )
320 {
321 return SetValueWorker (TokenNumber, &Value, sizeof (Value));
322 }
323
324
325
326 EFI_STATUS
327 EFIAPI
328 PeiPcdSet32 (
329 IN UINTN TokenNumber,
330 IN UINT32 Value
331 )
332 {
333 return SetValueWorker (TokenNumber, &Value, sizeof (Value));
334 }
335
336
337
338 EFI_STATUS
339 EFIAPI
340 PeiPcdSet64 (
341 IN UINTN TokenNumber,
342 IN UINT64 Value
343 )
344 {
345 return SetValueWorker (TokenNumber, &Value, sizeof (Value));
346 }
347
348
349 EFI_STATUS
350 EFIAPI
351 PeiPcdSetPtr (
352 IN UINTN TokenNumber,
353 IN OUT UINTN *SizeOfBuffer,
354 IN VOID *Buffer
355 )
356 {
357 return SetWorker (TokenNumber, Buffer, SizeOfBuffer, TRUE);
358 }
359
360
361
362 EFI_STATUS
363 EFIAPI
364 PeiPcdSetBool (
365 IN UINTN TokenNumber,
366 IN BOOLEAN Value
367 )
368 {
369 return SetValueWorker (TokenNumber, &Value, sizeof (Value));
370 }
371
372
373
374 EFI_STATUS
375 EFIAPI
376 PeiPcdSet8Ex (
377 IN CONST EFI_GUID *Guid,
378 IN UINTN ExTokenNumber,
379 IN UINT8 Value
380 )
381 {
382 return ExSetValueWorker (ExTokenNumber, Guid, &Value, sizeof (Value));
383 }
384
385
386
387 EFI_STATUS
388 EFIAPI
389 PeiPcdSet16Ex (
390 IN CONST EFI_GUID *Guid,
391 IN UINTN ExTokenNumber,
392 IN UINT16 Value
393 )
394 {
395 return ExSetValueWorker (ExTokenNumber, Guid, &Value, sizeof (Value));
396 }
397
398
399
400 EFI_STATUS
401 EFIAPI
402 PeiPcdSet32Ex (
403 IN CONST EFI_GUID *Guid,
404 IN UINTN ExTokenNumber,
405 IN UINT32 Value
406 )
407 {
408 return ExSetValueWorker (ExTokenNumber, Guid, &Value, sizeof (Value));
409 }
410
411
412
413 EFI_STATUS
414 EFIAPI
415 PeiPcdSet64Ex (
416 IN CONST EFI_GUID *Guid,
417 IN UINTN ExTokenNumber,
418 IN UINT64 Value
419 )
420 {
421 return ExSetValueWorker (ExTokenNumber, Guid, &Value, sizeof (Value));
422 }
423
424
425
426 EFI_STATUS
427 EFIAPI
428 PeiPcdSetPtrEx (
429 IN CONST EFI_GUID *Guid,
430 IN UINTN ExTokenNumber,
431 IN UINTN *SizeOfBuffer,
432 IN VOID *Value
433 )
434 {
435 return ExSetWorker (ExTokenNumber, Guid, Value, SizeOfBuffer, TRUE);
436 }
437
438
439
440 EFI_STATUS
441 EFIAPI
442 PeiPcdSetBoolEx (
443 IN CONST EFI_GUID *Guid,
444 IN UINTN ExTokenNumber,
445 IN BOOLEAN Value
446 )
447 {
448 return ExSetValueWorker (ExTokenNumber, Guid, &Value, sizeof (Value));
449 }
450
451
452
453
454 EFI_STATUS
455 EFIAPI
456 PeiRegisterCallBackOnSet (
457 IN CONST EFI_GUID *Guid, OPTIONAL
458 IN UINTN ExTokenNumber,
459 IN PCD_PPI_CALLBACK CallBackFunction
460 )
461 {
462 if (!FeaturePcdGet(PcdPeiPcdDatabaseCallbackOnSetEnabled)) {
463 return EFI_UNSUPPORTED;
464 }
465
466 ASSERT (CallBackFunction != NULL);
467
468 return PeiRegisterCallBackWorker (ExTokenNumber, Guid, CallBackFunction, TRUE);
469 }
470
471
472
473 EFI_STATUS
474 EFIAPI
475 PcdUnRegisterCallBackOnSet (
476 IN CONST EFI_GUID *Guid, OPTIONAL
477 IN UINTN ExTokenNumber,
478 IN PCD_PPI_CALLBACK CallBackFunction
479 )
480 {
481 if (!FeaturePcdGet(PcdPeiPcdDatabaseCallbackOnSetEnabled)) {
482 return EFI_UNSUPPORTED;
483 }
484
485 ASSERT (CallBackFunction != NULL);
486
487 return PeiRegisterCallBackWorker (ExTokenNumber, Guid, CallBackFunction, FALSE);
488 }
489
490
491
492 EFI_STATUS
493 EFIAPI
494 PeiPcdGetNextToken (
495 IN CONST EFI_GUID *Guid, OPTIONAL
496 IN OUT UINTN *TokenNumber
497 )
498 {
499 UINTN GuidTableIdx;
500 PEI_PCD_DATABASE *PeiPcdDb;
501 EFI_GUID *MatchGuid;
502 DYNAMICEX_MAPPING *ExMapTable;
503 UINTN i;
504 BOOLEAN Found;
505 BOOLEAN PeiExMapTableEmpty;
506
507 if (!FeaturePcdGet (PcdPeiPcdDatabaseTraverseEnabled)) {
508 return EFI_UNSUPPORTED;
509 }
510
511 PeiExMapTableEmpty = PEI_EXMAP_TABLE_EMPTY;
512
513 if (Guid == NULL) {
514 if (*TokenNumber > PEI_NEX_TOKEN_NUMBER) {
515 return EFI_NOT_FOUND;
516 }
517 (*TokenNumber)++;
518 if (*TokenNumber > PEI_NEX_TOKEN_NUMBER) {
519 *TokenNumber = PCD_INVALID_TOKEN_NUMBER;
520 }
521 return EFI_SUCCESS;
522 } else {
523 if (PeiExMapTableEmpty) {
524 *TokenNumber = PCD_INVALID_TOKEN_NUMBER;
525 return EFI_SUCCESS;
526 }
527
528 //
529 // Assume PCD Database AutoGen tool is sorting the ExMap based on the following order
530 // 1) ExGuid
531 // 2) ExTokenNumber
532 //
533 PeiPcdDb = GetPcdDatabase ();
534
535 MatchGuid = ScanGuid (PeiPcdDb->Init.GuidTable, sizeof(PeiPcdDb->Init.GuidTable), Guid);
536
537 if (MatchGuid == NULL) {
538 *TokenNumber = PCD_INVALID_TOKEN_NUMBER;
539 return EFI_NOT_FOUND;
540 }
541
542 GuidTableIdx = MatchGuid - PeiPcdDb->Init.GuidTable;
543
544 ExMapTable = PeiPcdDb->Init.ExMapTable;
545
546 Found = FALSE;
547 //
548 // Locate the GUID in ExMapTable first.
549 //
550 for (i = 0; i < PEI_EXMAPPING_TABLE_SIZE; i++) {
551 if (ExMapTable[i].ExGuidIndex == GuidTableIdx) {
552 Found = TRUE;
553 break;
554 }
555 }
556
557 if (Found) {
558 if (*TokenNumber == PCD_INVALID_TOKEN_NUMBER) {
559 *TokenNumber = ExMapTable[i].ExTokenNumber;
560 return EFI_SUCCESS;
561 }
562
563 for ( ; i < PEI_EXMAPPING_TABLE_SIZE; i++) {
564 if (ExMapTable[i].ExTokenNumber == *TokenNumber) {
565 i++;
566 if (i == PEI_EXMAPPING_TABLE_SIZE) {
567 //
568 // Exceed the length of ExMap Table
569 //
570 *TokenNumber = PCD_INVALID_TOKEN_NUMBER;
571 return EFI_SUCCESS;
572 }
573 if (ExMapTable[i].ExGuidIndex == GuidTableIdx) {
574 *TokenNumber = ExMapTable[i].ExTokenNumber;
575 return EFI_SUCCESS;
576 } else {
577 *TokenNumber = PCD_INVALID_TOKEN_NUMBER;
578 return EFI_SUCCESS;
579 }
580 }
581 }
582 return EFI_NOT_FOUND;
583 }
584 }
585
586 return EFI_NOT_FOUND;
587 }
588
589
590
591 EFI_STATUS
592 EFIAPI
593 PeiPcdGetNextTokenSpace (
594 IN OUT CONST EFI_GUID **Guid
595 )
596 {
597 UINTN GuidTableIdx;
598 EFI_GUID *MatchGuid;
599 PEI_PCD_DATABASE *PeiPcdDb;
600 DYNAMICEX_MAPPING *ExMapTable;
601 UINTN i;
602 BOOLEAN Found;
603 BOOLEAN PeiExMapTableEmpty;
604
605 if (!FeaturePcdGet (PcdPeiPcdDatabaseTraverseEnabled)) {
606 return EFI_UNSUPPORTED;
607 }
608
609 ASSERT (Guid != NULL);
610
611 PeiExMapTableEmpty = PEI_EXMAP_TABLE_EMPTY;
612
613 if (PeiExMapTableEmpty) {
614 if (*Guid != NULL) {
615 return EFI_NOT_FOUND;
616 } else {
617 return EFI_SUCCESS;
618 }
619 }
620
621 //
622 // Assume PCD Database AutoGen tool is sorting the ExMap based on the following order
623 // 1) ExGuid
624 // 2) ExTokenNumber
625 //
626 PeiPcdDb = GetPcdDatabase ();
627
628 ExMapTable = PeiPcdDb->Init.ExMapTable;
629
630 if (*Guid == NULL) {
631 //
632 // return the first Token Space Guid.
633 //
634 *Guid = &PeiPcdDb->Init.GuidTable[ExMapTable[0].ExGuidIndex];
635 return EFI_SUCCESS;
636 }
637
638 MatchGuid = ScanGuid (PeiPcdDb->Init.GuidTable, sizeof(PeiPcdDb->Init.GuidTable), *Guid);
639
640 if (MatchGuid == NULL) {
641 return EFI_NOT_FOUND;
642 }
643
644 GuidTableIdx = MatchGuid - PeiPcdDb->Init.GuidTable;
645
646 Found = FALSE;
647 for (i = 0; i < PEI_EXMAPPING_TABLE_SIZE; i++) {
648 if (ExMapTable[i].ExGuidIndex == GuidTableIdx) {
649 Found = TRUE;
650 break;
651 }
652 }
653
654 if (Found) {
655 i++;
656 for ( ; i < PEI_EXMAPPING_TABLE_SIZE; i++ ) {
657 if (ExMapTable[i].ExGuidIndex != GuidTableIdx ) {
658 *Guid = &PeiPcdDb->Init.GuidTable[ExMapTable[i].ExGuidIndex];
659 return EFI_SUCCESS;
660 }
661 }
662 *Guid = NULL;
663 return EFI_SUCCESS;
664 }
665
666 return EFI_NOT_FOUND;
667
668 }
669
670 UINTN
671 GetPtrTypeSize (
672 IN UINTN LocalTokenNumberTableIdx,
673 OUT UINTN *MaxSize,
674 IN PEI_PCD_DATABASE *Database
675 )
676 {
677 INTN SizeTableIdx;
678 UINTN LocalTokenNumber;
679 SKU_ID *SkuIdTable;
680 SIZE_INFO *SizeTable;
681 UINTN i;
682
683 SizeTableIdx = GetSizeTableIndex (LocalTokenNumberTableIdx, Database);
684
685 LocalTokenNumber = Database->Init.LocalTokenNumberTable[LocalTokenNumberTableIdx];
686
687 ASSERT ((LocalTokenNumber & PCD_DATUM_TYPE_ALL_SET) == PCD_DATUM_TYPE_POINTER);
688
689 SizeTable = Database->Init.SizeTable;
690
691 *MaxSize = SizeTable[SizeTableIdx];
692 //
693 // SizeTable only contain record for PCD_DATUM_TYPE_POINTER type
694 // PCD entry.
695 //
696 if (LocalTokenNumber & PCD_TYPE_VPD) {
697 //
698 // We have only one entry for VPD enabled PCD entry:
699 // 1) MAX Size.
700 // We consider current size is equal to MAX size.
701 //
702 return *MaxSize;
703 } else {
704 if ((LocalTokenNumber & PCD_TYPE_SKU_ENABLED) == 0) {
705 //
706 // We have only two entry for Non-Sku enabled PCD entry:
707 // 1) MAX SIZE
708 // 2) Current Size
709 //
710 return SizeTable[SizeTableIdx + 1];
711 } else {
712 //
713 // We have these entry for SKU enabled PCD entry
714 // 1) MAX SIZE
715 // 2) Current Size for each SKU_ID (It is equal to MaxSku).
716 //
717 SkuIdTable = GetSkuIdArray (LocalTokenNumberTableIdx, Database);
718 for (i = 0; i < SkuIdTable[0]; i++) {
719 if (SkuIdTable[1 + i] == Database->Init.SystemSkuId) {
720 return SizeTable[SizeTableIdx + 1 + i];
721 }
722 }
723 return SizeTable[SizeTableIdx + 1];
724 }
725 }
726 }
727
728
729
730 BOOLEAN
731 SetPtrTypeSize (
732 IN UINTN LocalTokenNumberTableIdx,
733 IN OUT UINTN *CurrentSize,
734 IN PEI_PCD_DATABASE *Database
735 )
736 {
737 INTN SizeTableIdx;
738 UINTN LocalTokenNumber;
739 SKU_ID *SkuIdTable;
740 SIZE_INFO *SizeTable;
741 UINTN i;
742 UINTN MaxSize;
743
744 SizeTableIdx = GetSizeTableIndex (LocalTokenNumberTableIdx, Database);
745
746 LocalTokenNumber = Database->Init.LocalTokenNumberTable[LocalTokenNumberTableIdx];
747
748 ASSERT ((LocalTokenNumber & PCD_DATUM_TYPE_ALL_SET) == PCD_DATUM_TYPE_POINTER);
749
750 SizeTable = Database->Init.SizeTable;
751
752 MaxSize = SizeTable[SizeTableIdx];
753 //
754 // SizeTable only contain record for PCD_DATUM_TYPE_POINTER type
755 // PCD entry.
756 //
757 if (LocalTokenNumber & PCD_TYPE_VPD) {
758 //
759 // We shouldn't come here as we don't support SET for VPD
760 //
761 ASSERT (FALSE);
762 return FALSE;
763 } else {
764 if ((*CurrentSize > MaxSize) ||
765 (*CurrentSize == MAX_ADDRESS)) {
766 *CurrentSize = MaxSize;
767 return FALSE;
768 }
769
770 if ((LocalTokenNumber & PCD_TYPE_SKU_ENABLED) == 0) {
771 //
772 // We have only two entry for Non-Sku enabled PCD entry:
773 // 1) MAX SIZE
774 // 2) Current Size
775 //
776 SizeTable[SizeTableIdx + 1] = (SIZE_INFO) *CurrentSize;
777 return TRUE;
778 } else {
779 //
780 // We have these entry for SKU enabled PCD entry
781 // 1) MAX SIZE
782 // 2) Current Size for each SKU_ID (It is equal to MaxSku).
783 //
784 SkuIdTable = GetSkuIdArray (LocalTokenNumberTableIdx, Database);
785 for (i = 0; i < SkuIdTable[0]; i++) {
786 if (SkuIdTable[1 + i] == Database->Init.SystemSkuId) {
787 SizeTable[SizeTableIdx + 1 + i] = (SIZE_INFO) *CurrentSize;
788 return TRUE;
789 }
790 }
791 SizeTable[SizeTableIdx + 1] = (SIZE_INFO) *CurrentSize;
792 return TRUE;
793 }
794 }
795
796 }