Coverage Report

Created: 2026-10-28 07:47

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
 * @state: (out) (nullable): the SecureBoot state
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, FuEfiSecureBootState *state, GError **error)
472
0
{
473
0
  gsize secure_boot_size = 0;
474
0
  gsize setup_mode_size = 0;
475
0
  FuEfiSecureBootState state_tmp = FU_EFI_SECURE_BOOT_STATE_DISABLED;
476
0
  g_autofree guint8 *secure_boot = NULL;
477
0
  g_autofree guint8 *setup_mode = NULL;
478
479
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
480
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
481
482
  /* available, but not enabled */
483
0
  if (!fu_efivars_get_data(self,
484
0
         FU_EFIVARS_GUID_EFI_GLOBAL,
485
0
         "SecureBoot",
486
0
         &secure_boot,
487
0
         &secure_boot_size,
488
0
         NULL,
489
0
         NULL)) {
490
0
    g_set_error_literal(error,
491
0
            FWUPD_ERROR,
492
0
            FWUPD_ERROR_NOT_SUPPORTED,
493
0
            "SecureBoot is not available");
494
0
    return FALSE;
495
0
  }
496
0
  if (secure_boot_size == 0) {
497
0
    g_set_error_literal(error,
498
0
            FWUPD_ERROR,
499
0
            FWUPD_ERROR_NOT_SUPPORTED,
500
0
            "SecureBoot variable was empty");
501
0
    return FALSE;
502
0
  }
503
0
  if (secure_boot != NULL) {
504
0
    if ((secure_boot[0] & 0x01) > 0)
505
0
      state_tmp |= FU_EFI_SECURE_BOOT_STATE_ENABLED;
506
0
  }
507
508
  /* available, but not enabled */
509
0
  if (!fu_efivars_get_data(self,
510
0
         FU_EFIVARS_GUID_EFI_GLOBAL,
511
0
         "SetupMode",
512
0
         &setup_mode,
513
0
         &setup_mode_size,
514
0
         NULL,
515
0
         NULL)) {
516
0
    g_set_error_literal(error,
517
0
            FWUPD_ERROR,
518
0
            FWUPD_ERROR_NOT_SUPPORTED,
519
0
            "SetupMode is not available");
520
0
    return FALSE;
521
0
  }
522
0
  if (setup_mode_size == 0) {
523
0
    g_set_error_literal(error,
524
0
            FWUPD_ERROR,
525
0
            FWUPD_ERROR_NOT_SUPPORTED,
526
0
            "SetupMode variable was empty");
527
0
    return FALSE;
528
0
  }
529
0
  if (setup_mode != NULL) {
530
0
    if ((setup_mode[0] & 0x01) > 0)
531
0
      state_tmp |= FU_EFI_SECURE_BOOT_STATE_IN_SETUP;
532
0
  }
533
534
  /* success */
535
0
  if (state != NULL)
536
0
    *state = state_tmp;
537
0
  return TRUE;
538
0
}
539
540
/**
541
 * fu_efivars_set_secure_boot: (skip):
542
 **/
543
gboolean
544
fu_efivars_set_secure_boot(FuEfivars *self, gboolean enabled, GError **error)
545
0
{
546
0
  guint8 value = enabled ? 0x01 : 0x00;
547
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
548
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
549
0
  return fu_efivars_set_data(self,
550
0
           FU_EFIVARS_GUID_EFI_GLOBAL,
551
0
           "SecureBoot",
552
0
           &value,
553
0
           sizeof(value),
554
0
           FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS,
555
0
           error);
556
0
}
557
558
/**
559
 * fu_efivars_get_boot_next:
560
 * @self: a #FuEfivars
561
 * @idx: (out) (nullable): boot index, typically 0x0001
562
 * @error: #GError
563
 *
564
 * Gets the index of the `BootNext` variable.
565
 *
566
 * Returns: %TRUE on success
567
 *
568
 * Since: 2.0.0
569
 **/
