Coverage Report

Created: 2026-09-03 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-efivars.c
Line
Count
Source
1
/*
2
 * Copyright 2018 Richard Hughes <richard@hughsie.com>
3
 * Copyright 2015 Peter Jones <pjones@redhat.com>
4
 *
5
 * SPDX-License-Identifier: LGPL-2.1-or-later
6
 */
7
8
#include "config.h"
9
10
#include "fwupd-error.h"
11
12
#include "fu-byte-array.h"
13
#include "fu-efi-device-path-list.h"
14
#include "fu-efi-file-path-device-path.h"
15
#include "fu-efi-hard-drive-device-path.h"
16
#include "fu-efivars-private.h"
17
#include "fu-mem.h"
18
#include "fu-pefile-firmware.h"
19
20
typedef struct {
21
  FuPathStore *pstore;
22
} FuEfivarsPrivate;
23
24
enum { PROP_0, PROP_PATH_STORE, PROP_LAST };
25
26
0
G_DEFINE_TYPE_WITH_PRIVATE(FuEfivars, fu_efivars, G_TYPE_OBJECT);
27
0
28
0
#define GET_PRIVATE(o) (fu_efivars_get_instance_private(o))
29
30
/**
31
 * fu_efivars_get_path_store:
32
 * @self: a #FuEfivars
33
 *
34
 * Gets the well-known path store.
35
 *
36
 * Returns: (transfer none): a #FuPathStore, or %NULL if not set
37
 *
38
 * Since: 2.1.1
39
 **/
40
FuPathStore *
41
fu_efivars_get_path_store(FuEfivars *self)
42
0
{
43
0
  FuEfivarsPrivate *priv = GET_PRIVATE(self);
44
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
45
0
  return priv->pstore;
46
0
}
47
48
/**
49
 * fu_efivars_supported:
50
 * @self: a #FuEfivars
51
 * @error: #GError
52
 *
53
 * Determines if the kernel supports EFI variables
54
 *
55
 * Returns: %TRUE on success
56
 *
57
 * Since: 2.0.0
58
 **/
59
gboolean
60
fu_efivars_supported(FuEfivars *self, GError **error)
61
0
{
62
0
  FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self);
63
64
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
65
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
66
67
0
  if (efivars_class->supported == NULL) {
68
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported");
69
0
    return FALSE;
70
0
  }
71
0
  return efivars_class->supported(self, error);
72
0
}
73
74
/**
75
 * fu_efivars_delete:
76
 * @self: a #FuEfivars
77
 * @guid: Globally unique identifier
78
 * @name: Variable name
79
 * @error: #GError
80
 *
81
 * Removes a variable from NVRAM, returning an error if it does not exist.
82
 *
83
 * Returns: %TRUE on success
84
 *
85
 * Since: 2.0.0
86
 **/
87
gboolean
88
fu_efivars_delete(FuEfivars *self, const gchar *guid, const gchar *name, GError **error)
89
0
{
90
0
  FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self);
91
92
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
93
0
  g_return_val_if_fail(guid != NULL, FALSE);
94
0
  g_return_val_if_fail(name != NULL, FALSE);
95
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
96
97
0
  if (efivars_class->delete == NULL) {
98
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported");
99
0
    return FALSE;
100
0
  }
101
0
  return efivars_class->delete(self, guid, name, error);
102
0
}
103
104
/**
105
 * fu_efivars_delete_with_glob:
106
 * @self: a #FuEfivars
107
 * @guid: Globally unique identifier
108
 * @name_glob: Variable name
109
 * @error: #GError
110
 *
111
 * Removes a group of variables from NVRAM
112
 *
113
 * Returns: %TRUE on success
114
 *
115
 * Since: 2.0.0
116
 **/
117
gboolean
118
fu_efivars_delete_with_glob(FuEfivars *self,
119
          const gchar *guid,
120
          const gchar *name_glob,
121
          GError **error)
122
0
{
123
0
  FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self);
124
125
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
126
0
  g_return_val_if_fail(guid != NULL, FALSE);
127
0
  g_return_val_if_fail(name_glob != NULL, FALSE);
128
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
129
130
0
  if (efivars_class->delete_with_glob == NULL) {
131
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported");
132
0
    return FALSE;
133
0
  }
134
0
  return efivars_class->delete_with_glob(self, guid, name_glob, error);
135
0
}
136
137
/**
138
 * fu_efivars_exists:
139
 * @self: a #FuEfivars
140
 * @guid: Globally unique identifier
141
 * @name: (not nullable): Variable name
142
 *
143
 * Test if a variable exists
144
 *
145
 * Returns: %TRUE on success
146
 *
147
 * Since: 2.0.0
148
 **/
