1 /* 2 * SPDX-License-Identifier: GPL-2.0-or-later 3 * 4 * uefi vars device - serialize non-volatile varstore from/to json, 5 * using qapi 6 * 7 * tools which can read/write these json files: 8 * - https://gitlab.com/kraxel/virt-firmware 9 * - https://github.com/awslabs/python-uefivars 10 */ 11 #include "qemu/osdep.h" 12 #include "qemu/cutils.h" 13 #include "qemu/error-report.h" 14 #include "system/dma.h" 15 16 #include "hw/uefi/var-service.h" 17 18 #include "qobject/qobject.h" 19 #include "qobject/qjson.h" 20 21 #include "qapi/dealloc-visitor.h" 22 #include "qapi/qobject-input-visitor.h" 23 #include "qapi/qobject-output-visitor.h" 24 #include "qapi/qapi-types-uefi.h" 25 #include "qapi/qapi-visit-uefi.h" 26 27 static char *generate_hexstr(void *data, size_t len) 28 { 29 static const char hex[] = { 30 '0', '1', '2', '3', '4', '5', '6', '7', 31 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 32 }; 33 uint8_t *src = data; 34 char *dest; 35 size_t i; 36 37 dest = g_malloc(len * 2 + 1); 38 for (i = 0; i < len * 2;) { 39 dest[i++] = hex[*src >> 4]; 40 dest[i++] = hex[*src & 15]; 41 src++; 42 } 43 dest[i++] = 0; 44 45 return dest; 46 } 47 48 static UefiVarStore *uefi_vars_to_qapi(uefi_vars_state *uv) 49 { 50 UefiVarStore *vs; 51 UefiVariableList **tail; 52 UefiVariable *v; 53 QemuUUID be; 54 uefi_variable *var; 55 56 vs = g_new0(UefiVarStore, 1); 57 vs->version = 2; 58 tail = &vs->variables; 59 60 QTAILQ_FOREACH(var, &uv->variables, next) { 61 if (!(var->attributes & EFI_VARIABLE_NON_VOLATILE)) { 62 continue; 63 } 64 65 v = g_new0(UefiVariable, 1); 66 be = qemu_uuid_bswap(var->guid); 67 v->guid = qemu_uuid_unparse_strdup(&be); 68 v->name = uefi_ucs2_to_ascii(var->name, var->name_size); 69 v->attr = var->attributes; 70 71 v->data = generate_hexstr(var->data, var->data_size); 72 73 if (var->attributes & 74 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { 75 v->time = generate_hexstr(&var->time, sizeof(var->time)); 76 if (var->digest && var->digest_size) { 77 v->digest = generate_hexstr(var->digest, var->digest_size); 78 } 79 } 80 81 QAPI_LIST_APPEND(tail, v); 82 } 83 return vs; 84 } 85 86 static unsigned parse_hexchar(char c) 87 { 88 switch (c) { 89 case '0' ... '9': return c - '0'; 90 case 'a' ... 'f': return c - 'a' + 0xa; 91 case 'A' ... 'F': return c - 'A' + 0xA; 92 default: return 0; 93 } 94 } 95 96 static void parse_hexstr(void *dest, char *src, int len) 97 { 98 uint8_t *data = dest; 99 size_t i; 100 101 for (i = 0; i < len; i += 2) { 102 *(data++) = 103 parse_hexchar(src[i]) << 4 | 104 parse_hexchar(src[i + 1]); 105 } 106 } 107 108 static void uefi_vars_from_qapi(uefi_vars_state *uv, UefiVarStore *vs) 109 { 110 UefiVariableList *item; 111 UefiVariable *v; 112 QemuUUID be; 113 uefi_variable *var; 114 uint8_t *data; 115 size_t i, len; 116 117 for (item = vs->variables; item != NULL; item = item->next) { 118 v = item->value; 119 120 var = g_new0(uefi_variable, 1); 121 var->attributes = v->attr; 122 qemu_uuid_parse(v->guid, &be); 123 var->guid = qemu_uuid_bswap(be); 124 125 len = strlen(v->name); 126 var->name_size = len * 2 + 2; 127 var->name = g_malloc(var->name_size); 128 for (i = 0; i <= len; i++) { 129 var->name[i] = v->name[i]; 130 } 131 132 len = strlen(v->data); 133 var->data_size = len / 2; 134 var->data = data = g_malloc(var->data_size); 135 parse_hexstr(var->data, v->data, len); 136 137 if (v->time && strlen(v->time) == 32) { 138 parse_hexstr(&var->time, v->time, 32); 139 } 140 141 if (v->digest) { 142 len = strlen(v->digest); 143 var->digest_size = len / 2; 144 var->digest = g_malloc(var->digest_size); 145 parse_hexstr(var->digest, v->digest, len); 146 } 147 148 QTAILQ_INSERT_TAIL(&uv->variables, var, next); 149 } 150 } 151 152 static GString *uefi_vars_to_json(uefi_vars_state *uv) 153 { 154 UefiVarStore *vs = uefi_vars_to_qapi(uv); 155 QObject *qobj = NULL; 156 Visitor *v; 157 GString *gstr; 158 159 v = qobject_output_visitor_new(&qobj); 160 if (visit_type_UefiVarStore(v, NULL, &vs, NULL)) { 161 visit_complete(v, &qobj); 162 } 163 visit_free(v); 164 qapi_free_UefiVarStore(vs); 165 166 gstr = qobject_to_json_pretty(qobj, true); 167 qobject_unref(qobj); 168 169 return gstr; 170 } 171 172 void uefi_vars_json_init(uefi_vars_state *uv, Error **errp) 173 { 174 if (uv->jsonfile) { 175 uv->jsonfd = qemu_create(uv->jsonfile, O_RDWR, 0666, errp); 176 } 177 } 178 179 void uefi_vars_json_save(uefi_vars_state *uv) 180 { 181 GString *gstr; 182 int rc; 183 184 if (uv->jsonfd == -1) { 185 return; 186 } 187 188 gstr = uefi_vars_to_json(uv); 189 190 lseek(uv->jsonfd, 0, SEEK_SET); 191 rc = ftruncate(uv->jsonfd, 0); 192 if (rc != 0) { 193 warn_report("%s: ftruncate error", __func__); 194 } 195 rc = write(uv->jsonfd, gstr->str, gstr->len); 196 if (rc != gstr->len) { 197 warn_report("%s: write error", __func__); 198 } 199 fsync(uv->jsonfd); 200 201 g_string_free(gstr, true); 202 } 203 204 void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) 205 { 206 UefiVarStore *vs; 207 QObject *qobj; 208 Visitor *v; 209 char *str; 210 size_t len; 211 int rc; 212 213 if (uv->jsonfd == -1) { 214 return; 215 } 216 217 len = lseek(uv->jsonfd, 0, SEEK_END); 218 if (len == 0) { 219 return; 220 } 221 222 str = g_malloc(len + 1); 223 lseek(uv->jsonfd, 0, SEEK_SET); 224 rc = read(uv->jsonfd, str, len); 225 if (rc != len) { 226 warn_report("%s: read error", __func__); 227 } 228 str[len] = 0; 229 230 qobj = qobject_from_json(str, errp); 231 v = qobject_input_visitor_new(qobj); 232 visit_type_UefiVarStore(v, NULL, &vs, errp); 233 visit_free(v); 234 235 if (!(*errp)) { 236 uefi_vars_from_qapi(uv, vs); 237 uefi_vars_update_storage(uv); 238 } 239 240 qapi_free_UefiVarStore(vs); 241 qobject_unref(qobj); 242 g_free(str); 243 } 244