570
gboolean
571
fu_efivars_get_boot_next(FuEfivars *self, guint16 *idx, GError **error)
572
0
{
573
0
  g_autofree guint8 *buf = NULL;
574
0
  gsize bufsz = 0;
575
576
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
577
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
578
579
0
  if (!fu_efivars_get_data(self,
580
0
         FU_EFIVARS_GUID_EFI_GLOBAL,
581
0
         "BootNext",
582
0
         &buf,
583
0
         &bufsz,
584
0
         NULL,
585
0
         error))
586
0
    return FALSE;
587
0
  if (bufsz != sizeof(guint16)) {
588
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid size");
589
0
    return FALSE;
590
0
  }
591
0
  if (idx != NULL)
592
0
    *idx = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
593
594
  /* success */
595
0
  return TRUE;
596
0
}
597
598
/**
599
 * fu_efivars_set_boot_next:
600
 * @self: a #FuEfivars
601
 * @idx: boot index, typically 0x0001
602
 * @error: #GError
603
 *
604
 * Sets the index of the `BootNext` variable.
605
 *
606
 * Returns: %TRUE on success
607
 *
608
 * Since: 2.0.0
609
 **/
610
gboolean
611
fu_efivars_set_boot_next(FuEfivars *self, guint16 idx, GError **error)
612
0
{
613
0
  guint8 buf[2] = {0};
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
0
  fu_memwrite_uint16(buf, idx, G_LITTLE_ENDIAN);
617
0
  return fu_efivars_set_data(self,
618
0
           FU_EFIVARS_GUID_EFI_GLOBAL,
619
0
           "BootNext",
620
0
           buf,
621
0
           sizeof(buf),
622
0
           FU_EFI_VARIABLE_ATTR_NON_VOLATILE |
623
0
               FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS |
624
0
               FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS,
625
0
           error);
626
0
}
627
628
/**
629
 * fu_efivars_get_boot_current:
630
 * @self: a #FuEfivars
631
 * @idx: (out): boot index, typically 0x0001
632
 * @error: #GError
633
 *
634
 * Gets the index of the `BootCurrent` variable.
635
 *
636
 * Returns: %TRUE on success
637
 *
638
 * Since: 2.0.0
639
 **/
640
gboolean
641
fu_efivars_get_boot_current(FuEfivars *self, guint16 *idx, GError **error)
642
0
{
643
0
  g_autofree guint8 *buf = NULL;
644
0
  gsize bufsz = 0;
645
646
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
647
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
648
649
0
  if (!fu_efivars_get_data(self,
650
0
         FU_EFIVARS_GUID_EFI_GLOBAL,
651
0
         "BootCurrent",
652
0
         &buf,
653
0
         &bufsz,
654
0
         NULL,
655
0
         error))
656
0
    return FALSE;
657
0
  if (bufsz != sizeof(guint16)) {
658
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid size");
659
0
    return FALSE;
660
0
  }
661
0
  if (idx != NULL)
662
0
    *idx = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
663
664
  /* success */
665
0
  return TRUE;
666
0
}
667
668
/**
669
 * fu_efivars_set_boot_current: (skip):
670
 **/
671
gboolean
672
fu_efivars_set_boot_current(FuEfivars *self, guint16 idx, GError **error)
673
0
{
674
0
  guint8 buf[2] = {0};
675
676
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
677
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
678
679
0
  fu_memwrite_uint16(buf, idx, G_LITTLE_ENDIAN);
680
0
  return fu_efivars_set_data(self,
681
0
           FU_EFIVARS_GUID_EFI_GLOBAL,
682
0
           "BootCurrent",
683
0
           buf,
684
0
           sizeof(buf),
685
0
           FU_EFI_VARIABLE_ATTR_NON_VOLATILE |
686
0
               FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS,
687
0
           error);
688
0
}
689
690
/**
691
 * fu_efivars_get_boot_order:
692
 * @self: a #FuEfivars
693
 * @error: #GError
694
 *
695
 * Gets the indexes of the `BootOrder` variable.
696
 *
697
 * Returns: (transfer full) (element-type guint16): boot order, or %NULL on error
698
 *
699
 * Since: 2.0.0
700
 **/