149
gboolean
150
fu_efivars_exists(FuEfivars *self, const gchar *guid, const gchar *name)
151
0
{
152
0
  FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self);
153
154
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
155
0
  g_return_val_if_fail(guid != NULL, FALSE);
156
0
  g_return_val_if_fail(name != NULL, FALSE);
157
158
0
  if (efivars_class->exists == NULL)
159
0
    return FALSE;
160
0
  return efivars_class->exists(self, guid, name);
161
0
}
162
163
/**
164
 * fu_efivars_get_data:
165
 * @self: a #FuEfivars
166
 * @guid: Globally unique identifier
167
 * @name: Variable name
168
 * @data: Data to set
169
 * @data_sz: size of data
170
 * @attr: (nullable) (out): #FuEfiVariableAttrs, e.g. %FU_EFI_VARIABLE_ATTR_NON_VOLATILE
171
 * @error: (nullable): optional return location for an error
172
 *
173
 * Gets the data from a UEFI variable in NVRAM
174
 *
175
 * Returns: %TRUE on success
176
 *
177
 * Since: 2.0.0
178
 **/
179
gboolean
180
fu_efivars_get_data(FuEfivars *self,
181
        const gchar *guid,
182
        const gchar *name,
183
        guint8 **data,
184
        gsize *data_sz,
185
        FuEfiVariableAttrs *attr,
186
        GError **error)
187
0
{
188
0
  FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self);
189
190
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
191
0
  g_return_val_if_fail(guid != NULL, FALSE);
192
0
  g_return_val_if_fail(name != NULL, FALSE);
193
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
194
195
0
  if (efivars_class->get_data == NULL) {
196
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported");
197
0
    return FALSE;
198
0
  }
199
0
  return efivars_class->get_data(self, guid, name, data, data_sz, attr, error);
200
0
}
201
202
/**
203
 * fu_efivars_get_attrs:
204
 * @self: a #FuEfivars
205
 * @guid: Globally unique identifier
206
 * @name: Variable name
207
 * @attrs: (nullable) (out): #FuEfiVariableAttrs, e.g. %FU_EFI_VARIABLE_ATTR_NON_VOLATILE
208
 * @error: (nullable): optional return location for an error
209
 *
210
 * Gets the attributes from a UEFI variable in NVRAM
211
 *
212
 * Returns: %TRUE on success
213
 *
214
 * Since: 2.1.1
215
 **/
216
gboolean
217
fu_efivars_get_attrs(FuEfivars *self,
218
         const gchar *guid,
219
         const gchar *name,
220
         FuEfiVariableAttrs *attrs,
221
         GError **error)
222
0
{
223
0
  FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self);
224
225
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
226
0
  g_return_val_if_fail(guid != NULL, FALSE);
227
0
  g_return_val_if_fail(name != NULL, FALSE);
228
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
229
230
0
  if (efivars_class->get_data == NULL) {
231
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported");
232
0
    return FALSE;
233
0
  }
234
0
  return efivars_class->get_data(self, guid, name, NULL, NULL, attrs, error);
235
0
}
236
237
/**
238
 * fu_efivars_get_data_bytes:
239
 * @self: a #FuEfivars
240
 * @guid: Globally unique identifier
241
 * @name: Variable name
242
 * @attr: (out): #FuEfiVariableAttrs, e.g. %FU_EFI_VARIABLE_ATTR_NON_VOLATILE
243
 * @error: (nullable): optional return location for an error
244
 *
245
 * Gets the data from a UEFI variable in NVRAM
246
 *
247
 * Returns: (transfer full): a #GBytes, or %NULL
248
 *
249
 * Since: 2.0.0
250
 **/
251
GBytes *
252
fu_efivars_get_data_bytes(FuEfivars *self,
253
        const gchar *guid,
254
        const gchar *name,
255
        FuEfiVariableAttrs *attr,
256
        GError **error)
257
0
{
258
0
  guint8 *data = NULL;
259
0
  gsize datasz = 0;
260
261
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
262
0
  g_return_val_if_fail(guid != NULL, NULL);
263
0
  g_return_val_if_fail(name != NULL, NULL);
264
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
265
266
0
  if (!fu_efivars_get_data(self, guid, name, &data, &datasz, attr, error))
267
0
    return NULL;
268
0
  return g_bytes_new_take(data, datasz);
269
0
}
270
271
/**
272
 * fu_efivars_get_names:
273
 * @self: a #FuEfivars
274
 * @guid: Globally unique identifier
275
 * @error: (nullable): optional return location for an error
276
 *
277
 * Gets the list of names where the GUID matches. An error is set if there are
278
 * no names matching the GUID.
279
 *
280
 * Returns: (transfer container) (element-type utf8): array of names
281
 *
282
 * Since: 2.0.0
283
 **/
