]> git.proxmox.com Git - mirror_edk2.git/blob - Nt32Pkg/PlatformBdsDxe/Generic/BootMaint/BootMaint.c
b23d8442f7ffc19da2b2b559d2eb2e65405191f1
[mirror_edk2.git] / Nt32Pkg / PlatformBdsDxe / Generic / BootMaint / BootMaint.c
1 /*++
2
3 Copyright (c) 2006 - 2007, 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 Module Name:
13
14 BootMaint.c
15
16 Abstract:
17
18 Boot Maintainence Main File
19
20 --*/
21
22 //
23 // Include common header file for this module.
24 //
25 #include "CommonHeader.h"
26
27 #include "Generic/Bds.h"
28 #include "BootMaint.h"
29 #include "BdsStrDefs.h"
30 #include "formguid.h"
31
32 //
33 // Form binary for Boot Maintenance
34 //
35 extern UINT8 bmBin[];
36 extern UINT8 FEBin[];
37 extern EFI_GUID gBdsStringPackGuid;
38 extern BOOLEAN gConnectAllHappened;
39
40 EFI_GUID EfiLegacyDevOrderGuid = EFI_LEGACY_DEV_ORDER_VARIABLE_GUID;
41
42 VOID
43 InitAllMenu (
44 IN BMM_CALLBACK_DATA *CallbackData
45 );
46
47 VOID
48 FreeAllMenu (
49 VOID
50 );
51
52 EFI_STATUS
53 CreateMenuStringToken (
54 IN BMM_CALLBACK_DATA *CallbackData,
55 IN EFI_HII_HANDLE HiiHandle,
56 IN BM_MENU_OPTION *MenuOption
57 )
58 /*++
59 Routine Description:
60
61 Create string tokens for a menu from its help strings and display strings
62
63 Arguments:
64
65 HiiHandle - Hii Handle of the package to be updated.
66
67 MenuOption - The Menu whose string tokens need to be created
68
69 Returns:
70
71 EFI_SUCCESS - string tokens created successfully
72
73 others - contain some errors
74
75 --*/
76 {
77 BM_MENU_ENTRY *NewMenuEntry;
78 UINTN Index;
79
80 for (Index = 0; Index < MenuOption->MenuNumber; Index++) {
81 NewMenuEntry = BOpt_GetMenuEntry (MenuOption, Index);
82 CallbackData->Hii->NewString (
83 CallbackData->Hii,
84 NULL,
85 HiiHandle,
86 &NewMenuEntry->DisplayStringToken,
87 NewMenuEntry->DisplayString
88 );
89
90 if (NULL == NewMenuEntry->HelpString) {
91 NewMenuEntry->HelpStringToken = NewMenuEntry->DisplayStringToken;
92 } else {
93 CallbackData->Hii->NewString (
94 CallbackData->Hii,
95 NULL,
96 HiiHandle,
97 &NewMenuEntry->HelpStringToken,
98 NewMenuEntry->HelpString
99 );
100 }
101 }
102
103 return EFI_SUCCESS;
104 }
105
106 EFI_STATUS
107 EFIAPI
108 DriverCallback (
109 IN EFI_FORM_CALLBACK_PROTOCOL *This,
110 IN UINT16 KeyValue,
111 IN EFI_IFR_DATA_ARRAY *Data,
112 OUT EFI_HII_CALLBACK_PACKET **Packet
113 )
114 /*++
115 Routine Description:
116
117 Callback Function for boot maintenance utility user interface interaction.
118
119 Arguments:
120
121 This - File explorer callback protocol pointer.
122 KeyValue - Key value to identify the type of data to expect.
123 Data - A pointer to the data being sent to the original exporting driver.
124 Packet - A pointer to a packet of information which a driver passes back to the browser.
125
126 Returns:
127
128 EFI_SUCCESS - Callback ended successfully.
129 Others - Contain some errors.
130
131 --*/
132 {
133 BMM_CALLBACK_DATA *Private;
134 BM_MENU_ENTRY *NewMenuEntry;
135 BMM_FAKE_NV_DATA *CurrentFakeNVMap;
136 EFI_STATUS Status;
137 UINTN OldValue;
138 UINTN NewValue;
139 UINTN Number;
140 UINTN Pos;
141 UINTN Bit;
142 UINT16 NewValuePos;
143 UINT16 Index2;
144 UINT16 Index;
145 UINT8 *OldLegacyDev;
146 UINT8 *NewLegacyDev;
147 UINT8 *Location;
148 UINT8 *DisMap;
149 FORM_ID FormId;
150
151 OldValue = 0;
152 NewValue = 0;
153 Number = 0;
154 OldLegacyDev = NULL;
155 NewLegacyDev = NULL;
156 NewValuePos = 0;
157 DisMap = NULL;
158
159 Private = BMM_CALLBACK_DATA_FROM_THIS (This);
160 UpdateData->FormCallbackHandle = (EFI_PHYSICAL_ADDRESS) (UINTN) Private->BmmCallbackHandle;
161 CurrentFakeNVMap = (BMM_FAKE_NV_DATA *) Data->NvRamMap;
162 Private->BmmFakeNvData = CurrentFakeNVMap;
163 Location = (UINT8 *) &UpdateData->Data;
164
165 UpdatePageId (Private, KeyValue);
166
167 //
168 // need to be subtituded.
169 //
170 // Update Select FD/HD/CD/NET/BEV Order Form
171 //
172 if (FORM_SET_FD_ORDER_ID == Private->BmmPreviousPageId ||
173 FORM_SET_HD_ORDER_ID == Private->BmmPreviousPageId ||
174 FORM_SET_CD_ORDER_ID == Private->BmmPreviousPageId ||
175 FORM_SET_NET_ORDER_ID == Private->BmmPreviousPageId ||
176 FORM_SET_BEV_ORDER_ID == Private->BmmPreviousPageId ||
177 ((FORM_BOOT_SETUP_ID == Private->BmmPreviousPageId) &&
178 (KeyValue >= LEGACY_FD_QUESTION_ID) &&
179 (KeyValue < (LEGACY_BEV_QUESTION_ID + 100)) )
180 ) {
181
182 DisMap = Private->BmmOldFakeNVData.DisableMap;
183
184 FormId = Private->BmmPreviousPageId;
185 if (FormId == FORM_BOOT_SETUP_ID) {
186 FormId = Private->BmmCurrentPageId;
187 }
188
189 switch (FormId) {
190 case FORM_SET_FD_ORDER_ID:
191 Number = (UINT16) LegacyFDMenu.MenuNumber;
192 OldLegacyDev = Private->BmmOldFakeNVData.LegacyFD;
193 NewLegacyDev = CurrentFakeNVMap->LegacyFD;
194 break;
195
196 case FORM_SET_HD_ORDER_ID:
197 Number = (UINT16) LegacyHDMenu.MenuNumber;
198 OldLegacyDev = Private->BmmOldFakeNVData.LegacyHD;
199 NewLegacyDev = CurrentFakeNVMap->LegacyHD;
200 break;
201
202 case FORM_SET_CD_ORDER_ID:
203 Number = (UINT16) LegacyCDMenu.MenuNumber;
204 OldLegacyDev = Private->BmmOldFakeNVData.LegacyCD;
205 NewLegacyDev = CurrentFakeNVMap->LegacyCD;
206 break;
207
208 case FORM_SET_NET_ORDER_ID:
209 Number = (UINT16) LegacyNETMenu.MenuNumber;
210 OldLegacyDev = Private->BmmOldFakeNVData.LegacyNET;
211 NewLegacyDev = CurrentFakeNVMap->LegacyNET;
212 break;
213
214 case FORM_SET_BEV_ORDER_ID:
215 Number = (UINT16) LegacyBEVMenu.MenuNumber;
216 OldLegacyDev = Private->BmmOldFakeNVData.LegacyBEV;
217 NewLegacyDev = CurrentFakeNVMap->LegacyBEV;
218 break;
219
220 default:
221 break;
222 }
223 //
224 // First, find the different position
225 // if there is change, it should be only one
226 //
227 for (Index = 0; Index < Number; Index++) {
228 if (OldLegacyDev[Index] != NewLegacyDev[Index]) {
229 OldValue = OldLegacyDev[Index];
230 NewValue = NewLegacyDev[Index];
231 break;
232 }
233 }
234
235 if (Index != Number) {
236 //
237 // there is change, now process
238 //
239 if (0xFF == NewValue) {
240 //
241 // This item will be disable
242 // Just move the items behind this forward to overlap it
243 //
244 Pos = OldValue / 8;
245 Bit = 7 - (OldValue % 8);
246 DisMap[Pos] |= (UINT8) (1 << Bit);
247 for (Index2 = Index; Index2 < Number - 1; Index2++) {
248 NewLegacyDev[Index2] = NewLegacyDev[Index2 + 1];
249 }
250
251 NewLegacyDev[Index2] = 0xFF;
252 } else {
253 for (Index2 = 0; Index2 < Number; Index2++) {
254 if (Index2 == Index) {
255 continue;
256 }
257
258 if (OldLegacyDev[Index2] == NewValue) {
259 //
260 // If NewValue is in OldLegacyDev array
261 // remember its old position
262 //
263 NewValuePos = Index2;
264 break;
265 }
266 }
267
268 if (Index2 != Number) {
269 //
270 // We will change current item to an existing item
271 // (It's hard to describe here, please read code, it's like a cycle-moving)
272 //
273 for (Index2 = NewValuePos; Index2 != Index;) {
274 if (NewValuePos < Index) {
275 NewLegacyDev[Index2] = OldLegacyDev[Index2 + 1];
276 Index2++;
277 } else {
278 NewLegacyDev[Index2] = OldLegacyDev[Index2 - 1];
279 Index2--;
280 }
281 }
282 } else {
283 //
284 // If NewValue is not in OldlegacyDev array, we are changing to a disabled item
285 // so we should modify DisMap to reflect the change
286 //
287 Pos = NewValue / 8;
288 Bit = 7 - (NewValue % 8);
289 DisMap[Pos] &= ~ (UINT8) (1 << Bit);
290 if (0xFF != OldValue) {
291 //
292 // Because NewValue is a item that was disabled before
293 // so after changing the OldValue should be disabled
294 // actually we are doing a swap of enable-disable states of two items
295 //
296 Pos = OldValue / 8;
297 Bit = 7 - (OldValue % 8);
298 DisMap[Pos] |= (UINT8) (1 << Bit);
299 }
300 }
301 }
302 //
303 // To prevent DISABLE appears in the middle of the list
304 // we should perform a re-ordering
305 //
306 Index = 0;
307 while (Index < Number) {
308 if (0xFF != NewLegacyDev[Index]) {
309 Index++;
310 continue;
311 }
312
313 Index2 = Index;
314 Index2++;
315 while (Index2 < Number) {
316 if (0xFF != NewLegacyDev[Index2]) {
317 break;
318 }
319
320 Index2++;
321 }
322
323 if (Index2 < Number) {
324 NewLegacyDev[Index] = NewLegacyDev[Index2];
325 NewLegacyDev[Index2] = 0xFF;
326 }
327
328 Index++;
329 }
330
331 CopyMem (
332 OldLegacyDev,
333 NewLegacyDev,
334 Number
335 );
336 }
337 }
338
339 if (KeyValue < FILE_OPTION_OFFSET) {
340 if (KeyValue < NORMAL_GOTO_OFFSET) {
341 switch (KeyValue) {
342 case KEY_VALUE_BOOT_FROM_FILE:
343 Private->FeCurrentState = BOOT_FROM_FILE_STATE;
344
345 //
346 // Exit Bmm main formset to send File Explorer formset.
347 //
348 CreateCallbackPacket (Packet, EXIT_REQUIRED);
349
350 break;
351
352 case FORM_BOOT_ADD_ID:
353 Private->FeCurrentState = ADD_BOOT_OPTION_STATE;
354
355 //
356 // Exit Bmm main formset to send File Explorer formset.
357 //
358 CreateCallbackPacket (Packet, EXIT_REQUIRED);
359 break;
360
361 case FORM_DRV_ADD_FILE_ID:
362 Private->FeCurrentState = ADD_DRIVER_OPTION_STATE;
363
364 //
365 // Exit Bmm main formset to send File Explorer formset.
366 //
367 CreateCallbackPacket (Packet, EXIT_REQUIRED);
368
369 break;
370
371 case FORM_DRV_ADD_HANDLE_ID:
372 CleanUpPage (FORM_DRV_ADD_HANDLE_ID, Private);
373 UpdateDrvAddHandlePage (Private);
374 break;
375
376 case FORM_BOOT_DEL_ID:
377 CleanUpPage (FORM_BOOT_DEL_ID, Private);
378 UpdateBootDelPage (Private);
379 break;
380
381 case FORM_BOOT_CHG_ID:
382 case FORM_DRV_CHG_ID:
383 UpdatePageBody (KeyValue, Private);
384 break;
385
386 case FORM_DRV_DEL_ID:
387 CleanUpPage (FORM_DRV_DEL_ID, Private);
388 UpdateDrvDelPage (Private);
389 break;
390
391 case FORM_BOOT_NEXT_ID:
392 CleanUpPage (FORM_BOOT_NEXT_ID, Private);
393 UpdateBootNextPage (Private);
394 break;
395
396 case FORM_TIME_OUT_ID:
397 CleanUpPage (FORM_TIME_OUT_ID, Private);
398 UpdateTimeOutPage (Private);
399 break;
400
401 case FORM_RESET:
402 gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
403 return EFI_UNSUPPORTED;
404
405 case FORM_CON_IN_ID:
406 case FORM_CON_OUT_ID:
407 case FORM_CON_ERR_ID:
408 UpdatePageBody (KeyValue, Private);
409 break;
410
411 case FORM_CON_COM_ID:
412 CleanUpPage (FORM_CON_COM_ID, Private);
413 UpdateConCOMPage (Private);
414 break;
415
416 case FORM_SET_FD_ORDER_ID:
417 case FORM_SET_HD_ORDER_ID:
418 case FORM_SET_CD_ORDER_ID:
419 case FORM_SET_NET_ORDER_ID:
420 case FORM_SET_BEV_ORDER_ID:
421 CleanUpPage (KeyValue, Private);
422 UpdateSetLegacyDeviceOrderPage (KeyValue, Private);
423 break;
424
425 case KEY_VALUE_SAVE_AND_EXIT:
426 case KEY_VALUE_NO_SAVE_AND_EXIT:
427
428 if (KeyValue == KEY_VALUE_SAVE_AND_EXIT) {
429 Status = ApplyChangeHandler (Private, CurrentFakeNVMap, Private->BmmPreviousPageId);
430 if (EFI_ERROR (Status)) {
431 return Status;
432 }
433 } else if (KeyValue == KEY_VALUE_NO_SAVE_AND_EXIT) {
434 DiscardChangeHandler (Private, CurrentFakeNVMap);
435 }
436 //
437 // Tell browser not to ask for confirmation of changes,
438 // since we have already applied or discarded.
439 //
440 CreateCallbackPacket (Packet, NV_NOT_CHANGED);
441 break;
442
443 default:
444 break;
445 }
446 } else if ((KeyValue >= TERMINAL_OPTION_OFFSET) && (KeyValue < CONSOLE_OPTION_OFFSET)) {
447 Index2 = (UINT16) (KeyValue - TERMINAL_OPTION_OFFSET);
448 Private->CurrentTerminal = Index2;
449
450 CleanUpPage (FORM_CON_COM_SETUP_ID, Private);
451 UpdateTerminalPage (Private);
452
453 } else if (KeyValue >= HANDLE_OPTION_OFFSET) {
454 Index2 = (UINT16) (KeyValue - HANDLE_OPTION_OFFSET);
455
456 NewMenuEntry = BOpt_GetMenuEntry (&DriverMenu, Index2);
457 ASSERT (NewMenuEntry != NULL);
458 Private->HandleContext = (BM_HANDLE_CONTEXT *) NewMenuEntry->VariableContext;
459
460 CleanUpPage (FORM_DRV_ADD_HANDLE_DESC_ID, Private);
461
462 Private->MenuEntry = NewMenuEntry;
463 Private->LoadContext->FilePathList = Private->HandleContext->DevicePath;
464
465 UpdateDriverAddHandleDescPage (Private);
466 }
467 }
468
469 return EFI_SUCCESS;
470 }
471
472 EFI_STATUS
473 ApplyChangeHandler (
474 IN BMM_CALLBACK_DATA *Private,
475 IN BMM_FAKE_NV_DATA *CurrentFakeNVMap,
476 IN FORM_ID FormId
477 )
478 /*++
479
480 Routine Description:
481
482 Function handling request to apply changes for BMM pages.
483
484 Arguments:
485
486 Private - Pointer to callback data buffer.
487 CurrentFakeNVMap - Pointer to buffer holding data of various values used by BMM
488 FormId - ID of the form which has sent the request to apply change.
489
490 Returns:
491
492 EFI_SUCCESS - Change successfully applied.
493 Other - Error occurs while trying to apply changes.
494
495 --*/
496 {
497 BM_CONSOLE_CONTEXT *NewConsoleContext;
498 BM_TERMINAL_CONTEXT *NewTerminalContext;
499 BM_LOAD_CONTEXT *NewLoadContext;
500 BM_MENU_ENTRY *NewMenuEntry;
501 EFI_STATUS Status;
502 UINT16 Index;
503
504 Status = EFI_SUCCESS;
505
506 switch (FormId) {
507 case FORM_SET_FD_ORDER_ID:
508 case FORM_SET_HD_ORDER_ID:
509 case FORM_SET_CD_ORDER_ID:
510 case FORM_SET_NET_ORDER_ID:
511 case FORM_SET_BEV_ORDER_ID:
512 Var_UpdateBBSOption (Private);
513 break;
514
515 case FORM_BOOT_DEL_ID:
516 for (Index = 0; Index < BootOptionMenu.MenuNumber; Index++) {
517 NewMenuEntry = BOpt_GetMenuEntry (&BootOptionMenu, Index);
518 NewLoadContext = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;
519 NewLoadContext->Deleted = CurrentFakeNVMap->BootOptionDel[Index];
520 }
521
522 Var_DelBootOption ();
523 break;
524
525 case FORM_DRV_DEL_ID:
526 for (Index = 0; Index < DriverOptionMenu.MenuNumber; Index++) {
527 NewMenuEntry = BOpt_GetMenuEntry (&DriverOptionMenu, Index);
528 NewLoadContext = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;
529 NewLoadContext->Deleted = CurrentFakeNVMap->DriverOptionDel[Index];
530 }
531
532 Var_DelDriverOption ();
533 break;
534
535 case FORM_BOOT_CHG_ID:
536 Status = Var_UpdateBootOrder (Private);
537 break;
538
539 case FORM_DRV_CHG_ID:
540 Status = Var_UpdateDriverOrder (Private);
541 break;
542
543 case FORM_TIME_OUT_ID:
544 Status = gRT->SetVariable (
545 L"Timeout",
546 &gEfiGlobalVariableGuid,
547 VAR_FLAG,
548 sizeof (UINT16),
549 &(CurrentFakeNVMap->BootTimeOut)
550 );
551 if (EFI_ERROR (Status)) {
552 goto Error;
553 }
554
555 Private->BmmOldFakeNVData.BootTimeOut = CurrentFakeNVMap->BootTimeOut;
556 break;
557
558 case FORM_BOOT_NEXT_ID:
559 Status = Var_UpdateBootNext (Private);
560 break;
561
562 case FORM_CON_COM_ID:
563 NewMenuEntry = BOpt_GetMenuEntry (&TerminalMenu, Private->CurrentTerminal);
564
565 ASSERT (NewMenuEntry != NULL);
566
567 NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
568
569 NewTerminalContext->BaudRateIndex = CurrentFakeNVMap->COMBaudRate;
570 NewTerminalContext->BaudRate = BaudRateList[CurrentFakeNVMap->COMBaudRate].Value;
571 NewTerminalContext->DataBitsIndex = CurrentFakeNVMap->COMDataRate;
572 NewTerminalContext->DataBits = (UINT8) DataBitsList[CurrentFakeNVMap->COMDataRate].Value;
573 NewTerminalContext->StopBitsIndex = CurrentFakeNVMap->COMStopBits;
574 NewTerminalContext->StopBits = (UINT8) StopBitsList[CurrentFakeNVMap->COMStopBits].Value;
575 NewTerminalContext->ParityIndex = CurrentFakeNVMap->COMParity;
576 NewTerminalContext->Parity = (UINT8) ParityList[CurrentFakeNVMap->COMParity].Value;
577 NewTerminalContext->TerminalType = CurrentFakeNVMap->COMTerminalType;
578
579 ChangeTerminalDevicePath (
580 NewTerminalContext->DevicePath,
581 FALSE
582 );
583
584 Var_UpdateConsoleInpOption ();
585 Var_UpdateConsoleOutOption ();
586 Var_UpdateErrorOutOption ();
587 break;
588
589 case FORM_CON_IN_ID:
590 for (Index = 0; Index < ConsoleInpMenu.MenuNumber; Index++) {
591 NewMenuEntry = BOpt_GetMenuEntry (&ConsoleInpMenu, Index);
592 NewConsoleContext = (BM_CONSOLE_CONTEXT *) NewMenuEntry->VariableContext;
593 NewConsoleContext->IsActive = CurrentFakeNVMap->ConsoleCheck[Index];
594 }
595
596 for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {
597 NewMenuEntry = BOpt_GetMenuEntry (&TerminalMenu, Index);
598 NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
599 NewTerminalContext->IsConIn = CurrentFakeNVMap->ConsoleCheck[Index + ConsoleInpMenu.MenuNumber];
600 }
601
602 Var_UpdateConsoleInpOption ();
603 break;
604
605 case FORM_CON_OUT_ID:
606 for (Index = 0; Index < ConsoleOutMenu.MenuNumber; Index++) {
607 NewMenuEntry = BOpt_GetMenuEntry (&ConsoleOutMenu, Index);
608 NewConsoleContext = (BM_CONSOLE_CONTEXT *) NewMenuEntry->VariableContext;
609 NewConsoleContext->IsActive = CurrentFakeNVMap->ConsoleCheck[Index];
610 }
611
612 for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {
613 NewMenuEntry = BOpt_GetMenuEntry (&TerminalMenu, Index);
614 NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
615 NewTerminalContext->IsConOut = CurrentFakeNVMap->ConsoleCheck[Index + ConsoleOutMenu.MenuNumber];
616 }
617
618 Var_UpdateConsoleOutOption ();
619 break;
620
621 case FORM_CON_ERR_ID:
622 for (Index = 0; Index < ConsoleErrMenu.MenuNumber; Index++) {
623 NewMenuEntry = BOpt_GetMenuEntry (&ConsoleErrMenu, Index);
624 NewConsoleContext = (BM_CONSOLE_CONTEXT *) NewMenuEntry->VariableContext;
625 NewConsoleContext->IsActive = CurrentFakeNVMap->ConsoleCheck[Index];
626 }
627
628 for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {
629 NewMenuEntry = BOpt_GetMenuEntry (&TerminalMenu, Index);
630 NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
631 NewTerminalContext->IsStdErr = CurrentFakeNVMap->ConsoleCheck[Index + ConsoleErrMenu.MenuNumber];
632 }
633
634 Var_UpdateErrorOutOption ();
635 break;
636
637 case FORM_DRV_ADD_HANDLE_DESC_ID:
638 Status = Var_UpdateDriverOption (
639 Private,
640 Private->BmmHiiHandle,
641 CurrentFakeNVMap->DriverAddHandleDesc,
642 CurrentFakeNVMap->DriverAddHandleOptionalData,
643 CurrentFakeNVMap->DriverAddForceReconnect
644 );
645 if (EFI_ERROR (Status)) {
646 goto Error;
647 }
648
649 BOpt_GetDriverOptions (Private);
650 CreateMenuStringToken (Private, Private->BmmHiiHandle, &DriverOptionMenu);
651 break;
652
653 default:
654 break;
655 }
656
657 Error:
658 return Status;
659 }
660
661 VOID
662 DiscardChangeHandler (
663 IN BMM_CALLBACK_DATA *Private,
664 IN BMM_FAKE_NV_DATA *CurrentFakeNVMap
665 )
666 {
667 UINT16 Index;
668
669 switch (Private->BmmPreviousPageId) {
670 case FORM_BOOT_CHG_ID:
671 case FORM_DRV_CHG_ID:
672 CopyMem (CurrentFakeNVMap->OptionOrder, Private->BmmOldFakeNVData.OptionOrder, 100);
673 break;
674
675 case FORM_BOOT_DEL_ID:
676 for (Index = 0; Index < BootOptionMenu.MenuNumber; Index++) {
677 CurrentFakeNVMap->BootOptionDel[Index] = 0x00;
678 }
679 break;
680
681 case FORM_DRV_DEL_ID:
682 for (Index = 0; Index < DriverOptionMenu.MenuNumber; Index++) {
683 CurrentFakeNVMap->DriverOptionDel[Index] = 0x00;
684 }
685 break;
686
687 case FORM_BOOT_NEXT_ID:
688 CurrentFakeNVMap->BootNext = Private->BmmOldFakeNVData.BootNext;
689 break;
690
691 case FORM_TIME_OUT_ID:
692 CurrentFakeNVMap->BootTimeOut = Private->BmmOldFakeNVData.BootTimeOut;
693 break;
694
695 case FORM_DRV_ADD_HANDLE_DESC_ID:
696 case FORM_DRV_ADD_FILE_ID:
697 case FORM_DRV_ADD_HANDLE_ID:
698 CurrentFakeNVMap->DriverAddHandleDesc[0] = 0x0000;
699 CurrentFakeNVMap->DriverAddHandleOptionalData[0] = 0x0000;
700 break;
701
702 default:
703 break;
704 }
705 }
706
707 EFI_STATUS
708 EFIAPI
709 NvWrite (
710 IN EFI_FORM_CALLBACK_PROTOCOL *This,
711 IN CHAR16 *VariableName,
712 IN EFI_GUID *VendorGuid,
713 OUT UINT32 Attributes OPTIONAL,
714 IN OUT UINTN DataSize,
715 OUT VOID *Buffer,
716 OUT BOOLEAN *ResetRequired
717 )
718 {
719 //
720 // Do nothing here. Just to catch the F10, we use "Apply Changes" tag to save.
721 //
722 return EFI_SUCCESS;
723 }
724
725 EFI_STATUS
726 InitializeBM (
727 VOID
728 )
729 /*++
730 Routine Description:
731
732 Initialize the Boot Maintenance Utitliy
733
734 Arguments:
735
736 ImageHandle - caller provided handle
737
738 SystemTable - caller provided system tables
739
740 Returns:
741
742 EFI_SUCCESS - utility ended successfully
743
744 others - contain some errors
745
746 --*/
747 {
748 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
749 EFI_HII_PACKAGES *PackageList;
750 BMM_CALLBACK_DATA *BmmCallbackInfo;
751 EFI_HII_PROTOCOL *Hii;
752 EFI_HII_HANDLE HiiHandle;
753 EFI_STATUS Status;
754 EFI_HANDLE Handle;
755 UINT8 *Ptr;
756 UINT8 *Location;
757
758 Status = EFI_SUCCESS;
759 UpdateData = NULL;
760 //
761 // Initialize EfiUtilityLib and EfiDriverLib
762 // Since many functions in UtilityLib must be used and
763 // SetupBrowser use DriverLib
764 //
765 //
766 // There should be only one EFI_HII_PROTOCOL Image
767 //
768 Status = EfiLibLocateProtocol (&gEfiHiiProtocolGuid, &Hii);
769 if (EFI_ERROR (Status)) {
770 return Status;
771 }
772 //
773 // Create CallbackData structures for Driver Callback
774 //
775 BmmCallbackInfo = AllocateZeroPool (sizeof (BMM_CALLBACK_DATA));
776 if (!BmmCallbackInfo) {
777 return EFI_OUT_OF_RESOURCES;
778 }
779 //
780 // Create LoadOption in BmmCallbackInfo for Driver Callback
781 //
782 Ptr = AllocateZeroPool (sizeof (BM_LOAD_CONTEXT) + sizeof (BM_FILE_CONTEXT) + sizeof (BM_HANDLE_CONTEXT) + sizeof (BM_MENU_ENTRY));
783 if (!Ptr) {
784 SafeFreePool (BmmCallbackInfo);
785 return EFI_OUT_OF_RESOURCES;
786 }
787 //
788 // Initialize Bmm callback data.
789 //
790 BmmCallbackInfo->LoadContext = (BM_LOAD_CONTEXT *) Ptr;
791 Ptr += sizeof (BM_LOAD_CONTEXT);
792
793 BmmCallbackInfo->FileContext = (BM_FILE_CONTEXT *) Ptr;
794 Ptr += sizeof (BM_FILE_CONTEXT);
795
796 BmmCallbackInfo->HandleContext = (BM_HANDLE_CONTEXT *) Ptr;
797 Ptr += sizeof (BM_HANDLE_CONTEXT);
798
799 BmmCallbackInfo->MenuEntry = (BM_MENU_ENTRY *) Ptr;
800
801 BmmCallbackInfo->BmmFakeNvData = &BmmCallbackInfo->BmmOldFakeNVData;
802
803 ZeroMem (BmmCallbackInfo->BmmFakeNvData, sizeof (BMM_FAKE_NV_DATA));
804
805 BmmCallbackInfo->Signature = BMM_CALLBACK_DATA_SIGNATURE;
806 BmmCallbackInfo->Hii = Hii;
807 BmmCallbackInfo->BmmDriverCallback.NvRead = NULL;
808 BmmCallbackInfo->BmmDriverCallback.NvWrite = NvWrite;
809 BmmCallbackInfo->BmmDriverCallback.Callback = DriverCallback;
810 BmmCallbackInfo->BmmPreviousPageId = FORM_MAIN_ID;
811 BmmCallbackInfo->BmmCurrentPageId = FORM_MAIN_ID;
812 BmmCallbackInfo->FeDriverCallback.NvRead = NULL;
813 BmmCallbackInfo->FeDriverCallback.NvWrite = NvWrite;
814 BmmCallbackInfo->FeDriverCallback.Callback = FileExplorerCallback;
815 BmmCallbackInfo->FeCurrentState = INACTIVE_STATE;
816 BmmCallbackInfo->FeDisplayContext = UNKNOWN_CONTEXT;
817
818 //
819 // Install bmm callback protocol interface
820 //
821 Handle = NULL;
822 Status = gBS->InstallProtocolInterface (
823 &Handle,
824 &gEfiFormCallbackProtocolGuid,
825 EFI_NATIVE_INTERFACE,
826 &BmmCallbackInfo->BmmDriverCallback
827 );
828
829 if (EFI_ERROR (Status)) {
830 return Status;
831 }
832
833 BmmCallbackInfo->BmmCallbackHandle = Handle;
834
835 //
836 // Install file explorer callback protocol interface
837 //
838 Handle = NULL;
839 Status = gBS->InstallProtocolInterface (
840 &Handle,
841 &gEfiFormCallbackProtocolGuid,
842 EFI_NATIVE_INTERFACE,
843 &BmmCallbackInfo->FeDriverCallback
844 );
845
846 if (EFI_ERROR (Status)) {
847 return Status;
848 }
849
850 BmmCallbackInfo->FeCallbackHandle = Handle;
851
852 //
853 // Post our VFR to the HII database.
854 //
855 PackageList = PreparePackages (1, &gBdsStringPackGuid, bmBin);
856 Status = Hii->NewPack (Hii, PackageList, &HiiHandle);
857 FreePool (PackageList);
858
859 BmmCallbackInfo->BmmHiiHandle = HiiHandle;
860
861 PackageList = PreparePackages (1, &gBdsStringPackGuid, FEBin);
862 Status = Hii->NewPack (Hii, PackageList, &HiiHandle);
863 FreePool (PackageList);
864
865 BmmCallbackInfo->FeHiiHandle = HiiHandle;
866
867 //
868 // Allocate space for creation of Buffer
869 //
870 UpdateData = AllocateZeroPool (UPDATE_DATA_SIZE);
871 if (!UpdateData) {
872 SafeFreePool (BmmCallbackInfo->LoadContext);
873 SafeFreePool (BmmCallbackInfo);
874 return EFI_OUT_OF_RESOURCES;
875 }
876 //
877 // Initialize UpdateData structure
878 //
879 RefreshUpdateData (TRUE, (EFI_PHYSICAL_ADDRESS) (UINTN) BmmCallbackInfo->BmmCallbackHandle, FALSE, 0, 0);
880
881 Location = (UINT8 *) &UpdateData->Data;
882
883 InitializeStringDepository ();
884
885 InitAllMenu (BmmCallbackInfo);
886
887 CreateMenuStringToken (BmmCallbackInfo, BmmCallbackInfo->BmmHiiHandle, &ConsoleInpMenu);
888 CreateMenuStringToken (BmmCallbackInfo, BmmCallbackInfo->BmmHiiHandle, &ConsoleOutMenu);
889 CreateMenuStringToken (BmmCallbackInfo, BmmCallbackInfo->BmmHiiHandle, &ConsoleErrMenu);
890 CreateMenuStringToken (BmmCallbackInfo, BmmCallbackInfo->BmmHiiHandle, &BootOptionMenu);
891 CreateMenuStringToken (BmmCallbackInfo, BmmCallbackInfo->BmmHiiHandle, &DriverOptionMenu);
892 CreateMenuStringToken (BmmCallbackInfo, BmmCallbackInfo->BmmHiiHandle, &TerminalMenu);
893 CreateMenuStringToken (BmmCallbackInfo, BmmCallbackInfo->BmmHiiHandle, &DriverMenu);
894
895 UpdateBootDelPage (BmmCallbackInfo);
896 UpdateDrvDelPage (BmmCallbackInfo);
897
898 if (TerminalMenu.MenuNumber > 0) {
899 BmmCallbackInfo->CurrentTerminal = 0;
900 UpdateTerminalPage (BmmCallbackInfo);
901 }
902
903 Location = (UINT8 *) &UpdateData->Data;
904 Status = EfiLibLocateProtocol (&gEfiLegacyBiosProtocolGuid, &LegacyBios);
905 if (!EFI_ERROR (Status)) {
906 //
907 // If LegacyBios Protocol is installed, add 3 tags about legacy boot option
908 // in BootOption form: legacy FD/HD/CD/NET/BEV
909 //
910 UpdateData->DataCount = 5;
911 CreateGotoOpCode (
912 FORM_SET_FD_ORDER_ID,
913 STRING_TOKEN (STR_FORM_SET_FD_ORDER_TITLE),
914 STRING_TOKEN (STR_FORM_SET_FD_ORDER_TITLE),
915 EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
916 FORM_SET_FD_ORDER_ID,
917 Location
918 );
919
920 Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
921
922 CreateGotoOpCode (
923 FORM_SET_HD_ORDER_ID,
924 STRING_TOKEN (STR_FORM_SET_HD_ORDER_TITLE),
925 STRING_TOKEN (STR_FORM_SET_HD_ORDER_TITLE),
926 EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
927 FORM_SET_HD_ORDER_ID,
928 Location
929 );
930
931 Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
932
933 CreateGotoOpCode (
934 FORM_SET_CD_ORDER_ID,
935 STRING_TOKEN (STR_FORM_SET_CD_ORDER_TITLE),
936 STRING_TOKEN (STR_FORM_SET_CD_ORDER_TITLE),
937 EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
938 FORM_SET_CD_ORDER_ID,
939 Location
940 );
941
942 Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
943
944 CreateGotoOpCode (
945 FORM_SET_NET_ORDER_ID,
946 STRING_TOKEN (STR_FORM_SET_NET_ORDER_TITLE),
947 STRING_TOKEN (STR_FORM_SET_NET_ORDER_TITLE),
948 EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
949 FORM_SET_NET_ORDER_ID,
950 Location
951 );
952
953 Location = Location + ((EFI_IFR_OP_HEADER *) Location)->Length;
954
955 CreateGotoOpCode (
956 FORM_SET_BEV_ORDER_ID,
957 STRING_TOKEN (STR_FORM_SET_BEV_ORDER_TITLE),
958 STRING_TOKEN (STR_FORM_SET_BEV_ORDER_TITLE),
959 EFI_IFR_FLAG_INTERACTIVE | EFI_IFR_FLAG_NV_ACCESS,
960 FORM_SET_BEV_ORDER_ID,
961 Location
962 );
963
964 Hii->UpdateForm (
965 Hii,
966 BmmCallbackInfo->BmmHiiHandle,
967 (EFI_FORM_LABEL) FORM_BOOT_LEGACY_DEVICE_ID,
968 TRUE,
969 UpdateData
970 );
971 }
972 //
973 // Dispatch BMM main formset and File Explorer formset.
974 //
975 FormSetDispatcher (BmmCallbackInfo);
976
977 Hii->ResetStrings (Hii, HiiHandle);
978
979 CleanUpStringDepository ();
980
981 if (EFI_ERROR (Status)) {
982 return Status;
983 }
984
985 FreeAllMenu ();
986
987 SafeFreePool (BmmCallbackInfo->LoadContext);
988 BmmCallbackInfo->LoadContext = NULL;
989 SafeFreePool (BmmCallbackInfo);
990 BmmCallbackInfo = NULL;
991 SafeFreePool (UpdateData);
992 UpdateData = NULL;
993
994 return Status;
995 }
996
997 VOID
998 InitAllMenu (
999 IN BMM_CALLBACK_DATA *CallbackData
1000 )
1001 {
1002 InitializeListHead (&BootOptionMenu.Head);
1003 InitializeListHead (&DriverOptionMenu.Head);
1004 BOpt_GetBootOptions (CallbackData);
1005 BOpt_GetDriverOptions (CallbackData);
1006 BOpt_GetLegacyOptions ();
1007 InitializeListHead (&FsOptionMenu.Head);
1008 BOpt_FindDrivers ();
1009 InitializeListHead (&DirectoryMenu.Head);
1010 InitializeListHead (&ConsoleInpMenu.Head);
1011 InitializeListHead (&ConsoleOutMenu.Head);
1012 InitializeListHead (&ConsoleErrMenu.Head);
1013 InitializeListHead (&TerminalMenu.Head);
1014 LocateSerialIo ();
1015 GetAllConsoles ();
1016 }
1017
1018 VOID
1019 FreeAllMenu (
1020 VOID
1021 )
1022 {
1023 BOpt_FreeMenu (&DirectoryMenu);
1024 BOpt_FreeMenu (&FsOptionMenu);
1025 BOpt_FreeMenu (&BootOptionMenu);
1026 BOpt_FreeMenu (&DriverOptionMenu);
1027 BOpt_FreeMenu (&DriverMenu);
1028 BOpt_FreeLegacyOptions ();
1029 FreeAllConsoles ();
1030 }
1031
1032 VOID
1033 InitializeStringDepository (
1034 VOID
1035 )
1036 /*++
1037 Routine Description:
1038 Intialize all the string depositories.
1039
1040 Arguments:
1041 None.
1042
1043 Returns:
1044 None.
1045 --*/
1046 {
1047 STRING_DEPOSITORY *StringDepository;
1048 StringDepository = AllocateZeroPool (sizeof (STRING_DEPOSITORY) * STRING_DEPOSITORY_NUMBER);
1049 FileOptionStrDepository = StringDepository++;
1050 ConsoleOptionStrDepository = StringDepository++;
1051 BootOptionStrDepository = StringDepository++;
1052 BootOptionHelpStrDepository = StringDepository++;
1053 DriverOptionStrDepository = StringDepository++;
1054 DriverOptionHelpStrDepository = StringDepository++;
1055 TerminalStrDepository = StringDepository;
1056 }
1057
1058 STRING_REF
1059 GetStringTokenFromDepository (
1060 IN BMM_CALLBACK_DATA *CallbackData,
1061 IN STRING_DEPOSITORY *StringDepository
1062 )
1063 /*++
1064 Routine Description:
1065 Fetch a usable string node from the string depository and return the string token.
1066
1067 Arguments:
1068 StringDepository - Pointer of the string depository.
1069
1070 Returns:
1071 STRING_REF - String token.
1072 --*/
1073 {
1074 STRING_LIST_NODE *CurrentListNode;
1075 STRING_LIST_NODE *NextListNode;
1076
1077 CurrentListNode = StringDepository->CurrentNode;
1078
1079 if ((NULL != CurrentListNode) && (NULL != CurrentListNode->Next)) {
1080 //
1081 // Fetch one reclaimed node from the list.
1082 //
1083 NextListNode = StringDepository->CurrentNode->Next;
1084 } else {
1085 //
1086 // If there is no usable node in the list, update the list.
1087 //
1088 NextListNode = AllocateZeroPool (sizeof (STRING_LIST_NODE));
1089
1090 CallbackData->Hii->NewString (
1091 CallbackData->Hii,
1092 NULL,
1093 CallbackData->BmmHiiHandle,
1094 &(NextListNode->StringToken),
1095 L" "
1096 );
1097
1098 ASSERT (NextListNode->StringToken != 0);
1099
1100 StringDepository->TotalNodeNumber++;
1101
1102 if (NULL == CurrentListNode) {
1103 StringDepository->ListHead = NextListNode;
1104 } else {
1105 CurrentListNode->Next = NextListNode;
1106 }
1107 }
1108
1109 StringDepository->CurrentNode = NextListNode;
1110
1111 return StringDepository->CurrentNode->StringToken;
1112 }
1113
1114 VOID
1115 ReclaimStringDepository (
1116 VOID
1117 )
1118 /*++
1119 Routine Description:
1120 Reclaim string depositories by moving the current node pointer to list head..
1121
1122 Arguments:
1123 None.
1124
1125 Returns:
1126 None.
1127 --*/
1128 {
1129 UINTN DepositoryIndex;
1130 STRING_DEPOSITORY *StringDepository;
1131
1132 StringDepository = FileOptionStrDepository;
1133 for (DepositoryIndex = 0; DepositoryIndex < STRING_DEPOSITORY_NUMBER; DepositoryIndex++) {
1134 StringDepository->CurrentNode = StringDepository->ListHead;
1135 StringDepository++;
1136 }
1137 }
1138
1139 VOID
1140 CleanUpStringDepository (
1141 VOID
1142 )
1143 /*++
1144 Routine Description:
1145 Release resource for all the string depositories.
1146
1147 Arguments:
1148 None.
1149
1150 Returns:
1151 None.
1152 --*/
1153 {
1154 UINTN NodeIndex;
1155 UINTN DepositoryIndex;
1156 STRING_LIST_NODE *CurrentListNode;
1157 STRING_LIST_NODE *NextListNode;
1158 STRING_DEPOSITORY *StringDepository;
1159
1160 //
1161 // Release string list nodes.
1162 //
1163 StringDepository = FileOptionStrDepository;
1164 for (DepositoryIndex = 0; DepositoryIndex < STRING_DEPOSITORY_NUMBER; DepositoryIndex++) {
1165 CurrentListNode = StringDepository->ListHead;
1166 for (NodeIndex = 0; NodeIndex < StringDepository->TotalNodeNumber; NodeIndex++) {
1167 NextListNode = CurrentListNode->Next;
1168 SafeFreePool (CurrentListNode);
1169 CurrentListNode = NextListNode;
1170 }
1171
1172 StringDepository++;
1173 }
1174 //
1175 // Release string depository.
1176 //
1177 SafeFreePool (FileOptionStrDepository);
1178 }
1179
1180 EFI_STATUS
1181 BdsStartBootMaint (
1182 VOID
1183 )
1184 /*++
1185
1186 Routine Description:
1187 Start boot maintenance manager
1188
1189 Arguments:
1190
1191 Returns:
1192
1193 --*/
1194 {
1195 EFI_STATUS Status;
1196 LIST_ENTRY BdsBootOptionList;
1197
1198 InitializeListHead (&BdsBootOptionList);
1199
1200 //
1201 // Connect all prior to entering the platform setup menu.
1202 //
1203 if (!gConnectAllHappened) {
1204 BdsLibConnectAllDriversToAllControllers ();
1205 gConnectAllHappened = TRUE;
1206 }
1207 //
1208 // Have chance to enumerate boot device
1209 //
1210 BdsLibEnumerateAllBootOption (&BdsBootOptionList);
1211
1212 //
1213 // Init the BMM
1214 //
1215 Status = InitializeBM ();
1216
1217 return Status;
1218 }
1219
1220 EFI_STATUS
1221 FormSetDispatcher (
1222 IN BMM_CALLBACK_DATA *CallbackData
1223 )
1224 /*++
1225
1226 Routine Description:
1227 Dispatch BMM formset and FileExplorer formset.
1228
1229 Arguments:
1230
1231 Returns:
1232
1233 --*/
1234 {
1235 EFI_FORM_BROWSER_PROTOCOL *FormConfig;
1236 UINT8 *Location;
1237 EFI_STATUS Status;
1238 UINTN Index;
1239 BM_MENU_ENTRY *NewMenuEntry;
1240 BM_FILE_CONTEXT *NewFileContext;
1241 BOOLEAN BootMaintMenuResetRequired;
1242
1243 Location = NULL;
1244 Index = 0;
1245 NewMenuEntry = NULL;
1246 NewFileContext = NULL;
1247
1248 //
1249 // There should only be one Form Configuration protocol
1250 //
1251 Status = EfiLibLocateProtocol (&gEfiFormBrowserProtocolGuid, &FormConfig);
1252 if (EFI_ERROR (Status)) {
1253 return Status;
1254 }
1255
1256 while (1) {
1257 UpdatePageId (CallbackData, FORM_MAIN_ID);
1258
1259 BootMaintMenuResetRequired = FALSE;
1260 Status = FormConfig->SendForm (
1261 FormConfig,
1262 TRUE,
1263 &(CallbackData->BmmHiiHandle),
1264 1,
1265 NULL,
1266 NULL,
1267 (UINT8 *) CallbackData->BmmFakeNvData,
1268 NULL,
1269 &BootMaintMenuResetRequired
1270 );
1271
1272 if (BootMaintMenuResetRequired) {
1273 EnableResetRequired ();
1274 }
1275
1276 ReclaimStringDepository ();
1277
1278 //
1279 // When this Formset returns, check if we are going to explore files.
1280 //
1281 if (INACTIVE_STATE != CallbackData->FeCurrentState) {
1282 UpdateFileExplorer (CallbackData, 0);
1283
1284 BootMaintMenuResetRequired = FALSE;
1285 Status = FormConfig->SendForm (
1286 FormConfig,
1287 TRUE,
1288 &(CallbackData->FeHiiHandle),
1289 1,
1290 NULL,
1291 NULL,
1292 NULL,
1293 NULL,
1294 &BootMaintMenuResetRequired
1295 );
1296
1297 if (BootMaintMenuResetRequired) {
1298 EnableResetRequired ();
1299 }
1300
1301 CallbackData->FeCurrentState = INACTIVE_STATE;
1302 CallbackData->FeDisplayContext = UNKNOWN_CONTEXT;
1303 ReclaimStringDepository ();
1304 } else {
1305 break;
1306 }
1307 }
1308
1309 return Status;
1310 }
1311
1312 VOID
1313 CreateCallbackPacket (
1314 OUT EFI_HII_CALLBACK_PACKET **Packet,
1315 IN UINT16 Flags
1316 )
1317 {
1318 *Packet = (EFI_HII_CALLBACK_PACKET *) AllocateZeroPool (sizeof (EFI_HII_CALLBACK_PACKET) + 2);
1319 ASSERT (*Packet != NULL);
1320
1321 (*Packet)->DataArray.EntryCount = 1;
1322 (*Packet)->DataArray.NvRamMap = NULL;
1323 ((EFI_IFR_DATA_ENTRY *) (&((*Packet)->DataArray) + 1))->Flags = Flags;
1324 }