701
GArray *
702
fu_efivars_get_boot_order(FuEfivars *self, GError **error)
703
0
{
704
0
  gsize bufsz = 0;
705
0
  g_autofree guint8 *buf = NULL;
706
0
  g_autoptr(GArray) order = g_array_new(FALSE, FALSE, sizeof(guint16));
707
708
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
709
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
710
711
0
  if (!fu_efivars_get_data(self,
712
0
         FU_EFIVARS_GUID_EFI_GLOBAL,
713
0
         "BootOrder",
714
0
         &buf,
715
0
         &bufsz,
716
0
         NULL,
717
0
         error))
718
0
    return NULL;
719
0
  if (bufsz % sizeof(guint16) != 0) {
720
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid size");
721
0
    return NULL;
722
0
  }
723
0
  for (gsize i = 0; i < bufsz; i += sizeof(guint16)) {
724
0
    guint16 idx = fu_memread_uint16(buf + i, G_LITTLE_ENDIAN);
725
0
    g_array_append_val(order, idx);
726
0
  }
727
728
  /* success */
729
0
  return g_steal_pointer(&order);
730
0
}
731
732
/**
733
 * fu_efivars_set_boot_order:
734
 * @self: a #FuEfivars
735
 * @order: (element-type guint16): boot order
736
 * @error: #GError
737
 *
738
 * Sets the index of the `BootNext` variable.
739
 *
740
 * Returns: %TRUE on success
741
 *
742
 * Since: 2.0.0
743
 **/
744
gboolean
745
fu_efivars_set_boot_order(FuEfivars *self, GArray *order, GError **error)
746
0
{
747
0
  g_autoptr(GByteArray) buf = g_byte_array_new();
748
749
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
750
0
  g_return_val_if_fail(order != NULL, FALSE);
751
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
752
753
0
  for (guint i = 0; i < order->len; i++) {
754
0
    guint16 idx = g_array_index(order, guint16, i);
755
0
    fu_byte_array_append_uint16(buf, idx, G_LITTLE_ENDIAN);
756
0
  }
757
0
  return fu_efivars_set_data(self,
758
0
           FU_EFIVARS_GUID_EFI_GLOBAL,
759
0
           "BootOrder",
760
0
           buf->data,
761
0
           buf->len,
762
0
           FU_EFI_VARIABLE_ATTR_NON_VOLATILE |
763
0
               FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS |
764
0
               FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS,
765
0
           error);
766
0
}
767
768
/**
769
 * fu_efivars_build_boot_order: (skip)
770
 **/
771
gboolean
772
fu_efivars_build_boot_order(FuEfivars *self, GError **error, ...)
773
0
{
774
0
  va_list args;
775
0
  g_autoptr(GArray) order = g_array_new(FALSE, FALSE, sizeof(guint16));
776
777
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
778
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
779
780
0
  va_start(args, error);
781
0
  while (TRUE) {
782
0
    guint16 idx = va_arg(args, guint);
783
0
    if (idx == G_MAXUINT16)
784
0
      break;
785
0
    g_array_append_val(order, idx);
786
0
  }
787
0
  va_end(args);
788
789
  /* success */
790
0
  return fu_efivars_set_boot_order(self, order, error);
791
0
}
792
793
/**
794
 * fu_efivars_create_boot_entry_for_volume:
795
 * @self: a #FuEfivars
796
 * @idx: boot index, typically 0x0001
797
 * @volume: a #FuVolume
798
 * @name: a display name, e.g. "Fedora"
799
 * @target: an EFI binary, e.g. "shim.efi"
800
 * @error: #GError
801
 *
802
 * Creates a BootXXXX variable for a given volume, name and target.
803
 *
804
 * If @target does not exist on the volume then a dummy file is created.
805
 *
806
 * Returns: %TRUE on success
807
 *
808
 * Since: 2.0.6
809
 **/
