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