284
GPtrArray *
285
fu_efivars_get_names(FuEfivars *self, const gchar *guid, GError **error)
286
0
{
287
0
  FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self);
288
289
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
290
0
  g_return_val_if_fail(guid != NULL, NULL);
291
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
292
293
0
  if (efivars_class->get_names == NULL) {
294
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported");
295
0
    return NULL;
296
0
  }
297
0
  return efivars_class->get_names(self, guid, error);
298
0
}
299
300
/**
301
 * fu_efivars_get_monitor:
302
 * @self: a #FuEfivars
303
 * @guid: Globally unique identifier
304
 * @name: Variable name
305
 * @error: (nullable): optional return location for an error
306
 *
307
 * Returns a file monitor for a specific key.
308
 *
309
 * Returns: (transfer full): a #GFileMonitor, or %NULL for an error
310
 *
311
 * Since: 2.0.0
312
 **/
313
GFileMonitor *
314
fu_efivars_get_monitor(FuEfivars *self, const gchar *guid, const gchar *name, GError **error)
315
0
{
316
0
  FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self);
317
318
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
319
0
  g_return_val_if_fail(guid != NULL, NULL);
320
0
  g_return_val_if_fail(name != NULL, NULL);
321
322
0
  if (efivars_class->get_monitor == NULL) {
323
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported");
324
0
    return NULL;
325
0
  }
326
0
  return efivars_class->get_monitor(self, guid, name, error);
327
0
}
328
329
/**
330
 * fu_efivars_space_used:
331
 * @self: a #FuEfivars
332
 * @error: (nullable): optional return location for an error
333
 *
334
 * Gets the total size used by all EFI variables. This may be less than the size reported by the
335
 * kernel as some (hopefully small) variables are hidden from userspace.
336
 *
337
 * Returns: total allocated size of all visible variables, or %G_MAXUINT64 on error
338
 *
339
 * Since: 2.0.0
340
 **/
341
guint64
342
fu_efivars_space_used(FuEfivars *self, GError **error)
343
0
{
344
0
  FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self);
345
346
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), G_MAXUINT64);
347
0
  g_return_val_if_fail(error == NULL || *error == NULL, G_MAXUINT64);
348
349
0
  if (efivars_class->space_used == NULL) {
350
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported");
351
0
    return G_MAXUINT64;
352
0
  }
353
0
  return efivars_class->space_used(self, error);
354
0
}
355
356
/**
357
 * fu_efivars_space_free:
358
 * @self: a #FuEfivars
359
 * @error: (nullable): optional return location for an error
360
 *
361
 * Gets the free size available for new EFI variables, as reported from QueryVariableInfo.
362
 *
363
 * Returns: free space in bytes, or %G_MAXUINT64 on error
364
 *
365
 * Since: 2.0.12
366
 **/
367
guint64
368
fu_efivars_space_free(FuEfivars *self, GError **error)
369
0
{
370
0
  FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self);
371
372
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), G_MAXUINT64);
373
0
  g_return_val_if_fail(error == NULL || *error == NULL, G_MAXUINT64);
374
375
0
  if (efivars_class->space_free == NULL) {
376
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported");
377
0
    return G_MAXUINT64;
378
0
  }
379
0
  return efivars_class->space_free(self, error);
380
0
}
381
382
/**
383
 * fu_efivars_set_data:
384
 * @self: a #FuEfivars
385
 * @guid: Globally unique identifier
386
 * @name: Variable name
387
 * @data: Data to set
388
 * @sz: size of @data
389
 * @attr: #FuEfiVariableAttrs, e.g. %FU_EFI_VARIABLE_ATTR_NON_VOLATILE
390
 * @error: (nullable): optional return location for an error
391
 *
392
 * Sets the data to a UEFI variable in NVRAM
393
 *
394
 * Returns: %TRUE on success
395
 *
396
 * Since: 2.0.0
397
 **/
398
gboolean
399
fu_efivars_set_data(FuEfivars *self,
400
        const gchar *guid,
401
        const gchar *name,
402
        const guint8 *data,
403
        gsize sz,
404
        FuEfiVariableAttrs attr,
405
        GError **error)
406
0
{
407
0
  FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self);
408
409
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
410
0
  g_return_val_if_fail(guid != NULL, FALSE);
411
0
  g_return_val_if_fail(name != NULL, FALSE);
412
0
  g_return_val_if_fail(data != NULL, FALSE);
413
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
414
415
0
  if (efivars_class->set_data == NULL) {
416
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported");
417
0
    return FALSE;
418
0
  }
419
0
  return efivars_class->set_data(self, guid, name, data, sz, attr, error);