810
gboolean
811
fu_efivars_create_boot_entry_for_volume(FuEfivars *self,
812
          guint16 idx,
813
          FuVolume *volume,
814
          const gchar *name,
815
          const gchar *target,
816
          GError **error)
817
0
{
818
0
  g_autoptr(FuEfiDevicePathList) devpath_list = fu_efi_device_path_list_new();
819
0
  g_autoptr(FuEfiFilePathDevicePath) dp_fp = NULL;
820
0
  g_autoptr(FuEfiHardDriveDevicePath) dp_hdd = NULL;
821
0
  g_autoptr(FuEfiLoadOption) entry = fu_efi_load_option_new();
822
0
  g_autoptr(GFile) file = NULL;
823
0
  g_autofree gchar *mount_point = NULL;
824
825
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
826
0
  g_return_val_if_fail(FU_IS_VOLUME(volume), FALSE);
827
0
  g_return_val_if_fail(name != NULL, FALSE);
828
0
  g_return_val_if_fail(target != NULL, FALSE);
829
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
830
831
  /* create plausible EFI file if not already exists */
832
0
  mount_point = fu_volume_get_mount_point(volume);
833
0
  if (mount_point == NULL) {
834
0
    g_set_error_literal(error,
835
0
            FWUPD_ERROR,
836
0
            FWUPD_ERROR_NOT_SUPPORTED,
837
0
            "volume has no mount point");
838
0
    return FALSE;
839
0
  }
840
0
  file = g_file_new_build_filename(mount_point, target, NULL);
841
0
  if (!g_file_query_exists(file, NULL)) {
842
0
    g_autoptr(FuFirmware) img_text = fu_firmware_new();
843
0
    g_autoptr(FuFirmware) pefile = fu_pefile_firmware_new();
844
0
    g_autoptr(GBytes) img_blob = g_bytes_new_static("hello", 5);
845
0
    fu_firmware_set_id(img_text, ".text");
846
0
    fu_firmware_set_bytes(img_text, img_blob);
847
0
    if (!fu_firmware_add_image(pefile, img_text, error))
848
0
      return FALSE;
849
0
    if (!fu_firmware_write_file(pefile, file, error))
850
0
      return FALSE;
851
0
  }
852
853
0
  dp_hdd = fu_efi_hard_drive_device_path_new_from_volume(volume, error);
854
0
  if (dp_hdd == NULL)
855
0
    return FALSE;
856
0
  dp_fp = fu_efi_file_path_device_path_new();
857
0
  if (!fu_efi_file_path_device_path_set_name(dp_fp, target, error))
858
0
    return FALSE;
859
0
  if (!fu_firmware_add_image(FU_FIRMWARE(devpath_list), FU_FIRMWARE(dp_hdd), error))
860
0
    return FALSE;
861
0
  if (!fu_firmware_add_image(FU_FIRMWARE(devpath_list), FU_FIRMWARE(dp_fp), error))
862
0
    return FALSE;
863
864
0
  fu_firmware_set_id(FU_FIRMWARE(entry), name);
865
0
  if (!fu_firmware_add_image(FU_FIRMWARE(entry), FU_FIRMWARE(devpath_list), error))
866
0
    return FALSE;
867
0
  return fu_efivars_set_boot_entry(self, idx, entry, error);
868
0
}
869
870
/**
871
 * fu_efivars_get_boot_data:
872
 * @self: a #FuEfivars
873
 * @idx: boot index, typically 0x0001
874
 * @error: #GError
875
 *
876
 * Gets the raw data of the `BootXXXX` variable.
877
 *
878
 * Returns: (transfer full): boot data
879
 *
880
 * Since: 2.0.0
881
 **/
