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
17EFI_STATUS
18BootOptionParseLoadOption (
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
71EFI_STATUS
72BootOptionFromLoadOptionVariable (
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
90EFI_STATUS
91BootOptionFromLoadOptionIndex (
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
109EFI_STATUS
110BootOptionToLoadOptionVariable (
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
241UINT16
242BootOptionAllocateBootIndex (
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