420
0
}
421
422
/**
423
 * fu_efivars_set_data_bytes:
424
 * @self: a #FuEfivars
425
 * @guid: globally unique identifier
426
 * @name: variable name
427
 * @bytes: data blob
428
 * @attr: #FuEfiVariableAttrs, e.g. %FU_EFI_VARIABLE_ATTR_NON_VOLATILE
429
 * @error: (nullable): optional return location for an error
430
 *
431
 * Sets the data to a UEFI variable in NVRAM
432
 *
433
 * Returns: %TRUE on success
434
 *
435
 * Since: 2.0.0
436
 **/
437
gboolean
438
fu_efivars_set_data_bytes(FuEfivars *self,
439
        const gchar *guid,
440
        const gchar *name,
441
        GBytes *bytes,
442
        FuEfiVariableAttrs attr,
443
        GError **error)
444
0
{
445
0
  gsize bufsz = 0;
446
0
  const guint8 *buf;
447
448
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
449
0
  g_return_val_if_fail(guid != NULL, FALSE);
450
0
  g_return_val_if_fail(name != NULL, FALSE);
451
0
  g_return_val_if_fail(bytes != NULL, FALSE);
452
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
453
454
0
  buf = g_bytes_get_data(bytes, &bufsz);
455
0
  return fu_efivars_set_data(self, guid, name, buf, bufsz, attr, error);
456
0
}
457
458
/**
459
 * fu_efivars_get_secure_boot:
460
 * @self: a #FuEfivars
461
 * @enabled: (out): SecureBoot value
462
 * @error: (nullable): optional return location for an error
463
 *
464
 * Determines if secure boot was enabled
465
 *
466
 * Returns: %TRUE on success
467
 *
468
 * Since: 2.0.0
469
 **/
470
gboolean
471
fu_efivars_get_secure_boot(FuEfivars *self, gboolean *enabled, GError **error)
472
0
{
473
0
  gsize data_size = 0;
474
0
  g_autofree guint8 *data = NULL;
475
476
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
477
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
478
479
0
  if (!fu_efivars_get_data(self,
480
0
         FU_EFIVARS_GUID_EFI_GLOBAL,
481
0
         "SecureBoot",
482
0
         &data,
483
0
         &data_size,
484
0
         NULL,
485
0
         NULL)) {
486
0
    g_set_error_literal(error,
487
0
            FWUPD_ERROR,
488
0
            FWUPD_ERROR_NOT_SUPPORTED,
489
0
            "SecureBoot is not available");
490
0
    return FALSE;
491
0
  }
492
0
  if (data_size == 0) {
493
0
    g_set_error_literal(error,
494
0
            FWUPD_ERROR,
495
0
            FWUPD_ERROR_NOT_SUPPORTED,
496
0
            "SecureBoot variable was empty");
497
0
    return FALSE;
498
0
  }
499
500
  /* available, but not enabled */
501
0
  if (enabled != NULL)
502
0
    *enabled = (data[0] & 0x01) > 0;
503
504
  /* success */
505
0
  return TRUE;
506
0
}
507
508
/**
509
 * fu_efivars_set_secure_boot: (skip):
510
 **/
511
gboolean
512
fu_efivars_set_secure_boot(FuEfivars *self, gboolean enabled, GError **error)
513
0
{
514
0
  guint8 value = enabled ? 0x01 : 0x00;
515
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
516
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
517
0
  return fu_efivars_set_data(self,
518
0
           FU_EFIVARS_GUID_EFI_GLOBAL,
519
0
           "SecureBoot",
520
0
           &value,
521
0
           sizeof(value),
522
0
           FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS,
523
0
           error);
524
0
}
525
526
/**
527
 * fu_efivars_get_boot_next:
528
 * @self: a #FuEfivars
529
 * @idx: (out) (nullable): boot index, typically 0x0001
530
 * @error: #GError
531
 *
532
 * Gets the index of the `BootNext` variable.
533
 *
534
 * Returns: %TRUE on success
535
 *
536
 * Since: 2.0.0
537
 **/
538
gboolean
539
fu_efivars_get_boot_next(FuEfivars *self, guint16 *idx, GError **error)
540
0
{
541
0
  g_autofree guint8 *buf = NULL;
542
0
  gsize bufsz = 0;
543
544
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
545
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
546
547
0
  if (!fu_efivars_get_data(self,
548
0
         FU_EFIVARS_GUID_EFI_GLOBAL,
549
0
         "BootNext",
550
0
         &buf,
551
0
         &bufsz,
552
0
         NULL,
553
0
         error))
554
0
    return FALSE;
555
0
  if (bufsz != sizeof(guint16)) {
556
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid size");
557
0
    return FALSE;
558
0
  }
559
0
  if (idx != NULL)
560
0
    *idx = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
561
562
  /* success */
563
0
  return TRUE;
564
0
}
565
566
/**
567
 * fu_efivars_set_boot_next:
568
 * @self: a #FuEfivars
569
 * @idx: boot index, typically 0x0001
570
 * @error: #GError
571
 *
572
 * Sets the index of the `BootNext` variable.
573
 *
574
 * Returns: %TRUE on success
575
 *
576
 * Since: 2.0.0
577
 **/