882
GBytes *
883
fu_efivars_get_boot_data(FuEfivars *self, guint16 idx, GError **error)
884
0
{
885
0
  g_autofree gchar *name = g_strdup_printf("Boot%04X", idx);
886
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
887
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
888
0
  return fu_efivars_get_data_bytes(self, FU_EFIVARS_GUID_EFI_GLOBAL, name, NULL, error);
889
0
}
890
891
/**
892
 * fu_efivars_set_boot_data:
893
 * @self: a #FuEfivars
894
 * @idx: boot index, typically 0x0001
895
 * @blob: #GBytes
896
 * @error: #GError
897
 *
898
 * Sets the raw data of the `BootXXXX` variable. If @blob is %NULL then the boot entry is deleted.
899
 *
900
 * Returns: %TRUE for success
901
 *
902
 * Since: 2.0.0
903
 **/
904
gboolean
905
fu_efivars_set_boot_data(FuEfivars *self, guint16 idx, GBytes *blob, GError **error)
906
0
{
907
0
  g_autofree gchar *name = g_strdup_printf("Boot%04X", idx);
908
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
909
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
910
911
0
  if (blob == NULL)
912
0
    return fu_efivars_delete(self, FU_EFIVARS_GUID_EFI_GLOBAL, name, error);
913
0
  return fu_efivars_set_data_bytes(self,
914
0
           FU_EFIVARS_GUID_EFI_GLOBAL,
915
0
           name,
916
0
           blob,
917
0
           FU_EFI_VARIABLE_ATTR_NON_VOLATILE |
918
0
               FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS |
919
0
               FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS,
920
0
           error);
921
0
}
922
923
/**
924
 * fu_efivars_get_boot_entry:
925
 * @self: a #FuEfivars
926
 * @idx: boot index, typically 0x0001
927
 * @error: #GError
928
 *
929
 * Gets the loadopt data of the `BootXXXX` variable.
930
 *
931
 * Returns: (transfer full): a #FuEfiLoadOption, or %NULL
932
 *
933
 * Since: 2.0.0
934
 **/
935
FuEfiLoadOption *
936
fu_efivars_get_boot_entry(FuEfivars *self, guint16 idx, GError **error)
937
0
{
938
0
  g_autofree gchar *name = g_strdup_printf("Boot%04X", idx);
939
0
  g_autoptr(FuEfiLoadOption) loadopt = fu_efi_load_option_new();
940
0
  g_autoptr(GBytes) blob = NULL;
941
942
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
943
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
944
945
  /* get data */
946
0
  blob = fu_efivars_get_data_bytes(self, FU_EFIVARS_GUID_EFI_GLOBAL, name, NULL, error);
947
0
  if (blob == NULL)
948
0
    return NULL;
949
0
  if (!fu_firmware_parse_bytes(FU_FIRMWARE(loadopt),
950
0
             blob,
951
0
             0x0,
952
0
             FU_FIRMWARE_PARSE_FLAG_NONE,
953
0
             error))
954
0
    return NULL;
955
0
  fu_firmware_set_idx(FU_FIRMWARE(loadopt), idx);
956
0
  return g_steal_pointer(&loadopt);
957
0
}
958
959
/**
960
 * fu_efivars_set_boot_entry:
961
 * @self: a #FuEfivars
962
 * @idx: boot index, typically 0x0001
963
 * @entry: a #FuEfiLoadOption
964
 * @error: #GError
965
 *
966
 * Sets the loadopt data of the `BootXXXX` variable.
967
 *
968
 * Returns: %TRUE for success
969
 *
970
 * Since: 2.0.0
971
 **/
972
gboolean
973
fu_efivars_set_boot_entry(FuEfivars *self, guint16 idx, FuEfiLoadOption *entry, GError **error)
974
0
{
975
0
  g_autoptr(GBytes) blob = NULL;
976
977
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), FALSE);
978
0
  g_return_val_if_fail(FU_IS_EFI_LOAD_OPTION(entry), FALSE);
979
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
980
981
0
  blob = fu_firmware_write(FU_FIRMWARE(entry), error);
982
0
  if (blob == NULL)
