1 /*
2  * Copyright (C) 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 #include "variable_mem.h"
19 
20 #include <stdio.h>
21 #include <string.h>
22 #include <malloc.h>
23 #include <lk/list.h>
24 #include <uefi/types.h>
25 #include "charset.h"
26 
27 namespace {
28   constexpr size_t kVarNameMax = 128;
29 
30   struct EfiVariable {
31     struct list_node node;
32     char16_t VariableName[kVarNameMax];
33     EfiGuid VendorGuid;
34     uint32_t Attributes;
35     char* Data;
36     size_t DataLen;
37   };
38 }
39 
40 static list_node variables_in_mem = LIST_INITIAL_VALUE(variables_in_mem);
41 
search_existing_variable(const char16_t * varname,const EfiGuid * guid)42 static EfiVariable * search_existing_variable(const char16_t *varname,
43                                               const EfiGuid *guid) {
44   /* search for existing variable */
45   struct EfiVariable *var = nullptr;
46   list_for_every_entry(&variables_in_mem, var, struct EfiVariable, node) {
47     if (guid != NULL && memcmp(&var->VendorGuid, guid, sizeof(EfiGuid)) != 0) {
48       continue;
49     }
50     if (utf16_strcmp(varname, var->VariableName) != 0) {
51       continue;
52     }
53     return var;
54   }
55   return nullptr;
56 }
57 
efi_get_variable(const char16_t * variable_name,const EfiGuid * guid,uint32_t * attribute,char ** data,size_t * data_size)58 EfiStatus efi_get_variable(const char16_t *variable_name,
59                            const EfiGuid *guid,
60                            uint32_t *attribute,
61                            char **data,
62                            size_t *data_size) {
63   struct EfiVariable *var = search_existing_variable(variable_name, guid);
64 
65   if (data)
66     *data = nullptr;
67   if (data_size)
68     *data_size = 0;
69   if (attribute)
70     *attribute = 0;
71   if (var) {
72     if (data)
73       *data = var->Data;
74     if (data_size)
75       *data_size = var->DataLen;
76     if (attribute)
77       *attribute = var->Attributes;
78     return SUCCESS;
79   }
80   return NOT_FOUND;
81 }
82 
efi_set_variable(const char16_t * variable_name,const EfiGuid * guid,uint32_t attribute,const char * data,size_t data_len)83 void efi_set_variable(const char16_t *variable_name,
84                       const EfiGuid *guid,
85                       uint32_t attribute,
86                       const char *data,
87                       size_t data_len) {
88   struct EfiVariable *var = search_existing_variable(variable_name, guid);
89 
90   /* alloc new variable if it is not existed */
91   if (!var) {
92     var = reinterpret_cast<struct EfiVariable *>(malloc(sizeof(struct EfiVariable)));
93     memset(var, 0, sizeof(struct EfiVariable));
94     var->node = LIST_INITIAL_CLEARED_VALUE;
95     size_t name_len = utf16_strlen(variable_name);
96     if (name_len >= kVarNameMax)
97 	name_len = kVarNameMax - 1;
98     memcpy(var->VariableName, variable_name, name_len * sizeof(char16_t));
99     list_add_tail(&variables_in_mem, &var->node);
100   }
101   if (var->Data) {
102     free(var->Data);
103   }
104   var->Data = nullptr;
105   var->DataLen = 0;
106   if (data_len > 0) {
107     var->Data = reinterpret_cast<char *>(malloc(data_len));
108     memcpy(var->Data, data, data_len);
109     var->DataLen = data_len;
110   }
111   var->Attributes = attribute;
112   if (guid) {
113     memcpy(&var->VendorGuid, guid, sizeof(EfiGuid));
114   }
115 }
116 
efi_list_variable(void)117 void efi_list_variable(void) {
118   struct EfiVariable *var = nullptr;
119   list_for_every_entry(&variables_in_mem, var, struct EfiVariable, node) {
120     char varname[kVarNameMax];
121 
122     utf16_to_utf8(varname, var->VariableName, sizeof(varname));
123     printf("%s\n", varname);
124     /* print GUID */
125     printf("    %08x-%04x-%04x",
126            var->VendorGuid.data1,
127            var->VendorGuid.data2,
128            var->VendorGuid.data3);
129     for (int i = 0; i < 8 ; i++) {
130       if (i == 0 || i == 2) {
131         printf("-");
132       }
133       printf("%02x", var->VendorGuid.data4[i]);
134     }
135     printf("\n");
136     printf("    ");
137     /* print attributes */
138     for (uint32_t attr = 1; attr <= var->Attributes; attr <<= 1) {
139       if (var->Attributes & attr) {
140         if (attr == EFI_VARIABLE_NON_VOLATILE) {
141           printf("NV");
142         } else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) {
143           printf("BS");
144         } else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) {
145           printf("RT");
146         } else {
147           printf("0x%02x", attr);
148         }
149         if (attr << 1 <= var->Attributes) {
150           printf("|");
151         }
152       }
153     }
154     printf(", DataSize = 0x%zx\n", var->DataLen);
155     /* dump data */
156     for (size_t i = 0; i < var->DataLen ; i+=16) {
157       printf("    %08zx:", i);
158       for (size_t j = 0; j < 16; j++) {
159         if (i + j < var->DataLen) {
160           printf(" %02x", var->Data[i + j]);
161         } else {
162           printf("   ");
163         }
164       }
165       printf("  ");
166       for (size_t j = 0; j < 16 && i + j < var->DataLen; j++) {
167         if (var->Data[i + j] >= 0x20 && var->Data[i + j] < 0x7e) {
168           printf("%c", var->Data[i + j]);
169         } else {
170           printf(".");
171         }
172       }
173       printf("\n");
174     }
175   }
176 }
177