578
gboolean
579
fu_efivars_set_boot_next(FuEfivars *self, guint16 idx, GError **error)
580
0
{
581
0
  guint8 buf[2] = {0};
582
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
583
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
584
0
  fu_memwrite_uint16(buf, idx, G_LITTLE_ENDIAN);
585
0
  return fu_efivars_set_data(self,
586
0
           FU_EFIVARS_GUID_EFI_GLOBAL,
587
0
           "BootNext",
588
0
           buf,
589
0
           sizeof(buf),
590
0
           FU_EFI_VARIABLE_ATTR_NON_VOLATILE |
591
0
               FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS |
592
0
               FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS,
593
0
           error);
594
0
}
595
596
/**
597
 * fu_efivars_get_boot_current:
598
 * @self: a #FuEfivars
599
 * @idx: (out): boot index, typically 0x0001
600
 * @error: #GError
601
 *
602
 * Gets the index of the `BootCurrent` variable.
603
 *
604
 * Returns: %TRUE on success
605
 *
606
 * Since: 2.0.0
607
 **/
608
gboolean
609
fu_efivars_get_boot_current(FuEfivars *self, guint16 *idx, GError **error)
610
0
{
611
0
  g_autofree guint8 *buf = NULL;
612
0
  gsize bufsz = 0;
613
614
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
615
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
616
617
0
  if (!fu_efivars_get_data(self,
618
0
         FU_EFIVARS_GUID_EFI_GLOBAL,
619
0
         "BootCurrent",
620
0
         &buf,
621
0
         &bufsz,
622
0
         NULL,
623
0
         error))
624
0
    return FALSE;
625
0
  if (bufsz != sizeof(guint16)) {
626
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid size");
627
0
    return FALSE;
628
0
  }
629
0
  if (idx != NULL)
630
0
    *idx = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
631
632
  /* success */
633
0
  return TRUE;
634
0
}
635
636
/**
637
 * fu_efivars_set_boot_current: (skip):
638
 **/
639
gboolean
640
fu_efivars_set_boot_current(FuEfivars *self, guint16 idx, GError **error)
641
0
{
642
0
  guint8 buf[2] = {0};
643
644
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
645
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
646
647
0
  fu_memwrite_uint16(buf, idx, G_LITTLE_ENDIAN);
648
0
  return fu_efivars_set_data(self,
649
0
           FU_EFIVARS_GUID_EFI_GLOBAL,
650
0
           "BootCurrent",
651
0
           buf,
652
0
           sizeof(buf),
653
0
           FU_EFI_VARIABLE_ATTR_NON_VOLATILE |
654
0
               FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS,
655
0
           error);
656
0
}
657
658
/**
659
 * fu_efivars_get_boot_order:
660
 * @self: a #FuEfivars
661
 * @error: #GError
662
 *
663
 * Gets the indexes of the `BootOrder` variable.
664
 *
665
 * Returns: (transfer full) (element-type guint16): boot order, or %NULL on error
666
 *
667
 * Since: 2.0.0
668
 **/
669
GArray *
670
fu_efivars_get_boot_order(FuEfivars *self, GError **error)
671
0
{
672
0
  gsize bufsz = 0;
673
0
  g_autofree guint8 *buf = NULL;
674
0
  g_autoptr(GArray) order = g_array_new(FALSE, FALSE, sizeof(guint16));
675
676
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
677
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
678
679
0
  if (!fu_efivars_get_data(self,
680
0
         FU_EFIVARS_GUID_EFI_GLOBAL,
681
0
         "BootOrder",
682
0
         &buf,
683
0
         &bufsz,
684
0
         NULL,
685
0
         error))
686
0
    return NULL;
687
0
  if (bufsz % sizeof(guint16) != 0) {
688
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid size");
689
0
    return NULL;
690
0
  }
691
0
  for (gsize i = 0; i < bufsz; i += sizeof(guint16)) {
692
0
    guint16 idx = fu_memread_uint16(buf + i, G_LITTLE_ENDIAN);
693
0
    g_array_append_val(order, idx);
694
0
  }
695
696
  /* success */
697
0
  return g_steal_pointer(&order);
698
0
}
699
700
/**
701
 * fu_efivars_set_boot_order:
702
 * @self: a #FuEfivars
703
 * @order: (element-type guint16): boot order
704
 * @error: #GError
705
 *
706
 * Sets the index of the `BootNext` variable.
707
 *
708
 * Returns: %TRUE on success
709
 *
710
 * Since: 2.0.0
711
 **/
