| Lorenz Brun | 509c709 | 2024-09-09 17:49:42 +0200 | [diff] [blame^] | 1 | package efivarfs |
| 2 | |
| 3 | import ( |
| 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. |
| 15 | type OSIndications uint64 |
| 16 | |
| 17 | const ( |
| 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. |
| 45 | var osIndicationsMutex sync.Mutex |
| 46 | |
| 47 | // OSIndicationsSupported indicates which of the OS indication features and |
| 48 | // actions that the firmware supports. |
| 49 | func 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. |
| 62 | func 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. |
| 70 | func ClearOSIndications(i OSIndications) error { |
| 71 | return modifyOSIndications(func(prev OSIndications) OSIndications { |
| 72 | return prev & ^i |
| 73 | }) |
| 74 | } |
| 75 | |
| 76 | func 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 | } |