Coverage Report

Created: 2026-04-09 06:28

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