712
gboolean
713
fu_efivars_set_boot_order(FuEfivars *self, GArray *order, GError **error)
714
0
{
715
0
  g_autoptr(GByteArray) buf = g_byte_array_new();
716
717
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
718
0
  g_return_val_if_fail(order != NULL, FALSE);
719
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
720
721
0
  for (guint i = 0; i < order->len; i++) {
722
0
    guint16 idx = g_array_index(order, guint16, i);
723
0
    fu_byte_array_append_uint16(buf, idx, G_LITTLE_ENDIAN);
724
0
  }
725
0
  return fu_efivars_set_data(self,
726
0
           FU_EFIVARS_GUID_EFI_GLOBAL,
727
0
           "BootOrder",
728
0
           buf->data,
729
0
           buf->len,
730
0
           FU_EFI_VARIABLE_ATTR_NON_VOLATILE |
731
0
               FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS |
732
0
               FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS,
733
0
           error);
734
0
}
735
736
/**
737
 * fu_efivars_build_boot_order: (skip)
738
 **/
739
gboolean
740
fu_efivars_build_boot_order(FuEfivars *self, GError **error, ...)
741
0
{
742
0
  va_list args;
743
0
  g_autoptr(GArray) order = g_array_new(FALSE, FALSE, sizeof(guint16));
744
745
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
746
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
747
748
0
  va_start(args, error);
749
0
  while (TRUE) {
750
0
    guint16 idx = va_arg(args, guint);
751
0
    if (idx == G_MAXUINT16)
752
0
      break;
753
0
    g_array_append_val(order, idx);
754
0
  }
755
0
  va_end(args);
756
757
  /* success */
758
0
  return fu_efivars_set_boot_order(self, order, error);
759
0
}
760
761
/**
762
 * fu_efivars_create_boot_entry_for_volume:
763
 * @self: a #FuEfivars
764
 * @idx: boot index, typically 0x0001
765
 * @volume: a #FuVolume
766
 * @name: a display name, e.g. "Fedora"
767
 * @target: an EFI binary, e.g. "shim.efi"
768
 * @error: #GError
769
 *
770
 * Creates a BootXXXX variable for a given volume, name and target.
771
 *
772
 * If @target does not exist on the volume then a dummy file is created.
773
 *
774
 * Returns: %TRUE on success
775
 *
776
 * Since: 2.0.6
777
 **/
778
gboolean
779
fu_efivars_create_boot_entry_for_volume(FuEfivars *self,
780
          guint16 idx,
781
          FuVolume *volume,
782
          const gchar *name,
783
          const gchar *target,
784
          GError **error)
785
0
{
786
0
  g_autoptr(FuEfiDevicePathList) devpath_list = fu_efi_device_path_list_new();
787
0
  g_autoptr(FuEfiFilePathDevicePath) dp_fp = NULL;
788
0
  g_autoptr(FuEfiHardDriveDevicePath) dp_hdd = NULL;
789
0
  g_autoptr(FuEfiLoadOption) entry = fu_efi_load_option_new();
790
0
  g_autoptr(GFile) file = NULL;
791
0
  g_autofree gchar *mount_point = NULL;
792
793
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
794
0
  g_return_val_if_fail(FU_IS_VOLUME(volume), FALSE);
795
0
  g_return_val_if_fail(name != NULL, FALSE);
796
0
  g_return_val_if_fail(target != NULL, FALSE);
797
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
798
799
  /* create plausible EFI file if not already exists */
800
0
  mount_point = fu_volume_get_mount_point(volume);
801
0
  if (mount_point == NULL) {
802
0
    g_set_error_literal(error,
803
0
            FWUPD_ERROR,
804
0
            FWUPD_ERROR_NOT_SUPPORTED,
805
0
            "volume has no mount point");
806
0
    return FALSE;
807
0
  }
808
0
  file = g_file_new_build_filename(mount_point, target, NULL);
809
0
  if (!g_file_query_exists(file, NULL)) {
810
0
    g_autoptr(FuFirmware) img_text = fu_firmware_new();
811
0
    g_autoptr(FuFirmware) pefile = fu_pefile_firmware_new();
812
0
    g_autoptr(GBytes) img_blob = g_bytes_new_static("hello", 5);
813
0
    fu_firmware_set_id(img_text, ".text");
814
0
    fu_firmware_set_bytes(img_text, img_blob);
815
0
    if (!fu_firmware_add_image(pefile, img_text, error))
816
0
      return FALSE;
817
0
    if (!fu_firmware_write_file(pefile, file, error))
818
0
      return FALSE;
819
0
  }
820
821
0
  dp_hdd = fu_efi_hard_drive_device_path_new_from_volume(volume, error);
822
0
  if (dp_hdd == NULL)
823
0
    return FALSE;
824
0
  dp_fp = fu_efi_file_path_device_path_new();
825
0
  if (!fu_efi_file_path_device_path_set_name(dp_fp, target, error))
826
0
    return FALSE;
827
0
  if (!fu_firmware_add_image(FU_FIRMWARE(devpath_list), FU_FIRMWARE(dp_hdd), error))
828
0
    return FALSE;
829
0
  if (!fu_firmware_add_image(FU_FIRMWARE(devpath_list), FU_FIRMWARE(dp_fp), error))
830
0
    return FALSE;
831
832
0
  fu_firmware_set_id(FU_FIRMWARE(entry), name);
833
0
  if (!fu_firmware_add_image(FU_FIRMWARE(entry), FU_FIRMWARE(devpath_list), error))
834
0
    return FALSE;
835
0
  return fu_efivars_set_boot_entry(self, idx, entry, error);
836
0
}
837
838
/**
839
 * fu_efivars_get_boot_data:
840
 * @self: a #FuEfivars
841
 * @idx: boot index, typically 0x0001
842
 * @error: #GError
843
 *
844
 * Gets the raw data of the `BootXXXX` variable.
845
 *
846
 * Returns: (transfer full): boot data
847
 *
848
 * Since: 2.0.0
849
 **/
