Coverage Report

Created: 2025-08-24 07:10

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