983
0
    return FALSE;
984
0
  return fu_efivars_set_boot_data(self, idx, blob, error);
985
0
}
986
987
/**
988
 * fu_efivars_get_boot_entries:
989
 * @self: a #FuEfivars
990
 * @error: #GError
991
 *
992
 * Gets the loadopt data for all the entries listed in `BootOrder`.
993
 *
994
 * Returns: (transfer full) (element-type FuEfiLoadOption): boot data
995
 *
996
 * Since: 2.0.0
997
 **/
998
GPtrArray *
999
fu_efivars_get_boot_entries(FuEfivars *self, GError **error)
1000
0
{
1001
0
  g_autoptr(GArray) order = NULL;
1002
0
  g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
1003
1004
0
  g_return_val_if_fail(FU_IS_EFIVARS(self), NULL);
1005
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1006
1007
0
  order = fu_efivars_get_boot_order(self, error);
1008
0
  if (order == NULL)
1009
0
    return NULL;
1010
0
  for (guint i = 0; i < order->len; i++) {
1011
0
    guint16 idx = g_array_index(order, guint16, i);
1012
0
    g_autoptr(FuEfiLoadOption) loadopt = NULL;
1013
1014
0
    loadopt = fu_efivars_get_boot_entry(self, idx, error);
1015
0
    if (loadopt == NULL) {
1016
0
      g_prefix_error(error, "failed to load Boot%04X: ", idx);
1017
0
      return NULL;
1018
0
    }
1019
0
    g_ptr_array_add(array, g_steal_pointer(&loadopt));
1020
0
  }
1021
1022
  /* success */
1023
0
  return g_steal_pointer(&array);
1024
0
}
1025
1026
static void
1027
fu_efivars_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1028
0
{
1029
0
  FuEfivars *self = FU_EFIVARS(object);
1030
0
  FuEfivarsPrivate *priv = GET_PRIVATE(self);
1031
0
  switch (prop_id) {
1032
0
  case PROP_PATH_STORE:
1033
0
    g_value_set_object(value, priv->pstore);
1034
0
    break;
1035
0
  default:
1036
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1037
0
    break;
1038
0
  }
1039
0
}
1040
1041
static void
1042
fu_efivars_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1043
0
{
1044
0
  FuEfivars *self = FU_EFIVARS(object);
1045
0
  FuEfivarsPrivate *priv = GET_PRIVATE(self);
1046
0
  switch (prop_id) {
1047
0
  case PROP_PATH_STORE:
1048
0
    g_set_object(&priv->pstore, g_value_get_object(value));
1049
0
    break;
1050
0
  default:
1051
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1052
0
    break;
1053
0
  }
1054
0
}
1055
1056
static void
1057
fu_efivars_init(FuEfivars *self)
1058
0
{
1059
0
}
1060
1061
static void
1062
fu_efivars_finalize(GObject *object)
1063
0
{
1064
0
  FuEfivars *self = FU_EFIVARS(object);
1065
0
  FuEfivarsPrivate *priv = GET_PRIVATE(self);
1066
1067
0
  if (priv->pstore != NULL)
1068
0
    g_object_unref(priv->pstore);
1069
1070
0
  G_OBJECT_CLASS(fu_efivars_parent_class)->finalize(object);
1071
0
}
1072
1073
static void
1074
fu_efivars_class_init(FuEfivarsClass *klass)
1075
0
{
1076
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
1077
0
  GParamSpec *pspec;
1078
1079
0
  object_class->get_property = fu_efivars_get_property;
1080
0
  object_class->set_property = fu_efivars_set_property;
1081
0
  object_class->finalize = fu_efivars_finalize;
1082
1083
0
  pspec = g_param_spec_object("path-store",
1084
0
            NULL,
1085
0
            NULL,
1086
0
            FU_TYPE_PATH_STORE,
1087
0
            G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME);
1088
0
  g_object_class_install_property(object_class, PROP_PATH_STORE, pspec);
1089
0
}