850
GBytes *
851
fu_efivars_get_boot_data(FuEfivars *self, guint16 idx, GError **error)
852
0
{
853
0
  g_autofree gchar *name = g_strdup_printf("Boot%04X", idx);
854
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
855
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
856
0
  return fu_efivars_get_data_bytes(self, FU_EFIVARS_GUID_EFI_GLOBAL, name, NULL, error);
857
0
}
858
859
/**
860
 * fu_efivars_set_boot_data:
861
 * @self: a #FuEfivars
862
 * @idx: boot index, typically 0x0001
863
 * @blob: #GBytes
864
 * @error: #GError
865
 *
866
 * Sets the raw data of the `BootXXXX` variable. If @blob is %NULL then the boot entry is deleted.
867
 *
868
 * Returns: %TRUE for success
869
 *
870
 * Since: 2.0.0
871
 **/
872
gboolean
873
fu_efivars_set_boot_data(FuEfivars *self, guint16 idx, GBytes *blob, GError **error)
874
0
{
875
0
  g_autofree gchar *name = g_strdup_printf("Boot%04X", idx);
876
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
877
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
878
879
0
  if (blob == NULL)
880
0
    return fu_efivars_delete(self, FU_EFIVARS_GUID_EFI_GLOBAL, name, error);
881
0
  return fu_efivars_set_data_bytes(self,
882
0
           FU_EFIVARS_GUID_EFI_GLOBAL,
883
0
           name,
884
0
           blob,
885
0
           FU_EFI_VARIABLE_ATTR_NON_VOLATILE |
886
0
               FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS |
887
0
               FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS,
888
0
           error);
889
0
}
890
891
/**
892
 * fu_efivars_get_boot_entry:
893
 * @self: a #FuEfivars
894
 * @idx: boot index, typically 0x0001
895
 * @error: #GError
896
 *
897
 * Gets the loadopt data of the `BootXXXX` variable.
898
 *
899
 * Returns: (transfer full): a #FuEfiLoadOption, or %NULL
900
 *
901
 * Since: 2.0.0
902
 **/
903
FuEfiLoadOption *
904
fu_efivars_get_boot_entry(FuEfivars *self, guint16 idx, GError **error)
905
0
{
906
0
  g_autofree gchar *name = g_strdup_printf("Boot%04X", idx);
907
0
  g_autoptr(FuEfiLoadOption) loadopt = fu_efi_load_option_new();
908
0
  g_autoptr(GBytes) blob = NULL;
909
910
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
911
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
912
913
  /* get data */
914
0
  blob = fu_efivars_get_data_bytes(self, FU_EFIVARS_GUID_EFI_GLOBAL, name, NULL, error);
915
0
  if (blob == NULL)
916
0
    return NULL;
917
0
  if (!fu_firmware_parse_bytes(FU_FIRMWARE(loadopt),
918
0
             blob,
919
0
             0x0,
920
0
             FU_FIRMWARE_PARSE_FLAG_NONE,
921
0
             error))
922
0
    return NULL;
923
0
  fu_firmware_set_idx(FU_FIRMWARE(loadopt), idx);
924
0
  return g_steal_pointer(&loadopt);
925
0
}
926
927
/**
928
 * fu_efivars_set_boot_entry:
929
 * @self: a #FuEfivars
930
 * @idx: boot index, typically 0x0001
931
 * @entry: a #FuEfiLoadOption
932
 * @error: #GError
933
 *
934
 * Sets the loadopt data of the `BootXXXX` variable.
935
 *
936
 * Returns: %TRUE for success
937
 *
938
 * Since: 2.0.0
939
 **/
