Home | History | Annotate | Download | only in BdsLib
      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
     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
     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
     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
    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
    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