1 /** @file
2 *
3 *  Copyright (c) 2011-2013, ARM Limited. All rights reserved.
4 *
5 *  This program and the accompanying materials
6 *  are licensed and made available under the terms and conditions of the BSD License
7 *  which accompanies this distribution.  The full text of the license may be found at
8 *  http://opensource.org/licenses/bsd-license.php
9 *
10 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14 
15 #include "BdsInternal.h"
16 
17 EFI_STATUS
BootOptionParseLoadOption(IN EFI_LOAD_OPTION * EfiLoadOption,IN UINTN EfiLoadOptionSize,IN OUT BDS_LOAD_OPTION ** BdsLoadOption)18 BootOptionParseLoadOption (
19   IN     EFI_LOAD_OPTION *EfiLoadOption,
20   IN     UINTN           EfiLoadOptionSize,
21   IN OUT BDS_LOAD_OPTION **BdsLoadOption
22   )
23 {
24   BDS_LOAD_OPTION *LoadOption;
25   UINTN           DescriptionLength;
26   UINTN           EfiLoadOptionPtr;
27 
28   if (EfiLoadOption == NULL) {
29     return EFI_INVALID_PARAMETER;
30   }
31 
32   if (EfiLoadOptionSize < sizeof(UINT32) + sizeof(UINT16) + sizeof(CHAR16) + sizeof(EFI_DEVICE_PATH_PROTOCOL)) {
33     return EFI_BAD_BUFFER_SIZE;
34   }
35 
36   if (*BdsLoadOption == NULL) {
37     LoadOption = (BDS_LOAD_OPTION*)AllocateZeroPool (sizeof(BDS_LOAD_OPTION));
38     if (LoadOption == NULL) {
39       return EFI_OUT_OF_RESOURCES;
40     }
41   } else {
42     LoadOption = *BdsLoadOption;
43   }
44 
45   EfiLoadOptionPtr           = (UINTN)EfiLoadOption;
46   LoadOption->LoadOption     = EfiLoadOption;
47   LoadOption->LoadOptionSize = EfiLoadOptionSize;
48 
49   LoadOption->Attributes         = *(UINT32*)EfiLoadOptionPtr;
50   LoadOption->FilePathListLength = *(UINT16*)(EfiLoadOptionPtr + sizeof(UINT32));
51   LoadOption->Description        = (CHAR16*)(EfiLoadOptionPtr + sizeof(UINT32) + sizeof(UINT16));
52   DescriptionLength              = StrSize (LoadOption->Description);
53   LoadOption->FilePathList       = (EFI_DEVICE_PATH_PROTOCOL*)(EfiLoadOptionPtr + sizeof(UINT32) + sizeof(UINT16) + DescriptionLength);
54 
55   // If ((End of EfiLoadOptiony - Start of EfiLoadOption) == EfiLoadOptionSize) then No Optional Data
56   if ((UINTN)((UINTN)LoadOption->FilePathList + LoadOption->FilePathListLength - EfiLoadOptionPtr) == EfiLoadOptionSize) {
57     LoadOption->OptionalData     = NULL;
58     LoadOption->OptionalDataSize = 0;
59   } else {
60     LoadOption->OptionalData     = (VOID*)((UINTN)(LoadOption->FilePathList) + LoadOption->FilePathListLength);
61     LoadOption->OptionalDataSize = EfiLoadOptionSize - ((UINTN)LoadOption->OptionalData - EfiLoadOptionPtr);
62   }
63 
64   if (*BdsLoadOption == NULL) {
65     *BdsLoadOption = LoadOption;
66   }
67 
68   return EFI_SUCCESS;
69 }
70 
71 EFI_STATUS
BootOptionFromLoadOptionVariable(IN CHAR16 * BootVariableName,OUT BDS_LOAD_OPTION ** BdsLoadOption)72 BootOptionFromLoadOptionVariable (
73   IN  CHAR16*           BootVariableName,
74   OUT BDS_LOAD_OPTION** BdsLoadOption
75   )
76 {
77   EFI_STATUS            Status;
78   EFI_LOAD_OPTION       *EfiLoadOption;
79   UINTN                 EfiLoadOptionSize;
80 
81   Status = GetGlobalEnvironmentVariable (BootVariableName, NULL, &EfiLoadOptionSize, (VOID**)&EfiLoadOption);
82   if (!EFI_ERROR(Status)) {
83     *BdsLoadOption = NULL;
84     Status = BootOptionParseLoadOption (EfiLoadOption, EfiLoadOptionSize, BdsLoadOption);
85   }
86 
87   return Status;
88 }
89 
90 EFI_STATUS
BootOptionFromLoadOptionIndex(IN UINT16 LoadOptionIndex,OUT BDS_LOAD_OPTION ** BdsLoadOption)91 BootOptionFromLoadOptionIndex (
92   IN  UINT16            LoadOptionIndex,
93   OUT BDS_LOAD_OPTION **BdsLoadOption
94   )
95 {
96   CHAR16        BootVariableName[9];
97   EFI_STATUS    Status;
98 
99   UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", LoadOptionIndex);
100 
101   Status = BootOptionFromLoadOptionVariable (BootVariableName, BdsLoadOption);
102   if (!EFI_ERROR(Status)) {
103     (*BdsLoadOption)->LoadOptionIndex = LoadOptionIndex;
104   }
105 
106   return Status;
107 }
108 
109 EFI_STATUS
BootOptionToLoadOptionVariable(IN BDS_LOAD_OPTION * BdsLoadOption)110 BootOptionToLoadOptionVariable (
111   IN BDS_LOAD_OPTION*   BdsLoadOption
112   )
113 {
114   EFI_STATUS                    Status;
115   UINTN                         DescriptionSize;
116   //UINT16                        FilePathListLength;
117   EFI_DEVICE_PATH_PROTOCOL*     DevicePathNode;
118   UINTN                         NodeLength;
119   UINT8*                        EfiLoadOptionPtr;
120   VOID*                         OldLoadOption;
121   CHAR16                        BootVariableName[9];
122   UINTN                         BootOrderSize;
123   UINT16*                       BootOrder;
124 
125   // If we are overwriting an existent Boot Option then we have to free previously allocated memory
126   if (BdsLoadOption->LoadOptionSize > 0) {
127     OldLoadOption = BdsLoadOption->LoadOption;
128   } else {
129     OldLoadOption = NULL;
130 
131     // If this function is called at the creation of the Boot Device entry (not at the update) the
132     // BootOption->LoadOptionSize must be zero then we get a new BootIndex for this entry
133     BdsLoadOption->LoadOptionIndex = BootOptionAllocateBootIndex ();
134 
135     //TODO: Add to the the Boot Entry List
136   }
137 
138   DescriptionSize = StrSize(BdsLoadOption->Description);
139 
140   // Ensure the FilePathListLength information is correct
141   ASSERT (GetDevicePathSize (BdsLoadOption->FilePathList) == BdsLoadOption->FilePathListLength);
142 
143   // Allocate the memory for the EFI Load Option
144   BdsLoadOption->LoadOptionSize = sizeof(UINT32) + sizeof(UINT16) + DescriptionSize + BdsLoadOption->FilePathListLength + BdsLoadOption->OptionalDataSize;
145 
146   BdsLoadOption->LoadOption = (EFI_LOAD_OPTION *)AllocateZeroPool (BdsLoadOption->LoadOptionSize);
147   if (BdsLoadOption->LoadOption == NULL) {
148     return EFI_OUT_OF_RESOURCES;
149   }
150 
151   EfiLoadOptionPtr = (UINT8 *) BdsLoadOption->LoadOption;
152 
153   //
154   // Populate the EFI Load Option and BDS Boot Option structures
155   //
156 
157   // Attributes fields
158   *(UINT32*)EfiLoadOptionPtr = BdsLoadOption->Attributes;
159   EfiLoadOptionPtr += sizeof(UINT32);
160 
161   // FilePath List fields
162   *(UINT16*)EfiLoadOptionPtr = BdsLoadOption->FilePathListLength;
163   EfiLoadOptionPtr += sizeof(UINT16);
164 
165   // Boot description fields
166   CopyMem (EfiLoadOptionPtr, BdsLoadOption->Description, DescriptionSize);
167   EfiLoadOptionPtr += DescriptionSize;
168 
169   // File path fields
170   DevicePathNode = BdsLoadOption->FilePathList;
171   while (!IsDevicePathEndType (DevicePathNode)) {
172     NodeLength = DevicePathNodeLength(DevicePathNode);
173     CopyMem (EfiLoadOptionPtr, DevicePathNode, NodeLength);
174     EfiLoadOptionPtr += NodeLength;
175     DevicePathNode = NextDevicePathNode (DevicePathNode);
176   }
177 
178   // Set the End Device Path Type
179   SetDevicePathEndNode (EfiLoadOptionPtr);
180   EfiLoadOptionPtr += sizeof(EFI_DEVICE_PATH);
181 
182   // Fill the Optional Data
183   if (BdsLoadOption->OptionalDataSize > 0) {
184     CopyMem (EfiLoadOptionPtr, BdsLoadOption->OptionalData, BdsLoadOption->OptionalDataSize);
185   }
186 
187   // Case where the fields have been updated
188   if (OldLoadOption) {
189     // Now, the old data has been copied to the new allocated packed structure, we need to update the pointers of BdsLoadOption
190     BootOptionParseLoadOption (BdsLoadOption->LoadOption, BdsLoadOption->LoadOptionSize, &BdsLoadOption);
191     // Free the old packed structure
192     FreePool (OldLoadOption);
193   }
194 
195   // Create/Update Boot#### environment variable
196   UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BdsLoadOption->LoadOptionIndex);
197   Status = gRT->SetVariable (
198       BootVariableName,
199       &gEfiGlobalVariableGuid,
200       EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
201       BdsLoadOption->LoadOptionSize,
202       BdsLoadOption->LoadOption
203       );
204 
205   // When it is a new entry we must add the entry to the BootOrder
206   if (OldLoadOption == NULL) {
207     // Add the new Boot Index to the list
208     Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
209     if (!EFI_ERROR(Status)) {
210       BootOrder = ReallocatePool (BootOrderSize, BootOrderSize + sizeof(UINT16), BootOrder);
211       // Add the new index at the end
212       BootOrder[BootOrderSize / sizeof(UINT16)] = BdsLoadOption->LoadOptionIndex;
213       BootOrderSize += sizeof(UINT16);
214     } else {
215       // BootOrder does not exist. Create it
216       BootOrderSize = sizeof(UINT16);
217       BootOrder = &(BdsLoadOption->LoadOptionIndex);
218     }
219 
220     // Update (or Create) the BootOrder environment variable
221     gRT->SetVariable (
222         L"BootOrder",
223         &gEfiGlobalVariableGuid,
224         EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
225         BootOrderSize,
226         BootOrder
227         );
228     DEBUG((EFI_D_ERROR,"Create %s\n",BootVariableName));
229 
230     // Free memory allocated by GetGlobalEnvironmentVariable
231     if (!EFI_ERROR(Status)) {
232       FreePool (BootOrder);
233     }
234   } else {
235     DEBUG((EFI_D_ERROR,"Update %s\n",BootVariableName));
236   }
237 
238   return EFI_SUCCESS;
239 }
240 
241 UINT16
BootOptionAllocateBootIndex(VOID)242 BootOptionAllocateBootIndex (
243   VOID
244   )
245 {
246   EFI_STATUS        Status;
247   UINTN             Index;
248   UINT32            BootIndex;
249   UINT16            *BootOrder;
250   UINTN             BootOrderSize;
251   BOOLEAN           Found;
252 
253   // Get the Boot Option Order from the environment variable
254   Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
255   if (!EFI_ERROR(Status)) {
256     for (BootIndex = 0; BootIndex <= 0xFFFF; BootIndex++) {
257       Found = FALSE;
258       for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
259         if (BootOrder[Index] == BootIndex) {
260           Found = TRUE;
261           break;
262         }
263       }
264       if (!Found) {
265         return BootIndex;
266       }
267     }
268     FreePool (BootOrder);
269   }
270   // Return the first index
271   return 0;
272 }
273