940
gboolean
941
fu_efivars_set_boot_entry(FuEfivars *self, guint16 idx, FuEfiLoadOption *entry, GError **error)
942
0
{
943
0
  g_autoptr(GBytes) blob = NULL;
944
945
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
946
0
  g_return_val_if_fail(FU_IS_EFI_LOAD_OPTION(entry), FALSE);
947
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
948
949
0
  blob = fu_firmware_write(FU_FIRMWARE(entry), error);
950
0
  if (blob == NULL)
951
0
    return FALSE;
952
0
  return fu_efivars_set_boot_data(self, idx, blob, error);
953
0
}
954
955
/**
956
 * fu_efivars_get_boot_entries:
957
 * @self: a #FuEfivars
958
 * @error: #GError
959
 *
960
 * Gets the loadopt data for all the entries listed in `BootOrder`.
961
 *
962
 * Returns: (transfer full) (element-type FuEfiLoadOption): boot data
963
 *
964
 * Since: 2.0.0
965
 **/
966
GPtrArray *
967
fu_efivars_get_boot_entries(FuEfivars *self, GError **error)
968
0
{
969
0
  g_autoptr(GArray) order = NULL;
970
0
  g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
971
972
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
973
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
974
975
0
  order = fu_efivars_get_boot_order(self, error);
976
0
  if (order == NULL)
977
0
    return NULL;
978
0
  for (guint i = 0; i < order->len; i++) {
979
0
    guint16 idx = g_array_index(order, guint16, i);
980
0
    g_autoptr(FuEfiLoadOption) loadopt = NULL;
981
982
0
    loadopt = fu_efivars_get_boot_entry(self, idx, error);
983
0
    if (loadopt == NULL) {
984
0
      g_prefix_error(error, "failed to load Boot%04X: ", idx);
985
0
      return NULL;
986
0
    }
987
0
    g_ptr_array_add(array, g_steal_pointer(&loadopt));
988
0
  }
989
990
  /* success */
991
0
  return g_steal_pointer(&array);
992
0
}
993
994
static void
995
fu_efivars_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
996
0
{
997
0
  FuEfivars *self = FU_EFIVARS(object);
998
0
  FuEfivarsPrivate *priv = GET_PRIVATE(self);
999
0
  switch (prop_id) {
1000
0
  case PROP_PATH_STORE:
1001
0
    g_value_set_object(value, priv->pstore);
1002
0
    break;
1003
0
  default:
1004
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1005
0
    break;
1006
0
  }
1007
0
}
1008
1009
static void
1010
fu_efivars_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1011
0
{
1012
0
  FuEfivars *self = FU_EFIVARS(object);
1013
0
  FuEfivarsPrivate *priv = GET_PRIVATE(self);
1014
0
  switch (prop_id) {
1015
0
  case PROP_PATH_STORE:
1016
0
    g_set_object(&priv->pstore, g_value_get_object(value));
1017
0
    break;
1018
0
  default:
1019
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1020
0
    break;
1021
0
  }
1022
0
}
1023
1024
static void
1025
fu_efivars_init(FuEfivars *self)
1026
0
{
1027
0
}
1028
1029
static void
1030
fu_efivars_finalize(GObject *object)
1031
0
{
1032
0
  FuEfivars *self = FU_EFIVARS(object);
1033
0
  FuEfivarsPrivate *priv = GET_PRIVATE(self);
1034
1035
0
  if (priv->pstore != NULL)
1036
0
    g_object_unref(priv->pstore);
1037
1038
0
  G_OBJECT_CLASS(fu_efivars_parent_class)->finalize(object);
1039
0
}
1040
1041
static void
1042
fu_efivars_class_init(FuEfivarsClass *klass)
1043
0
{
1044
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
1045
0
  GParamSpec *pspec;
1046
1047
0
  object_class->get_property = fu_efivars_get_property;
1048
0
  object_class->set_property = fu_efivars_set_property;
1049
0
  object_class->finalize = fu_efivars_finalize;
1050
1051
0
  pspec = g_param_spec_object("path-store",
1052
0
            NULL,
1053
0
            NULL,
1054
0
            FU_TYPE_PATH_STORE,
1055
0
            G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME);
1056
0
  g_object_class_install_property(object_class, PROP_PATH_STORE, pspec);
1057
0
}