blob: a1121631e8cfbfb4a53bbbdb1d23742dcd288bbd [file] [log] [blame]
Lorenz Brun509c7092024-09-09 17:49:42 +02001package efivarfs
2
3import (
4 "encoding/binary"
5 "errors"
6 "fmt"
7 "os"
8 "sync"
9)
10
11// OSIndications is a bitset used to indicate firmware support for various
12// features as well as to trigger some of these features.
13// If a constant ends in Supported, it cannot be triggered, the others
14// can be.
15type OSIndications uint64
16
17const (
18 // Indicates that on next boot firmware should boot to a firmware-provided
19 // UI instead of the normal boot order.
20 BootToFirmwareUI = OSIndications(1 << iota)
21 // Indicates that firmware supports timestamp-based revocation and the
22 // "dbt" authorized timestamp database variable.
23 TimestampRevocationSupported
24 // Indicates that on next boot firmware should look for an EFI update
25 // capsule on an EFI system partition and try to install it.
26 FileCapsuleDelivery
27 // Indicates that firmware supports UEFI FMP update capsules.
28 FirmwareManagementProtocolCapsuleSupported
29 // Indicates that firmware supports reporting results of deferred (i.e.
30 // processed on next boot) capsule installs via variables.
31 CapsuleResultVarSupported
32 // Indicates that firmware should skip Boot# processing on next boot
33 // and instead use OsRecovery# for selecting a load option.
34 StartOSRecovery
35 // Indicates that firmware should skip Boot# processing on next boot
36 // and instead use PlatformRecovery# for selecting a load option.
37 StartPlatformRecovery
38 // Indicates that firmware should collect the current config and report
39 // the data to the EFI system configuration table on next boot.
40 JSONConfigDataRefresh
41)
42
43// osIndicationMutex protects against race conditions in read-modify-write
44// sequences on the OsIndications EFI variable.
45var osIndicationsMutex sync.Mutex
46
47// OSIndicationsSupported indicates which of the OS indication features and
48// actions that the firmware supports.
49func OSIndicationsSupported() (OSIndications, error) {
50 osIndicationsRaw, _, err := Read(ScopeGlobal, "OsIndicationsSupported")
51 if err != nil {
52 return 0, fmt.Errorf("unable to read OsIndicationsSupported: %w", err)
53 }
54 if len(osIndicationsRaw) != 8 {
55 return 0, fmt.Errorf("value of OsIndicationsSupported is not 8 bytes / 64 bits, is %d bytes", len(osIndicationsRaw))
56 }
57 return OSIndications(binary.LittleEndian.Uint64(osIndicationsRaw)), nil
58}
59
60// SetOSIndications sets all OS indication bits set in i in firmware. It does
61// not clear any already-set bits, use ClearOSIndications for that.
62func SetOSIndications(i OSIndications) error {
63 return modifyOSIndications(func(prev OSIndications) OSIndications {
64 return prev | i
65 })
66}
67
68// ClearOSIndications clears all OS indication bits set in i in firmware.
69// Note that this effectively inverts i, bits set in i will be cleared.
70func ClearOSIndications(i OSIndications) error {
71 return modifyOSIndications(func(prev OSIndications) OSIndications {
72 return prev & ^i
73 })
74}
75
76func modifyOSIndications(f func(prev OSIndications) OSIndications) error {
77 osIndicationsMutex.Lock()
78 defer osIndicationsMutex.Unlock()
79
80 var osIndications OSIndications
81 rawIn, _, err := Read(ScopeGlobal, "OsIndications")
82 if err == nil && len(rawIn) == 8 {
83 osIndications = OSIndications(binary.LittleEndian.Uint64(rawIn))
84 } else if err != nil && !errors.Is(err, os.ErrNotExist) {
85 return fmt.Errorf("unable to read OsIndications variable: %w", err)
86 }
87 osIndications = f(osIndications)
88 var raw [8]byte
89 binary.LittleEndian.PutUint64(raw[:], uint64(osIndications))
90 if err := Write(ScopeGlobal, "OsIndications", AttrNonVolatile|AttrRuntimeAccess, raw[:]); err != nil {
91 return fmt.Errorf("failed to write OSIndications variable: %w", err)
92 }
93 return nil
94}